summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-16 18:10:10 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-16 18:10:10 +0000
commitd755061465c24de58568183df5e7b826e934ae5d (patch)
treeb378078e37c3ae384ea7e3b7ca91398754fd7fac
parent6364c14cc1f445d471bca118dca5af5a85b2c5dc (diff)
downloadgitlab-ce-d755061465c24de58568183df5e7b826e934ae5d.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/message_field.vue4
-rw-r--r--app/assets/javascripts/terraform/components/states_table_actions.vue106
-rw-r--r--app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql5
-rw-r--r--app/assets/javascripts/vue_shared/directives/popover.js22
-rw-r--r--app/assets/stylesheets/components/popover.scss111
-rw-r--r--app/assets/stylesheets/framework/mixins.scss12
-rw-r--r--app/assets/stylesheets/page_bundles/_pipeline_mixins.scss10
-rw-r--r--app/assets/stylesheets/page_bundles/build.scss14
-rw-r--r--app/assets/stylesheets/page_bundles/ci_status.scss34
-rw-r--r--app/assets/stylesheets/page_bundles/oncall_schedules.scss12
-rw-r--r--app/assets/stylesheets/page_bundles/pipeline.scss32
-rw-r--r--app/assets/stylesheets/page_bundles/pipelines.scss6
-rw-r--r--app/controllers/profiles/gpg_keys_controller.rb19
-rw-r--r--app/controllers/projects/alerting/notifications_controller.rb2
-rw-r--r--app/controllers/projects/prometheus/alerts_controller.rb2
-rw-r--r--app/controllers/users_controller.rb5
-rw-r--r--app/finders/ci/daily_build_group_report_results_finder.rb2
-rw-r--r--app/graphql/types/merge_request_type.rb6
-rw-r--r--app/services/alert_management/process_prometheus_alert_service.rb12
-rw-r--r--app/services/clusters/applications/prometheus_health_check_service.rb2
-rw-r--r--app/services/projects/alerting/notify_service.rb14
-rw-r--r--app/services/projects/prometheus/alerts/notify_service.rb25
-rw-r--r--app/views/notify/new_release_email.html.haml2
-rw-r--r--changelogs/unreleased/258810-2-itteration-be.yml5
-rw-r--r--changelogs/unreleased/273592-terraform-delete.yml5
-rw-r--r--changelogs/unreleased/290762-move-the-action-from-profiles-gpgkeyscontroller-get_keys-to-usersc.yml5
-rw-r--r--changelogs/unreleased/psi-dark-pipelines.yml5
-rw-r--r--changelogs/unreleased/sh-fix-release-markdown.yml5
-rw-r--r--config/routes/user.rb2
-rw-r--r--doc/administration/geo/glossary.md112
-rw-r--r--doc/administration/logs.md2
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql10
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json28
-rw-r--r--doc/api/graphql/reference/index.md2
-rw-r--r--doc/development/README.md6
-rw-r--r--doc/development/api_graphql_styleguide.md26
-rw-r--r--doc/development/chatops_on_gitlabcom.md51
-rw-r--r--doc/development/feature_flags/index.md25
-rw-r--r--doc/development/feature_flags/process.md21
-rw-r--r--doc/development/pipelines.md4
-rw-r--r--doc/topics/autodevops/customize.md78
-rw-r--r--doc/user/application_security/api_fuzzing/index.md70
-rw-r--r--doc/user/application_security/index.md37
-rw-r--r--doc/user/gitlab_com/index.md85
-rw-r--r--doc/user/packages/dependency_proxy/index.md22
-rw-r--r--locale/gitlab.pot35
-rw-r--r--spec/controllers/profiles/gpg_keys_controller_spec.rb117
-rw-r--r--spec/controllers/projects/alerting/notifications_controller_spec.rb2
-rw-r--r--spec/controllers/projects/prometheus/alerts_controller_spec.rb2
-rw-r--r--spec/controllers/users_controller_spec.rb120
-rw-r--r--spec/features/projects/terraform_spec.rb18
-rw-r--r--spec/frontend/helpers/stub_component.js12
-rw-r--r--spec/frontend/invite_members/components/members_token_select_spec.js4
-rw-r--r--spec/frontend/monitoring/components/group_empty_state_spec.js12
-rw-r--r--spec/frontend/packages/details/components/package_history_spec.js6
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_status_token_spec.js16
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js16
-rw-r--r--spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap4
-rw-r--r--spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap5
-rw-r--r--spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap5
-rw-r--r--spec/frontend/registry/explorer/pages/list_spec.js4
-rw-r--r--spec/frontend/registry/explorer/stubs.js32
-rw-r--r--spec/frontend/terraform/components/states_table_actions_spec.js53
-rw-r--r--spec/graphql/types/merge_request_type_spec.rb2
-rw-r--r--spec/mailers/emails/releases_spec.rb9
-rw-r--r--spec/routing/routing_spec.rb13
-rw-r--r--spec/services/alert_management/process_prometheus_alert_service_spec.rb2
-rw-r--r--spec/services/projects/alerting/notify_service_spec.rb2
-rw-r--r--spec/services/projects/prometheus/alerts/notify_service_spec.rb4
-rw-r--r--spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb3
70 files changed, 989 insertions, 574 deletions
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
index 7d08815b033..8f0e5aef456 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
@@ -1,13 +1,9 @@
<script>
import { GlIcon, GlPopover } from '@gitlab/ui';
import { __, sprintf } from '../../../locale';
-import popover from '../../../vue_shared/directives/popover';
import { MAX_TITLE_LENGTH, MAX_BODY_LENGTH } from '../../constants';
export default {
- directives: {
- popover,
- },
components: {
GlIcon,
GlPopover,
diff --git a/app/assets/javascripts/terraform/components/states_table_actions.vue b/app/assets/javascripts/terraform/components/states_table_actions.vue
index 5375e4e45f7..44b0713e544 100644
--- a/app/assets/javascripts/terraform/components/states_table_actions.vue
+++ b/app/assets/javascripts/terraform/components/states_table_actions.vue
@@ -1,14 +1,29 @@
<script>
-import { GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui';
+import {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlFormGroup,
+ GlFormInput,
+ GlIcon,
+ GlModal,
+ GlSprintf,
+} from '@gitlab/ui';
import { s__ } from '~/locale';
import lockState from '../graphql/mutations/lock_state.mutation.graphql';
import unlockState from '../graphql/mutations/unlock_state.mutation.graphql';
+import removeState from '../graphql/mutations/remove_state.mutation.graphql';
export default {
components: {
GlDropdown,
+ GlDropdownDivider,
GlDropdownItem,
+ GlFormGroup,
+ GlFormInput,
GlIcon,
+ GlModal,
+ GlSprintf,
},
props: {
state: {
@@ -19,20 +34,59 @@ export default {
data() {
return {
loading: false,
+ showRemoveModal: false,
+ removeConfirmText: '',
};
},
i18n: {
downloadJSON: s__('Terraform|Download JSON'),
lock: s__('Terraform|Lock'),
+ modalBody: s__(
+ 'Terraform|You are about to remove the State file %{name}. This will permanently delete all the State versions and history. The infrastructure provisioned previously will remain intact, only the state file with all its versions are to be removed. This action is non-revertible.',
+ ),
+ modalCancel: s__('Terraform|Cancel'),
+ modalHeader: s__('Terraform|Are you sure you want to remove the Terraform State %{name}?'),
+ modalInputLabel: s__(
+ 'Terraform|To remove the State file and its versions, type %{name} to confirm:',
+ ),
+ modalRemove: s__('Terraform|Remove'),
+ remove: s__('Terraform|Remove state file and versions'),
unlock: s__('Terraform|Unlock'),
},
+ computed: {
+ cancelModalProps() {
+ return {
+ text: this.$options.i18n.modalCancel,
+ attributes: [],
+ };
+ },
+ disableModalSubmit() {
+ return this.removeConfirmText !== this.state.name;
+ },
+ primaryModalProps() {
+ return {
+ text: this.$options.i18n.modalRemove,
+ attributes: [{ disabled: this.disableModalSubmit }, { variant: 'danger' }],
+ };
+ },
+ },
methods: {
+ hideModal() {
+ this.showRemoveModal = false;
+ this.removeConfirmText = '';
+ },
lock() {
this.stateMutation(lockState);
},
unlock() {
this.stateMutation(unlockState);
},
+ remove() {
+ if (!this.disableModalSubmit) {
+ this.hideModal();
+ this.stateMutation(removeState);
+ }
+ },
stateMutation(mutation) {
this.loading = true;
this.$apollo
@@ -83,6 +137,56 @@ export default {
<gl-dropdown-item v-else data-testid="terraform-state-lock" @click="lock">
{{ $options.i18n.lock }}
</gl-dropdown-item>
+
+ <gl-dropdown-divider />
+
+ <gl-dropdown-item data-testid="terraform-state-remove" @click="showRemoveModal = true">
+ {{ $options.i18n.remove }}
+ </gl-dropdown-item>
</gl-dropdown>
+
+ <gl-modal
+ :modal-id="`terraform-state-actions-remove-modal-${state.name}`"
+ :visible="showRemoveModal"
+ :action-primary="primaryModalProps"
+ :action-cancel="cancelModalProps"
+ @ok="remove"
+ @cancel="hideModal"
+ @close="hideModal"
+ @hide="hideModal"
+ >
+ <template #modal-title>
+ <gl-sprintf :message="$options.i18n.modalHeader">
+ <template #name>
+ <span>{{ state.name }}</span>
+ </template>
+ </gl-sprintf>
+ </template>
+
+ <p>
+ <gl-sprintf :message="$options.i18n.modalBody">
+ <template #name>
+ <span>{{ state.name }}</span>
+ </template>
+ </gl-sprintf>
+ </p>
+
+ <gl-form-group>
+ <template #label>
+ <gl-sprintf :message="$options.i18n.modalInputLabel">
+ <template #name>
+ <code>{{ state.name }}</code>
+ </template>
+ </gl-sprintf>
+ </template>
+ <gl-form-input
+ :id="`terraform-state-remove-input-${state.name}`"
+ ref="input"
+ v-model="removeConfirmText"
+ type="text"
+ @keyup.enter="remove"
+ />
+ </gl-form-group>
+ </gl-modal>
</div>
</template>
diff --git a/app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql b/app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql
new file mode 100644
index 00000000000..d85ebb9cea2
--- /dev/null
+++ b/app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql
@@ -0,0 +1,5 @@
+mutation removeState($stateID: TerraformStateID!) {
+ terraformStateDelete(input: { id: $stateID }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/vue_shared/directives/popover.js b/app/assets/javascripts/vue_shared/directives/popover.js
deleted file mode 100644
index c913bc34c68..00000000000
--- a/app/assets/javascripts/vue_shared/directives/popover.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import $ from 'jquery';
-
-/**
- * Helper to user bootstrap popover in vue.js.
- * Follow docs for html attributes: https://getbootstrap.com/docs/3.3/javascript/#static-popover
- *
- * @example
- * import popover from 'vue_shared/directives/popover.js';
- * {
- * directives: [popover]
- * }
- * <a v-popover="{options}">popover</a>
- */
-export default {
- bind(el, binding) {
- $(el).popover(binding.value);
- },
-
- unbind(el) {
- $(el).popover('dispose');
- },
-};
diff --git a/app/assets/stylesheets/components/popover.scss b/app/assets/stylesheets/components/popover.scss
deleted file mode 100644
index f870948cc4f..00000000000
--- a/app/assets/stylesheets/components/popover.scss
+++ /dev/null
@@ -1,111 +0,0 @@
-.popover {
- max-width: $popover-max-width;
- border: 1px solid $gray-100;
- box-shadow: $popover-box-shadow;
- font-size: $gl-font-size-small;
-
- /**
- * Blue popover variation
- */
- &.blue {
- background-color: $blue-600;
- border-color: $blue-600;
-
- .popover-body {
- color: $white;
- }
-
- &.bs-popover-bottom {
- .arrow::before,
- .arrow::after {
- border-bottom-color: $blue-600;
- }
- }
-
- &.bs-popover-top {
- .arrow::before,
- .arrow::after {
- border-top-color: $blue-600;
- }
- }
-
- &.bs-popover-right {
- .arrow::after,
- .arrow::before {
- border-right-color: $blue-600;
- }
- }
-
- &.bs-popover-left {
- .arrow::before,
- .arrow::after {
- border-left-color: $blue-600;
- }
- }
- }
-}
-
-.bs-popover-top {
- /* When popover position is top, the arrow is translated 1 pixel
- * due to the box-shadow include in our custom styles.
- */
- > .arrow::before {
- border-top-color: $gray-100;
- bottom: 1px;
- }
-
- > .arrow::after {
- bottom: 2px;
- }
-}
-
-.bs-popover-bottom {
- > .arrow::before {
- border-bottom-color: $gray-100;
- }
-
- > .popover-header::before {
- border-color: $white;
- }
-}
-
-.bs-popover-right > .arrow::before {
- border-right-color: $gray-100;
-}
-
-.bs-popover-left > .arrow::before {
- border-left-color: $gray-100;
-}
-
-.popover-header {
- background-color: $white;
- font-size: $gl-font-size-small;
-}
-
-.popover-body {
- padding: $gl-padding $gl-padding-12;
-
- > .popover-hr {
- margin: $gl-padding 0;
- }
-}
-
-/**
-* mr_popover component
-*/
-.mr-popover {
- .text-secondary {
- font-size: 12px;
- line-height: 1.33;
- }
-}
-
-.suggest-gitlab-ci-yml {
- margin-top: -1em;
-
- .popover-header {
- padding: $gl-padding;
- display: flex;
- align-items: center;
- }
-}
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index e40ef04c4a0..7ba9236b833 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -146,7 +146,11 @@
}
@mixin green-status-color {
- @include status-color($green-100, $green-500, $green-700);
+ @include status-color(
+ var(--green-100, $green-100),
+ var(--green-500, $green-500),
+ var(--green-700, $green-700)
+ );
}
@mixin fade($gradient-direction, $gradient-color) {
@@ -254,9 +258,9 @@
@mixin build-trace-bar($height) {
height: $height;
min-height: $height;
- background: $gray-light;
- border: 1px solid $border-color;
- color: $gl-text-color;
+ background: var(--gray-50, $gray-50);
+ border: 1px solid var(--border-color, $border-color);
+ color: var(--gl-text-color, $gl-text-color);
padding: $grid-size;
}
diff --git a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
index 9db3baf0fdf..cc876c9a635 100644
--- a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
+++ b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
@@ -4,7 +4,7 @@
position: absolute;
top: 48%;
left: -$length;
- border-top: 2px solid $border-color;
+ border-top: 2px solid var(--border-color, $border-color);
width: $length;
height: 1px;
}
@@ -14,14 +14,14 @@
display: inline-block;
padding: 8px 10px 9px;
width: 100%;
- border: 1px solid $border-color;
+ border: 1px solid var(--border-color, $border-color);
border-radius: $border-radius;
- background-color: $white;
+ background-color: var(--white, $white);
&:hover {
- background-color: $gray-darker;
+ background-color: var(--gray-50, $gray-50);
border: 1px solid $dropdown-toggle-active-border-color;
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
}
}
diff --git a/app/assets/stylesheets/page_bundles/build.scss b/app/assets/stylesheets/page_bundles/build.scss
index 2f0f4a46658..3962c546b51 100644
--- a/app/assets/stylesheets/page_bundles/build.scss
+++ b/app/assets/stylesheets/page_bundles/build.scss
@@ -61,7 +61,7 @@
}
.environment-information {
- border: 1px solid $border-color;
+ border: 1px solid var(--border-color, $border-color);
padding: 8px $gl-padding 12px;
border-radius: $border-radius-default;
@@ -219,9 +219,9 @@
}
.builds-container {
- background-color: $white;
- border-top: 1px solid $border-color;
- border-bottom: 1px solid $border-color;
+ background-color: var(--white, $white);
+ border-top: 1px solid var(--border-color, $border-color);
+ border-bottom: 1px solid var(--border-color, $border-color);
max-height: 300px;
width: 289px;
overflow: auto;
@@ -237,7 +237,7 @@
width: 270px;
&:hover {
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
}
}
@@ -256,13 +256,13 @@
}
&:hover {
- background-color: $gray-darker;
+ background-color: var(--gray-50, $gray-50);
}
}
}
.link-commit {
- color: $blue-600;
+ color: var(--blue-600, $blue-600);
}
}
diff --git a/app/assets/stylesheets/page_bundles/ci_status.scss b/app/assets/stylesheets/page_bundles/ci_status.scss
index 89fbe1c4caf..232d363b7f1 100644
--- a/app/assets/stylesheets/page_bundles/ci_status.scss
+++ b/app/assets/stylesheets/page_bundles/ci_status.scss
@@ -2,7 +2,7 @@
.ci-status {
padding: 2px 7px 4px;
- border: 1px solid $gray-darker;
+ border: 1px solid var(--border-color, $border-color);
white-space: nowrap;
border-radius: 4px;
@@ -18,7 +18,11 @@
}
&.ci-failed {
- @include status-color($red-100, $red-500, $red-600);
+ @include status-color(
+ var(--red-100, $red-100),
+ var(--red-500, $red-500),
+ var(--red-600, $red-600)
+ );
}
&.ci-success {
@@ -30,8 +34,8 @@
&.ci-disabled,
&.ci-scheduled,
&.ci-manual {
- color: $gl-text-color;
- border-color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
+ border-color: currentColor;
&:not(span):hover {
background-color: rgba($gl-text-color, 0.07);
@@ -39,25 +43,37 @@
}
&.ci-preparing {
- @include status-color($gray-100, $gray-300, $gray-400);
+ @include status-color(
+ var(--gray-100, $gray-100),
+ var(--gray-300, $gray-300),
+ var(--gray-400, $gray-400)
+ );
}
&.ci-pending,
&.ci-waiting-for-resource,
&.ci-failed-with-warnings,
&.ci-success-with-warnings {
- @include status-color($orange-50, $orange-500, $orange-700);
+ @include status-color(
+ var(--orange-50, $orange-50),
+ var(--orange-500, $orange-500),
+ var(--orange-700, $orange-700)
+ );
}
&.ci-info,
&.ci-running {
- @include status-color($blue-100, $blue-500, $blue-600);
+ @include status-color(
+ var(--blue-100, $blue-100),
+ var(--blue-500, $blue-500),
+ var(--blue-600, $blue-600)
+ );
}
&.ci-created,
&.ci-skipped {
- color: $gl-text-color-secondary;
- border-color: $gl-text-color-secondary;
+ color: var(--gray-500, $gray-500);
+ border-color: currentColor;
&:not(span):hover {
background-color: rgba($gl-text-color-secondary, 0.07);
diff --git a/app/assets/stylesheets/page_bundles/oncall_schedules.scss b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
index 61163ebc830..3c95ecc9bf0 100644
--- a/app/assets/stylesheets/page_bundles/oncall_schedules.scss
+++ b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
@@ -32,7 +32,7 @@
//// Copied from roadmaps.scss - adapted for on-call schedules
$header-item-height: 72px;
$item-height: 40px;
-$details-cell-width: 150px;
+$details-cell-width: 180px;
$timeline-cell-height: 32px;
$timeline-cell-width: 180px;
$border-style: 1px solid var(--gray-100, $gray-100);
@@ -98,7 +98,7 @@ $column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradi
.item-label {
@include gl-py-4;
- @include gl-pl-7;
+ @include gl-pl-4;
border-right: $border-style;
border-bottom: $border-style;
}
@@ -147,7 +147,6 @@ $column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradi
.timeline-cell {
@include float-left;
height: $item-height;
- border-bottom: $border-style;
}
.details-cell {
@@ -181,3 +180,10 @@ $column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradi
transform: translateX(-50%);
}
}
+
+.gl-token {
+ .gl-avatar-labeled-label {
+ @include gl-text-white;
+ @include gl-font-weight-normal;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/pipeline.scss b/app/assets/stylesheets/page_bundles/pipeline.scss
index 6b70cda4891..d9ab52774bd 100644
--- a/app/assets/stylesheets/page_bundles/pipeline.scss
+++ b/app/assets/stylesheets/page_bundles/pipeline.scss
@@ -33,7 +33,7 @@
}
.stage {
- color: $gl-text-color-secondary;
+ color: var(--gray-500, $gray-500);
font-weight: $gl-font-weight-normal;
vertical-align: middle;
}
@@ -62,7 +62,7 @@
a {
font-weight: $gl-font-weight-bold;
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
text-decoration: none;
&:focus,
@@ -124,7 +124,7 @@
display: flex;
width: 100%;
min-height: $dropdown-max-height-lg;
- background-color: $gray-light;
+ background-color: var(--gray-50, $gray-50);
padding: $gl-padding 0;
overflow: auto;
}
@@ -177,7 +177,7 @@
a {
text-decoration: none;
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
}
svg {
@@ -249,18 +249,18 @@
height: 25px;
position: absolute;
top: -31px;
- border-top: 2px solid $border-color;
+ border-top: 2px solid var(--border-color, $border-color);
}
&::after {
left: -44px;
- border-right: 2px solid $border-color;
+ border-right: 2px solid var(--border-color, $border-color);
border-radius: 0 20px;
}
&::before {
right: -44px;
- border-left: 2px solid $border-color;
+ border-left: 2px solid var(--border-color, $border-color);
border-radius: 20px 0 0;
}
}
@@ -316,7 +316,7 @@
a.build-content:hover,
button.build-content:hover {
- background-color: $gray-darker;
+ background-color: var(--gray-100, $gray-100);
border: 1px solid $dropdown-toggle-active-border-color;
}
@@ -327,7 +327,7 @@
position: absolute;
top: 48%;
right: -48px;
- border-top: 2px solid $border-color;
+ border-top: 2px solid var(--border-color, $border-color);
width: 48px;
height: 1px;
}
@@ -340,7 +340,7 @@
content: '';
top: -49px;
position: absolute;
- border-bottom: 2px solid $border-color;
+ border-bottom: 2px solid var(--border-color, $border-color);
width: 25px;
height: 69px;
}
@@ -348,14 +348,14 @@
// Right connecting curves
&::after {
right: -25px;
- border-right: 2px solid $border-color;
+ border-right: 2px solid var(--border-color, $border-color);
border-radius: 0 0 20px;
}
// Left connecting curves
&::before {
left: -25px;
- border-left: 2px solid $border-color;
+ border-left: 2px solid var(--border-color, $border-color);
border-radius: 0 0 0 20px;
}
}
@@ -390,7 +390,7 @@
line-height: 0;
svg {
- fill: $gl-text-color-secondary;
+ fill: var(--gray-500, $gray-500);
}
.spinner {
@@ -488,13 +488,13 @@
left: -6px;
margin-top: 3px;
border-width: 7px 5px 7px 0;
- border-right-color: $border-color;
+ border-right-color: var(--border-color, $border-color);
}
&::after {
left: -5px;
border-width: 10px 7px 10px 0;
- border-right-color: $white;
+ border-right-color: var(--white, $white);
}
}
@@ -519,5 +519,5 @@
}
.progress-bar.bg-primary {
- background-color: $blue-500 !important;
+ background-color: var(--blue-500, $blue-500) !important;
}
diff --git a/app/assets/stylesheets/page_bundles/pipelines.scss b/app/assets/stylesheets/page_bundles/pipelines.scss
index 26b65f4dd38..dbde7933a8b 100644
--- a/app/assets/stylesheets/page_bundles/pipelines.scss
+++ b/app/assets/stylesheets/page_bundles/pipelines.scss
@@ -22,7 +22,7 @@
min-width: 170px; //Guarantees buttons don't break in several lines.
.btn-default {
- color: $gl-text-color-secondary;
+ color: var(--gray-500, $gray-500);
}
.btn.btn-retry:hover,
@@ -32,7 +32,7 @@
}
svg path {
- fill: $gl-text-color-secondary;
+ fill: var(--gray-500, $gray-500);
}
.dropdown-menu {
@@ -42,7 +42,7 @@
.dropdown-toggle,
.dropdown-menu {
- color: $gl-text-color-secondary;
+ color: var(--gray-500, $gray-500);
}
.btn-group.open .btn-default {
diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb
index f1d4f9b2cc0..7f04927f517 100644
--- a/app/controllers/profiles/gpg_keys_controller.rb
+++ b/app/controllers/profiles/gpg_keys_controller.rb
@@ -2,7 +2,6 @@
class Profiles::GpgKeysController < Profiles::ApplicationController
before_action :set_gpg_key, only: [:destroy, :revoke]
- skip_before_action :authenticate_user!, only: [:get_keys]
feature_category :users
@@ -40,24 +39,6 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
end
end
- # Get all gpg keys of a user(params[:username]) in a text format
- def get_keys
- if params[:username].present?
- begin
- user = UserFinder.new(params[:username]).find_by_username
- if user.present?
- render plain: user.gpg_keys.select(&:verified?).map(&:key).join("\n")
- else
- render_404
- end
- rescue => e
- render html: e.message
- end
- else
- render_404
- end
- end
-
private
def gpg_key_params
diff --git a/app/controllers/projects/alerting/notifications_controller.rb b/app/controllers/projects/alerting/notifications_controller.rb
index 86121bed381..db5d91308db 100644
--- a/app/controllers/projects/alerting/notifications_controller.rb
+++ b/app/controllers/projects/alerting/notifications_controller.rb
@@ -31,7 +31,7 @@ module Projects
end
def notify_service
- notify_service_class.new(project, current_user, notification_payload)
+ notify_service_class.new(project, notification_payload)
end
def notify_service_class
diff --git a/app/controllers/projects/prometheus/alerts_controller.rb b/app/controllers/projects/prometheus/alerts_controller.rb
index 7f711417f0b..19c908026cf 100644
--- a/app/controllers/projects/prometheus/alerts_controller.rb
+++ b/app/controllers/projects/prometheus/alerts_controller.rb
@@ -73,7 +73,7 @@ module Projects
def notify_service
Projects::Prometheus::Alerts::NotifyService
- .new(project, current_user, params.permit!)
+ .new(project, params.permit!)
end
def create_service
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 0701a63fe0b..46245286820 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -58,6 +58,11 @@ class UsersController < ApplicationController
end
end
+ # Get all gpg keys of a user(params[:username]) in a text format
+ def gpg_keys
+ render plain: user.gpg_keys.select(&:verified?).map(&:key).join("\n")
+ end
+
def groups
load_groups
diff --git a/app/finders/ci/daily_build_group_report_results_finder.rb b/app/finders/ci/daily_build_group_report_results_finder.rb
index 2f6e0e47017..ef97ccb4c0f 100644
--- a/app/finders/ci/daily_build_group_report_results_finder.rb
+++ b/app/finders/ci/daily_build_group_report_results_finder.rb
@@ -40,7 +40,7 @@ module Ci
date: start_date..end_date
}
- if ref_path
+ if ref_path.present?
params[:ref_path] = ref_path
else
params[:default_branch] = true
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 9346a6b9f27..816160e58f7 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -167,6 +167,8 @@ module Types
description: 'Indicates if the merge request is mergeable'
field :commits_without_merge_commits, Types::CommitType.connection_type, null: true,
calls_gitaly: true, description: 'Merge request commits excluding merge commits'
+ field :security_auto_fix, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Indicates if the merge request is created by @GitLab-Security-Bot.'
def approved_by
object.approved_by_users
@@ -229,6 +231,10 @@ module Types
def commits_without_merge_commits
object.recent_commits.without_merge_commits
end
+
+ def security_auto_fix
+ object.author == User.security_bot
+ end
end
end
diff --git a/app/services/alert_management/process_prometheus_alert_service.rb b/app/services/alert_management/process_prometheus_alert_service.rb
index 28ce5401a6c..753162bfdbf 100644
--- a/app/services/alert_management/process_prometheus_alert_service.rb
+++ b/app/services/alert_management/process_prometheus_alert_service.rb
@@ -1,10 +1,16 @@
# frozen_string_literal: true
module AlertManagement
- class ProcessPrometheusAlertService < BaseService
+ class ProcessPrometheusAlertService
+ include BaseServiceUtility
include Gitlab::Utils::StrongMemoize
include ::IncidentManagement::Settings
+ def initialize(project, payload)
+ @project = project
+ @payload = payload
+ end
+
def execute
return bad_request unless incoming_payload.has_required_attributes?
@@ -19,6 +25,8 @@ module AlertManagement
private
+ attr_reader :project, :payload
+
def process_alert_management_alert
if incoming_payload.resolved?
process_resolved_alert_management_alert
@@ -127,7 +135,7 @@ module AlertManagement
strong_memoize(:incoming_payload) do
Gitlab::AlertManagement::Payload.parse(
project,
- params,
+ payload,
monitoring_tool: Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
)
end
diff --git a/app/services/clusters/applications/prometheus_health_check_service.rb b/app/services/clusters/applications/prometheus_health_check_service.rb
index 843202ab6ff..eda47f56e72 100644
--- a/app/services/clusters/applications/prometheus_health_check_service.rb
+++ b/app/services/clusters/applications/prometheus_health_check_service.rb
@@ -65,7 +65,7 @@ module Clusters
notification_payload = build_notification_payload(project)
integration = project.alert_management_http_integrations.active.first
- Projects::Alerting::NotifyService.new(project, nil, notification_payload).execute(integration&.token, integration)
+ Projects::Alerting::NotifyService.new(project, notification_payload).execute(integration&.token, integration)
@logger.info(message: 'Successfully notified of Prometheus newly unhealthy', cluster_id: @cluster.id, project_id: project.id)
end
diff --git a/app/services/projects/alerting/notify_service.rb b/app/services/projects/alerting/notify_service.rb
index ab8f53a3757..014fb0e3ed3 100644
--- a/app/services/projects/alerting/notify_service.rb
+++ b/app/services/projects/alerting/notify_service.rb
@@ -2,10 +2,16 @@
module Projects
module Alerting
- class NotifyService < BaseService
+ class NotifyService
+ include BaseServiceUtility
include Gitlab::Utils::StrongMemoize
include ::IncidentManagement::Settings
+ def initialize(project, payload)
+ @project = project
+ @payload = payload
+ end
+
def execute(token, integration = nil)
@integration = integration
@@ -24,7 +30,7 @@ module Projects
private
- attr_reader :integration
+ attr_reader :project, :payload, :integration
def process_alert
if alert.persisted?
@@ -101,7 +107,7 @@ module Projects
def incoming_payload
strong_memoize(:incoming_payload) do
- Gitlab::AlertManagement::Payload.parse(project, params.to_h)
+ Gitlab::AlertManagement::Payload.parse(project, payload.to_h)
end
end
@@ -110,7 +116,7 @@ module Projects
end
def valid_payload_size?
- Gitlab::Utils::DeepSize.new(params).valid?
+ Gitlab::Utils::DeepSize.new(payload).valid?
end
def active_integration?
diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb
index 1f90263ba4a..93165a58470 100644
--- a/app/services/projects/prometheus/alerts/notify_service.rb
+++ b/app/services/projects/prometheus/alerts/notify_service.rb
@@ -3,7 +3,7 @@
module Projects
module Prometheus
module Alerts
- class NotifyService < BaseService
+ class NotifyService
include Gitlab::Utils::StrongMemoize
include ::IncidentManagement::Settings
@@ -17,9 +17,14 @@ module Projects
SUPPORTED_VERSION = '4'
+ def initialize(project, payload)
+ @project = project
+ @payload = payload
+ end
+
def execute(token, integration = nil)
return bad_request unless valid_payload_size?
- return unprocessable_entity unless self.class.processable?(params)
+ return unprocessable_entity unless self.class.processable?(payload)
return unauthorized unless valid_alert_manager_token?(token, integration)
process_prometheus_alerts
@@ -27,18 +32,20 @@ module Projects
ServiceResponse.success
end
- def self.processable?(params)
+ def self.processable?(payload)
# Workaround for https://gitlab.com/gitlab-org/gitlab/-/issues/220496
- return false unless params
+ return false unless payload
- REQUIRED_PAYLOAD_KEYS.subset?(params.keys.to_set) &&
- params['version'] == SUPPORTED_VERSION
+ REQUIRED_PAYLOAD_KEYS.subset?(payload.keys.to_set) &&
+ payload['version'] == SUPPORTED_VERSION
end
private
+ attr_reader :project, :payload
+
def valid_payload_size?
- Gitlab::Utils::DeepSize.new(params).valid?
+ Gitlab::Utils::DeepSize.new(payload).valid?
end
def firings
@@ -50,7 +57,7 @@ module Projects
end
def alerts
- params['alerts']
+ payload['alerts']
end
def valid_alert_manager_token?(token, integration)
@@ -121,7 +128,7 @@ module Projects
def process_prometheus_alerts
alerts.each do |alert|
AlertManagement::ProcessPrometheusAlertService
- .new(project, nil, alert.to_h)
+ .new(project, alert.to_h)
.execute
end
end
diff --git a/app/views/notify/new_release_email.html.haml b/app/views/notify/new_release_email.html.haml
index 45e99f3c07a..9cef4cd85cd 100644
--- a/app/views/notify/new_release_email.html.haml
+++ b/app/views/notify/new_release_email.html.haml
@@ -15,4 +15,4 @@
%p
%h4= _("Release notes:")
- = markdown_field(@release, :description)
+ = markdown(@release.description, pipeline: :email, author: @release.author)
diff --git a/changelogs/unreleased/258810-2-itteration-be.yml b/changelogs/unreleased/258810-2-itteration-be.yml
new file mode 100644
index 00000000000..48547e115bd
--- /dev/null
+++ b/changelogs/unreleased/258810-2-itteration-be.yml
@@ -0,0 +1,5 @@
+---
+title: Add MergeRequest to VulnerabilityType in GraphQL
+merge_request: 50082
+author:
+type: added
diff --git a/changelogs/unreleased/273592-terraform-delete.yml b/changelogs/unreleased/273592-terraform-delete.yml
new file mode 100644
index 00000000000..1f33e75ed89
--- /dev/null
+++ b/changelogs/unreleased/273592-terraform-delete.yml
@@ -0,0 +1,5 @@
+---
+title: Add delete button to terraform list vue
+merge_request: 48485
+author:
+type: added
diff --git a/changelogs/unreleased/290762-move-the-action-from-profiles-gpgkeyscontroller-get_keys-to-usersc.yml b/changelogs/unreleased/290762-move-the-action-from-profiles-gpgkeyscontroller-get_keys-to-usersc.yml
new file mode 100644
index 00000000000..e334c6f5d48
--- /dev/null
+++ b/changelogs/unreleased/290762-move-the-action-from-profiles-gpgkeyscontroller-get_keys-to-usersc.yml
@@ -0,0 +1,5 @@
+---
+title: Move profiles/gpg_keys#get_keys to users#gpg_keys
+merge_request: 49448
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/psi-dark-pipelines.yml b/changelogs/unreleased/psi-dark-pipelines.yml
new file mode 100644
index 00000000000..34dde2f7563
--- /dev/null
+++ b/changelogs/unreleased/psi-dark-pipelines.yml
@@ -0,0 +1,5 @@
+---
+title: Fix pipeline page in dark mode
+merge_request: 49214
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-release-markdown.yml b/changelogs/unreleased/sh-fix-release-markdown.yml
new file mode 100644
index 00000000000..e92a83f8ab9
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-release-markdown.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Markdown attachments in Releases not rendering with full URL
+merge_request: 50146
+author:
+type: fixed
diff --git a/config/routes/user.rb b/config/routes/user.rb
index ae6d3b2c3f1..515a9a23360 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -58,7 +58,7 @@ constraints(::Constraints::UserUrlConstrainer.new) do
get ':username.keys' => 'users#ssh_keys', constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }
# Get all GPG keys of user
- get ':username.gpg' => 'profiles/gpg_keys#get_keys', constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }
+ get ':username.gpg' => 'users#gpg_keys', constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }
scope(path: ':username',
as: :user,
diff --git a/doc/administration/geo/glossary.md b/doc/administration/geo/glossary.md
new file mode 100644
index 00000000000..e9d57284dd2
--- /dev/null
+++ b/doc/administration/geo/glossary.md
@@ -0,0 +1,112 @@
+---
+stage: Enablement
+group: Geo
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+type: howto
+---
+
+
+# Geo Glossary
+
+NOTE:
+We are updating the Geo documentation, user interface and commands to reflect these changes. Not all pages comply with
+these definitions yet.
+
+ These are the defined terms to describe all aspects of Geo. Using a set of clearly
+ defined terms helps us to communicate efficiently and avoids confusion. The language
+ on this page aims to be [ubiquitous](https://about.gitlab.com/handbook/communication/#ubiquitous-language)
+ and [as simple as possible](https://about.gitlab.com/handbook/communication/#simple-language).
+
+ We provide example diagrams and statements to demonstrate correct usage of terms.
+
+| Term | Definition | Scope | Discouraged synonyms |
+|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|-------------------------------------------------|
+| Node | An individual server that runs GitLab either with a specific role or as a whole (e.g. a Rails application node). In a cloud context this can be a specific machine type. | GitLab | instance, server |
+| Site | One or a collection of nodes running a single GitLab application. A site can be single-node or multi-node. | GitLab | deployment, installation instance |
+| Single-node site | A specific configuration of GitLab that uses exactly one node. | GitLab | single-server, single-instance
+| Multi-node site | A specific configuration of GitLab that uses more than one node. | GitLab | multi-server, multi-instance, high availability |
+| Primary site | A GitLab site that is configured to be read and writable. There can only be a single primary site. | Geo-specific | Geo deployment, Primary node |
+| Secondary site(s) | GitLab site that is configured to be read-only. There can be one or more secondary sites. | Geo-specific | Geo deployment, Secondary node |
+| Geo deployment | A collection of two or more GitLab sites with exactly one primary site being replicated by one or more secondary sites. | Geo-specific | |
+| Reference architecture(s) | A [specified configuration of GitLab for a number of users](../reference_architectures/index.md), possibly including multiple nodes and multiple sites. | GitLab | |
+| Promoting | Changing the role of a site from secondary to primary. | Geo-specific | |
+| Demoting | Changing the role of a site from primary to secondary. | Geo-specific | |
+| Failover | The entire process that shifts users from a primary Site to a secondary site. This includes promoting a secondary, but contains other parts as well e.g. scheduling maintenance. | Geo-specific | |
+
+## Examples
+
+### Single-node site
+
+```mermaid
+ graph TD
+ subgraph S-Site[Single-node site]
+ Node_3[GitLab node]
+ end
+```
+
+### Multi-node site
+
+```mermaid
+ graph TD
+ subgraph MN-Site[Multi-node site]
+ Node_1[Application node]
+ Node_2[Database node]
+ Node_3[Gitaly node]
+ end
+```
+
+### Geo deployment - Single-node sites
+
+This Geo deployment has a single-node primary site, a single-node secondary site:
+
+```mermaid
+ graph TD
+ subgraph Geo deployment
+ subgraph Primary[Primary site, single-node]
+ Node_1[GitLab node]
+ end
+ subgraph Secondary1[Secondary site 1, single-node]
+ Node_2[GitLab node]
+ end
+ end
+```
+
+### Geo deployment - Multi-node sites
+
+This Geo deployment has a multi-node primary site, a multi-node secondary site:
+
+```mermaid
+ graph TD
+ subgraph Geo deployment
+ subgraph Primary[Primary site, multi-node]
+ Node_1[Application node]
+ Node_2[Database node]
+ end
+ subgraph Secondary1[Secondary site 1, multi-node]
+ Node_5[Application node]
+ Node_6[Database node]
+ end
+ end
+```
+
+### Geo deployment - Mixed sites
+
+This Geo deployment has a multi-node primary site, a multi-node secondary site and another single-node secondary site:
+
+```mermaid
+ graph TD
+ subgraph Geo deployment
+ subgraph Primary[Primary site, multi-node]
+ Node_1[Application node]
+ Node_2[Database node]
+ Node_3[Gitaly node]
+ end
+ subgraph Secondary1[Secondary site 1, multi-node]
+ Node_5[Application node]
+ Node_6[Database node]
+ end
+ subgraph Secondary2[Secondary site 2, single-node]
+ Node_7[Single GitLab node]
+ end
+ end
+```
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index c0f5a38b0fe..3d5ba903941 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -1021,7 +1021,7 @@ previously listed components, it's helpful to simultaneously gather multiple log
from a GitLab instance.
NOTE:
-GitLab Support will often ask for one of these, and maintains the required tools.
+GitLab Support often asks for one of these, and maintains the required tools.
### Briefly tail the main logs
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index b7e76987d49..d7ad70c808e 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -13457,6 +13457,11 @@ type MergeRequest implements CurrentUserTodos & Noteable {
): String!
"""
+ Indicates if the merge request is created by @GitLab-Security-Bot.
+ """
+ securityAutoFix: Boolean
+
+ """
Indicates if the merge request will be rebased
"""
shouldBeRebased: Boolean!
@@ -25002,6 +25007,11 @@ type Vulnerability implements Noteable {
location: VulnerabilityLocation
"""
+ Merge request that fixes the vulnerability.
+ """
+ mergeRequest: MergeRequest
+
+ """
All notes on this noteable
"""
notes(
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index a8c20a1ee57..4adb92d351e 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -37122,6 +37122,20 @@
"deprecationReason": null
},
{
+ "name": "securityAutoFix",
+ "description": "Indicates if the merge request is created by @GitLab-Security-Bot.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "shouldBeRebased",
"description": "Indicates if the merge request will be rebased",
"args": [
@@ -72825,6 +72839,20 @@
"deprecationReason": null
},
{
+ "name": "mergeRequest",
+ "description": "Merge request that fixes the vulnerability.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "MergeRequest",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "notes",
"description": "All notes on this noteable",
"args": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 789a1ba16e3..a06b1159034 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -2088,6 +2088,7 @@ Autogenerated return type of MarkAsSpamSnippet.
| `rebaseCommitSha` | String | Rebase commit SHA of the merge request |
| `rebaseInProgress` | Boolean! | Indicates if there is a rebase currently in progress for the merge request |
| `reference` | String! | Internal reference of the merge request. Returned in shortened format by default |
+| `securityAutoFix` | Boolean | Indicates if the merge request is created by @GitLab-Security-Bot. |
| `shouldBeRebased` | Boolean! | Indicates if the merge request will be rebased |
| `shouldRemoveSourceBranch` | Boolean | Indicates if the source branch of the merge request will be deleted after merge |
| `sourceBranch` | String! | Source branch of the merge request |
@@ -3772,6 +3773,7 @@ Represents a vulnerability.
| `identifiers` | VulnerabilityIdentifier! => Array | Identifiers of the vulnerability. |
| `issueLinks` | VulnerabilityIssueLinkConnection! | List of issue links related to the vulnerability |
| `location` | VulnerabilityLocation | Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability |
+| `mergeRequest` | MergeRequest | Merge request that fixes the vulnerability. |
| `notes` | NoteConnection! | All notes on this noteable |
| `primaryIdentifier` | VulnerabilityIdentifier | Primary identifier of the vulnerability. |
| `project` | Project | The project on which the vulnerability was found |
diff --git a/doc/development/README.md b/doc/development/README.md
index 7adcfe091c6..1aa3b1aff06 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -72,8 +72,8 @@ Complementary reads:
### Development guidelines review
-When you submit a change to GitLab's development guidelines, the people
-you ask for reviews from depend on the level of change, as described below.
+When you submit a change to GitLab's development guidelines, who
+you ask for reviews depends on the level of change.
#### Wording, style, or link changes
@@ -88,7 +88,7 @@ maintainer or Technical Writer. These can include:
#### Specific changes
-If the MR proposes changes limited to a particular stage, group, or team,
+If the MR proposes changes that are limited to a particular stage, group, or team,
request a review and approval from an experienced GitLab Team Member in that
group. For example, if you're documenting a new internal API used exclusively by
a given group, request an engineering review from one of the group's members.
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 698fbd35a82..f3964fad38e 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -803,6 +803,32 @@ overhead. If you are writing:
- A `Mutation`, feel free to lookup objects directly.
- A `Resolver` or methods on a `BaseObject`, then you want to allow for batching.
+### Error handling
+
+Resolvers may raise errors, which will be converted to top-level errors as
+appropriate. All anticipated errors should be caught and transformed to an
+appropriate GraphQL error (see
+[`Gitlab::Graphql::Errors`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/graphql/errors.rb)).
+Any uncaught errors will be suppressed and the client will receive the message
+`Internal service error`.
+
+The one special case is permission errors. In the REST API we return
+`404 Not Found` for any resources that the user does not have permission to
+access. The equivalent behavior in GraphQL is for us to return `null` for
+all absent or unauthorized resources.
+Query resolvers **should not raise errors for unauthorized resources**.
+
+The rationale for this is that clients must not be able to distinguish between
+the absence of a record and the presence of one they do not have access to. To
+do so is a security vulnerability, since it leaks information we want to keep
+hidden.
+
+In most cases you don't need to worry about this - this is handled correctly by
+the resolver field authorization we declare with the `authorize` DSL calls. If
+you need to do something more custom however, remember, if you encounter an
+object the `current_user` does not have access to when resolving a field, then
+the entire field should resolve to `null`.
+
### Deriving resolvers (`BaseResolver.single` and `BaseResolver.last`)
For some simple use cases, we can derive resolvers from others.
diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md
index 16549f64c32..a2a0005f7cb 100644
--- a/doc/development/chatops_on_gitlabcom.md
+++ b/doc/development/chatops_on_gitlabcom.md
@@ -4,28 +4,63 @@ group: Configure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Chatops on GitLab.com
+# ChatOps on GitLab.com
ChatOps on GitLab.com allows GitLab team members to run various automation tasks on GitLab.com using Slack.
## Requesting access
-GitLab team-members may need access to Chatops on GitLab.com for administration
+GitLab team-members may need access to ChatOps on GitLab.com for administration
tasks such as:
- Configuring feature flags.
- Running `EXPLAIN` queries against the GitLab.com production replica.
- Get deployment status of all of our environments or for a specific commit: `/chatops run auto_deploy status [commit_sha]`
-To request access to Chatops on GitLab.com:
+To request access to ChatOps on GitLab.com:
-1. Log into <https://ops.gitlab.net/users/sign_in> **using the same username** as for GitLab.com (you may have to rename it).
- 1. You could also use the "Sign in with" Google button to sign in, with your GitLab.com email address.
-1. Ask one of your team members to add you to the `chatops` project in Ops. They can do it by running `/chatops run member add <username> gitlab-com/chatops --ops` command in the `#chat-ops-test` Slack channel.
-1. If you had to change your username for GitLab.com on the first step, make sure [to reflect this information](https://gitlab.com/gitlab-com/www-gitlab-com#adding-yourself-to-the-team-page) on [the team page](https://about.gitlab.com/company/team/).
+1. Sign in to [Internal GitLab for Operations](https://ops.gitlab.net/users/sign_in)
+ with one of the following methods:
+
+ - The same username you use on GitLab.com. You may have to choose a different
+ username later.
+ - Clicking the **Sign in with Google** button to sign in with your GitLab.com email address.
+
+1. Confirm that your username in [Internal GitLab for Operations](https://ops.gitlab.net/)
+ is the same as your username in [GitLab.com](https://gitlab.com/). If the usernames
+ don't match, update the username at [Internal GitLab for Operations](https://ops.gitlab.net/).
+
+1. Comment in your onboarding issue, and tag your onboarding buddy and your manager.
+ Request they add you to the `ops` ChatOps project by running this command
+ in the `#chat-ops-test` Slack channel, replacing `<username>` with your username:
+ `/chatops run member add <username> gitlab-com/chatops --ops`
+
+ <!-- vale gitlab.FirstPerson = NO -->
+
+ > Hi `__BUDDY_HANDLE__` and `__MANAGER_HANDLE__`, could you please add me to
+ > the ChatOps project in Ops by running this command:
+ > `/chatops run member add <username> gitlab-com/chatops --ops` in the
+ > `#chat-ops-test` Slack channel? Thanks in advance.
+
+ <!-- vale gitlab.FirstPerson = YES -->
+
+1. Ensure you've set up two-factor authentication.
+1. After you're added to the ChatOps project, run this command to check your user
+ status and ensure you can execute commands in the `#chat-ops-test` Slack channel:
+
+ ```plaintext
+ /chatops run user find <username>
+ ```
+
+ The bot guides you through the process of allowing your user to execute
+ commands in the `#chat-ops-test` Slack channel.
+
+1. If you had to change your username for GitLab.com on the first step, make sure
+ [to reflect this information](https://gitlab.com/gitlab-com/www-gitlab-com#adding-yourself-to-the-team-page)
+ on [the team page](https://about.gitlab.com/company/team/).
## See also
-- [Chatops Usage](../ci/chatops/README.md)
+- [ChatOps Usage](../ci/chatops/README.md)
- [Understanding EXPLAIN plans](understanding_explain_plans.md)
- [Feature Groups](feature_flags/development.md#feature-groups)
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index b75234a21d4..270e07ed755 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -6,6 +6,31 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
# Feature flags in development of GitLab
+## When to use feature flags
+
+Starting with GitLab 11.4, developers are required to use feature flags for
+non-trivial changes. Such changes include:
+
+- New features (e.g. a new merge request widget, epics, etc).
+- Complex performance improvements that may require additional testing in
+ production, such as rewriting complex queries.
+- Invasive changes to the user interface, such as a new navigation bar or the
+ removal of a sidebar.
+- Adding support for importing projects from a third-party service.
+- Risk of data loss
+
+In all cases, those working on the changes can best decide if a feature flag is
+necessary. For example, changing the color of a button doesn't need a feature
+flag, while changing the navigation bar definitely needs one. In case you are
+uncertain if a feature flag is necessary, simply ask about this in the merge
+request, and those reviewing the changes will likely provide you with an answer.
+
+When using a feature flag for UI elements, make sure to _also_ use a feature
+flag for the underlying backend code, if there is any. This ensures there is
+absolutely no way to use the feature until it is enabled.
+
+## How to use Feature Flags
+
Feature flags can be used to gradually deploy changes, regardless of whether
they are new features or performance improvements. By using feature flags,
you can determine the impact of GitLab-directed changes, while still being able
diff --git a/doc/development/feature_flags/process.md b/doc/development/feature_flags/process.md
index 64def57e51d..a70edc2a052 100644
--- a/doc/development/feature_flags/process.md
+++ b/doc/development/feature_flags/process.md
@@ -53,27 +53,6 @@ problems, such as outages.
Please also read the [development guide for feature flags](development.md).
-### When to use feature flags
-
-Starting with GitLab 11.4, developers are required to use feature flags for
-non-trivial changes. Such changes include:
-
-- New features (e.g. a new merge request widget, epics, etc).
-- Complex performance improvements that may require additional testing in
- production, such as rewriting complex queries.
-- Invasive changes to the user interface, such as a new navigation bar or the
- removal of a sidebar.
-- Adding support for importing projects from a third-party service.
-
-In all cases, those working on the changes can best decide if a feature flag is
-necessary. For example, changing the color of a button doesn't need a feature
-flag, while changing the navigation bar definitely needs one. In case you are
-uncertain if a feature flag is necessary, simply ask about this in the merge
-request, and those reviewing the changes will likely provide you with an answer.
-
-When using a feature flag for UI elements, make sure to _also_ use a feature
-flag for the underlying backend code, if there is any. This ensures there is
-absolutely no way to use the feature until it is enabled.
### Including a feature behind feature flag in the final release
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index b4a3cbd028c..e1fd5828875 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -661,9 +661,10 @@ and included in `rules` definitions via [YAML anchors](../ci/yaml/README.md#anch
#### `if:` conditions
<!-- vale gitlab.Substitutions = NO -->
+
| `if:` conditions | Description | Notes |
|------------------|-------------|-------|
-| `if-not-canonical-namespace` | Matches if the project isn't in the canonical (`gitlab-org/`) or security (`gitlab-org/security`) namespace. | Use to create a job for forks (by using `when: on_success\|manual`), or **not** create a job for forks (by using `when: never`). |
+| `if-not-canonical-namespace` | Matches if the project isn't in the canonical (`gitlab-org/`) or security (`gitlab-org/security`) namespace. | Use to create a job for forks (by using `when: on_success|manual`), or **not** create a job for forks (by using `when: never`). |
| `if-not-ee` | Matches if the project isn't EE (i.e. project name isn't `gitlab` or `gitlab-ee`). | Use to create a job only in the FOSS project (by using `when: on_success|manual`), or **not** create a job if the project is EE (by using `when: never`). |
| `if-not-foss` | Matches if the project isn't FOSS (i.e. project name isn't `gitlab-foss`, `gitlab-ce`, or `gitlabhq`). | Use to create a job only in the EE project (by using `when: on_success|manual`), or **not** create a job if the project is FOSS (by using `when: never`). |
| `if-default-refs` | Matches if the pipeline is for `master`, `/^[\d-]+-stable(-ee)?$/` (stable branches), `/^\d+-\d+-auto-deploy-\d+$/` (auto-deploy branches), `/^security\//` (security branches), merge requests, and tags. | Note that jobs aren't created for branches with this default configuration. |
@@ -691,6 +692,7 @@ and included in `rules` definitions via [YAML anchors](../ci/yaml/README.md#anch
| `if-rspec-fail-fast-disabled` | Limits jobs to pipelines with `$RSPEC_FAIL_FAST_ENABLED` variable not set to `"true"`. | |
| `if-rspec-fail-fast-skipped` | Matches if the pipeline is for a merge request and the MR title includes "SKIP RSPEC FAIL-FAST". | |
| `if-security-pipeline-merge-result` | Matches if the pipeline is for a security merge request triggered by `@gitlab-release-tools-bot`. | |
+
<!-- vale gitlab.Substitutions = YES -->
#### `changes:` patterns
diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md
index 210ee8a7248..0d4f86f1284 100644
--- a/doc/topics/autodevops/customize.md
+++ b/doc/topics/autodevops/customize.md
@@ -385,48 +385,48 @@ The following table lists variables used to disable jobs.
| **Job Name** | **Variable** | **GitLab version** | **Description** |
|----------------------------------------|---------------------------------|-----------------------|-----------------|
-| `.fuzz_base` | `COVFUZZ_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34984) | [Read more](../../user/application_security/coverage_fuzzing/) about how `.fuzz_base` provide capability for your own jobs. If the variable is present, your jobs won't be created. |
-| `apifuzzer_fuzz` | `API_FUZZING_DISABLED` | [From GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39135) | If the variable is present, the job won't be created. |
-| `bandit-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `brakeman-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `bundler-audit-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job won't be created. |
+| `.fuzz_base` | `COVFUZZ_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34984) | [Read more](../../user/application_security/coverage_fuzzing/) about how `.fuzz_base` provide capability for your own jobs. If the variable is present, your jobs aren't created. |
+| `apifuzzer_fuzz` | `API_FUZZING_DISABLED` | [From GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39135) | If the variable is present, the job isn't created. |
+| `bandit-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `brakeman-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `bundler-audit-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
| `canary` | `CANARY_ENABLED` | | This manual job is created if the variable is present. |
| `code_intelligence` | `CODE_INTELLIGENCE_DISABLED` | From GitLab 13.6 | If the variable is present, the job isn't created. |
-| `codequality` | `CODE_QUALITY_DISABLED` | Until GitLab 11.0 | If the variable is present, the job won't be created. |
-| `code_quality` | `CODE_QUALITY_DISABLED` | [From GitLab 11.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5773) | If the variable is present, the job won't be created. |
-| `container_scanning` | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0 | If the variable is present, the job won't be created. |
-| `dast` | `DAST_DISABLED` | From GitLab 11.0 | If the variable is present, the job won't be created. |
-| `dast_environment_deploy` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job won't be created. |
-| `dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0 | If the variable is present, the job won't be created. |
-| `eslint-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `flawfinder-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `gemnasium-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job won't be created. |
-| `gemnasium-maven-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job won't be created. |
-| `gemnasium-python-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job won't be created. |
-| `gosec-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `kubesec-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `license_management` | `LICENSE_MANAGEMENT_DISABLED` | GitLab 11.0 to 12.7 | If the variable is present, the job won't be created. Job deprecated [from GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) |
-| `license_scanning` | `LICENSE_MANAGEMENT_DISABLED` | [From GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job won't be created. |
-| `load_performance` | `LOAD_PERFORMANCE_DISABLED` | From GitLab 13.2 | If the variable is present, the job won't be created. |
-| `nodejs-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `performance` | `PERFORMANCE_DISABLED` | From GitLab 11.0 | Browser performance. If the variable is present, the job won't be created. |
-| `phpcs-security-audit-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `pmd-apex-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `retire-js-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job won't be created. |
-| `review` | `REVIEW_DISABLED` | From GitLab 11.0 | If the variable is present, the job won't be created. |
-| `review:stop` | `REVIEW_DISABLED` | From GitLab 11.0 | Manual job. If the variable is present, the job won't be created. |
-| `sast` | `SAST_DISABLED` | From GitLab 11.0 | If the variable is present, the job won't be created. |
-| `sast:container` | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0 | If the variable is present, the job won't be created. |
-| `secret_detection` | `SECRET_DETECTION_DISABLED` | From GitLab 13.1 | If the variable is present, the job won't be created. |
-| `secret_detection_default_branch` | `SECRET_DETECTION_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job won't be created. |
-| `security-code-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `secrets-sast` | `SAST_DISABLED` | From GitLab 11.0 | If the variable is present, the job won't be created. |
-| `sobelaw-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `stop_dast_environment` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job won't be created. |
-| `spotbugs-sast` | `SAST_DISABLED` | | If the variable is present, the job won't be created. |
-| `test` | `TEST_DISABLED` | From GitLab 11.0 | If the variable is present, the job won't be created. |
+| `codequality` | `CODE_QUALITY_DISABLED` | Until GitLab 11.0 | If the variable is present, the job isn't created. |
+| `code_quality` | `CODE_QUALITY_DISABLED` | [From GitLab 11.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5773) | If the variable is present, the job isn't created. |
+| `container_scanning` | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `dast` | `DAST_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `dast_environment_deploy` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job isn't created. |
+| `dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `eslint-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `flawfinder-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `gemnasium-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `gemnasium-maven-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `gemnasium-python-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `gosec-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `kubesec-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `license_management` | `LICENSE_MANAGEMENT_DISABLED` | GitLab 11.0 to 12.7 | If the variable is present, the job isn't created. Job deprecated [from GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) |
+| `license_scanning` | `LICENSE_MANAGEMENT_DISABLED` | [From GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job isn't created. |
+| `load_performance` | `LOAD_PERFORMANCE_DISABLED` | From GitLab 13.2 | If the variable is present, the job isn't created. |
+| `nodejs-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `performance` | `PERFORMANCE_DISABLED` | From GitLab 11.0 | Browser performance. If the variable is present, the job isn't created. |
+| `phpcs-security-audit-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `pmd-apex-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `retire-js-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `review` | `REVIEW_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `review:stop` | `REVIEW_DISABLED` | From GitLab 11.0 | Manual job. If the variable is present, the job isn't created. |
+| `sast` | `SAST_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `sast:container` | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `secret_detection` | `SECRET_DETECTION_DISABLED` | From GitLab 13.1 | If the variable is present, the job isn't created. |
+| `secret_detection_default_branch` | `SECRET_DETECTION_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job isn't created. |
+| `security-code-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `secrets-sast` | `SAST_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `sobelaw-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `stop_dast_environment` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job isn't created. |
+| `spotbugs-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `test` | `TEST_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
| `staging` | `STAGING_ENABLED` | | The job is created if the variable is present. |
-| `stop_review` | `REVIEW_DISABLED` | | If the variable is present, the job won't be created. |
+| `stop_review` | `REVIEW_DISABLED` | | If the variable is present, the job isn't created. |
### Application secret variables
diff --git a/doc/user/application_security/api_fuzzing/index.md b/doc/user/application_security/api_fuzzing/index.md
index 0607d759d28..bdc58f4ae86 100644
--- a/doc/user/application_security/api_fuzzing/index.md
+++ b/doc/user/application_security/api_fuzzing/index.md
@@ -720,45 +720,43 @@ Repeat this configuration for each profile as needed.
## Running your first scan
-When configured correctly, a CI/CD pipeline contains a `Fuzz` stage and a `apifuzzer_fuzz` job. The
-job only fails when an invalid configuration is provided. During normal operation, the job always
-succeeds even if faults are identified during fuzz testing.
+When configured correctly, a CI/CD pipeline contains a `fuzz` stage and an `apifuzzer_fuzz` or
+`apifuzzer_fuzz_dnd` job. The job only fails when an invalid configuration is provided. During
+normal operation, the job always succeeds even if faults are identified during fuzz testing.
-Faults are displayed on the **Tests** pipeline tab with the suite name **API-Fuzzing**. The **Name**
-field on the **Tests** page includes the fuzz-tested operation and parameter. The **Trace** field
-contains a writeup of the identified fault. This writeup contains information on what the fuzzer
-tested and how it detected something wrong.
+Faults are displayed on the **Security** pipeline tab with the suite name. When testing against the
+repositories default branch, the fuzzing faults are also shown on the Security & Compliance's
+Vulnerability Report page.
To prevent an excessive number of reported faults, the API fuzzing scanner limits the number of
-faults it reports to one per parameter.
-
-### Fault Writeup
-
-The faults that API fuzzing finds aren't associated with a specific vulnerability type. They require
-investigation to determine what type of issue they are and if they should be fixed. See
-[handling false positives](#handling-false-positives) for information about configuration changes
-you can make to limit the number of false positives reported.
-
-This table contains a description of fields in an API fuzzing fault writeup.
-
-| Writeup Item | Description |
-|:-------------|:------------|
-| Operation | The operation tested. |
-| Parameter | The field modified. This can be a path segment, header, query string, or body element. |
-| Endpoint | The endpoint being tested. |
-| Check | Check module producing the test. Checks can be turned on and off. |
-| Assert | Assert module that detected a failure. Assertions can be configured and turned on and off. |
-| CWE | Fuzzing faults always have the same CWE. |
-| OWASP | Fuzzing faults always have the same OWASP ID. |
-| Exploitability | Fuzzing faults always have an `unknown` exploitability. |
-| Impact | Fuzzing faults always have an `unknown` risk impact. |
-| Description | Verbose description of what the check did. Includes the original parameter value and the modified (mutated) value. |
-| Detection | Why a failure was detected and reported. This is related to the Assert that was used. |
-| Original Request | The original, unmodified HTTP request. Useful when reviewing the actual request to see what changes were made. |
-| Actual Request | The request that produced the failure. This request has been modified in some way by the Check logic. |
-| Actual Response | The response to the actual request. |
-| Recorded Request | An unmodified request. |
-| Recorded Response | The response to the unmodified request. You can compare this with the actual request when triaging this fault. |
+faults it reports.
+
+## Viewing fuzzing faults
+
+The API Fuzzing analyzer produces a JSON report that is collected and used
+[to populate the faults into GitLab's vulnerability screens](../index.md#view-details-of-an-api-fuzzing-vulnerability).
+Fuzzing faults show up as vulnerabilities with a severity of Unknown.
+
+The faults that API fuzzing finds require manual investigation and aren't associated with a specific
+vulnerability type. They require investigation to determine if they are a security issue, and if
+they should be fixed. See [handling false positives](#handling-false-positives)
+for information about configuration changes you can make to limit the number of false positives
+reported.
+
+For additional information, see
+[View details of an API Fuzzing vulnerability](../index.md#view-details-of-an-api-fuzzing-vulnerability).
+
+### Security Dashboard
+
+Fuzzing faults show up as vulnerabilities with a severity of Unknown. The Security Dashboard is a
+good place to get an overview of all the security vulnerabilities in your groups, projects and
+pipelines. For more information, see the [Security Dashboard documentation](../security_dashboard/index.md).
+
+### Interacting with the vulnerabilities
+
+Fuzzing faults show up as vulnerabilities with a severity of Unknown.
+Once a fault is found, you can interact with it. Read more on how to
+[interact with the vulnerabilities](../index.md#interacting-with-the-vulnerabilities).
## Handling False Positives
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 862b83e1775..ba614701e61 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -201,6 +201,43 @@ authorization credentials. By default, content of specific headers are masked in
reports. You can specify the list of all headers to be masked. For details, see
[Hide sensitive information](dast/index.md#hide-sensitive-information).
+### View details of an API Fuzzing vulnerability
+
+> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.7.
+
+Faults detected by API Fuzzing occur in the live web application, and require manual investigation
+to determine if they are vulnerabilities. Fuzzing faults are included as vulnerabilities with a
+severity of Unknown. To facilitate investigation of the fuzzing faults, detailed information is
+provided about the HTTP messages sent and received along with a description of the modification(s)
+made.
+
+Follow these steps to view details of a fuzzing fault:
+
+1. You can view faults in a project, or a merge request:
+
+ - In a project, go to the project's **{shield}** **Security & Compliance > Vulnerability Report**
+ page. This page shows all vulnerabilities from the default branch only.
+ - In a merge request, go the merge request's **Security** section and click the **Expand**
+ button. API Fuzzing faults are available in a section labeled
+ **API Fuzzing detected N potential vulnerabilities**. Click the title to display the fault
+ details.
+
+1. Click the fault's title to display the fault's details. The table below describes these details.
+
+| Field | Description |
+|:-----------------|:------------------------------------------------------------------ |
+| Description | Description of the fault including what was modified. |
+| Project | Namespace and project in which the vulnerability was detected. |
+| Method | HTTP method used to detect the vulnerability. |
+| URL | URL at which the vulnerability was detected. |
+| Request | The HTTP request that caused the fault. |
+| Unmodified Response | Response from an unmodified request. This is what a normal working response looks like. |
+| Actual Response | Response received from fuzzed request. |
+| Evidence | How we determined a fault occurred. |
+| Identifiers | The fuzzing check used to find this fault. |
+| Severity | Severity of the finding is always Unknown. |
+| Scanner Type | Scanner used to perform testing. |
+
### Dismissing a vulnerability
To dismiss a vulnerability, you must set its status to Dismissed. This dismisses the vulnerability
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 9a9bb6038f4..7aafa52799d 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -509,50 +509,38 @@ NOTE:
See [Rate limits](../../security/rate_limits.md) for administrator
documentation.
-IP blocks usually happen when GitLab.com receives unusual traffic from a single
-IP address that the system views as potentially malicious based on rate limit
-settings. After the unusual traffic ceases, the IP address is automatically
-released depending on the type of block, as described below.
-
-If you receive a `403 Forbidden` error for all requests to GitLab.com, please
-check for any automated processes that may be triggering a block. For
-assistance, contact [GitLab Support](https://support.gitlab.com/hc/en-us)
-with details, such as the affected IP address.
-
-### HAProxy API throttle
-
-GitLab.com responds with HTTP status code `429` to API requests that exceed 10
-requests
-per second per IP address.
-
-The following example headers are included for all API requests:
+When a request is rate limited, GitLab responds with a `429` status
+code. The client should wait before attempting the request again. There
+are also informational headers with this response detailed in [rate
+limiting responses](#rate-limiting-responses).
-```plaintext
-RateLimit-Limit: 600
-RateLimit-Observed: 6
-RateLimit-Remaining: 594
-RateLimit-Reset: 1563325137
-RateLimit-ResetTime: Wed, 17 Jul 2019 00:58:57 GMT
-```
+The following table describes the rate limits for GitLab.com, both before and
+after the limits change in January, 2021:
-Source:
+| Rate limit | Before 2021-01-18 | From 2021-01-18 |
+|:--------------------------------------------------------------------------|:----------------------------|:------------------------------|
+| **Protected paths** (for a given **IP address**) | **10** requests per minute | **10** requests per minute |
+| **Raw endpoint** traffic (for a given **project, commit, and file path**) | **300** requests per minute | **300** requests per minute |
+| **Unauthenticated** traffic (from a given **IP address**) | No specific limit | **500** requests per minute |
+| **Authenticated** API traffic (for a given **user**) | No specific limit | **2,000** requests per minute |
+| **Authenticated** non-API HTTP traffic (for a given **user**) | No specific limit | **1,000** requests per minute |
+| **All** traffic (from a given **IP address**) | **600** requests per minute | **2,000** requests per minute |
-- Search for `rate_limit_http_rate_per_minute` and `rate_limit_sessions_per_second` in [GitLab.com's current HAProxy settings](https://gitlab.com/gitlab-cookbooks/gitlab-haproxy/blob/master/attributes/default.rb).
+More details are available on the rate limits for [protected
+paths](#protected-paths-throttle) and [raw
+endpoints](../../user/admin_area/settings/rate_limits_on_raw_endpoints.md).
-### Pagination response headers
+### Rate limiting responses
-For performance reasons, if a query returns more than 10,000 records, GitLab
-doesn't return the following headers:
+The [`Retry-After`
+header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After)
+indicates when the client should retry.
-- `x-total`.
-- `x-total-pages`.
-- `rel="last"` `link`.
+Rate limits applied by HAProxy (instead of Cloudflare or the
+GitLab application) have `RateLimit-Reset` and `RateLimit-ResetTime`
+headers.
-### Rack Attack initializer
-
-Details of rate limits enforced by [Rack Attack](../../security/rack_attack.md).
-
-#### Protected paths throttle
+### Protected paths throttle
GitLab.com responds with HTTP status code `429` to POST requests at protected
paths that exceed 10 requests per **minute** per IP address.
@@ -568,6 +556,18 @@ Retry-After: 60
See [Protected Paths](../admin_area/settings/protected_paths.md) for more details.
+### IP blocks
+
+IP blocks can occur when GitLab.com receives unusual traffic from a single
+IP address that the system views as potentially malicious, based on rate limit
+settings. After the unusual traffic ceases, the IP address is automatically
+released depending on the type of block, as described in a following section.
+
+If you receive a `403 Forbidden` error for all requests to GitLab.com,
+check for any automated processes that may be triggering a block. For
+assistance, contact [GitLab Support](https://support.gitlab.com/hc/en-us)
+with details, such as the affected IP address.
+
#### Git and container registry failed authentication ban
GitLab.com responds with HTTP status code `403` for 1 hour, if 30 failed
@@ -585,13 +585,14 @@ This limit:
No response headers are provided.
-### Admin Area settings
+### Pagination response headers
-GitLab.com:
+For performance reasons, if a query returns more than 10,000 records, GitLab
+doesn't return the following headers:
-- Has [rate limits on raw endpoints](../../user/admin_area/settings/rate_limits_on_raw_endpoints.md)
- set to the default.
-- Does not have the user and IP rate limits settings enabled.
+- `x-total`.
+- `x-total-pages`.
+- `rel="last"` `link`.
### Visibility settings
diff --git a/doc/user/packages/dependency_proxy/index.md b/doc/user/packages/dependency_proxy/index.md
index 4b70769892c..fbede6d13b7 100644
--- a/doc/user/packages/dependency_proxy/index.md
+++ b/doc/user/packages/dependency_proxy/index.md
@@ -57,8 +57,6 @@ You can use GitLab as a source for your Docker images.
Prerequisites:
- Your images must be stored on [Docker Hub](https://hub.docker.com/).
-- Docker Hub must be available. Follow [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/241639)
- for progress on accessing images when Docker Hub is down.
### Authenticate with the Dependency Proxy
@@ -119,6 +117,12 @@ dependency-proxy-pull-master:
- docker pull "$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX"/alpine:latest
```
+`CI_DEPENDENCY_PROXY_SERVER` and `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` include the server port. So if you use `CI_DEPENDENCY_PROXY_SERVER` to log in, for example, you must explicitly include the port in your pull command and vice-versa:
+
+```shell
+docker pull gitlab.example.com:443/my-group/dependency_proxy/containers/alpine:latest
+```
+
You can also use [custom environment variables](../../../ci/variables/README.md#custom-environment-variables) to store and access your personal access token or other valid credentials.
##### Authenticate with `DOCKER_AUTH_CONFIG`
@@ -157,11 +161,23 @@ named `DOCKER_AUTH_CONFIG` with a value of:
}
```
+ To use `$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` when referencing images, you must explicitly include the port in your `DOCKER_AUTH_CONFIG` value:
+
+ ```json
+ {
+ "auths": {
+ "https://gitlab.example.com:443": {
+ "auth": "(Base64 content from above)"
+ }
+ }
+ }
+ ```
+
1. Now reference the Dependency Proxy in your base image:
```yaml
# .gitlab-ci.yml
- image: "$CI_SERVER_HOST":"$CI_SERVER_PORT"/groupname/dependency_proxy/containers/node:latest
+ image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/node:latest
...
```
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e15354baf02..779a1d14ad6 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -10581,6 +10581,9 @@ msgstr ""
msgid "End Time"
msgstr ""
+msgid "Ends at %{endsAt}"
+msgstr ""
+
msgid "Ends at (UTC)"
msgstr ""
@@ -19349,9 +19352,15 @@ msgstr ""
msgid "OnCallSchedules|Create on-call schedules in GitLab"
msgstr ""
+msgid "OnCallSchedules|Delete rotation"
+msgstr ""
+
msgid "OnCallSchedules|Delete schedule"
msgstr ""
+msgid "OnCallSchedules|Edit rotation"
+msgstr ""
+
msgid "OnCallSchedules|Edit schedule"
msgstr ""
@@ -19367,7 +19376,7 @@ msgstr ""
msgid "OnCallSchedules|On-call schedule"
msgstr ""
-msgid "OnCallSchedules|On-call schedule for the %{tzShort}"
+msgid "OnCallSchedules|On-call schedule for the %{timezone}"
msgstr ""
msgid "OnCallSchedules|Rotation length"
@@ -19720,9 +19729,6 @@ msgstr ""
msgid "Owner"
msgstr ""
-msgid "PST"
-msgstr ""
-
msgid "Package Registry"
msgstr ""
@@ -26399,6 +26405,9 @@ msgstr ""
msgid "Starts %{startsIn}"
msgstr ""
+msgid "Starts at %{startsAt}"
+msgstr ""
+
msgid "Starts at (UTC)"
msgstr ""
@@ -27201,6 +27210,12 @@ msgstr ""
msgid "Terraform|An error occurred while loading your Terraform States"
msgstr ""
+msgid "Terraform|Are you sure you want to remove the Terraform State %{name}?"
+msgstr ""
+
+msgid "Terraform|Cancel"
+msgstr ""
+
msgid "Terraform|Details"
msgstr ""
@@ -27234,6 +27249,12 @@ msgstr ""
msgid "Terraform|Pipeline"
msgstr ""
+msgid "Terraform|Remove"
+msgstr ""
+
+msgid "Terraform|Remove state file and versions"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
@@ -27246,12 +27267,18 @@ msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|To remove the State file and its versions, type %{name} to confirm:"
+msgstr ""
+
msgid "Terraform|Unknown User"
msgstr ""
msgid "Terraform|Unlock"
msgstr ""
+msgid "Terraform|You are about to remove the State file %{name}. This will permanently delete all the State versions and history. The infrastructure provisioned previously\twill remain intact, only the state file with all its versions are to be removed. This action is non-revertible."
+msgstr ""
+
msgid "Test"
msgstr ""
diff --git a/spec/controllers/profiles/gpg_keys_controller_spec.rb b/spec/controllers/profiles/gpg_keys_controller_spec.rb
index 1860bb0c93b..93f899df484 100644
--- a/spec/controllers/profiles/gpg_keys_controller_spec.rb
+++ b/spec/controllers/profiles/gpg_keys_controller_spec.rb
@@ -16,121 +16,4 @@ RSpec.describe Profiles::GpgKeysController do
end.to change { GpgKey.count }.by(1)
end
end
-
- describe "#get_keys" do
- describe "non existent user" do
- it "does not generally work" do
- get :get_keys, params: { username: 'not-existent' }
-
- expect(response).not_to be_successful
- end
- end
-
- describe "user with no keys" do
- it "does generally work" do
- get :get_keys, params: { username: user.username }
-
- expect(response).to be_successful
- end
-
- it "renders all keys separated with a new line" do
- get :get_keys, params: { username: user.username }
-
- expect(response.body).to eq("")
- end
-
- it "responds with text/plain content type" do
- get :get_keys, params: { username: user.username }
-
- expect(response.content_type).to eq("text/plain")
- end
- end
-
- describe "user with keys" do
- let!(:gpg_key) { create(:gpg_key, user: user) }
- let!(:another_gpg_key) { create(:another_gpg_key, user: user) }
-
- describe "while signed in" do
- before do
- sign_in(user)
- end
-
- it "does generally work" do
- get :get_keys, params: { username: user.username }
-
- expect(response).to be_successful
- end
-
- it "renders all verified keys separated with a new line" do
- get :get_keys, params: { username: user.username }
-
- expect(response.body).not_to eq('')
- expect(response.body).to eq(user.gpg_keys.select(&:verified?).map(&:key).join("\n"))
-
- expect(response.body).to include(gpg_key.key)
- expect(response.body).to include(another_gpg_key.key)
- end
-
- it "responds with text/plain content type" do
- get :get_keys, params: { username: user.username }
-
- expect(response.content_type).to eq("text/plain")
- end
- end
-
- describe 'when logged out' do
- before do
- sign_out(user)
- end
-
- it "still does generally work" do
- get :get_keys, params: { username: user.username }
-
- expect(response).to be_successful
- end
-
- it "renders all verified keys separated with a new line" do
- get :get_keys, params: { username: user.username }
-
- expect(response.body).not_to eq('')
- expect(response.body).to eq(user.gpg_keys.map(&:key).join("\n"))
-
- expect(response.body).to include(gpg_key.key)
- expect(response.body).to include(another_gpg_key.key)
- end
-
- it "responds with text/plain content type" do
- get :get_keys, params: { username: user.username }
-
- expect(response.content_type).to eq("text/plain")
- end
- end
-
- describe 'when revoked' do
- before do
- sign_in(user)
- another_gpg_key.revoke
- end
-
- it "doesn't render revoked keys" do
- get :get_keys, params: { username: user.username }
-
- expect(response.body).not_to eq('')
-
- expect(response.body).to include(gpg_key.key)
- expect(response.body).not_to include(another_gpg_key.key)
- end
-
- it "doesn't render revoked keys for non-authorized users" do
- sign_out(user)
- get :get_keys, params: { username: user.username }
-
- expect(response.body).not_to eq('')
-
- expect(response.body).to include(gpg_key.key)
- expect(response.body).not_to include(another_gpg_key.key)
- end
- end
- end
- end
end
diff --git a/spec/controllers/projects/alerting/notifications_controller_spec.rb b/spec/controllers/projects/alerting/notifications_controller_spec.rb
index b3d37723ccf..3656cfbcc30 100644
--- a/spec/controllers/projects/alerting/notifications_controller_spec.rb
+++ b/spec/controllers/projects/alerting/notifications_controller_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Projects::Alerting::NotificationsController do
expect(notify_service_class)
.to have_received(:new)
- .with(project, nil, permitted_params)
+ .with(project, permitted_params)
end
end
diff --git a/spec/controllers/projects/prometheus/alerts_controller_spec.rb b/spec/controllers/projects/prometheus/alerts_controller_spec.rb
index cbd599506df..46de8aa4baf 100644
--- a/spec/controllers/projects/prometheus/alerts_controller_spec.rb
+++ b/spec/controllers/projects/prometheus/alerts_controller_spec.rb
@@ -168,7 +168,7 @@ RSpec.describe Projects::Prometheus::AlertsController do
expect(Projects::Prometheus::Alerts::NotifyService)
.to receive(:new)
- .with(project, nil, duck_type(:permitted?))
+ .with(project, duck_type(:permitted?))
.and_return(notify_service)
end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 862395069f0..916befe3f62 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe UsersController do
- let(:user) { create(:user) }
+ # This user should have the same e-mail address associated with the GPG key prepared for tests
+ let(:user) { create(:user, email: GpgHelpers::User1.emails[0]) }
let(:private_user) { create(:user, private_profile: true) }
let(:public_user) { create(:user) }
@@ -326,6 +327,123 @@ RSpec.describe UsersController do
end
end
+ describe "#gpg_keys" do
+ describe "non existent user" do
+ it "does not generally work" do
+ get :gpg_keys, params: { username: 'not-existent' }
+
+ expect(response).not_to be_successful
+ end
+ end
+
+ describe "user with no keys" do
+ it "does generally work" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response).to be_successful
+ end
+
+ it "renders all keys separated with a new line" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).to eq("")
+ end
+
+ it "responds with text/plain content type" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.content_type).to eq("text/plain")
+ end
+ end
+
+ describe "user with keys" do
+ let!(:gpg_key) { create(:gpg_key, user: user) }
+ let!(:another_gpg_key) { create(:another_gpg_key, user: user) }
+
+ describe "while signed in" do
+ before do
+ sign_in(user)
+ end
+
+ it "does generally work" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response).to be_successful
+ end
+
+ it "renders all verified keys separated with a new line" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+ expect(response.body).to eq(user.gpg_keys.select(&:verified?).map(&:key).join("\n"))
+
+ expect(response.body).to include(gpg_key.key)
+ expect(response.body).to include(another_gpg_key.key)
+ end
+
+ it "responds with text/plain content type" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.content_type).to eq("text/plain")
+ end
+ end
+
+ describe 'when logged out' do
+ before do
+ sign_out(user)
+ end
+
+ it "still does generally work" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response).to be_successful
+ end
+
+ it "renders all verified keys separated with a new line" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+ expect(response.body).to eq(user.gpg_keys.map(&:key).join("\n"))
+
+ expect(response.body).to include(gpg_key.key)
+ expect(response.body).to include(another_gpg_key.key)
+ end
+
+ it "responds with text/plain content type" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.content_type).to eq("text/plain")
+ end
+ end
+
+ describe 'when revoked' do
+ before do
+ sign_in(user)
+ another_gpg_key.revoke
+ end
+
+ it "doesn't render revoked keys" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+
+ expect(response.body).to include(gpg_key.key)
+ expect(response.body).not_to include(another_gpg_key.key)
+ end
+
+ it "doesn't render revoked keys for non-authorized users" do
+ sign_out(user)
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+
+ expect(response.body).to include(gpg_key.key)
+ expect(response.body).not_to include(another_gpg_key.key)
+ end
+ end
+ end
+ end
+
describe 'GET #calendar' do
context 'for user' do
let(:project) { create(:project) }
diff --git a/spec/features/projects/terraform_spec.rb b/spec/features/projects/terraform_spec.rb
index 0ad2f9a882d..dfa4dad8490 100644
--- a/spec/features/projects/terraform_spec.rb
+++ b/spec/features/projects/terraform_spec.rb
@@ -54,6 +54,24 @@ RSpec.describe 'Terraform', :js do
expect(page).to have_content(terraform_state.name)
end
end
+
+ context 'when clicking on the delete button' do
+ let(:additional_state) { create(:terraform_state, project: project) }
+
+ it 'removes the state', :aggregate_failures do
+ visit project_terraform_index_path(project)
+
+ expect(page).to have_content(additional_state.name)
+
+ find("[data-testid='terraform-state-actions-#{additional_state.name}']").click
+ find('[data-testid="terraform-state-remove"]').click
+ fill_in "terraform-state-remove-input-#{additional_state.name}", with: additional_state.name
+ click_button 'Remove'
+
+ expect(page).not_to have_content(additional_state.name)
+ expect { additional_state.reload }.to raise_error ActiveRecord::RecordNotFound
+ end
+ end
end
end
diff --git a/spec/frontend/helpers/stub_component.js b/spec/frontend/helpers/stub_component.js
new file mode 100644
index 00000000000..45550450517
--- /dev/null
+++ b/spec/frontend/helpers/stub_component.js
@@ -0,0 +1,12 @@
+export function stubComponent(Component, options = {}) {
+ return {
+ props: Component.props,
+ model: Component.model,
+ // Do not render any slots/scoped slots except default
+ // This differs from VTU behavior which renders all slots
+ template: '<div><slot></slot></div>',
+ // allows wrapper.find(Component) to work for stub
+ $_vueTestUtils_original: Component,
+ ...options,
+ };
+}
diff --git a/spec/frontend/invite_members/components/members_token_select_spec.js b/spec/frontend/invite_members/components/members_token_select_spec.js
index fb0bd6bb195..106a2df783d 100644
--- a/spec/frontend/invite_members/components/members_token_select_spec.js
+++ b/spec/frontend/invite_members/components/members_token_select_spec.js
@@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { GlTokenSelector } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises';
+import { stubComponent } from 'helpers/stub_component';
import Api from '~/api';
import MembersTokenSelect from '~/invite_members/components/members_token_select.vue';
@@ -17,6 +18,9 @@ const createComponent = () => {
ariaLabelledby: label,
placeholder,
},
+ stubs: {
+ GlTokenSelector: stubComponent(GlTokenSelector),
+ },
});
};
diff --git a/spec/frontend/monitoring/components/group_empty_state_spec.js b/spec/frontend/monitoring/components/group_empty_state_spec.js
index 3b94c4c6806..4a550efe23c 100644
--- a/spec/frontend/monitoring/components/group_empty_state_spec.js
+++ b/spec/frontend/monitoring/components/group_empty_state_spec.js
@@ -1,13 +1,9 @@
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import { stubComponent } from 'helpers/stub_component';
import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import { metricStates } from '~/monitoring/constants';
-const MockGlEmptyState = {
- props: GlEmptyState.props,
- template: '<div><slot name="description"></slot></div>',
-};
-
function createComponent(props) {
return shallowMount(GroupEmptyState, {
propsData: {
@@ -17,7 +13,9 @@ function createComponent(props) {
svgPath: '/path/to/empty-group-illustration.svg',
},
stubs: {
- GlEmptyState: MockGlEmptyState,
+ GlEmptyState: stubComponent(GlEmptyState, {
+ template: '<div><slot name="description"></slot></div>',
+ }),
},
});
}
@@ -47,7 +45,7 @@ describe('GroupEmptyState', () => {
});
it('passes the expected props to GlEmptyState', () => {
- expect(wrapper.find(MockGlEmptyState).props()).toMatchSnapshot();
+ expect(wrapper.find(GlEmptyState).props()).toMatchSnapshot();
});
});
});
diff --git a/spec/frontend/packages/details/components/package_history_spec.js b/spec/frontend/packages/details/components/package_history_spec.js
index 311aa9e7e68..c43ac9b9c40 100644
--- a/spec/frontend/packages/details/components/package_history_spec.js
+++ b/spec/frontend/packages/details/components/package_history_spec.js
@@ -1,5 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import { GlLink, GlSprintf } from '@gitlab/ui';
+import { stubComponent } from 'helpers/stub_component';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
import { HISTORY_PIPELINES_LIMIT } from '~/packages/details/constants';
@@ -21,10 +22,9 @@ describe('Package History', () => {
wrapper = shallowMount(component, {
propsData: { ...defaultProps, ...props },
stubs: {
- HistoryItem: {
- props: HistoryItem.props,
+ HistoryItem: stubComponent(HistoryItem, {
template: '<div data-testid="history-element"><slot></slot></div>',
- },
+ }),
GlSprintf,
},
});
diff --git a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
index b53955ab743..1db736ba01e 100644
--- a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
@@ -1,18 +1,12 @@
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import { stubComponent } from 'helpers/stub_component';
import PipelineStatusToken from '~/pipelines/components/pipelines_list/tokens/pipeline_status_token.vue';
describe('Pipeline Status Token', () => {
let wrapper;
- const stubs = {
- GlFilteredSearchToken: {
- props: GlFilteredSearchToken.props,
- template: `<div><slot name="suggestions"></slot></div>`,
- },
- };
-
- const findFilteredSearchToken = () => wrapper.find(stubs.GlFilteredSearchToken);
+ const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findAllGlIcons = () => wrapper.findAll(GlIcon);
@@ -33,7 +27,11 @@ describe('Pipeline Status Token', () => {
propsData: {
...defaultProps,
},
- stubs,
+ stubs: {
+ GlFilteredSearchToken: stubComponent(GlFilteredSearchToken, {
+ template: `<div><slot name="suggestions"></slot></div>`,
+ }),
+ },
});
};
diff --git a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
index 9363944a719..375325c0c6a 100644
--- a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
@@ -1,4 +1,5 @@
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
+import { stubComponent } from 'helpers/stub_component';
import { shallowMount } from '@vue/test-utils';
import Api from '~/api';
import PipelineTriggerAuthorToken from '~/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue';
@@ -7,14 +8,7 @@ import { users } from '../mock_data';
describe('Pipeline Trigger Author Token', () => {
let wrapper;
- const stubs = {
- GlFilteredSearchToken: {
- props: GlFilteredSearchToken.props,
- template: `<div><slot name="suggestions"></slot></div>`,
- },
- };
-
- const findFilteredSearchToken = () => wrapper.find(stubs.GlFilteredSearchToken);
+ const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
@@ -42,7 +36,11 @@ describe('Pipeline Trigger Author Token', () => {
...data,
};
},
- stubs,
+ stubs: {
+ GlFilteredSearchToken: stubComponent(GlFilteredSearchToken, {
+ template: `<div><slot name="suggestions"></slot></div>`,
+ }),
+ },
});
};
diff --git a/spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap b/spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap
index aeb49f88770..5f191ef5561 100644
--- a/spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap
+++ b/spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap
@@ -2,9 +2,7 @@
exports[`TagsLoader component has the correct markup 1`] = `
<div>
- <div
- preserve-aspect-ratio="xMinYMax meet"
- >
+ <div>
<rect
height="15"
rx="4"
diff --git a/spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap b/spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap
index a8412e2bde9..56579847468 100644
--- a/spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap
+++ b/spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap
@@ -1,10 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Registry Group Empty state to match the default snapshot 1`] = `
-<div
- svg-path="foo"
- title="There are no container images available in this group"
->
+<div>
<p>
With the Container Registry, every project can have its own space to store its Docker images. Push at least one Docker image in one of this group's projects in order to show up here.
<gl-link-stub
diff --git a/spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap b/spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap
index e690d32ad00..bab6b25cc15 100644
--- a/spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap
+++ b/spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap
@@ -1,10 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Registry Project Empty state to match the default snapshot 1`] = `
-<div
- svg-path="bazFoo"
- title="There are no container images stored for this project"
->
+<div>
<p>
With the Container Registry, every project can have its own space to store its Docker images.
<gl-link-stub
diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js
index ff7885f158d..7d32a667011 100644
--- a/spec/frontend/registry/explorer/pages/list_spec.js
+++ b/spec/frontend/registry/explorer/pages/list_spec.js
@@ -135,13 +135,13 @@ describe('List Page', () => {
it('empty state should have an svg-path', () => {
mountComponent({ config });
- expect(findEmptyState().attributes('svg-path')).toBe(config.containersErrorImage);
+ expect(findEmptyState().props('svgPath')).toBe(config.containersErrorImage);
});
it('empty state should have a description', () => {
mountComponent({ config });
- expect(findEmptyState().html()).toContain('connection error');
+ expect(findEmptyState().props('title')).toContain('connection error');
});
it('should not show the loading or default state', () => {
diff --git a/spec/frontend/registry/explorer/stubs.js b/spec/frontend/registry/explorer/stubs.js
index b6c0ee67757..d6fba863ee0 100644
--- a/spec/frontend/registry/explorer/stubs.js
+++ b/spec/frontend/registry/explorer/stubs.js
@@ -1,35 +1,33 @@
+import {
+ GlModal as RealGlModal,
+ GlEmptyState as RealGlEmptyState,
+ GlSkeletonLoader as RealGlSkeletonLoader,
+} from '@gitlab/ui';
+import { RouterLinkStub } from '@vue/test-utils';
+import { stubComponent } from 'helpers/stub_component';
import RealDeleteModal from '~/registry/explorer/components/details_page/delete_modal.vue';
import RealListItem from '~/vue_shared/components/registry/list_item.vue';
-export const GlModal = {
+export const GlModal = stubComponent(RealGlModal, {
template: '<div><slot name="modal-title"></slot><slot></slot><slot name="modal-ok"></slot></div>',
methods: {
show: jest.fn(),
},
-};
+});
-export const GlEmptyState = {
+export const GlEmptyState = stubComponent(RealGlEmptyState, {
template: '<div><slot name="description"></slot></div>',
- name: 'GlEmptyStateSTub',
-};
+});
-export const RouterLink = {
- template: `<div><slot></slot></div>`,
- props: ['to'],
-};
+export const RouterLink = RouterLinkStub;
-export const DeleteModal = {
- template: '<div></div>',
+export const DeleteModal = stubComponent(RealDeleteModal, {
methods: {
show: jest.fn(),
},
- props: RealDeleteModal.props,
-};
+});
-export const GlSkeletonLoader = {
- template: `<div><slot></slot></div>`,
- props: ['width', 'height'],
-};
+export const GlSkeletonLoader = stubComponent(RealGlSkeletonLoader);
export const ListItem = {
...RealListItem,
diff --git a/spec/frontend/terraform/components/states_table_actions_spec.js b/spec/frontend/terraform/components/states_table_actions_spec.js
index bbbac2dd9d9..264f4b7939a 100644
--- a/spec/frontend/terraform/components/states_table_actions_spec.js
+++ b/spec/frontend/terraform/components/states_table_actions_spec.js
@@ -1,9 +1,10 @@
-import { GlDropdown } from '@gitlab/ui';
+import { GlDropdown, GlModal, GlSprintf } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
import VueApollo from 'vue-apollo';
import StateActions from '~/terraform/components/states_table_actions.vue';
import lockStateMutation from '~/terraform/graphql/mutations/lock_state.mutation.graphql';
+import removeStateMutation from '~/terraform/graphql/mutations/remove_state.mutation.graphql';
import unlockStateMutation from '~/terraform/graphql/mutations/unlock_state.mutation.graphql';
const localVue = createLocalVue();
@@ -11,6 +12,7 @@ localVue.use(VueApollo);
describe('StatesTableActions', () => {
let lockResponse;
+ let removeResponse;
let unlockResponse;
let wrapper;
@@ -26,12 +28,17 @@ describe('StatesTableActions', () => {
const createMockApolloProvider = () => {
lockResponse = jest.fn().mockResolvedValue({ data: { terraformStateLock: { errors: [] } } });
+ removeResponse = jest
+ .fn()
+ .mockResolvedValue({ data: { terraformStateDelete: { errors: [] } } });
+
unlockResponse = jest
.fn()
.mockResolvedValue({ data: { terraformStateUnlock: { errors: [] } } });
return createMockApollo([
[lockStateMutation, lockResponse],
+ [removeStateMutation, removeResponse],
[unlockStateMutation, unlockResponse],
]);
};
@@ -43,7 +50,7 @@ describe('StatesTableActions', () => {
apolloProvider,
localVue,
propsData,
- stubs: { GlDropdown },
+ stubs: { GlDropdown, GlModal, GlSprintf },
});
return wrapper.vm.$nextTick();
@@ -52,6 +59,8 @@ describe('StatesTableActions', () => {
const findLockBtn = () => wrapper.find('[data-testid="terraform-state-lock"]');
const findUnlockBtn = () => wrapper.find('[data-testid="terraform-state-unlock"]');
const findDownloadBtn = () => wrapper.find('[data-testid="terraform-state-download"]');
+ const findRemoveBtn = () => wrapper.find('[data-testid="terraform-state-remove"]');
+ const findRemoveModal = () => wrapper.find(GlModal);
beforeEach(() => {
return createComponent();
@@ -59,6 +68,7 @@ describe('StatesTableActions', () => {
afterEach(() => {
lockResponse = null;
+ removeResponse = null;
unlockResponse = null;
wrapper.destroy();
});
@@ -137,4 +147,43 @@ describe('StatesTableActions', () => {
});
});
});
+
+ describe('remove button', () => {
+ it('displays a remove button', () => {
+ expect(findRemoveBtn().text()).toBe(StateActions.i18n.remove);
+ });
+
+ describe('when clicking the remove button', () => {
+ beforeEach(() => {
+ findRemoveBtn().vm.$emit('click');
+ return wrapper.vm.$nextTick();
+ });
+
+ it('displays a remove modal', () => {
+ expect(findRemoveModal().text()).toContain(
+ `You are about to remove the State file ${defaultProps.state.name}`,
+ );
+ });
+
+ describe('when submitting the remove modal', () => {
+ it('does not call the remove mutation when state name is missing', async () => {
+ findRemoveModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ expect(removeResponse).not.toHaveBeenCalledWith();
+ });
+
+ it('calls the remove mutation when state name is present', async () => {
+ await wrapper.setData({ removeConfirmText: defaultProps.state.name });
+
+ findRemoveModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ expect(removeResponse).toHaveBeenCalledWith({
+ stateID: defaultProps.state.id,
+ });
+ });
+ });
+ });
+ });
});
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index 68387bbc82e..51e7b4029d5 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do
total_time_spent reference author merged_at commit_count current_user_todos
conflicts auto_merge_enabled approved_by source_branch_protected
default_merge_commit_message_with_description squash_on_merge available_auto_merge_strategies
- has_ci mergeable commits_without_merge_commits
+ has_ci mergeable commits_without_merge_commits security_auto_fix
]
if Gitlab.ee?
diff --git a/spec/mailers/emails/releases_spec.rb b/spec/mailers/emails/releases_spec.rb
index 60e522c7cfa..6ee87724c83 100644
--- a/spec/mailers/emails/releases_spec.rb
+++ b/spec/mailers/emails/releases_spec.rb
@@ -49,5 +49,14 @@ RSpec.describe Emails::Releases do
is_expected.to have_body_text('Release notes:')
is_expected.to have_body_text(release.description)
end
+
+ context 'release notes with attachment' do
+ let(:upload_path) { '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg' }
+ let(:release) { create(:release, project: project, description: "Attachment: [Test file](#{upload_path})") }
+
+ it 'renders absolute links' do
+ is_expected.to have_body_text(%Q(<a href="#{project.web_url}#{upload_path}" data-link="true" class="gfm">Test file</a>))
+ end
+ end
end
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index cbfb9f080ce..26ad1f14786 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
# user GET /:username
# user_ssh_keys GET /:username.keys
+# user_gpg_keys GET /:username.gpg
# user_groups GET /users/:username/groups(.:format)
# user_projects GET /users/:username/projects(.:format)
# user_contributed_projects GET /users/:username/contributed(.:format)
@@ -17,6 +18,12 @@ RSpec.describe UsersController, "routing" do
expect(get("/User")).to route_to('users#show', username: 'User')
end
+ it "to #gpg_keys" do
+ allow_any_instance_of(::Constraints::UserUrlConstrainer).to receive(:matches?).and_return(true)
+
+ expect(get("/User.gpg")).to route_to('users#gpg_keys', username: 'User')
+ end
+
it "to #groups" do
expect(get("/users/User/groups")).to route_to('users#groups', username: 'User')
end
@@ -197,12 +204,6 @@ RSpec.describe Profiles::GpgKeysController, "routing" do
it "to #destroy" do
expect(delete("/profile/gpg_keys/1")).to route_to('profiles/gpg_keys#destroy', id: '1')
end
-
- it "to #get_keys" do
- allow_any_instance_of(::Constraints::UserUrlConstrainer).to receive(:matches?).and_return(true)
-
- expect(get("/foo.gpg")).to route_to('profiles/gpg_keys#get_keys', username: 'foo')
- end
end
# emails GET /emails(.:format) emails#index
diff --git a/spec/services/alert_management/process_prometheus_alert_service_spec.rb b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
index 2f920de7fc7..8f81c1967d5 100644
--- a/spec/services/alert_management/process_prometheus_alert_service_spec.rb
+++ b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
end
describe '#execute' do
- let(:service) { described_class.new(project, nil, payload) }
+ let(:service) { described_class.new(project, payload) }
let(:auto_close_incident) { true }
let(:create_issue) { true }
let(:send_email) { true }
diff --git a/spec/services/projects/alerting/notify_service_spec.rb b/spec/services/projects/alerting/notify_service_spec.rb
index 3d7d928d744..4b7b7b0b200 100644
--- a/spec/services/projects/alerting/notify_service_spec.rb
+++ b/spec/services/projects/alerting/notify_service_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Projects::Alerting::NotifyService do
let(:token) { 'invalid-token' }
let(:starts_at) { Time.current.change(usec: 0) }
let(:fingerprint) { 'testing' }
- let(:service) { described_class.new(project, nil, payload) }
+ let(:service) { described_class.new(project, payload) }
let_it_be(:environment) { create(:environment, project: project) }
let(:environment) { create(:environment, project: project) }
let(:ended_at) { nil }
diff --git a/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
index c8f7359ac38..8ae47ec266c 100644
--- a/spec/services/projects/prometheus/alerts/notify_service_spec.rb
+++ b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
let_it_be(:project, reload: true) { create(:project) }
- let(:service) { described_class.new(project, nil, payload) }
+ let(:service) { described_class.new(project, payload) }
let(:token_input) { 'token' }
let!(:setting) do
@@ -218,7 +218,7 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
it 'processes Prometheus alerts' do
expect(AlertManagement::ProcessPrometheusAlertService)
.to receive(:new)
- .with(project, nil, kind_of(Hash))
+ .with(project, kind_of(Hash))
.exactly(3).times
.and_return(process_service)
expect(process_service).to receive(:execute).exactly(3).times
diff --git a/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb
index e07d3e2dec9..5b3d30df739 100644
--- a/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb
@@ -125,6 +125,9 @@ RSpec.shared_examples 'write access for a read-only GitLab instance' do
where(:description, :path) do
'LFS request to batch' | '/root/rouge.git/info/lfs/objects/batch'
'request to git-upload-pack' | '/root/rouge.git/git-upload-pack'
+ 'user sign out' | '/users/sign_out'
+ 'admin session' | '/admin/session'
+ 'admin session destroy' | '/admin/session/destroy'
end
with_them do