summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-13 09:09:23 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-13 09:09:23 +0000
commit4cb5e5011abfe8d50ac3a7ebd0018c563c6d7af4 (patch)
tree82591df15758864325897043f855b4e4dfcb6a56
parent0301a0cad0063d76b1607358dc6c711ea043fdda (diff)
downloadgitlab-ce-4cb5e5011abfe8d50ac3a7ebd0018c563c6d7af4.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/images/cluster_app_logos/modsecurity.pngbin0 -> 6235 bytes
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js6
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue3
-rw-r--r--app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue122
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js4
-rw-r--r--app/assets/javascripts/notes/components/discussion_counter.vue3
-rw-r--r--app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue3
-rw-r--r--app/assets/javascripts/pages/admin/integrations/edit/index.js16
-rw-r--r--app/assets/javascripts/pages/projects/snippets/show/index.js1
-rw-r--r--app/assets/javascripts/pages/snippets/show/index.js1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue2
-rw-r--r--app/assets/stylesheets/pages/profile.scss8
-rw-r--r--app/controllers/admin/integrations_controller.rb67
-rw-r--r--app/controllers/profiles/keys_controller.rb2
-rw-r--r--app/controllers/projects/services_controller.rb30
-rw-r--r--app/models/clusters/applications/ingress.rb2
-rw-r--r--app/models/key.rb1
-rw-r--r--app/views/admin/integrations/_form.html.haml12
-rw-r--r--app/views/admin/integrations/edit.html.haml5
-rw-r--r--app/views/admin/services/_form.html.haml2
-rw-r--r--app/views/profiles/keys/_form.html.haml13
-rw-r--r--app/views/profiles/keys/_key.html.haml41
-rw-r--r--app/views/profiles/keys/_key_details.html.haml5
-rw-r--r--app/views/profiles/keys/_key_table.html.haml2
-rw-r--r--app/views/projects/find_file/show.html.haml3
-rw-r--r--app/views/projects/services/_form.html.haml2
-rw-r--r--app/views/shared/_service_settings.html.haml2
-rw-r--r--changelogs/unreleased/208886-optimize-deployment-counters-related-to-the-deployment.yml5
-rw-r--r--changelogs/unreleased/208914-crud-for-instance-level-integrations.yml5
-rw-r--r--changelogs/unreleased/27227-widget-showing-changed-pages-for-visual-reviews.yml5
-rw-r--r--changelogs/unreleased/36243-introduce-an-optional-expiration-date-for-ssh-keys.yml5
-rw-r--r--changelogs/unreleased/Resolve-Migrate--fa-spinner-app-views-projects-find_file.yml5
-rw-r--r--changelogs/unreleased/feature-199458-track-jump-to-next-unresolved-thread.yml5
-rw-r--r--changelogs/unreleased/replace_checkbox_by_toggle_for_modsecurity.yml5
-rw-r--r--config/routes/admin.rb5
-rw-r--r--db/migrate/20200303055348_add_expires_at_to_keys.rb9
-rw-r--r--db/migrate/20200311084025_add_index_on_user_id_status_created_at_to_deployments.rb17
-rw-r--r--db/migrate/20200311094020_add_index_on_id_and_status_to_deployments.rb17
-rw-r--r--db/schema.rb3
-rw-r--r--doc/.linting/vale/styles/gitlab/SentenceSpacing.yml11
-rw-r--r--doc/README.md2
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md6
-rw-r--r--doc/administration/geo/replication/troubleshooting.md6
-rw-r--r--doc/administration/gitaly/index.md4
-rw-r--r--doc/administration/high_availability/README.md53
-rw-r--r--doc/administration/high_availability/monitoring_node.md4
-rw-r--r--doc/administration/lfs/manage_large_binaries_with_git_lfs.md2
-rw-r--r--doc/administration/monitoring/performance/influxdb_configuration.md2
-rw-r--r--doc/administration/troubleshooting/sidekiq.md2
-rw-r--r--doc/api/epic_issues.md14
-rw-r--r--doc/api/epics.md6
-rw-r--r--doc/api/group_level_variables.md2
-rw-r--r--doc/api/keys.md2
-rw-r--r--doc/api/merge_request_context_commits.md2
-rw-r--r--doc/api/merge_requests.md14
-rw-r--r--doc/api/pipeline_schedules.md2
-rw-r--r--doc/api/services.md2
-rw-r--r--doc/api/settings.md2
-rw-r--r--doc/api/users.md12
-rw-r--r--doc/ci/examples/deployment/composer-npm-deploy.md2
-rw-r--r--doc/ci/jenkins/index.md6
-rw-r--r--doc/ci/pipelines/schedules.md2
-rw-r--r--doc/ci/review_apps/index.md3
-rw-r--r--doc/ci/services/postgres.md2
-rw-r--r--doc/ci/services/redis.md2
-rw-r--r--doc/ci/triggers/README.md2
-rw-r--r--doc/ci/yaml/README.md12
-rw-r--r--doc/development/code_review.md2
-rw-r--r--doc/development/contributing/issue_workflow.md2
-rw-r--r--doc/development/database_review.md2
-rw-r--r--doc/development/fe_guide/development_process.md2
-rw-r--r--doc/development/fe_guide/icons.md2
-rw-r--r--doc/development/file_storage.md2
-rw-r--r--doc/development/permissions.md4
-rw-r--r--doc/development/query_recorder.md2
-rw-r--r--doc/development/scalability.md2
-rw-r--r--doc/development/testing_guide/end_to_end/best_practices.md2
-rw-r--r--doc/development/testing_guide/end_to_end/quick_start_guide.md2
-rw-r--r--doc/install/aws/index.md8
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/install/requirements.md2
-rw-r--r--doc/integration/jira_development_panel.md4
-rw-r--r--doc/integration/kerberos.md2
-rw-r--r--doc/ssh/README.md3
-rw-r--r--doc/topics/autodevops/index.md2
-rw-r--r--doc/user/analytics/value_stream_analytics.md10
-rw-r--r--doc/user/application_security/index.md29
-rw-r--r--doc/user/compliance/license_compliance/img/policies_maintainer_add_v12_9.pngbin0 -> 22423 bytes
-rw-r--r--doc/user/compliance/license_compliance/img/policies_maintainer_edit_v12_9.pngbin0 -> 35281 bytes
-rw-r--r--doc/user/compliance/license_compliance/img/policies_v12_9.pngbin0 -> 66698 bytes
-rw-r--r--doc/user/compliance/license_compliance/index.md18
-rw-r--r--doc/user/gitlab_com/index.md11
-rw-r--r--doc/user/project/integrations/prometheus.md2
-rw-r--r--doc/user/project/issues/design_management.md15
-rw-r--r--doc/user/project/issues/img/design_drag_and_drop_uploads_v12_9.pngbin0 -> 402118 bytes
-rw-r--r--doc/user/project/merge_requests/code_quality.md2
-rw-r--r--doc/user/project/releases/img/release_with_milestone_v12_9.pngbin0 -> 27783 bytes
-rw-r--r--doc/user/project/releases/img/releases_v12_9.pngbin0 -> 51974 bytes
-rw-r--r--doc/user/project/releases/index.md9
-rw-r--r--lib/api/entities/ssh_key.rb2
-rw-r--r--lib/gitlab/auth/key_status_checker.rb29
-rw-r--r--lib/gitlab/git_access.rb26
-rw-r--r--locale/gitlab.pot85
-rw-r--r--spec/controllers/admin/integrations_controller_spec.rb86
-rw-r--r--spec/controllers/profiles/keys_controller_spec.rb16
-rw-r--r--spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js52
-rw-r--r--spec/frontend/clusters/services/mock_data.js1
-rw-r--r--spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap3
-rw-r--r--spec/frontend/notes/components/discussion_jump_to_next_button_spec.js23
-rw-r--r--spec/lib/gitlab/auth/key_status_checker_spec.rb68
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb1
-rw-r--r--spec/requests/api/internal/base_spec.rb31
-rw-r--r--spec/requests/api/keys_spec.rb3
115 files changed, 931 insertions, 267 deletions
diff --git a/app/assets/images/cluster_app_logos/modsecurity.png b/app/assets/images/cluster_app_logos/modsecurity.png
new file mode 100644
index 00000000000..fd58275e1d7
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/modsecurity.png
Binary files differ
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index dc1328a2236..e20c87ed8a0 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -256,6 +256,7 @@ export default class Clusters {
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
eventHub.$on('setCrossplaneProviderStack', data => this.setCrossplaneProviderStack(data));
eventHub.$on('setIngressModSecurityEnabled', data => this.setIngressModSecurityEnabled(data));
+ eventHub.$on('resetIngressModSecurityEnabled', id => this.resetIngressModSecurityEnabled(id));
// Add event listener to all the banner close buttons
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
@@ -270,6 +271,7 @@ export default class Clusters {
eventHub.$off('setCrossplaneProviderStack');
eventHub.$off('uninstallApplication');
eventHub.$off('setIngressModSecurityEnabled');
+ eventHub.$off('resetIngressModSecurityEnabled');
}
initPolling(method, successCallback, errorCallback) {
@@ -523,6 +525,10 @@ export default class Clusters {
this.store.updateAppProperty(id, 'modsecurity_enabled', modSecurityEnabled);
}
+ resetIngressModSecurityEnabled(id) {
+ this.store.updateAppProperty(id, 'isEditingModSecurityEnabled', false);
+ }
+
destroy() {
this.destroyed = true;
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 9058f1c0141..442c52110f2 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -119,9 +119,6 @@ export default {
ingressInstalled() {
return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED;
},
- ingressEnableModsecurity() {
- return this.applications.ingress.modsecurity_enabled;
- },
ingressExternalEndpoint() {
return this.applications.ingress.externalIp || this.applications.ingress.externalHostname;
},
diff --git a/app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue b/app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue
index c30015f31de..98a783aab6e 100644
--- a/app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue
+++ b/app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue
@@ -1,19 +1,22 @@
<script>
import _ from 'lodash';
import { __ } from '../../locale';
-import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants';
-import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
+import { GlAlert, GlSprintf, GlLink, GlToggle, GlButton } from '@gitlab/ui';
import eventHub from '~/clusters/event_hub';
+import modSecurityLogo from 'images/cluster_app_logos/modsecurity.png';
-const { UPDATING, UNINSTALLING } = APPLICATION_STATUS;
+const { UPDATING, UNINSTALLING, INSTALLING, INSTALLED, UPDATED } = APPLICATION_STATUS;
export default {
+ title: 'ModSecurity Web Application Firewall',
+ modsecurityUrl: 'https://modsecurity.org/about.html',
components: {
- LoadingButton,
GlAlert,
GlSprintf,
GlLink,
+ GlToggle,
+ GlButton,
},
props: {
ingress: {
@@ -26,6 +29,10 @@ export default {
default: '',
},
},
+ data: () => ({
+ modSecurityLogo,
+ hasValueChanged: false,
+ }),
computed: {
modSecurityEnabled: {
get() {
@@ -36,6 +43,11 @@ export default {
id: INGRESS,
modSecurityEnabled: isEnabled,
});
+ if (this.hasValueChanged) {
+ this.resetStatus();
+ } else {
+ this.hasValueChanged = true;
+ }
},
},
ingressModSecurityDescription() {
@@ -45,13 +57,21 @@ export default {
return [UPDATING].includes(this.ingress.status);
},
saveButtonDisabled() {
- return [UNINSTALLING, UPDATING].includes(this.ingress.status);
+ return [UNINSTALLING, UPDATING, INSTALLING].includes(this.ingress.status);
},
saveButtonLabel() {
return this.saving ? __('Saving') : __('Save changes');
},
- ingressInstalled() {
- return this.ingress.installed;
+ /**
+ * Returns true either when:
+ * - The application is getting updated.
+ * - The user has changed some of the settings for an application which is
+ * neither getting installed nor updated.
+ */
+ showButtons() {
+ return (
+ this.saving || (this.hasValueChanged && [INSTALLED, UPDATED].includes(this.ingress.status))
+ );
},
},
methods: {
@@ -60,6 +80,11 @@ export default {
id: INGRESS,
params: { modsecurity_enabled: this.ingress.modsecurity_enabled },
});
+ this.resetStatus();
+ },
+ resetStatus() {
+ eventHub.$emit('resetIngressModSecurityEnabled', INGRESS);
+ this.hasValueChanged = false;
},
},
};
@@ -75,42 +100,65 @@ export default {
@dismiss="alert = null"
>
{{
- s__('ClusterIntegration|Something went wrong while updating the Web Application Firewall.')
+ s__(
+ 'ClusterIntegration|Something went wrong while trying to save your settings. Please try again.',
+ )
}}
</gl-alert>
- <div class="form-group">
- <div class="form-check form-check-inline">
- <input
- v-model="modSecurityEnabled"
- type="checkbox"
- autocomplete="off"
- class="form-check-input"
+ <div class="gl-responsive-table-row-layout" role="row">
+ <div class="table-section append-right-8 section-align-top" role="gridcell">
+ <img
+ :src="modSecurityLogo"
+ :alt="`${$options.title} logo`"
+ class="cluster-application-logo avatar s40"
/>
- <label class="form-check-label label-bold" for="ingress-enable-modsecurity">
- {{ s__('ClusterIntegration|Enable Web Application Firewall') }}
- </label>
</div>
- <p class="form-text text-muted">
+ <div class="table-section section-wrap" role="gridcell">
<strong>
- <gl-sprintf
- :message="s__('ClusterIntegration|Learn more about %{linkStart}ModSecurity%{linkEnd}')"
- >
- <template #link="{ content }">
- <gl-link :href="ingressModSecurityDescription" target="_blank"
- >{{ content }}
- </gl-link>
- </template>
- </gl-sprintf>
+ <gl-link :href="$options.modsecurityUrl" target="_blank">{{ $options.title }} </gl-link>
</strong>
- </p>
- <loading-button
- v-if="ingressInstalled"
- class="btn-success mt-1"
- :loading="saving"
- :disabled="saveButtonDisabled"
- :label="saveButtonLabel"
- @click="updateApplication"
- />
+ <div class="form-group">
+ <p class="form-text text-muted">
+ <strong>
+ <gl-sprintf
+ :message="
+ s__(
+ 'ClusterIntegration|Real-time web application monitoring, logging and access control. %{linkStart}More information%{linkEnd}',
+ )
+ "
+ >
+ <template #link="{ content }">
+ <gl-link :href="ingressModSecurityDescription" target="_blank"
+ >{{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </strong>
+ </p>
+ <div class="form-check form-check-inline mt-3">
+ <gl-toggle
+ v-model="modSecurityEnabled"
+ :label-on="__('Enabled')"
+ :label-off="__('Disabled')"
+ :disabled="saveButtonDisabled"
+ label-position="right"
+ />
+ </div>
+ <div v-if="showButtons">
+ <gl-button
+ class="btn-success inline mr-1"
+ :loading="saving"
+ :disabled="saveButtonDisabled"
+ @click="updateApplication"
+ >
+ {{ saveButtonLabel }}
+ </gl-button>
+ <gl-button :disabled="saveButtonDisabled" @click="resetStatus">
+ {{ __('Cancel') }}
+ </gl-button>
+ </div>
+ </div>
+ </div>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index ffe71455b2d..1d17170cea1 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -211,9 +211,7 @@ export default class ClusterStore {
this.state.applications.ingress.externalIp = serverAppEntry.external_ip;
this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname;
if (!this.state.applications.ingress.isEditingModSecurityEnabled) {
- this.state.applications.ingress.modsecurity_enabled =
- serverAppEntry.modsecurity_enabled ||
- this.state.applications.ingress.modsecurity_enabled;
+ this.state.applications.ingress.modsecurity_enabled = serverAppEntry.modsecurity_enabled;
}
} else if (appId === CERT_MANAGER) {
this.state.applications.cert_manager.email =
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index 515d513e3ee..577612de06a 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -77,6 +77,9 @@ export default {
v-gl-tooltip
title="Jump to next unresolved thread"
class="btn btn-default discussion-next-btn"
+ data-track-event="click_button"
+ data-track-label="mr_next_unresolved_thread"
+ data-track-property="click_next_unresolved_thread_top"
@click="jumpToNextDiscussion"
>
<icon name="comment-next" />
diff --git a/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue b/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue
index e66abcfddbb..b71ce1b6a0a 100644
--- a/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue
+++ b/app/assets/javascripts/notes/components/discussion_jump_to_next_button.vue
@@ -28,6 +28,9 @@ export default {
v-gl-tooltip
class="btn btn-default discussion-next-btn"
:title="s__('MergeRequests|Jump to next unresolved thread')"
+ data-track-event="click_button"
+ data-track-label="mr_next_unresolved_thread"
+ data-track-property="click_next_unresolved_thread"
@click="jumpToNextRelativeDiscussion(fromDiscussionId)"
>
<icon name="comment-next" />
diff --git a/app/assets/javascripts/pages/admin/integrations/edit/index.js b/app/assets/javascripts/pages/admin/integrations/edit/index.js
new file mode 100644
index 00000000000..2d77f2686f7
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/integrations/edit/index.js
@@ -0,0 +1,16 @@
+import IntegrationSettingsForm from '~/integrations/integration_settings_form';
+import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
+import initAlertsSettings from '~/alerts_service_settings';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const prometheusSettingsWrapper = document.querySelector('.js-prometheus-metrics-monitoring');
+ const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
+ integrationSettingsForm.init();
+
+ if (prometheusSettingsWrapper) {
+ const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
+ prometheusMetrics.loadActiveMetrics();
+ }
+
+ initAlertsSettings(document.querySelector('.js-alerts-service-settings'));
+});
diff --git a/app/assets/javascripts/pages/projects/snippets/show/index.js b/app/assets/javascripts/pages/projects/snippets/show/index.js
index 738bf08f1bf..d8fbb851ffb 100644
--- a/app/assets/javascripts/pages/projects/snippets/show/index.js
+++ b/app/assets/javascripts/pages/projects/snippets/show/index.js
@@ -14,5 +14,6 @@ document.addEventListener('DOMContentLoaded', () => {
snippetEmbed();
} else {
initSnippetsApp();
+ initNotes();
}
});
diff --git a/app/assets/javascripts/pages/snippets/show/index.js b/app/assets/javascripts/pages/snippets/show/index.js
index 6e00c14f43e..3bc9d4f957f 100644
--- a/app/assets/javascripts/pages/snippets/show/index.js
+++ b/app/assets/javascripts/pages/snippets/show/index.js
@@ -14,5 +14,6 @@ document.addEventListener('DOMContentLoaded', () => {
snippetEmbed();
} else {
initSnippetsApp();
+ initNotes();
}
});
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment.vue
index 6f77d2fa779..9c476d5b2e0 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment.vue
@@ -64,7 +64,7 @@ export default {
:deployment="deployment"
:computed-deployment-status="computedDeploymentStatus"
:show-visual-review-app="showVisualReviewApp"
- :visual-review-app-metadata="visualReviewAppMeta"
+ :visual-review-app-meta="visualReviewAppMeta"
/>
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue
index a11e62b048a..573fc388cca 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue
@@ -173,7 +173,7 @@ export default {
:app-button-text="appButtonText"
:deployment="deployment"
:show-visual-review-app="showVisualReviewApp"
- :visual-review-app-metadata="visualReviewAppMeta"
+ :visual-review-app-meta="visualReviewAppMeta"
/>
<deployment-action-button
v-if="stopUrl"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue
index e1a1b1022e3..5dabd9fe5fe 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue
@@ -93,8 +93,10 @@ export default {
/>
<visual-review-app-link
v-if="showVisualReviewApp"
+ :view-app-display="appButtonText"
:link="deploymentExternalUrl"
:app-metadata="visualReviewAppMeta"
+ :changes="deployment.changes"
/>
</span>
</template>
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 08796742f08..43cf0d4bd70 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -118,6 +118,14 @@
}
}
+.ssh-keys-list {
+ .last-used-at,
+ .expires,
+ .key-created-at {
+ line-height: 32px;
+ }
+}
+
.key-created-at {
line-height: 42px;
}
diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb
new file mode 100644
index 00000000000..715aa882bda
--- /dev/null
+++ b/app/controllers/admin/integrations_controller.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+class Admin::IntegrationsController < Admin::ApplicationController
+ include ServiceParams
+
+ before_action :not_found, unless: :instance_level_integrations_enabled?
+ before_action :service, only: [:edit, :update, :test]
+
+ def edit
+ end
+
+ def update
+ @service.attributes = service_params[:service]
+
+ if @service.save(context: :manual_change)
+ redirect_to edit_admin_application_settings_integration_path(@service), notice: success_message
+ else
+ render :edit
+ end
+ end
+
+ def test
+ if @service.can_test?
+ render json: service_test_response, status: :ok
+ else
+ render json: {}, status: :not_found
+ end
+ end
+
+ private
+
+ def instance_level_integrations_enabled?
+ Feature.enabled?(:instance_level_integrations)
+ end
+
+ def project
+ # TODO: Change to something more meaningful
+ Project.first
+ end
+
+ def service
+ @service ||= project.find_or_initialize_service(params[:id])
+ end
+
+ def success_message
+ message = @service.active? ? _('activated') : _('settings saved, but not activated')
+
+ _('%{service_title} %{message}.') % { service_title: @service.title, message: message }
+ end
+
+ def service_test_response
+ unless @service.update(service_params[:service])
+ return { error: true, message: _('Validations failed.'), service_response: @service.errors.full_messages.join(','), test_failed: false }
+ end
+
+ data = @service.test_data(project, current_user)
+ outcome = @service.test(data)
+
+ unless outcome[:success]
+ return { error: true, message: _('Test failed.'), service_response: outcome[:result].to_s, test_failed: true }
+ end
+
+ {}
+ rescue Gitlab::HTTP::BlockedUrlError => e
+ { error: true, message: _('Test failed.'), service_response: e.message, test_failed: true }
+ end
+end
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 055d900eece..b9cb71ae89a 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -55,6 +55,6 @@ class Profiles::KeysController < Profiles::ApplicationController
private
def key_params
- params.require(:key).permit(:title, :key)
+ params.require(:key).permit(:title, :key, :expires_at)
end
end
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index c916140211e..92c6ce324f7 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -52,28 +52,26 @@ class Projects::ServicesController < Projects::ApplicationController
private
def service_test_response
- if @service.update(service_params[:service])
- data = @service.test_data(project, current_user)
- outcome = @service.test(data)
-
- if outcome[:success]
- {}
- else
- { error: true, message: _('Test failed.'), service_response: outcome[:result].to_s, test_failed: true }
- end
- else
- { error: true, message: _('Validations failed.'), service_response: @service.errors.full_messages.join(','), test_failed: false }
+ unless @service.update(service_params[:service])
+ return { error: true, message: _('Validations failed.'), service_response: @service.errors.full_messages.join(','), test_failed: false }
+ end
+
+ data = @service.test_data(project, current_user)
+ outcome = @service.test(data)
+
+ unless outcome[:success]
+ return { error: true, message: _('Test failed.'), service_response: outcome[:result].to_s, test_failed: true }
end
+
+ {}
rescue Gitlab::HTTP::BlockedUrlError => e
{ error: true, message: _('Test failed.'), service_response: e.message, test_failed: true }
end
def success_message
- if @service.active?
- _("%{service_title} activated.") % { service_title: @service.title }
- else
- _("%{service_title} settings saved, but not activated.") % { service_title: @service.title }
- end
+ message = @service.active? ? _('activated') : _('settings saved, but not activated')
+
+ _('%{service_title} %{message}.') % { service_title: @service.title, message: message }
end
def service
diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb
index 0158d909468..64659208315 100644
--- a/app/models/clusters/applications/ingress.rb
+++ b/app/models/clusters/applications/ingress.rb
@@ -16,7 +16,7 @@ module Clusters
include AfterCommitQueue
default_value_for :ingress_type, :nginx
- default_value_for :modsecurity_enabled, false
+ default_value_for :modsecurity_enabled, true
default_value_for :version, VERSION
enum ingress_type: {
diff --git a/app/models/key.rb b/app/models/key.rb
index afa0d489ef6..18fa8aaaa16 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -6,6 +6,7 @@ class Key < ApplicationRecord
include AfterCommitQueue
include Sortable
include Sha256Attribute
+ include Expirable
sha256_attribute :fingerprint_sha256
diff --git a/app/views/admin/integrations/_form.html.haml b/app/views/admin/integrations/_form.html.haml
new file mode 100644
index 00000000000..aa865c3b052
--- /dev/null
+++ b/app/views/admin/integrations/_form.html.haml
@@ -0,0 +1,12 @@
+%h3.page-title
+ = @service.title
+
+%p= @service.description
+
+= form_for @service, as: :service, url: admin_application_settings_integration_path, method: :put, html: { class: 'gl-show-field-errors fieldset-form integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_admin_application_settings_integration_path(@service) } } do |form|
+ = render 'shared/service_settings', form: form, service: @service
+
+ - if @service.editable?
+ .footer-block.row-content-block
+ = service_save_button(@service)
+ = link_to _('Cancel'), admin_application_settings_integration_path, class: 'btn btn-cancel'
diff --git a/app/views/admin/integrations/edit.html.haml b/app/views/admin/integrations/edit.html.haml
new file mode 100644
index 00000000000..dea0f524f03
--- /dev/null
+++ b/app/views/admin/integrations/edit.html.haml
@@ -0,0 +1,5 @@
+- add_to_breadcrumbs _('Integrations'), admin_application_settings_integration_path
+- breadcrumb_title @service.title
+- page_title @service.title, _('Integrations')
+
+= render 'form'
diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml
index 495ee6a04ea..d18e91c0b14 100644
--- a/app/views/admin/services/_form.html.haml
+++ b/app/views/admin/services/_form.html.haml
@@ -4,7 +4,7 @@
%p #{@service.description} template.
= form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'fieldset-form' } do |form|
- = render 'shared/service_settings', form: form, subject: @service
+ = render 'shared/service_settings', form: form, service: @service
.footer-block.row-content-block
= form.submit 'Save', class: 'btn btn-success'
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index 63ef5eaa172..34e81285328 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -6,10 +6,15 @@
= f.label :key, s_('Profiles|Key'), class: 'label-bold'
%p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Don't use your private SSH key.")
= f.text_area :key, class: "form-control js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-ed25519 …" or "ssh-rsa …"')
- .form-group
- = f.label :title, _('Title'), class: 'label-bold'
- = f.text_field :title, class: "form-control input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
- %p.form-text.text-muted= _('Name your individual key via a title')
+ .form-row
+ .col.form-group
+ = f.label :title, _('Title'), class: 'label-bold'
+ = f.text_field :title, class: "form-control input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
+ %p.form-text.text-muted= s_('Profiles|Give your individual key a title')
+
+ .col.form-group
+ = f.label :expires_at, s_('Profiles|Expires at'), class: 'label-bold'
+ = f.date_field :expires_at, class: "form-control input-lg qa-key-expiry-field", min: Date.tomorrow
.js-add-ssh-key-validation-warning.hide
.bs-callout.bs-callout-warning{ role: 'alert', aria_live: 'assertive' }
diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml
index 0e94e6563fd..b227041c9de 100644
--- a/app/views/profiles/keys/_key.html.haml
+++ b/app/views/profiles/keys/_key.html.haml
@@ -1,24 +1,31 @@
-%li.key-list-item
- .float-left.append-right-10
+%li.d-flex.align-items-center.key-list-item
+ .append-right-10
- if key.valid?
- = icon 'key', class: 'settings-list-icon d-none d-sm-block'
+ - if key.expired?
+ %span.d-inline-block.has-tooltip{ title: s_('Profiles|Your key has expired') }
+ = sprite_icon('warning-solid', size: 16, css_class: 'settings-list-icon d-none d-sm-block')
+ - else
+ = sprite_icon('key', size: 16, css_class: 'settings-list-icon d-none d-sm-block ')
- else
- = icon 'exclamation-triangle', class: 'settings-list-icon d-none d-sm-block has-tooltip',
- title: key.errors.full_messages.join(', ')
+ %span.d-inline-block.has-tooltip{ title: key.errors.full_messages.join(', ') }
+ = sprite_icon('warning-solid', size: 16, css_class: 'settings-list-icon d-none d-sm-block')
-
- .key-list-item-info
+ .key-list-item-info.w-100.float-none
= link_to path_to_key(key, is_admin), class: "title" do
= key.title
%span.text-truncate
= key.fingerprint
- .last-used-at
- last used:
- = key.last_used_at ? time_ago_with_tooltip(key.last_used_at) : 'n/a'
- .float-right
- %span.key-created-at
- = s_('Profiles|Created %{time_ago}'.html_safe) % { time_ago:time_ago_with_tooltip(key.created_at)}
- - if key.can_delete?
- = link_to path_to_key(key, is_admin), data: { confirm: _('Are you sure?')}, method: :delete, class: "btn btn-transparent prepend-left-10" do
- %span.sr-only= _('Remove')
- = icon('trash')
+
+ .key-list-item-dates.d-flex.align-items-start.justify-content-between
+ %span.last-used-at.append-right-10
+ = s_('Profiles|Last used:')
+ = key.last_used_at ? time_ago_with_tooltip(key.last_used_at) : _('Never')
+ %span.expires.append-right-10
+ = s_('Profiles|Expires:')
+ = key.expires_at ? key.expires_at.to_date : _('Never')
+ %span.key-created-at
+ = s_('Profiles|Created %{time_ago}'.html_safe) % { time_ago:time_ago_with_tooltip(key.created_at)}
+ - if key.can_delete?
+ = link_to path_to_key(key, is_admin), data: { confirm: _('Are you sure?')}, method: :delete, class: "btn btn-transparent prepend-left-10 align-baseline" do
+ %span.sr-only= _('Remove')
+ = sprite_icon('remove', size: 16)
diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml
index 02f1a267044..88deb0f11cb 100644
--- a/app/views/profiles/keys/_key_details.html.haml
+++ b/app/views/profiles/keys/_key_details.html.haml
@@ -12,8 +12,11 @@
%span.light= _('Created on:')
%strong= @key.created_at.to_s(:medium)
%li
+ %span.light= _('Expires:')
+ %strong= @key.expires_at.try(:to_s, :medium) || _('Never')
+ %li
%span.light= _('Last used on:')
- %strong= @key.last_used_at.try(:to_s, :medium) || 'N/A'
+ %strong= @key.last_used_at.try(:to_s, :medium) || _('Never')
.col-md-8
= form_errors(@key, type: 'key') unless @key.valid?
diff --git a/app/views/profiles/keys/_key_table.html.haml b/app/views/profiles/keys/_key_table.html.haml
index 8b862522645..176d7a42002 100644
--- a/app/views/profiles/keys/_key_table.html.haml
+++ b/app/views/profiles/keys/_key_table.html.haml
@@ -1,7 +1,7 @@
- is_admin = local_assigns.fetch(:admin, false)
- if @keys.any?
- %ul.content-list{ data: { qa_selector: 'ssh_keys_list' } }
+ %ul.content-list.ssh-keys-list{ data: { qa_selector: 'ssh_keys_list' } }
= render partial: 'profiles/keys/key', collection: @keys, locals: { is_admin: is_admin }
- else
%p.settings-message.text-center
diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml
index caaf164a763..971107675ab 100644
--- a/app/views/projects/find_file/show.html.haml
+++ b/app/views/projects/find_file/show.html.haml
@@ -23,4 +23,5 @@
= _('There are no matching files')
%p.text-secondary
= _('Try using a different search term to find the file you are looking for.')
- = spinner nil, true
+ .text-center.prepend-top-default.loading
+ .spinner.spinner-md
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 582f3d6fce4..a0d9d29a7ae 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -11,7 +11,7 @@
%p= @service.detailed_description
.col-lg-9
= form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
- = render 'shared/service_settings', form: form, subject: @service
+ = render 'shared/service_settings', form: form, service: @service
- if @service.editable?
.footer-block.row-content-block
= service_save_button(@service)
diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml
index 4415c654ab9..aeda7ea9909 100644
--- a/app/views/shared/_service_settings.html.haml
+++ b/app/views/shared/_service_settings.html.haml
@@ -1,7 +1,7 @@
= form_errors(@service)
- if lookup_context.template_exists?('help', "projects/services/#{@service.to_param}", true)
- = render "projects/services/#{@service.to_param}/help", subject: subject
+ = render "projects/services/#{@service.to_param}/help", subject: @service
- elsif @service.help.present?
.info-well
.well-segment
diff --git a/changelogs/unreleased/208886-optimize-deployment-counters-related-to-the-deployment.yml b/changelogs/unreleased/208886-optimize-deployment-counters-related-to-the-deployment.yml
new file mode 100644
index 00000000000..d8e0eca9075
--- /dev/null
+++ b/changelogs/unreleased/208886-optimize-deployment-counters-related-to-the-deployment.yml
@@ -0,0 +1,5 @@
+---
+title: Optimize Deployment related counters
+merge_request: 26757
+author:
+type: performance
diff --git a/changelogs/unreleased/208914-crud-for-instance-level-integrations.yml b/changelogs/unreleased/208914-crud-for-instance-level-integrations.yml
new file mode 100644
index 00000000000..94311b14adb
--- /dev/null
+++ b/changelogs/unreleased/208914-crud-for-instance-level-integrations.yml
@@ -0,0 +1,5 @@
+---
+title: Add CRUD for Instance-Level Integrations
+merge_request: 26454
+author:
+type: added
diff --git a/changelogs/unreleased/27227-widget-showing-changed-pages-for-visual-reviews.yml b/changelogs/unreleased/27227-widget-showing-changed-pages-for-visual-reviews.yml
new file mode 100644
index 00000000000..7d2cdcd4bde
--- /dev/null
+++ b/changelogs/unreleased/27227-widget-showing-changed-pages-for-visual-reviews.yml
@@ -0,0 +1,5 @@
+---
+title: Add changed pages dropdown to visual review modal
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/36243-introduce-an-optional-expiration-date-for-ssh-keys.yml b/changelogs/unreleased/36243-introduce-an-optional-expiration-date-for-ssh-keys.yml
new file mode 100644
index 00000000000..1a51203095f
--- /dev/null
+++ b/changelogs/unreleased/36243-introduce-an-optional-expiration-date-for-ssh-keys.yml
@@ -0,0 +1,5 @@
+---
+title: Introduce optional expiry date for SSH Keys
+merge_request: 26351
+author:
+type: added
diff --git a/changelogs/unreleased/Resolve-Migrate--fa-spinner-app-views-projects-find_file.yml b/changelogs/unreleased/Resolve-Migrate--fa-spinner-app-views-projects-find_file.yml
new file mode 100644
index 00000000000..47da290fb43
--- /dev/null
+++ b/changelogs/unreleased/Resolve-Migrate--fa-spinner-app-views-projects-find_file.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate .fa-spinner to .spinner for app/views/projects/find_file
+merge_request: 25051
+author: nuwe1
+type: other
diff --git a/changelogs/unreleased/feature-199458-track-jump-to-next-unresolved-thread.yml b/changelogs/unreleased/feature-199458-track-jump-to-next-unresolved-thread.yml
new file mode 100644
index 00000000000..a34de5df008
--- /dev/null
+++ b/changelogs/unreleased/feature-199458-track-jump-to-next-unresolved-thread.yml
@@ -0,0 +1,5 @@
+---
+title: Added tracking to merge request jump to next thread buttons
+merge_request: 26319
+author: Martin Hobert
+type: added
diff --git a/changelogs/unreleased/replace_checkbox_by_toggle_for_modsecurity.yml b/changelogs/unreleased/replace_checkbox_by_toggle_for_modsecurity.yml
new file mode 100644
index 00000000000..73ec392fbaa
--- /dev/null
+++ b/changelogs/unreleased/replace_checkbox_by_toggle_for_modsecurity.yml
@@ -0,0 +1,5 @@
+---
+title: Replace checkbox by toggle for ModSecurity on Cluster App Page
+merge_request: 26720
+author:
+type: changed
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 54df15137d6..c92484316e4 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -121,6 +121,11 @@ namespace :admin do
get '/', to: redirect('admin/application_settings/general'), as: nil
resources :services, only: [:index, :edit, :update]
+ resources :integrations, only: [:edit, :update, :test] do
+ member do
+ put :test
+ end
+ end
get :usage_data
put :reset_registration_token
diff --git a/db/migrate/20200303055348_add_expires_at_to_keys.rb b/db/migrate/20200303055348_add_expires_at_to_keys.rb
new file mode 100644
index 00000000000..ef7b813a2ef
--- /dev/null
+++ b/db/migrate/20200303055348_add_expires_at_to_keys.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddExpiresAtToKeys < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :keys, :expires_at, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20200311084025_add_index_on_user_id_status_created_at_to_deployments.rb b/db/migrate/20200311084025_add_index_on_user_id_status_created_at_to_deployments.rb
new file mode 100644
index 00000000000..1744b701f0a
--- /dev/null
+++ b/db/migrate/20200311084025_add_index_on_user_id_status_created_at_to_deployments.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexOnUserIdStatusCreatedAtToDeployments < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :deployments, [:user_id, :status, :created_at]
+ end
+
+ def down
+ remove_concurrent_index :deployments, [:user_id, :status, :created_at]
+ end
+end
diff --git a/db/migrate/20200311094020_add_index_on_id_and_status_to_deployments.rb b/db/migrate/20200311094020_add_index_on_id_and_status_to_deployments.rb
new file mode 100644
index 00000000000..77c746ed88b
--- /dev/null
+++ b/db/migrate/20200311094020_add_index_on_id_and_status_to_deployments.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexOnIdAndStatusToDeployments < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :deployments, [:id, :status]
+ end
+
+ def down
+ remove_concurrent_index :deployments, [:id, :status]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 06f2011c568..ca12b77bd2e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1409,6 +1409,7 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.index ["environment_id", "id"], name: "index_deployments_on_environment_id_and_id"
t.index ["environment_id", "iid", "project_id"], name: "index_deployments_on_environment_id_and_iid_and_project_id"
t.index ["environment_id", "status"], name: "index_deployments_on_environment_id_and_status"
+ t.index ["id", "status"], name: "index_deployments_on_id_and_status"
t.index ["id"], name: "partial_index_deployments_for_legacy_successful_deployments", where: "((finished_at IS NULL) AND (status = 2))"
t.index ["project_id", "id"], name: "index_deployments_on_project_id_and_id", order: { id: :desc }
t.index ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true
@@ -1417,6 +1418,7 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.index ["project_id", "status"], name: "index_deployments_on_project_id_and_status"
t.index ["project_id", "updated_at", "id"], name: "index_deployments_on_project_id_and_updated_at_and_id", order: { updated_at: :desc, id: :desc }
t.index ["project_id"], name: "partial_index_deployments_for_project_id_and_tag", where: "(tag IS TRUE)"
+ t.index ["user_id", "status", "created_at"], name: "index_deployments_on_user_id_and_status_and_created_at"
end
create_table "description_versions", force: :cascade do |t|
@@ -2291,6 +2293,7 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.boolean "public", default: false, null: false
t.datetime "last_used_at"
t.binary "fingerprint_sha256"
+ t.datetime_with_timezone "expires_at"
t.index ["fingerprint"], name: "index_keys_on_fingerprint", unique: true
t.index ["fingerprint_sha256"], name: "index_keys_on_fingerprint_sha256"
t.index ["id", "type"], name: "index_on_deploy_keys_id_and_type_and_public", unique: true, where: "(public = true)"
diff --git a/doc/.linting/vale/styles/gitlab/SentenceSpacing.yml b/doc/.linting/vale/styles/gitlab/SentenceSpacing.yml
index 0fc8bfedadc..b061f7f6f9e 100644
--- a/doc/.linting/vale/styles/gitlab/SentenceSpacing.yml
+++ b/doc/.linting/vale/styles/gitlab/SentenceSpacing.yml
@@ -1,12 +1,15 @@
---
-# Checks the presence of more than one space between sentences or clauses.
+# Check for the following in common content scenarios:
+#
+# - No spaces.
+# - More than one space.
#
# For a list of all options, see https://errata-ai.github.io/vale/styles/
extends: existence
-message: "'%s' should have one space between sentences or clauses."
+message: '"%s" must contain one and only one space.'
link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#punctuation
-level: warning
+level: error
nonword: true
tokens:
- '[a-z][.?!,][A-Z]'
- - '[.?!,] {2,}[\w]'
+ - '[\w.?!,\(\)\-":] {2,}[\w.?!,\(\)\-":]'
diff --git a/doc/README.md b/doc/README.md
index 44d8b3b1932..198f37c9ea1 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -109,7 +109,7 @@ The following documentation relates to the DevOps **Plan** stage:
| Plan Topics | Description |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------|
| [Burndown Charts](user/project/milestones/burndown_charts.md) **(STARTER)** | Watch your project's progress throughout a specific milestone. |
-| [Discussions](user/discussions/index.md) | Threads, comments, and resolvable threads in issues, commits, and merge requests. |
+| [Discussions](user/discussions/index.md) | Threads, comments, and resolvable threads in issues, commits, and merge requests. |
| [Due Dates](user/project/issues/due_dates.md) | Keep track of issue deadlines. |
| [Epics](user/group/epics/index.md) **(ULTIMATE)** | Tracking groups of issues that share a theme. |
| [Issues](user/project/issues/index.md), including [confidential issues](user/project/issues/confidential_issues.md),<br/>[issue and merge request templates](user/project/description_templates.md),<br/>and [moving issues](user/project/issues/managing_issues.md#moving-issues) | Project issues and restricting access to issues as well as creating templates for submitting new issues and merge requests. Also, moving issues between projects. |
diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
index 35620be7d7e..acb39ae0f78 100644
--- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
+++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
@@ -14,7 +14,7 @@ GitLab has supported LDAP integration since [version 2.2](https://about.gitlab.c
### Choosing an LDAP Server
-The main reason organizations choose to utilize a LDAP server is to keep the entire organization's user base consolidated into a central repository. Users can access multiple applications and systems across the IT environment using a single login. Because LDAP is an open, vendor-neutral, industry standard application protocol, the number of applications using LDAP authentication continues to increase.
+The main reason organizations choose to utilize a LDAP server is to keep the entire organization's user base consolidated into a central repository. Users can access multiple applications and systems across the IT environment using a single login. Because LDAP is an open, vendor-neutral, industry standard application protocol, the number of applications using LDAP authentication continues to increase.
There are many commercial and open source [directory servers](https://en.wikipedia.org/wiki/Directory_service#LDAP_implementations) that support the LDAP protocol. Deciding on the right directory server highly depends on the existing IT environment in which the server will be integrated with.
@@ -32,9 +32,9 @@ For example, [Active Directory](https://docs.microsoft.com/en-us/previous-versio
We won't cover the installation and configuration of Windows Server or Active Directory Domain Services in this tutorial. There are a number of resources online to guide you through this process:
-- Install Windows Server 2012 - (`technet.microsoft.com`) - [Installing Windows Server 2012](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/jj134246(v=ws.11))
+- Install Windows Server 2012 - (`technet.microsoft.com`) - [Installing Windows Server 2012](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/jj134246(v=ws.11))
-- Install Active Directory Domain Services (AD DS) (`technet.microsoft.com`)- [Install Active Directory Domain Services](https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/deploy/install-active-directory-domain-services--level-100-#BKMK_PS)
+- Install Active Directory Domain Services (AD DS) (`technet.microsoft.com`) - [Install Active Directory Domain Services](https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/deploy/install-active-directory-domain-services--level-100-#BKMK_PS)
> **Shortcut:** You can quickly install AD DS via PowerShell using
`Install-WindowsFeature AD-Domain-Services -IncludeManagementTools`
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index e075a2b132e..3a8219bdbc2 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -215,7 +215,7 @@ sudo gitlab-rake gitlab:geo:check
- Ensure that you entered the `external_url` or `gitlab_rails['geo_node_name']` when adding the secondary node in the admin are of the **primary** node.
- Prior to GitLab 12.4, edit the secondary node in the Admin Area of the **primary** node and ensure that there is a trailing `/` in the `Name` field.
-1. Check returns Exception: PG::UndefinedTable: ERROR: relation "geo_nodes" does not exist
+1. Check returns `Exception: PG::UndefinedTable: ERROR: relation "geo_nodes" does not exist`
```plaintext
Checking Geo ...
@@ -252,7 +252,7 @@ sudo gitlab-rake gitlab:geo:check
The following sections outline troubleshooting steps for fixing replication
errors.
-### Message: "ERROR: replication slots can only be used if max_replication_slots > 0"?
+### Message: `ERROR: replication slots can only be used if max_replication_slots > 0`?
This means that the `max_replication_slots` PostgreSQL variable needs to
be set on the **primary** database. In GitLab 9.4, we have made this setting
@@ -263,7 +263,7 @@ Be sure to restart PostgreSQL for this to take
effect. See the [PostgreSQL replication
setup][database-pg-replication] guide for more details.
-### Message: "FATAL: could not start WAL streaming: ERROR: replication slot "geo_secondary_my_domain_com" does not exist"?
+### Message: `FATAL: could not start WAL streaming: ERROR: replication slot "geo_secondary_my_domain_com" does not exist`?
This occurs when PostgreSQL does not have a replication slot for the
**secondary** node by that name.
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index c12826c80cd..1bd027ac0c9 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -777,7 +777,7 @@ two checks. The result of both of these checks is cached.
see if we can access filesystem underneath the Gitaly server
directly. If so, use the Rugged patch.
-To see if GitLab Rails can access the repo filesystem directly, we use
+To see if GitLab Rails can access the repo filesystem directly, we use
the following heuristic:
- Gitaly ensures that the filesystem has a metadata file in its root
@@ -1010,7 +1010,7 @@ unset https_proxy
When updating the `gitaly['listen_addr']` or `gitaly['prometheus_listen_addr']` values, Gitaly may continue to listen on the old address after a `sudo gitlab-ctl reconfigure`.
-When this occurs, performing a `sudo gitlab-ctl restart` will resolve the issue. This will no longer be necessary after [this issue](https://gitlab.com/gitlab-org/gitaly/issues/2521) is resolved.
+When this occurs, performing a `sudo gitlab-ctl restart` will resolve the issue. This will no longer be necessary after [this issue](https://gitlab.com/gitlab-org/gitaly/issues/2521) is resolved.
### Praefect
diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md
index e05a34a9c3e..c74beb11241 100644
--- a/doc/administration/high_availability/README.md
+++ b/doc/administration/high_availability/README.md
@@ -78,24 +78,41 @@ References:
## GitLab components and configuration instructions
-The GitLab application depends on the following [components](../../development/architecture.md#component-diagram)
-and services. They are included in the reference architectures along with our
-recommendations for their use and configuration. They are presented in the order
-in which you would typically configure them.
-
-| Component | Description | Configuration Instructions |
-|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|
-| [Load Balancer(s)](load_balancer.md)[^6] | Handles load balancing for the GitLab nodes where required. | [Load balancer HA configuration](load_balancer.md) |
-| [Consul](../../development/architecture.md#consul)[^3] | Service discovery and health checks/failover | [Consul HA configuration](consul.md) **(PREMIUM ONLY)** |
-| [PostgreSQL](../../development/architecture.md#postgresql) | Database | [Database HA configuration](database.md) |
-| [PgBouncer](../../development/architecture.md#pgbouncer) | Database Pool Manager | [PgBouncer HA configuration](pgbouncer.md) **(PREMIUM ONLY)** |
-| [Redis](../../development/architecture.md#redis)[^3] with Redis Sentinel | Key/Value store for shared data with HA watcher service | [Redis HA configuration](redis.md) |
-| [Gitaly](../../development/architecture.md#gitaly)[^2] [^5] [^7] | Recommended high-level storage for Git repository data. | [Gitaly HA configuration](gitaly.md) |
-| [Sidekiq](../../development/architecture.md#sidekiq) | Asynchronous/Background jobs | |
-| [Cloud Object Storage service](object_storage.md)[^4] | Recommended store for shared data objects such as LFS, Uploads, Artifacts, etc... | [Cloud Object Storage configuration](object_storage.md) |
-| [GitLab application nodes](../../development/architecture.md#unicorn)[^1] | (Unicorn / Puma, Workhorse) - Web-requests (UI, API, Git over HTTP) | [GitLab app HA/scaling configuration](gitlab.md) |
-| [NFS](nfs.md)[^5] [^7] | Shared disk storage service. Can be used as an alternative for Gitaly or Object Storage. Required for GitLab Pages. | [NFS configuration](nfs.md) |
-| [Prometheus](../../development/architecture.md#prometheus) and [Grafana](../../development/architecture.md#grafana) | GitLab environment monitoring | [Monitoring node for scaling/HA](monitoring_node.md) |
+The GitLab application depends on the following [components](../../development/architecture.md#component-diagram).
+It can also depend on several third party services depending on
+your environment setup. Here we'll detail both in the order in which
+you would typically configure them along with our recommendations for
+their use and configuration.
+
+### Third party services
+
+Here's some details of several third party services a typical environment
+will depend on. The services can be provided by numerous applications
+or providers and further advice can be given on how best to select.
+These should be configured first, before the [GitLab components](#gitlab-components).
+
+| Component | Description | Configuration instructions |
+|--------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------|
+| [Load Balancer(s)](load_balancer.md)[^6] | Handles load balancing for the GitLab nodes where required | [Load balancer HA configuration](load_balancer.md) |
+| [Cloud Object Storage service](object_storage.md)[^4] | Recommended store for shared data objects | [Cloud Object Storage configuration](object_storage.md) |
+| [NFS](nfs.md)[^5] [^7] | Shared disk storage service. Can be used as an alternative for Gitaly or Object Storage. Required for GitLab Pages | [NFS configuration](nfs.md) |
+
+### GitLab components
+
+Next are all of the components provided directly by GitLab. As mentioned
+earlier, they are presented in the typical order you would configure
+them.
+
+| Component | Description | Configuration instructions |
+|---------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|---------------------------------------------------------------|
+| [Consul](../../development/architecture.md#consul)[^3] | Service discovery and health checks/failover | [Consul HA configuration](consul.md) **(PREMIUM ONLY)** |
+| [PostgreSQL](../../development/architecture.md#postgresql) | Database | [Database HA configuration](database.md) |
+| [PgBouncer](../../development/architecture.md#pgbouncer) | Database Pool Manager | [PgBouncer HA configuration](pgbouncer.md) **(PREMIUM ONLY)** |
+| [Redis](../../development/architecture.md#redis)[^3] with Redis Sentinel | Key/Value store for shared data with HA watcher service | [Redis HA configuration](redis.md) |
+| [Gitaly](../../development/architecture.md#gitaly)[^2] [^5] [^7] | Recommended high-level storage for Git repository data | [Gitaly HA configuration](gitaly.md) |
+| [Sidekiq](../../development/architecture.md#sidekiq) | Asynchronous/Background jobs | |
+| [GitLab application nodes](../../development/architecture.md#unicorn)[^1] | (Unicorn / Puma, Workhorse) - Web-requests (UI, API, Git over HTTP) | [GitLab app HA/scaling configuration](gitlab.md) |
+| [Prometheus](../../development/architecture.md#prometheus) and [Grafana](../../development/architecture.md#grafana) | GitLab environment monitoring | [Monitoring node for scaling/HA](monitoring_node.md) |
In some cases, components can be combined on the same nodes to reduce complexity as well.
diff --git a/doc/administration/high_availability/monitoring_node.md b/doc/administration/high_availability/monitoring_node.md
index d293fc350fa..a12b865cb91 100644
--- a/doc/administration/high_availability/monitoring_node.md
+++ b/doc/administration/high_availability/monitoring_node.md
@@ -74,8 +74,8 @@ Omnibus:
## Migrating to Service Discovery
Once monitoring using Service Discovery is enabled with `consul['monitoring_service_discovery'] = true`,
-ensure that `prometheus['scrape_configs']` is not set in `/etc/gitlab/gitlab.rb`. Setting both
-`consul['monitoring_service_discovery'] = true` and `prometheus['scrape_configs']` in `/etc/gitlab/gitlab.rb`
+ensure that `prometheus['scrape_configs']` is not set in `/etc/gitlab/gitlab.rb`. Setting both
+`consul['monitoring_service_discovery'] = true` and `prometheus['scrape_configs']` in `/etc/gitlab/gitlab.rb`
will result in errors.
<!-- ## Troubleshooting
diff --git a/doc/administration/lfs/manage_large_binaries_with_git_lfs.md b/doc/administration/lfs/manage_large_binaries_with_git_lfs.md
index 025b547c37e..da6567348aa 100644
--- a/doc/administration/lfs/manage_large_binaries_with_git_lfs.md
+++ b/doc/administration/lfs/manage_large_binaries_with_git_lfs.md
@@ -113,7 +113,7 @@ created or updated with the following content:
```
You can also register a file type as lockable without using LFS
-(In order to be able to lock/unlock a file you need a remote server that implements the LFS File Locking API),
+(In order to be able to lock/unlock a file you need a remote server that implements the LFS File Locking API),
in order to do that you can edit the `.gitattributes` file manually:
```shell
diff --git a/doc/administration/monitoring/performance/influxdb_configuration.md b/doc/administration/monitoring/performance/influxdb_configuration.md
index 234d0dc2e88..b81f0ce1506 100644
--- a/doc/administration/monitoring/performance/influxdb_configuration.md
+++ b/doc/administration/monitoring/performance/influxdb_configuration.md
@@ -150,7 +150,7 @@ before creating a database.
_**Note:** If you [created an admin user](#create-a-new-admin-user) and enabled
[HTTP authentication](#http), remember to append the username (`-username <username>`)
-and password (`-password <password>`) you set earlier to the commands below._
+and password (`-password <password>`) you set earlier to the commands below._
Run the following command to create a database named `gitlab`:
diff --git a/doc/administration/troubleshooting/sidekiq.md b/doc/administration/troubleshooting/sidekiq.md
index 8176ebab2e8..73598bb9441 100644
--- a/doc/administration/troubleshooting/sidekiq.md
+++ b/doc/administration/troubleshooting/sidekiq.md
@@ -307,4 +307,4 @@ has number of drawbacks, as mentioned in [Why Ruby’s Timeout is dangerous (and
> - while creating an object to save to the database afterwards
> - in any of your code, regardless of whether it could have possibly raised an exception before
>
-> Nobody writes code to defend against an exception being raised on literally any line. That’s not even possible. So Thread.raise is basically like a sneak attack on your code that could result in almost anything. It would probably be okay if it were pure-functional code that did not modify any state. But this is Ruby, so that’s unlikely :)
+> Nobody writes code to defend against an exception being raised on literally any line. That’s not even possible. So Thread.raise is basically like a sneak attack on your code that could result in almost anything. It would probably be okay if it were pure-functional code that did not modify any state. But this is Ruby, so that’s unlikely :)
diff --git a/doc/api/epic_issues.md b/doc/api/epic_issues.md
index e23639ce8b0..b001749ff5b 100644
--- a/doc/api/epic_issues.md
+++ b/doc/api/epic_issues.md
@@ -8,7 +8,7 @@ Epics are available only in Ultimate. If epics feature is not available a `403`
## List issues for an epic
-Gets all issues that are assigned to an epic and the authenticated user has access to.
+Gets all issues that are assigned to an epic and the authenticated user has access to.
```plaintext
GET /groups/:id/epics/:epic_iid/issues
@@ -17,7 +17,7 @@ GET /groups/:id/epics/:epic_iid/issues
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `epic_iid` | integer/string | yes | The internal ID of the epic. |
+| `epic_iid` | integer/string | yes | The internal ID of the epic. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/1/epics/5/issues/
@@ -113,8 +113,8 @@ POST /groups/:id/epics/:epic_iid/issues/:issue_id
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `epic_iid` | integer/string | yes | The internal ID of the epic. |
-| `issue_id` | integer/string | yes | The ID of the issue. |
+| `epic_iid` | integer/string | yes | The internal ID of the epic. |
+| `issue_id` | integer/string | yes | The ID of the issue. |
```shell
curl --header POST "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/1/epics/5/issues/55
@@ -219,8 +219,8 @@ DELETE /groups/:id/epics/:epic_iid/issues/:epic_issue_id
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | -----------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `epic_iid` | integer/string | yes | The internal ID of the epic. |
-| `epic_issue_id` | integer/string | yes | The ID of the issue - epic association. |
+| `epic_iid` | integer/string | yes | The internal ID of the epic. |
+| `epic_issue_id` | integer/string | yes | The ID of the issue - epic association. |
```shell
curl --header DELETE "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/1/epics/5/issues/11
@@ -325,7 +325,7 @@ PUT /groups/:id/epics/:epic_iid/issues/:epic_issue_id
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | -----------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `epic_iid` | integer/string | yes | The internal ID of the epic. |
+| `epic_iid` | integer/string | yes | The internal ID of the epic. |
| `epic_issue_id` | integer/string | yes | The ID of the issue - epic association. |
| `move_before_id` | integer/string | no | The ID of the issue - epic association that should be placed before the link in the question. |
| `move_after_id` | integer/string | no | The ID of the issue - epic association that should be placed after the link in the question. |
diff --git a/doc/api/epics.md b/doc/api/epics.md
index fddf7e22d0d..0a99ac6262b 100644
--- a/doc/api/epics.md
+++ b/doc/api/epics.md
@@ -167,7 +167,7 @@ GET /groups/:id/epics/:epic_iid
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `epic_iid` | integer/string | yes | The internal ID of the epic. |
+| `epic_iid` | integer/string | yes | The internal ID of the epic. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/1/epics/5
@@ -309,7 +309,7 @@ PUT /groups/:id/epics/:epic_iid
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `epic_iid` | integer/string | yes | The internal ID of the epic |
+| `epic_iid` | integer/string | yes | The internal ID of the epic |
| `title` | string | no | The title of an epic |
| `description` | string | no | The description of an epic. Limited to 1,048,576 characters. |
| `labels` | string | no | The comma separated list of labels |
@@ -379,7 +379,7 @@ DELETE /groups/:id/epics/:epic_iid
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `epic_iid` | integer/string | yes | The internal ID of the epic. |
+| `epic_iid` | integer/string | yes | The internal ID of the epic. |
```shell
curl --header DELETE "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/1/epics/5
diff --git a/doc/api/group_level_variables.md b/doc/api/group_level_variables.md
index 95a68283f1d..1812aaa3bff 100644
--- a/doc/api/group_level_variables.md
+++ b/doc/api/group_level_variables.md
@@ -1,4 +1,4 @@
-# Group-level Variables API
+# Group-level Variables API
> [Introduced][ce-34519] in GitLab 9.5
diff --git a/doc/api/keys.md b/doc/api/keys.md
index 05933e5a1d1..d4eb1161a97 100644
--- a/doc/api/keys.md
+++ b/doc/api/keys.md
@@ -24,6 +24,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/a
"title": "Sample key 25",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2015-09-03T07:24:44.627Z",
+ "expires_at": "2020-05-05T00:00:00.000Z"
"user": {
"name": "John Smith",
"username": "john_smith",
@@ -92,6 +93,7 @@ Example response:
"title": "Sample key 1",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1016k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2019-11-14T15:11:13.222Z",
+ "expires_at": "2020-05-05T00:00:00.000Z"
"user": {
"id": 1,
"name": "Administrator",
diff --git a/doc/api/merge_request_context_commits.md b/doc/api/merge_request_context_commits.md
index 260be467a06..4fbef3594d6 100644
--- a/doc/api/merge_request_context_commits.md
+++ b/doc/api/merge_request_context_commits.md
@@ -1,4 +1,4 @@
-# Merge request context commits API
+# Merge request context commits API
## List MR context commits
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index bda4f05d23d..3c31ebf067b 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -1296,13 +1296,13 @@ PUT /projects/:id/merge_requests/:merge_request_iid/merge
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
-- `merge_request_iid` (required) - Internal ID of MR
-- `merge_commit_message` (optional) - Custom merge commit message
-- `squash_commit_message` (optional) - Custom squash commit message
-- `squash` (optional) - if `true` the commits will be squashed into a single commit on merge
-- `should_remove_source_branch` (optional) - if `true` removes the source branch
+- `merge_request_iid` (required) - Internal ID of MR
+- `merge_commit_message` (optional) - Custom merge commit message
+- `squash_commit_message` (optional) - Custom squash commit message
+- `squash` (optional) - if `true` the commits will be squashed into a single commit on merge
+- `should_remove_source_branch` (optional) - if `true` removes the source branch
- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds
-- `sha` (optional) - if present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail
+- `sha` (optional) - if present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail
```json
{
@@ -1457,7 +1457,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/merge_ref
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
-- `merge_request_iid` (required) - Internal ID of MR
+- `merge_request_iid` (required) - Internal ID of MR
```json
{
diff --git a/doc/api/pipeline_schedules.md b/doc/api/pipeline_schedules.md
index 580d7efb891..859e88d0945 100644
--- a/doc/api/pipeline_schedules.md
+++ b/doc/api/pipeline_schedules.md
@@ -141,7 +141,7 @@ curl --request POST --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" --form descri
## Edit a pipeline schedule
-Updates the pipeline schedule of a project. Once the update is done, it will be rescheduled automatically.
+Updates the pipeline schedule of a project. Once the update is done, it will be rescheduled automatically.
```plaintext
PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id
diff --git a/doc/api/services.md b/doc/api/services.md
index 061dd3f4ead..8c70033d71d 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -658,7 +658,7 @@ Send IRC messages, on update, to a list of recipients through an Irker gateway.
Set Irker (IRC gateway) service for a project.
-> NOTE: Irker does NOT have built-in authentication, which makes it vulnerable to spamming IRC channels if it is hosted outside of a firewall. Please make sure you run the daemon within a secured network to prevent abuse. For more details, read: <http://www.catb.org/~esr/irker/security.html>.
+> NOTE: Irker does NOT have built-in authentication, which makes it vulnerable to spamming IRC channels if it is hosted outside of a firewall. Please make sure you run the daemon within a secured network to prevent abuse. For more details, read: <http://www.catb.org/~esr/irker/security.html>.
```plaintext
PUT /projects/:id/services/irker
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 1996f1ce144..2c75c175fdd 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -239,7 +239,7 @@ are listed in the descriptions of the relevant settings.
| `external_auth_client_key_pass` | string | no | Passphrase to use for the private key when authenticating with the external service this is encrypted when stored |
| `external_auth_client_key` | string | required by: `external_auth_client_cert` | Private key for the certificate when authentication is required for the external authorization service, this is encrypted when stored |
| `external_authorization_service_default_label` | string | required by: `external_authorization_service_enabled` | The default classification label to use when requesting authorization and no classification label has been specified on the project |
-| `external_authorization_service_enabled` | boolean | no | (**If enabled, requires:** `external_authorization_service_default_label`, `external_authorization_service_timeout` and `external_authorization_service_url` ) Enable using an external authorization service for accessing projects |
+| `external_authorization_service_enabled` | boolean | no | (**If enabled, requires:** `external_authorization_service_default_label`, `external_authorization_service_timeout` and `external_authorization_service_url`) Enable using an external authorization service for accessing projects |
| `external_authorization_service_timeout` | float | required by: `external_authorization_service_enabled` | The timeout after which an authorization request is aborted, in seconds. When a request times out, access is denied to the user. (min: 0.001, max: 10, step: 0.001) |
| `external_authorization_service_url` | string | required by: `external_authorization_service_enabled` | URL to which authorization requests will be directed |
| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from |
diff --git a/doc/api/users.md b/doc/api/users.md
index c80a65aae45..239afa38548 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -754,7 +754,7 @@ POST /user/keys
Parameters:
- `title` (required) - new SSH Key's title
-- `key` (required) - new SSH key
+- `key` (required) - new SSH key
```json
{
@@ -791,9 +791,9 @@ POST /users/:id/keys
Parameters:
-- `id` (required) - id of specified user
+- `id` (required) - id of specified user
- `title` (required) - new SSH Key's title
-- `key` (required) - new SSH key
+- `key` (required) - new SSH key
## Delete SSH key for current user
@@ -819,7 +819,7 @@ DELETE /users/:id/keys/:key_id
Parameters:
- `id` (required) - id of specified user
-- `key_id` (required) - SSH key ID
+- `key_id` (required) - SSH key ID
## List all GPG keys
@@ -1130,7 +1130,7 @@ POST /users/:id/emails
Parameters:
-- `id` (required) - id of specified user
+- `id` (required) - id of specified user
- `email` (required) - email address
- `skip_confirmation` (optional) - Skip confirmation and assume e-mail is verified - true or false (default)
@@ -1158,7 +1158,7 @@ DELETE /users/:id/emails/:email_id
Parameters:
- `id` (required) - id of specified user
-- `email_id` (required) - email ID
+- `email_id` (required) - email ID
## Block user
diff --git a/doc/ci/examples/deployment/composer-npm-deploy.md b/doc/ci/examples/deployment/composer-npm-deploy.md
index 4ca817908ed..ae0880adef2 100644
--- a/doc/ci/examples/deployment/composer-npm-deploy.md
+++ b/doc/ci/examples/deployment/composer-npm-deploy.md
@@ -23,7 +23,7 @@ before_script:
- php -r "unlink('composer-setup.php');"
```
-This will make sure we have all requirements ready. Next, we want to run `composer install` to fetch all PHP dependencies and `npm install` to load Node.js packages, then run the `npm` script. We need to append them into `before_script` section:
+This will make sure we have all requirements ready. Next, we want to run `composer install` to fetch all PHP dependencies and `npm install` to load Node.js packages, then run the `npm` script. We need to append them into `before_script` section:
```yaml
before_script:
diff --git a/doc/ci/jenkins/index.md b/doc/ci/jenkins/index.md
index fe937bb1f3a..2678936ae70 100644
--- a/doc/ci/jenkins/index.md
+++ b/doc/ci/jenkins/index.md
@@ -53,7 +53,7 @@ There are some high level differences between the products worth mentioning:
- by [webhook](../triggers/README.md#triggering-a-pipeline-from-a-webhook)
- by [ChatOps](../chatops/README.md)
- You can control which jobs run in which cases, depending on how they are triggered,
+- You can control which jobs run in which cases, depending on how they are triggered,
with the [`rules` syntax](../yaml/README.md#rules).
- GitLab [pipeline scheduling concepts](../pipelines/schedules.md) are also different than with Jenkins.
- All jobs within a single stage always run in parallel, and all stages run in sequence. We are planning
@@ -78,7 +78,9 @@ There are some high level differences between the products worth mentioning:
also leverage [`protected environments`](../yaml/README.md#protecting-manual-jobs-premium)
to control who is able to approve them.
- GitLab comes with a [container registry](../../user/packages/container_registry/index.md), and we recommend using
- container images to set up your build environment.
+ container images to set up your build environment. For example, set up one pipeline that builds your build environment
+ itself and publish that to the container registry. Then, have your pipelines use this instead of each building their
+ own environment, which will be slower and may be less consistent. We have extensive docs on [how to use the Container Registry](../../user/packages/container_registry/index.md).
- Totally stuck and not sure where to turn for advice? The [GitLab community forum](https://forum.gitlab.com/) can be a great resource.
## Groovy vs. YAML
diff --git a/doc/ci/pipelines/schedules.md b/doc/ci/pipelines/schedules.md
index 3bd42899542..ea98638ae53 100644
--- a/doc/ci/pipelines/schedules.md
+++ b/doc/ci/pipelines/schedules.md
@@ -86,7 +86,7 @@ To change the Sidekiq worker's frequency:
1. Edit the `gitlab_rails['pipeline_schedule_worker_cron']` value in your instance's `gitlab.rb` file.
1. [Reconfigure GitLab](../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-For GitLab.com, refer to the [dedicated settings page](../../user/gitlab_com/index.md#cron-jobs).
+For GitLab.com, refer to the [dedicated settings page](../../user/gitlab_com/index.md#gitlab-cicd).
## Working with scheduled pipelines
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index bdef2eca1f2..e19167f6d9a 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -200,7 +200,8 @@ Feature.enable(:anonymous_visual_review_feedback)
The feedback form is served through a script you add to pages in your Review App.
If you have [Developer permissions](../../user/permissions.md) to the project,
you can access it by clicking the **Review** button in the **Pipeline** section
-of the merge request.
+of the merge request. The form modal will also show a dropdown for changed pages
+if [route maps](#route-maps) are configured in the project.
![review button](img/review_button.png)
diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md
index bd389783a1d..4cf3e429c63 100644
--- a/doc/ci/services/postgres.md
+++ b/doc/ci/services/postgres.md
@@ -119,7 +119,7 @@ We have set up an [Example PostgreSQL Project][postgres-example-repo] for your
convenience that runs on [GitLab.com](https://gitlab.com) using our publicly
available [shared runners](../runners/README.md).
-Want to hack on it? Simply fork it, commit and push your changes. Within a few
+Want to hack on it? Simply fork it, commit and push your changes. Within a few
moments the changes will be picked by a public runner and the job will begin.
[hub-pg]: https://hub.docker.com/_/postgres
diff --git a/doc/ci/services/redis.md b/doc/ci/services/redis.md
index b04b36f7a04..f22ee87a9d3 100644
--- a/doc/ci/services/redis.md
+++ b/doc/ci/services/redis.md
@@ -66,5 +66,5 @@ We have set up an [Example Redis Project](https://gitlab.com/gitlab-examples/red
that runs on [GitLab.com](https://gitlab.com) using our publicly available
[shared runners](../runners/README.md).
-Want to hack on it? Simply fork it, commit and push your changes. Within a few
+Want to hack on it? Simply fork it, commit and push your changes. Within a few
moments the changes will be picked by a public runner and the job will begin.
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index 4e597b883d0..00070594ded 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -123,7 +123,7 @@ settings page which provides self-explanatory examples.
When a rerun of a pipeline is triggered, the information is exposed in GitLab's
UI under the **Jobs** page and the jobs are marked as triggered 'by API'.
-![Marked rebuilds as on jobs page](img/builds_page.png)
+![Marked rebuilds as on jobs page](img/builds_page.png)
---
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 84bd67b42c3..e0c41d821a6 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -867,6 +867,10 @@ CAUTION: **Warning:**
There are some points to be aware of when
[using this feature with new branches or tags *without* pipelines for merge requests](#using-onlychanges-without-pipelines-for-merge-requests).
+CAUTION: **Warning:**
+There are some points to be aware of when
+[using this feature with scheduled pipelines](#using-onlychanges-with-scheduled-pipelines).
+
##### Using `only:changes` with pipelines for merge requests
With [pipelines for merge requests](../merge_request_pipelines/index.md),
@@ -931,6 +935,12 @@ This could result in some unexpected behavior, including:
- When pushing a new commit, the changed files are calculated using the previous commit
as the base SHA.
+##### Using `only:changes` with scheduled pipelines
+
+`only:changes` always evaluates as "true" in [Scheduled pipelines](../pipelines/schedules.md).
+All files are considered to have "changed" when a scheduled pipeline
+runs.
+
### `rules`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29011) in GitLab 12.3.
@@ -3042,7 +3052,7 @@ include:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/53903) in GitLab 11.7.
To include files from another private project under the same GitLab instance,
-use `include:file`. This file is referenced using full paths relative to the
+use `include:file`. This file is referenced using full paths relative to the
root directory (`/`). For example:
```yaml
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 7b00e3cea0d..471dcba4c2a 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -455,7 +455,7 @@ helped us with overall code quality (using delegation, `&.` those
types of things), and making the code more robust.
**["Support multiple assignees for merge requests"](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/10161)**:
-A good example of collaboration on an MR touching multiple parts of the codebase. Nick pointed out interesting edge cases, James Lopes also joined in raising concerns on import/export feature.
+A good example of collaboration on an MR touching multiple parts of the codebase. Nick pointed out interesting edge cases, James Lopes also joined in raising concerns on import/export feature.
### Credits
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index a00f7cff651..7239bd2e52a 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -137,7 +137,7 @@ their color is `#A8D695`.
with `_` replaced with a space.
For instance, the "Continuous Integration" group is represented by the
-~"group::continuous integration" label in the `gitlab-org` group since its key
+~"group::continuous integration" label in the `gitlab-org` group since its key
under `stages.manage.groups` is `continuous_integration`.
The current group labels can be found by [searching the labels list for `group::`](https://gitlab.com/groups/gitlab-org/-/labels?search=group::).
diff --git a/doc/development/database_review.md b/doc/development/database_review.md
index 32f67e14fe3..a624abc2929 100644
--- a/doc/development/database_review.md
+++ b/doc/development/database_review.md
@@ -185,6 +185,6 @@ NOTE: **Note:** Keep in mind that all runtimes should be measured against GitLab
| Migration Type | Execution Time Recommended | Notes |
|----|----|---|
-| Regular migrations on `db/migrate` | `3 minutes` | A valid exception are index creation as this can take a long time. |
+| Regular migrations on `db/migrate` | `3 minutes` | A valid exception are index creation as this can take a long time. |
| Post migrations on `db/post_migrate` | `10 minutes` | |
| Background migrations | --- | Since these are suitable for larger tables, it's not possible to set a precise timing guideline, however, any single query must stay below `1 second` execution time with cold caches. |
diff --git a/doc/development/fe_guide/development_process.md b/doc/development/fe_guide/development_process.md
index 64bc01c181c..92f3c82a6ea 100644
--- a/doc/development/fe_guide/development_process.md
+++ b/doc/development/fe_guide/development_process.md
@@ -4,7 +4,7 @@ You can find more about the organization of the frontend team in the [handbook](
## Development Checklist
-The idea is to remind us about specific topics during the time we build a new feature or start something. This is a common practice in other industries (like pilots) that also use standardised checklists to reduce problems early on.
+The idea is to remind us about specific topics during the time we build a new feature or start something. This is a common practice in other industries (like pilots) that also use standardised checklists to reduce problems early on.
Copy the content over to your issue or merge request and if something doesn't apply simply remove it from your current list.
diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md
index 36537a22e67..ea321330c41 100644
--- a/doc/development/fe_guide/icons.md
+++ b/doc/development/fe_guide/icons.md
@@ -65,7 +65,7 @@ export default {
</template>
```
-- **name** Name of the Icon in the SVG Sprite ([Overview is available here][svg-preview]).
+- **name** Name of the Icon in the SVG Sprite ([Overview is available here][svg-preview]).
- **size (optional)** Number value for the size which is then mapped to a specific CSS class
(Available Sizes: 8, 12, 16, 18, 24, 32, 48, 72 are mapped to `sXX` css classes)
- **css-classes (optional)** Additional CSS Classes to add to the svg tag.
diff --git a/doc/development/file_storage.md b/doc/development/file_storage.md
index a4a5f84313a..bc3c16bd45d 100644
--- a/doc/development/file_storage.md
+++ b/doc/development/file_storage.md
@@ -40,7 +40,7 @@ they are still not 100% standardized. You can see them below:
| Issues/MR/Notes Legacy Markdown attachments | no | uploads/-/system/note/attachment/:id/:filename | `AttachmentUploader` | Note |
| Design Management design thumbnails (EE) | yes | uploads/-/system/design_management/action/image_v432x230/:id/:filename | `DesignManagement::DesignV432x230Uploader` | DesignManagement::Action |
| CI Artifacts (CE) | yes | `shared/artifacts/:disk_hash[0..1]/:disk_hash[2..3]/:disk_hash/:year_:month_:date/:job_id/:job_artifact_id` (:disk_hash is SHA256 digest of project_id) | `JobArtifactUploader` | Ci::JobArtifact |
-| LFS Objects (CE) | yes | shared/lfs-objects/:hex/:hex/:object_hash | `LfsObjectUploader` | LfsObject |
+| LFS Objects (CE) | yes | shared/lfs-objects/:hex/:hex/:object_hash | `LfsObjectUploader` | LfsObject |
| External merge request diffs | yes | shared/external-diffs/merge_request_diffs/mr-:parent_id/diff-:id | `ExternalDiffUploader` | MergeRequestDiff |
CI Artifacts and LFS Objects behave differently in CE and EE. In CE they inherit the `GitlabUploader`
diff --git a/doc/development/permissions.md b/doc/development/permissions.md
index 9e67079d1bc..bca137337fc 100644
--- a/doc/development/permissions.md
+++ b/doc/development/permissions.md
@@ -9,11 +9,11 @@ anything that deals with permissions, all of them should be considered.
Groups and projects can have the following visibility levels:
-- public (20) - an entity is visible to everyone
+- public (20) - an entity is visible to everyone
- internal (10) - an entity is visible to logged in users
- private (0) - an entity is visible only to the approved members of the entity
-The visibility level of a group can be changed only if all subgroups and
+The visibility level of a group can be changed only if all subgroups and
subprojects have the same or lower visibility level. (e.g., a group can be set
to internal only if all subgroups and projects are internal or private).
diff --git a/doc/development/query_recorder.md b/doc/development/query_recorder.md
index 4963090ef27..f2c5b8b9848 100644
--- a/doc/development/query_recorder.md
+++ b/doc/development/query_recorder.md
@@ -65,7 +65,7 @@ There are multiple ways to find the source of queries.
`QueryRecorder#occurrences_by_line_method` returns a sorted array based on `data`, sorted by `count`.
1. You can output the call backtrace for the specific `QueryRecorder` instance you want
- by using `ActiveRecord::QueryRecorder.new(query_recorder_debug: true)`. The output
+ by using `ActiveRecord::QueryRecorder.new(query_recorder_debug: true)`. The output
will be in `test.log`
1. Using the environment variable `QUERY_RECORDER_DEBUG`, the call backtrace will be output for all tests.
diff --git a/doc/development/scalability.md b/doc/development/scalability.md
index 3a9f5387b11..b3387526ccd 100644
--- a/doc/development/scalability.md
+++ b/doc/development/scalability.md
@@ -67,7 +67,7 @@ kind of partitioning.
Sharding is likely more difficult and will require significant changes
to the schema and application. For example, if we have to store projects
in many different databases, we immediately run into the question, "How
-can we retrieve data across different projects?" One answer to this is
+can we retrieve data across different projects?" One answer to this is
to abstract data access into API calls that abstract the database from
the application, but this is a significant amount of work.
diff --git a/doc/development/testing_guide/end_to_end/best_practices.md b/doc/development/testing_guide/end_to_end/best_practices.md
index 97daf4885ca..e1807f2b53f 100644
--- a/doc/development/testing_guide/end_to_end/best_practices.md
+++ b/doc/development/testing_guide/end_to_end/best_practices.md
@@ -10,7 +10,7 @@ But if the login feature is already covered with end-to-end tests through the GU
Let's say that, on average, the process to perform a successful login through the GUI takes 2 seconds.
-Now, realize that almost all tests need the user to be logged in, and that we need every test to run in isolation, meaning that tests cannot interfere with each other. This would mean that for every test the user needs to log in, and "waste 2 seconds".
+Now, realize that almost all tests need the user to be logged in, and that we need every test to run in isolation, meaning that tests cannot interfere with each other. This would mean that for every test the user needs to log in, and "waste 2 seconds".
Now, multiply the number of tests per 2 seconds, and as your test suite grows, the time to run it grows with it, and this is not sustainable.
diff --git a/doc/development/testing_guide/end_to_end/quick_start_guide.md b/doc/development/testing_guide/end_to_end/quick_start_guide.md
index 5d5715df372..e1024eace40 100644
--- a/doc/development/testing_guide/end_to_end/quick_start_guide.md
+++ b/doc/development/testing_guide/end_to_end/quick_start_guide.md
@@ -39,7 +39,7 @@ Sometimes you may notice that there is already good coverage in lower test level
- Take a look at the [How to test at the correct level?](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md#how-to-test-at-the-correct-level) section of the [Testing levels](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md) document
-- Look into the frequency in which such a feature is changed (_Stable features that don't change very often might not be worth covering with end-to-end tests if they're already covered in lower levels_)
+- Look into the frequency in which such a feature is changed (_Stable features that don't change very often might not be worth covering with end-to-end tests if they're already covered in lower levels_)
- Finally, discuss with the developer(s) involved in developing the feature and the tests themselves, to get their feeling
diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md
index e763a6919f7..c05d8b4b0da 100644
--- a/doc/install/aws/index.md
+++ b/doc/install/aws/index.md
@@ -353,7 +353,7 @@ TIP: **Tip:** If you do not want to maintain bastion hosts, you can set up [AWS
1. Review all your settings and, if you're happy, click **Launch**.
1. Acknowledge that you have access to an existing key pair or create a new one. Click **Launch Instance**.
-Confirm that you can SHH into the instance:
+Confirm that you can SSH into the instance:
1. On the EC2 Dashboard, click on **Instances** in the left menu.
1. Select **Bastion Host A** from your list of instances.
@@ -367,6 +367,12 @@ Confirm that you can SHH into the instance:
1. Under the **Add Tags** section, we’ll set `Key: Name` and `Value: Bastion Host B` so that we can easily identify our two instances.
1. For the security group, select the existing `bastion-sec-group` we created above.
+### Use SSH Agent Forwarding
+
+EC2 instances running Linux use private key files for SSH authentication. You'll connect to your bastion host using an SSH client and the private key file stored on your client. Since the private key file is not present on the bastion host, you will not be able to connect to your instances in private subnets.
+
+Storing private key files on your bastion host is a bad idea. To get around this, use SSH agent forwarding on your client. See [Securely Connect to Linux Instances Running in a Private Amazon VPC](https://aws.amazon.com/blogs/security/securely-connect-to-linux-instances-running-in-a-private-amazon-vpc/) for a step-by-step guide on how to use SSH agent forwarding.
+
## Deploying GitLab inside an auto scaling group
We'll use AWS's wizard to deploy GitLab and then SSH into the instance to
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 61d04258e31..c742d9ca464 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -563,7 +563,7 @@ NOTE: **Note:**
If you want to use HTTPS, see [Using HTTPS](#using-https) for the additional steps.
NOTE: **Note:**
-Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in `/etc/hosts` ("127.0.0.1 hostname"). This might be necessary, for example, if you set up GitLab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with `Check GitLab API access: FAILED. code: 401` and pushing commits will be rejected with `[remote rejected] master -> master (hook declined)`.
+Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in `/etc/hosts` ("127.0.0.1 hostname"). This might be necessary, for example, if you set up GitLab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with `Check GitLab API access: FAILED. code: 401` and pushing commits will be rejected with `[remote rejected] master -> master (hook declined)`.
NOTE: **Note:**
GitLab Shell application startup time can be greatly reduced by disabling RubyGems. This can be done in several ways:
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index cfab6352e46..a6bea3938b3 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -42,7 +42,7 @@ Please consider using a virtual machine to run GitLab.
### Ruby versions
-GitLab requires Ruby (MRI) 2.6. Beginning in GitLab 12.2, we no longer support Ruby 2.5 and lower.
+GitLab requires Ruby (MRI) 2.6. Beginning in GitLab 12.2, we no longer support Ruby 2.5 and lower.
You must use the standard MRI implementation of Ruby.
We love [JRuby](https://www.jruby.org/) and [Rubinius](https://rubinius.com), but GitLab
diff --git a/doc/integration/jira_development_panel.md b/doc/integration/jira_development_panel.md
index 2f365cc04da..8b83f885fdd 100644
--- a/doc/integration/jira_development_panel.md
+++ b/doc/integration/jira_development_panel.md
@@ -131,7 +131,7 @@ The **GitLab for Jira** App is only compatible with GitLab.com **and** Jira Clou
1. After installing, click **Get started** to go to the configurations page. This page is always available under **Jira Settings > Apps > Manage apps**.
![Start GitLab App configuration on Jira](img/jira_dev_panel_setup_com_2.png)
-1. Enter the group or personal namespace in the **Namespace** field and click **Link namespace to Jira**. Make sure you are logged in on GitLab.com and the namespace has a Silver or above license. The user setting up _GitLab for Jira_ must have **Maintainer** access to the GitLab namespace.
+1. Enter the group or personal namespace in the **Namespace** field and click **Link namespace to Jira**. Make sure you are logged in on GitLab.com and the namespace has a Silver or above license. The user setting up _GitLab for Jira_ must have **Maintainer** access to the GitLab namespace.
NOTE: **Note:**
The GitLab user only needs access when adding a new namespace. For syncing with Jira, we do not depend on the user's token.
@@ -150,7 +150,7 @@ In this case, enable cross-site cookies in your browser.
## Usage
-Once the integration is set up on GitLab and Jira you may refer any Jira issue by its ID in branch names, commit messages and merge request titles on GitLab's side,
+Once the integration is set up on GitLab and Jira you may refer any Jira issue by its ID in branch names, commit messages and merge request titles on GitLab's side,
and you will be able to see the linked `branches`, `commits`, and `merge requests` when entering a Jira issue
(inside the Jira issue, merge requests will be called "pull requests").
diff --git a/doc/integration/kerberos.md b/doc/integration/kerberos.md
index 14f3bdae864..68cf193a434 100644
--- a/doc/integration/kerberos.md
+++ b/doc/integration/kerberos.md
@@ -248,7 +248,7 @@ OmniauthKerberosSpnegoController: failed to process Negotiate/Kerberos authentic
```
This is usually seen when the browser is unable to contact the Kerberos server
-directly. It will fall back to an unsupported mechanism known as
+directly. It will fall back to an unsupported mechanism known as
[`IAKERB`](https://k5wiki.kerberos.org/wiki/Projects/IAKERB), which tries to use
the GitLab server as an intermediary to the Kerberos server.
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index fa4a5bdccdf..c08e83677ea 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -186,8 +186,11 @@ Now, it's time to add the newly created public key to your GitLab account.
1. Navigating to **SSH Keys** and pasting your **public** key from the clipboard into the **Key** field. If you:
- Created the key with a comment, this will appear in the **Title** field.
- Created the key without a comment, give your key an identifiable title like _Work Laptop_ or _Home Workstation_.
+ 1. Choose an (optional) expiry date for the key under "Expires at" section. (Introduced in [GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/issues/36243))
1. Click the **Add key** button.
+SSH keys that have "expired" using this procedure will still be valid in GitLab workflows. As the GitLab-configured expiration date is not included in the SSH key itself, you can still export public SSH keys as needed.
+
NOTE: **Note:**
If you manually copied your public SSH key make sure you copied the entire
key starting with `ssh-ed25519` (or `ssh-rsa`) and ending with your email.
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index db012ef584b..4e75102d808 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -666,7 +666,7 @@ To use Auto Deploy on a Kubernetes 1.16+ cluster, you must:
This will opt-in to using a version of the PostgreSQL chart that supports Kubernetes
1.16 and higher.
-CAUTION: **Caution:** Opting into `AUTO_DEVOPS_POSTGRES_CHANNEL` version `2` will delete
+DANGER: **Danger:** Opting into `AUTO_DEVOPS_POSTGRES_CHANNEL` version `2` will delete
the version `1` PostgreSQL database. Please backup the contents of the PostgreSQL database
first before opting into version `2`, so that you can restore into the version `2` database.
diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md
index 71863b8b643..6bc629427aa 100644
--- a/doc/user/analytics/value_stream_analytics.md
+++ b/doc/user/analytics/value_stream_analytics.md
@@ -133,12 +133,12 @@ environments is configured.
From the above example you can conclude the time it took each stage to complete
as long as their total time:
-- **Issue**: 2h (11:00 - 09:00)
-- **Plan**: 1h (12:00 - 11:00)
-- **Code**: 2h (14:00 - 12:00)
-- **Test**: 5min
+- **Issue**: 2h (11:00 - 09:00)
+- **Plan**: 1h (12:00 - 11:00)
+- **Code**: 2h (14:00 - 12:00)
+- **Test**: 5min
- **Review**: 5h (19:00 - 14:00)
-- **Staging**: 30min (19:30 - 19:00)
+- **Staging**: 30min (19:30 - 19:00)
- **Total**: Since this stage measures the sum of median time of all
previous stages, we cannot calculate it if we don't know the status of the
stages before. In case this is the very first cycle that is run in the project,
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 4382c69a9ac..da9cecf110c 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -13,6 +13,35 @@ information provided, you can immediately begin risk analysis and remediation.
For an overview of application security with GitLab, see
[Security Deep Dive](https://www.youtube.com/watch?v=k4vEJnGYy84).
+## Quick start
+
+Get started quickly with Dependency Scanning, License Scanning, and Static Application Security
+Testing (SAST) by adding the following to your `.gitlab-ci.yml`:
+
+```yaml
+include:
+ - template: Dependency-Scanning.gitlab-ci.yml
+ - template: License-Scanning.gitlab-ci.yml
+ - template: SAST.gitlab-ci.yml
+```
+
+To add Dynamic Application Security Testing (DAST) scanning, add the following to your
+`.gitlab-ci.yml` and replace `https://staging.example.com` with a staging server's web address:
+
+```yaml
+include:
+ - template: DAST.gitlab-ci.yml
+
+variables:
+ DAST_WEBSITE: https://staging.example.com
+```
+
+To ensure the DAST scanner runs *after* deploying the application to the staging server, review the [DAST full documentation](dast/index.md).
+
+To add Container Scanning, follow the steps listed in the [Container Scanning documentation](container_scanning/index.md#requirements).
+
+To further configure any of the other scanners, refer to each scanner's documentation.
+
## Security scanning tools
GitLab uses the following tools to scan and report known vulnerabilities found in your project.
diff --git a/doc/user/compliance/license_compliance/img/policies_maintainer_add_v12_9.png b/doc/user/compliance/license_compliance/img/policies_maintainer_add_v12_9.png
new file mode 100644
index 00000000000..6dc7d3a0924
--- /dev/null
+++ b/doc/user/compliance/license_compliance/img/policies_maintainer_add_v12_9.png
Binary files differ
diff --git a/doc/user/compliance/license_compliance/img/policies_maintainer_edit_v12_9.png b/doc/user/compliance/license_compliance/img/policies_maintainer_edit_v12_9.png
new file mode 100644
index 00000000000..31abbcf2d44
--- /dev/null
+++ b/doc/user/compliance/license_compliance/img/policies_maintainer_edit_v12_9.png
Binary files differ
diff --git a/doc/user/compliance/license_compliance/img/policies_v12_9.png b/doc/user/compliance/license_compliance/img/policies_v12_9.png
new file mode 100644
index 00000000000..6c6247320dc
--- /dev/null
+++ b/doc/user/compliance/license_compliance/img/policies_v12_9.png
Binary files differ
diff --git a/doc/user/compliance/license_compliance/index.md b/doc/user/compliance/license_compliance/index.md
index 03a7ffd195a..2679dc85f42 100644
--- a/doc/user/compliance/license_compliance/index.md
+++ b/doc/user/compliance/license_compliance/index.md
@@ -50,7 +50,7 @@ The following languages and package managers are supported.
| Language | Package managers | Scan Tool |
|------------|-------------------------------------------------------------------|----------------------------------------------------------|
| JavaScript | [Bower](https://bower.io/), [npm](https://www.npmjs.com/), [yarn](https://yarnpkg.com/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| Go | [Godep](https://github.com/tools/godep), go get ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), gvt ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), glide ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), dep ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), trash ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) and govendor ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), [go mod](https://github.com/golang/go/wiki/Modules) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)|
+| Go | [Godep](https://github.com/tools/godep), go get ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), gvt ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), glide ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), dep ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), trash ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) and govendor ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), [go mod](https://github.com/golang/go/wiki/Modules) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Java | [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| .NET | [Nuget](https://www.nuget.org/) (.NET Framework is supported via the [mono project](https://www.mono-project.com/). Windows specific dependencies are not supported at this time.) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Python | [pip](https://pip.pypa.io/en/stable/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
@@ -324,3 +324,19 @@ in your project's sidebar, and you'll see the licenses displayed, where:
- **Component:** The components which have this license.
![License List](img/license_list_v12_6.png)
+
+## Policies
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22465) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
+
+The **Policies** tab allows you to see your project's software license policies
+and the associated classifications for each.
+
+Policies can be configured by maintainers of the project.
+
+![Edit Policy](img/policies_maintainer_edit_v12_9.png)
+![Add Policy](img/policies_maintainer_add_v12_9.png)
+
+Developers of the project can view the policies configured in a project.
+
+![View Policies](img/policies_v12_9.png)
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index bb43cb9ed4a..8c6da131c6d 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -74,7 +74,7 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md).
| ----------- | ----------------- | ------------- |
| Artifacts maximum size (uncompressed) | 1G | 100M |
| Artifacts [expiry time](../../ci/yaml/README.md#artifactsexpire_in) | kept forever | deleted after 30 days unless otherwise specified |
-| Scheduled Pipeline Cron | `*/5 * * * *` | `*/19 * * * *` |
+| Scheduled Pipeline Cron | `*/5 * * * *` | `19 * * * *` |
| [Max jobs in active pipelines](../../administration/instance_limits.md#number-of-jobs-in-active-pipelines) | `500` for Free tier, unlimited otherwise | Unlimited
## Repository size limit
@@ -372,15 +372,6 @@ NOTE: **Note:**
The `SIDEKIQ_MEMORY_KILLER_MAX_RSS` setting is `16000000` on Sidekiq import
nodes and Sidekiq export nodes.
-## Cron jobs
-
-Periodically executed jobs by Sidekiq, to self-heal GitLab, do external
-synchronizations, run scheduled pipelines, etc.:
-
-| Setting | GitLab.com | Default |
-|-------- |------------- |------------- |
-| `pipeline_schedule_worker` | `19 * * * *` | `19 * * * *` |
-
## PostgreSQL
GitLab.com being a fairly large installation of GitLab means we have changed
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 51e687252a1..4976ca75bf7 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -170,7 +170,7 @@ Variables for Prometheus queries must be lowercase.
There are 2 methods to specify a variable in a query or dashboard:
1. Variables can be specified using the [Liquid template format](https://help.shopify.com/en/themes/liquid/basics), for example `{{ci_environment_slug}}` ([added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20793) in GitLab 12.6).
-1. You can also enclose it in quotation marks with curly braces with a leading percent, for example `"%{ci_environment_slug}"`. This method is deprecated though and support will be [removed in the next major release](https://gitlab.com/gitlab-org/gitlab/issues/37990).
+1. You can also enclose it in quotation marks with curly braces with a leading percent, for example `"%{ci_environment_slug}"`. This method is deprecated though and support will be [removed in the next major release](https://gitlab.com/gitlab-org/gitlab/issues/37990).
### Defining custom dashboards per project
diff --git a/doc/user/project/issues/design_management.md b/doc/user/project/issues/design_management.md
index f7fa8c84d9e..79ad6efd150 100644
--- a/doc/user/project/issues/design_management.md
+++ b/doc/user/project/issues/design_management.md
@@ -46,10 +46,16 @@ If the requirements are not met, the **Designs** tab displays a message to the u
Designs support short references in Markdown, but this needs to be enabled by setting
the `:design_management_reference_filter_gfm_pipeline` feature flag.
+## Supported files
+
+Files uploaded must have a file extension of either `png`, `jpg`, `jpeg`,
+`gif`, `bmp`, `tiff` or `ico`.
+
+Support for [SVG files](https://gitlab.com/gitlab-org/gitlab/issues/12771)
+and [PDFs](https://gitlab.com/gitlab-org/gitlab/issues/32811) is planned for a future release.
+
## Limitations
-- Files uploaded must have a file extension of either `png`, `jpg`, `jpeg`, `gif`, `bmp`, `tiff` or `ico`.
- The [`svg` extension is not yet supported](https://gitlab.com/gitlab-org/gitlab/issues/12771).
- Design uploads are limited to 10 files at a time.
- Design Management data
[isn't deleted when a project is destroyed](https://gitlab.com/gitlab-org/gitlab/issues/13429) yet.
@@ -77,6 +83,11 @@ of the design, and will replace the previous version.
Designs cannot be added if the issue has been moved, or its
[discussion is locked](../../discussions/#lock-discussions).
+[Introduced](https://gitlab.com/gitlab-org/gitlab/issues/34353) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.9,
+you can drag and drop designs onto the dedicated dropzone to upload them.
+
+![Drag and drop design uploads](img/design_drag_and_drop_uploads_v12_9.png)
+
### Skipped designs
Designs with the same filename as an existing uploaded design _and_ whose content has not changed will be skipped.
diff --git a/doc/user/project/issues/img/design_drag_and_drop_uploads_v12_9.png b/doc/user/project/issues/img/design_drag_and_drop_uploads_v12_9.png
new file mode 100644
index 00000000000..61ce3692808
--- /dev/null
+++ b/doc/user/project/issues/img/design_drag_and_drop_uploads_v12_9.png
Binary files differ
diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md
index a4b7f0e5fb4..ddc9e427ac2 100644
--- a/doc/user/project/merge_requests/code_quality.md
+++ b/doc/user/project/merge_requests/code_quality.md
@@ -27,6 +27,8 @@ in the merge request widget area:
![Code Quality Widget](img/code_quality.png)
+For more information, see the Code Climate list of [Supported Languages for Maintainability](https://docs.codeclimate.com/docs/supported-languages-for-maintainability).
+
## Use cases
For instance, consider the following workflow:
diff --git a/doc/user/project/releases/img/release_with_milestone_v12_9.png b/doc/user/project/releases/img/release_with_milestone_v12_9.png
new file mode 100644
index 00000000000..53100e33955
--- /dev/null
+++ b/doc/user/project/releases/img/release_with_milestone_v12_9.png
Binary files differ
diff --git a/doc/user/project/releases/img/releases_v12_9.png b/doc/user/project/releases/img/releases_v12_9.png
new file mode 100644
index 00000000000..bd23cf76651
--- /dev/null
+++ b/doc/user/project/releases/img/releases_v12_9.png
Binary files differ
diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md
index f6e35ab03db..85da9d9be43 100644
--- a/doc/user/project/releases/index.md
+++ b/doc/user/project/releases/index.md
@@ -83,10 +83,11 @@ Releases can optionally be associated with one or more
by including a `milestones` array in your requests to the
[Releases API](../../../api/releases/index.md#create-a-release).
-Releases display this association with the **Milestone** indicator near
-the top of the Release block on the **Project overview > Releases** page.
+Releases display this association with the **Milestone** indicator in the top
+section of the Release block on the **Project overview > Releases** page, along
+with some statistics about the issues in the milestone(s).
-![A Release with one associated milestone](img/release_with_milestone_v12_5.png)
+![A Release with one associated milestone](img/release_with_milestone_v12_9.png)
Below is an example of milestones with no Releases, one Release, and two
Releases, respectively.
@@ -104,7 +105,7 @@ associated with a large number of Releases.
Navigate to **Project > Releases** in order to see the list of releases for a given
project.
-![Releases list](img/releases.png)
+![Releases list](img/releases_v12_9.png)
### Number of Releases
diff --git a/lib/api/entities/ssh_key.rb b/lib/api/entities/ssh_key.rb
index 0e2f6ebae8c..aae216173c7 100644
--- a/lib/api/entities/ssh_key.rb
+++ b/lib/api/entities/ssh_key.rb
@@ -3,7 +3,7 @@
module API
module Entities
class SSHKey < Grape::Entity
- expose :id, :title, :key, :created_at
+ expose :id, :title, :key, :created_at, :expires_at
end
end
end
diff --git a/lib/gitlab/auth/key_status_checker.rb b/lib/gitlab/auth/key_status_checker.rb
new file mode 100644
index 00000000000..af654c0caad
--- /dev/null
+++ b/lib/gitlab/auth/key_status_checker.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Auth
+ class KeyStatusChecker
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :key
+
+ def initialize(key)
+ @key = key
+ end
+
+ def show_console_message?
+ console_message.present?
+ end
+
+ def console_message
+ strong_memoize(:console_message) do
+ if key.expired?
+ _('INFO: Your SSH key has expired. Please generate a new key.')
+ elsif key.expires_soon?
+ _('INFO: Your SSH key is expiring soon. Please generate a new key.')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 7a13a35c096..7361ccf4aa2 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -81,7 +81,7 @@ module Gitlab
check_push_access!
end
- success_result(cmd)
+ success_result
end
def guest_can_download_code?
@@ -119,12 +119,24 @@ module Gitlab
nil
end
- def check_for_console_messages(cmd)
+ def check_for_console_messages
+ return console_messages unless key?
+
+ key_status = Gitlab::Auth::KeyStatusChecker.new(actor)
+
+ if key_status.show_console_message?
+ console_messages.push(key_status.console_message)
+ else
+ console_messages
+ end
+ end
+
+ def console_messages
[]
end
def check_valid_actor!
- return unless actor.is_a?(Key)
+ return unless key?
unless actor.valid?
raise ForbiddenError, "Your SSH key #{actor.errors[:key].first}."
@@ -340,6 +352,10 @@ module Gitlab
actor == :ci
end
+ def key?
+ actor.is_a?(Key)
+ end
+
def can_read_project?
if deploy_key?
deploy_key.has_access_to?(project)
@@ -374,8 +390,8 @@ module Gitlab
protected
- def success_result(cmd)
- ::Gitlab::GitAccessResult::Success.new(console_messages: check_for_console_messages(cmd))
+ def success_result
+ ::Gitlab::GitAccessResult::Success.new(console_messages: check_for_console_messages)
end
def changes_list
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9684efba728..7b22fc00f24 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -333,6 +333,9 @@ msgstr ""
msgid "%{level_name} is not allowed since the fork source project has lower visibility."
msgstr ""
+msgid "%{lineOneStart}Drag and drop to upload your designs%{lineOneEnd} or %{linkStart}click to upload%{linkEnd}."
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -404,10 +407,7 @@ msgstr ""
msgid "%{screenreaderOnlyStart}Keyboard shorcuts%{screenreaderOnlyEnd} Enabled"
msgstr ""
-msgid "%{service_title} activated."
-msgstr ""
-
-msgid "%{service_title} settings saved, but not activated."
+msgid "%{service_title} %{message}."
msgstr ""
msgid "%{size} GiB"
@@ -4249,9 +4249,6 @@ msgstr ""
msgid "ClusterIntegration|Enable Cloud Run for Anthos"
msgstr ""
-msgid "ClusterIntegration|Enable Web Application Firewall"
-msgstr ""
-
msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
msgstr ""
@@ -4429,9 +4426,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about %{linkStart}ModSecurity%{linkEnd}"
-msgstr ""
-
msgid "ClusterIntegration|Learn more about %{startLink}Regions %{externalLinkIcon}%{endLink}."
msgstr ""
@@ -4564,6 +4558,9 @@ msgstr ""
msgid "ClusterIntegration|Read our %{link_start}help page%{link_end} on Kubernetes cluster integration."
msgstr ""
+msgid "ClusterIntegration|Real-time web application monitoring, logging and access control. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ClusterIntegration|Region"
msgstr ""
@@ -4711,13 +4708,13 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|Something went wrong while uninstalling %{title}"
+msgid "ClusterIntegration|Something went wrong while trying to save your settings. Please try again."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while updating Knative domain name."
+msgid "ClusterIntegration|Something went wrong while uninstalling %{title}"
msgstr ""
-msgid "ClusterIntegration|Something went wrong while updating the Web Application Firewall."
+msgid "ClusterIntegration|Something went wrong while updating Knative domain name."
msgstr ""
msgid "ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain."
@@ -5663,6 +5660,9 @@ msgstr ""
msgid "Could not save prometheus manual configuration"
msgstr ""
+msgid "Could not upload your designs as one or more files uploaded are not supported."
+msgstr ""
+
msgid "Country"
msgstr ""
@@ -6794,15 +6794,9 @@ msgstr ""
msgid "DesignManagement|The maximum number of designs allowed to be uploaded is %{upload_limit}. Please try again."
msgstr ""
-msgid "DesignManagement|The one place for your designs"
-msgstr ""
-
msgid "DesignManagement|To enable design management, you'll need to %{requirements_link_start}meet the requirements%{requirements_link_end}. If you need help, reach out to our %{support_link_start}support team%{support_link_end} for assistance."
msgstr ""
-msgid "DesignManagement|Upload and view the latest designs for this issue. Consistent and easy to find, so everyone is up to date."
-msgstr ""
-
msgid "DesignManagement|Upload skipped."
msgstr ""
@@ -7120,6 +7114,9 @@ msgstr ""
msgid "Downvotes"
msgstr ""
+msgid "Drop your designs to start your upload."
+msgstr ""
+
msgid "Due date"
msgstr ""
@@ -8215,6 +8212,9 @@ msgstr ""
msgid "Expires in %{expires_at}"
msgstr ""
+msgid "Expires:"
+msgstr ""
+
msgid "Explain the problem. If appropriate, provide a link to the relevant issue or comment."
msgstr ""
@@ -10500,6 +10500,12 @@ msgstr ""
msgid "IDE|This option is disabled because you don't have write permissions for the current branch."
msgstr ""
+msgid "INFO: Your SSH key has expired. Please generate a new key."
+msgstr ""
+
+msgid "INFO: Your SSH key is expiring soon. Please generate a new key."
+msgstr ""
+
msgid "IP Address"
msgstr ""
@@ -10767,6 +10773,9 @@ msgstr ""
msgid "Incoming email"
msgstr ""
+msgid "Incoming!"
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
@@ -12867,9 +12876,6 @@ msgstr ""
msgid "Name new label"
msgstr ""
-msgid "Name your individual key via a title"
-msgstr ""
-
msgid "Name:"
msgstr ""
@@ -13514,6 +13520,9 @@ msgstr ""
msgid "OfSearchInADropdown|Filter"
msgstr ""
+msgid "Oh no!"
+msgstr ""
+
msgid "Ok let's go"
msgstr ""
@@ -14857,12 +14866,21 @@ msgstr ""
msgid "Profiles|Enter your name, so people you know can recognize you"
msgstr ""
+msgid "Profiles|Expires at"
+msgstr ""
+
+msgid "Profiles|Expires:"
+msgstr ""
+
msgid "Profiles|Feed token was successfully reset"
msgstr ""
msgid "Profiles|Full name"
msgstr ""
+msgid "Profiles|Give your individual key a title"
+msgstr ""
+
msgid "Profiles|Impersonation"
msgstr ""
@@ -14884,6 +14902,9 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
+msgid "Profiles|Last used:"
+msgstr ""
+
msgid "Profiles|Learn more"
msgstr ""
@@ -15040,6 +15061,9 @@ msgstr ""
msgid "Profiles|Your email address was automatically set based on your %{provider_label} account"
msgstr ""
+msgid "Profiles|Your key has expired"
+msgstr ""
+
msgid "Profiles|Your location was automatically set based on your %{provider_label} account"
msgstr ""
@@ -22146,6 +22170,9 @@ msgstr ""
msgid "VisualReviewApp|%{stepStart}Step 4%{stepEnd}. Leave feedback in the Review App."
msgstr ""
+msgid "VisualReviewApp|Cancel"
+msgstr ""
+
msgid "VisualReviewApp|Copy merge request ID"
msgstr ""
@@ -22158,13 +22185,16 @@ msgstr ""
msgid "VisualReviewApp|Follow the steps below to enable Visual Reviews inside your application."
msgstr ""
+msgid "VisualReviewApp|No review app found or available."
+msgstr ""
+
msgid "VisualReviewApp|Open review app"
msgstr ""
msgid "VisualReviewApp|Review"
msgstr ""
-msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4, and 5 are performed by the reviewer each time they perform a review."
+msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
msgid "Vulnerabilities"
@@ -22682,6 +22712,9 @@ msgstr ""
msgid "You are receiving this message because you are a GitLab administrator for %{url}."
msgstr ""
+msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico."
+msgstr ""
+
msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
@@ -23203,6 +23236,9 @@ msgid_plural "about %d hours"
msgstr[0] ""
msgstr[1] ""
+msgid "activated"
+msgstr ""
+
msgid "added %{created_at_timeago}"
msgstr ""
@@ -24258,6 +24294,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "settings saved, but not activated"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
diff --git a/spec/controllers/admin/integrations_controller_spec.rb b/spec/controllers/admin/integrations_controller_spec.rb
new file mode 100644
index 00000000000..0641f64b0e3
--- /dev/null
+++ b/spec/controllers/admin/integrations_controller_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Admin::IntegrationsController do
+ let(:admin) { create(:admin) }
+ let!(:project) { create(:project) }
+
+ before do
+ sign_in(admin)
+ end
+
+ describe '#edit' do
+ context 'when instance_level_integrations not enabled' do
+ it 'returns not_found' do
+ allow(Feature).to receive(:enabled?).with(:instance_level_integrations) { false }
+
+ get :edit, params: { id: Service.available_services_names.sample }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ Service.available_services_names.each do |integration_name|
+ context "#{integration_name}" do
+ it 'successfully displays the template' do
+ get :edit, params: { id: integration_name }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:edit)
+ end
+ end
+ end
+ end
+
+ describe '#update' do
+ let(:integration) { create(:jira_service, project: project) }
+
+ before do
+ put :update, params: { id: integration.class.to_param, service: { url: url } }
+ end
+
+ context 'valid params' do
+ let(:url) { 'https://jira.gitlab-example.com' }
+
+ it 'updates the integration' do
+ expect(response).to have_gitlab_http_status(:found)
+ expect(integration.reload.url).to eq(url)
+ end
+ end
+
+ context 'invalid params' do
+ let(:url) { 'https://jira.localhost' }
+
+ it 'does not update the integration' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:edit)
+ expect(integration.reload.url).not_to eq(url)
+ end
+ end
+ end
+
+ describe '#test' do
+ context 'testable' do
+ let(:integration) { create(:jira_service, project: project) }
+
+ it 'returns ok' do
+ allow_any_instance_of(integration.class).to receive(:test) { { success: true } }
+
+ put :test, params: { id: integration.class.to_param }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'not testable' do
+ let(:integration) { create(:alerts_service, project: project) }
+
+ it 'returns not found' do
+ put :test, params: { id: integration.class.to_param }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb
index 3bed117deb0..8582ecbb06d 100644
--- a/spec/controllers/profiles/keys_controller_spec.rb
+++ b/spec/controllers/profiles/keys_controller_spec.rb
@@ -5,6 +5,22 @@ require 'spec_helper'
describe Profiles::KeysController do
let(:user) { create(:user) }
+ describe 'POST #create' do
+ before do
+ sign_in(user)
+ end
+
+ it 'creates a new key' do
+ expires_at = 3.days.from_now
+
+ expect do
+ post :create, params: { key: build(:key, expires_at: expires_at).attributes }
+ end.to change { Key.count }.by(1)
+
+ expect(Key.last.expires_at).to be_like_time(expires_at)
+ end
+ end
+
describe "#get_keys" do
describe "non existent user" do
it "does not generally work" do
diff --git a/spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js b/spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js
index e7d2b7bf5c5..beb0721260b 100644
--- a/spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js
+++ b/spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js
@@ -1,8 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import IngressModsecuritySettings from '~/clusters/components/ingress_modsecurity_settings.vue';
-import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants';
-import { GlAlert } from '@gitlab/ui';
+import { GlAlert, GlToggle } from '@gitlab/ui';
import eventHub from '~/clusters/event_hub';
const { UPDATING } = APPLICATION_STATUS;
@@ -27,32 +26,55 @@ describe('IngressModsecuritySettings', () => {
});
};
- const findSaveButton = () => wrapper.find(LoadingButton);
- const findModSecurityCheckbox = () => wrapper.find('input').element;
+ const findSaveButton = () => wrapper.find('.btn-success');
+ const findCancelButton = () => wrapper.find('[variant="secondary"]');
+ const findModSecurityToggle = () => wrapper.find(GlToggle);
describe('when ingress is installed', () => {
beforeEach(() => {
- createComponent({ installed: true });
+ createComponent({ installed: true, status: 'installed' });
jest.spyOn(eventHub, '$emit');
});
- it('renders save button', () => {
- expect(findSaveButton().exists()).toBe(true);
- expect(findModSecurityCheckbox().checked).toBe(false);
+ it('does not render save and cancel buttons', () => {
+ expect(findSaveButton().exists()).toBe(false);
+ expect(findCancelButton().exists()).toBe(false);
});
- describe('and the save changes button is clicked', () => {
+ describe('with toggle changed by the user', () => {
beforeEach(() => {
- findSaveButton().vm.$emit('click');
+ findModSecurityToggle().vm.$emit('change');
+ });
+
+ it('renders both save and cancel buttons', () => {
+ expect(findSaveButton().exists()).toBe(true);
+ expect(findCancelButton().exists()).toBe(true);
});
- it('triggers save event and pass current modsecurity value', () =>
- wrapper.vm.$nextTick().then(() => {
+ describe('and the save changes button is clicked', () => {
+ beforeEach(() => {
+ findSaveButton().vm.$emit('click');
+ });
+
+ it('triggers save event and pass current modsecurity value', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('updateApplication', {
id: INGRESS,
params: { modsecurity_enabled: false },
});
- }));
+ });
+ });
+
+ describe('and the cancel button is clicked', () => {
+ beforeEach(() => {
+ findCancelButton().vm.$emit('click');
+ });
+
+ it('triggers reset event and hides both cancel and save changes button', () => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('resetIngressModSecurityEnabled', INGRESS);
+ expect(findSaveButton().exists()).toBe(false);
+ expect(findCancelButton().exists()).toBe(false);
+ });
+ });
});
it('triggers set event to be propagated with the current modsecurity value', () => {
@@ -79,7 +101,7 @@ describe('IngressModsecuritySettings', () => {
});
it('renders save button with "Saving" label', () => {
- expect(findSaveButton().props('label')).toBe('Saving');
+ expect(findSaveButton().text()).toBe('Saving');
});
});
@@ -101,7 +123,7 @@ describe('IngressModsecuritySettings', () => {
it('does not render the save button', () => {
expect(findSaveButton().exists()).toBe(false);
- expect(findModSecurityCheckbox().checked).toBe(false);
+ expect(findModSecurityToggle().props('value')).toBe(false);
});
});
});
diff --git a/spec/frontend/clusters/services/mock_data.js b/spec/frontend/clusters/services/mock_data.js
index f0bcf5d980f..52d78ea1176 100644
--- a/spec/frontend/clusters/services/mock_data.js
+++ b/spec/frontend/clusters/services/mock_data.js
@@ -20,6 +20,7 @@ const CLUSTERS_MOCK_DATA = {
external_ip: null,
external_hostname: null,
can_uninstall: false,
+ modsecurity_enabled: false,
},
{
name: 'runner',
diff --git a/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap b/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap
index 2f4c114dd3d..b1a718d58b5 100644
--- a/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap
+++ b/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap
@@ -7,6 +7,9 @@ exports[`JumpToNextDiscussionButton matches the snapshot 1`] = `
>
<button
class="btn btn-default discussion-next-btn"
+ data-track-event="click_button"
+ data-track-label="mr_next_unresolved_thread"
+ data-track-property="click_next_unresolved_thread"
title="Jump to next unresolved thread"
>
<icon-stub
diff --git a/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js b/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js
index 2ff9dbc5c19..183966cf435 100644
--- a/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js
+++ b/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js
@@ -1,14 +1,21 @@
import { shallowMount } from '@vue/test-utils';
import JumpToNextDiscussionButton from '~/notes/components/discussion_jump_to_next_button.vue';
+import { mockTracking } from '../../helpers/tracking_helper';
describe('JumpToNextDiscussionButton', () => {
- let wrapper;
const fromDiscussionId = 'abc123';
+ let wrapper;
+ let trackingSpy;
+ let jumpFn;
beforeEach(() => {
+ jumpFn = jest.fn();
wrapper = shallowMount(JumpToNextDiscussionButton, {
propsData: { fromDiscussionId },
});
+ wrapper.setMethods({ jumpToNextRelativeDiscussion: jumpFn });
+
+ trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
});
afterEach(() => {
@@ -20,9 +27,17 @@ describe('JumpToNextDiscussionButton', () => {
});
it('calls jumpToNextRelativeDiscussion when clicked', () => {
- const jumpToNextRelativeDiscussion = jest.fn();
- wrapper.setMethods({ jumpToNextRelativeDiscussion });
wrapper.find({ ref: 'button' }).trigger('click');
- expect(jumpToNextRelativeDiscussion).toHaveBeenCalledWith(fromDiscussionId);
+
+ expect(jumpFn).toHaveBeenCalledWith(fromDiscussionId);
+ });
+
+ it('sends the correct tracking event when clicked', () => {
+ wrapper.find({ ref: 'button' }).trigger('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_button', {
+ label: 'mr_next_unresolved_thread',
+ property: 'click_next_unresolved_thread',
+ });
});
});
diff --git a/spec/lib/gitlab/auth/key_status_checker_spec.rb b/spec/lib/gitlab/auth/key_status_checker_spec.rb
new file mode 100644
index 00000000000..b1a540eae81
--- /dev/null
+++ b/spec/lib/gitlab/auth/key_status_checker_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Auth::KeyStatusChecker do
+ let_it_be(:never_expires_key) { build(:personal_key, expires_at: nil) }
+ let_it_be(:expired_key) { build(:personal_key, expires_at: 3.days.ago) }
+ let_it_be(:expiring_soon_key) { build(:personal_key, expires_at: 3.days.from_now) }
+ let_it_be(:expires_in_future_key) { build(:personal_key, expires_at: 14.days.from_now) }
+
+ let(:key_status_checker) { described_class.new(key) }
+
+ describe '#show_console_message?' do
+ subject { key_status_checker.show_console_message? }
+
+ context 'for an expired key' do
+ let(:key) { expired_key }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'for a key expiring in the next 7 days' do
+ let(:key) { expiring_soon_key }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'for a key expiring after the next 7 days' do
+ let(:key) { expires_in_future_key }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'for a key that never expires' do
+ let(:key) { never_expires_key }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#console_message' do
+ subject { key_status_checker.console_message }
+
+ context 'for an expired key' do
+ let(:key) { expired_key }
+
+ it { is_expected.to eq('INFO: Your SSH key has expired. Please generate a new key.') }
+ end
+
+ context 'for a key expiring in the next 7 days' do
+ let(:key) { expiring_soon_key }
+
+ it { is_expected.to eq('INFO: Your SSH key is expiring soon. Please generate a new key.') }
+ end
+
+ context 'for a key expiring after the next 7 days' do
+ let(:key) { expires_in_future_key }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'for a key that never expires' do
+ let(:key) { never_expires_key }
+
+ it { is_expected.to be_nil }
+ end
+ end
+end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index 754e26dcec8..b1dd8ede3eb 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -177,6 +177,7 @@ describe Clusters::Applications::Ingress do
context 'when modsecurity_enabled is disabled' do
before do
allow(subject).to receive(:cluster).and_return(cluster)
+ allow(subject).to receive(:modsecurity_enabled).and_return(false)
end
it 'excludes modsecurity module enablement' do
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 95513776f39..be592ac6a5c 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -575,30 +575,35 @@ describe API::Internal::Base do
project.add_developer(user)
end
- context "git pull" do
- context "with no console message" do
- it "has the correct payload" do
+ context 'git pull' do
+ context 'with a key that has expired' do
+ let(:key) { create(:key, user: user, expires_at: 2.days.ago) }
+
+ it 'includes the `key expired` message in the response' do
pull(key, project)
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['gl_console_messages']).to eq([])
+ expect(json_response['gl_console_messages']).to eq(['INFO: Your SSH key has expired. Please generate a new key.'])
end
end
- context "with a console message" do
- let(:console_messages) { ['message for the console'] }
+ context 'with a key that will expire in the next 7 days' do
+ let(:key) { create(:key, user: user, expires_at: 2.days.from_now) }
- it "has the correct payload" do
- expect_next_instance_of(Gitlab::GitAccess) do |access|
- expect(access).to receive(:check_for_console_messages)
- .with('git-upload-pack')
- .and_return(console_messages)
- end
+ it 'includes the `key expiring soon` message in the response' do
+ pull(key, project)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['gl_console_messages']).to eq(['INFO: Your SSH key is expiring soon. Please generate a new key.'])
+ end
+ end
+
+ context 'with a key that has no expiry' do
+ it 'does not include any message in the response' do
pull(key, project)
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['gl_console_messages']).to eq(console_messages)
+ expect(json_response['gl_console_messages']).to eq([])
end
end
end
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
index df0bae603b1..089ee22982c 100644
--- a/spec/requests/api/keys_spec.rb
+++ b/spec/requests/api/keys_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe API::Keys do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
- let(:key) { create(:key, user: user) }
+ let(:key) { create(:key, user: user, expires_at: 1.day.from_now) }
let(:email) { create(:email, user: user) }
describe 'GET /keys/:uid' do
@@ -28,6 +28,7 @@ describe API::Keys do
get api("/keys/#{key.id}", admin)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['title']).to eq(key.title)
+ expect(Time.parse(json_response['expires_at'])).to be_like_time(key.expires_at)
expect(json_response['user']['id']).to eq(user.id)
expect(json_response['user']['username']).to eq(user.username)
end