diff options
56 files changed, 1138 insertions, 710 deletions
diff --git a/.gitlab/issue_templates/Feature Flag Roll Out.md b/.gitlab/issue_templates/Feature Flag Roll Out.md index 3972368ddc4..8aa631dce76 100644 --- a/.gitlab/issue_templates/Feature Flag Roll Out.md +++ b/.gitlab/issue_templates/Feature Flag Roll Out.md @@ -113,12 +113,14 @@ For visibility, all `/chatops` commands that target production should be execute For visibility, all `/chatops` commands that target production should be executed in the `#production` slack channel and cross-posted (with the command results) to the responsible team's slack channel (`#g_TEAM_NAME`). - [ ] [Incrementally roll out](https://docs.gitlab.com/ee/development/feature_flags/controls.html#process) the feature. + - [ ] Between every step wait for at least 15 minutes and monitor the appropriate graphs on https://dashboards.gitlab.net. - If the feature flag in code has [an actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), perform **actor-based** rollout. - [ ] `/chatops run feature set <feature-flag-name> <rollout-percentage> --actors` - If the feature flag in code does **NOT** have [an actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), perform time-based rollout (**random** rollout). - [ ] `/chatops run feature set <feature-flag-name> <rollout-percentage> --random` - Enable the feature globally on production environment. - [ ] `/chatops run feature set <feature-flag-name> true` +- [ ] Observe appropriate graphs on https://dashboards.gitlab.net and verify that services are not affected. - [ ] Leave a comment on [the feature issue][main-issue] announcing that the feature has been globally enabled. - [ ] Wait for [at least one day for the verification term](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/#including-a-feature-behind-feature-flag-in-the-final-release). diff --git a/app/assets/javascripts/members/components/action_dropdowns/constants.js b/app/assets/javascripts/members/components/action_dropdowns/constants.js index f6718713e2b..eb5b2182ece 100644 --- a/app/assets/javascripts/members/components/action_dropdowns/constants.js +++ b/app/assets/javascripts/members/components/action_dropdowns/constants.js @@ -11,4 +11,8 @@ export const I18N = { confirmOrphanedUserRemoval: s__( 'Members|Are you sure you want to remove this orphaned member from "%{group}"?', ), + personalProjectOwnerCannotBeRemoved: s__("Members|A personal project's owner cannot be removed."), + lastGroupOwnerCannotBeRemoved: s__( + 'Members|A group must have at least one owner. To remove the member, assign a new owner.', + ), }; diff --git a/app/assets/javascripts/members/components/action_dropdowns/leave_group_dropdown_item.vue b/app/assets/javascripts/members/components/action_dropdowns/leave_group_dropdown_item.vue index b5e0317d54f..15606ad567c 100644 --- a/app/assets/javascripts/members/components/action_dropdowns/leave_group_dropdown_item.vue +++ b/app/assets/javascripts/members/components/action_dropdowns/leave_group_dropdown_item.vue @@ -18,6 +18,10 @@ export default { type: Object, required: true, }, + permissions: { + type: Object, + required: true, + }, }, }; </script> @@ -27,6 +31,6 @@ export default { <span class="gl-text-red-500"> <slot></slot> </span> - <leave-modal :member="member" /> + <leave-modal :member="member" :permissions="permissions" /> </gl-dropdown-item> </template> diff --git a/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue b/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue index 18f1a8739bf..f224aaa31f7 100644 --- a/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue +++ b/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue @@ -40,6 +40,11 @@ export default { required: false, default: () => ({}), }, + preventRemoval: { + type: Boolean, + required: false, + default: false, + }, }, computed: { ...mapState({ @@ -55,6 +60,7 @@ export default { memberModelType: this.memberModelType, message: this.modalMessage, userDeletionObstacles: this.userDeletionObstacles, + preventRemoval: this.preventRemoval, }; }, }, diff --git a/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue b/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue index 4e5e6b0cab4..816481d4329 100644 --- a/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue +++ b/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue @@ -2,6 +2,10 @@ import { GlDropdown, GlTooltipDirective } from '@gitlab/ui'; import { sprintf } from '~/locale'; import { parseUserDeletionObstacles } from '~/vue_shared/components/user_deletion_obstacles/utils'; +import { + MEMBER_MODEL_TYPE_GROUP_MEMBER, + MEMBER_MODEL_TYPE_PROJECT_MEMBER, +} from '~/members/constants'; import { I18N } from './constants'; import LeaveGroupDropdownItem from './leave_group_dropdown_item.vue'; import RemoveMemberDropdownItem from './remove_member_dropdown_item.vue'; @@ -37,6 +41,16 @@ export default { modalMessage() { const { user, source } = this.member; + if (this.permissions.canRemoveBlockedByLastOwner) { + if (this.member.type === MEMBER_MODEL_TYPE_PROJECT_MEMBER) { + return I18N.personalProjectOwnerCannotBeRemoved; + } + + if (this.member.type === MEMBER_MODEL_TYPE_GROUP_MEMBER) { + return I18N.lastGroupOwnerCannotBeRemoved; + } + } + if (user) { return sprintf( this.$options.i18n.confirmNormalUserRemoval, @@ -54,7 +68,10 @@ export default { }; }, showDropdown() { - return this.permissions.canRemove || this.showLdapOverride; + return this.showLeaveOrRemove || this.showLdapOverride; + }, + showLeaveOrRemove() { + return this.permissions.canRemove || this.permissions.canRemoveBlockedByLastOwner; }, showLdapOverride() { return this.permissions.canOverride && !this.member.isOverridden; @@ -76,8 +93,8 @@ export default { data-testid="user-action-dropdown" data-qa-selector="user_action_dropdown" > - <template v-if="permissions.canRemove"> - <leave-group-dropdown-item v-if="isCurrentUser" :member="member">{{ + <template v-if="showLeaveOrRemove"> + <leave-group-dropdown-item v-if="isCurrentUser" :member="member" :permissions="permissions">{{ $options.i18n.leaveGroup }}</leave-group-dropdown-item> <remove-member-dropdown-item @@ -86,6 +103,7 @@ export default { :member-model-type="member.type" :user-deletion-obstacles="userDeletionObstaclesUserData" :modal-message="modalMessage" + :prevent-removal="permissions.canRemoveBlockedByLastOwner" >{{ $options.i18n.removeMember }}</remove-member-dropdown-item > </template> diff --git a/app/assets/javascripts/members/components/modals/leave_modal.vue b/app/assets/javascripts/members/components/modals/leave_modal.vue index e39669e17dd..8bc6aca9cc1 100644 --- a/app/assets/javascripts/members/components/modals/leave_modal.vue +++ b/app/assets/javascripts/members/components/modals/leave_modal.vue @@ -5,22 +5,30 @@ import csrf from '~/lib/utils/csrf'; import { __, s__, sprintf } from '~/locale'; import UserDeletionObstaclesList from '~/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.vue'; import { parseUserDeletionObstacles } from '~/vue_shared/components/user_deletion_obstacles/utils'; -import { LEAVE_MODAL_ID } from '../../constants'; +import { + LEAVE_MODAL_ID, + MEMBER_MODEL_TYPE_GROUP_MEMBER, + MEMBER_MODEL_TYPE_PROJECT_MEMBER, +} from '../../constants'; export default { name: 'LeaveModal', actionCancel: { text: __('Cancel'), }, - actionPrimary: { - text: __('Leave'), - attributes: { - variant: 'danger', - }, - }, csrf, modalId: LEAVE_MODAL_ID, - modalContent: s__('Members|Are you sure you want to leave "%{source}"?'), + i18n: { + title: s__('Members|Leave "%{source}"'), + body: s__('Members|Are you sure you want to leave "%{source}"?'), + preventedTitle: s__('Members|Cannot leave "%{source}"'), + preventedBodyProjectMemberModelType: s__( + 'Members|You cannot remove yourself from a personal project.', + ), + preventedBodyGroupMemberModelType: s__( + 'Members|A group must have at least one owner. To leave this group, assign a new owner.', + ), + }, components: { GlModal, GlForm, GlSprintf, UserDeletionObstaclesList }, directives: { GlTooltip: GlTooltipDirective, @@ -31,6 +39,10 @@ export default { type: Object, required: true, }, + permissions: { + type: Object, + required: true, + }, }, computed: { ...mapState({ @@ -42,7 +54,35 @@ export default { return this.memberPath.replace(/:id$/, 'leave'); }, modalTitle() { - return sprintf(s__('Members|Leave "%{source}"'), { source: this.member.source.fullName }); + return sprintf( + this.permissions.canRemoveBlockedByLastOwner + ? this.$options.i18n.preventedTitle + : this.$options.i18n.title, + { source: this.member.source.fullName }, + ); + }, + preventedModalBody() { + if (this.member.type === MEMBER_MODEL_TYPE_PROJECT_MEMBER) { + return this.$options.i18n.preventedBodyProjectMemberModelType; + } + + if (this.member.type === MEMBER_MODEL_TYPE_GROUP_MEMBER) { + return this.$options.i18n.preventedBodyGroupMemberModelType; + } + + return null; + }, + actionPrimary() { + if (this.permissions.canRemoveBlockedByLastOwner) { + return null; + } + + return { + text: __('Leave'), + attributes: { + variant: 'danger', + }, + }; }, obstacles() { return parseUserDeletionObstacles(this.member.user); @@ -64,13 +104,14 @@ export default { v-bind="$attrs" :modal-id="$options.modalId" :title="modalTitle" - :action-primary="$options.actionPrimary" + :action-primary="actionPrimary" :action-cancel="$options.actionCancel" @primary="handlePrimary" > <gl-form ref="form" :action="leavePath" method="post"> <p> - <gl-sprintf :message="$options.modalContent"> + <template v-if="permissions.canRemoveBlockedByLastOwner">{{ preventedModalBody }}</template> + <gl-sprintf v-else :message="$options.i18n.body"> <template #source>{{ member.source.fullName }}</template> </gl-sprintf> </p> diff --git a/app/assets/javascripts/members/components/modals/remove_member_modal.vue b/app/assets/javascripts/members/components/modals/remove_member_modal.vue index edd464574fb..337379d8b4e 100644 --- a/app/assets/javascripts/members/components/modals/remove_member_modal.vue +++ b/app/assets/javascripts/members/components/modals/remove_member_modal.vue @@ -42,6 +42,9 @@ export default { userDeletionObstacles(state) { return state[this.namespace].removeMemberModalData.userDeletionObstacles ?? {}; }, + preventRemoval(state) { + return state[this.namespace].removeMemberModalData.preventRemoval; + }, removeMemberModalVisible(state) { return state[this.namespace].removeMemberModalVisible; }, @@ -59,6 +62,10 @@ export default { return __('Remove member'); }, actionPrimary() { + if (this.preventRemoval) { + return null; + } + return { text: this.actionText, attributes: { @@ -101,21 +108,22 @@ export default { > <form ref="form" :action="memberPath" method="post"> <p>{{ message }}</p> + <template v-if="!preventRemoval"> + <user-deletion-obstacles-list + v-if="hasObstaclesToUserDeletion" + :obstacles="userDeletionObstacles.obstacles" + :user-name="userDeletionObstacles.name" + /> - <user-deletion-obstacles-list - v-if="hasObstaclesToUserDeletion" - :obstacles="userDeletionObstacles.obstacles" - :user-name="userDeletionObstacles.name" - /> - - <input ref="method" type="hidden" name="_method" value="delete" /> - <input :value="$options.csrf.token" type="hidden" name="authenticity_token" /> - <gl-form-checkbox v-if="isGroupMember" name="remove_sub_memberships"> - {{ __('Also remove direct user membership from subgroups and projects') }} - </gl-form-checkbox> - <gl-form-checkbox v-if="hasWorkspaceAccess" name="unassign_issuables"> - {{ __('Also unassign this user from related issues and merge requests') }} - </gl-form-checkbox> + <input ref="method" type="hidden" name="_method" value="delete" /> + <input :value="$options.csrf.token" type="hidden" name="authenticity_token" /> + <gl-form-checkbox v-if="isGroupMember" name="remove_sub_memberships"> + {{ __('Also remove direct user membership from subgroups and projects') }} + </gl-form-checkbox> + <gl-form-checkbox v-if="hasWorkspaceAccess" name="unassign_issuables"> + {{ __('Also unassign this user from related issues and merge requests') }} + </gl-form-checkbox> + </template> </form> </gl-modal> </template> diff --git a/app/assets/javascripts/members/components/table/members_table.vue b/app/assets/javascripts/members/components/table/members_table.vue index c847f9c8311..6d242f38d0e 100644 --- a/app/assets/javascripts/members/components/table/members_table.vue +++ b/app/assets/javascripts/members/components/table/members_table.vue @@ -2,7 +2,14 @@ import { GlTable, GlBadge, GlPagination } from '@gitlab/ui'; import { mapState } from 'vuex'; import MembersTableCell from 'ee_else_ce/members/components/table/members_table_cell.vue'; -import { canUnban, canOverride, canRemove, canResend, canUpdate } from 'ee_else_ce/members/utils'; +import { + canUnban, + canOverride, + canRemove, + canRemoveBlockedByLastOwner, + canResend, + canUpdate, +} from 'ee_else_ce/members/utils'; import { mergeUrlParams } from '~/lib/utils/url_utility'; import { FIELD_KEY_ACTIONS, @@ -43,7 +50,7 @@ export default { LdapOverrideConfirmationModal: () => import('ee_component/members/components/ldap/ldap_override_confirmation_modal.vue'), }, - inject: ['namespace', 'currentUserId'], + inject: ['namespace', 'currentUserId', 'canManageMembers'], props: { tabQueryParamValue: { type: String, @@ -84,6 +91,7 @@ export default { hasActionButtons(member) { return ( canRemove(member) || + canRemoveBlockedByLastOwner(member, this.canManageMembers) || canResend(member) || canUpdate(member, this.currentUserId) || canOverride(member) || diff --git a/app/assets/javascripts/members/components/table/members_table_cell.vue b/app/assets/javascripts/members/components/table/members_table_cell.vue index 51eff428d63..407cbc55dd3 100644 --- a/app/assets/javascripts/members/components/table/members_table_cell.vue +++ b/app/assets/javascripts/members/components/table/members_table_cell.vue @@ -5,13 +5,14 @@ import { isDirectMember, isCurrentUser, canRemove, + canRemoveBlockedByLastOwner, canResend, canUpdate, } from '../../utils'; export default { name: 'MembersTableCell', - inject: ['currentUserId'], + inject: ['currentUserId', 'canManageMembers'], props: { member: { type: Object, @@ -45,6 +46,9 @@ export default { isCurrentUser() { return isCurrentUser(this.member, this.currentUserId); }, + canRemoveBlockedByLastOwner() { + return canRemoveBlockedByLastOwner(this.member, this.canManageMembers); + }, canRemove() { return canRemove(this.member); }, @@ -62,6 +66,7 @@ export default { isCurrentUser: this.isCurrentUser, permissions: { canRemove: this.canRemove, + canRemoveBlockedByLastOwner: this.canRemoveBlockedByLastOwner, canResend: this.canResend, canUpdate: this.canUpdate, }, diff --git a/app/assets/javascripts/members/utils.js b/app/assets/javascripts/members/utils.js index bf87ab53d36..b7af97ebac5 100644 --- a/app/assets/javascripts/members/utils.js +++ b/app/assets/javascripts/members/utils.js @@ -51,6 +51,9 @@ export const canRemove = (member) => { return isDirectMember(member) && member.canRemove; }; +export const canRemoveBlockedByLastOwner = (member, canManageMembers) => + isDirectMember(member) && canManageMembers && member.isLastOwner; + export const canResend = (member) => { return Boolean(member.invite?.canResend); }; diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb index 69cf1c42f34..8676dfa1da2 100644 --- a/app/controllers/registrations/welcome_controller.rb +++ b/app/controllers/registrations/welcome_controller.rb @@ -26,13 +26,14 @@ module Registrations return redirect_to issues_dashboard_path(assignee_username: current_user.username) if show_tasks_to_be_done? - return redirect_to update_success_path if show_signup_onboarding? + return redirect_to update_success_path if redirect_to_signup_onboarding? members = current_user.members if registering_from_invite?(members) redirect_to members_activity_path(members), notice: helpers.invite_accepted_notice(members.last) else + # subscription registrations goes through here as well redirect_to path_for_signed_in_user(current_user) end else @@ -79,7 +80,7 @@ module Registrations end # overridden in EE - def show_signup_onboarding? + def redirect_to_signup_onboarding? false end diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml index 0b26db64ffa..8547795b4b7 100644 --- a/app/views/groups/_group_admin_settings.html.haml +++ b/app/views/groups/_group_admin_settings.html.haml @@ -36,4 +36,4 @@ = f.gitlab_ui_checkbox_component :runner_registration_enabled, s_('Runners|New group runners can be registered'), checkbox_options: { checked: @group.runner_registration_enabled && !parent_disabled, disabled: parent_disabled }, - help_text: s_('Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings > CI/CD.').html_safe + help_text: s_('Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings > CI/CD.').html_safe diff --git a/config/feature_flags/development/admin_emails_vue.yml b/config/feature_flags/development/admin_emails_vue.yml new file mode 100644 index 00000000000..63fe56e089f --- /dev/null +++ b/config/feature_flags/development/admin_emails_vue.yml @@ -0,0 +1,8 @@ +--- +name: admin_emails_vue +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106358 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/386976 +milestone: '15.8' +type: development +group: group::workspace +default_enabled: false diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md index d76c728fb78..80a6d785789 100644 --- a/doc/administration/packages/container_registry.md +++ b/doc/administration/packages/container_registry.md @@ -777,7 +777,7 @@ auth: Without these entries, the registry logins cannot authenticate with GitLab. GitLab also remains unaware of -[nested image names](../../user/packages/container_registry/index.md#image-naming-convention) +[nested image names](../../user/packages/container_registry/index.md#naming-convention-for-your-container-images) under the project hierarchy, like `registry.example.com/group/project/image-name:tag` or `registry.example.com/group/project/my/image-name:tag`, and only recognizes diff --git a/doc/api/graphql/users_example.md b/doc/api/graphql/users_example.md index 9d223f9e618..20185d81513 100644 --- a/doc/api/graphql/users_example.md +++ b/doc/api/graphql/users_example.md @@ -12,7 +12,6 @@ You can run the same query directly via a HTTP endpoint, using `cURL`. For more guidance on getting started from the [command line](getting_started.md#command-line). The [example users query](#set-up-the-graphiql-explorer) looks for a subset of users in -o a GitLab instance either by username or [Global ID](../../development/api_graphql_styleguide.md#global-ids). The query includes: diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md index 371e3f9ae47..14f8ae3b15b 100644 --- a/doc/api/oauth2.md +++ b/doc/api/oauth2.md @@ -417,7 +417,7 @@ Standard OAuth 2.0 tokens support different degrees of access to GitLab registries, as they: - Do not allow users to authenticate to: - - The GitLab [Container registry](../user/packages/container_registry/index.md#authenticate-with-the-container-registry). + - The GitLab [Container registry](../user/packages/container_registry/authenticate_with_container_registry.md). - Packages listed in the GitLab [Package registry](../user/packages/package_registry/index.md). - Allow users to get, list, and delete registries through the [Container registry API](container_registry.md). diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index ea62133cb7f..04c790cf3c6 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -872,7 +872,7 @@ build: ## Use the GitLab Container Registry After you've built a Docker image, you can push it up to the built-in -[GitLab Container Registry](../../user/packages/container_registry/index.md#build-and-push-by-using-gitlab-cicd). +[GitLab Container Registry](../../user/packages/container_registry/build_and_push_images.md#use-gitlab-cicd). ## Troubleshooting diff --git a/doc/ci/jobs/ci_job_token.md b/doc/ci/jobs/ci_job_token.md index d95451a67dc..4ae4456c56c 100644 --- a/doc/ci/jobs/ci_job_token.md +++ b/doc/ci/jobs/ci_job_token.md @@ -14,7 +14,7 @@ You can use a GitLab CI/CD job token to authenticate with specific API endpoints - Packages: - [Package Registry](../../user/packages/package_registry/index.md#use-gitlab-cicd-to-build-packages). - [Packages API](../../api/packages.md) (project-level). - - [Container Registry](../../user/packages/container_registry/index.md#build-and-push-by-using-gitlab-cicd) + - [Container Registry](../../user/packages/container_registry/build_and_push_images.md#use-gitlab-cicd) (the `$CI_REGISTRY_PASSWORD` is `$CI_JOB_TOKEN`). - [Container Registry API](../../api/container_registry.md) (scoped to the job's project, when the `ci_job_token_scope` feature flag is enabled). diff --git a/doc/ci/migration/jenkins.md b/doc/ci/migration/jenkins.md index 235dd0e80ca..5e5d67fdaac 100644 --- a/doc/ci/migration/jenkins.md +++ b/doc/ci/migration/jenkins.md @@ -21,7 +21,7 @@ that were able to quickly complete this migration: 1. Educate and enable your developers to independently perform the following steps in their projects: 1. Review the [Quick Start Guide](../quick_start/index.md) and [Pipeline Configuration Reference](../yaml/index.md). 1. Use the [Jenkins Wrapper](#jenkinsfile-wrapper) to temporarily maintain fragile Jenkins jobs. - 1. Migrate the build and CI jobs and configure them to show results directly in your merge requests. They can use [Auto DevOps](../../topics/autodevops/index.md) as a starting point, and [customize](../../topics/autodevops/customize.md) or [decompose](../../topics/autodevops/customize.md#using-components-of-auto-devops) the configuration as needed. + 1. Migrate the build and CI jobs and configure them to show results directly in your merge requests. They can use [Auto DevOps](../../topics/autodevops/index.md) as a starting point, and [customize](../../topics/autodevops/customize.md) or [decompose](../../topics/autodevops/customize.md#use-individual-components-of-auto-devops) the configuration as needed. 1. Add [Review Apps](../review_apps/index.md). 1. Migrate the deployment jobs using [cloud deployment templates](../cloud_deployment/index.md), adding [environments](../environments/index.md), and [deploy boards](../../user/project/deploy_boards.md). 1. Work to unwrap any jobs still running with the use of the Jenkins wrapper. diff --git a/doc/development/architecture.md b/doc/development/architecture.md index 5eb1dcc3208..c7aa3ed09c4 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -761,7 +761,7 @@ See our [Redis guidelines](redis.md) for more information about how GitLab uses - [Source](../administration/packages/container_registry.md#enable-the-container-registry) - [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/registry.md) - Layer: Core Service (Processor) -- GitLab.com: [GitLab Container Registry](../user/packages/container_registry/index.md#build-and-push-by-using-gitlab-cicd) +- GitLab.com: [GitLab Container Registry](../user/packages/container_registry/build_and_push_images.md#use-gitlab-cicd) The registry is what users use to store their own Docker images. The bundled registry uses NGINX as a load balancer and GitLab as an authentication manager. diff --git a/doc/development/features_inside_dot_gitlab.md b/doc/development/features_inside_dot_gitlab.md index 7a46cd40da1..dda2c05288f 100644 --- a/doc/development/features_inside_dot_gitlab.md +++ b/doc/development/features_inside_dot_gitlab.md @@ -15,7 +15,7 @@ When implementing new features, please refer to these existing features to avoid - [GitLab agent](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/configuration_repository.md#layout): `.gitlab/agents/`. - [CODEOWNERS](../user/project/code_owners.md#set-up-code-owners): `.gitlab/CODEOWNERS`. - [Route Maps](../ci/review_apps/index.md#route-maps): `.gitlab/route-map.yml`. -- [Customize Auto DevOps Helm Values](../topics/autodevops/customize.md#customize-values-for-helm-chart): `.gitlab/auto-deploy-values.yaml`. +- [Customize Auto DevOps Helm Values](../topics/autodevops/customize.md#customize-helm-chart-values): `.gitlab/auto-deploy-values.yaml`. - [Insights](../user/project/insights/index.md#configure-project-insights): `.gitlab/insights.yml`. - [Service Desk Templates](../user/project/service_desk.md#using-customized-email-templates): `.gitlab/service_desk_templates/`. - [Web IDE](../user/project/web_ide/index.md#web-ide-configuration-file): `.gitlab/.gitlab-webide.yml`. diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md index f828fa62244..1e5f4191375 100644 --- a/doc/development/service_ping/implement.md +++ b/doc/development/service_ping/implement.md @@ -804,7 +804,7 @@ and run a local container instance: 1. In the downstream pipeline, wait for the `gitlab-docker` job to finish. 1. Open the job logs and locate the full container name including the version. It takes the following form: `registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:<VERSION>`. 1. On your local machine, make sure you are signed in to the GitLab Docker registry. You can find the instructions for this in - [Authenticate to the GitLab Container Registry](../../user/packages/container_registry/index.md#authenticate-with-the-container-registry). + [Authenticate to the GitLab Container Registry](../../user/packages/container_registry/authenticate_with_container_registry.md). 1. Once signed in, download the new image by using `docker pull registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:<VERSION>` 1. For more information about working with and running Omnibus GitLab containers in Docker, refer to [GitLab Docker images](../../install/docker.md) documentation. diff --git a/doc/topics/autodevops/cicd_variables.md b/doc/topics/autodevops/cicd_variables.md index ad248bc3ccd..470f973a318 100644 --- a/doc/topics/autodevops/cicd_variables.md +++ b/doc/topics/autodevops/cicd_variables.md @@ -22,7 +22,7 @@ Use these variables to customize and deploy your build. | `AUTO_DEVOPS_ATOMIC_RELEASE` | As of GitLab 13.0, Auto DevOps uses [`--atomic`](https://v2.helm.sh/docs/helm/#options-43) for Helm deployments by default. Set this variable to `false` to disable the use of `--atomic` | | `AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED` | Set to `false` to use Herokuish instead of Cloud Native Buildpacks with Auto Build. [More details](stages.md#auto-build-using-cloud-native-buildpacks). | | `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER` | The builder used when building with Cloud Native Buildpacks. The default builder is `heroku/buildpacks:18`. [More details](stages.md#auto-build-using-cloud-native-buildpacks). | -| `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` | Extra arguments to be passed to the `docker build` command. Using quotes doesn't prevent word splitting. [More details](customize.md#passing-arguments-to-docker-build). | +| `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` | Extra arguments to be passed to the `docker build` command. Using quotes doesn't prevent word splitting. [More details](customize.md#pass-arguments-to-docker-build). | | `AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` | A [comma-separated list of CI/CD variable names](customize.md#forward-cicd-variables-to-the-build-environment) to be forwarded to the build environment (the buildpack builder or `docker build`). | | `AUTO_DEVOPS_BUILD_IMAGE_CNB_PORT` | In GitLab 15.0 and later, port exposed by the generated Docker image. Set to `false` to prevent exposing any ports. Defaults to `5000`. | | `AUTO_DEVOPS_CHART` | Helm Chart used to deploy your apps. Defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app). | @@ -42,7 +42,7 @@ Use these variables to customize and deploy your build. | `CI_APPLICATION_REPOSITORY` | The repository of container image being built or deployed, `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG`. For more details, read [Custom container image](customize.md#custom-container-image). | | `CI_APPLICATION_TAG` | The tag of the container image being built or deployed, `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG`. For more details, read [Custom container image](customize.md#custom-container-image). | | `DAST_AUTO_DEPLOY_IMAGE_VERSION` | Customize the image version used for DAST deployments on the default branch. Should usually be the same as `AUTO_DEPLOY_IMAGE_VERSION`. See [list of versions](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/releases). | -| `DOCKERFILE_PATH` | From GitLab 13.2, allows overriding the [default Dockerfile path for the build stage](customize.md#custom-dockerfile) | +| `DOCKERFILE_PATH` | From GitLab 13.2, allows overriding the [default Dockerfile path for the build stage](customize.md#custom-dockerfiles) | | `HELM_RELEASE_NAME` | Allows the `helm` release name to be overridden. Can be used to assign unique release names when deploying multiple projects to a single namespace. | | `HELM_UPGRADE_VALUES_FILE` | Allows the `helm upgrade` values file to be overridden. Defaults to `.gitlab/auto-deploy-values.yaml`. | | `HELM_UPGRADE_EXTRA_ARGS` | Allows extra options in `helm upgrade` commands when deploying the application. Using quotes doesn't prevent word splitting. | @@ -53,7 +53,7 @@ Use these variables to customize and deploy your build. | `KUBE_NAMESPACE` | The namespace used for deployments. When using certificate-based clusters, [this value should not be overwritten directly](../../user/project/clusters/deploy_to_cluster.md#custom-namespace). | | `KUBECONFIG` | The kubeconfig to use for deployments. User-provided values take priority over GitLab-provided values. | | `PRODUCTION_REPLICAS` | Number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. | -| `REPLICAS` | Number of replicas to deploy. Defaults to 1. Change this variable instead of [modifying](customize.md#customize-values-for-helm-chart) `replicaCount`. | +| `REPLICAS` | Number of replicas to deploy. Defaults to 1. Change this variable instead of [modifying](customize.md#customize-helm-chart-values) `replicaCount`. | | `ROLLOUT_RESOURCE_TYPE` | Allows specification of the resource type being deployed when using a custom Helm chart. Default value is `deployment`. | | `ROLLOUT_STATUS_DISABLED` | Used to disable rollout status check because it does not support all resource types, for example, `cronjob`. | | `STAGING_ENABLED` | Used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). | diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md index c42f5825b6a..43769410eba 100644 --- a/doc/topics/autodevops/customize.md +++ b/doc/topics/autodevops/customize.md @@ -4,23 +4,22 @@ group: Configure info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Customizing Auto DevOps **(FREE)** +# Customize Auto DevOps **(FREE)** -While [Auto DevOps](index.md) provides great defaults to get you started, you can customize -almost everything to fit your needs. Auto DevOps offers everything from custom -[buildpacks](#custom-buildpacks), to [Dockerfiles](#custom-dockerfile), and -[Helm charts](#custom-helm-chart). You can even copy the complete -[CI/CD configuration](#customizing-gitlab-ciyml) into your project to enable -staging and canary deployments, -[manage Auto DevOps with GitLab APIs](customize.md#extend-auto-devops-with-the-api), and more. +You can customize components of Auto DevOps to fit your needs. For example, you can: + +- Add custom [buildpacks](#custom-buildpacks), [Dockerfiles](#custom-dockerfiles), and [Helm charts](#custom-helm-chart). +- Enable staging and canary deployments with a custom [CI/CD configuration](#customize-gitlab-ciyml). +- Extend Auto DevOps with the [GitLab API](#extend-auto-devops-with-the-api). ## Custom buildpacks -If the automatic buildpack detection fails for your project, or if you -need more control over your build, you can customize the buildpacks -used for the build. +You can customize your buildpacks when either: + +- The automatic buildpack detection fails for your project. +- You need more control over your build. -### Custom buildpacks with Cloud Native Buildpacks +### Customize buildpacks with Cloud Native Buildpacks > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28165) in GitLab 12.10. @@ -29,17 +28,18 @@ Specify either: - The CI/CD variable `BUILDPACK_URL` with any of [`pack`'s URI specification formats](https://buildpacks.io/docs/app-developer-guide/specify-buildpacks/). - A [`project.toml` project descriptor](https://buildpacks.io/docs/app-developer-guide/using-project-descriptor/) with the buildpacks you would like to include. -### Custom buildpacks with Herokuish +### Customize buildpacks with Herokuish Specify either: - The CI/CD variable `BUILDPACK_URL`. -- A `.buildpacks` file at the root of your project, containing one buildpack URL per line. +- A `.buildpacks` file at the root of your project that contains one buildpack URL per line. The buildpack URL can point to either a Git repository URL or a tarball URL. -For Git repositories, you can point to a specific Git reference (such as -commit SHA, tag name, or branch name) by appending `#<ref>` to the Git repository URL. -For example: + +For Git repositories, you can point to a specific Git reference by +appending `#<ref>` to the Git repository URL. For example, you can +reference: - The tag `v142`: `https://github.com/heroku/heroku-buildpack-ruby.git#v142`. - The branch `mybranch`: `https://github.com/heroku/heroku-buildpack-ruby.git#mybranch`. @@ -47,35 +47,38 @@ For example: ### Multiple buildpacks -Using multiple buildpacks is not fully supported by Auto DevOps, because Auto Test -can't use the `.buildpacks` file. The buildpack -[heroku-buildpack-multi](https://github.com/heroku/heroku-buildpack-multi/), used -in the backend to parse the `.buildpacks` file, does not provide the necessary commands -`bin/test-compile` and `bin/test`. +Because Auto Test cannot use the `.buildpacks` file, Auto DevOps does +not support multiple buildpacks. The buildpack +[heroku-buildpack-multi](https://github.com/heroku/heroku-buildpack-multi/), +used in the backend to parse the `.buildpacks` file, does not provide +the necessary commands `bin/test-compile` and `bin/test`. -If your goal is to use only a single custom buildpack, you should provide the project CI/CD variable +To use only a single custom buildpack, you should provide the project CI/CD variable `BUILDPACK_URL` instead. -## Custom `Dockerfile` +## Custom Dockerfiles > Support for `DOCKERFILE_PATH` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35662) in GitLab 13.2 -If your project has a `Dockerfile` in the root of the project repository, Auto DevOps -builds a Docker image based on the Dockerfile, rather than using buildpacks. -This can be much faster and result in smaller images, especially if your -Dockerfile is based on [Alpine](https://hub.docker.com/_/alpine/). +If you have a Dockerfile in the root of your project repository, Auto +DevOps builds a Docker image based on the Dockerfile. This can be +faster than using a buildpack. It can also result in smaller images, +especially if your Dockerfile is based on +[Alpine](https://hub.docker.com/_/alpine/). If you set the `DOCKERFILE_PATH` CI/CD variable, Auto Build looks for a Dockerfile there instead. -## Passing arguments to `docker build` +### Pass arguments to `docker build` -Arguments can be passed to the `docker build` command using the -`AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` project CI/CD variable. For example, to build a -Docker image based on based on the `ruby:alpine` instead of the default `ruby:latest`: +You can pass arguments to `docker build` with the +`AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` project CI/CD variable. + +For example, to build a Docker image based on based on the +`ruby:alpine` instead of the default `ruby:latest`: 1. Set `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` to `--build-arg=RUBY_VERSION=alpine`. -1. Add the following to a custom `Dockerfile`: +1. Add the following to a custom Dockerfile: ```dockerfile ARG RUBY_VERSION=latest @@ -84,14 +87,12 @@ Docker image based on based on the `ruby:alpine` instead of the default `ruby:la # ... put your stuff here ``` -Use Base64 encoding if you need to pass complex values, such as newlines and -spaces. Left unencoded, complex values like these can cause escaping issues -due to how Auto DevOps uses the arguments. +To pass complex values like spaces and newlines, use Base64 encoding. +Complex, unencoded values can cause issues with character escaping. WARNING: -Avoid passing secrets as Docker build arguments if possible, as they may be -persisted in your image. See -[this discussion of best practices with secrets](https://github.com/moby/moby/issues/13490) for details. +Do not pass secrets as Docker build arguments. Secrets might persist in your image. For more information, see +[this discussion of best practices with secrets](https://github.com/moby/moby/issues/13490). ## Custom container image @@ -104,12 +105,12 @@ You can override this behavior by defining specific variables: | Image Tag | `$CI_COMMIT_SHA` for branch pipelines. `$CI_COMMIT_TAG` for tag pipelines. | `$CI_APPLICATION_TAG` | These variables also affect Auto Build and Auto Container Scanning. If you don't want to build and push an image to -`$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG`, consider -including only `Jobs/Deploy.gitlab-ci.yml`, or [disabling the `build` jobs](cicd_variables.md#job-disabling-variables). +`$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG`, include only `Jobs/Deploy.gitlab-ci.yml`, or +[disable the `build` jobs](cicd_variables.md#job-disabling-variables). If you use Auto Container Scanning and set a value for `$CI_APPLICATION_REPOSITORY`, then you should -also update `$CS_DEFAULT_BRANCH_IMAGE`. See [Setting the default branch image](../../user/application_security/container_scanning/index.md#setting-the-default-branch-image) -for more details. +also update `$CS_DEFAULT_BRANCH_IMAGE`. For more information, see +[Setting the default branch image](../../user/application_security/container_scanning/index.md#setting-the-default-branch-image). Here is an example setup in your `.gitlab-ci.yml`: @@ -123,119 +124,120 @@ variables: You can extend and manage your Auto DevOps configuration with GitLab APIs: -- [Settings that can be accessed with API calls](../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls), +- [Use API calls to access settings](../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls), which include `auto_devops_enabled`, to enable Auto DevOps on projects by default. -- [Creating a new project](../../api/projects.md#create-project). -- [Editing groups](../../api/groups.md#update-group). -- [Editing projects](../../api/projects.md#edit-project). +- [Create a new project](../../api/projects.md#create-project). +- [Edit groups](../../api/groups.md#update-group). +- [Edit projects](../../api/projects.md#edit-project). ## Forward CI/CD variables to the build environment -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/25514) in GitLab 12.3, but available in GitLab 12.0 and later. +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/25514) in GitLab 12.3. + +To forward CI/CD variables to the build environment, add the names of the variables +you want to forward to the `AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` CI/CD variable. +Separate multiple variables with commas. -CI/CD variables can be forwarded into the build environment using the -`AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` CI/CD variable. -The forwarded variables should be specified by name in a comma-separated -list. For example, to forward the variables `CI_COMMIT_SHA` and -`CI_ENVIRONMENT_NAME`, set `AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` -to `CI_COMMIT_SHA,CI_ENVIRONMENT_NAME`. +For example, to forward the variables `CI_COMMIT_SHA` and `CI_ENVIRONMENT_NAME`: -- When using Buildpacks, the forwarded variables are available automatically - as environment variables. -- When using a `Dockerfile`, the following additional steps are required: +```yaml +variables: + AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES: CI_COMMIT_SHA,CI_ENVIRONMENT_NAME +``` - 1. Activate the experimental `Dockerfile` syntax by adding the following code - to the top of the file: +If you use buildpacks, the forwarded variables are available automatically as environment variables. - ```dockerfile - # syntax = docker/dockerfile:experimental - ``` +If you use a Dockerfile: - 1. To make secrets available in any `RUN $COMMAND` in the `Dockerfile`, mount - the secret file and source it prior to running `$COMMAND`: +1. To activate the experimental Dockerfile syntax, add the following to your Dockerfile: - ```dockerfile - RUN --mount=type=secret,id=auto-devops-build-secrets . /run/secrets/auto-devops-build-secrets && $COMMAND - ``` + ```dockerfile + # syntax = docker/dockerfile:experimental + ``` + +1. To make secrets available in any `RUN $COMMAND` in the `Dockerfile`, mount + the secret file and source it before you run `$COMMAND`: + + ```dockerfile + RUN --mount=type=secret,id=auto-devops-build-secrets . /run/secrets/auto-devops-build-secrets && $COMMAND + ``` When `AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` is set, Auto DevOps enables the experimental [Docker BuildKit](https://docs.docker.com/build/buildkit/) feature to use the `--secret` flag. -## Custom Helm Chart +## Custom Helm chart Auto DevOps uses [Helm](https://helm.sh/) to deploy your application to Kubernetes. -You can override the Helm chart used by bundling up a chart into your project +You can override the Helm chart used by bundling a chart in your project repository or by specifying a project CI/CD variable: - **Bundled chart** - If your project has a `./chart` directory with a `Chart.yaml` file in it, Auto DevOps detects the chart and uses it instead of the - [default chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app), enabling - you to control exactly how your application is deployed. + [default chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app). - **Project variable** - Create a [project CI/CD variable](../../ci/variables/index.md) - `AUTO_DEVOPS_CHART` with the URL of a custom chart to use, or create two project - variables: `AUTO_DEVOPS_CHART_REPOSITORY` with the URL of a custom chart repository, - and `AUTO_DEVOPS_CHART` with the path to the chart. + `AUTO_DEVOPS_CHART` with the URL of a custom chart. You can also create two project + variables: + + - `AUTO_DEVOPS_CHART_REPOSITORY` - The URL of a custom chart repository. + - `AUTO_DEVOPS_CHART` - The path to the chart. -## Customize values for Helm Chart +### Customize Helm chart values > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30628) in GitLab 12.6, `.gitlab/auto-deploy-values.yaml` is used by default for Helm upgrades. -You can override the default values in the `values.yaml` file in the -[default Helm chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app) by either: +To override the default values in the `values.yaml` file in the +[default Helm chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app), either: -- Adding a file named `.gitlab/auto-deploy-values.yaml` to your repository, which is - automatically used, if found. -- Adding a file with a different name or path to the repository, and setting the - `HELM_UPGRADE_VALUES_FILE` [CI/CD variable](cicd_variables.md) with - the path and name. +- Add a file named `.gitlab/auto-deploy-values.yaml` to your repository. This file is automatically used. +- Add a file with a different name or path to the repository. Set the + `HELM_UPGRADE_VALUES_FILE` [CI/CD variable](cicd_variables.md) with the path and name of the file. -Some values cannot be overridden with the options above. Settings like `replicaCount` should instead be overridden with the `REPLICAS` -[build and deployment](cicd_variables.md#build-and-deployment-variables) CI/CD variable. Follow [this issue](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/issues/31) for more information. +Some values cannot be overridden with the options above, but [this issue](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/issues/31) proposes to change this behavior. +To override settings like `replicaCount`, use the `REPLICAS` [build and deployment](cicd_variables.md#build-and-deployment-variables) CI/CD variable. -NOTE: -For GitLab 12.5 and earlier, use the `HELM_UPGRADE_EXTRA_ARGS` variable -to override the default chart values by setting `HELM_UPGRADE_EXTRA_ARGS` to `--values <my-values.yaml>`. +### Customize `helm upgrade` -## Customize the `helm upgrade` command +The `helm upgrade` command is used by the [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image). +To customize this command, pass it options with the `HELM_UPGRADE_EXTRA_ARGS` CI/CD variable. -You can customize the `helm upgrade` command used in the [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) -by passing options to the command with the `HELM_UPGRADE_EXTRA_ARGS` CI/CD variable. -For example, set the value of `HELM_UPGRADE_EXTRA_ARGS` to `--no-hooks` to disable -pre-upgrade and post-upgrade hooks when the command is executed. +For example, to disable pre- and post-upgrade hooks when `helm upgrade` runs: -See [the official documentation](https://helm.sh/docs/helm/helm_upgrade/) for the full -list of options. +```yaml +variables: + HELM_UPGRADE_EXTRA_ARGS: --no-hooks +``` -## Custom Helm chart per environment +For a full list of options, see [the official `helm upgrade` documentation](https://helm.sh/docs/helm/helm_upgrade/). -You can specify the use of a custom Helm chart per environment by scoping the CI/CD variable -to the desired environment. See [Limit environment scope of CI/CD variables](../../ci/variables/index.md#limit-the-environment-scope-of-a-cicd-variable). +### Limit a Helm chart to one environment -## Customizing `.gitlab-ci.yml` +To limit a custom chart to one environment, add the environment scope to your CI/CD variables. +For more information, see [Limit the environment scope of CI/CD variables](../../ci/variables/index.md#limit-the-environment-scope-of-a-cicd-variable). -Auto DevOps is completely customizable because the -[Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml) -is just an implementation of a [`.gitlab-ci.yml`](../../ci/yaml/index.md) file, -and uses only features available to any implementation of `.gitlab-ci.yml`. +## Customize `.gitlab-ci.yml` -To modify the CI/CD pipeline used by Auto DevOps, -[`include` the template](../../ci/yaml/index.md#includetemplate), and customize -it as needed by adding a `.gitlab-ci.yml` file to the root of your repository -containing the following: +Auto DevOps is highly customizable because the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml) +is an implementation of a [`.gitlab-ci.yml`](../../ci/yaml/index.md) file. +The template uses only features available to any implementation of `.gitlab-ci.yml`. -```yaml -include: - - template: Auto-DevOps.gitlab-ci.yml -``` +To add custom behaviors to the CI/CD pipeline used by Auto DevOps: + +1. To the root of your repository, add a `.gitlab-ci.yml` file with the following contents: + + ```yaml + include: + - template: Auto-DevOps.gitlab-ci.yml + ``` + +1. Add your changes to the `.gitlab-ci.yml` file. Your changes are merged with the Auto DevOps template. For more information about +how `include` merges your changes, see [the `include` documentation](../../ci/yaml/index.md#include). -Add your changes, and your additions are merged with the -[Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml) -using the behavior described for [`include`](../../ci/yaml/index.md#include). +To remove behaviors from the Auto DevOps pipeline: -If you need to specifically remove a part of the file, you can also copy and paste the contents of the -[Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml) -into your project and edit it as needed. +1. Copy the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml) +into your project. +1. Edit your copy of the template as needed. ## Use multiple Kubernetes clusters @@ -249,13 +251,14 @@ However, this feature was [deprecated](https://gitlab.com/groups/gitlab-org/conf along with certificate-based integration. You should now use the `KUBE_NAMESPACE` environment variable and -[limit the environments it is available for](../../ci/environments/index.md#scope-environments-with-specs). +[limit its environment scope](../../ci/environments/index.md#scope-environments-with-specs). -## Using components of Auto DevOps +## Use individual components of Auto DevOps -If you only require a subset of the features offered by Auto DevOps, you can include -individual Auto DevOps jobs into your own `.gitlab-ci.yml`. Each component job relies -on a stage that should be defined in the `.gitlab-ci.yml` that includes the template. +If you only require a subset of the features offered by Auto DevOps, +you can include individual Auto DevOps jobs in your own +`.gitlab-ci.yml`. Be sure to also define the stage required by each +job in your `.gitlab-ci.yml` file. For example, to make use of [Auto Build](stages.md#auto-build), you can add the following to your `.gitlab-ci.yml`: @@ -268,18 +271,17 @@ include: - template: Jobs/Build.gitlab-ci.yml ``` -See the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml) for information on available jobs. +For a list of available jobs, see the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml). WARNING: -Auto DevOps templates using the [`only`](../../ci/yaml/index.md#only--except) or +From [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/213336), +Auto DevOps templates that use the [`only`](../../ci/yaml/index.md#only--except) or [`except`](../../ci/yaml/index.md#only--except) syntax have switched -to the [`rules`](../../ci/yaml/index.md#rules) syntax, starting in -[GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/213336). -If your `.gitlab-ci.yml` extends these Auto DevOps templates and override the `only` or +to the [`rules`](../../ci/yaml/index.md#rules) syntax. +If your `.gitlab-ci.yml` extends these Auto DevOps templates and overrides the `only` or `except` keywords, you must migrate your templates to use the -[`rules`](../../ci/yaml/index.md#rules) syntax after the -base template is migrated to use the `rules` syntax. -For users who cannot migrate just yet, you can alternatively pin your templates to +[`rules`](../../ci/yaml/index.md#rules) syntax. +If you cannot migrate just yet, you can alternatively pin your templates to the [GitLab 12.10 based templates](https://gitlab.com/gitlab-org/auto-devops-v12-10). ## Use images hosted in a local Docker registry @@ -287,7 +289,7 @@ the [GitLab 12.10 based templates](https://gitlab.com/gitlab-org/auto-devops-v12 You can configure many Auto DevOps jobs to run in an [offline environment](../../user/application_security/offline_deployments/index.md): 1. Copy the required Auto DevOps Docker images from Docker Hub and `registry.gitlab.com` to their local GitLab container registry. -1. After the images are hosted and available in a local registry, edit `.gitlab-ci.yml` to point to the locally-hosted images. For example: +1. After the images are hosted and available in a local registry, edit `.gitlab-ci.yml` to point to the locally hosted images. For example: ```yaml include: @@ -316,12 +318,6 @@ postgres://user:password@postgres-host:postgres-port/postgres-database ### Upgrading PostgreSQL -WARNING: -The CI/CD variable `AUTO_DEVOPS_POSTGRES_CHANNEL` that controls default provisioned -PostgreSQL was changed to `2` in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/210499). -To keep using the old PostgreSQL, set the `AUTO_DEVOPS_POSTGRES_CHANNEL` variable to -`1`. - The version of the chart used to provision PostgreSQL: - Is 8.2.1 in GitLab 13.0 and later, but can be set back to 0.7.1 if needed. @@ -331,6 +327,11 @@ The version of the chart used to provision PostgreSQL: GitLab encourages users to [migrate their database](upgrading_postgresql.md) to the newer PostgreSQL. +The CI/CD variable `AUTO_DEVOPS_POSTGRES_CHANNEL` that controls default provisioned +PostgreSQL was changed to `2` in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/210499). +To use the old PostgreSQL, set the `AUTO_DEVOPS_POSTGRES_CHANNEL` variable to +`1`. + ### Customize values for PostgreSQL Helm Chart > [Introduced](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/issues/113) in auto-deploy-image v2, in GitLab 13.8. @@ -344,32 +345,29 @@ To set custom values, do one of the following: and name. - Set the `POSTGRES_HELM_UPGRADE_EXTRA_ARGS` [environment variable](cicd_variables.md#database-variables). -### Using external PostgreSQL database providers +### Use external PostgreSQL database providers -While Auto DevOps provides out-of-the-box support for a PostgreSQL container for -production environments, for some use cases, it may not be sufficiently secure or -resilient, and you may want to use an external managed provider (such as +Auto DevOps provides out-of-the-box support for a PostgreSQL container for +production environments. However, you might want to use an external managed provider (such as AWS Relational Database Service) for PostgreSQL. -You must define environment-scoped CI/CD variables for `POSTGRES_ENABLED` and -`DATABASE_URL` in your project's CI/CD settings: +To use an external managed provider: -1. Disable the built-in PostgreSQL installation for the required environments using +1. Disable the built-in PostgreSQL installation for the required environments with environment-scoped [CI/CD variables](../../ci/environments/index.md#scope-environments-with-specs). - For this use case, it's likely that only `production` must be added to this - list. The built-in PostgreSQL setup for Review Apps and staging is sufficient. + Because the built-in PostgreSQL setup for Review Apps and staging is sufficient, you might only need to + disable the installation for `production`. ![Auto Metrics](img/disable_postgres.png) -1. Define the `DATABASE_URL` variable as an environment-scoped variable that is +1. Define the `DATABASE_URL` variable as an environment-scoped variable available to your application. This should be a URL in the following format: ```yaml postgres://user:password@postgres-host:postgres-port/postgres-database ``` -You must ensure that your Kubernetes cluster has network access to wherever -PostgreSQL is hosted. +1. Ensure your Kubernetes cluster has network access to where PostgreSQL is hosted. ## Auto DevOps banner diff --git a/doc/topics/autodevops/stages.md b/doc/topics/autodevops/stages.md index 0ca9c6fa3de..aba0b2d7fae 100644 --- a/doc/topics/autodevops/stages.md +++ b/doc/topics/autodevops/stages.md @@ -451,7 +451,7 @@ To use Auto Deploy on a Kubernetes 1.16+ cluster: 1. If you are deploying your application for the first time in GitLab 13.0 or later, no configuration should be required. -1. In GitLab 12.10 and earlier, set the following in the [`.gitlab/auto-deploy-values.yaml` file](customize.md#customize-values-for-helm-chart): +1. In GitLab 12.10 and earlier, set the following in the [`.gitlab/auto-deploy-values.yaml` file](customize.md#customize-helm-chart-values): ```yaml deploymentApiVersion: apps/v1 @@ -535,7 +535,7 @@ you must: After configuring your worker to respond to health checks, run a Sidekiq worker for your Rails application. You can enable workers by setting the -following in the [`.gitlab/auto-deploy-values.yaml` file](customize.md#customize-values-for-helm-chart): +following in the [`.gitlab/auto-deploy-values.yaml` file](customize.md#customize-helm-chart-values): ```yaml workers: diff --git a/doc/topics/autodevops/troubleshooting.md b/doc/topics/autodevops/troubleshooting.md index ef420323b32..dd715e95512 100644 --- a/doc/topics/autodevops/troubleshooting.md +++ b/doc/topics/autodevops/troubleshooting.md @@ -230,7 +230,7 @@ LAST SEEN TYPE REASON OBJECT ``` To change the port used for the liveness checks, pass -[custom values to the Helm chart](customize.md#customize-values-for-helm-chart) +[custom values to the Helm chart](customize.md#customize-helm-chart-values) used by Auto DevOps: 1. Create a directory and file at the root of your repository named `.gitlab/auto-deploy-values.yaml`. diff --git a/doc/tutorials/move_personal_project_to_a_group.md b/doc/tutorials/move_personal_project_to_a_group.md index 1431dc48d99..e3ab1e8fbd8 100644 --- a/doc/tutorials/move_personal_project_to_a_group.md +++ b/doc/tutorials/move_personal_project_to_a_group.md @@ -53,7 +53,7 @@ If you don't have a group, create one: Before you move your project to a group: - You must have the Owner role for the project. -- Remove any [container images](../user/packages/container_registry/index.md#known-issues) +- Remove any [container images](../user/packages/container_registry/index.md#move-or-rename-container-registry-repositories) - Remove any npm packages. If you transfer a project to a different root namespace, the project must not contain any npm packages. When you update the path of a user or group, or transfer a subgroup or project, you must remove any npm packages first. You cannot update the root namespace of a project with npm packages. Make sure you update your .npmrc files to follow the naming convention and run npm publish if necessary. Now you're ready to move your project: 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 7c1616394a2..7f678344955 100644 --- a/doc/user/admin_area/settings/account_and_limit_settings.md +++ b/doc/user/admin_area/settings/account_and_limit_settings.md @@ -102,7 +102,8 @@ to find tokens more quickly, or for use with automation tools. The default prefix is `glpat-` but administrators can change it. -[Project access tokens](../../project/settings/project_access_tokens.md) also inherit this prefix. +[Project access tokens](../../project/settings/project_access_tokens.md) and +[group access tokens](../../group/settings/group_access_tokens.md) also inherit this prefix. ### Set a prefix diff --git a/doc/user/analytics/img/devops_metrics_comparison_v15_8.png b/doc/user/analytics/img/devops_metrics_comparison_v15_8.png Binary files differnew file mode 100644 index 00000000000..3a52d9e0781 --- /dev/null +++ b/doc/user/analytics/img/devops_metrics_comparison_v15_8.png diff --git a/doc/user/analytics/value_streams_dashboard.md b/doc/user/analytics/value_streams_dashboard.md new file mode 100644 index 00000000000..6efef3830d4 --- /dev/null +++ b/doc/user/analytics/value_streams_dashboard.md @@ -0,0 +1,62 @@ +--- +stage: Plan +group: Optimize +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Value Streams Dashboard **(PREMIUM)** + +> Introduced in GitLab 15.8 as a [Closed Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#closed-beta) feature. + +You can leave feedback on dashboard bugs or functionality in [issue 381787](https://gitlab.com/gitlab-org/gitlab/-/issues/381787). + +This feature is not ready for production use. + +The Value Streams Dashboard is a customizable dashboard to enable decision-makers to identify trends, patterns, and opportunities for digital transformation improvements. +This page is a work in progress, and we're updating the information as we add more features. +For more information, see the [Value Stream Management category direction page](https://about.gitlab.com/direction/plan/value_stream_management/). + +## Initial use case + +Our initial use case is focused on providing the ability to compare software delivery metrics. +This comparison can help decision-makers understand whether projects and groups are improving. + +The beta version of the Value Streams Dashboard includes the following metrics: + +- [DORA metrics](dora_metrics.md) +- [Value Stream Analytics (VSA) - flow metrics](value_stream_analytics.md) + +The Value Streams Dashboard allows you to: + +- Aggregate data records from different APIs. +- Track software performance (DORA) and flow of value (VSA) across the organization. + +## DevSecOps metrics comparison + +The DevOps metrics comparison displays DORA4 and flow metrics for a group or project in the +month-to-date, last month, the month before, and the past 180 days. + +This visualization helps you get a high-level custom view over multiple DevOps metrics and +understand whether they're improving month over month. You can compare the performance between +groups, projects, and teams at a glance. This visualization helps you identify the teams and projects +that are the largest value contributors, overperforming, or underperforming. + +![DevOps metrics comparison](img/devops_metrics_comparison_v15_8.png) + +You can also drill down the metrics for further analysis. +When you hover over a metric, a tooltip displays an explanation of the metric and a link to the related documentation page. + +## Customize the dashboard widgets + +You can customize the Value Streams Dashboard and configure what subgroups and projects to include in the page. + +A view can display maximum four subgroups or projects. + +To display multiple subgroups and projects, specify their path as a URL parameter. + +For example, the parameter `query=gitlab-org/gitlab-foss,gitlab-org/gitlab,gitlab-org/gitlab-design,gitlab-org/gitlab-docs` displays three separate widgets, one each for the: + +- `gitlab-org` group +- `gitlab-ui` project +- `gitlab-org/plan-stage` subgroup +- Select which groups, subgroups, or projects to display on the page. Each of them has its own widget. diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md index e198f967eea..cc8f9d34f5a 100644 --- a/doc/user/application_security/container_scanning/index.md +++ b/doc/user/application_security/container_scanning/index.md @@ -81,7 +81,7 @@ To enable container scanning in your pipeline, you need the following: - Docker `18.09.03` or higher installed on the same computer as the runner. If you're using the shared runners on GitLab.com, then this is already the case. - An image matching the [supported distributions](#supported-distributions). -- [Build and push](../../packages/container_registry/index.md#build-and-push-by-using-gitlab-cicd) +- [Build and push](../../packages/container_registry/build_and_push_images.md#use-gitlab-cicd) the Docker image to your project's container registry. - If you're using a third-party container registry, you might need to provide authentication credentials through the `CS_REGISTRY_USER` and `CS_REGISTRY_PASSWORD` [configuration variables](#available-cicd-variables). diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md index 6629c798cfa..e8b1267fda6 100644 --- a/doc/user/application_security/index.md +++ b/doc/user/application_security/index.md @@ -126,7 +126,7 @@ To enable all GitLab Security scanning tools, with default settings, enable - [Auto License Compliance](../../topics/autodevops/stages.md#auto-license-compliance) - [Auto Container Scanning](../../topics/autodevops/stages.md#auto-container-scanning) -While you cannot directly customize Auto DevOps, you can [include the Auto DevOps template in your project's `.gitlab-ci.yml` file](../../topics/autodevops/customize.md#customizing-gitlab-ciyml). +While you cannot directly customize Auto DevOps, you can [include the Auto DevOps template in your project's `.gitlab-ci.yml` file](../../topics/autodevops/customize.md#customize-gitlab-ciyml). ## Security scanning without Auto DevOps diff --git a/doc/user/application_security/offline_deployments/index.md b/doc/user/application_security/offline_deployments/index.md index 05e56560f95..2082e2291e3 100644 --- a/doc/user/application_security/offline_deployments/index.md +++ b/doc/user/application_security/offline_deployments/index.md @@ -227,7 +227,7 @@ these steps: The AutoDevOps templates leverage the `SECURE_ANALYZERS_PREFIX` variable to identify the location of analyzer images. This variable is discussed above in [Using the secure bundle created](#using-the-secure-bundle-created). Ensure that you set this variable to the correct value for where you loaded the analyzer images. - You could consider doing this with a project CI/CD variable or by [modifying](../../../topics/autodevops/customize.md#customizing-gitlab-ciyml) + You could consider doing this with a project CI/CD variable or by [modifying](../../../topics/autodevops/customize.md#customize-gitlab-ciyml) the `.gitlab-ci.yml` file directly. Once these steps are complete, GitLab has local copies of the Secure analyzers and is set up to use diff --git a/doc/user/packages/container_registry/authenticate_with_container_registry.md b/doc/user/packages/container_registry/authenticate_with_container_registry.md new file mode 100644 index 00000000000..cdc7cbe947b --- /dev/null +++ b/doc/user/packages/container_registry/authenticate_with_container_registry.md @@ -0,0 +1,60 @@ +--- +stage: Package +group: Container Registry +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Authenticate with the Container Registry **(FREE)** + +To authenticate with the Container Registry, you can use a: + +- [Personal access token](../../profile/personal_access_tokens.md). +- [Deploy token](../../project/deploy_tokens/index.md). +- [Project access token](../../project/settings/project_access_tokens.md). +- [Group access token](../../group/settings/group_access_tokens.md). + +All of these authentication methods require the minimum scope: + +- For read (pull) access, to be `read_registry`. +- For write (push) access, to be`write_registry` and `read_registry`. + +To authenticate, run the `docker login` command. For example: + + ```shell + docker login registry.example.com -u <username> -p <token> + ``` + +## Use GitLab CI/CD to authenticate + +To use CI/CD to authenticate with the Container Registry, you can use: + +- The `CI_REGISTRY_USER` CI/CD variable. + + This variable has read-write access to the Container Registry and is valid for + one job only. Its password is also automatically created and assigned to `CI_REGISTRY_PASSWORD`. + + ```shell + docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + ``` + +- A [CI job token](../../../ci/jobs/ci_job_token.md). + + ```shell + docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY + ``` + +- A [deploy token](../../project/deploy_tokens/index.md#gitlab-deploy-token) with the minimum scope of: + - For read (pull) access, `read_registry`. + - For write (push) access, `write_registry`. + + ```shell + docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY + ``` + +- A [personal access token](../../profile/personal_access_tokens.md) with the minimum scope of: + - For read (pull) access, `read_registry`. + - For write (push) access, `write_registry`. + + ```shell + docker login -u <username> -p <access_token> $CI_REGISTRY + ``` diff --git a/doc/user/packages/container_registry/build_and_push_images.md b/doc/user/packages/container_registry/build_and_push_images.md new file mode 100644 index 00000000000..bbb82300488 --- /dev/null +++ b/doc/user/packages/container_registry/build_and_push_images.md @@ -0,0 +1,214 @@ +--- +stage: Package +group: Container Registry +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Build and push container images to the Container Registry **(FREE)** + +Before you can build and push container images, you must [authenticate](authenticate_with_container_registry.md) with the Container Registry. + +## Use Docker commands + +You can use Docker commands to build and push container images to your Container Registry: + +1. [Authenticate](authenticate_with_container_registry.md) with the Container Registry. +1. Run the Docker command to build or push. For example: + + - To build: + + ```shell + docker build -t registry.example.com/group/project/image . + ``` + + - To push: + + ```shell + docker push registry.example.com/group/project/image + ``` + +## Configure your `.gitlab-ci.yml` file + +You can configure your `.gitlab-ci.yml` file to build and push container images to the Container Registry. + +- If multiple jobs require authentication, put the authentication command in the `before_script`. +- Before building, use `docker build --pull` to fetch changes to base images. It takes slightly + longer, but it ensures your image is up-to-date. +- Before each `docker run`, do an explicit `docker pull` to fetch + the image that was just built. This step is especially important if you are + using multiple runners that cache images locally. + + If you use the Git SHA in your image tag, each job is unique and you + should never have a stale image. However, it's still possible to have a + stale image if you rebuild a given commit after a dependency has changed. +- Don't build directly to the `latest` tag because multiple jobs may be + happening simultaneously. + +## Use GitLab CI/CD + +You can use [GitLab CI/CD](../../../ci/yaml/index.md) to build and push container images to the +Container Registry. You can use CI/CD to test, build, and deploy your project from the container +image you created. + +### Use a Docker-in-Docker container image from your Container Registry + +You can use your own container images for Docker-in-Docker. + +1. Set up [Docker-in-Docker](../../../ci/docker/using_docker_build.md#use-docker-in-docker). +1. Update the `image` and `service` to point to your registry. +1. Add a service [alias](../../../ci/services/index.md#available-settings-for-services). + +Your `.gitlab-ci.yml` should look similar to this: + +```yaml +build: + image: $CI_REGISTRY/group/project/docker:20.10.16 + services: + - name: $CI_REGISTRY/group/project/docker:20.10.16-dind + alias: docker + stage: build + script: + - docker build -t my-docker-image . + - docker run my-docker-image /script/to/run/tests +``` + +If you forget to set the service alias, the container image can't find the `dind` service, +and an error like the following is shown: + +```plaintext +error during connect: Get http://docker:2376/v1.39/info: dial tcp: lookup docker on 192.168.0.1:53: no such host +``` + +### Use a Docker-in-Docker container image with Dependency Proxy + +You can use your own container images with Dependency Proxy. + +1. Set up [Docker-in-Docker](../../../ci/docker/using_docker_build.md#use-docker-in-docker). +1. Update the `image` and `service` to point to your registry. +1. Add a service [alias](../../../ci/services/index.md#available-settings-for-services). + +Your `.gitlab-ci.yml` should look similar to this: + +```yaml +build: + image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:20.10.16 + services: + - name: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:18.09.7-dind + alias: docker + stage: build + script: + - docker build -t my-docker-image . + - docker run my-docker-image /script/to/run/tests +``` + +If you forget to set the service alias, the container image can't find the `dind` service, +and an error like the following is shown: + +```plaintext +error during connect: Get http://docker:2376/v1.39/info: dial tcp: lookup docker on 192.168.0.1:53: no such host +``` + +## Container Registry examples with GitLab CI/CD + +If you're using Docker-in-Docker on your runners, your `.gitlab-ci.yml` file should look similar to this: + +```yaml +build: + image: docker:20.10.16 + stage: build + services: + - docker:20.10.16-dind + script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker build -t $CI_REGISTRY/group/project/image:latest . + - docker push $CI_REGISTRY/group/project/image:latest +``` + +You can use [CI/CD variables](../../../ci/variables/index.md) in your `.gitlab-ci.yml` file. For example: + +```yaml +build: + image: docker:20.10.16 + stage: build + services: + - docker:20.10.16-dind + variables: + IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG + script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker build -t $IMAGE_TAG . + - docker push $IMAGE_TAG +``` + +In this example, `$CI_REGISTRY_IMAGE` resolves to the address of the registry tied +to this project. `$CI_COMMIT_REF_NAME` resolves to the branch or tag name, which +can contain forward slashes. Image tags can't contain forward slashes. Use +`$CI_COMMIT_REF_SLUG` as the image tag. You can declare the variable, `$IMAGE_TAG`, +combining `$CI_REGISTRY_IMAGE` and `$CI_REGISTRY_IMAGE` to save some typing in the +`script` section. + +This example splits the tasks into 4 pipeline stages, including two tests that run in parallel. The `build` is stored in the container +registry and used by subsequent stages, downloading the container image when needed. Changes to `main` also get tagged as +`latest` and deployed using an application-specific deploy script: + +```yaml +image: docker:20.10.16 +services: + - docker:20.10.16-dind + +stages: + - build + - test + - release + - deploy + +variables: + # Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled + DOCKER_HOST: tcp://docker:2376 + DOCKER_TLS_CERTDIR: "/certs" + CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG + CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest + +before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + +build: + stage: build + script: + - docker build --pull -t $CONTAINER_TEST_IMAGE . + - docker push $CONTAINER_TEST_IMAGE + +test1: + stage: test + script: + - docker pull $CONTAINER_TEST_IMAGE + - docker run $CONTAINER_TEST_IMAGE /script/to/run/tests + +test2: + stage: test + script: + - docker pull $CONTAINER_TEST_IMAGE + - docker run $CONTAINER_TEST_IMAGE /script/to/run/another/test + +release-image: + stage: release + script: + - docker pull $CONTAINER_TEST_IMAGE + - docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE + - docker push $CONTAINER_RELEASE_IMAGE + only: + - main + +deploy: + stage: deploy + script: + - ./deploy.sh + only: + - main + environment: production +``` + +NOTE: +This example explicitly calls `docker pull`. If you prefer to implicitly pull the container image using `image:`, +and use either the [Docker](https://docs.gitlab.com/runner/executors/docker.html) or [Kubernetes](https://docs.gitlab.com/runner/executors/kubernetes.html) executor, +make sure that [`pull_policy`](https://docs.gitlab.com/runner/executors/docker.html#how-pull-policies-work) is set to `always`. diff --git a/doc/user/packages/container_registry/delete_container_registry_images.md b/doc/user/packages/container_registry/delete_container_registry_images.md new file mode 100644 index 00000000000..9adb9313f09 --- /dev/null +++ b/doc/user/packages/container_registry/delete_container_registry_images.md @@ -0,0 +1,117 @@ +--- +stage: Package +group: Container Registry +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Delete container images from the Container Registry **(FREE)** + +You can delete container images from your Container Registry. + +WARNING: +Deleting container images is a destructive action and can't be undone. To restore +a deleted container image, you must rebuild and re-upload it. + +Deleting a container image on self-managed instances doesn't free up storage space, it only marks the image +as eligible for deletion. To actually delete unreferenced container images and recover storage space, administrators +must run [garbage collection](../../../administration/packages/container_registry.md#container-registry-garbage-collection). + +On GitLab.com, the latest version of the Container Registry includes an automatic online garbage +collector. For more information, see [this blog post](https://about.gitlab.com/blog/2021/10/25/gitlab-com-container-registry-update/). +The automatic online garbage collector is an instance-wide feature, rolling out gradually to a subset +of the user base. Some new container image repositories created from GitLab 14.5 onward are served by this +new version of the Container Registry. In this new version of the Container Registry, layers that aren't +referenced by any image manifest, and image manifests that have no tags and aren't referenced by another +manifest (such as multi-architecture images), are automatically scheduled for deletion after 24 hours if +left unreferenced. + +## Use the GitLab UI + +To delete container images using the GitLab UI: + +1. On the top bar, select **Main menu**, and: + - For a project, select **Projects** and find your project. + - For a group, select **Groups** and find your group. +1. On the left sidebar, select **Packages and registries > Container Registry**. +1. From the **Container Registry** page, you can select what you want to delete, + by either: + + - Deleting the entire repository, and all the tags it contains, by selecting + the red **{remove}** **Trash** icon. + - Navigating to the repository, and deleting tags individually or in bulk + by selecting the red **{remove}** **Trash** icon next to the tag you want + to delete. + +1. In the dialog box, select **Remove tag**. + +## Use the GitLab API + +You can use the API to automate the process of deleting container images. For more +information, see the following endpoints: + +- [Delete a Registry repository](../../../api/container_registry.md#delete-registry-repository) +- [Delete an individual Registry repository tag](../../../api/container_registry.md#delete-a-registry-repository-tag) +- [Delete Registry repository tags in bulk](../../../api/container_registry.md#delete-registry-repository-tags-in-bulk) + +## Use GitLab CI/CD + +NOTE: +GitLab CI/CD doesn't provide a built-in way to remove your container images. This example uses a +third-party tool called [reg](https://github.com/genuinetools/reg) that talks to the GitLab Registry API. +For assistance with this third-party tool, see [the issue queue for reg](https://github.com/genuinetools/reg/issues). + +The following example defines two stages: `build`, and `clean`. The `build_image` job builds a container +image for the branch, and the `delete_image` job deletes it. The `reg` executable is downloaded and used to +remove the container image matching the `$CI_PROJECT_PATH:$CI_COMMIT_REF_SLUG` +[predefined CI/CD variable](../../../ci/variables/predefined_variables.md). + +To use this example, change the `IMAGE_TAG` variable to match your needs. + +```yaml +stages: + - build + - clean + +build_image: + image: docker:20.10.16 + stage: build + services: + - docker:20.10.16-dind + variables: + IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG + script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker build -t $IMAGE_TAG . + - docker push $IMAGE_TAG + only: + - branches + except: + - main + +delete_image: + before_script: + - curl --fail --show-error --location "https://github.com/genuinetools/reg/releases/download/v$REG_VERSION/reg-linux-amd64" --output ./reg + - echo "$REG_SHA256 ./reg" | sha256sum -c - + - chmod a+x ./reg + image: curlimages/curl:7.86.0 + script: + - ./reg rm -d --auth-url $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $IMAGE_TAG + stage: clean + variables: + IMAGE_TAG: $CI_PROJECT_PATH:$CI_COMMIT_REF_SLUG + REG_SHA256: ade837fc5224acd8c34732bf54a94f579b47851cc6a7fd5899a98386b782e228 + REG_VERSION: 0.16.1 + only: + - branches + except: + - main +``` + +NOTE: +You can download the latest `reg` release from [the releases page](https://github.com/genuinetools/reg/releases), then update +the code example by changing the `REG_SHA256` and `REG_VERSION` variables defined in the `delete_image` job. + +## Use a cleanup policy + +You can create a per-project [cleanup policy](reduce_container_registry_storage.md#cleanup-policy) to ensure older tags and +images are regularly removed from the Container Registry. diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md index 4b4d6190dc2..c3790c252cc 100644 --- a/doc/user/packages/container_registry/index.md +++ b/doc/user/packages/container_registry/index.md @@ -8,84 +8,86 @@ info: To determine the technical writer assigned to the Stage/Group associated w > Searching by image repository name was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31322) in GitLab 13.0. -NOTE: -If you pull container images from Docker Hub, you can use the [GitLab Dependency Proxy](../dependency_proxy/index.md#use-the-dependency-proxy-for-docker-images) -to avoid rate limits and speed up your pipelines. - -With the Docker Container Registry integrated into GitLab, every GitLab project can -have its own space to store its Docker images. +You can use the integrated Container Registry to store container images for each GitLab project -You can read more about Docker Registry at <https://docs.docker.com/registry/introduction/>. +To enable the Container Registry for your GitLab instance, see the [administrator documentation](../../../administration/packages/container_registry.md). -This document is the user guide. To learn how to enable the Container -Registry for your GitLab instance, visit the -[administrator documentation](../../../administration/packages/container_registry.md). +NOTE: +If you pull Docker container images from Docker Hub, you can use the +[GitLab Dependency Proxy](../dependency_proxy/index.md#use-the-dependency-proxy-for-docker-images) to avoid +rate limits and speed up your pipelines. For more information about the Docker Registry, see <https://docs.docker.com/registry/introduction/>. ## View the Container Registry You can view the Container Registry for a project or group. -1. Go to your project or group. -1. Go to **Packages and registries > Container Registry**. +1. On the top bar, select **Main menu**, and: + - For a project, select **Projects** and find your project. + - For a group, select **Groups** and find your group. +1. On the left sidebar, select **Packages and registries > Container Registry**. -You can search, sort, filter, and [delete](#delete-images-using-the-gitlab-ui) -containers on this page. You can share a filtered view by copying the URL from your browser. +You can search, sort, filter, and [delete](delete_container_registry_images.md#use-the-gitlab-ui) + your container images. You can share a filtered view by copying the URL from your browser. -Only members of the project or group can access a private project's Container Registry. -Images downloaded from a private registry may be [available to other users in a shared runner](https://docs.gitlab.com/runner/security/index.html#usage-of-private-docker-images-with-if-not-present-pull-policy). +Only members of the project or group can access the Container Registry for a private project. +Container images downloaded from a private registry may be [available to other users in a shared runner](https://docs.gitlab.com/runner/security/index.html#usage-of-private-docker-images-with-if-not-present-pull-policy). -If a project is public, so is the Container Registry. +If a project is public, the Container Registry is also public. -### View the tags of a specific image +### View the tags of a specific container image in the Container Registry You can use the Container Registry **Tag Details** page to view a list of tags associated with a given container image: -1. Go to your project or group. -1. Go to **Packages and registries > Container Registry**. -1. Select the container image you are interested in. +1. On the top bar, select **Main menu**, and: + - For a project, select **Projects** and find your project. + - For a group, select **Groups** and find your group. +1. On the left sidebar, select **Packages and registries > Container Registry**. +1. Select your container image. You can view details about each tag, such as when it was published, how much storage it consumes, and the manifest and configuration digests. -You can search, sort (by tag name), filter, and [delete](#delete-images-using-the-gitlab-ui) +You can search, sort (by tag name), filter, and [delete](delete_container_registry_images.md#use-the-gitlab-ui) tags on this page. You can share a filtered view by copying the URL from your browser. -## Use images from the Container Registry +## Use container images from the Container Registry -To download and run a container image hosted in the GitLab Container Registry: +To download and run a container image hosted in the Container Registry: -1. Copy the link to your container image: - - Go to your project or group's **Packages and registries > Container Registry** - and find the image you want. - - Next to the image name, select **Copy**. +1. On the top bar, select **Main menu**, and: + - For a project, select **Projects** and find your project. + - For a group, select **Groups** and find your group. +1. On the left sidebar, select **Packages and registries > Container Registry**. +1. Find the container image you want to work with and select **Copy**. ![Container Registry image URL](img/container_registry_hover_path_13_4.png) -1. Use `docker run` with the image link: +1. Use `docker run` with the copied link: ```shell docker run [options] registry.example.com/group/project/image [arguments] ``` -[Authentication](#authenticate-with-the-container-registry) is needed to download images from a private repository. +NOTE: +You must [authenticate with the container registry](authenticate_with_container_registry.md) to download +container images from a private repository. -For more information on running Docker containers, visit the -[Docker documentation](https://docs.docker.com/get-started/). +For more information on running container images, visit the [Docker documentation](https://docs.docker.com/get-started/). -## Image naming convention +## Naming convention for your container images -Images follow this naming convention: +Your container images must follow this naming convention: ```plaintext <registry URL>/<namespace>/<project>/<image> ``` -If your project is `gitlab.example.com/mynamespace/myproject`, for example, -then your image must be named `gitlab.example.com/mynamespace/myproject` at a minimum. +For example, if your project is `gitlab.example.com/mynamespace/myproject`, +then your container image must be named `gitlab.example.com/mynamespace/myproject`. -You can append additional names to the end of an image name, up to two levels deep. +You can append additional names to the end of a container image name, up to two levels deep. -For example, these are all valid image names for images in the project named `myproject`: +For example, these are all valid names for container images in the project named `myproject`: ```plaintext registry.example.com/mynamespace/myproject:some-tag @@ -99,399 +101,12 @@ registry.example.com/mynamespace/myproject/image:latest registry.example.com/mynamespace/myproject/my/image:rc1 ``` -## Authenticate with the Container Registry - -To authenticate with the Container Registry, you can use a: - -- [Personal access token](../../profile/personal_access_tokens.md). -- [Deploy token](../../project/deploy_tokens/index.md). -- [Project access token](../../project/settings/project_access_tokens.md). -- [Group access token](../../group/settings/group_access_tokens.md). - -All of these require the minimum scope to be: - -- For read (pull) access, `read_registry`. -- For write (push) access, `write_registry` & `read_registry`. - -To authenticate, run the `docker` command. For example: - - ```shell - docker login registry.example.com -u <username> -p <token> - ``` - -## Build and push images by using Docker commands - -Before you can build and push images, you must [authenticate](#authenticate-with-the-container-registry) with the Container Registry. - -To build and push to the Container Registry: - -1. Authenticate with the Container Registry. - -1. Run the command to build or push. For example, to build: - - ```shell - docker build -t registry.example.com/group/project/image . - ``` - - Or to push: - - ```shell - docker push registry.example.com/group/project/image - ``` - -To view these commands, go to your project's **Packages and registries > Container Registry**. - -## Build and push by using GitLab CI/CD - -Use [GitLab CI/CD](../../../ci/yaml/index.md) to build and push images to the -Container Registry. Use it to test, build, and deploy your project from the Docker -image you created. - -### Authenticate by using GitLab CI/CD - -Before you can build and push images by using GitLab CI/CD, you must authenticate with the Container Registry. - -To use CI/CD to authenticate, you can use: - -- The `CI_REGISTRY_USER` CI/CD variable. - - This variable has read-write access to the Container Registry and is valid for - one job only. Its password is also automatically created and assigned to `CI_REGISTRY_PASSWORD`. - - ```shell - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - ``` - -- A [CI job token](../../../ci/jobs/ci_job_token.md). - - ```shell - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY - ``` - -- A [deploy token](../../project/deploy_tokens/index.md#gitlab-deploy-token) with the minimum scope of: - - For read (pull) access, `read_registry`. - - For write (push) access, `write_registry`. - - ```shell - docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY - ``` - -- A [personal access token](../../profile/personal_access_tokens.md) with the minimum scope of: - - For read (pull) access, `read_registry`. - - For write (push) access, `write_registry`. - - ```shell - docker login -u <username> -p <access_token> $CI_REGISTRY - ``` - -### Configure your `.gitlab-ci.yml` file - -You can configure your `.gitlab-ci.yml` file to build and push images to the Container Registry. - -- If multiple jobs require authentication, put the authentication command in the `before_script`. -- Before building, use `docker build --pull` to fetch changes to base images. It takes slightly - longer, but it ensures your image is up-to-date. -- Before each `docker run`, do an explicit `docker pull` to fetch - the image that was just built. This step is especially important if you are - using multiple runners that cache images locally. - - If you use the Git SHA in your image tag, each job is unique and you - should never have a stale image. However, it's still possible to have a - stale image if you rebuild a given commit after a dependency has changed. -- Don't build directly to the `latest` tag because multiple jobs may be - happening simultaneously. - -### Container Registry examples with GitLab CI/CD - -If you're using Docker-in-Docker on your runners, this is how your `.gitlab-ci.yml` -should look: - -```yaml -build: - image: docker:20.10.16 - stage: build - services: - - docker:20.10.16-dind - script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - - docker build -t $CI_REGISTRY/group/project/image:latest . - - docker push $CI_REGISTRY/group/project/image:latest -``` - -You can also make use of [other CI/CD variables](../../../ci/variables/index.md) to avoid hard-coding: - -```yaml -build: - image: docker:20.10.16 - stage: build - services: - - docker:20.10.16-dind - variables: - IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG - script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - - docker build -t $IMAGE_TAG . - - docker push $IMAGE_TAG -``` - -In this example, `$CI_REGISTRY_IMAGE` resolves to the address of the registry tied -to this project. `$CI_COMMIT_REF_NAME` resolves to the branch or tag name, which -can contain forward slashes. Image tags can't contain forward slashes. Use -`$CI_COMMIT_REF_SLUG` as the image tag. You can declare the variable, `$IMAGE_TAG`, -combining `$CI_REGISTRY_IMAGE` and `$CI_REGISTRY_IMAGE` to save some typing in the -`script` section. - -Here's a more elaborate example that splits up the tasks into 4 pipeline stages, -including two tests that run in parallel. The `build` is stored in the container -registry and used by subsequent stages, downloading the image -when needed. Changes to `main` also get tagged as `latest` and deployed using -an application-specific deploy script: - -```yaml -image: docker:20.10.16 -services: - - docker:20.10.16-dind - -stages: - - build - - test - - release - - deploy - -variables: - # Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled - DOCKER_HOST: tcp://docker:2376 - DOCKER_TLS_CERTDIR: "/certs" - CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG - CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest - -before_script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - -build: - stage: build - script: - - docker build --pull -t $CONTAINER_TEST_IMAGE . - - docker push $CONTAINER_TEST_IMAGE - -test1: - stage: test - script: - - docker pull $CONTAINER_TEST_IMAGE - - docker run $CONTAINER_TEST_IMAGE /script/to/run/tests - -test2: - stage: test - script: - - docker pull $CONTAINER_TEST_IMAGE - - docker run $CONTAINER_TEST_IMAGE /script/to/run/another/test - -release-image: - stage: release - script: - - docker pull $CONTAINER_TEST_IMAGE - - docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE - - docker push $CONTAINER_RELEASE_IMAGE - only: - - main - -deploy: - stage: deploy - script: - - ./deploy.sh - only: - - main - environment: production -``` - -NOTE: -This example explicitly calls `docker pull`. If you prefer to implicitly pull the -built image using `image:`, and use either the [Docker](https://docs.gitlab.com/runner/executors/docker.html) -or [Kubernetes](https://docs.gitlab.com/runner/executors/kubernetes.html) executor, -make sure that [`pull_policy`](https://docs.gitlab.com/runner/executors/docker.html#how-pull-policies-work) -is set to `always`. - -### Using a Docker-in-Docker image from your Container Registry - -To use your own Docker images for Docker-in-Docker, follow these steps -in addition to the steps in the -[Docker-in-Docker](../../../ci/docker/using_docker_build.md#use-docker-in-docker) section: - -1. Update the `image` and `service` to point to your registry. -1. Add a service [alias](../../../ci/services/index.md#available-settings-for-services). - -Below is an example of what your `.gitlab-ci.yml` should look like: - -```yaml -build: - image: $CI_REGISTRY/group/project/docker:20.10.16 - services: - - name: $CI_REGISTRY/group/project/docker:20.10.16-dind - alias: docker - stage: build - script: - - docker build -t my-docker-image . - - docker run my-docker-image /script/to/run/tests -``` - -If you forget to set the service alias, the `docker:20.10.16` image is unable to find the -`dind` service, and an error like the following is thrown: - -```plaintext -error during connect: Get http://docker:2376/v1.39/info: dial tcp: lookup docker on 192.168.0.1:53: no such host -``` - -### Using a Docker-in-Docker image with Dependency Proxy - -To use your own Docker images with Dependency Proxy, follow these steps -in addition to the steps in the -[Docker-in-Docker](../../../ci/docker/using_docker_build.md#use-docker-in-docker) section: - -1. Update the `image` and `service` to point to your registry. -1. Add a service [alias](../../../ci/services/index.md#available-settings-for-services). - -Below is an example of what your `.gitlab-ci.yml` should look like: - -```yaml -build: - image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:20.10.16 - services: - - name: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:18.09.7-dind - alias: docker - stage: build - script: - - docker build -t my-docker-image . - - docker run my-docker-image /script/to/run/tests -``` - -If you forget to set the service alias, the `docker:20.10.16` image is unable to find the -`dind` service, and an error like the following is thrown: - -```plaintext -error during connect: Get http://docker:2376/v1.39/info: dial tcp: lookup docker on 192.168.0.1:53: no such host -``` - -## Delete images - -You can delete images from your Container Registry in multiple ways. - -WARNING: -Deleting images is a destructive action and can't be undone. To restore -a deleted image, you must rebuild and re-upload it. - -On self-managed instances, deleting an image doesn't free up storage space - it only marks the image -as eligible for deletion. To actually delete images and recover storage space, in case they're -unreferenced, administrators must run [garbage collection](../../../administration/packages/container_registry.md#container-registry-garbage-collection). - -On GitLab.com, the latest version of the Container Registry includes an automatic online garbage -collector. For more information, see [this blog post](https://about.gitlab.com/blog/2021/10/25/gitlab-com-container-registry-update/). -The automatic online garbage collector is an instance-wide feature, rolling out gradually to a subset -of the user base. Some new image repositories created from GitLab 14.5 onward are served by this -new version of the Container Registry. In this new version of the Container Registry, layers that aren't -referenced by any image manifest, and image manifests that have no tags and aren't referenced by another -manifest (such as multi-architecture images), are automatically scheduled for deletion after 24 hours if -left unreferenced. - -### Delete images using the GitLab UI - -To delete images using the GitLab UI: - -1. Go to your project's or group's **Packages and registries > Container Registry**. -1. From the **Container Registry** page, you can select what you want to delete, - by either: - - - Deleting the entire repository, and all the tags it contains, by selecting - the red **{remove}** **Trash** icon. - - Navigating to the repository, and deleting tags individually or in bulk - by selecting the red **{remove}** **Trash** icon next to the tag you want - to delete. - -1. In the dialog box, select **Remove tag**. - -### Delete images using the API - -If you want to automate the process of deleting images, GitLab provides an API. For more -information, see the following endpoints: - -- [Delete a Registry repository](../../../api/container_registry.md#delete-registry-repository) -- [Delete an individual Registry repository tag](../../../api/container_registry.md#delete-a-registry-repository-tag) -- [Delete Registry repository tags in bulk](../../../api/container_registry.md#delete-registry-repository-tags-in-bulk) - -### Delete images using GitLab CI/CD - -WARNING: -GitLab CI/CD doesn't provide a built-in way to remove your images. This example -uses a third-party tool called [reg](https://github.com/genuinetools/reg) -that talks to the GitLab Registry API. You are responsible for your own actions. -For assistance with this tool, see -[the issue queue for reg](https://github.com/genuinetools/reg/issues). - -The following example defines two stages: `build`, and `clean`. The -`build_image` job builds the Docker image for the branch, and the -`delete_image` job deletes it. The `reg` executable is downloaded and used to -remove the image matching the `$CI_PROJECT_PATH:$CI_COMMIT_REF_SLUG` -[predefined CI/CD variable](../../../ci/variables/predefined_variables.md). - -To use this example, change the `IMAGE_TAG` variable to match your needs: - -```yaml -stages: - - build - - clean - -build_image: - image: docker:20.10.16 - stage: build - services: - - docker:20.10.16-dind - variables: - IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG - script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - - docker build -t $IMAGE_TAG . - - docker push $IMAGE_TAG - only: - - branches - except: - - main - -delete_image: - before_script: - - curl --fail --show-error --location "https://github.com/genuinetools/reg/releases/download/v$REG_VERSION/reg-linux-amd64" --output ./reg - - echo "$REG_SHA256 ./reg" | sha256sum -c - - - chmod a+x ./reg - image: curlimages/curl:7.86.0 - script: - - ./reg rm -d --auth-url $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $IMAGE_TAG - stage: clean - variables: - IMAGE_TAG: $CI_PROJECT_PATH:$CI_COMMIT_REF_SLUG - REG_SHA256: ade837fc5224acd8c34732bf54a94f579b47851cc6a7fd5899a98386b782e228 - REG_VERSION: 0.16.1 - only: - - branches - except: - - main -``` - -NOTE: -You can download the latest `reg` release from -[the releases page](https://github.com/genuinetools/reg/releases), then update -the code example by changing the `REG_SHA256` and `REG_VERSION` variables -defined in the `delete_image` job. - -### Delete images by using a cleanup policy - -You can create a per-project [cleanup policy](reduce_container_registry_storage.md#cleanup-policy) to ensure older tags and images are regularly removed from the -Container Registry. - -## Known issues +## Move or rename Container Registry repositories -Moving or renaming existing Container Registry repositories is not supported -after you have pushed images. The images are stored in a path that matches -the repository path. To move or rename a repository with a -Container Registry, you must delete all existing images. -Community suggestions to work around this known issue have been shared in +Moving or renaming existing Container Registry repositories is not supported after you have pushed +container images. The container images are stored in a path that matches the repository path. To move +or rename a repository with a Container Registry, you must delete all existing container images. +Community suggestions to work around this known issue are shared in [issue 18383](https://gitlab.com/gitlab-org/gitlab/-/issues/18383#possible-workaround). ## Disable the Container Registry for a project @@ -500,7 +115,8 @@ The Container Registry is enabled by default. You can, however, remove the Container Registry for a project: -1. Go to your project's **Settings > General** page. +1. On the top bar, select **Main menu > Projects**. +1. On the left sidebar, select **Settings > General**. 1. Expand the **Visibility, project features, permissions** section and disable **Container Registry**. 1. Select **Save changes**. @@ -514,10 +130,11 @@ The **Packages and registries > Container Registry** entry is removed from the p By default, the Container Registry is visible to everyone with access to the project. You can, however, change the visibility of the Container Registry for a project. -See the [Container Registry visibility permissions](#container-registry-visibility-permissions) -for more details about the permissions that this setting grants to users. +For more information about the permissions that this setting grants to users, +see [Container Registry visibility permissions](#container-registry-visibility-permissions). -1. Go to your project's **Settings > General** page. +1. On the top bar, select **Main menu > Projects**. +1. On the left sidebar, select **Settings > General**. 1. Expand the section **Visibility, project features, permissions**. 1. Under **Container Registry**, select an option from the dropdown list: @@ -533,19 +150,18 @@ for more details about the permissions that this setting grants to users. ## Container Registry visibility permissions -The ability to view the Container Registry and pull images is controlled by the Container Registry's -visibility permissions. You can change this through the [visibility setting on the UI](#change-visibility-of-the-container-registry) +The ability to view the Container Registry and pull container images is controlled by the Container Registry's +visibility permissions. You can change the visibility through the [visibility setting on the UI](#change-visibility-of-the-container-registry) or the [API](../../../api/container_registry.md#change-the-visibility-of-the-container-registry). -[Other permissions](../../permissions.md) -such as updating the Container Registry and pushing or deleting images are not affected by +[Other permissions](../../permissions.md) such as updating the Container Registry and pushing or deleting container images are not affected by this setting. However, disabling the Container Registry disables all Container Registry operations. -| | | Anonymous<br/>(Everyone on internet) | Guest | Reporter, Developer, Maintainer, Owner | -| -------------------- | --------------------- | --------- | ----- | ------------------------------------------ | -| Public project with Container Registry visibility <br/> set to **Everyone With Access** (UI) or `enabled` (API) | View Container Registry <br/> and pull images | Yes | Yes | Yes | -| Public project with Container Registry visibility <br/> set to **Only Project Members** (UI) or `private` (API) | View Container Registry <br/> and pull images | No | No | Yes | -| Internal project with Container Registry visibility <br/> set to **Everyone With Access** (UI) or `enabled` (API) | View Container Registry <br/> and pull images | No | Yes | Yes | -| Internal project with Container Registry visibility <br/> set to **Only Project Members** (UI) or `private` (API) | View Container Registry <br/> and pull images | No | No | Yes | -| Private project with Container Registry visibility <br/> set to **Everyone With Access** (UI) or `enabled` (API) | View Container Registry <br/> and pull images | No | No | Yes | -| Private project with Container Registry visibility <br/> set to **Only Project Members** (UI) or `private` (API) | View Container Registry <br/> and pull images | No | No | Yes | -| Any project with Container Registry `disabled` | All operations on Container Registry | No | No | No | +| | | Anonymous<br/>(Everyone on internet) | Guest | Reporter, Developer, Maintainer, Owner | +|-------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|--------------------------------------|-------|----------------------------------------| +| Public project with Container Registry visibility <br/> set to **Everyone With Access** (UI) or `enabled` (API) | View Container Registry <br/> and pull images | Yes | Yes | Yes | +| Public project with Container Registry visibility <br/> set to **Only Project Members** (UI) or `private` (API) | View Container Registry <br/> and pull images | No | No | Yes | +| Internal project with Container Registry visibility <br/> set to **Everyone With Access** (UI) or `enabled` (API) | View Container Registry <br/> and pull images | No | Yes | Yes | +| Internal project with Container Registry visibility <br/> set to **Only Project Members** (UI) or `private` (API) | View Container Registry <br/> and pull images | No | No | Yes | +| Private project with Container Registry visibility <br/> set to **Everyone With Access** (UI) or `enabled` (API) | View Container Registry <br/> and pull images | No | No | Yes | +| Private project with Container Registry visibility <br/> set to **Only Project Members** (UI) or `private` (API) | View Container Registry <br/> and pull images | No | No | Yes | +| Any project with Container Registry `disabled` | All operations on Container Registry | No | No | No | diff --git a/doc/user/packages/container_registry/reduce_container_registry_storage.md b/doc/user/packages/container_registry/reduce_container_registry_storage.md index cbf9af633ac..15948569cb8 100644 --- a/doc/user/packages/container_registry/reduce_container_registry_storage.md +++ b/doc/user/packages/container_registry/reduce_container_registry_storage.md @@ -36,7 +36,7 @@ An image layer is only counted once if: - You share the image layer across different repositories. Only layers that are referenced by tagged images are accounted for. Untagged images and any layers -referenced exclusively by them are subject to [online garbage collection](index.md#delete-images). +referenced exclusively by them are subject to [online garbage collection](delete_container_registry_images.md). Untagged images are automatically deleted after 24 hours if they remain unreferenced during that period. Image layers are stored on the storage backend in the original (usually compressed) format. This @@ -269,7 +269,7 @@ only if the number of tags being cleaned up is minimal. Here are some other options you can use to reduce the Container Registry storage used by your project: -- Use the [GitLab UI](index.md#delete-images) +- Use the [GitLab UI](delete_container_registry_images.md#use-the-gitlab-ui) to delete individual image tags or the entire repository containing all the tags. - Use the API to [delete individual image tags](../../../api/container_registry.md#delete-a-registry-repository-tag). - Use the API to [delete the entire container registry repository containing all the tags](../../../api/container_registry.md#delete-registry-repository). diff --git a/doc/user/packages/container_registry/troubleshoot_container_registry.md b/doc/user/packages/container_registry/troubleshoot_container_registry.md index eac7e0fcacd..68fe430e531 100644 --- a/doc/user/packages/container_registry/troubleshoot_container_registry.md +++ b/doc/user/packages/container_registry/troubleshoot_container_registry.md @@ -87,7 +87,7 @@ The following procedure uses these sample project names: docker tag gitlab.example.com/org/build/sample_project/cr:v2.9.1 gitlab.example.com/new_org/build/new_sample_project/cr:v2.9.1 ``` -1. Delete the images in the old project by using the [UI](index.md#delete-images) or [API](../../../api/packages.md#delete-a-project-package). +1. Delete the images in the old project by using the [UI](delete_container_registry_images.md) or [API](../../../api/packages.md#delete-a-project-package). There may be a delay while the images are queued and deleted. 1. Change the path or transfer the project: diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 5d1db760bff..321b351a86a 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -71,7 +71,7 @@ The following table lists project permissions available for each role: | [Application security](application_security/index.md):<br>Create or assign [security policy project](application_security/policies/index.md) | | | | | ✓ | | [Clusters](infrastructure/clusters/index.md):<br>View clusters | | | ✓ | ✓ | ✓ | | [Clusters](infrastructure/clusters/index.md):<br>Manage clusters | | | | ✓ | ✓ | -| [Container Registry](packages/container_registry/index.md):<br>Create, edit, delete [cleanup policies](packages/container_registry/index.md#delete-images-by-using-a-cleanup-policy) | | | | ✓ | ✓ | +| [Container Registry](packages/container_registry/index.md):<br>Create, edit, delete [cleanup policies](packages/container_registry/delete_container_registry_images.md#use-a-cleanup-policy) | | | | ✓ | ✓ | | [Container Registry](packages/container_registry/index.md):<br>Push an image to the Container Registry | | | ✓ | ✓ | ✓ | | [Container Registry](packages/container_registry/index.md):<br>Pull an image from the Container Registry | ✓ (*19*) | ✓ (*19*) | ✓ | ✓ | ✓ | | [Container Registry](packages/container_registry/index.md):<br>Remove a Container Registry image | | | ✓ | ✓ | ✓ | diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md index 39826bf59c4..5f23f50f439 100644 --- a/doc/user/profile/account/two_factor_authentication.md +++ b/doc/user/profile/account/two_factor_authentication.md @@ -444,7 +444,7 @@ This error occurs in the following scenarios: password. For 2FA-enabled users, a [personal access token](../personal_access_tokens.md) (PAT) must be used instead of a password. To authenticate: - Git requests over HTTP(S), a PAT with `read_repository` or `write_repository` scope is required. - - [GitLab Container Registry](../../packages/container_registry/index.md#authenticate-with-the-container-registry) requests, a PAT + - [GitLab Container Registry](../../packages/container_registry/authenticate_with_container_registry.md) requests, a PAT with `read_registry` or `write_registry` scope is required. - [Dependency Proxy](../../packages/dependency_proxy/index.md#authenticate-with-the-dependency-proxy) requests, a PAT with `read_registry` and `write_registry` scopes is required. diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index 507ad6378bc..9490ab151aa 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -31,7 +31,7 @@ Personal access tokens are: - Required when [two-factor authentication (2FA)](account/two_factor_authentication.md) is enabled. - Used with a GitLab username to authenticate with GitLab features that require usernames. For example, [GitLab-managed Terraform state backend](../infrastructure/iac/terraform_state.md#use-your-gitlab-backend-as-a-remote-data-source) - and [Docker container registry](../packages/container_registry/index.md#authenticate-with-the-container-registry), + and [Docker container registry](../packages/container_registry/authenticate_with_container_registry.md), - Similar to [project access tokens](../project/settings/project_access_tokens.md) and [group access tokens](../group/settings/group_access_tokens.md), but are attached to a user rather than a project or group. diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index a88427ab20b..6c29cba281d 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -239,7 +239,7 @@ Prerequisites: - You must have at least the Maintainer role for the [group](../../group/manage.md#create-a-group) to which you are transferring. - You must be the Owner of the project you transfer. - The group must allow creation of new projects. -- The project must not contain any [container images](../../packages/container_registry/index.md#known-issues). +- The project must not contain any [container images](../../packages/container_registry/index.md#move-or-rename-container-registry-repositories). - Remove any npm packages. If you transfer a project to a different root namespace, the project must not contain any npm packages. When you update the path of a user or group, or transfer a subgroup or project, you must remove any npm packages first. You cannot update the root namespace of a project with npm packages. Make sure you update your .npmrc files to follow the naming convention and run npm publish if necessary. To transfer a project: diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 735c7fcf80c..20eff74c4b1 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -52,11 +52,29 @@ module Gitlab private_class_method :interceptors def self.channel_args - # These values match the go Gitaly client - # https://gitlab.com/gitlab-org/gitaly/-/blob/bf9f52bc/client/dial.go#L78 { + # These keepalive values match the go Gitaly client + # https://gitlab.com/gitlab-org/gitaly/-/blob/bf9f52bc/client/dial.go#L78 'grpc.keepalive_time_ms': 20000, - 'grpc.keepalive_permit_without_calls': 1 + 'grpc.keepalive_permit_without_calls': 1, + # Enable client-side automatic retry. After enabled, gRPC requests will be retried when there are connectivity + # problems with the target host. Only transparent failures, which mean requests fail before leaving clients, are + # eligible. Other cases are configurable via retry policy in service config (below). In theory, we can auto-retry + # read-only RPCs. Gitaly defines a custom field in service proto. Unfortunately, gRPC ruby doesn't support + # descriptor reflection. + # For more information please visit https://github.com/grpc/proposal/blob/master/A6-client-retries.md + 'grpc.enable_retries': 1, + # Service config is a mechanism for grpc to control the behavior of gRPC client. It defines the client-side + # balancing strategy and retry policy. The config receives a raw JSON string. The format is defined here: + # https://github.com/grpc/grpc-proto/blob/master/grpc/service_config/service_config.proto + 'grpc.service_config': { + # By default, gRPC uses pick_first strategy. This strategy establishes one single connection to the first + # target returned by the name resolver. We would like to use round_robin load-balancing strategy so that + # grpc creates multiple subchannels to all targets retrurned by the resolver. Requests are distributed to + # those subchannels in a round-robin fashion. + # More about client-side load-balancing: https://gitlab.com/groups/gitlab-org/-/epics/8971#note_1207008162 + "loadBalancingConfig": [{ "round_robin": {} }] + }.to_json } end private_class_method :channel_args diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d05f0c9807d..47b744ef423 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -25905,6 +25905,15 @@ msgstr "" msgid "Members|2FA" msgstr "" +msgid "Members|A group must have at least one owner. To leave this group, assign a new owner." +msgstr "" + +msgid "Members|A group must have at least one owner. To remove the member, assign a new owner." +msgstr "" + +msgid "Members|A personal project's owner cannot be removed." +msgstr "" + msgid "Members|Access granted" msgstr "" @@ -25944,6 +25953,9 @@ msgstr "" msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\"" msgstr "" +msgid "Members|Cannot leave \"%{source}\"" +msgstr "" + msgid "Members|Direct" msgstr "" @@ -26010,6 +26022,9 @@ msgstr "" msgid "Members|User created" msgstr "" +msgid "Members|You cannot remove yourself from a personal project." +msgstr "" + msgid "Member|Deny access" msgstr "" @@ -36161,7 +36176,7 @@ msgstr "" msgid "Runners|Executor" msgstr "" -msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings > CI/CD." +msgid "Runners|Existing runners are not affected. To permit runner registration for all groups, enable this setting in the Admin Area in Settings > CI/CD." msgstr "" msgid "Runners|Filter projects" diff --git a/spec/frontend/members/components/action_dropdowns/leave_group_dropdown_item_spec.js b/spec/frontend/members/components/action_dropdowns/leave_group_dropdown_item_spec.js index 4b47b156cba..90f5b217007 100644 --- a/spec/frontend/members/components/action_dropdowns/leave_group_dropdown_item_spec.js +++ b/spec/frontend/members/components/action_dropdowns/leave_group_dropdown_item_spec.js @@ -4,7 +4,7 @@ import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import LeaveGroupDropdownItem from '~/members/components/action_dropdowns/leave_group_dropdown_item.vue'; import LeaveModal from '~/members/components/modals/leave_modal.vue'; import { LEAVE_MODAL_ID } from '~/members/constants'; -import { member } from '../../mock_data'; +import { member, permissions } from '../../mock_data'; describe('LeaveGroupDropdownItem', () => { let wrapper; @@ -14,6 +14,7 @@ describe('LeaveGroupDropdownItem', () => { wrapper = shallowMount(LeaveGroupDropdownItem, { propsData: { member, + permissions, ...propsData, }, directives: { @@ -42,7 +43,7 @@ describe('LeaveGroupDropdownItem', () => { it('contains LeaveModal component', () => { const leaveModal = wrapper.findComponent(LeaveModal); - expect(leaveModal.props('member')).toEqual(member); + expect(leaveModal.props()).toEqual({ member, permissions }); }); it('binds to the LeaveModal component', () => { diff --git a/spec/frontend/members/components/action_dropdowns/remove_member_dropdown_item_spec.js b/spec/frontend/members/components/action_dropdowns/remove_member_dropdown_item_spec.js index 0ae9e257f2b..e1c498249d7 100644 --- a/spec/frontend/members/components/action_dropdowns/remove_member_dropdown_item_spec.js +++ b/spec/frontend/members/components/action_dropdowns/remove_member_dropdown_item_spec.js @@ -69,6 +69,9 @@ describe('RemoveMemberDropdownItem', () => { it('calls Vuex action to show `remove member` modal when clicked', () => { findDropdownItem().vm.$emit('click'); - expect(actions.showRemoveMemberModal).toHaveBeenCalledWith(expect.any(Object), modalData); + expect(actions.showRemoveMemberModal).toHaveBeenCalledWith(expect.any(Object), { + ...modalData, + preventRemoval: false, + }); }); }); diff --git a/spec/frontend/members/components/action_dropdowns/user_action_dropdown_spec.js b/spec/frontend/members/components/action_dropdowns/user_action_dropdown_spec.js index ae45e737581..5a2de1cac80 100644 --- a/spec/frontend/members/components/action_dropdowns/user_action_dropdown_spec.js +++ b/spec/frontend/members/components/action_dropdowns/user_action_dropdown_spec.js @@ -74,6 +74,7 @@ describe('UserActionDropdown', () => { name: member.user.name, obstacles: parseUserDeletionObstacles(member.user), }, + preventRemoval: false, }); }); @@ -120,6 +121,63 @@ describe('UserActionDropdown', () => { }); }); + describe('when user can remove but it is blocked by last owner', () => { + const permissions = { + canRemove: false, + canRemoveBlockedByLastOwner: true, + }; + + it('renders remove member dropdown', () => { + createComponent({ + permissions, + }); + + expect(findRemoveMemberDropdownItem().exists()).toBe(true); + }); + + describe('when member model type is `GroupMember`', () => { + it('passes correct message to the modal', () => { + createComponent({ + permissions, + }); + + expect(findRemoveMemberDropdownItem().props('modalMessage')).toBe( + I18N.lastGroupOwnerCannotBeRemoved, + ); + }); + }); + + describe('when member model type is `ProjectMember`', () => { + it('passes correct message to the modal', () => { + createComponent({ + member: { + ...member, + type: MEMBER_MODEL_TYPE_PROJECT_MEMBER, + }, + permissions, + }); + + expect(findRemoveMemberDropdownItem().props('modalMessage')).toBe( + I18N.personalProjectOwnerCannotBeRemoved, + ); + }); + }); + + describe('when member is the current user', () => { + it('renders leave dropdown with correct props', () => { + createComponent({ + isCurrentUser: true, + permissions, + }); + + expect(wrapper.findComponent(LeaveGroupDropdownItem).props()).toEqual({ + member, + permissions, + }); + }); + }); + }); + describe('when group member', () => { beforeEach(() => { createComponent({ diff --git a/spec/frontend/members/components/modals/leave_modal_spec.js b/spec/frontend/members/components/modals/leave_modal_spec.js index cdbabb2f646..ba587c6f0b3 100644 --- a/spec/frontend/members/components/modals/leave_modal_spec.js +++ b/spec/frontend/members/components/modals/leave_modal_spec.js @@ -1,11 +1,14 @@ import { GlModal, GlForm } from '@gitlab/ui'; -import { within } from '@testing-library/dom'; -import { mount, createWrapper } from '@vue/test-utils'; import { cloneDeep } from 'lodash'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; +import { mountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper'; import LeaveModal from '~/members/components/modals/leave_modal.vue'; -import { LEAVE_MODAL_ID, MEMBER_TYPES } from '~/members/constants'; +import { + LEAVE_MODAL_ID, + MEMBER_TYPES, + MEMBER_MODEL_TYPE_PROJECT_MEMBER, +} from '~/members/constants'; import UserDeletionObstaclesList from '~/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.vue'; import { parseUserDeletionObstacles } from '~/vue_shared/components/user_deletion_obstacles/utils'; import { member } from '../../mock_data'; @@ -31,14 +34,17 @@ describe('LeaveModal', () => { }); }; - const createComponent = (propsData = {}, state) => { - wrapper = mount(LeaveModal, { + const createComponent = async (propsData = {}, state) => { + wrapper = mountExtended(LeaveModal, { store: createStore(state), provide: { namespace: MEMBER_TYPES.user, }, propsData: { member, + permissions: { + canRemove: true, + }, ...propsData, }, attrs: { @@ -46,39 +52,98 @@ describe('LeaveModal', () => { visible: true, }, }); + + await nextTick(); }; - const findModal = () => wrapper.findComponent(GlModal); + const findModal = () => extendedWrapper(wrapper.findComponent(GlModal)); const findForm = () => findModal().findComponent(GlForm); const findUserDeletionObstaclesList = () => findModal().findComponent(UserDeletionObstaclesList); - const getByText = (text, options) => - createWrapper(within(findModal().element).getByText(text, options)); - - beforeEach(async () => { - createComponent(); - await nextTick(); - }); - afterEach(() => { wrapper.destroy(); }); - it('sets modal ID', () => { + it('sets modal ID', async () => { + await createComponent(); + expect(findModal().props('modalId')).toBe(LEAVE_MODAL_ID); }); - it('displays modal title', () => { - expect(getByText(`Leave "${member.source.fullName}"`).exists()).toBe(true); + describe('when leave is allowed', () => { + it('displays modal title', async () => { + await createComponent(); + + expect(findModal().findByText(`Leave "${member.source.fullName}"`).exists()).toBe(true); + }); + + it('displays modal body', async () => { + await createComponent(); + + expect( + findModal() + .findByText(`Are you sure you want to leave "${member.source.fullName}"?`) + .exists(), + ).toBe(true); + }); }); - it('displays modal body', () => { - expect(getByText(`Are you sure you want to leave "${member.source.fullName}"?`).exists()).toBe( - true, - ); + describe('when leave is blocked by last owner', () => { + const permissions = { + canRemove: false, + canRemoveBlockedByLastOwner: true, + }; + + it('does not show primary action button', async () => { + await createComponent({ + permissions, + }); + + expect(findModal().props('actionPrimary')).toBe(null); + }); + + it('displays modal title', async () => { + await createComponent({ + permissions, + }); + + expect(findModal().findByText(`Cannot leave "${member.source.fullName}"`).exists()).toBe( + true, + ); + }); + + describe('when member model type is `GroupMember`', () => { + it('displays modal body', async () => { + await createComponent({ + permissions, + }); + + expect( + findModal().findByText(LeaveModal.i18n.preventedBodyGroupMemberModelType).exists(), + ).toBe(true); + }); + }); + + describe('when member model type is `ProjectMember`', () => { + it('displays modal body', async () => { + await createComponent({ + member: { + ...member, + type: MEMBER_MODEL_TYPE_PROJECT_MEMBER, + }, + permissions, + }); + + expect( + findModal().findByText(LeaveModal.i18n.preventedBodyProjectMemberModelType).exists(), + ).toBe(true); + }); + }); }); - it('displays form with correct action and inputs', () => { + it('displays form with correct action and inputs', async () => { + await createComponent(); + const form = findForm(); expect(form.attributes('action')).toBe('/groups/foo-bar/-/group_members/leave'); @@ -89,7 +154,9 @@ describe('LeaveModal', () => { }); describe('User deletion obstacles list', () => { - it("displays obstacles list when member's user is part of on-call management", () => { + it("displays obstacles list when member's user is part of on-call management", async () => { + await createComponent(); + const obstaclesList = findUserDeletionObstaclesList(); expect(obstaclesList.exists()).toBe(true); expect(obstaclesList.props()).toMatchObject({ @@ -105,17 +172,18 @@ describe('LeaveModal', () => { delete memberWithoutOncall.user.oncallSchedules; delete memberWithoutOncall.user.escalationPolicies; - createComponent({ member: memberWithoutOncall }); - await nextTick(); + await createComponent({ member: memberWithoutOncall }); expect(findUserDeletionObstaclesList().exists()).toBe(false); }); }); - it('submits the form when "Leave" button is clicked', () => { + it('submits the form when "Leave" button is clicked', async () => { + await createComponent(); + const submitSpy = jest.spyOn(findForm().element, 'submit'); - getByText('Leave').trigger('click'); + findModal().findByText('Leave').trigger('click'); expect(submitSpy).toHaveBeenCalled(); diff --git a/spec/frontend/members/components/modals/remove_member_modal_spec.js b/spec/frontend/members/components/modals/remove_member_modal_spec.js index a9ff95dd948..47a03b5083a 100644 --- a/spec/frontend/members/components/modals/remove_member_modal_spec.js +++ b/spec/frontend/members/components/modals/remove_member_modal_spec.js @@ -137,4 +137,28 @@ describe('RemoveMemberModal', () => { }); }, ); + + describe('when removal is prevented', () => { + const message = + 'A group must have at least one owner. To remove the member, assign a new owner.'; + + beforeEach(() => { + createComponent({ + actionText: 'Remove member', + memberModelType: MEMBER_MODEL_TYPE_GROUP_MEMBER, + isAccessRequest: false, + isInvite: false, + message, + preventRemoval: true, + }); + }); + + it('does not show primary action button', () => { + expect(findGlModal().props('actionPrimary')).toBe(null); + }); + + it('only shows the message', () => { + expect(findGlModal().text()).toBe(message); + }); + }); }); diff --git a/spec/frontend/members/components/table/members_table_cell_spec.js b/spec/frontend/members/components/table/members_table_cell_spec.js index 0b0140b0cdb..ac5d83d028d 100644 --- a/spec/frontend/members/components/table/members_table_cell_spec.js +++ b/spec/frontend/members/components/table/members_table_cell_spec.js @@ -3,6 +3,7 @@ import Vue from 'vue'; import Vuex from 'vuex'; import MembersTableCell from '~/members/components/table/members_table_cell.vue'; import { MEMBER_TYPES } from '~/members/constants'; +import { canRemoveBlockedByLastOwner } from '~/members/utils'; import { member as memberMock, directMember, @@ -12,6 +13,11 @@ import { accessRequest, } from '../../mock_data'; +jest.mock('~/members/utils', () => ({ + ...jest.requireActual('~/members/utils'), + canRemoveBlockedByLastOwner: jest.fn().mockImplementation(() => true), +})); + describe('MembersTableCell', () => { const WrappedComponent = { props: { @@ -55,6 +61,7 @@ describe('MembersTableCell', () => { provide: { sourceId: 1, currentUserId: 1, + canManageMembers: true, }, scopedSlots: { default: ` @@ -179,6 +186,15 @@ describe('MembersTableCell', () => { }); }); + describe('canRemoveBlockedByLastOwner', () => { + it('calls util and returns value', () => { + createComponentWithDirectMember(); + + expect(canRemoveBlockedByLastOwner).toHaveBeenCalledWith(directMember, true); + expect(findWrappedComponent().props('permissions').canRemoveBlockedByLastOwner).toBe(true); + }); + }); + describe('canResend', () => { describe('when member type is `invite`', () => { it('returns `true` when `canResend` is `true`', () => { diff --git a/spec/frontend/members/components/table/members_table_spec.js b/spec/frontend/members/components/table/members_table_spec.js index 3165173ce21..1d18026a410 100644 --- a/spec/frontend/members/components/table/members_table_spec.js +++ b/spec/frontend/members/components/table/members_table_spec.js @@ -63,6 +63,7 @@ describe('MembersTable', () => { provide: { sourceId: 1, currentUserId: 1, + canManageMembers: true, namespace: MEMBER_TYPES.invite, ...provide, }, @@ -200,16 +201,23 @@ describe('MembersTable', () => { canRemove: true, }; + const memberCanRemoveBlockedLastOwner = { + ...directMember, + canRemove: false, + isLastOwner: true, + }; + const memberNoPermissions = { ...memberMock, id: 2, }; describe.each` - permission | members - ${'canUpdate'} | ${[memberNoPermissions, memberCanUpdate]} - ${'canRemove'} | ${[memberNoPermissions, memberCanRemove]} - ${'canResend'} | ${[memberNoPermissions, invite]} + permission | members + ${'canUpdate'} | ${[memberNoPermissions, memberCanUpdate]} + ${'canRemove'} | ${[memberNoPermissions, memberCanRemove]} + ${'canRemoveBlockedByLastOwner'} | ${[memberNoPermissions, memberCanRemoveBlockedLastOwner]} + ${'canResend'} | ${[memberNoPermissions, invite]} `('when one of the members has $permission permissions', ({ members }) => { it('renders the "Actions" field', () => { createComponent({ members, tableFields: ['actions'] }); @@ -228,10 +236,11 @@ describe('MembersTable', () => { }); describe.each` - permission | members - ${'canUpdate'} | ${[memberMock]} - ${'canRemove'} | ${[memberMock]} - ${'canResend'} | ${[{ ...invite, invite: { ...invite.invite, canResend: false } }]} + permission | members + ${'canUpdate'} | ${[memberMock]} + ${'canRemove'} | ${[memberMock]} + ${'canRemoveBlockedByLastOwner'} | ${[memberMock]} + ${'canResend'} | ${[{ ...invite, invite: { ...invite.invite, canResend: false } }]} `('when none of the members have $permission permissions', ({ members }) => { it('does not render the "Actions" field', () => { createComponent({ members, tableFields: ['actions'] }); diff --git a/spec/frontend/members/mock_data.js b/spec/frontend/members/mock_data.js index 3f88cb7b386..7ab642c84a7 100644 --- a/spec/frontend/members/mock_data.js +++ b/spec/frontend/members/mock_data.js @@ -17,7 +17,7 @@ export const member = { fullName: 'Foo Bar', webUrl: 'https://gitlab.com/groups/foo-bar', }, - type: 'GroupMember', + type: MEMBER_MODEL_TYPE_GROUP_MEMBER, state: MEMBER_STATE_CREATED, user: { id: 123, @@ -131,3 +131,10 @@ export const dataAttribute = JSON.stringify({ source_id: 234, can_manage_members: true, }); + +export const permissions = { + canRemove: true, + canRemoveBlockedByLastOwner: false, + canResend: true, + canUpdate: true, +}; diff --git a/spec/frontend/members/utils_spec.js b/spec/frontend/members/utils_spec.js index 8bef2096a2a..9d52c789db2 100644 --- a/spec/frontend/members/utils_spec.js +++ b/spec/frontend/members/utils_spec.js @@ -13,6 +13,7 @@ import { isDirectMember, isCurrentUser, canRemove, + canRemoveBlockedByLastOwner, canResend, canUpdate, canOverride, @@ -129,6 +130,17 @@ describe('Members Utils', () => { }); }); + describe('canRemoveBlockedByLastOwner', () => { + it.each` + member | canManageMembers | expected + ${{ ...directMember, isLastOwner: true }} | ${true} | ${true} + ${{ ...inheritedMember, isLastOwner: false }} | ${true} | ${false} + ${{ ...directMember, isLastOwner: true }} | ${false} | ${false} + `('returns $expected', ({ member, canManageMembers, expected }) => { + expect(canRemoveBlockedByLastOwner(member, canManageMembers)).toBe(expected); + }); + }); + describe('canResend', () => { it.each` member | expected diff --git a/workhorse/internal/lsif_transformer/parser/docs.go b/workhorse/internal/lsif_transformer/parser/docs.go index f87bc7fd86c..9cdec4c8d42 100644 --- a/workhorse/internal/lsif_transformer/parser/docs.go +++ b/workhorse/internal/lsif_transformer/parser/docs.go @@ -5,6 +5,7 @@ import ( "bufio" "encoding/json" "io" + "path/filepath" "strings" ) @@ -116,7 +117,7 @@ func (d *Docs) addMetadata(line []byte) error { return err } - d.Root = strings.TrimSpace(metadata.Root) + "/" + d.Root = strings.TrimSpace(metadata.Root) return nil } @@ -127,7 +128,12 @@ func (d *Docs) addDocument(line []byte) error { return err } - d.Entries[doc.Id] = strings.TrimPrefix(doc.Uri, d.Root) + relativePath, err := filepath.Rel(d.Root, doc.Uri) + if err != nil { + relativePath = doc.Uri + } + + d.Entries[doc.Id] = relativePath return nil } diff --git a/workhorse/internal/lsif_transformer/parser/docs_test.go b/workhorse/internal/lsif_transformer/parser/docs_test.go index 24e3eba8ac5..805bc53c0b7 100644 --- a/workhorse/internal/lsif_transformer/parser/docs_test.go +++ b/workhorse/internal/lsif_transformer/parser/docs_test.go @@ -18,16 +18,32 @@ func TestParse(t *testing.T) { require.NoError(t, err) defer d.Close() - data := []byte(`{"id":"1","label":"metaData","projectRoot":"file:///Users/nested"}` + "\n") - data = append(data, createLine("2", "document", "file:///Users/nested/file.rb")...) - data = append(data, createLine("3", "document", "file:///Users/nested/folder/file.rb")...) - data = append(data, createLine("4", "document", "file:///Users/wrong/file.rb")...) + for _, root := range []string{ + "file:///Users/nested", + "file:///Users/nested/.", + "file:///Users/nested/", + } { + t.Run("Document with root: "+root, func(t *testing.T) { + data := []byte(`{"id":"1","label":"metaData","projectRoot":"` + root + `"}` + "\n") + data = append(data, createLine("2", "document", "file:///Users/nested/file.rb")...) + data = append(data, createLine("3", "document", "file:///Users/nested/folder/file.rb")...) - require.NoError(t, d.Parse(bytes.NewReader(data))) + require.NoError(t, d.Parse(bytes.NewReader(data))) + + require.Equal(t, "file.rb", d.Entries[2]) + require.Equal(t, "folder/file.rb", d.Entries[3]) + }) + } + + t.Run("Relative path cannot be calculated", func(t *testing.T) { + originalUri := "file:///Users/nested/folder/file.rb" + data := []byte(`{"id":"1","label":"metaData","projectRoot":"/a"}` + "\n") + data = append(data, createLine("2", "document", originalUri)...) + + require.NoError(t, d.Parse(bytes.NewReader(data))) - require.Equal(t, d.Entries[2], "file.rb") - require.Equal(t, d.Entries[3], "folder/file.rb") - require.Equal(t, d.Entries[4], "file:///Users/wrong/file.rb") + require.Equal(t, originalUri, d.Entries[2]) + }) } func TestParseContainsLine(t *testing.T) { |