Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-06-14 21:10:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-14 21:10:28 +0300
commitf69bc1dab50e86440bb4ffdc507ca5efd94bf459 (patch)
tree8b4d3da3106fee1271eece2dad1cc5e14052542b
parent81f257d72ef398933453d215019c850f57dae252 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_manual_todo.yml9
-rw-r--r--README.md2
-rw-r--r--app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js4
-rw-r--r--app/assets/javascripts/packages/details/components/app.vue15
-rw-r--r--app/assets/javascripts/packages/details/components/installation_commands.vue4
-rw-r--r--app/assets/javascripts/packages/shared/constants.js3
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/components/details_title.vue82
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/components/terraform_installation.vue66
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/details_app_bundle.js3
-rw-r--r--app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue4
-rw-r--r--app/controllers/concerns/membership_actions.rb6
-rw-r--r--app/controllers/groups/group_members_controller.rb4
-rw-r--r--app/controllers/projects/project_members_controller.rb4
-rw-r--r--app/controllers/users_controller.rb10
-rw-r--r--app/models/concerns/packages/debian/distribution.rb8
-rw-r--r--app/models/merge_request_diff.rb18
-rw-r--r--app/services/merge_requests/base_service.rb6
-rw-r--r--app/services/packages/debian/create_distribution_service.rb21
-rw-r--r--app/services/packages/debian/destroy_distribution_service.rb33
-rw-r--r--app/services/packages/debian/update_distribution_service.rb10
-rw-r--r--app/services/packages/nuget/metadata_extraction_service.rb16
-rw-r--r--app/views/projects/packages/infrastructure_registry/show.html.haml2
-rw-r--r--app/views/shared/_search_settings.html.haml2
-rw-r--r--config/feature_flags/development/packages_nuget_archive_new_file_reader.yml8
-rw-r--r--config/routes/user.rb1
-rw-r--r--db/migrate/20210604082145_create_external_status_checks_table.rb42
-rw-r--r--db/migrate/20210604085600_rename_status_check_responses_approval_rule.rb34
-rw-r--r--db/migrate/20210609192728_add_status_check_foreign_key_to_external_status_check_id.rb17
-rw-r--r--db/migrate/20210609193101_add_status_check_foreign_key_to_protected_branch_id.rb17
-rw-r--r--db/schema_migrations/202106040821451
-rw-r--r--db/schema_migrations/202106040856001
-rw-r--r--db/schema_migrations/202106091927281
-rw-r--r--db/schema_migrations/202106091931011
-rw-r--r--db/structure.sql75
-rw-r--r--doc/administration/geo/index.md3
-rw-r--r--doc/administration/geo/setup/database.md5
-rw-r--r--doc/administration/gitaly/faq.md90
-rw-r--r--doc/administration/gitaly/index.md97
-rw-r--r--doc/administration/gitaly/praefect.md11
-rw-r--r--doc/administration/integration/plantuml.md2
-rw-r--r--doc/administration/logs.md36
-rw-r--r--doc/administration/nfs.md5
-rw-r--r--doc/administration/reference_architectures/troubleshooting.md207
-rw-r--r--doc/api/merge_request_approvals.md114
-rw-r--r--doc/api/status_checks.md104
-rw-r--r--doc/ci/jobs/job_control.md113
-rw-r--r--doc/ci/variables/README.md148
-rw-r--r--doc/ci/variables/where_variables_can_be_used.md2
-rw-r--r--doc/ci/yaml/README.md8
-rw-r--r--doc/development/polymorphic_associations.md14
-rw-r--r--doc/development/usage_ping/dictionary.md8
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/install/requirements.md1
-rw-r--r--doc/integration/sourcegraph.md3
-rw-r--r--doc/push_rules/push_rules.md19
-rw-r--r--doc/update/upgrading_from_source.md2
-rw-r--r--doc/user/admin_area/diff_limits.md3
-rw-r--r--doc/user/admin_area/merge_requests_approvals.md4
-rw-r--r--doc/user/admin_area/settings/account_and_limit_settings.md59
-rw-r--r--doc/user/admin_area/settings/index.md18
-rw-r--r--doc/user/admin_area/settings/instance_template_repository.md13
-rw-r--r--doc/user/admin_area/settings/push_event_activities_limit.md13
-rw-r--r--doc/user/admin_area/settings/visibility_and_access_controls.md5
-rw-r--r--doc/user/group/saml_sso/img/member_enterprise_badge_v14_0.pngbin0 -> 31502 bytes
-rw-r--r--doc/user/group/saml_sso/scim_setup.md4
-rw-r--r--doc/user/packages/terraform_module_registry/index.md14
-rw-r--r--doc/user/project/merge_requests/approvals/index.md30
-rw-r--r--doc/user/project/repository/branches/default.md3
-rw-r--r--doc/user/project/web_ide/index.md2
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/concerns/packages/debian_distribution_endpoints.rb152
-rw-r--r--lib/api/concerns/packages/debian_package_endpoints.rb (renamed from lib/api/concerns/packages/debian_endpoints.rb)34
-rw-r--r--lib/api/debian_group_packages.rb10
-rw-r--r--lib/api/debian_project_packages.rb22
-rw-r--r--lib/api/entities/packages/debian/distribution.rb23
-rw-r--r--lib/api/project_debian_distributions.rb37
-rw-r--r--lib/gitlab/database.rb2
-rw-r--r--lib/gitlab/database/load_balancing/connection_proxy.rb1
-rw-r--r--lib/gitlab/diff/file_collection/base.rb15
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff_batch.rb24
-rw-r--r--locale/gitlab.pot21
-rw-r--r--spec/db/schema_spec.rb1
-rw-r--r--spec/frontend/editor/editor_ci_schema_ext_spec.js7
-rw-r--r--spec/frontend/members/mock_data.js1
-rw-r--r--spec/frontend/packages/details/components/app_spec.js5
-rw-r--r--spec/frontend/packages/details/components/installations_commands_spec.js4
-rw-r--r--spec/frontend/packages/mock_data.js14
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/__snapshots__/terraform_installation_spec.js.snap44
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/details_title_spec.js93
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/terraform_installation_spec.js61
-rw-r--r--spec/frontend/pipeline_editor/components/editor/text_editor_spec.js2
-rw-r--r--spec/lib/gitlab/database_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/models/merge_request_diff_spec.rb55
-rw-r--r--spec/requests/api/debian_group_packages_spec.rb14
-rw-r--r--spec/requests/api/debian_project_packages_spec.rb14
-rw-r--r--spec/requests/api/project_debian_distributions_spec.rb66
-rw-r--r--spec/requests/users_controller_spec.rb42
-rw-r--r--spec/services/packages/debian/create_distribution_service_spec.rb9
-rw-r--r--spec/services/packages/debian/destroy_distribution_service_spec.rb78
-rw-r--r--spec/services/packages/debian/update_distribution_service_spec.rb2
-rw-r--r--spec/services/packages/nuget/metadata_extraction_service_spec.rb17
-rw-r--r--spec/services/packages/nuget/update_package_from_metadata_service_spec.rb279
-rw-r--r--spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb211
104 files changed, 1963 insertions, 1096 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index b88ccbabdea..be5f70a3c03 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -375,7 +375,6 @@ RSpec/EmptyLineAfterFinalLetItBe:
- ee/spec/requests/api/commits_spec.rb
- ee/spec/requests/api/dora/metrics_spec.rb
- ee/spec/requests/api/epics_spec.rb
- - ee/spec/requests/api/external_approval_rules_spec.rb
- ee/spec/requests/api/geo_spec.rb
- ee/spec/requests/api/graphql/boards/epic_board_list_epics_query_spec.rb
- ee/spec/requests/api/graphql/current_user/todos_query_spec.rb
@@ -439,9 +438,9 @@ RSpec/EmptyLineAfterFinalLetItBe:
- ee/spec/services/dast_scanner_profiles/destroy_service_spec.rb
- ee/spec/services/dast_scanner_profiles/update_service_spec.rb
- ee/spec/services/dast_site_profiles/destroy_service_spec.rb
- - ee/spec/services/external_approval_rules/create_service_spec.rb
- - ee/spec/services/external_approval_rules/destroy_service_spec.rb
- - ee/spec/services/external_approval_rules/update_service_spec.rb
+ - ee/spec/services/external_status_checks/create_service_spec.rb
+ - ee/spec/services/external_status_checks/destroy_service_spec.rb
+ - ee/spec/services/external_status_checks/update_service_spec.rb
- ee/spec/services/gitlab_subscriptions/activate_service_spec.rb
- ee/spec/services/gitlab_subscriptions/apply_trial_service_spec.rb
- ee/spec/services/incident_management/incidents/upload_metric_service_spec.rb
@@ -2829,7 +2828,7 @@ Gitlab/FeatureAvailableUsage:
- 'ee/app/views/shared/promotions/_promote_repository_features.html.haml'
- 'ee/app/workers/analytics/code_review_metrics_worker.rb'
- 'ee/app/workers/group_saml_group_sync_worker.rb'
- - 'ee/lib/api/external_approval_rules.rb'
+ - 'ee/lib/api/external_status_checks.rb'
- 'ee/lib/ee/api/entities/approval_state.rb'
- 'ee/lib/ee/api/entities/board.rb'
- 'ee/lib/ee/api/entities/issue.rb'
diff --git a/README.md b/README.md
index caafa30ead5..05193e71c7e 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ GitLab is a Ruby on Rails application that runs on the following software:
- Ruby (MRI) 2.7.2
- Git 2.31+
- Redis 5.0+
-- PostgreSQL 11+
+- PostgreSQL 12+
For more information please see the [architecture](https://docs.gitlab.com/ee/development/architecture.html) and [requirements](https://docs.gitlab.com/ee/install/requirements.html) documentation.
diff --git a/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js b/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js
index ae6c89d3942..c5ee61ec86e 100644
--- a/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js
+++ b/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js
@@ -14,9 +14,9 @@ export class CiSchemaExtension extends EditorLiteExtension {
* @param {Object} opts
* @param {String} opts.projectNamespace
* @param {String} opts.projectPath
- * @param {String?} opts.ref - Current ref. Defaults to master
+ * @param {String?} opts.ref - Current ref. Defaults to main
*/
- registerCiSchema({ projectNamespace, projectPath, ref = 'master' } = {}) {
+ registerCiSchema({ projectNamespace, projectPath, ref } = {}) {
const ciSchemaPath = Api.buildUrl(Api.projectFileSchemaPath)
.replace(':namespace_path', projectNamespace)
.replace(':project_path', projectPath)
diff --git a/app/assets/javascripts/packages/details/components/app.vue b/app/assets/javascripts/packages/details/components/app.vue
index 9d7ba83c001..55ffe10a608 100644
--- a/app/assets/javascripts/packages/details/components/app.vue
+++ b/app/assets/javascripts/packages/details/components/app.vue
@@ -24,7 +24,6 @@ import DependencyRow from './dependency_row.vue';
import InstallationCommands from './installation_commands.vue';
import PackageFiles from './package_files.vue';
import PackageHistory from './package_history.vue';
-import PackageTitle from './package_title.vue';
export default {
name: 'PackagesApp',
@@ -36,7 +35,9 @@ export default {
GlTab,
GlTabs,
GlSprintf,
- PackageTitle,
+ PackageTitle: () => import('./package_title.vue'),
+ TerraformTitle: () =>
+ import('~/packages_and_registries/infrastructure_registry/components/details_title.vue'),
PackagesListLoader,
PackageListRow,
DependencyRow,
@@ -50,6 +51,12 @@ export default {
GlModal: GlModalDirective,
},
mixins: [Tracking.mixin()],
+ inject: {
+ titleComponent: {
+ default: 'PackageTitle',
+ from: 'titleComponent',
+ },
+ },
trackingActions: { ...TrackingActions },
data() {
return {
@@ -160,7 +167,7 @@ export default {
/>
<div v-else class="packages-app">
- <package-title>
+ <component :is="titleComponent">
<template #delete-button>
<gl-button
v-if="canDelete"
@@ -173,7 +180,7 @@ export default {
{{ __('Delete') }}
</gl-button>
</template>
- </package-title>
+ </component>
<gl-tabs>
<gl-tab :title="__('Detail')">
diff --git a/app/assets/javascripts/packages/details/components/installation_commands.vue b/app/assets/javascripts/packages/details/components/installation_commands.vue
index 28978913e6e..ed55d7fe782 100644
--- a/app/assets/javascripts/packages/details/components/installation_commands.vue
+++ b/app/assets/javascripts/packages/details/components/installation_commands.vue
@@ -1,5 +1,6 @@
<script>
-import { PackageType } from '../../shared/constants';
+import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
+import { PackageType, TERRAFORM_PACKAGE_TYPE } from '../../shared/constants';
import ComposerInstallation from './composer_installation.vue';
import ConanInstallation from './conan_installation.vue';
import MavenInstallation from './maven_installation.vue';
@@ -16,6 +17,7 @@ export default {
[PackageType.NUGET]: NugetInstallation,
[PackageType.PYPI]: PypiInstallation,
[PackageType.COMPOSER]: ComposerInstallation,
+ [TERRAFORM_PACKAGE_TYPE]: TerraformInstallation,
},
props: {
packageEntity: {
diff --git a/app/assets/javascripts/packages/shared/constants.js b/app/assets/javascripts/packages/shared/constants.js
index 67bf2636f43..0ef6a3d0d12 100644
--- a/app/assets/javascripts/packages/shared/constants.js
+++ b/app/assets/javascripts/packages/shared/constants.js
@@ -11,6 +11,9 @@ export const PackageType = {
GENERIC: 'generic',
};
+// we want this separated from the main dictionary to avoid it being pulled in the search of package
+export const TERRAFORM_PACKAGE_TYPE = 'terraform_module';
+
export const TrackingActions = {
DELETE_PACKAGE: 'delete_package',
REQUEST_DELETE_PACKAGE: 'request_delete_package',
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/components/details_title.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/components/details_title.vue
new file mode 100644
index 00000000000..3e551706ed0
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/components/details_title.vue
@@ -0,0 +1,82 @@
+<script>
+import { GlIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
+import { mapState, mapGetters } from 'vuex';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { __ } from '~/locale';
+import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
+import TitleArea from '~/vue_shared/components/registry/title_area.vue';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+
+export default {
+ name: 'DetailsTitle',
+ components: {
+ TitleArea,
+ GlIcon,
+ GlSprintf,
+ MetadataItem,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ mixins: [timeagoMixin],
+ i18n: {
+ packageInfo: __('v%{version} published %{timeAgo}'),
+ },
+ computed: {
+ ...mapState(['packageEntity', 'packageFiles']),
+ ...mapGetters(['packagePipeline']),
+ totalSize() {
+ return numberToHumanSize(this.packageFiles.reduce((acc, p) => acc + p.size, 0));
+ },
+ },
+ methods: {
+ dynamicSlotName(index) {
+ return `metadata-tag${index}`;
+ },
+ },
+};
+</script>
+
+<template>
+ <title-area :title="packageEntity.name" data-qa-selector="package_title">
+ <template #sub-header>
+ <gl-icon name="eye" class="gl-mr-3" />
+ <gl-sprintf :message="$options.i18n.packageInfo">
+ <template #version>
+ {{ packageEntity.version }}
+ </template>
+
+ <template #timeAgo>
+ <span v-gl-tooltip :title="tooltipTitle(packageEntity.created_at)">
+ &nbsp;{{ timeFormatted(packageEntity.created_at) }}
+ </span>
+ </template>
+ </gl-sprintf>
+ </template>
+
+ <template #metadata-type>
+ <metadata-item data-testid="package-type" icon="infrastructure-registry" text="Terraform" />
+ </template>
+
+ <template #metadata-size>
+ <metadata-item data-testid="package-size" icon="disk" :text="totalSize" />
+ </template>
+
+ <template v-if="packagePipeline" #metadata-pipeline>
+ <metadata-item
+ data-testid="pipeline-project"
+ icon="review-list"
+ :text="packagePipeline.project.name"
+ :link="packagePipeline.project.web_url"
+ />
+ </template>
+
+ <template v-if="packagePipeline" #metadata-ref>
+ <metadata-item data-testid="package-ref" icon="branch" :text="packagePipeline.ref" />
+ </template>
+
+ <template #right-actions>
+ <slot name="delete-button"></slot>
+ </template>
+ </title-area>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/components/terraform_installation.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/components/terraform_installation.vue
new file mode 100644
index 00000000000..399a3e086f1
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/components/terraform_installation.vue
@@ -0,0 +1,66 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { mapState } from 'vuex';
+import { s__ } from '~/locale';
+import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+export default {
+ name: 'ConanInstallation',
+ components: {
+ CodeInstruction,
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['packageEntity', 'terraformHelpPath', 'projectPath']),
+ provisionInstructions() {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ return `module "${this.packageEntity.name}" {
+ source = "${this.projectPath}/${this.packageEntity.name}"
+ version = "${this.packageEntity.version}"
+}`;
+ },
+ registrySetup() {
+ return `credentials "gitlab.com" {
+ token = "<TOKEN>"
+}`;
+ },
+ },
+ i18n: {
+ helpText: s__(
+ 'InfrastructureRegistry|For more information on the Terraform registry, %{linkStart}see our documentation%{linkEnd}.',
+ ),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <h3 class="gl-font-lg">{{ __('Provision instructions') }}</h3>
+
+ <code-instruction
+ :label="
+ s__(
+ 'InfrastructureRegistry|Copy and paste into your Terraform configuration, insert the variables, and run Terraform init:',
+ )
+ "
+ :instruction="provisionInstructions"
+ :copy-text="s__('InfrastructureRegistry|Copy Terraform Command')"
+ multiline
+ />
+
+ <h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
+
+ <code-instruction
+ :label="s__('InfrastructureRegistry|To authorize access to the Terraform registry:')"
+ :instruction="registrySetup"
+ :copy-text="s__('InfrastructureRegistry|Copy Terraform Setup Command')"
+ multiline
+ />
+ <gl-sprintf :message="$options.i18n.helpText">
+ <template #link="{ content }">
+ <gl-link :href="terraformHelpPath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details_app_bundle.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details_app_bundle.js
index b6c49a10ee8..98942b1e578 100644
--- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details_app_bundle.js
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details_app_bundle.js
@@ -22,6 +22,9 @@ export default () => {
return new Vue({
el,
store,
+ provide: {
+ titleComponent: 'TerraformTitle',
+ },
render(createElement) {
return createElement(PackagesApp);
},
diff --git a/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue b/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue
index d39c67669a8..d373f74a5c4 100644
--- a/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue
+++ b/app/assets/javascripts/pipeline_editor/components/editor/text_editor.vue
@@ -10,7 +10,7 @@ export default {
EditorLite,
},
mixins: [glFeatureFlagMixin()],
- inject: ['ciConfigPath', 'projectPath', 'projectNamespace'],
+ inject: ['ciConfigPath', 'projectPath', 'projectNamespace', 'defaultBranch'],
inheritAttrs: false,
data() {
return {
@@ -34,7 +34,7 @@ export default {
editorInstance.registerCiSchema({
projectPath: this.projectPath,
projectNamespace: this.projectNamespace,
- ref: this.commitSha,
+ ref: this.commitSha || this.defaultBranch,
});
}
},
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index 1d548fd4f85..565484495ba 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -6,7 +6,7 @@ module MembershipActions
def create
create_params = params.permit(:user_ids, :access_level, :expires_at)
- result = Members::CreateService.new(current_user, create_params.merge({ source: membershipable, invite_source: "#{source_type}-members-page" })).execute
+ result = Members::CreateService.new(current_user, create_params.merge({ source: membershipable, invite_source: "#{plain_source_type}-members-page" })).execute
if result[:status] == :success
redirect_to members_page_url, notice: _('Users were successfully added.')
@@ -175,6 +175,10 @@ module MembershipActions
end
end
+ def plain_source_type
+ raise NotImplementedError
+ end
+
def requested_relations
case params[:with_inherited_permissions].presence
when 'exclude'
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index fa0737d5b13..f3b1d71d06e 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -78,6 +78,10 @@ class Groups::GroupMembersController < Groups::ApplicationController
def membershipable_members
group.members
end
+
+ def plain_source_type
+ 'group'
+ end
end
Groups::GroupMembersController.prepend_mod_with('Groups::GroupMembersController')
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index cc2157a7d51..4f342980bf0 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -62,6 +62,10 @@ class Projects::ProjectMembersController < Projects::ApplicationController
def membershipable_members
project.members
end
+
+ def plain_source_type
+ 'project'
+ end
end
Projects::ProjectMembersController.prepend_mod_with('Projects::ProjectMembersController')
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 287ee2d5ab8..7282fc26121 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -20,7 +20,7 @@ class UsersController < ApplicationController
skip_before_action :authenticate_user!
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
- before_action :user, except: [:exists, :suggests, :ssh_keys]
+ before_action :user, except: [:exists, :ssh_keys]
before_action :authorize_read_user_profile!,
only: [:calendar, :calendar_activities, :groups, :projects, :contributed, :starred, :snippets, :followers, :following]
@@ -153,14 +153,6 @@ class UsersController < ApplicationController
render json: { exists: !!Namespace.find_by_path_or_name(params[:username]) }
end
- def suggests
- namespace_path = params[:username]
- exists = !!Namespace.find_by_path_or_name(namespace_path)
- suggestions = exists ? [Namespace.clean_path(namespace_path)] : []
-
- render json: { exists: exists, suggests: suggestions }
- end
-
def follow
current_user.follow(user)
diff --git a/app/models/concerns/packages/debian/distribution.rb b/app/models/concerns/packages/debian/distribution.rb
index ec69ff830d8..159f0044c82 100644
--- a/app/models/concerns/packages/debian/distribution.rb
+++ b/app/models/concerns/packages/debian/distribution.rb
@@ -95,6 +95,14 @@ module Packages
mount_file_store_uploader Packages::Debian::DistributionReleaseFileUploader
+ def component_names
+ components.pluck(:name).sort
+ end
+
+ def architecture_names
+ architectures.pluck(:name).sort
+ end
+
def needs_update?
!file.exists? || time_duration_expired?
end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 2dc6796732f..f58d7788432 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -389,11 +389,23 @@ class MergeRequestDiff < ApplicationRecord
def diffs_in_batch(batch_page, batch_size, diff_options:)
fetching_repository_diffs(diff_options) do |comparison|
+ reorder_diff_files!
+ diffs_batch = diffs_in_batch_collection(batch_page, batch_size, diff_options: diff_options)
+
if comparison
- comparison.diffs_in_batch(batch_page, batch_size, diff_options: diff_options)
+ if diff_options[:paths].blank? && !without_files?
+ # Return the empty MergeRequestDiffBatch for an out of bound batch request
+ break diffs_batch if diffs_batch.diff_file_paths.blank?
+
+ diff_options.merge!(
+ paths: diffs_batch.diff_file_paths,
+ pagination_data: diffs_batch.pagination_data
+ )
+ end
+
+ comparison.diffs(diff_options)
else
- reorder_diff_files!
- diffs_in_batch_collection(batch_page, batch_size, diff_options: diff_options)
+ diffs_batch
end
end
end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 1c135330777..7ebdbf94932 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -24,9 +24,15 @@ module MergeRequests
merge_request.project.execute_hooks(merge_data, :merge_request_hooks)
merge_request.project.execute_services(merge_data, :merge_request_hooks)
+ execute_external_hooks(merge_request, merge_data)
+
enqueue_jira_connect_messages_for(merge_request)
end
+ def execute_external_hooks(merge_request, merge_data)
+ # Implemented in EE
+ end
+
def handle_changes(merge_request, options)
old_associations = options.fetch(:old_associations, {})
old_assignees = old_associations.fetch(:assignees, [])
diff --git a/app/services/packages/debian/create_distribution_service.rb b/app/services/packages/debian/create_distribution_service.rb
index f947d2e4293..b4b1ec952ef 100644
--- a/app/services/packages/debian/create_distribution_service.rb
+++ b/app/services/packages/debian/create_distribution_service.rb
@@ -38,14 +38,19 @@ module Packages
append_errors(distribution)
return error unless errors.empty?
- distribution.transaction do
- if distribution.save
- create_components
- create_architectures
-
- success
- end
- end || error
+ result = distribution.transaction do
+ next unless distribution.save
+
+ create_components
+ create_architectures
+ success
+ end
+
+ result ||= error
+
+ ::Packages::Debian::GenerateDistributionWorker.perform_async(distribution.class.container_type, distribution.reset.id) if result.success?
+
+ result
end
def create_components
diff --git a/app/services/packages/debian/destroy_distribution_service.rb b/app/services/packages/debian/destroy_distribution_service.rb
deleted file mode 100644
index bef1127fece..00000000000
--- a/app/services/packages/debian/destroy_distribution_service.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module Packages
- module Debian
- class DestroyDistributionService
- def initialize(distribution)
- @distribution = distribution
- end
-
- def execute
- destroy_distribution
- end
-
- private
-
- def destroy_distribution
- if @distribution.destroy
- success
- else
- error("Unable to destroy Debian #{@distribution.model_name.human.downcase}")
- end
- end
-
- def success
- ServiceResponse.success
- end
-
- def error(message)
- ServiceResponse.error(message: message, payload: { distribution: @distribution })
- end
- end
- end
-end
diff --git a/app/services/packages/debian/update_distribution_service.rb b/app/services/packages/debian/update_distribution_service.rb
index 95face912d5..218167ecdc5 100644
--- a/app/services/packages/debian/update_distribution_service.rb
+++ b/app/services/packages/debian/update_distribution_service.rb
@@ -31,7 +31,7 @@ module Packages
end
def update_distribution
- distribution.transaction do
+ result = distribution.transaction do
if distribution.update(params)
update_components if components
update_architectures if architectures
@@ -41,7 +41,13 @@ module Packages
append_errors(distribution)
error
end
- end || error
+ end
+
+ result ||= error
+
+ ::Packages::Debian::GenerateDistributionWorker.perform_async(distribution.class.container_type, distribution.id) if result.success?
+
+ result
end
def update_components
diff --git a/app/services/packages/nuget/metadata_extraction_service.rb b/app/services/packages/nuget/metadata_extraction_service.rb
index 6a0721b4884..63da98dde43 100644
--- a/app/services/packages/nuget/metadata_extraction_service.rb
+++ b/app/services/packages/nuget/metadata_extraction_service.rb
@@ -105,22 +105,6 @@ module Packages
end
def with_zip_file(&block)
- if ::Feature.enabled?(:packages_nuget_archive_new_file_reader, project, default_enabled: :yaml)
- with_new_file_reader(&block)
- else
- with_legacy_file_reader(&block)
- end
- end
-
- def with_legacy_file_reader
- package_file.file.use_file do |file_path|
- Zip::File.open(file_path) do |zip_file|
- yield(zip_file)
- end
- end
- end
-
- def with_new_file_reader
package_file.file.use_open_file do |open_file|
zip_file = Zip::File.new(open_file, false, true)
yield(zip_file)
diff --git a/app/views/projects/packages/infrastructure_registry/show.html.haml b/app/views/projects/packages/infrastructure_registry/show.html.haml
index a36dd685f45..1d826466aad 100644
--- a/app/views/projects/packages/infrastructure_registry/show.html.haml
+++ b/app/views/projects/packages/infrastructure_registry/show.html.haml
@@ -10,4 +10,6 @@
can_delete: can?(current_user, :destroy_package, @project).to_s,
svg_path: image_path('illustrations/no-packages.svg'),
project_name: @project.name,
+ project_path: expose_url(@project.full_path),
+ terraform_help_path: help_page_path('user/infrastructure/index'),
project_list_url: project_infrastructure_registry_index_path(@project)} }
diff --git a/app/views/shared/_search_settings.html.haml b/app/views/shared/_search_settings.html.haml
index 2974b2bf4d0..7265f090967 100644
--- a/app/views/shared/_search_settings.html.haml
+++ b/app/views/shared/_search_settings.html.haml
@@ -1,3 +1,5 @@
+- return if @hide_search_settings
+
- container_class = local_assigns.fetch(:container_class, 'gl-mt-5')
%div{ class: container_class }
diff --git a/config/feature_flags/development/packages_nuget_archive_new_file_reader.yml b/config/feature_flags/development/packages_nuget_archive_new_file_reader.yml
deleted file mode 100644
index 33ad84dc797..00000000000
--- a/config/feature_flags/development/packages_nuget_archive_new_file_reader.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: packages_nuget_archive_new_file_reader
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62471
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331799
-milestone: '13.13'
-type: development
-group: group::package
-default_enabled: false
diff --git a/config/routes/user.rb b/config/routes/user.rb
index 41319b6d730..5f746eb6670 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -49,7 +49,6 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d
get :followers
get :following
get :exists
- get :suggests
get :activity
post :follow
post :unfollow
diff --git a/db/migrate/20210604082145_create_external_status_checks_table.rb b/db/migrate/20210604082145_create_external_status_checks_table.rb
new file mode 100644
index 00000000000..c1ad3df6c9e
--- /dev/null
+++ b/db/migrate/20210604082145_create_external_status_checks_table.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class CreateExternalStatusChecksTable < ActiveRecord::Migration[6.1]
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ def up
+ create_table_with_constraints :external_status_checks, if_not_exists: true do |t|
+ t.references :project, foreign_key: { on_delete: :cascade }, null: false, index: false
+ t.timestamps_with_timezone
+ t.text :external_url, null: false
+ t.text_limit :external_url, 255
+ t.text :name, null: false
+ t.text_limit :name, 255
+
+ t.index([:project_id, :name],
+ unique: true,
+ name: 'idx_on_external_status_checks_project_id_name')
+ t.index([:project_id, :external_url],
+ unique: true,
+ name: 'idx_on_external_status_checks_project_id_external_url')
+ end
+
+ create_table :external_status_checks_protected_branches do |t|
+ t.bigint :external_status_check_id, null: false
+ t.bigint :protected_branch_id, null: false
+
+ t.index :external_status_check_id, name: 'index_esc_protected_branches_on_external_status_check_id'
+ t.index :protected_branch_id, name: 'index_esc_protected_branches_on_protected_branch_id'
+ end
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :external_status_checks_protected_branches, force: :cascade, if_exists: true
+ end
+
+ with_lock_retries do
+ drop_table :external_status_checks, force: :cascade, if_exists: true
+ end
+ end
+end
diff --git a/db/migrate/20210604085600_rename_status_check_responses_approval_rule.rb b/db/migrate/20210604085600_rename_status_check_responses_approval_rule.rb
new file mode 100644
index 00000000000..a12aef9455d
--- /dev/null
+++ b/db/migrate/20210604085600_rename_status_check_responses_approval_rule.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+class RenameStatusCheckResponsesApprovalRule < ActiveRecord::Migration[6.1]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ execute('DELETE FROM status_check_responses')
+
+ unless column_exists?(:status_check_responses, :external_status_check_id)
+ add_column :status_check_responses, :external_status_check_id, :bigint, null: false # rubocop:disable Rails/NotNullColumn
+ end
+
+ add_concurrent_foreign_key :status_check_responses, :external_status_checks, column: :external_status_check_id, on_delete: :cascade
+ add_concurrent_foreign_key :status_check_responses, :merge_requests, column: :merge_request_id, on_delete: :cascade
+
+ add_concurrent_index :status_check_responses, :external_status_check_id
+
+ # Setting this to true so that we can remove the column in a future release once the column has been removed. It has been ignored in 14.0
+ change_column_null :status_check_responses, :external_approval_rule_id, true
+
+ with_lock_retries do
+ remove_foreign_key :status_check_responses, :external_approval_rules
+ end
+ end
+
+ def down
+ change_column_null :status_check_responses, :external_approval_rule_id, false
+ with_lock_retries do
+ add_foreign_key :status_check_responses, :external_approval_rules
+ end
+ remove_column :status_check_responses, :external_status_check_id
+ end
+end
diff --git a/db/migrate/20210609192728_add_status_check_foreign_key_to_external_status_check_id.rb b/db/migrate/20210609192728_add_status_check_foreign_key_to_external_status_check_id.rb
new file mode 100644
index 00000000000..461d5838aed
--- /dev/null
+++ b/db/migrate/20210609192728_add_status_check_foreign_key_to_external_status_check_id.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddStatusCheckForeignKeyToExternalStatusCheckId < ActiveRecord::Migration[6.1]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :external_status_checks_protected_branches, :external_status_checks, column: :external_status_check_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :external_status_checks_protected_branches, column: :external_status_check_id
+ end
+ end
+end
diff --git a/db/migrate/20210609193101_add_status_check_foreign_key_to_protected_branch_id.rb b/db/migrate/20210609193101_add_status_check_foreign_key_to_protected_branch_id.rb
new file mode 100644
index 00000000000..6eeee60ec3a
--- /dev/null
+++ b/db/migrate/20210609193101_add_status_check_foreign_key_to_protected_branch_id.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddStatusCheckForeignKeyToProtectedBranchId < ActiveRecord::Migration[6.1]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :external_status_checks_protected_branches, :protected_branches, column: :protected_branch_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :external_status_checks_protected_branches, column: :protected_branch_id
+ end
+ end
+end
diff --git a/db/schema_migrations/20210604082145 b/db/schema_migrations/20210604082145
new file mode 100644
index 00000000000..2c568f0f2b6
--- /dev/null
+++ b/db/schema_migrations/20210604082145
@@ -0,0 +1 @@
+f4191b4b8ae7c282c0012f533a01ebe341d62cb0418e39ad543d06ed2dac63a4 \ No newline at end of file
diff --git a/db/schema_migrations/20210604085600 b/db/schema_migrations/20210604085600
new file mode 100644
index 00000000000..aa8b3120916
--- /dev/null
+++ b/db/schema_migrations/20210604085600
@@ -0,0 +1 @@
+8b6e1c7bacf2cbc05ba94e3fea2ab20e30b78ccaa6833949c11f89d1bdec8110 \ No newline at end of file
diff --git a/db/schema_migrations/20210609192728 b/db/schema_migrations/20210609192728
new file mode 100644
index 00000000000..e39cbc077b4
--- /dev/null
+++ b/db/schema_migrations/20210609192728
@@ -0,0 +1 @@
+b6c503eddc1c5e36957b59efc8fc5dd75da18104499667c3fcc435fcbd739af3 \ No newline at end of file
diff --git a/db/schema_migrations/20210609193101 b/db/schema_migrations/20210609193101
new file mode 100644
index 00000000000..42fc6427247
--- /dev/null
+++ b/db/schema_migrations/20210609193101
@@ -0,0 +1 @@
+09771c6f56e54a4d3dc0caab4891cbaf2a1d5685ccb1161d141ce38e44d6cfdb \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 3c96987bb5c..549a09ebc60 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -12893,6 +12893,41 @@ CREATE SEQUENCE external_pull_requests_id_seq
ALTER SEQUENCE external_pull_requests_id_seq OWNED BY external_pull_requests.id;
+CREATE TABLE external_status_checks (
+ id bigint NOT NULL,
+ project_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ external_url text NOT NULL,
+ name text NOT NULL,
+ CONSTRAINT check_7e3b9eb41a CHECK ((char_length(name) <= 255)),
+ CONSTRAINT check_ae0dec3f61 CHECK ((char_length(external_url) <= 255))
+);
+
+CREATE SEQUENCE external_status_checks_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE external_status_checks_id_seq OWNED BY external_status_checks.id;
+
+CREATE TABLE external_status_checks_protected_branches (
+ id bigint NOT NULL,
+ external_status_check_id bigint NOT NULL,
+ protected_branch_id bigint NOT NULL
+);
+
+CREATE SEQUENCE external_status_checks_protected_branches_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE external_status_checks_protected_branches_id_seq OWNED BY external_status_checks_protected_branches.id;
+
CREATE TABLE feature_gates (
id integer NOT NULL,
feature_key character varying NOT NULL,
@@ -18127,8 +18162,9 @@ ALTER SEQUENCE sprints_id_seq OWNED BY sprints.id;
CREATE TABLE status_check_responses (
id bigint NOT NULL,
merge_request_id bigint NOT NULL,
- external_approval_rule_id bigint NOT NULL,
- sha bytea NOT NULL
+ external_approval_rule_id bigint,
+ sha bytea NOT NULL,
+ external_status_check_id bigint NOT NULL
);
CREATE SEQUENCE status_check_responses_id_seq
@@ -19863,6 +19899,10 @@ ALTER TABLE ONLY external_approval_rules_protected_branches ALTER COLUMN id SET
ALTER TABLE ONLY external_pull_requests ALTER COLUMN id SET DEFAULT nextval('external_pull_requests_id_seq'::regclass);
+ALTER TABLE ONLY external_status_checks ALTER COLUMN id SET DEFAULT nextval('external_status_checks_id_seq'::regclass);
+
+ALTER TABLE ONLY external_status_checks_protected_branches ALTER COLUMN id SET DEFAULT nextval('external_status_checks_protected_branches_id_seq'::regclass);
+
ALTER TABLE ONLY feature_gates ALTER COLUMN id SET DEFAULT nextval('feature_gates_id_seq'::regclass);
ALTER TABLE ONLY features ALTER COLUMN id SET DEFAULT nextval('features_id_seq'::regclass);
@@ -21174,6 +21214,12 @@ ALTER TABLE ONLY external_approval_rules_protected_branches
ALTER TABLE ONLY external_pull_requests
ADD CONSTRAINT external_pull_requests_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY external_status_checks
+ ADD CONSTRAINT external_status_checks_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY external_status_checks_protected_branches
+ ADD CONSTRAINT external_status_checks_protected_branches_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY feature_gates
ADD CONSTRAINT feature_gates_pkey PRIMARY KEY (id);
@@ -22388,6 +22434,10 @@ CREATE UNIQUE INDEX idx_on_external_approval_rules_project_id_external_url ON ex
CREATE UNIQUE INDEX idx_on_external_approval_rules_project_id_name ON external_approval_rules USING btree (project_id, name);
+CREATE UNIQUE INDEX idx_on_external_status_checks_project_id_external_url ON external_status_checks USING btree (project_id, external_url);
+
+CREATE UNIQUE INDEX idx_on_external_status_checks_project_id_name ON external_status_checks USING btree (project_id, name);
+
CREATE INDEX idx_packages_build_infos_on_package_id ON packages_build_infos USING btree (package_id);
CREATE INDEX idx_packages_debian_group_component_files_on_architecture_id ON packages_debian_group_component_files USING btree (architecture_id);
@@ -23278,6 +23328,10 @@ CREATE INDEX index_epics_on_start_date_sourcing_epic_id ON epics USING btree (st
CREATE INDEX index_epics_on_start_date_sourcing_milestone_id ON epics USING btree (start_date_sourcing_milestone_id);
+CREATE INDEX index_esc_protected_branches_on_external_status_check_id ON external_status_checks_protected_branches USING btree (external_status_check_id);
+
+CREATE INDEX index_esc_protected_branches_on_protected_branch_id ON external_status_checks_protected_branches USING btree (protected_branch_id);
+
CREATE INDEX index_events_on_action ON events USING btree (action);
CREATE INDEX index_events_on_author_id_and_created_at ON events USING btree (author_id, created_at);
@@ -24668,6 +24722,8 @@ CREATE INDEX index_sprints_on_title_trigram ON sprints USING gin (title gin_trgm
CREATE INDEX index_status_check_responses_on_external_approval_rule_id ON status_check_responses USING btree (external_approval_rule_id);
+CREATE INDEX index_status_check_responses_on_external_status_check_id ON status_check_responses USING btree (external_status_check_id);
+
CREATE INDEX index_status_check_responses_on_merge_request_id ON status_check_responses USING btree (merge_request_id);
CREATE UNIQUE INDEX index_status_page_published_incidents_on_issue_id ON status_page_published_incidents USING btree (issue_id);
@@ -25449,9 +25505,6 @@ ALTER TABLE ONLY ci_unit_test_failures
ALTER TABLE ONLY project_pages_metadata
ADD CONSTRAINT fk_0fd5b22688 FOREIGN KEY (pages_deployment_id) REFERENCES pages_deployments(id) ON DELETE SET NULL;
-ALTER TABLE ONLY status_check_responses
- ADD CONSTRAINT fk_116e7e7369 FOREIGN KEY (external_approval_rule_id) REFERENCES external_approval_rules(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY group_deletion_schedules
ADD CONSTRAINT fk_11e3ebfcdd FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -25623,6 +25676,9 @@ ALTER TABLE ONLY clusters_applications_prometheus
ALTER TABLE ONLY terraform_states
ADD CONSTRAINT fk_558901b030 FOREIGN KEY (locked_by_user_id) REFERENCES users(id) ON DELETE SET NULL;
+ALTER TABLE ONLY status_check_responses
+ ADD CONSTRAINT fk_55bd2abc83 FOREIGN KEY (external_status_check_id) REFERENCES external_status_checks(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY merge_request_metrics
ADD CONSTRAINT fk_56067dcb44 FOREIGN KEY (target_project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -25911,6 +25967,9 @@ ALTER TABLE ONLY bulk_import_entities
ALTER TABLE ONLY compliance_management_frameworks
ADD CONSTRAINT fk_b74c45b71f FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY external_status_checks_protected_branches
+ ADD CONSTRAINT fk_b7d788e813 FOREIGN KEY (protected_branch_id) REFERENCES protected_branches(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY issue_assignees
ADD CONSTRAINT fk_b7d881734a FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
@@ -25971,6 +26030,9 @@ ALTER TABLE ONLY external_approval_rules_protected_branches
ALTER TABLE ONLY external_approval_rules_protected_branches
ADD CONSTRAINT fk_ca2ffb55e6 FOREIGN KEY (protected_branch_id) REFERENCES protected_branches(id) ON DELETE CASCADE;
+ALTER TABLE ONLY external_status_checks_protected_branches
+ ADD CONSTRAINT fk_cc0dcc36d1 FOREIGN KEY (external_status_check_id) REFERENCES external_status_checks(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY dast_profiles_pipelines
ADD CONSTRAINT fk_cc206a8c13 FOREIGN KEY (dast_profile_id) REFERENCES dast_profiles(id) ON DELETE CASCADE;
@@ -26358,6 +26420,9 @@ ALTER TABLE ONLY boards_epic_board_positions
ALTER TABLE ONLY geo_repository_created_events
ADD CONSTRAINT fk_rails_1f49e46a61 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY external_status_checks
+ ADD CONSTRAINT fk_rails_1f5a8aa809 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY dora_daily_metrics
ADD CONSTRAINT fk_rails_1fd07aff6f FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE;
diff --git a/doc/administration/geo/index.md b/doc/administration/geo/index.md
index e4272696c1a..422e4705d86 100644
--- a/doc/administration/geo/index.md
+++ b/doc/administration/geo/index.md
@@ -56,7 +56,8 @@ Geo provides:
### Gitaly Cluster
Geo should not be confused with [Gitaly Cluster](../gitaly/praefect.md). For more information about
-the difference between Geo and Gitaly Cluster, see [Gitaly Cluster compared to Geo](../gitaly/index.md#gitaly-cluster-compared-to-geo).
+the difference between Geo and Gitaly Cluster, see
+[How does Gitaly Cluster compare to Geo?](../gitaly/faq.md#how-does-gitaly-cluster-compare-to-geo).
## How it works
diff --git a/doc/administration/geo/setup/database.md b/doc/administration/geo/setup/database.md
index 5b53f881f29..c1a9e695cef 100644
--- a/doc/administration/geo/setup/database.md
+++ b/doc/administration/geo/setup/database.md
@@ -728,6 +728,10 @@ For each Patroni instance on the secondary site:
postgresql['sql_replication_password'] = 'POSTGRESQL_REPLICATION_PASSWORD_HASH'
postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH'
postgresql['listen_address'] = '0.0.0.0' # You can use a public or VPC address here instead
+
+ gitlab_rails['dbpassword'] = 'POSTGRESQL_PASSWORD'
+ gitlab_rails['enable'] = true
+ gitlab_rails['auto_migrate'] = false
```
1. Reconfigure GitLab for the changes to take effect.
@@ -882,6 +886,7 @@ For each Patroni instance on the secondary site for the tracking database:
# GitLab database settings
gitlab_rails['db_database'] = 'gitlabhq_geo_production'
gitlab_rails['db_username'] = 'gitlab_geo'
+ gitlab_rails['enable'] = true
# Disable automatic database migrations
gitlab_rails['auto_migrate'] = false
diff --git a/doc/administration/gitaly/faq.md b/doc/administration/gitaly/faq.md
new file mode 100644
index 00000000000..98a90925d32
--- /dev/null
+++ b/doc/administration/gitaly/faq.md
@@ -0,0 +1,90 @@
+---
+stage: Create
+group: Gitaly
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+type: reference
+---
+
+# Frequently asked questions **(FREE SELF)**
+
+The following are answers to frequently asked questions about Gitaly and Gitaly Cluster.
+
+## How does Gitaly Cluster compare to Geo?
+
+Gitaly Cluster and [Geo](../geo/index.md) both provide redundancy. However the redundancy of:
+
+- Gitaly Cluster provides fault tolerance for data storage and is invisible to the user. Users are
+ not aware when Gitaly Cluster is used.
+- Geo provides [replication](../geo/index.md) and [disaster recovery](../geo/disaster_recovery/index.md) for
+ an entire instance of GitLab. Users know when they are using Geo for
+ [replication](../geo/index.md). Geo [replicates multiple data types](../geo/replication/datatypes.md#limitations-on-replicationverification),
+ including Git data.
+
+The following table outlines the major differences between Gitaly Cluster and Geo:
+
+| Tool | Nodes | Locations | Latency tolerance | Failover | Consistency | Provides redundancy for |
+|:---------------|:---------|:----------|:-------------------|:----------------------------------------------------------------------------|:-----------------------------------------|:------------------------|
+| Gitaly Cluster | Multiple | Single | Approximately 1 ms | [Automatic](praefect.md#automatic-failover-and-primary-election-strategies) | [Strong](praefect.md#strong-consistency) | Data storage in Git |
+| Geo | Multiple | Multiple | Up to one minute | [Manual](../geo/disaster_recovery/index.md) | Eventual | Entire GitLab instance |
+
+For more information, see:
+
+- Geo [use cases](../geo/index.md#use-cases).
+- Geo [architecture](../geo/index.md#architecture).
+
+## Are there instructions for migrating to Gitaly Cluster?
+
+Yes! For more information, see [Migrate to Gitaly Cluster](praefect.md#migrate-to-gitaly-cluster).
+
+## What are some repository storage recommendations?
+
+The size of the required storage can vary between instances and depends on the set
+[replication factor](praefect.md#replication-factor). You might want to include implementing
+repository storage redundancy.
+
+For a replication factor:
+
+- Of `1`: NFS, Gitaly, and Gitaly Cluster have roughly the same storage requirements.
+- More than `1`: The amount of required storage is `used space * replication factor`. `used space`
+ should include any planned future growth.
+
+## What are some Praefect database storage requirements?
+
+The requirements are relatively low because the database contains only metadata of:
+
+- Where repositories are located.
+- Some queued work.
+
+It depends on the number of repositories, but a useful minimum is 5-10 GB, similar to the main
+GitLab application database.
+
+## Can the GitLab application database and the Praefect database be on the same servers?
+
+Yes, however Praefect should have it's own database server when using Omnibus GitLab PostgreSQL. If
+there is a failover, Praefect isn't aware and starts to fail as the database it's trying to use would
+either:
+
+- Be unavailable.
+- In read-only mode.
+
+A future solution may allow for Praefect and Omnibus GitLab databases on the same PostgreSQL server.
+For more information, see the relevant:
+
+- [Omnibus GitLab issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5919).
+- [Gitaly issue](https://gitlab.com/gitlab-org/gitaly/-/issues/3398).
+
+## Is PgBouncer required for the Praefect database?
+
+No, because the number of connections Praefect makes is low. You can use the same PgBouncer instance
+for both the GitLab application database and the Praefect database if you wish.
+
+## Are there any special considerations for Gitaly Cluster when PostgreSQL is upgraded?
+
+There are no special requirements. Gitaly Cluster requires PostgreSQL version 11 or later.
+
+## Praefect database tables are empty?
+
+These tables are created per the [specific configuration section](praefect.md#postgresql).
+
+If you find you have an empty Praefect database table, see the
+[relevant troubleshooting section](index.md#relation-does-not-exist-errors).
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 6177932937c..0aa6bfbd501 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -235,29 +235,6 @@ A hybrid approach can be used in these instances, where each shard is configured
cluster. [Variable replication factor](https://gitlab.com/groups/gitlab-org/-/epics/3372) is planned
to provide greater flexibility for extremely large GitLab instances.
-### Gitaly Cluster compared to Geo
-
-Gitaly Cluster and [Geo](../geo/index.md) both provide redundancy. However the redundancy of:
-
-- Gitaly Cluster provides fault tolerance for data storage and is invisible to the user. Users are
- not aware when Gitaly Cluster is used.
-- Geo provides [replication](../geo/index.md) and [disaster recovery](../geo/disaster_recovery/index.md) for
- an entire instance of GitLab. Users know when they are using Geo for
- [replication](../geo/index.md). Geo [replicates multiple data types](../geo/replication/datatypes.md#limitations-on-replicationverification),
- including Git data.
-
-The following table outlines the major differences between Gitaly Cluster and Geo:
-
-| Tool | Nodes | Locations | Latency tolerance | Failover | Consistency | Provides redundancy for |
-|:---------------|:---------|:----------|:-------------------|:----------------------------------------------------------------------------|:-----------------------------------------|:------------------------|
-| Gitaly Cluster | Multiple | Single | Approximately 1 ms | [Automatic](praefect.md#automatic-failover-and-primary-election-strategies) | [Strong](praefect.md#strong-consistency) | Data storage in Git |
-| Geo | Multiple | Multiple | Up to one minute | [Manual](../geo/disaster_recovery/index.md) | Eventual | Entire GitLab instance |
-
-For more information, see:
-
-- Geo [use cases](../geo/index.md#use-cases).
-- Geo [architecture](../geo/index.md#architecture).
-
### Architecture
Praefect is a router and transaction manager for Gitaly, and a required
@@ -407,30 +384,31 @@ We welcome your feedback on this process: raise a support ticket, or [comment on
Refer to the information below when troubleshooting Gitaly and Gitaly Cluster.
+Before troubleshooting, see the Gitaly and Gitaly Cluster
+[frequently asked questions](faq.md).
+
### Troubleshoot Gitaly
The following sections provide possible solutions to Gitaly errors.
-See also:
-
-- [Gitaly timeout](../../user/admin_area/settings/gitaly_timeouts.md) settings.
-- [Gitaly troubleshooting information](../reference_architectures/troubleshooting.md#troubleshooting-gitaly)
- in reference architecture documentation.
+See also [Gitaly timeout](../../user/admin_area/settings/gitaly_timeouts.md) settings.
#### Check versions when using standalone Gitaly servers
When using standalone Gitaly servers, you must make sure they are the same version
-as GitLab to ensure full compatibility. Check **Admin Area > Overview > Gitaly Servers** on
-your GitLab instance and confirm all Gitaly servers indicate that they are up to date.
+as GitLab to ensure full compatibility:
-#### `gitaly-debug`
+1. Go to **Admin Area > Overview > Gitaly Servers** on your GitLab instance.
+1. Confirm all Gitaly servers indicate that they are up to date.
+
+#### Use `gitaly-debug`
The `gitaly-debug` command provides "production debugging" tools for Gitaly and Git
performance. It is intended to help production engineers and support
engineers investigate Gitaly performance problems.
If you're using GitLab 11.6 or newer, this tool should be installed on
-your GitLab / Gitaly server already at `/opt/gitlab/embedded/bin/gitaly-debug`.
+your GitLab or Gitaly server already at `/opt/gitlab/embedded/bin/gitaly-debug`.
If you're investigating an older GitLab version you can compile this
tool offline and copy the executable to your server:
@@ -452,13 +430,13 @@ gitaly-debug -h
remote: GitLab: 401 Unauthorized
```
-You need to sync your `gitlab-secrets.json` file with your Gitaly clients (GitLab
-app nodes).
+You need to sync your `gitlab-secrets.json` file with your GitLab
+application nodes.
#### Client side gRPC logs
Gitaly uses the [gRPC](https://grpc.io/) RPC framework. The Ruby gRPC
-client has its own log file which may contain debugging information when
+client has its own log file which may contain useful information when
you are seeing Gitaly errors. You can control the log level of the
gRPC client with the `GRPC_LOG_LEVEL` environment variable. The
default level is `WARN`.
@@ -510,12 +488,13 @@ so, there's not that much visibility into what goes on inside
If you have Prometheus set up to scrape your Gitaly process, you can see
request rates and error codes for individual RPCs in `gitaly-ruby` by
-querying `grpc_client_handled_total`. Strictly speaking, this metric does
-not differentiate between `gitaly-ruby` and other RPCs. However from GitLab 11.9,
-all gRPC calls made by Gitaly itself are internal calls from the main Gitaly process to one of its
-`gitaly-ruby` sidecars.
+querying `grpc_client_handled_total`.
+
+- In theory, this metric does not differentiate between `gitaly-ruby` and other RPCs.
+- In practice from GitLab 11.9, all gRPC calls made by Gitaly itself are internal calls from the
+ main Gitaly process to one of its `gitaly-ruby` sidecars.
-Assuming your `grpc_client_handled_total` counter observes only Gitaly,
+Assuming your `grpc_client_handled_total` counter only observes Gitaly,
the following query shows you RPCs are (most likely) internally
implemented as calls to `gitaly-ruby`:
@@ -549,7 +528,7 @@ Confirm the following are all true:
- When any user adds or modifies a file from the repository using the GitLab
UI, it immediately fails with a red `401 Unauthorized` banner.
- Creating a new project and [initializing it with a README](../../user/project/working_with_projects.md#blank-projects)
- successfully creates the project, but doesn't create the README.
+ successfully creates the project but doesn't create the README.
- When [tailing the logs](https://docs.gitlab.com/omnibus/settings/logs.html#tail-logs-in-a-console-on-the-server)
on a Gitaly client and reproducing the error, you get `401` errors
when reaching the [`/api/v4/internal/allowed`](../../development/internal_api.md) endpoint:
@@ -631,22 +610,24 @@ Verify you can reach Gitaly by using TCP:
sudo gitlab-rake gitlab:tcp_check[GITALY_SERVER_IP,GITALY_LISTEN_PORT]
```
-If the TCP connection fails, check your network settings and your firewall rules.
-If the TCP connection succeeds, your networking and firewall rules are correct.
+If the TCP connection:
+
+- Fails, check your network settings and your firewall rules.
+- Succeeds, your networking and firewall rules are correct.
-If you use proxy servers in your command line environment, such as Bash, these
-can interfere with your gRPC traffic.
+If you use proxy servers in your command line environment such as Bash, these can interfere with
+your gRPC traffic.
-If you use Bash or a compatible command line environment, run the following commands
-to determine whether you have proxy servers configured:
+If you use Bash or a compatible command line environment, run the following commands to determine
+whether you have proxy servers configured:
```shell
echo $http_proxy
echo $https_proxy
```
-If either of these variables have a value, your Gitaly CLI connections may be
-getting routed through a proxy which cannot connect to Gitaly.
+If either of these variables have a value, your Gitaly CLI connections may be getting routed through
+a proxy which cannot connect to Gitaly.
To remove the proxy setting, run the following commands (depending on which variables had values):
@@ -683,6 +664,22 @@ it's likely that the Gitaly servers are experiencing
Ensure the Gitaly clients and servers are synchronized, and use an NTP time
server to keep them synchronized.
+#### Gitaly not listening on new address after reconfiguring
+
+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, run `sudo gitlab-ctl restart` to resolve the issue. This should no longer be
+necessary because [this issue](https://gitlab.com/gitlab-org/gitaly/-/issues/2521) is resolved.
+
+#### Permission denied errors appearing in Gitaly logs when accessing repositories from a standalone Gitaly node
+
+If this error occurs even though file permissions are correct, it's likely that the Gitaly node is
+experiencing [clock drift](https://en.wikipedia.org/wiki/Clock_drift).
+
+Please ensure that the GitLab and Gitaly nodes are synchronized and use an NTP time
+server to keep them synchronized if possible.
+
### Troubleshoot Praefect (Gitaly Cluster)
The following sections provide possible solutions to Gitaly Cluster errors.
@@ -737,7 +734,7 @@ For example:
{"level":"error","msg":"Error updating node: pq: relation \"node_status\" does not exist","pid":210882,"praefectName":"gitlab1x4m:0.0.0.0:2305","time":"2021-04-01T19:26:19.473Z","virtual_storage":"praefect-cluster-1"}
```
-To solve this, the database schema migration can be done using `sql-migrate` subcommand of
+To solve this, the database schema migration can be done using `sql-migrate` sub-command of
the `praefect` command:
```shell
diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md
index d50b394a94e..d8407b04ac0 100644
--- a/doc/administration/gitaly/praefect.md
+++ b/doc/administration/gitaly/praefect.md
@@ -1485,15 +1485,8 @@ or to move from single Gitaly nodes, the basic process involves:
1. Create and configure Gitaly Cluster.
1. [Move the repositories](#move-repositories).
-The size of the required storage can vary between instances and depends on the set
-[replication factor](#replication-factor). The migration to Gitaly Cluster might include
-implementing repository storage redundancy.
-
-For a replication factor:
-
-- Of `1`: NFS, Gitaly, and Gitaly Cluster have roughly the same storage requirements.
-- More than `1`: The amount of required storage is `used space * replication factor`. `used space`
- should include any planned future growth.
+When creating the storage, see some
+[repository storage recommendations](faq.md#what-are-some-repository-storage-recommendations).
### Move Repositories
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index 834f4047fdd..2b18efde95d 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -206,7 +206,7 @@ stop;
After configuring your local PlantUML server, you're ready to enable the PlantUML integration:
1. Sign in to GitLab as an [Administrator](../../user/permissions.md) user.
-1. In the top menu, click **{admin}** **Admin Area**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, go to **Settings > General** and expand the **PlantUML** section.
1. Select the **Enable PlantUML** check box.
1. Set the PlantUML instance as `https://gitlab.example.com/-/plantuml/`,
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index a7876f2c745..cf9c2143d8c 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -21,6 +21,42 @@ including adjusting log retention, log forwarding,
switching logs from JSON to plain text logging, and more.
- [How to parse and analyze JSON logs](troubleshooting/log_parsing.md).
+## Log Rotation
+
+The logs for a given service may be managed and rotated by:
+
+- `logrotate`
+- `svlogd` (`runit`'s service logging daemon)
+- `logrotate` and `svlogd`
+- Or not at all
+
+The table below includes information about what is responsible for managing and rotating logs for
+the included services. Logs
+[managed by `svlogd`](https://docs.gitlab.com/omnibus/settings/logs.html#runit-logs)
+are written to a file called `current`. The `logrotate` service built into GitLab
+[manages all logs](https://docs.gitlab.com/omnibus/settings/logs.html#logrotate)
+except those captured by `runit`.
+
+| Log Type | Managed by logrotate | Managed by svlogd/runit |
+| ----------------------------------------------- | -------------------- | ----------------------- |
+| [Alertmanager Logs](#alertmanager-logs) | N | Y |
+| [Crond Logs](#crond-logs) | N | Y |
+| [Gitaly](#gitaly-logs) | Y | Y |
+| [GitLab Exporter For Omnibus](#gitlab-exporter) | N | Y |
+| [GitLab Pages Logs](#pages-logs) | Y | Y |
+| GitLab Rails | Y | N |
+| [GitLab Shell Logs](#gitlab-shelllog) | Y | N |
+| [Grafana Logs](#grafana-logs) | N | Y |
+| [LogRotate Logs](#logrotate-logs) | N | Y |
+| [Mailroom](#mail_room_jsonlog-default) | Y | Y |
+| [NGINX](#nginx-logs) | Y | Y |
+| [PostgreSQL Logs](#postgresql-logs) | N | Y |
+| [Prometheus Logs](#prometheus-logs) | N | Y |
+| [Puma](#puma-logs) | Y | Y |
+| [Redis Logs](#redis-logs) | N | Y |
+| [Registry Logs](#registry-logs) | N | Y |
+| [Workhorse Logs](#workhorse-logs) | Y | Y |
+
## `production_json.log`
This file lives in `/var/log/gitlab/gitlab-rails/production_json.log` for
diff --git a/doc/administration/nfs.md b/doc/administration/nfs.md
index 765e2981bba..e53f2af3440 100644
--- a/doc/administration/nfs.md
+++ b/doc/administration/nfs.md
@@ -8,8 +8,7 @@ type: reference
# Using NFS with GitLab **(FREE SELF)**
NFS can be used as an alternative for object storage but this isn't typically
-recommended for performance reasons. Note however it is required for [GitLab
-Pages](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/196).
+recommended for performance reasons.
For data objects such as LFS, Uploads, Artifacts, etc., an [Object Storage service](object_storage.md)
is recommended over NFS where possible, due to better performance.
@@ -17,7 +16,7 @@ is recommended over NFS where possible, due to better performance.
File system performance can impact overall GitLab performance, especially for
actions that read or write to Git repositories. For steps you can use to test
file system performance, see
-[File system Performance Benchmarking](operations/filesystem_benchmarking.md).
+[File System Performance Benchmarking](operations/filesystem_benchmarking.md).
## Gitaly and NFS deprecation
diff --git a/doc/administration/reference_architectures/troubleshooting.md b/doc/administration/reference_architectures/troubleshooting.md
index d5f57965a80..4b07cff7de2 100644
--- a/doc/administration/reference_architectures/troubleshooting.md
+++ b/doc/administration/reference_architectures/troubleshooting.md
@@ -206,211 +206,8 @@ To make sure your configuration is correct:
## Troubleshooting Gitaly
-If you have any problems when using standalone Gitaly nodes, first
-[check all the versions are up to date](../gitaly/index.md#check-versions-when-using-standalone-gitaly-servers).
-
-### `gitaly-debug`
-
-The `gitaly-debug` command provides "production debugging" tools for Gitaly and Git
-performance. It is intended to help production engineers and support
-engineers investigate Gitaly performance problems.
-
-If you're using GitLab 11.6 or newer, this tool should be installed on
-your GitLab / Gitaly server already at `/opt/gitlab/embedded/bin/gitaly-debug`.
-If you're investigating an older GitLab version you can compile this
-tool offline and copy the executable to your server:
-
-```shell
-git clone https://gitlab.com/gitlab-org/gitaly.git
-cd cmd/gitaly-debug
-GOOS=linux GOARCH=amd64 go build -o gitaly-debug
-```
-
-To see the help page of `gitaly-debug` for a list of supported sub-commands, run:
-
-```shell
-gitaly-debug -h
-```
-
-### Commits, pushes, and clones return a 401
-
-```plaintext
-remote: GitLab: 401 Unauthorized
-```
-
-You will need to sync your `gitlab-secrets.json` file with your GitLab
-app nodes.
-
-### Client side gRPC logs
-
-Gitaly uses the [gRPC](https://grpc.io/) RPC framework. The Ruby gRPC
-client has its own log file which may contain useful information when
-you are seeing Gitaly errors. You can control the log level of the
-gRPC client with the `GRPC_LOG_LEVEL` environment variable. The
-default level is `WARN`.
-
-You can run a gRPC trace with:
-
-```shell
-sudo GRPC_TRACE=all GRPC_VERBOSITY=DEBUG gitlab-rake gitlab:gitaly:check
-```
-
-### Observing `gitaly-ruby` traffic
-
-[`gitaly-ruby`](../gitaly/configure_gitaly.md#gitaly-ruby) is an internal implementation detail of Gitaly,
-so, there's not that much visibility into what goes on inside
-`gitaly-ruby` processes.
-
-If you have Prometheus set up to scrape your Gitaly process, you can see
-request rates and error codes for individual RPCs in `gitaly-ruby` by
-querying `grpc_client_handled_total`. Strictly speaking, this metric does
-not differentiate between `gitaly-ruby` and other RPCs, but in practice
-(as of GitLab 11.9), all gRPC calls made by Gitaly itself are internal
-calls from the main Gitaly process to one of its `gitaly-ruby` sidecars.
-
-Assuming your `grpc_client_handled_total` counter only observes Gitaly,
-the following query shows you RPCs are (most likely) internally
-implemented as calls to `gitaly-ruby`:
-
-```prometheus
-sum(rate(grpc_client_handled_total[5m])) by (grpc_method) > 0
-```
-
-### Repository changes fail with a `401 Unauthorized` error
-
-If you're running Gitaly on its own server and notice that users can
-successfully clone and fetch repositories (via both SSH and HTTPS), but can't
-push to them or make changes to the repository in the web UI without getting a
-`401 Unauthorized` message, then it's possible Gitaly is failing to authenticate
-with the other nodes due to having the wrong secrets file.
-
-Confirm the following are all true:
-
-- When any user performs a `git push` to any repository on this Gitaly node, it
- fails with the following error (note the `401 Unauthorized`):
-
- ```shell
- remote: GitLab: 401 Unauthorized
- To <REMOTE_URL>
- ! [remote rejected] branch-name -> branch-name (pre-receive hook declined)
- error: failed to push some refs to '<REMOTE_URL>'
- ```
-
-- When any user adds or modifies a file from the repository using the GitLab
- UI, it immediately fails with a red `401 Unauthorized` banner.
-- Creating a new project and [initializing it with a README](../../user/project/working_with_projects.md#blank-projects)
- successfully creates the project but doesn't create the README.
-- When [tailing the logs](https://docs.gitlab.com/omnibus/settings/logs.html#tail-logs-in-a-console-on-the-server) on an app node and reproducing the error, you get `401` errors
- when reaching the [`/api/v4/internal/allowed`](../../development/internal_api.md) endpoint:
-
- ```shell
- # api_json.log
- {
- "time": "2019-07-18T00:30:14.967Z",
- "severity": "INFO",
- "duration": 0.57,
- "db": 0,
- "view": 0.57,
- "status": 401,
- "method": "POST",
- "path": "\/api\/v4\/internal\/allowed",
- "params": [
- {
- "key": "action",
- "value": "git-receive-pack"
- },
- {
- "key": "changes",
- "value": "REDACTED"
- },
- {
- "key": "gl_repository",
- "value": "REDACTED"
- },
- {
- "key": "project",
- "value": "\/path\/to\/project.git"
- },
- {
- "key": "protocol",
- "value": "web"
- },
- {
- "key": "env",
- "value": "{\"GIT_ALTERNATE_OBJECT_DIRECTORIES\":[],\"GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE\":[],\"GIT_OBJECT_DIRECTORY\":null,\"GIT_OBJECT_DIRECTORY_RELATIVE\":null}"
- },
- {
- "key": "user_id",
- "value": "2"
- },
- {
- "key": "secret_token",
- "value": "[FILTERED]"
- }
- ],
- "host": "gitlab.example.com",
- "ip": "REDACTED",
- "ua": "Ruby",
- "route": "\/api\/:version\/internal\/allowed",
- "queue_duration": 4.24,
- "gitaly_calls": 0,
- "gitaly_duration": 0,
- "correlation_id": "XPUZqTukaP3"
- }
-
- # nginx_access.log
- [IP] - - [18/Jul/2019:00:30:14 +0000] "POST /api/v4/internal/allowed HTTP/1.1" 401 30 "" "Ruby"
- ```
-
-To fix this problem, confirm that your `gitlab-secrets.json` file
-on the Gitaly node matches the one on all other nodes. If it doesn't match,
-update the secrets file on the Gitaly node to match the others, then
-[reconfigure the node](../restart_gitlab.md#omnibus-gitlab-reconfigure).
-
-### Command line tools cannot connect to Gitaly
-
-If you are having trouble connecting to a Gitaly node with command line (CLI) tools, and certain actions result in a `14: Connect Failed` error message, it means that gRPC cannot reach your Gitaly node.
-
-Verify that you can reach Gitaly via TCP:
-
-```shell
-sudo gitlab-rake gitlab:tcp_check[GITALY_SERVER_IP,GITALY_LISTEN_PORT]
-```
-
-If the TCP connection fails, check your network settings and your firewall rules. If the TCP connection succeeds, your networking and firewall rules are correct.
-
-If you use proxy servers in your command line environment, such as Bash, these can interfere with your gRPC traffic.
-
-If you use Bash or a compatible command line environment, run the following commands to determine whether you have proxy servers configured:
-
-```shell
-echo $http_proxy
-echo $https_proxy
-```
-
-If either of these variables have a value, your Gitaly CLI connections may be getting routed through a proxy which cannot connect to Gitaly.
-
-To remove the proxy setting, run the following commands (depending on which variables had values):
-
-```shell
-unset http_proxy
-unset https_proxy
-```
-
-### Gitaly not listening on new address after reconfiguring
-
-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.
-
-### Permission denied errors appearing in Gitaly logs when accessing repositories from a standalone Gitaly node
-
-If this error occurs even though file permissions are correct, it's likely that
-the Gitaly node is experiencing
-[clock drift](https://en.wikipedia.org/wiki/Clock_drift).
-
-Please ensure that the GitLab and Gitaly nodes are synchronized and use an NTP time
-server to keep them synchronized if possible.
+For troubleshooting information, see Gitaly and Gitaly Cluster
+[troubleshooting information](../gitaly/index.md).
## Troubleshooting the GitLab Rails application
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index 978cbff625c..76ffa90fec0 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -503,120 +503,6 @@ DELETE /projects/:id/approval_rules/:approval_rule_id
| `id` | integer | yes | The ID of a project |
| `approval_rule_id` | integer | yes | The ID of a approval rule
-## External Project-level MR approvals **(ULTIMATE)**
-
-Configuration for approvals on a specific Merge Request which makes a call to an external HTTP resource.
-
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3869) in GitLab 13.10.
-> - It's [deployed behind a feature flag](../user/feature_flags.md), disabled by default.
-> - It's disabled on GitLab.com.
-> - It's not recommended for production use.
-> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-external-project-level-mr-approvals). **(ULTIMATE SELF)**
-
-### Get project external approval rules **(ULTIMATE)**
-
-You can request information about a project's external approval rules using the following endpoint:
-
-```plaintext
-GET /projects/:id/external_approval_rules
-```
-
-**Parameters:**
-
-| Attribute | Type | Required | Description |
-|---------------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
-
-```json
-[
- {
- "id": 1,
- "name": "Compliance Check",
- "project_id": 6,
- "external_url": "https://gitlab.com/example/test.json",
- "protected_branches": [
- {
- "id": 14,
- "project_id": 6,
- "name": "master",
- "created_at": "2020-10-12T14:04:50.787Z",
- "updated_at": "2020-10-12T14:04:50.787Z",
- "code_owner_approval_required": false
- }
- ]
- }
-]
-```
-
-### Create external approval rule **(ULTIMATE)**
-
-You can create a new external approval rule for a project using the following endpoint:
-
-```plaintext
-POST /projects/:id/external_approval_rules
-```
-
-| Attribute | Type | Required | Description |
-|------------------------|----------------|----------|----------------------------------------------------|
-| `id` | integer | yes | The ID of a project |
-| `name` | string | yes | Display name of approval rule |
-| `external_url` | string | yes | URL of external approval resource |
-| `protected_branch_ids` | `array<Integer>` | no | The ids of protected branches to scope the rule by |
-
-### Delete external approval rule **(ULTIMATE)**
-
-You can delete an external approval rule for a project using the following endpoint:
-
-```plaintext
-DELETE /projects/:id/external_approval_rules/:rule_id
-```
-
-| Attribute | Type | Required | Description |
-|------------------------|----------------|----------|----------------------------------------------------|
-| `rule_id` | integer | yes | The ID of an approval rule |
-| `id` | integer | yes | The ID of a project |
-
-### Update external approval rule **(ULTIMATE)**
-
-You can update an existing external approval rule for a project using the following endpoint:
-
-```plaintext
-PUT /projects/:id/external_approval_rules/:rule_id
-```
-
-| Attribute | Type | Required | Description |
-|------------------------|----------------|----------|----------------------------------------------------|
-| `id` | integer | yes | The ID of a project |
-| `rule_id` | integer | yes | The ID of an external approval rule |
-| `name` | string | no | Display name of approval rule |
-| `external_url` | string | no | URL of external approval resource |
-| `protected_branch_ids` | `array<Integer>` | no | The ids of protected branches to scope the rule by |
-
-### Enable or disable External Project-level MR approvals **(ULTIMATE SELF)**
-
-Enable or disable External Project-level MR approvals is under development and not ready for production use. It is
-deployed behind a feature flag that is **disabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../user/feature_flags.md)
-can enable it.
-
-To enable it:
-
-```ruby
-# For the instance
-Feature.enable(:ff_compliance_approval_gates)
-# For a single project
-Feature.enable(:ff_compliance_approval_gates, Project.find(<project id>))
-```
-
-To disable it:
-
-```ruby
-# For the instance
-Feature.disable(:ff_compliance_approval_gates)
-# For a single project
-Feature.disable(:ff_compliance_approval_gates, Project.find(<project id>))
-```
-
## Merge Request-level MR approvals
Configuration for approvals on a specific Merge Request. Must be authenticated for all endpoints.
diff --git a/doc/api/status_checks.md b/doc/api/status_checks.md
index 116044adb41..e4640e26a47 100644
--- a/doc/api/status_checks.md
+++ b/doc/api/status_checks.md
@@ -74,6 +74,110 @@ deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
can enable it.
+## Get project external status checks **(ULTIMATE)**
+
+You can request information about a project's external status checks using the following endpoint:
+
+```plaintext
+GET /projects/:id/external_status_checks
+```
+
+**Parameters:**
+
+| Attribute | Type | Required | Description |
+|---------------------|---------|----------|---------------------|
+| `id` | integer | yes | The ID of a project |
+
+```json
+[
+ {
+ "id": 1,
+ "name": "Compliance Check",
+ "project_id": 6,
+ "external_url": "https://gitlab.com/example/test.json",
+ "protected_branches": [
+ {
+ "id": 14,
+ "project_id": 6,
+ "name": "master",
+ "created_at": "2020-10-12T14:04:50.787Z",
+ "updated_at": "2020-10-12T14:04:50.787Z",
+ "code_owner_approval_required": false
+ }
+ ]
+ }
+]
+```
+
+### Create external status check **(ULTIMATE)**
+
+You can create a new external status check for a project using the following endpoint:
+
+```plaintext
+POST /projects/:id/external_status_checks
+```
+
+| Attribute | Type | Required | Description |
+|------------------------|----------------|----------|----------------------------------------------------|
+| `id` | integer | yes | The ID of a project |
+| `name` | string | yes | Display name of status check |
+| `external_url` | string | yes | URL of status check resource |
+| `protected_branch_ids` | `array<Integer>` | no | The ids of protected branches to scope the rule by |
+
+### Delete external status check **(ULTIMATE)**
+
+You can delete an external status check for a project using the following endpoint:
+
+```plaintext
+DELETE /projects/:id/external_status_checks/:check_id
+```
+
+| Attribute | Type | Required | Description |
+|------------------------|----------------|----------|----------------------------------------------------|
+| `rule_id` | integer | yes | The ID of an status check |
+| `id` | integer | yes | The ID of a project |
+
+### Update external status check **(ULTIMATE)**
+
+You can update an existing external status check for a project using the following endpoint:
+
+```plaintext
+PUT /projects/:id/external_status_checks/:check_id
+```
+
+| Attribute | Type | Required | Description |
+|------------------------|----------------|----------|----------------------------------------------------|
+| `id` | integer | yes | The ID of a project |
+| `rule_id` | integer | yes | The ID of an external status check |
+| `name` | string | no | Display name of status check |
+| `external_url` | string | no | URL of external status check resource |
+| `protected_branch_ids` | `array<Integer>` | no | The ids of protected branches to scope the rule by |
+
+### Enable or disable External Project-level MR status checks **(ULTIMATE SELF)**
+
+Enable or disable External Project-level MR status checks is under development and not ready for production use. It is
+deployed behind a feature flag that is **disabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../user/feature_flags.md)
+can enable it.
+
+To enable it:
+
+```ruby
+# For the instance
+Feature.enable(:ff_compliance_approval_gates)
+# For a single project
+Feature.enable(:ff_compliance_approval_gates, Project.find(<project id>))
+```
+
+To disable it:
+
+```ruby
+# For the instance
+Feature.disable(:ff_compliance_approval_gates)
+# For a single project
+Feature.disable(:ff_compliance_approval_gates, Project.find(<project id>))
+```
+
To enable it:
```ruby
diff --git a/doc/ci/jobs/job_control.md b/doc/ci/jobs/job_control.md
index 9efa63e072c..a9d5de3941c 100644
--- a/doc/ci/jobs/job_control.md
+++ b/doc/ci/jobs/job_control.md
@@ -73,7 +73,7 @@ end-to-end:
- $CI_COMMIT_MESSAGE =~ /skip-end-to-end-tests/
```
-You can use [parentheses](../variables/README.md#parentheses) with `&&` and `||`
+You can use [parentheses](#group-variable-expressions-together-with-parentheses) with `&&` and `||`
to build more complicated variable expressions:
```yaml
@@ -318,3 +318,114 @@ this feature flag again:
```ruby
Feature.enable(:allow_unsafe_ruby_regexp)
```
+
+## CI/CD variable expressions
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/37397) in GitLab 10.7 for [the `only` and `except` CI keywords](../yaml/README.md#onlyvariables--exceptvariables)
+> - [Expanded](https://gitlab.com/gitlab-org/gitlab/-/issues/27863) in GitLab 12.3 with [the `rules` keyword](../yaml/README.md#rules)
+
+Use variable expressions to control which jobs are created in a pipeline after changes
+are pushed to GitLab. You can use variable expressions with:
+
+- [`rules:if`](../yaml/README.md#rules).
+- [`only:variables` and `except:variables`](../yaml/README.md#onlyvariables--exceptvariables).
+
+For example, with `rules:if`:
+
+```yaml
+job1:
+ variables:
+ VAR1: "variable1"
+ script:
+ - echo "Test variable comparison
+ rules:
+ - if: $VAR1 == "variable1"
+```
+
+### Compare a variable to a string
+
+You can use the equality operators `==` and `!=` to compare a variable with a
+string. Both single quotes and double quotes are valid. The order doesn't matter,
+so the variable can be first, or the string can be first. For example:
+
+- `if: $VARIABLE == "some value"`
+- `if: $VARIABLE != "some value"`
+- `if: "some value" == $VARIABLE`
+
+### Compare two variables
+
+You can compare the values of two variables. For example:
+
+- `if: $VARIABLE_1 == $VARIABLE_2`
+- `if: $VARIABLE_1 != $VARIABLE_2`
+
+### Check if a variable is undefined
+
+You can compare a variable to the `null` keyword to see if it is defined. For example:
+
+- `if: $VARIABLE == null`
+- `if: $VARIABLE != null`
+
+### Check if a variable is empty
+
+You can check if a variable is defined but empty. For example:
+
+- `if: $VARIABLE == ""`
+- `if: $VARIABLE != ""`
+
+### Check if a variable exists
+
+You can check for the existence of a variable by using just the variable name in
+the expression. The variable must not be empty. For example:
+
+- `if: $VARIABLE`
+
+### Compare a variable to a regex pattern
+
+You can do regex pattern matching on variable values with the `=~` and `!~` operators.
+Variable pattern matching with regular expressions uses the
+[RE2 regular expression syntax](https://github.com/google/re2/wiki/Syntax).
+
+Expressions evaluate as `true` if:
+
+- Matches are found when using `=~`.
+- Matches are *not* found when using `!~`.
+
+For example:
+
+- `$VARIABLE =~ /^content.*/`
+- `$VARIABLE_1 !~ /^content.*/`
+
+Pattern matching is case-sensitive by default. Use the `i` flag modifier to make a
+pattern case-insensitive. For example: `/pattern/i`.
+
+### Join variable expressions together with `&&` or `||`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/62867) in GitLab 12.0
+
+You can join multiple expressions using `&&` (and) or `||` (or), for example:
+
+- `$VARIABLE1 =~ /^content.*/ && $VARIABLE2 == "something"`
+- `$VARIABLE1 =~ /^content.*/ && $VARIABLE2 =~ /thing$/ && $VARIABLE3`
+- `$VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/ && $VARIABLE3`
+
+The precedence of operators follows the [Ruby 2.5 standard](https://ruby-doc.org/core-2.5.0/doc/syntax/precedence_rdoc.html),
+so `&&` is evaluated before `||`.
+
+#### Group variable expressions together with parentheses
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230938) in GitLab 13.3.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/238174) in GitLab 13.5.
+
+You can use parentheses to group expressions together. Parentheses take precedence over
+`&&` and `||`, so expressions enclosed in parentheses are evaluated first, and the
+result is used for the rest of the expression.
+
+You can nest parentheses to create complex conditions, and the inner-most expressions
+in parentheses are evaluated first.
+
+For example:
+
+- `($VARIABLE1 =~ /^content.*/ || $VARIABLE2) && ($VARIABLE3 =~ /thing$/ || $VARIABLE4)`
+- `($VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/) && $VARIABLE3`
+- `$CI_COMMIT_BRANCH == "my-branch" || (($VARIABLE1 == "thing" || $VARIABLE2 == "thing") && $VARIABLE3)`
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index cb7669a973c..8b416dd0da9 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -647,154 +647,6 @@ with `K8S_SECRET_`.
CI/CD variables with multi-line values are not supported.
-## CI/CD variable expressions
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/37397) in GitLab 10.7 for [the `only` and `except` CI keywords](../yaml/README.md#onlyvariables--exceptvariables)
-> - [Expanded](https://gitlab.com/gitlab-org/gitlab/-/issues/27863) in GitLab 12.3 with [the `rules` keyword](../yaml/README.md#rules)
-
-Use variable expressions to limit which jobs are created
-in a pipeline after changes are pushed to GitLab.
-
-In `.gitlab-ci.yml`, variable expressions work with both:
-
-- [`rules`](../yaml/README.md#rules), which is the recommended approach, and
-- [`only` and `except`](../yaml/README.md#only--except), which are candidates for deprecation.
-
-This is particularly useful in combination with variables and triggered
-pipeline variables.
-
-```yaml
-deploy:
- script: cap staging deploy
- environment: staging
- only:
- variables:
- - $RELEASE == "staging"
- - $STAGING
-```
-
-Each expression provided is evaluated before a pipeline is created.
-
-If any of the conditions in `variables` evaluates to true when using `only`,
-a new job is created. If any of the expressions evaluates to true
-when `except` is being used, a job is not created.
-
-This follows the usual rules for [`only` / `except` policies](../yaml/README.md#onlyvariables--exceptvariables).
-
-### Syntax of CI/CD variable expressions
-
-Below you can find supported syntax reference.
-
-#### Equality matching using a string
-
-Examples:
-
-- `$VARIABLE == "some value"`
-- `$VARIABLE != "some value"` (introduced in GitLab 11.11)
-
-You can use equality operator `==` or `!=` to compare a variable content to a
-string. We support both, double quotes and single quotes to define a string
-value, so both `$VARIABLE == "some value"` and `$VARIABLE == 'some value'`
-are supported. `"some value" == $VARIABLE` is correct too.
-
-#### Checking for an undefined value
-
-Examples:
-
-- `$VARIABLE == null`
-- `$VARIABLE != null` (introduced in GitLab 11.11)
-
-It sometimes happens that you want to check whether a variable is defined
-or not. To do that, you can compare a variable to `null` keyword, like
-`$VARIABLE == null`. This expression evaluates to true if
-variable is not defined when `==` is used, or to false if `!=` is used.
-
-#### Checking for an empty variable
-
-Examples:
-
-- `$VARIABLE == ""`
-- `$VARIABLE != ""` (introduced in GitLab 11.11)
-
-To check if a variable is defined but empty, compare it to:
-
-- An empty string: `$VARIABLE == ''`
-- A non-empty string: `$VARIABLE != ""`
-
-#### Comparing two variables
-
-Examples:
-
-- `$VARIABLE_1 == $VARIABLE_2`
-- `$VARIABLE_1 != $VARIABLE_2` (introduced in GitLab 11.11)
-
-It is possible to compare two variables. This compares values
-of these variables.
-
-#### Variable presence check
-
-Example: `$STAGING`
-
-To create a job when there is some variable present, meaning it is defined and non-empty,
-use the variable name as an expression, like `$STAGING`. If the `$STAGING` variable
-is defined, and is non empty, expression evaluates to `true`.
-`$STAGING` value needs to be a string, with length higher than zero.
-Variable that contains only whitespace characters is not an empty variable.
-
-#### Regex pattern matching
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/43601) in GitLab 11.0
-
-Examples:
-
-- `=~`: True if pattern is matched. Ex: `$VARIABLE =~ /^content.*/`
-- `!~`: True if pattern is not matched. Ex: `$VARIABLE_1 !~ /^content.*/` ([Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/61900) in GitLab 11.11)
-
-Variable pattern matching with regular expressions uses the
-[RE2 regular expression syntax](https://github.com/google/re2/wiki/Syntax).
-Expressions evaluate as `true` if:
-
-- Matches are found when using `=~`.
-- Matches are *not* found when using `!~`.
-
-Pattern matching is case-sensitive by default. Use `i` flag modifier, like
-`/pattern/i` to make a pattern case-insensitive.
-
-#### Conjunction / Disjunction
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/62867) in GitLab 12.0
-
-Examples:
-
-- `$VARIABLE1 =~ /^content.*/ && $VARIABLE2 == "something"`
-- `$VARIABLE1 =~ /^content.*/ && $VARIABLE2 =~ /thing$/ && $VARIABLE3`
-- `$VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/ && $VARIABLE3`
-
-It is possible to join multiple conditions using `&&` or `||`. Any of the otherwise
-supported syntax may be used in a conjunctive or disjunctive statement.
-Precedence of operators follows the
-[Ruby 2.5 standard](https://ruby-doc.org/core-2.5.0/doc/syntax/precedence_rdoc.html),
-so `&&` is evaluated before `||`.
-
-#### Parentheses
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230938) in GitLab 13.3.
-> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/238174) in GitLab 13.5.
-
-It is possible to use parentheses to group conditions. Parentheses have the highest
-precedence of all operators. Expressions enclosed in parentheses are evaluated first,
-and the result is used for the rest of the expression.
-
-Many nested parentheses can be used to create complex conditions, and the inner-most
-expressions in parentheses are evaluated first. For an expression to be valid an equal
-number of `(` and `)` need to be used.
-
-Examples:
-
-- `($VARIABLE1 =~ /^content.*/ || $VARIABLE2) && ($VARIABLE3 =~ /thing$/ || $VARIABLE4)`
-- `($VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/) && $VARIABLE3`
-- `$CI_COMMIT_BRANCH == "my-branch" || (($VARIABLE1 == "thing" || $VARIABLE2 == "thing") && $VARIABLE3)`
-
## Debug logging
> Introduced in GitLab Runner 1.7.
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index ea54891a99b..c449d2f86da 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -174,7 +174,7 @@ They are:
- Script execution shell.
- Not supported:
- For definitions where the ["Expansion place"](#gitlab-ciyml-file) is GitLab.
- - In the `only` and `except` [variables expressions](README.md#cicd-variable-expressions).
+ - In the `only` and `except` [variables expressions](../jobs/job_control.md#cicd-variable-expressions).
Some of the persisted variables contain tokens and cannot be used by some definitions
due to security reasons.
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 9d2ce998c25..446be98ec27 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1323,8 +1323,8 @@ Use `rules:if` clauses to specify when to add a job to a pipeline:
`rules:if` differs slightly from `only:variables` by accepting only a single
expression string per rule, rather than an array of them. Any set of expressions to be
-evaluated can be [conjoined into a single expression](../variables/README.md#conjunction--disjunction)
-by using `&&` or `||`, and the [variable matching operators (`==`, `!=`, `=~` and `!~`)](../variables/README.md#syntax-of-cicd-variable-expressions).
+evaluated can be [conjoined into a single expression](../jobs/job_control.md#join-variable-expressions-together-with--or-)
+by using `&&` or `||`, and the [variable matching operators (`==`, `!=`, `=~` and `!~`)](../jobs/job_control.md#cicd-variable-expressions).
Unlike variables in [`script`](../variables/README.md#use-cicd-variables-in-job-scripts)
sections, variables in rules expressions are always formatted as `$VARIABLE`.
@@ -1598,7 +1598,7 @@ considered for their usage and behavior in this context. Future keyword improvem
are being discussed in our [epic for improving `rules`](https://gitlab.com/groups/gitlab-org/-/epics/2783),
where anyone can add suggestions or requests.
-You can use [parentheses](../variables/README.md#parentheses) with `&&` and `||` to build more complicated variable expressions.
+You can use [parentheses](../jobs/job_control.md#group-variable-expressions-together-with-parentheses) with `&&` and `||` to build more complicated variable expressions.
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230938) in GitLab 13.3:
```yaml
@@ -1726,7 +1726,7 @@ to a pipeline, based on the status of [CI/CD variables](../variables/README.md).
**Keyword type**: Job keyword. You can use it only as part of a job.
-**Possible inputs**: An array of [CI/CD variable expressions](../variables/README.md#cicd-variable-expressions).
+**Possible inputs**: An array of [CI/CD variable expressions](../jobs/job_control.md#cicd-variable-expressions).
**Example of `only:variables`**:
diff --git a/doc/development/polymorphic_associations.md b/doc/development/polymorphic_associations.md
index b71e66c8671..f341255a7e1 100644
--- a/doc/development/polymorphic_associations.md
+++ b/doc/development/polymorphic_associations.md
@@ -62,19 +62,19 @@ AND source_id = 13083;
```
Here PostgreSQL can perform the query quite efficiently if both columns are
-indexed, but as the query gets more complex it may not be able to use these
-indexes efficiently.
+indexed. As the query gets more complex, it may not be able to use these
+indexes effectively.
## Mixed Responsibilities
-Similar to functions and classes a table should have a single responsibility:
+Similar to functions and classes, a table should have a single responsibility:
storing data with a certain set of pre-defined columns. When using polymorphic
-associations you are instead storing different types of data (possibly with
+associations, you are storing different types of data (possibly with
different columns set) in the same table.
## The Solution
-Fortunately there is a very simple solution to these problems: use a
+Fortunately, there is a solution to these problems: use a
separate table for every type you would otherwise store in the same table. Using
a separate table allows you to use everything a database may provide to ensure
consistency and query data efficiently, without any additional application logic
@@ -120,8 +120,8 @@ FROM pending_group_members
WHERE group_id = 4
```
-If you want to get both you can use a UNION, though you need to be explicit
-about what columns you want to SELECT as otherwise the result set uses the
+If you want to get both you can use a `UNION`, though you need to be explicit
+about what columns you want to `SELECT` as otherwise the result set uses the
columns of the first query. For example:
```sql
diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md
index 103166fab0f..4b31fd113e1 100644
--- a/doc/development/usage_ping/dictionary.md
+++ b/doc/development/usage_ping/dictionary.md
@@ -6396,7 +6396,7 @@ Tiers: `free`, `premium`, `ultimate`
### `counts.user_preferences_group_overview_details`
-Count of users who set personal preference to see Details on Group overview page
+Count of users who set personal preference to see Details on Group information page
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216182203_user_preferences_group_overview_details.yml)
@@ -6408,7 +6408,7 @@ Tiers: `ultimate`
### `counts.user_preferences_group_overview_security_dashboard`
-Count of users who set personal preference to see Security Dashboard on Group overview page
+Count of users who set personal preference to see Security Dashboard on Group information page
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216182205_user_preferences_group_overview_security_dashboard.yml)
@@ -17440,7 +17440,7 @@ Tiers: `ultimate`
### `usage_activity_by_stage.secure.user_preferences_group_overview_security_dashboard`
-Users who set personal preference to see Details on Group overview page
+Users who set personal preference to see Details on Group information page
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216182207_user_preferences_group_overview_security_dashboard.yml)
@@ -19480,7 +19480,7 @@ Tiers: `ultimate`
### `usage_activity_by_stage_monthly.secure.user_preferences_group_overview_security_dashboard`
-Users who set personal preference to see Security Dashboard on Group overview page
+Users who set personal preference to see Security Dashboard on Group information page
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210216182209_user_preferences_group_overview_security_dashboard.yml)
diff --git a/doc/install/installation.md b/doc/install/installation.md
index dc16846df80..572c6de18d0 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -292,7 +292,7 @@ sudo adduser --disabled-login --gecos 'GitLab' git
## 6. Database
NOTE:
-In GitLab 12.1 and later, only PostgreSQL is supported. In GitLab 13.0 and later, we [require PostgreSQL 11+](requirements.md#postgresql-requirements).
+In GitLab 12.1 and later, only PostgreSQL is supported. In GitLab 14.0 and later, we [require PostgreSQL 12+](requirements.md#postgresql-requirements).
1. Install the database packages.
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index f8f43c733c3..3a8b7bf1004 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -116,6 +116,7 @@ the following table) as these were used for development and testing:
|----------------|----------------------------|
| 10.0 | 9.6 |
| 13.0 | 11 |
+| 14.0 | 12 |
You must also ensure the following extensions are loaded into every
GitLab database. [Read more about this requirement, and troubleshooting](postgresql_extensions.md).
diff --git a/doc/integration/sourcegraph.md b/doc/integration/sourcegraph.md
index d068aabed41..86ca389e9b2 100644
--- a/doc/integration/sourcegraph.md
+++ b/doc/integration/sourcegraph.md
@@ -76,7 +76,8 @@ You can skip this step if you already have your GitLab repositories searchable i
### Configure your GitLab instance with Sourcegraph
-1. In GitLab, go to **Admin Area > Settings > General**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**.
1. Expand the **Sourcegraph** configuration section.
1. Check **Enable Sourcegraph**.
1. Set the Sourcegraph URL to your Sourcegraph instance, such as `https://sourcegraph.example.com`.
diff --git a/doc/push_rules/push_rules.md b/doc/push_rules/push_rules.md
index 581f01c5ae3..34a63f425eb 100644
--- a/doc/push_rules/push_rules.md
+++ b/doc/push_rules/push_rules.md
@@ -82,14 +82,19 @@ See [server hooks](../administration/server_hooks.md) for more information.
## Enabling push rules
-NOTE:
-GitLab administrators can set push rules globally under
-**Admin Area > Push Rules** that all new projects inherit. You can later
-override them in a project's settings. They can be also set on a [group level](../user/group/index.md#group-push-rules).
+You can create push rules for all new projects to inherit, but they can be overridden
+at the project level or the [group level](../user/group/index.md#group-push-rules).
+
+To create global push rules:
+
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Push rules**.
+
+To override global push rules in a project's settings:
-1. Navigate to your project's **Settings > Repository** and expand **Push rules**
-1. Set the rule you want
-1. Click **Save Push Rules** for the changes to take effect
+1. Navigate to your project's **Settings > Repository** and expand **Push rules**.
+1. Set the rule you want.
+1. Select **Save Push Rules** for the changes to take effect.
The following options are available:
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index 553c97f3008..39e25fc11e7 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -149,7 +149,7 @@ Remember to set `git -> bin_path` to `/usr/local/bin/git` in `config/gitlab.yml`
### 7. Update PostgreSQL
WARNING:
-From GitLab 13.0, you must use at least PostgreSQL 11.
+From GitLab 14.0, you must use at least PostgreSQL 12.
The latest version of GitLab might depend on a more recent PostgreSQL version
than what you're currently running. You may also need to enable some
diff --git a/doc/user/admin_area/diff_limits.md b/doc/user/admin_area/diff_limits.md
index 88d24680500..c6c2190b7c7 100644
--- a/doc/user/admin_area/diff_limits.md
+++ b/doc/user/admin_area/diff_limits.md
@@ -24,7 +24,8 @@ This affects merge requests and branch comparison views.
To set the maximum diff patch size:
-1. Go to the Admin Area (**{admin}**) and select **Settings > General**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**.
1. Expand **Diff limits**.
1. Enter a value for **Maximum diff patch size**, measured in bytes.
1. Select **Save changes**.
diff --git a/doc/user/admin_area/merge_requests_approvals.md b/doc/user/admin_area/merge_requests_approvals.md
index fadccadaf2c..b221b1d51a7 100644
--- a/doc/user/admin_area/merge_requests_approvals.md
+++ b/doc/user/admin_area/merge_requests_approvals.md
@@ -15,8 +15,8 @@ project level.
To enable merge request approval rules for an instance:
-1. Navigate to **Admin Area >** **{push-rules}** **Push Rules** and expand **Merge
-requests approvals**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **{push-rules}** **Push Rules**, and expand **Merge request (MR) approvals**.
1. Set the required rule.
1. Click **Save changes**.
diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md
index 651f65c49d4..f2a849e1894 100644
--- a/doc/user/admin_area/settings/account_and_limit_settings.md
+++ b/doc/user/admin_area/settings/account_and_limit_settings.md
@@ -9,18 +9,22 @@ type: reference
## Default projects limit
-You can change the default maximum number of projects that users can create in their personal namespace.
-Navigate to **Admin Area > Settings > General**, then expand **Account and Limit**.
-You can increase or decrease that `Default projects limit` value.
+You can change the default maximum number of projects that users can create in their personal namespace:
-- If you set `Default projects limit` to 0, users are not allowed to create projects
- in their users personal namespace. However, projects can still be created in a group.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**, then expand **Account and limit**.
+1. Increase or decrease that **Default projects limit** value.
+
+If you set **Default projects limit** to 0, users are not allowed to create projects
+in their users personal namespace. However, projects can still be created in a group.
## Max attachment size
-You can change the maximum file size for attachments in comments and replies in GitLab.
-Navigate to **Admin Area > Settings > General**, then expand **Account and Limit**.
-From here, you can increase or decrease by changing the value in `Maximum attachment size (MB)`.
+You can change the maximum file size for attachments in comments and replies in GitLab:
+
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**, then expand **Account and limit**.
+1. Increase or decrease by changing the value in **Maximum attachment size (MB)**.
NOTE:
If you choose a size larger than the configured value for the web server,
@@ -29,9 +33,11 @@ details.
## Max push size
-You can change the maximum push size for your repository.
-Navigate to **Admin Area > Settings > General**, then expand **Account and Limit**.
-From here, you can increase or decrease by changing the value in `Maximum push size (MB)`.
+You can change the maximum push size for your repository:
+
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**, then expand **Account and limit**.
+1. Increase or decrease by changing the value in **Maximum push size (MB)**.
NOTE:
When you [add files to a repository](../../project/repository/web_editor.md#create-a-file)
@@ -42,9 +48,11 @@ Use [Git LFS](../../../topics/git/lfs/index.md) to add large files to a reposito
## Max import size
-You can change the maximum file size for imports in GitLab.
-Navigate to **Admin Area > Settings > General**, then expand **Account and Limit**.
-From here, you can increase or decrease by changing the value in `Maximum import size (MB)`.
+You can change the maximum file size for imports in GitLab:
+
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**, then expand **Account and limit**.
+1. Increase or decrease by changing the value in **Maximum import size (MB)**.
NOTE:
If you choose a size larger than the configured value for the web server,
@@ -62,7 +70,8 @@ A prefix can help you identify PATs visually, as well as with automation tools.
Only a GitLab administrator can set the prefix, which is a global setting applied
to any PAT generated in the system by any user:
-1. Navigate to **Admin Area > Settings > General**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Personal Access Token prefix** field.
1. Click **Save changes**.
@@ -104,7 +113,8 @@ These settings can be found in:
1. Fill in the **Repository size limit (MB)** field in the **Naming, visibility** section.
1. Click **Save changes**.
- GitLab global settings:
- 1. From the Dashboard, navigate to **Admin Area > Settings > General**.
+ 1. On the top bar, select **Menu >** **{admin}** **Admin**.
+ 1. In the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Size limit per repository (MB)** field.
1. Click **Save changes**.
@@ -150,7 +160,8 @@ GitLab administrators can choose to customize the session duration (in minutes)
To set a limit on how long these sessions are valid:
-1. Navigate to **Admin Area > Settings > General**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Session duration for Git operations when 2FA is enabled (minutes)** field.
1. Click **Save changes**.
@@ -174,7 +185,8 @@ there are no restrictions.
To set a lifetime on how long personal access tokens are valid:
-1. Navigate to **Admin Area > Settings > General**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Maximum allowable lifetime for personal access tokens (days)** field.
1. Click **Save changes**.
@@ -196,7 +208,8 @@ By default, expired SSH keys **are not usable**.
To allow the use of expired SSH keys:
-1. Navigate to **Admin Area > Settings > General**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Uncheck the **Enforce SSH key expiration** checkbox.
@@ -212,7 +225,8 @@ You can allow the use of expired PATs with the following steps:
To do this:
-1. Navigate to **Admin Area > Settings > General**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Uncheck the **Enforce personal access token expiration** checkbox.
@@ -224,8 +238,9 @@ To maintain integrity of user details in [Audit Events](../../../administration/
To do this:
-1. Navigate to **Admin Area > Settings > General**, then expand **Account and Limit**.
-1. Check the **Prevent users from changing their profile name** checkbox.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**, then expand **Account and limit**.
+1. Select the **Prevent users from changing their profile name** checkbox.
NOTE:
When this ability is disabled, GitLab administrators can still use the
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
index b980e6c1c3c..6a5af09358d 100644
--- a/doc/user/admin_area/settings/index.md
+++ b/doc/user/admin_area/settings/index.md
@@ -9,13 +9,16 @@ type: index
As an administrator of a GitLab self-managed instance, you can manage the behavior of your deployment. To do so, select **Admin Area > Settings**.
-The admin area is not accessible on GitLab.com, and settings can only be changed by the
+The Admin Area is not accessible on GitLab.com, and settings can only be changed by the
GitLab.com administrators. See the [GitLab.com settings](../../gitlab_com/index.md)
documentation for all current settings and limits on the GitLab.com instance.
## General
-Access the default page for Admin Area settings by navigating to **Admin Area > Settings > General**:
+To access the default page for Admin Area settings:
+
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**.
| Option | Description |
| ------ | ----------- |
@@ -116,6 +119,11 @@ Access the default page for Admin Area settings by navigating to **Admin Area >
| [Gitaly timeouts](gitaly_timeouts.md) | Configure Gitaly timeouts. |
| Localization | [Default first day of the week](../../profile/preferences.md) and [Time tracking](../../project/time_tracking.md#limit-displayed-units-to-hours). |
-NOTE:
-You can change the [Default first day of the week](../../profile/preferences.md) for the entire GitLab instance
-in the **Localization** section of **Admin Area > Settings > Preferences**.
+### Default first day of the week
+
+You can change the [Default first day of the week](../../profile/preferences.md)
+for the entire GitLab instance:
+
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > Preferences**.
+1. Scroll to the **Localization** section, and select your desired first day of the week.
diff --git a/doc/user/admin_area/settings/instance_template_repository.md b/doc/user/admin_area/settings/instance_template_repository.md
index 600d99934aa..c8a4c2866ca 100644
--- a/doc/user/admin_area/settings/instance_template_repository.md
+++ b/doc/user/admin_area/settings/instance_template_repository.md
@@ -17,12 +17,17 @@ while the project remains secure.
## Configuration
-As an administrator, navigate to **Admin Area > Settings > Templates** and
-select the project to serve as the custom template repository.
+To select a project to serve as the custom template repository:
-![File templates in the Admin Area](img/file_template_admin_area.png)
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > Templates**.
+1. Select the project:
-After that, you can add custom templates to the selected repository and use them for the entire instance.
+ ![File templates in the Admin Area](img/file_template_admin_area.png)
+
+1. Add custom templates to the selected repository.
+
+After you add templates, you can use them for the entire instance.
They are available in the [Web Editor's dropdown](../../project/repository/web_editor.md#template-dropdowns)
and through the [API settings](../../../api/settings.md).
diff --git a/doc/user/admin_area/settings/push_event_activities_limit.md b/doc/user/admin_area/settings/push_event_activities_limit.md
index 7032c1031a9..21e4f32ff8d 100644
--- a/doc/user/admin_area/settings/push_event_activities_limit.md
+++ b/doc/user/admin_area/settings/push_event_activities_limit.md
@@ -23,9 +23,14 @@ branches), only 1 bulk push event is created instead of 1,000 push
events. This helps in maintaining good system performance and preventing spam on
the activity feed.
-This setting can be modified in **Admin Area > Settings > Network > Performance Optimization**.
-This can also be configured via the [Application settings API](../../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls)
-as `push_event_activities_limit`. The default value is 3, but it can be greater
-than or equal 0.
+To modify this setting:
+
+- In the Admin Area:
+ 1. On the top bar, select **Menu >** **{admin}** **Admin**.
+ 1. In the left sidebar, select **Settings > Network**, then expand **Performance optimization**.
+- Through the [Application settings API](../../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls)
+ as `push_event_activities_limit`.
+
+The default value is 3, but it can be greater than or equal 0.
![Push event activities limit](img/push_event_activities_limit_v12_4.png)
diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md
index d8d13fd0039..752856371bf 100644
--- a/doc/user/admin_area/settings/visibility_and_access_controls.md
+++ b/doc/user/admin_area/settings/visibility_and_access_controls.md
@@ -11,8 +11,9 @@ GitLab allows administrators to enforce specific controls.
To access the visibility and access control options:
-1. Sign in to GitLab as a user with Administrator [permissions](../../permissions.md).
-1. Go to **Admin Area > Settings > General**.
+1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
## Default branch protection
diff --git a/doc/user/group/saml_sso/img/member_enterprise_badge_v14_0.png b/doc/user/group/saml_sso/img/member_enterprise_badge_v14_0.png
new file mode 100644
index 00000000000..f9534b14a51
--- /dev/null
+++ b/doc/user/group/saml_sso/img/member_enterprise_badge_v14_0.png
Binary files differ
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index 65b3e2d095d..fd75c49fa6c 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -175,6 +175,10 @@ We recommend users do this prior to turning on sync, because while synchronizati
New users and existing users on subsequent visits can access the group through the identify provider's dashboard or by visiting links directly.
+[In GitLab 14.0 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/325712), GitLab users created with a SCIM identity display with an **Enterprise** badge in the **Members** view.
+
+![Enterprise badge for users created with a SCIM identity](img/member_enterprise_badge_v14_0.png)
+
For role information, please see the [Group SAML page](index.md#user-access-and-management)
### Blocking access
diff --git a/doc/user/packages/terraform_module_registry/index.md b/doc/user/packages/terraform_module_registry/index.md
index 95d6ab069f8..efb2b8ddf8e 100644
--- a/doc/user/packages/terraform_module_registry/index.md
+++ b/doc/user/packages/terraform_module_registry/index.md
@@ -15,9 +15,8 @@ as a Terraform module registry.
To authenticate to the Terraform module registry, you need either:
-- A [personal access token](../../../api/README.md#personalproject-access-tokens).
+- A [personal access token](../../../api/README.md#personalproject-access-tokens) with at least `read_api` rights.
- A [CI/CD job token](../../../api/README.md#gitlab-cicd-job-token).
-- A [deploy token](../../project/deploy_tokens/index.md).
## Publish a Terraform Module
@@ -78,9 +77,9 @@ Example response:
Prerequisites:
-- You need to [authenticate with the API](../../../api/README.md#authentication). If authenticating with a deploy token, it must be configured with the `read_package_registry` and/or `write_package_registry` scope.
+- You need to [authenticate with the API](../../../api/README.md#authentication). If authenticating with a personal access token, it must be configured with the `read_api` scope.
-Authentication tokens (Deploy Token, Job Token, or Personal Access Token) can be provided for `terraform` in your `~/.terraformrc` file:
+Authentication tokens (Job Token or Personal Access Token) can be provided for `terraform` in your `~/.terraformrc` file:
```plaintext
credentials "gitlab.com" {
@@ -116,3 +115,10 @@ upload:
script:
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file path/to/file.tgz "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/terraform/modules/my-module/my-system/0.0.1/file"'
```
+
+## Example projects
+
+For examples of the Terraform module registry, check the projects below:
+
+- The [_GitLab local file_ project](https://gitlab.com/mattkasa/gitlab-local-file) creates a minimal Terraform module and uploads it into the Terraform module registry using GitLab CI/CD.
+- The [_Terraform module test_ project](https://gitlab.com/mattkasa/terraform-module-test) uses the module from the previous example.
diff --git a/doc/user/project/merge_requests/approvals/index.md b/doc/user/project/merge_requests/approvals/index.md
index daea0dff26d..3c47c2af344 100644
--- a/doc/user/project/merge_requests/approvals/index.md
+++ b/doc/user/project/merge_requests/approvals/index.md
@@ -97,36 +97,6 @@ Without the approvals, the work cannot merge. Required approvals enable multiple
- [Require approval from a security team](../../../application_security/index.md#security-approvals-in-merge-requests)
before merging code that could introduce a vulnerability. **(ULTIMATE)**
-## Notify external services **(ULTIMATE)**
-
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3869) in GitLab Ultimate 13.10.
-> - [Deployed behind a feature flag](../../../feature_flags.md), disabled by default.
-> - Disabled on GitLab.com.
-> - Not recommended for production use.
-> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](../../../../api/merge_request_approvals.md#enable-or-disable-external-project-level-mr-approvals). **(ULTIMATE SELF)**
-
-WARNING:
-This feature might not be available to you. Check the **version history** note above for details.
-
-You can create an external approval rule to integrate approvals with third-party tools.
-When users create, change, or close merge requests, GitLab sends a notification.
-The users of the third-party tools can then approve merge requests from outside of GitLab.
-
-With this integration, you can integrate with third-party workflow tools, like
-[ServiceNow](https://www.servicenow.co.uk/), or the custom tool of your choice.
-You can modify your external approval rules
-[by using the REST API](../../../../api/merge_request_approvals.md#external-project-level-mr-approvals).
-
-The lack of an external approval doesn't block the merging of a merge request.
-
-When [approval rule overrides](settings.md#prevent-overrides-of-default-approvals) are allowed,
-changes to default approval rules will **not** be applied to existing
-merge requests, except for changes to the [target branch](rules.md#approvals-for-protected-branches)
-of the rule.
-
-To learn more about use cases, feature discovery, and development timelines,
-see the [External API approval rules epic](https://gitlab.com/groups/gitlab-org/-/epics/3869).
-
## Related links
- [Merge request approvals API](../../../../api/merge_request_approvals.md)
diff --git a/doc/user/project/repository/branches/default.md b/doc/user/project/repository/branches/default.md
index deacf119d38..6c2469ac377 100644
--- a/doc/user/project/repository/branches/default.md
+++ b/doc/user/project/repository/branches/default.md
@@ -65,7 +65,8 @@ GitLab [administrators](../../../permissions.md) of self-managed instances can
customize the initial branch for projects hosted on that instance. Individual
groups and subgroups can override this instance-wide setting for their projects.
-1. Go to **Admin Area > Settings > Repository**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. In the left sidebar, select **Settings > Repository**.
1. Expand **Default initial branch name**.
1. Change the default initial branch to a custom name of your choice.
1. Select **Save changes**.
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index f50a5608ddb..0e597725611 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -267,7 +267,7 @@ a `main` entry point inside the Web IDE.
Live Preview is enabled for all projects on GitLab.com. If you are an administrator
of a self-managed GitLab instance, and you want to enable Live Preview:
-1. In the top navigation bar, go to **Admin Area**.
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Scroll to **Web IDE** and select **Expand**:
![Administrator Live Preview setting](img/admin_live_preview_v13_0.png)
diff --git a/lib/api/api.rb b/lib/api/api.rb
index c952ae4d982..b551c42d004 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -228,6 +228,7 @@ module API
mount ::API::PagesDomains
mount ::API::ProjectClusters
mount ::API::ProjectContainerRepositories
+ mount ::API::ProjectDebianDistributions
mount ::API::ProjectEvents
mount ::API::ProjectExport
mount ::API::ProjectImport
diff --git a/lib/api/concerns/packages/debian_distribution_endpoints.rb b/lib/api/concerns/packages/debian_distribution_endpoints.rb
new file mode 100644
index 00000000000..4670c3e3521
--- /dev/null
+++ b/lib/api/concerns/packages/debian_distribution_endpoints.rb
@@ -0,0 +1,152 @@
+# frozen_string_literal: true
+
+module API
+ module Concerns
+ module Packages
+ module DebianDistributionEndpoints
+ extend ActiveSupport::Concern
+
+ included do
+ include PaginationParams
+
+ feature_category :package_registry
+
+ helpers ::API::Helpers::PackagesHelpers
+ helpers ::API::Helpers::Packages::BasicAuthHelpers
+ include ::API::Helpers::Authentication
+
+ namespace 'debian_distributions' do
+ helpers do
+ params :optional_distribution_params do
+ optional :suite, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Suite'
+ optional :origin, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Origin'
+ optional :label, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Label'
+ optional :version, type: String, regexp: Gitlab::Regex.debian_version_regex, desc: 'The Debian Version'
+ optional :description, type: String, desc: 'The Debian Description'
+ optional :valid_time_duration_seconds, type: Integer, desc: 'The duration before the Release file should be considered expired by the client'
+
+ optional :components, type: Array[String],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ regexp: Gitlab::Regex.debian_component_regex,
+ desc: 'The list of Components'
+ optional :architectures, type: Array[String],
+ coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
+ regexp: Gitlab::Regex.debian_architecture_regex,
+ desc: 'The list of Architectures'
+ end
+ end
+
+ authenticate_with do |accept|
+ accept.token_types(:personal_access_token, :deploy_token, :job_token)
+ .sent_through(:http_basic_auth)
+ end
+
+ content_type :json, 'application/json'
+ format :json
+
+ # POST {projects|groups}/:id/debian_distributions
+ desc 'Create a Debian Distribution' do
+ detail 'This feature was introduced in 14.0'
+ success ::API::Entities::Packages::Debian::Distribution
+ end
+
+ params do
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ use :optional_distribution_params
+ end
+ post '/' do
+ authorize_create_package!(project_or_group)
+
+ distribution_params = declared_params(include_missing: false)
+ result = ::Packages::Debian::CreateDistributionService.new(project_or_group, current_user, distribution_params).execute
+ distribution = result.payload[:distribution]
+
+ if result.success?
+ present distribution, with: ::API::Entities::Packages::Debian::Distribution
+ else
+ render_validation_error!(distribution)
+ end
+ end
+
+ # GET {projects|groups}/:id/debian_distributions
+ desc 'Get a list of Debian Distributions' do
+ detail 'This feature was introduced in 14.0'
+ success ::API::Entities::Packages::Debian::Distribution
+ end
+
+ params do
+ use :pagination
+ optional :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ use :optional_distribution_params
+ end
+ get '/' do
+ distribution_params = declared_params(include_missing: false)
+ distributions = ::Packages::Debian::DistributionsFinder.new(project_or_group, distribution_params).execute
+
+ present paginate(distributions), with: ::API::Entities::Packages::Debian::Distribution
+ end
+
+ # GET {projects|groups}/:id/debian_distributions/:codename
+ desc 'Get a Debian Distribution' do
+ detail 'This feature was introduced in 14.0'
+ success ::API::Entities::Packages::Debian::Distribution
+ end
+
+ params do
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ end
+ get '/:codename' do
+ distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last!
+
+ present distribution, with: ::API::Entities::Packages::Debian::Distribution
+ end
+
+ # PUT {projects|groups}/:id/debian_distributions/:codename
+ desc 'Update a Debian Distribution' do
+ detail 'This feature was introduced in 14.0'
+ success ::API::Entities::Packages::Debian::Distribution
+ end
+
+ params do
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ use :optional_distribution_params
+ end
+ put '/:codename' do
+ authorize_create_package!(project_or_group)
+
+ distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last!
+ distribution_params = declared_params(include_missing: false).except(:codename)
+ result = ::Packages::Debian::UpdateDistributionService.new(distribution, distribution_params).execute
+ distribution = result.payload[:distribution]
+
+ if result.success?
+ present distribution, with: ::API::Entities::Packages::Debian::Distribution
+ else
+ render_validation_error!(distribution)
+ end
+ end
+
+ # DELETE {projects|groups}/:id/debian_distributions/:codename
+ desc 'Delete a Debian Distribution' do
+ detail 'This feature was introduced in 14.0'
+ end
+
+ params do
+ requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename'
+ use :optional_distribution_params
+ end
+ delete '/:codename' do
+ authorize_destroy_package!(project_or_group)
+
+ distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last!
+
+ accepted! if distribution.destroy
+
+ render_api_error!('Failed to delete distribution', 400)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/concerns/packages/debian_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb
index 6fc7c439464..c79ae3068b4 100644
--- a/lib/api/concerns/packages/debian_endpoints.rb
+++ b/lib/api/concerns/packages/debian_package_endpoints.rb
@@ -3,7 +3,7 @@
module API
module Concerns
module Packages
- module DebianEndpoints
+ module DebianPackageEndpoints
extend ActiveSupport::Concern
DISTRIBUTION_REGEX = %r{[a-zA-Z0-9][a-zA-Z0-9.-]*}.freeze
@@ -32,23 +32,17 @@ module API
helpers ::API::Helpers::PackagesHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
+ include ::API::Helpers::Authentication
- format :txt
- content_type :txt, 'text/plain'
-
- rescue_from ArgumentError do |e|
- render_api_error!(e.message, 400)
- end
-
- rescue_from ActiveRecord::RecordInvalid do |e|
- render_api_error!(e.message, 400)
- end
+ namespace 'packages/debian' do
+ authenticate_with do |accept|
+ accept.token_types(:personal_access_token, :deploy_token, :job_token)
+ .sent_through(:http_basic_auth)
+ end
- before do
- require_packages_enabled!
- end
+ format :txt
+ content_type :txt, 'text/plain'
- namespace 'packages/debian' do
params do
requires :distribution, type: String, desc: 'The Debian Codename', regexp: Gitlab::Regex.debian_distribution_regex
end
@@ -59,7 +53,7 @@ module API
detail 'This feature was introduced in GitLab 13.5'
end
- route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true
+ route_setting :authentication, authenticate_non_public: true
get 'Release.gpg' do
not_found!
end
@@ -69,7 +63,7 @@ module API
detail 'This feature was introduced in GitLab 13.5'
end
- route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true
+ route_setting :authentication, authenticate_non_public: true
get 'Release' do
# https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286
'TODO Release'
@@ -80,7 +74,7 @@ module API
detail 'This feature was introduced in GitLab 13.5'
end
- route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true
+ route_setting :authentication, authenticate_non_public: true
get 'InRelease' do
not_found!
end
@@ -96,7 +90,7 @@ module API
detail 'This feature was introduced in GitLab 13.5'
end
- route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true
+ route_setting :authentication, authenticate_non_public: true
get 'Packages' do
# https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286
'TODO Packages'
@@ -119,7 +113,7 @@ module API
detail 'This feature was introduced in GitLab 13.5'
end
- route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true
+ route_setting :authentication, authenticate_non_public: true
get ':file_name', requirements: FILE_NAME_REQUIREMENTS do
# https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286
'TODO File'
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
index 06edab662bf..c6116a8b28f 100644
--- a/lib/api/debian_group_packages.rb
+++ b/lib/api/debian_group_packages.rb
@@ -7,6 +7,14 @@ module API
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ rescue_from ArgumentError do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ rescue_from ActiveRecord::RecordInvalid do |e|
+ render_api_error!(e.message, 400)
+ end
+
before do
require_packages_enabled!
@@ -16,7 +24,7 @@ module API
end
namespace ':id/-' do
- include ::API::Concerns::Packages::DebianEndpoints
+ include ::API::Concerns::Packages::DebianPackageEndpoints
end
end
end
diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb
index eadb0646a67..70ddf9dea37 100644
--- a/lib/api/debian_project_packages.rb
+++ b/lib/api/debian_project_packages.rb
@@ -7,7 +7,15 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before do
+ rescue_from ArgumentError do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ rescue_from ActiveRecord::RecordInvalid do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ after_validation do
require_packages_enabled!
not_found! unless ::Feature.enabled?(:debian_packages, user_project)
@@ -16,13 +24,20 @@ module API
end
namespace ':id' do
- include ::API::Concerns::Packages::DebianEndpoints
+ helpers do
+ def project_or_group
+ user_project
+ end
+ end
+
+ include ::API::Concerns::Packages::DebianPackageEndpoints
params do
requires :file_name, type: String, desc: 'The file name'
end
namespace 'packages/debian/:file_name', requirements: FILE_NAME_REQUIREMENTS do
+ format :txt
content_type :json, Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
# PUT {projects|groups}/:id/packages/debian/:file_name
@@ -35,8 +50,6 @@ module API
authorize_upload!(authorized_user_project)
bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:debian_max_file_size, params[:file].size)
- track_package_event('push_package', :debian, user: current_user, project: authorized_user_project, namespace: authorized_user_project.namespace)
-
file_params = {
file: params['file'],
file_name: params['file_name'],
@@ -52,6 +65,7 @@ module API
::Packages::Debian::ProcessChangesWorker.perform_async(package_file.id, current_user.id) # rubocop:disable CodeReuse/Worker
end
+ track_package_event('push_package', :debian, user: current_user, project: authorized_user_project, namespace: authorized_user_project.namespace)
created!
rescue ObjectStorage::RemoteStoreError => e
Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: authorized_user_project.id })
diff --git a/lib/api/entities/packages/debian/distribution.rb b/lib/api/entities/packages/debian/distribution.rb
new file mode 100644
index 00000000000..97a3c479f40
--- /dev/null
+++ b/lib/api/entities/packages/debian/distribution.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Packages
+ module Debian
+ class Distribution < Grape::Entity
+ expose :id
+ expose :codename
+ expose :suite
+ expose :origin
+ expose :label
+ expose :version
+ expose :description
+ expose :valid_time_duration_seconds
+
+ expose :component_names, as: :components
+ expose :architecture_names, as: :architectures
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/project_debian_distributions.rb b/lib/api/project_debian_distributions.rb
new file mode 100644
index 00000000000..58edf51f4f7
--- /dev/null
+++ b/lib/api/project_debian_distributions.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module API
+ class ProjectDebianDistributions < ::API::Base
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ rescue_from ArgumentError do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ rescue_from ActiveRecord::RecordInvalid do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ after_validation do
+ require_packages_enabled!
+
+ not_found! unless ::Feature.enabled?(:debian_packages, user_project)
+
+ authorize_read_package!
+ end
+
+ namespace ':id' do
+ helpers do
+ def project_or_group
+ user_project
+ end
+ end
+
+ include ::API::Concerns::Packages::DebianDistributionEndpoints
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 14a65ac1f5d..88419ff6cec 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -14,7 +14,7 @@ module Gitlab
# Minimum PostgreSQL version requirement per documentation:
# https://docs.gitlab.com/ee/install/requirements.html#postgresql-requirements
- MINIMUM_POSTGRES_VERSION = 11
+ MINIMUM_POSTGRES_VERSION = 12
# https://www.postgresql.org/docs/9.2/static/datatype-numeric.html
MAX_INT_VALUE = 2147483647
diff --git a/lib/gitlab/database/load_balancing/connection_proxy.rb b/lib/gitlab/database/load_balancing/connection_proxy.rb
index 43edc99b961..3a09689a724 100644
--- a/lib/gitlab/database/load_balancing/connection_proxy.rb
+++ b/lib/gitlab/database/load_balancing/connection_proxy.rb
@@ -29,6 +29,7 @@ module Gitlab
sanitize_limit
select
select_one
+ select_rows
quote_column_name
).freeze
diff --git a/lib/gitlab/diff/file_collection/base.rb b/lib/gitlab/diff/file_collection/base.rb
index 9ed03c05f0b..f3f0f227a8c 100644
--- a/lib/gitlab/diff/file_collection/base.rb
+++ b/lib/gitlab/diff/file_collection/base.rb
@@ -19,6 +19,7 @@ module Gitlab
@diffable = diffable
@include_stats = diff_options.delete(:include_stats)
+ @pagination_data = diff_options.delete(:pagination_data)
@project = project
@diff_options = diff_options
@diff_refs = diff_refs
@@ -47,11 +48,7 @@ module Gitlab
end
def pagination_data
- {
- current_page: nil,
- next_page: nil,
- total_pages: nil
- }
+ @pagination_data || empty_pagination_data
end
# This mutates `diff_files` lines.
@@ -90,6 +87,14 @@ module Gitlab
private
+ def empty_pagination_data
+ {
+ current_page: nil,
+ next_page: nil,
+ total_pages: nil
+ }
+ end
+
def diff_stats_collection
strong_memoize(:diff_stats) do
next unless fetch_diff_stats?
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb b/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb
index 64523f3b730..5ff7c88970c 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb
@@ -21,9 +21,9 @@ module Gitlab
@paginated_collection = load_paginated_collection(batch_page, batch_size, diff_options)
@pagination_data = {
- current_page: batch_gradual_load? ? nil : @paginated_collection.current_page,
- next_page: batch_gradual_load? ? nil : @paginated_collection.next_page,
- total_pages: batch_gradual_load? ? relation.size : @paginated_collection.total_pages
+ current_page: current_page,
+ next_page: next_page,
+ total_pages: total_pages
}
end
@@ -62,6 +62,24 @@ module Gitlab
@merge_request_diff.merge_request_diff_files
end
+ def current_page
+ return if @paginated_collection.blank?
+
+ batch_gradual_load? ? nil : @paginated_collection.current_page
+ end
+
+ def next_page
+ return if @paginated_collection.blank?
+
+ batch_gradual_load? ? nil : @paginated_collection.next_page
+ end
+
+ def total_pages
+ return if @paginated_collection.blank?
+
+ batch_gradual_load? ? relation.size : @paginated_collection.total_pages
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def load_paginated_collection(batch_page, batch_size, diff_options)
batch_page ||= DEFAULT_BATCH_PAGE
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 40e0483e3c2..63be6e28249 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12382,6 +12382,9 @@ msgstr ""
msgid "Enter your password to approve"
msgstr ""
+msgid "Enterprise"
+msgstr ""
+
msgid "Environment"
msgstr ""
@@ -17441,6 +17444,18 @@ msgstr ""
msgid "Infrastructure Registry"
msgstr ""
+msgid "InfrastructureRegistry|Copy Terraform Command"
+msgstr ""
+
+msgid "InfrastructureRegistry|Copy Terraform Setup Command"
+msgstr ""
+
+msgid "InfrastructureRegistry|Copy and paste into your Terraform configuration, insert the variables, and run Terraform init:"
+msgstr ""
+
+msgid "InfrastructureRegistry|For more information on the Terraform registry, %{linkStart}see our documentation%{linkEnd}."
+msgstr ""
+
msgid "InfrastructureRegistry|Infrastructure Registry"
msgstr ""
@@ -17453,6 +17468,9 @@ msgstr ""
msgid "InfrastructureRegistry|Terraform modules are the main way to package and reuse resource configurations with Terraform. Learn more about how to %{noPackagesLinkStart}create Terraform modules%{noPackagesLinkEnd} in GitLab."
msgstr ""
+msgid "InfrastructureRegistry|To authorize access to the Terraform registry:"
+msgstr ""
+
msgid "InfrastructureRegistry|You have no Terraform modules in your project"
msgstr ""
@@ -26594,6 +26612,9 @@ msgstr ""
msgid "Provider"
msgstr ""
+msgid "Provision instructions"
+msgstr ""
+
msgid "Provisioned by:"
msgstr ""
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index 325d675a68c..8b02cfa30ab 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -74,6 +74,7 @@ RSpec.describe 'Database schema' do
slack_integrations: %w[team_id user_id],
snippets: %w[author_id],
spam_logs: %w[user_id],
+ status_check_responses: %w[external_approval_rule_id],
subscriptions: %w[user_id subscribable_id],
suggestions: %w[commit_id],
taggings: %w[tag_id taggable_id tagger_id],
diff --git a/spec/frontend/editor/editor_ci_schema_ext_spec.js b/spec/frontend/editor/editor_ci_schema_ext_spec.js
index 17a9ae7335f..2f0ecfb151e 100644
--- a/spec/frontend/editor/editor_ci_schema_ext_spec.js
+++ b/spec/frontend/editor/editor_ci_schema_ext_spec.js
@@ -4,6 +4,8 @@ import { EXTENSION_CI_SCHEMA_FILE_NAME_MATCH } from '~/editor/constants';
import EditorLite from '~/editor/editor_lite';
import { CiSchemaExtension } from '~/editor/extensions/editor_ci_schema_ext';
+const mockRef = 'AABBCCDD';
+
describe('~/editor/editor_ci_config_ext', () => {
const defaultBlobPath = '.gitlab-ci.yml';
@@ -75,8 +77,6 @@ describe('~/editor/editor_ci_config_ext', () => {
});
it('with an schema uri that contains project and ref', () => {
- const mockRef = 'AABBCCDD';
-
instance.registerCiSchema({
projectNamespace: mockProjectNamespace,
projectPath: mockProjectPath,
@@ -95,10 +95,11 @@ describe('~/editor/editor_ci_config_ext', () => {
instance.registerCiSchema({
projectNamespace: mockProjectNamespace,
projectPath: mockProjectPath,
+ ref: mockRef,
});
expect(getConfiguredYmlSchema()).toEqual({
- uri: `${TEST_HOST}/${mockProjectNamespace}/${mockProjectPath}/-/schema/master/${EXTENSION_CI_SCHEMA_FILE_NAME_MATCH}`,
+ uri: `${TEST_HOST}/${mockProjectNamespace}/${mockProjectPath}/-/schema/${mockRef}/${EXTENSION_CI_SCHEMA_FILE_NAME_MATCH}`,
fileMatch: ['another-ci-filename.yml'],
});
});
diff --git a/spec/frontend/members/mock_data.js b/spec/frontend/members/mock_data.js
index 678cea87dba..4275db5fa9f 100644
--- a/spec/frontend/members/mock_data.js
+++ b/spec/frontend/members/mock_data.js
@@ -30,6 +30,7 @@ export const member = {
usingLicense: false,
groupSso: false,
groupManagedAccount: false,
+ provisionedByThisGroup: false,
validRoles: {
Guest: 10,
Reporter: 20,
diff --git a/spec/frontend/packages/details/components/app_spec.js b/spec/frontend/packages/details/components/app_spec.js
index 4b890f868f4..3132ec61942 100644
--- a/spec/frontend/packages/details/components/app_spec.js
+++ b/spec/frontend/packages/details/components/app_spec.js
@@ -1,5 +1,6 @@
import { GlEmptyState } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
+import { nextTick } from 'vue';
import Vuex from 'vuex';
import stubChildren from 'helpers/stub_children';
@@ -109,9 +110,11 @@ describe('PackagesApp', () => {
window.location = location;
});
- it('renders the app and displays the package title', () => {
+ it('renders the app and displays the package title', async () => {
createComponent();
+ await nextTick();
+
expect(packageTitle().exists()).toBe(true);
});
diff --git a/spec/frontend/packages/details/components/installations_commands_spec.js b/spec/frontend/packages/details/components/installations_commands_spec.js
index 065bf503585..164f9f69741 100644
--- a/spec/frontend/packages/details/components/installations_commands_spec.js
+++ b/spec/frontend/packages/details/components/installations_commands_spec.js
@@ -7,6 +7,7 @@ import MavenInstallation from '~/packages/details/components/maven_installation.
import NpmInstallation from '~/packages/details/components/npm_installation.vue';
import NugetInstallation from '~/packages/details/components/nuget_installation.vue';
import PypiInstallation from '~/packages/details/components/pypi_installation.vue';
+import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
import {
conanPackage,
@@ -15,6 +16,7 @@ import {
nugetPackage,
pypiPackage,
composerPackage,
+ terraformModule,
} from '../../mock_data';
describe('InstallationCommands', () => {
@@ -32,6 +34,7 @@ describe('InstallationCommands', () => {
const nugetInstallation = () => wrapper.find(NugetInstallation);
const pypiInstallation = () => wrapper.find(PypiInstallation);
const composerInstallation = () => wrapper.find(ComposerInstallation);
+ const terraformInstallation = () => wrapper.findComponent(TerraformInstallation);
afterEach(() => {
wrapper.destroy();
@@ -46,6 +49,7 @@ describe('InstallationCommands', () => {
${nugetPackage} | ${nugetInstallation}
${pypiPackage} | ${pypiInstallation}
${composerPackage} | ${composerInstallation}
+ ${terraformModule} | ${terraformInstallation}
`('renders', ({ packageEntity, selector }) => {
it(`${packageEntity.package_type} instructions exist`, () => {
createComponent({ packageEntity });
diff --git a/spec/frontend/packages/mock_data.js b/spec/frontend/packages/mock_data.js
index 46ccf5994b9..33b47cca68b 100644
--- a/spec/frontend/packages/mock_data.js
+++ b/spec/frontend/packages/mock_data.js
@@ -178,6 +178,20 @@ export const composerPackage = {
version: '1.0.0',
};
+export const terraformModule = {
+ created_at: '2015-12-10',
+ id: 2,
+ name: 'Test/system-22',
+ package_type: 'terraform_module',
+ project_path: 'foo/bar/baz',
+ projectPathName: 'foo/bar/baz',
+ project_id: 1,
+ updated_at: '2015-12-10',
+ version: '0.1',
+ versions: [],
+ _links,
+};
+
export const mockTags = [
{
name: 'foo-1',
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/__snapshots__/terraform_installation_spec.js.snap b/spec/frontend/packages_and_registries/infrastructure_registry/components/__snapshots__/terraform_installation_spec.js.snap
new file mode 100644
index 00000000000..427160b45e3
--- /dev/null
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/__snapshots__/terraform_installation_spec.js.snap
@@ -0,0 +1,44 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`TerraformInstallation renders all the messages 1`] = `
+<div>
+ <h3
+ class="gl-font-lg"
+ >
+ Provision instructions
+ </h3>
+
+ <code-instruction-stub
+ copytext="Copy Terraform Command"
+ instruction="module \\"Test/system-22\\" {
+ source = \\"foo/Test/system-22\\"
+ version = \\"0.1\\"
+}"
+ label="Copy and paste into your Terraform configuration, insert the variables, and run Terraform init:"
+ multiline="true"
+ trackingaction=""
+ trackinglabel=""
+ />
+
+ <h3
+ class="gl-font-lg"
+ >
+ Registry setup
+ </h3>
+
+ <code-instruction-stub
+ copytext="Copy Terraform Setup Command"
+ instruction="credentials \\"gitlab.com\\" {
+ token = \\"<TOKEN>\\"
+}"
+ label="To authorize access to the Terraform registry:"
+ multiline="true"
+ trackingaction=""
+ trackinglabel=""
+ />
+
+ <gl-sprintf-stub
+ message="For more information on the Terraform registry, %{linkStart}see our documentation%{linkEnd}."
+ />
+</div>
+`;
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/details_title_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/details_title_spec.js
new file mode 100644
index 00000000000..87e0059344c
--- /dev/null
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/details_title_spec.js
@@ -0,0 +1,93 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { terraformModule, mavenFiles, npmPackage } from 'jest/packages/mock_data';
+import component from '~/packages_and_registries/infrastructure_registry/components/details_title.vue';
+import TitleArea from '~/vue_shared/components/registry/title_area.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('PackageTitle', () => {
+ let wrapper;
+ let store;
+
+ function createComponent({ packageFiles = mavenFiles, packageEntity = terraformModule } = {}) {
+ store = new Vuex.Store({
+ state: {
+ packageEntity,
+ packageFiles,
+ },
+ getters: {
+ packagePipeline: ({ packageEntity: { pipeline = null } }) => pipeline,
+ },
+ });
+
+ wrapper = shallowMount(component, {
+ localVue,
+ store,
+ stubs: {
+ TitleArea,
+ },
+ });
+ return wrapper.vm.$nextTick();
+ }
+
+ const findTitleArea = () => wrapper.findComponent(TitleArea);
+ const packageSize = () => wrapper.find('[data-testid="package-size"]');
+ const pipelineProject = () => wrapper.find('[data-testid="pipeline-project"]');
+ const packageRef = () => wrapper.find('[data-testid="package-ref"]');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('module title', () => {
+ it('is correctly bound', async () => {
+ await createComponent();
+
+ expect(findTitleArea().props('title')).toBe(terraformModule.name);
+ });
+ });
+
+ describe('calculates the package size', () => {
+ it('correctly calculates the size', async () => {
+ await createComponent();
+
+ expect(packageSize().props('text')).toBe('300 bytes');
+ });
+ });
+
+ describe('package ref', () => {
+ it('does not display the ref if missing', async () => {
+ await createComponent();
+
+ expect(packageRef().exists()).toBe(false);
+ });
+
+ it('correctly shows the package ref if there is one', async () => {
+ await createComponent({ packageEntity: npmPackage });
+ expect(packageRef().props()).toMatchObject({
+ text: npmPackage.pipeline.ref,
+ icon: 'branch',
+ });
+ });
+ });
+
+ describe('pipeline project', () => {
+ it('does not display the project if missing', async () => {
+ await createComponent();
+
+ expect(pipelineProject().exists()).toBe(false);
+ });
+
+ it('correctly shows the pipeline project if there is one', async () => {
+ await createComponent({ packageEntity: npmPackage });
+
+ expect(pipelineProject().props()).toMatchObject({
+ text: npmPackage.pipeline.project.name,
+ icon: 'review-list',
+ link: npmPackage.pipeline.project.web_url,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/terraform_installation_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/terraform_installation_spec.js
new file mode 100644
index 00000000000..7a129794d54
--- /dev/null
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/terraform_installation_spec.js
@@ -0,0 +1,61 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { terraformModule as packageEntity } from 'jest/packages/mock_data';
+import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
+import CodeInstructions from '~/vue_shared/components/registry/code_instruction.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('TerraformInstallation', () => {
+ let wrapper;
+
+ const store = new Vuex.Store({
+ state: {
+ packageEntity,
+ projectPath: 'foo',
+ },
+ });
+
+ const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions);
+
+ function createComponent() {
+ wrapper = shallowMount(TerraformInstallation, {
+ localVue,
+ store,
+ });
+ }
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders all the messages', () => {
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ describe('installation commands', () => {
+ it('renders the correct command', () => {
+ expect(findCodeInstructions().at(0).props('instruction')).toMatchInlineSnapshot(`
+ "module \\"Test/system-22\\" {
+ source = \\"foo/Test/system-22\\"
+ version = \\"0.1\\"
+ }"
+ `);
+ });
+ });
+
+ describe('setup commands', () => {
+ it('renders the correct command', () => {
+ expect(findCodeInstructions().at(1).props('instruction')).toMatchInlineSnapshot(`
+ "credentials \\"gitlab.com\\" {
+ token = \\"<TOKEN>\\"
+ }"
+ `);
+ });
+ });
+});
diff --git a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
index 756b8dd980c..6f9245e39aa 100644
--- a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
+++ b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
@@ -9,6 +9,7 @@ import {
mockCommitSha,
mockProjectPath,
mockProjectNamespace,
+ mockDefaultBranch,
} from '../../mock_data';
describe('Pipeline Editor | Text editor component', () => {
@@ -38,6 +39,7 @@ describe('Pipeline Editor | Text editor component', () => {
projectPath: mockProjectPath,
projectNamespace: mockProjectNamespace,
ciConfigPath: mockCiConfigPath,
+ defaultBranch: mockDefaultBranch,
glFeatures,
},
attrs: {
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 5c9af1206c0..847f7ec2d74 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -125,10 +125,10 @@ RSpec.describe Gitlab::Database do
expect(described_class.postgresql_minimum_supported_version?).to eq(false)
end
- it 'returns true when using PostgreSQL 11' do
+ it 'returns false when using PostgreSQL 11' do
allow(described_class).to receive(:version).and_return('11')
- expect(described_class.postgresql_minimum_supported_version?).to eq(true)
+ expect(described_class.postgresql_minimum_supported_version?).to eq(false)
end
it 'returns true when using PostgreSQL 12' do
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 8d27e61450b..085b0b4e259 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -342,7 +342,7 @@ container_repositories:
- project
- name
project:
-- external_approval_rules
+- external_status_checks
- taggings
- base_tags
- topic_taggings
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 4075eb96fc2..3741e01e99a 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -418,8 +418,8 @@ RSpec.describe MergeRequestDiff do
shared_examples_for 'fetching full diffs' do
it 'returns diffs from repository comparison' do
expect_next_instance_of(Compare) do |comparison|
- expect(comparison).to receive(:diffs_in_batch)
- .with(1, 10, diff_options: diff_options)
+ expect(comparison).to receive(:diffs)
+ .with(diff_options)
.and_call_original
end
@@ -448,13 +448,13 @@ RSpec.describe MergeRequestDiff do
end
it_behaves_like 'fetching full diffs'
- end
- context 'when diff_options include ignore_whitespace_change' do
- it_behaves_like 'fetching full diffs' do
+ context 'when diff_options include ignore_whitespace_change' do
let(:diff_options) do
{ ignore_whitespace_change: true }
end
+
+ it_behaves_like 'fetching full diffs'
end
end
@@ -485,6 +485,51 @@ RSpec.describe MergeRequestDiff do
'files/whitespace'
])
end
+
+ context 'when diff_options include ignore_whitespace_change' do
+ let(:diff_options) do
+ { ignore_whitespace_change: true }
+ end
+
+ it 'returns a Gitlab::Diff::FileCollection::Compare with paginated diffs' do
+ diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options)
+
+ expect(diffs).to be_a(Gitlab::Diff::FileCollection::Compare)
+ expect(diffs.diff_files.size).to eq 10
+ expect(diffs.pagination_data).to eq(current_page: 1, next_page: 2, total_pages: 2)
+ end
+
+ it 'returns an empty MergeRequestBatch with empty pagination data when the batch is empty' do
+ diffs = diff_with_commits.diffs_in_batch(3, 10, diff_options: diff_options)
+
+ expect(diffs).to be_a(Gitlab::Diff::FileCollection::MergeRequestDiffBatch)
+ expect(diffs.diff_files.size).to eq 0
+ expect(diffs.pagination_data).to eq(current_page: nil, next_page: nil, total_pages: nil)
+ end
+
+ context 'with gradual load enabled' do
+ before do
+ stub_feature_flags(diffs_gradual_load: true)
+ end
+
+ it 'returns pagination data from MergeRequestDiffBatch' do
+ diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options)
+ file_count = diff_with_commits.merge_request_diff_files.count
+
+ expect(diffs).to be_a(Gitlab::Diff::FileCollection::Compare)
+ expect(diffs.diff_files.size).to eq 10
+ expect(diffs.pagination_data).to eq(current_page: nil, next_page: nil, total_pages: file_count)
+ end
+
+ it 'returns an empty MergeRequestBatch with empty pagination data when the batch is empty' do
+ diffs = diff_with_commits.diffs_in_batch(30, 10, diff_options: diff_options)
+
+ expect(diffs).to be_a(Gitlab::Diff::FileCollection::MergeRequestDiffBatch)
+ expect(diffs.diff_files.size).to eq 0
+ expect(diffs.pagination_data).to eq(current_page: nil, next_page: nil, total_pages: nil)
+ end
+ end
+ end
end
end
diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb
index 42c6c987872..c3abb06c5c1 100644
--- a/spec/requests/api/debian_group_packages_spec.rb
+++ b/spec/requests/api/debian_group_packages_spec.rb
@@ -7,33 +7,33 @@ RSpec.describe API::DebianGroupPackages do
include_context 'Debian repository shared context', :group, false do
describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release.gpg' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/Release.gpg" }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release.gpg" }
it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/Release" }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Release'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Release$/
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/InRelease' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/InRelease" }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/InRelease" }
it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found
end
describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do
- let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" }
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component}/binary-#{architecture}/Packages" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Packages'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Packages$/
end
describe 'GET groups/:id/-/packages/debian/pool/:component/:letter/:source_package/:file_name' do
let(:url) { "/groups/#{container.id}/-/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO File'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO File$/
end
end
end
diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb
index 023904cb2b8..c11c4ecc12a 100644
--- a/spec/requests/api/debian_project_packages_spec.rb
+++ b/spec/requests/api/debian_project_packages_spec.rb
@@ -7,33 +7,33 @@ RSpec.describe API::DebianProjectPackages do
include_context 'Debian repository shared context', :project, true do
describe 'GET projects/:id/packages/debian/dists/*distribution/Release.gpg' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/Release.gpg" }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release.gpg" }
it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found
end
describe 'GET projects/:id/packages/debian/dists/*distribution/Release' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/Release" }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Release'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Release$/
end
describe 'GET projects/:id/packages/debian/dists/*distribution/InRelease' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/InRelease" }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/InRelease" }
it_behaves_like 'Debian repository read endpoint', 'GET request', :not_found
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do
- let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" }
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component}/binary-#{architecture}/Packages" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO Packages'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO Packages$/
end
describe 'GET projects/:id/packages/debian/pool/:component/:letter/:source_package/:file_name' do
let(:url) { "/projects/#{container.id}/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" }
- it_behaves_like 'Debian repository read endpoint', 'GET request', :success, 'TODO File'
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^TODO File$/
end
describe 'PUT projects/:id/packages/debian/:file_name' do
diff --git a/spec/requests/api/project_debian_distributions_spec.rb b/spec/requests/api/project_debian_distributions_spec.rb
new file mode 100644
index 00000000000..de7362758f7
--- /dev/null
+++ b/spec/requests/api/project_debian_distributions_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe API::ProjectDebianDistributions do
+ include HttpBasicAuthHelpers
+ include WorkhorseHelpers
+
+ include_context 'Debian repository shared context', :project, true do
+ describe 'POST projects/:id/debian_distributions' do
+ let(:method) { :post }
+ let(:url) { "/projects/#{container.id}/debian_distributions" }
+ let(:api_params) { { 'codename': 'my-codename' } }
+
+ it_behaves_like 'Debian repository write endpoint', 'POST distribution request', :created, /^{.*"codename":"my-codename",.*"components":\["main"\],.*"architectures":\["all","amd64"\]/, authenticate_non_public: false
+
+ context 'with invalid parameters' do
+ let(:api_params) { { codename: distribution.codename } }
+
+ it_behaves_like 'Debian repository write endpoint', 'GET request', :bad_request, /^{"message":{"codename":\["has already been taken"\]}}$/, authenticate_non_public: false
+ end
+ end
+
+ describe 'GET projects/:id/debian_distributions' do
+ let(:url) { "/projects/#{container.id}/debian_distributions" }
+
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^\[{.*"codename":"existing-codename\",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/, authenticate_non_public: false
+ end
+
+ describe 'GET projects/:id/debian_distributions/:codename' do
+ let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" }
+
+ it_behaves_like 'Debian repository read endpoint', 'GET request', :success, /^{.*"codename":"existing-codename\",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/, authenticate_non_public: false
+ end
+
+ describe 'PUT projects/:id/debian_distributions/:codename' do
+ let(:method) { :put }
+ let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" }
+ let(:api_params) { { suite: 'my-suite' } }
+
+ it_behaves_like 'Debian repository write endpoint', 'PUT distribution request', :success, /^{.*"codename":"existing-codename",.*"suite":"my-suite",/, authenticate_non_public: false
+
+ context 'with invalid parameters' do
+ let(:api_params) { { suite: distribution.codename } }
+
+ it_behaves_like 'Debian repository write endpoint', 'GET request', :bad_request, /^{"message":{"suite":\["has already been taken as Codename"\]}}$/, authenticate_non_public: false
+ end
+ end
+
+ describe 'DELETE projects/:id/debian_distributions/:codename' do
+ let(:method) { :delete }
+ let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" }
+
+ it_behaves_like 'Debian repository maintainer write endpoint', 'DELETE distribution request', :success, /^{\"message\":\"202 Accepted\"}$/, authenticate_non_public: false
+
+ context 'when destroy fails' do
+ before do
+ allow_next_found_instance_of(::Packages::Debian::ProjectDistribution) do |distribution|
+ expect(distribution).to receive(:destroy).and_return(false)
+ end
+ end
+
+ it_behaves_like 'Debian repository maintainer write endpoint', 'GET request', :bad_request, /^{"message":"Failed to delete distribution"}$/, authenticate_non_public: false
+ end
+ end
+ end
+end
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index f092cbf26a4..5a38f92221f 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -675,48 +675,6 @@ RSpec.describe UsersController do
end
end
- describe 'GET #suggests' do
- context 'when user exists' do
- it 'returns JSON indicating the user exists and a suggestion' do
- get user_suggests_url user.username
-
- expected_json = { exists: true, suggests: ["#{user.username}1"] }.to_json
- expect(response.body).to eq(expected_json)
- end
-
- context 'when the casing is different' do
- let(:user) { create(:user, username: 'CamelCaseUser') }
-
- it 'returns JSON indicating the user exists and a suggestion' do
- get user_suggests_url user.username.downcase
-
- expected_json = { exists: true, suggests: ["#{user.username.downcase}1"] }.to_json
- expect(response.body).to eq(expected_json)
- end
- end
- end
-
- context 'when the user does not exist' do
- it 'returns JSON indicating the user does not exist' do
- get user_suggests_url 'foo'
-
- expected_json = { exists: false, suggests: [] }.to_json
- expect(response.body).to eq(expected_json)
- end
-
- context 'when a user changed their username' do
- let(:redirect_route) { user.namespace.redirect_routes.create!(path: 'old-username') }
-
- it 'returns JSON indicating a user by that username does not exist' do
- get user_suggests_url 'old-username'
-
- expected_json = { exists: false, suggests: [] }.to_json
- expect(response.body).to eq(expected_json)
- end
- end
- end
- end
-
describe '#ensure_canonical_path' do
before do
sign_in(user)
diff --git a/spec/services/packages/debian/create_distribution_service_spec.rb b/spec/services/packages/debian/create_distribution_service_spec.rb
index 87cf1070075..ecf82c6a1db 100644
--- a/spec/services/packages/debian/create_distribution_service_spec.rb
+++ b/spec/services/packages/debian/create_distribution_service_spec.rb
@@ -4,8 +4,12 @@ require 'spec_helper'
RSpec.describe Packages::Debian::CreateDistributionService do
RSpec.shared_examples 'Create Debian Distribution' do |expected_message, expected_components, expected_architectures|
+ let_it_be(:container) { create(container_type) } # rubocop:disable Rails/SaveBang
+
it 'returns ServiceResponse', :aggregate_failures do
if expected_message.nil?
+ expect(::Packages::Debian::GenerateDistributionWorker).to receive(:perform_async).with(container_type, an_instance_of(Integer))
+
expect { response }
.to change { container.debian_distributions.klass.all.count }
.from(0).to(1)
@@ -18,6 +22,7 @@ RSpec.describe Packages::Debian::CreateDistributionService do
.and not_change { Packages::Debian::ProjectComponentFile.count }
.and not_change { Packages::Debian::GroupComponentFile.count }
else
+ expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async)
expect { response }
.to not_change { container.debian_distributions.klass.all.count }
.and not_change { container.debian_distributions.count }
@@ -109,13 +114,13 @@ RSpec.describe Packages::Debian::CreateDistributionService do
let(:response) { subject.execute }
context 'within a projet' do
- let_it_be(:container) { create(:project) }
+ let_it_be(:container_type) { :project }
it_behaves_like 'Debian Create Distribution Service'
end
context 'within a group' do
- let_it_be(:container) { create(:group) }
+ let_it_be(:container_type) { :group }
it_behaves_like 'Debian Create Distribution Service'
end
diff --git a/spec/services/packages/debian/destroy_distribution_service_spec.rb b/spec/services/packages/debian/destroy_distribution_service_spec.rb
deleted file mode 100644
index e4c43884bb4..00000000000
--- a/spec/services/packages/debian/destroy_distribution_service_spec.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Packages::Debian::DestroyDistributionService do
- RSpec.shared_examples 'Destroy Debian Distribution' do |expected_message|
- it 'returns ServiceResponse', :aggregate_failures do
- if expected_message.nil?
- expect { response }
- .to change { container.debian_distributions.klass.all.count }
- .from(1).to(0)
- .and change { container.debian_distributions.count }
- .from(1).to(0)
- .and change { component1.class.all.count }
- .from(2).to(0)
- .and change { architecture1.class.all.count }
- .from(3).to(0)
- .and change { component_file1.class.all.count }
- .from(4).to(0)
- else
- expect { response }
- .to not_change { container.debian_distributions.klass.all.count }
- .and not_change { container.debian_distributions.count }
- .and not_change { component1.class.all.count }
- .and not_change { architecture1.class.all.count }
- .and not_change { component_file1.class.all.count }
- end
-
- expect(response).to be_a(ServiceResponse)
- expect(response.success?).to eq(expected_message.nil?)
- expect(response.error?).to eq(!expected_message.nil?)
- expect(response.message).to eq(expected_message)
-
- if expected_message.nil?
- expect(response.payload).to eq({})
- else
- expect(response.payload).to eq(distribution: distribution)
- end
- end
- end
-
- RSpec.shared_examples 'Debian Destroy Distribution Service' do |container_type, can_freeze|
- context "with a Debian #{container_type} distribution" do
- let_it_be(:container, freeze: can_freeze) { create(container_type) } # rubocop:disable Rails/SaveBang
- let_it_be(:distribution, freeze: can_freeze) { create("debian_#{container_type}_distribution", container: container) }
- let_it_be(:component1, freeze: can_freeze) { create("debian_#{container_type}_component", distribution: distribution, name: 'component1') }
- let_it_be(:component2, freeze: can_freeze) { create("debian_#{container_type}_component", distribution: distribution, name: 'component2') }
- let_it_be(:architecture0, freeze: true) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'all') }
- let_it_be(:architecture1, freeze: can_freeze) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'architecture1') }
- let_it_be(:architecture2, freeze: can_freeze) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'architecture2') }
- let_it_be(:component_file1, freeze: can_freeze) { create("debian_#{container_type}_component_file", :source, component: component1) }
- let_it_be(:component_file2, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component1, architecture: architecture1) }
- let_it_be(:component_file3, freeze: can_freeze) { create("debian_#{container_type}_component_file", :source, component: component2) }
- let_it_be(:component_file4, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component2, architecture: architecture2) }
-
- subject { described_class.new(distribution) }
-
- let(:response) { subject.execute }
-
- context 'with a distribution' do
- it_behaves_like 'Destroy Debian Distribution'
- end
-
- context 'when destroy fails' do
- let(:distribution) { create("debian_#{container_type}_distribution", container: container) }
-
- before do
- expect(distribution).to receive(:destroy).and_return(false)
- end
-
- it_behaves_like 'Destroy Debian Distribution', "Unable to destroy Debian #{container_type} distribution"
- end
- end
- end
-
- it_behaves_like 'Debian Destroy Distribution Service', :project, true
- it_behaves_like 'Debian Destroy Distribution Service', :group, false
-end
diff --git a/spec/services/packages/debian/update_distribution_service_spec.rb b/spec/services/packages/debian/update_distribution_service_spec.rb
index 852fc713e34..2aa34a62111 100644
--- a/spec/services/packages/debian/update_distribution_service_spec.rb
+++ b/spec/services/packages/debian/update_distribution_service_spec.rb
@@ -6,6 +6,8 @@ RSpec.describe Packages::Debian::UpdateDistributionService do
RSpec.shared_examples 'Update Debian Distribution' do |expected_message, expected_components, expected_architectures, component_file_delta = 0|
it 'returns ServiceResponse', :aggregate_failures do
expect(distribution).to receive(:update).with(simple_params).and_call_original if expected_message.nil?
+ expect(::Packages::Debian::GenerateDistributionWorker).to receive(:perform_async).with(distribution.class.container_type, distribution.id).and_call_original if expected_message.nil?
+ expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async) unless expected_message.nil?
if component_file_delta.zero?
expect { response }
diff --git a/spec/services/packages/nuget/metadata_extraction_service_spec.rb b/spec/services/packages/nuget/metadata_extraction_service_spec.rb
index b3980d3b7fa..79428b58bd9 100644
--- a/spec/services/packages/nuget/metadata_extraction_service_spec.rb
+++ b/spec/services/packages/nuget/metadata_extraction_service_spec.rb
@@ -24,22 +24,7 @@ RSpec.describe Packages::Nuget::MetadataExtractionService do
package_tags: []
}
- context 'with packages_nuget_archive_new_file_reader enabled' do
- before do
- expect(service).to receive(:with_new_file_reader).and_call_original
- end
-
- it { is_expected.to eq(expected_metadata) }
- end
-
- context 'with packages_nuget_archive_new_file_reader disabled' do
- before do
- stub_feature_flags(packages_nuget_archive_new_file_reader: false)
- expect(service).to receive(:with_legacy_file_reader).and_call_original
- end
-
- it { is_expected.to eq(expected_metadata) }
- end
+ it { is_expected.to eq(expected_metadata) }
end
context 'with nuspec file' do
diff --git a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
index e41475f786d..ffe1a5b7646 100644
--- a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
+++ b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
@@ -63,191 +63,178 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_
end
end
- shared_examples 'handling all conditions' do
- context 'with no existing package' do
- let(:package_id) { package.id }
-
- it 'updates package and package file' do
- expect { subject }
- .to change { ::Packages::Package.count }.by(1)
- .and change { Packages::Dependency.count }.by(1)
- .and change { Packages::DependencyLink.count }.by(1)
- .and change { ::Packages::Nuget::Metadatum.count }.by(0)
-
- expect(package.reload.name).to eq(package_name)
- expect(package.version).to eq(package_version)
- expect(package).to be_default
- expect(package_file.reload.file_name).to eq(package_file_name)
- # hard reset needed to properly reload package_file.file
- expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0
- end
-
- it_behaves_like 'taking the lease'
-
- it_behaves_like 'not updating the package if the lease is taken'
+ context 'with no existing package' do
+ let(:package_id) { package.id }
+
+ it 'updates package and package file' do
+ expect { subject }
+ .to change { ::Packages::Package.count }.by(1)
+ .and change { Packages::Dependency.count }.by(1)
+ .and change { Packages::DependencyLink.count }.by(1)
+ .and change { ::Packages::Nuget::Metadatum.count }.by(0)
+
+ expect(package.reload.name).to eq(package_name)
+ expect(package.version).to eq(package_version)
+ expect(package).to be_default
+ expect(package_file.reload.file_name).to eq(package_file_name)
+ # hard reset needed to properly reload package_file.file
+ expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0
end
- context 'with existing package' do
- let!(:existing_package) { create(:nuget_package, project: package.project, name: package_name, version: package_version) }
- let(:package_id) { existing_package.id }
+ it_behaves_like 'taking the lease'
- it 'link existing package and updates package file' do
- expect(service).to receive(:try_obtain_lease).and_call_original
+ it_behaves_like 'not updating the package if the lease is taken'
+ end
- expect { subject }
- .to change { ::Packages::Package.count }.by(-1)
- .and change { Packages::Dependency.count }.by(0)
- .and change { Packages::DependencyLink.count }.by(0)
- .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(0)
- .and change { ::Packages::Nuget::Metadatum.count }.by(0)
- expect(package_file.reload.file_name).to eq(package_file_name)
- expect(package_file.package).to eq(existing_package)
- end
+ context 'with existing package' do
+ let!(:existing_package) { create(:nuget_package, project: package.project, name: package_name, version: package_version) }
+ let(:package_id) { existing_package.id }
- it_behaves_like 'taking the lease'
+ it 'link existing package and updates package file' do
+ expect(service).to receive(:try_obtain_lease).and_call_original
- it_behaves_like 'not updating the package if the lease is taken'
+ expect { subject }
+ .to change { ::Packages::Package.count }.by(-1)
+ .and change { Packages::Dependency.count }.by(0)
+ .and change { Packages::DependencyLink.count }.by(0)
+ .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(0)
+ .and change { ::Packages::Nuget::Metadatum.count }.by(0)
+ expect(package_file.reload.file_name).to eq(package_file_name)
+ expect(package_file.package).to eq(existing_package)
end
- context 'with a nuspec file with metadata' do
- let(:nuspec_filepath) { 'packages/nuget/with_metadata.nuspec' }
- let(:expected_tags) { %w(foo bar test tag1 tag2 tag3 tag4 tag5) }
+ it_behaves_like 'taking the lease'
- before do
- allow_next_instance_of(Packages::Nuget::MetadataExtractionService) do |service|
- allow(service)
- .to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath))
- end
- end
+ it_behaves_like 'not updating the package if the lease is taken'
+ end
- it 'creates tags' do
- expect(service).to receive(:try_obtain_lease).and_call_original
- expect { subject }.to change { ::Packages::Tag.count }.by(8)
- expect(package.reload.tags.map(&:name)).to contain_exactly(*expected_tags)
- end
+ context 'with a nuspec file with metadata' do
+ let(:nuspec_filepath) { 'packages/nuget/with_metadata.nuspec' }
+ let(:expected_tags) { %w(foo bar test tag1 tag2 tag3 tag4 tag5) }
- context 'with existing package and tags' do
- let!(:existing_package) { create(:nuget_package, project: package.project, name: 'DummyProject.WithMetadata', version: '1.2.3') }
- let!(:tag1) { create(:packages_tag, package: existing_package, name: 'tag1') }
- let!(:tag2) { create(:packages_tag, package: existing_package, name: 'tag2') }
- let!(:tag3) { create(:packages_tag, package: existing_package, name: 'tag_not_in_metadata') }
-
- it 'creates tags and deletes those not in metadata' do
- expect(service).to receive(:try_obtain_lease).and_call_original
- expect { subject }.to change { ::Packages::Tag.count }.by(5)
- expect(existing_package.tags.map(&:name)).to contain_exactly(*expected_tags)
- end
+ before do
+ allow_next_instance_of(Packages::Nuget::MetadataExtractionService) do |service|
+ allow(service)
+ .to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath))
end
+ end
- it 'creates nuget metadatum' do
- expect { subject }
- .to change { ::Packages::Package.count }.by(1)
- .and change { ::Packages::Nuget::Metadatum.count }.by(1)
+ it 'creates tags' do
+ expect(service).to receive(:try_obtain_lease).and_call_original
+ expect { subject }.to change { ::Packages::Tag.count }.by(8)
+ expect(package.reload.tags.map(&:name)).to contain_exactly(*expected_tags)
+ end
- metadatum = package_file.reload.package.nuget_metadatum
- expect(metadatum.license_url).to eq('https://opensource.org/licenses/MIT')
- expect(metadatum.project_url).to eq('https://gitlab.com/gitlab-org/gitlab')
- expect(metadatum.icon_url).to eq('https://opensource.org/files/osi_keyhole_300X300_90ppi_0.png')
+ context 'with existing package and tags' do
+ let!(:existing_package) { create(:nuget_package, project: package.project, name: 'DummyProject.WithMetadata', version: '1.2.3') }
+ let!(:tag1) { create(:packages_tag, package: existing_package, name: 'tag1') }
+ let!(:tag2) { create(:packages_tag, package: existing_package, name: 'tag2') }
+ let!(:tag3) { create(:packages_tag, package: existing_package, name: 'tag_not_in_metadata') }
+
+ it 'creates tags and deletes those not in metadata' do
+ expect(service).to receive(:try_obtain_lease).and_call_original
+ expect { subject }.to change { ::Packages::Tag.count }.by(5)
+ expect(existing_package.tags.map(&:name)).to contain_exactly(*expected_tags)
end
+ end
- context 'with too long url' do
- let_it_be(:too_long_url) { "http://localhost/#{'bananas' * 50}" }
+ it 'creates nuget metadatum' do
+ expect { subject }
+ .to change { ::Packages::Package.count }.by(1)
+ .and change { ::Packages::Nuget::Metadatum.count }.by(1)
- let(:metadata) { { package_name: package_name, package_version: package_version, license_url: too_long_url } }
+ metadatum = package_file.reload.package.nuget_metadatum
+ expect(metadatum.license_url).to eq('https://opensource.org/licenses/MIT')
+ expect(metadatum.project_url).to eq('https://gitlab.com/gitlab-org/gitlab')
+ expect(metadatum.icon_url).to eq('https://opensource.org/files/osi_keyhole_300X300_90ppi_0.png')
+ end
+
+ context 'with too long url' do
+ let_it_be(:too_long_url) { "http://localhost/#{'bananas' * 50}" }
- before do
- allow(service).to receive(:metadata).and_return(metadata)
- end
+ let(:metadata) { { package_name: package_name, package_version: package_version, license_url: too_long_url } }
- it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
+ before do
+ allow(service).to receive(:metadata).and_return(metadata)
end
+
+ it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
end
+ end
- context 'with nuspec file with dependencies' do
- let(:nuspec_filepath) { 'packages/nuget/with_dependencies.nuspec' }
- let(:package_name) { 'Test.Package' }
- let(:package_version) { '3.5.2' }
- let(:package_file_name) { 'test.package.3.5.2.nupkg' }
+ context 'with nuspec file with dependencies' do
+ let(:nuspec_filepath) { 'packages/nuget/with_dependencies.nuspec' }
+ let(:package_name) { 'Test.Package' }
+ let(:package_version) { '3.5.2' }
+ let(:package_file_name) { 'test.package.3.5.2.nupkg' }
- before do
- allow_next_instance_of(Packages::Nuget::MetadataExtractionService) do |service|
- allow(service)
- .to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath))
- end
+ before do
+ allow_next_instance_of(Packages::Nuget::MetadataExtractionService) do |service|
+ allow(service)
+ .to receive(:nuspec_file_content).and_return(fixture_file(nuspec_filepath))
end
+ end
- it 'updates package and package file' do
- expect { subject }
- .to change { ::Packages::Package.count }.by(1)
- .and change { Packages::Dependency.count }.by(4)
- .and change { Packages::DependencyLink.count }.by(4)
- .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(2)
-
- expect(package.reload.name).to eq(package_name)
- expect(package.version).to eq(package_version)
- expect(package).to be_default
- expect(package_file.reload.file_name).to eq(package_file_name)
- # hard reset needed to properly reload package_file.file
- expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0
- end
+ it 'updates package and package file' do
+ expect { subject }
+ .to change { ::Packages::Package.count }.by(1)
+ .and change { Packages::Dependency.count }.by(4)
+ .and change { Packages::DependencyLink.count }.by(4)
+ .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(2)
+
+ expect(package.reload.name).to eq(package_name)
+ expect(package.version).to eq(package_version)
+ expect(package).to be_default
+ expect(package_file.reload.file_name).to eq(package_file_name)
+ # hard reset needed to properly reload package_file.file
+ expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0
end
+ end
- context 'with package file not containing a nuspec file' do
- before do
- allow_next_instance_of(Zip::File) do |file|
- allow(file).to receive(:glob).and_return([])
- end
+ context 'with package file not containing a nuspec file' do
+ before do
+ allow_next_instance_of(Zip::File) do |file|
+ allow(file).to receive(:glob).and_return([])
end
-
- it_behaves_like 'raising an', ::Packages::Nuget::MetadataExtractionService::ExtractionError
end
- context 'with an invalid package name' do
- invalid_names = [
- '',
- 'My/package',
- '../../../my_package',
- '%2e%2e%2fmy_package'
- ]
+ it_behaves_like 'raising an', ::Packages::Nuget::MetadataExtractionService::ExtractionError
+ end
- invalid_names.each do |invalid_name|
- before do
- allow(service).to receive(:package_name).and_return(invalid_name)
- end
+ context 'with an invalid package name' do
+ invalid_names = [
+ '',
+ 'My/package',
+ '../../../my_package',
+ '%2e%2e%2fmy_package'
+ ]
- it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
+ invalid_names.each do |invalid_name|
+ before do
+ allow(service).to receive(:package_name).and_return(invalid_name)
end
- end
- context 'with an invalid package version' do
- invalid_versions = [
- '',
- '555',
- '1.2',
- '1./2.3',
- '../../../../../1.2.3',
- '%2e%2e%2f1.2.3'
- ]
-
- invalid_versions.each do |invalid_version|
- before do
- allow(service).to receive(:package_version).and_return(invalid_version)
- end
-
- it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
- end
+ it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
end
end
- it_behaves_like 'handling all conditions'
+ context 'with an invalid package version' do
+ invalid_versions = [
+ '',
+ '555',
+ '1.2',
+ '1./2.3',
+ '../../../../../1.2.3',
+ '%2e%2e%2f1.2.3'
+ ]
+
+ invalid_versions.each do |invalid_version|
+ before do
+ allow(service).to receive(:package_version).and_return(invalid_version)
+ end
- context 'with packages_nuget_archive_new_file_reader disabled' do
- before do
- stub_feature_flags(packages_nuget_archive_new_file_reader: false)
- stub_package_file_object_storage(enabled: true, direct_upload: true)
+ it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
end
-
- it_behaves_like 'handling all conditions'
end
end
end
diff --git a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
index 7c47b900dc0..0530aa8c760 100644
--- a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
@@ -12,7 +12,35 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
let_it_be(:user, freeze: true) { create(:user) }
let_it_be(:personal_access_token, freeze: true) { create(:personal_access_token, user: user) }
- let(:distribution) { 'bullseye' }
+ let_it_be(:private_distribution, freeze: true) { create("debian_#{container_type}_distribution", container: private_container, codename: 'existing-codename') }
+ let_it_be(:private_component, freeze: true) { create("debian_#{container_type}_component", distribution: private_distribution, name: 'existing-component') }
+ let_it_be(:private_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'all') }
+ let_it_be(:private_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'existing-arch') }
+
+ let_it_be(:public_distribution, freeze: true) { create("debian_#{container_type}_distribution", container: public_container, codename: 'existing-codename') }
+ let_it_be(:public_component, freeze: true) { create("debian_#{container_type}_component", distribution: public_distribution, name: 'existing-component') }
+ let_it_be(:public_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'all') }
+ let_it_be(:public_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'existing-arch') }
+
+ if container_type == :group
+ let_it_be(:private_project) { create(:project, :private, group: private_container) }
+ let_it_be(:public_project) { create(:project, :public, group: public_container) }
+ let_it_be(:private_project_distribution) { create(:debian_project_distribution, container: private_project, codename: 'existing-codename') }
+ let_it_be(:public_project_distribution) { create(:debian_project_distribution, container: public_project, codename: 'existing-codename') }
+ else
+ let_it_be(:private_project) { private_container }
+ let_it_be(:public_project) { public_container }
+ let_it_be(:private_project_distribution) { private_distribution }
+ let_it_be(:public_project_distribution) { public_distribution }
+ end
+
+ let_it_be(:private_package) { create(:debian_package, project: private_project, published_in: private_project_distribution) }
+ let_it_be(:public_package) { create(:debian_package, project: public_project, published_in: public_project_distribution) }
+
+ let(:visibility_level) { :public }
+
+ let(:distribution) { { private: private_distribution, public: public_distribution }[visibility_level] }
+
let(:component) { 'main' }
let(:architecture) { 'amd64' }
let(:source_package) { 'sample' }
@@ -97,7 +125,7 @@ RSpec.shared_examples 'Debian repository GET request' do |status, body = nil|
expect(response).to have_gitlab_http_status(status)
unless body.nil?
- expect(response.body).to eq(body)
+ expect(response.body).to match(body)
end
end
end
@@ -125,7 +153,7 @@ RSpec.shared_examples 'Debian repository upload request' do |status, body = nil|
expect(response.media_type).to eq('text/plain')
unless body.nil?
- expect(response.body).to eq(body)
+ expect(response.body).to match(body)
end
end
it_behaves_like 'a package tracking event', described_class.name, 'push_package'
@@ -136,7 +164,7 @@ RSpec.shared_examples 'Debian repository upload request' do |status, body = nil|
expect(response).to have_gitlab_http_status(status)
unless body.nil?
- expect(response.body).to eq(body)
+ expect(response.body).to match(body)
end
end
end
@@ -182,18 +210,112 @@ RSpec.shared_examples 'Debian repository upload authorize request' do |status, b
expect(response).to have_gitlab_http_status(status)
unless body.nil?
- expect(response.body).to eq(body)
+ expect(response.body).to match(body)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'Debian repository POST distribution request' do |status, body|
+ and_body = body.nil? ? '' : ' and expected body'
+
+ if status == :created
+ it 'creates distribution', :aggregate_failures do
+ expect(::Packages::Debian::CreateDistributionService).to receive(:new).with(container, user, api_params).and_call_original
+
+ expect { subject }
+ .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(1)
+ .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(1)
+ .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(2)
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq('application/json')
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ else
+ it "returns #{status}#{and_body}", :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'Debian repository PUT distribution request' do |status, body|
+ and_body = body.nil? ? '' : ' and expected body'
+
+ if status == :success
+ it 'updates distribution', :aggregate_failures do
+ expect(::Packages::Debian::UpdateDistributionService).to receive(:new).with(distribution, api_params.except(:codename)).and_call_original
+
+ expect { subject }
+ .to not_change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }
+ .and not_change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }
+ .and not_change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq('application/json')
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ else
+ it "returns #{status}#{and_body}", :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+
+ unless body.nil?
+ expect(response.body).to match(body)
end
end
end
end
-RSpec.shared_examples 'rejects Debian access with unknown container id' do
+RSpec.shared_examples 'Debian repository DELETE distribution request' do |status, body|
+ and_body = body.nil? ? '' : ' and expected body'
+
+ if status == :success
+ it 'updates distribution', :aggregate_failures do
+ expect { subject }
+ .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(-1)
+ .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(-1)
+ .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(-2)
+
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq('application/json')
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ else
+ it "returns #{status}#{and_body}", :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(status)
+
+ unless body.nil?
+ expect(response.body).to match(body)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'rejects Debian access with unknown container id' do |hidden_status|
context 'with an unknown container' do
let(:container) { double(id: non_existing_record_id) }
context 'as anonymous' do
- it_behaves_like 'Debian repository GET request', :unauthorized, nil
+ it_behaves_like 'Debian repository GET request', hidden_status, nil
end
context 'as authenticated user' do
@@ -204,19 +326,25 @@ RSpec.shared_examples 'rejects Debian access with unknown container id' do
end
end
-RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, success_status, success_body|
+RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true|
+ hidden_status = if authenticate_non_public
+ :unauthorized
+ else
+ :not_found
+ end
+
context 'with valid container' do
using RSpec::Parameterized::TableSyntax
where(:visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do
:public | :developer | true | true | success_status | success_body
:public | :guest | true | true | success_status | success_body
- :public | :developer | true | false | success_status | success_body
- :public | :guest | true | false | success_status | success_body
+ :public | :developer | true | false | :unauthorized | nil
+ :public | :guest | true | false | :unauthorized | nil
:public | :developer | false | true | success_status | success_body
:public | :guest | false | true | success_status | success_body
- :public | :developer | false | false | success_status | success_body
- :public | :guest | false | false | success_status | success_body
+ :public | :developer | false | false | :unauthorized | nil
+ :public | :guest | false | false | :unauthorized | nil
:public | :anonymous | false | true | success_status | success_body
:private | :developer | true | true | success_status | success_body
:private | :guest | true | true | :forbidden | nil
@@ -226,7 +354,7 @@ RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, su
:private | :guest | false | true | :not_found | nil
:private | :developer | false | false | :unauthorized | nil
:private | :guest | false | false | :unauthorized | nil
- :private | :anonymous | false | true | :unauthorized | nil
+ :private | :anonymous | false | true | hidden_status | nil
end
with_them do
@@ -236,10 +364,16 @@ RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, su
end
end
- it_behaves_like 'rejects Debian access with unknown container id'
+ it_behaves_like 'rejects Debian access with unknown container id', hidden_status
end
-RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, success_status, success_body|
+RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true|
+ hidden_status = if authenticate_non_public
+ :unauthorized
+ else
+ :not_found
+ end
+
context 'with valid container' do
using RSpec::Parameterized::TableSyntax
@@ -261,7 +395,50 @@ RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, s
:private | :guest | false | true | :not_found | nil
:private | :developer | false | false | :unauthorized | nil
:private | :guest | false | false | :unauthorized | nil
- :private | :anonymous | false | true | :unauthorized | nil
+ :private | :anonymous | false | true | hidden_status | nil
+ end
+
+ with_them do
+ include_context 'Debian repository access', params[:visibility_level], params[:user_role], params[:member], params[:user_token], :basic do
+ it_behaves_like "Debian repository #{desired_behavior}", params[:expected_status], params[:expected_body]
+ end
+ end
+ end
+
+ it_behaves_like 'rejects Debian access with unknown container id', hidden_status
+end
+
+RSpec.shared_examples 'Debian repository maintainer write endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true|
+ hidden_status = if authenticate_non_public
+ :unauthorized
+ else
+ :not_found
+ end
+
+ context 'with valid container' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do
+ :public | :maintainer | true | true | success_status | success_body
+ :public | :developer | true | true | :forbidden | nil
+ :public | :guest | true | true | :forbidden | nil
+ :public | :maintainer | true | false | :unauthorized | nil
+ :public | :guest | true | false | :unauthorized | nil
+ :public | :maintainer | false | true | :forbidden | nil
+ :public | :guest | false | true | :forbidden | nil
+ :public | :maintainer | false | false | :unauthorized | nil
+ :public | :guest | false | false | :unauthorized | nil
+ :public | :anonymous | false | true | :unauthorized | nil
+ :private | :maintainer | true | true | success_status | success_body
+ :private | :developer | true | true | :forbidden | nil
+ :private | :guest | true | true | :forbidden | nil
+ :private | :maintainer | true | false | :unauthorized | nil
+ :private | :guest | true | false | :unauthorized | nil
+ :private | :maintainer | false | true | :not_found | nil
+ :private | :guest | false | true | :not_found | nil
+ :private | :maintainer | false | false | :unauthorized | nil
+ :private | :guest | false | false | :unauthorized | nil
+ :private | :anonymous | false | true | hidden_status | nil
end
with_them do
@@ -271,5 +448,5 @@ RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, s
end
end
- it_behaves_like 'rejects Debian access with unknown container id'
+ it_behaves_like 'rejects Debian access with unknown container id', hidden_status
end