diff options
29 files changed, 627 insertions, 405 deletions
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml index ca8d81737b8..b4a3232fcf7 100644 --- a/.gitlab/ci/global.gitlab-ci.yml +++ b/.gitlab/ci/global.gitlab-ci.yml @@ -509,4 +509,4 @@ url="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/repository/files/scripts%2Futils.sh/raw?ref=${CI_COMMIT_SHA}" curl -f --header "Private-Token: ${PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE}" "${url}" --create-dirs --output scripts/utils.sh - source scripts/utils.sh - - download_files ${FILES_TO_DOWNLOAD} + - run_timed_command "download_files ${FILES_TO_DOWNLOAD}" diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 2844669a89d..5b654627465 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -447,6 +447,7 @@ rspec:artifact-collector ee: rspec:coverage: extends: - .coverage-base + - .fast-no-clone-job - .rails:rules:rspec-coverage stage: post-test needs: @@ -477,7 +478,20 @@ rspec:coverage: # Memory jobs - job: memory-on-boot optional: true + variables: + FILES_TO_DOWNLOAD: > + config/bundler_setup.rb + Gemfile + Gemfile.lock + scripts/merge-simplecov + spec/simplecov_env_core.rb + spec/simplecov_env.rb + before_script: + - !reference [".fast-no-clone-job", before_script] + - run_timed_command "download_local_gems" + - !reference [".coverage-base", before_script] script: + - chmod u+x scripts/merge-simplecov # Not the right permissions when downloading the script via the API. - run_timed_command "bundle exec scripts/merge-simplecov" coverage: '/LOC \((\d+\.\d+%)\) covered.$/' artifacts: diff --git a/.gitlab/ci/review-apps/main.gitlab-ci.yml b/.gitlab/ci/review-apps/main.gitlab-ci.yml index f97aa445310..e94455c2893 100644 --- a/.gitlab/ci/review-apps/main.gitlab-ci.yml +++ b/.gitlab/ci/review-apps/main.gitlab-ci.yml @@ -69,6 +69,7 @@ review-build-cng: GITLAB_REPO_URL: ${CI_PROJECT_URL} GITLAB_IMAGE_REPOSITORY: "registry.gitlab.com/gitlab-org/build/cng-mirror" GITLAB_IMAGE_SUFFIX: "ee" + GITLAB_REVIEW_APP_BASE_CONFIG_FILE: "scripts/review_apps/base-config.yaml" GITLAB_HELM_CHART_REF: "75b1486a9aec212d0f49ef1251526d8e51004bbc" # 7.0.1: https://gitlab.com/gitlab-org/charts/gitlab/-/commit/75b1486a9aec212d0f49ef1251526d8e51004bbc environment: name: review/${CI_COMMIT_REF_SLUG}${SCHEDULE_TYPE} # No separator for SCHEDULE_TYPE so it's compatible as before and looks nice without it @@ -120,7 +120,7 @@ gem 'gitlab_omniauth-ldap', '~> 2.2.0', require: 'omniauth-ldap' gem 'net-ldap', '~> 0.17.1' # API -gem 'grape', '~> 1.7.0' +gem 'grape', '~> 1.7.1' gem 'grape-entity', '~> 0.10.0' gem 'rack-cors', '~> 1.1.1', require: 'rack/cors' gem 'grape-swagger', '~> 1.6.1', group: [:development, :test] diff --git a/Gemfile.checksum b/Gemfile.checksum index 9ca019df145..d785b0e030f 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -254,7 +254,7 @@ {"name":"googleapis-common-protos-types","version":"1.5.0","platform":"ruby","checksum":"5769cf7376abc86ef7f5897a4aaca1d5c5a3c49ddabeddd2c251fcf8155f858b"}, {"name":"googleauth","version":"1.3.0","platform":"ruby","checksum":"51dd7362353cf1e90a2d01e1fb94321ae3926c776d4dc4a79db65230217ffcc2"}, {"name":"gpgme","version":"2.0.22","platform":"ruby","checksum":"7c6904952afdd0bf2c7c3ed6de98a5143f86c6b7390dbcd9d7012bddfa3ec862"}, -{"name":"grape","version":"1.7.0","platform":"ruby","checksum":"88331a9627d7e61525f82b012450b345cec81f6152c91026d3909826baf66f51"}, +{"name":"grape","version":"1.7.1","platform":"ruby","checksum":"6b679d8918ee3dc19b0da95a5069dc95a71a15cf5788f5f787bb2ededf58cbb6"}, {"name":"grape-entity","version":"0.10.0","platform":"ruby","checksum":"9aed1e7cbbc96d9e73f72e5f32c776d4ba8a5baf54c3acda2682008dba2b2cfe"}, {"name":"grape-path-helpers","version":"1.7.1","platform":"ruby","checksum":"2e27271a20d4073e3a3b2b955425c7f803e198be3ba8f6e59e3d59643c5381e2"}, {"name":"grape-swagger","version":"1.6.1","platform":"ruby","checksum":"0fd2d38476524b66e8d06de71e6c481d34286d895b12161f5df4427d66d5c69f"}, diff --git a/Gemfile.lock b/Gemfile.lock index a321f3be7fc..a9e0754dc7b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -768,12 +768,12 @@ GEM signet (>= 0.16, < 2.a) gpgme (2.0.22) mini_portile2 (~> 2.7) - grape (1.7.0) + grape (1.7.1) activesupport builder dry-types (>= 1.1) mustermann-grape (~> 1.0.0) - rack (>= 1.3.0) + rack (>= 1.3.0, < 3) rack-accept grape-entity (0.10.0) activesupport (>= 3.0.0) @@ -1841,7 +1841,7 @@ DEPENDENCIES google-cloud-storage (~> 1.44.0) google-protobuf (~> 3.23, >= 3.23.4) gpgme (~> 2.0.22) - grape (~> 1.7.0) + grape (~> 1.7.1) grape-entity (~> 0.10.0) grape-path-helpers (~> 1.7.1) grape-swagger (~> 1.6.1) diff --git a/app/assets/javascripts/issues/create_merge_request_dropdown.js b/app/assets/javascripts/issues/create_merge_request_dropdown.js index de0334b4ffe..98888f9f9b2 100644 --- a/app/assets/javascripts/issues/create_merge_request_dropdown.js +++ b/app/assets/javascripts/issues/create_merge_request_dropdown.js @@ -137,8 +137,6 @@ export default class CreateMergeRequestDropdown { this.refInput.addEventListener('keyup', this.onChangeInput.bind(this)); // Detect when user clicks inside the input to apply the suggested ref this.refInput.addEventListener('click', this.onChangeInput.bind(this)); - // Detect when user clicks outside the input to apply the suggested ref - this.refInput.addEventListener('blur', this.onChangeInput.bind(this)); // Detect when user presses tab to apply the suggested ref this.refInput.addEventListener('keydown', CreateMergeRequestDropdown.processTab.bind(this)); } @@ -178,8 +176,16 @@ export default class CreateMergeRequestDropdown { createBranch(navigateToBranch = true) { this.isCreatingBranch = true; + const endpoint = createEndpoint( + this.projectPath, + mergeUrlParams( + { ref: this.refInput.value, branch_name: this.branchInput.value }, + this.createBranchPath, + ), + ); + return axios - .post(createEndpoint(this.projectPath, this.createBranchPath), { + .post(endpoint, { confidential_issue_project_id: canCreateConfidentialMergeRequest() ? this.projectId : null, }) .then(({ data }) => { @@ -407,9 +413,6 @@ export default class CreateMergeRequestDropdown { // If the input is empty, use the original value generated by the backend. if (!value) { - this.createBranchPath = this.wrapperEl.dataset.createBranchPath; - this.createMrPath = this.wrapperEl.dataset.createMrPath; - if (target === INPUT_TARGET_BRANCH) { this.branchIsValid = true; } else { @@ -539,7 +542,6 @@ export default class CreateMergeRequestDropdown { updateBranchName(suggestedBranchName) { this.branchInput.value = suggestedBranchName; this.updateInputState(INPUT_TARGET_BRANCH, suggestedBranchName, ''); - this.updateCreatePaths(INPUT_TARGET_BRANCH, suggestedBranchName); } updateInputState(target, ref, result) { @@ -561,7 +563,6 @@ export default class CreateMergeRequestDropdown { if (ref === result) { this.refIsValid = true; this.showAvailableMessage(INPUT_TARGET_REF); - this.updateCreatePaths(INPUT_TARGET_REF, ref); } else { this.refIsValid = false; this.refInput.dataset.value = ref; @@ -585,7 +586,6 @@ export default class CreateMergeRequestDropdown { // Or user typed input contains invalid chars, // that means a new branch cannot be created as it already exists. this.showAvailableMessage(INPUT_TARGET_BRANCH, VALIDATION_TYPE_BRANCH_UNAVAILABLE); - this.updateCreatePaths(INPUT_TARGET_BRANCH, ref); } else if (isInvalidString) { this.branchIsValid = false; this.showNotAvailableMessage(INPUT_TARGET_BRANCH, VALIDATION_TYPE_INVALID_CHARS); @@ -594,22 +594,4 @@ export default class CreateMergeRequestDropdown { this.showNotAvailableMessage(INPUT_TARGET_BRANCH); } } - - // target - 'branch' or 'ref' - // ref - string - the new value to use as branch or ref - updateCreatePaths(target, ref) { - const pathReplacement = `$1${encodeURIComponent(ref)}`; - - this.createBranchPath = this.createBranchPath.replace( - this.regexps[target].createBranchPath, - pathReplacement, - ); - this.createMrPath = this.createMrPath.replace( - this.regexps[target].createMrPath, - pathReplacement, - ); - - this.wrapperEl.dataset.createBranchPath = this.createBranchPath; - this.wrapperEl.dataset.createMrPath = this.createMrPath; - } } diff --git a/app/assets/javascripts/super_sidebar/components/flyout_menu.vue b/app/assets/javascripts/super_sidebar/components/flyout_menu.vue index 285fa8ee90b..fa7960da2f4 100644 --- a/app/assets/javascripts/super_sidebar/components/flyout_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/flyout_menu.vue @@ -1,5 +1,5 @@ <script> -import { computePosition, autoUpdate, flip, shift } from '@floating-ui/dom'; +import { computePosition, autoUpdate, offset, flip, shift } from '@floating-ui/dom'; import NavItem from './nav_item.vue'; export default { @@ -22,7 +22,7 @@ export default { function updatePosition() { return computePosition(target, flyout, { - middleware: [flip(), shift()], + middleware: [offset({ alignmentAxis: -12 }), flip(), shift()], placement: 'right-start', strategy: 'fixed', }).then(({ x, y }) => { @@ -44,13 +44,13 @@ export default { <template> <div :id="`${targetId}-flyout`" - class="gl-fixed gl-pl-3 gl-z-index-9999 gl-max-h-full gl-overflow-y-auto" + class="gl-fixed gl-p-4 gl-mx-n1 gl-z-index-9999 gl-max-h-full gl-overflow-y-auto" @mouseover="$emit('mouseover')" @mouseleave="$emit('mouseleave')" > <ul v-if="items.length > 0" - class="gl-min-w-20 gl-max-w-34 gl-border-1 gl-rounded-base gl-border-solid gl-border-gray-100 gl-shadow gl-bg-white gl-p-2 gl-pb-1 gl-list-style-none" + class="gl-min-w-20 gl-max-w-34 gl-border-1 gl-rounded-base gl-border-solid gl-border-gray-100 gl-shadow-md gl-bg-white gl-p-2 gl-pb-1 gl-list-style-none" > <nav-item v-for="item of items" diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue index 842893af931..79d476ee74b 100644 --- a/app/assets/javascripts/super_sidebar/components/nav_item.vue +++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue @@ -109,7 +109,8 @@ export default { return { 'gl-px-2 gl-mx-2 gl-line-height-normal': this.isSubitem, 'gl-px-3': !this.isSubitem, - 'gl-pl-5!': this.isFlyout, + 'gl-pl-5! gl-rounded-small': this.isFlyout, + 'gl-rounded-base': !this.isFlyout, [this.item.link_classes]: this.item.link_classes, ...this.linkClasses, }; @@ -127,7 +128,7 @@ export default { :is="navItemLinkComponent" #default="{ isActive }" v-bind="linkProps" - class="nav-item-link gl-rounded-base gl-relative gl-display-flex gl-align-items-center gl-min-h-7 gl-gap-3 gl-mb-1 gl-py-2 gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-focus--focus" + class="nav-item-link gl-relative gl-display-flex gl-align-items-center gl-min-h-7 gl-gap-3 gl-mb-1 gl-py-2 gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-focus--focus" :class="computedLinkClasses" data-qa-selector="nav_item_link" data-testid="nav-item-link" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue index 258fa4edcda..9bb39ba22e0 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue @@ -5,8 +5,9 @@ export default { import( '~/vue_merge_request_widget/extensions/security_reports/mr_widget_security_reports.vue' ), - MrTerraformWidget: () => import('~/vue_merge_request_widget/extensions/terraform/index.vue'), + MrCodeQualityWidget: () => + import('~/vue_merge_request_widget/extensions/code_quality/index.vue'), }, props: { @@ -21,8 +22,14 @@ export default { return this.mr.terraformReportsPath && 'MrTerraformWidget'; }, + codeQualityWidget() { + return this.mr.codequalityReportsPath ? 'MrCodeQualityWidget' : undefined; + }, + widgets() { - return [this.terraformPlansWidget, 'MrSecurityWidget'].filter((w) => w); + return [this.codeQualityWidget, this.terraformPlansWidget, 'MrSecurityWidget'].filter( + (w) => w, + ); }, }, }; diff --git a/app/assets/javascripts/vue_shared/components/groups_list/groups_list.stories.js b/app/assets/javascripts/vue_shared/components/groups_list/groups_list.stories.js new file mode 100644 index 00000000000..235523054c3 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/groups_list/groups_list.stories.js @@ -0,0 +1,19 @@ +import { groups } from 'jest/vue_shared/components/groups_list/mock_data'; +import GroupsList from './groups_list.vue'; + +export default { + component: GroupsList, + title: 'vue_shared/groups_list', +}; + +const Template = (args, { argTypes }) => ({ + components: { GroupsList }, + props: Object.keys(argTypes), + template: '<groups-list v-bind="$props" />', +}); + +export const Default = Template.bind({}); +Default.args = { + groups, + showGroupIcon: true, +}; diff --git a/app/assets/javascripts/vue_shared/components/groups_list/groups_list.vue b/app/assets/javascripts/vue_shared/components/groups_list/groups_list.vue new file mode 100644 index 00000000000..7da45169fee --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/groups_list/groups_list.vue @@ -0,0 +1,29 @@ +<script> +import GroupsListItem from './groups_list_item.vue'; + +export default { + components: { GroupsListItem }, + props: { + groups: { + type: Array, + required: true, + }, + showGroupIcon: { + type: Boolean, + required: false, + default: false, + }, + }, +}; +</script> + +<template> + <ul class="gl-p-0 gl-list-style-none"> + <groups-list-item + v-for="group in groups" + :key="group.id" + :group="group" + :show-group-icon="showGroupIcon" + /> + </ul> +</template> diff --git a/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue b/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue new file mode 100644 index 00000000000..8a301cd0dd0 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue @@ -0,0 +1,168 @@ +<script> +import { GlAvatarLabeled, GlIcon, GlTooltipDirective, GlTruncateText } from '@gitlab/ui'; + +import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '~/visibility_level/constants'; +import { ACCESS_LEVEL_LABELS } from '~/access_level/constants'; +import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue'; +import { __ } from '~/locale'; +import { numberToMetricPrefix } from '~/lib/utils/number_utils'; +import SafeHtml from '~/vue_shared/directives/safe_html'; + +export default { + i18n: { + subgroups: __('Subgroups'), + projects: __('Projects'), + directMembers: __('Direct members'), + showMore: __('Show more'), + showLess: __('Show less'), + }, + avatarSize: { default: 32, md: 48 }, + safeHtmlConfig: { + ADD_TAGS: ['gl-emoji'], + }, + components: { + GlAvatarLabeled, + GlIcon, + UserAccessRoleBadge, + GlTruncateText, + }, + directives: { + GlTooltip: GlTooltipDirective, + SafeHtml, + }, + props: { + group: { + type: Object, + required: true, + }, + showGroupIcon: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + visibility() { + return this.group.visibility; + }, + visibilityIcon() { + return VISIBILITY_TYPE_ICON[this.visibility]; + }, + visibilityTooltip() { + return GROUP_VISIBILITY_TYPE[this.visibility]; + }, + accessLevel() { + return this.group.accessLevel?.integerValue; + }, + accessLevelLabel() { + return ACCESS_LEVEL_LABELS[this.accessLevel]; + }, + shouldShowAccessLevel() { + return this.accessLevel !== undefined; + }, + groupIconName() { + return this.group.parent ? 'subgroup' : 'group'; + }, + statsPadding() { + return this.showGroupIcon ? 'gl-pl-11' : 'gl-pl-8'; + }, + descendantGroupsCount() { + return numberToMetricPrefix(this.group.descendantGroupsCount); + }, + projectsCount() { + return numberToMetricPrefix(this.group.projectsCount); + }, + groupMembersCount() { + return numberToMetricPrefix(this.group.groupMembersCount); + }, + }, +}; +</script> + +<template> + <li class="groups-list-item gl-py-5 gl-md-display-flex gl-align-items-center gl-border-b"> + <div class="gl-display-flex gl-flex-grow-1"> + <gl-icon + v-if="showGroupIcon" + class="gl-mr-3 gl-mt-3 gl-md-mt-5 gl-flex-shrink-0 gl-text-secondary" + :name="groupIconName" + /> + <gl-avatar-labeled + :entity-id="group.id" + :entity-name="group.fullName" + :label="group.fullName" + :label-link="group.webUrl" + shape="rect" + :size="$options.avatarSize" + > + <template #meta> + <div class="gl-px-2"> + <div class="gl-mx-n2 gl-display-flex gl-align-items-center gl-flex-wrap"> + <div class="gl-px-2"> + <gl-icon + v-if="visibility" + v-gl-tooltip="visibilityTooltip" + :name="visibilityIcon" + class="gl-text-secondary" + /> + </div> + <div class="gl-px-2"> + <user-access-role-badge v-if="shouldShowAccessLevel">{{ + accessLevelLabel + }}</user-access-role-badge> + </div> + </div> + </div> + </template> + <gl-truncate-text + v-if="group.descriptionHtml" + :lines="2" + :mobile-lines="2" + :show-more-text="$options.i18n.showMore" + :show-less-text="$options.i18n.showLess" + class="gl-mt-2" + > + <div + v-safe-html:[$options.safeHtmlConfig]="group.descriptionHtml" + class="gl-font-sm md" + data-testid="group-description" + ></div> + </gl-truncate-text> + </gl-avatar-labeled> + </div> + <div + class="gl-md-display-flex gl-flex-direction-column gl-align-items-flex-end gl-flex-shrink-0 gl-mt-3 gl-md-pl-0 gl-md-mt-0 gl-md-ml-3" + :class="statsPadding" + > + <div class="gl-display-flex gl-align-items-center gl-gap-x-3"> + <div + v-gl-tooltip="$options.i18n.subgroups" + :aria-label="$options.i18n.subgroups" + class="gl-text-secondary" + data-testid="subgroups-count" + > + <gl-icon name="subgroup" /> + <span>{{ descendantGroupsCount }}</span> + </div> + <div + v-gl-tooltip="$options.i18n.projects" + :aria-label="$options.i18n.projects" + class="gl-text-secondary" + data-testid="projects-count" + > + <gl-icon name="project" /> + <span>{{ projectsCount }}</span> + </div> + <div + v-gl-tooltip="$options.i18n.directMembers" + :aria-label="$options.i18n.directMembers" + class="gl-text-secondary" + data-testid="members-count" + > + <gl-icon name="users" /> + <span>{{ groupMembersCount }}</span> + </div> + </div> + </div> + </li> +</template> diff --git a/config/feature_flags/development/group_saml_jit_errors.yml b/config/feature_flags/development/group_saml_jit_errors.yml deleted file mode 100644 index 6d093e7c492..00000000000 --- a/config/feature_flags/development/group_saml_jit_errors.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: group_saml_jit_errors -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127654 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/419836 -milestone: '16.3' -type: development -group: group::authentication and authorization -default_enabled: false diff --git a/config/feature_flags/development/telesign_callback.yml b/config/feature_flags/development/telesign_callback.yml deleted file mode 100644 index 33bdb24a727..00000000000 --- a/config/feature_flags/development/telesign_callback.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: telesign_callback -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125983 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/417703 -milestone: '16.3' -type: development -group: group::anti-abuse -default_enabled: false diff --git a/doc/administration/troubleshooting/postgresql.md b/doc/administration/troubleshooting/postgresql.md index 42d4d9c6d8c..53bde005232 100644 --- a/doc/administration/troubleshooting/postgresql.md +++ b/doc/administration/troubleshooting/postgresql.md @@ -212,10 +212,8 @@ To resolve the error, run `VACUUM` manually: ### GitLab database requirements -The [database requirements](../../install/requirements.md#database) for GitLab include: - -- Support for MySQL was removed in [GitLab 12.1](../../update/index.md#1210). -- Review and install the [required extension list](../../install/postgresql_extensions.md). +See [database requirements](../../install/requirements.md#database) and review and install the +[required extension list](../../install/postgresql_extensions.md). ### Serialization errors in the `production/sidekiq` log diff --git a/doc/development/pipelines/internals.md b/doc/development/pipelines/internals.md index 97424e02437..904ac11511d 100644 --- a/doc/development/pipelines/internals.md +++ b/doc/development/pipelines/internals.md @@ -368,6 +368,8 @@ my-job: - This pattern does not work if a script relies on `git` to access the repository, because we don't have the repository without cloning or fetching. - The job using this pattern needs to have `curl` available. +- If you need to run `bundle install` in the job (even using `BUNDLE_ONLY`), you need to download the gems that are stored in the `gitlab-org/gitlab` project. + - You can use the `download_local_gems` shell command for that purpose. #### Where is this pattern used? @@ -387,5 +389,12 @@ my-job: - `scripts/review_apps/review-apps.sh` - `scripts/review_apps/seed-dast-test-data.sh` - `VERSION` + - `rspec:coverage` for: + - `config/bundler_setup.rb` + - `Gemfile.lock` + - `Gemfile` + - `scripts/merge-simplecov` + - `spec/simplecov_env_core.rb` + - `spec/simplecov_env.rb` Additionally, `scripts/utils.sh` is always downloaded from the API when this pattern is used (this file contains the code for `.fast-no-clone-job`). diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 5e13628e815..b60d3fd8359 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -68,7 +68,6 @@ process, such as PostgreSQL, which can have disastrous consequences. PostgreSQL is the only supported database, which is bundled with the Omnibus GitLab package. You can also use an [external PostgreSQL database](https://docs.gitlab.com/omnibus/settings/database.html#using-a-non-packaged-postgresql-database-management-server). -Support for MySQL was removed in [GitLab 12.1](../update/index.md#1210). ### PostgreSQL Requirements diff --git a/doc/update/index.md b/doc/update/index.md index 723b5199e98..e29c6d1b989 100644 --- a/doc/update/index.md +++ b/doc/update/index.md @@ -176,30 +176,21 @@ A *major* upgrade requires the following steps: Upgrading across multiple GitLab versions in one go is *only possible by accepting downtime*. If you don't want any downtime, read how to [upgrade with zero downtime](zero_downtime.md). -For a dynamic view of examples of supported upgrade paths, try the [Upgrade Path tool](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/) maintained by the [GitLab Support team](https://about.gitlab.com/handbook/support/#about-the-support-team). To share feedback and help improve the tool, create an issue or MR in the [upgrade-path project](https://gitlab.com/gitlab-com/support/toolbox/upgrade-path). +For a dynamic view of examples of supported upgrade paths, try the [Upgrade Path tool](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/) +maintained by the [GitLab Support team](https://about.gitlab.com/handbook/support/#about-the-support-team). To share +feedback and help improve the tool, create an issue or MR in the [upgrade-path project](https://gitlab.com/gitlab-com/support/toolbox/upgrade-path). -Required upgrade stops are versions of GitLab that you must upgrade to before upgrading to later versions. Required upgrade stops allow required background -migrations to finish. +When upgrading: -During GitLab 16.x, we are scheduling two or three required upgrade stops. We will give at least two milestones of notice when we -schedule a required upgrade stop. +1. Find where your version sits in the upgrade path: -The first planned required upgrade stop is scheduled for GitLab 16.3. If nothing is introduced requiring an upgrade stop, GitLab 16.3 will be treated as a -regular upgrade. + - GitLab 14: [`14.0.12`](#1400) > [`14.3.6`](#1430) > [`14.9.5`](#1490) > [`14.10.5`](#14100). + - GitLab 15: [`15.0.5`](#1500) > [`15.1.6`](#1510) (for GitLab instances with multiple web nodes) > [`15.4.6`](#1540) > [`15.11.x`](#15110). + - GitLab 16: [latest `16.Y.Z`](https://gitlab.com/gitlab-org/gitlab/-/releases). -Find where your version sits in the upgrade path below, and upgrade GitLab -accordingly, while also consulting the -[version-specific upgrade instructions](#version-specific-upgrading-instructions): - -- GitLab 8: `8.11.Z` > `8.12.0` > `8.17.7` -- GitLab 9: `9.0.13` > `9.5.10` -- GitLab 10: `10.0.7` > `10.8.7` -- GitLab 11: `11.0.6` > [`11.11.8`](#1200) -- GitLab 12: `12.0.12` > [`12.1.17`](#1210) > [`12.10.14`](#12100) -- GitLab 13: `13.0.14` > [`13.1.11`](#1310) > [`13.8.8`](#1388) > [`13.12.15`](#13120) -- GitLab 14: [`14.0.12`](#1400) > [`14.3.6`](#1430) > [`14.9.5`](#1490) > [`14.10.5`](#14100) -- GitLab 15: [`15.0.5`](#1500) > [`15.1.6`](#1510) (for GitLab instances with multiple web nodes) > [`15.4.6`](#1540) > [`15.11.x`](#15110) -- GitLab 16: [latest `16.Y.Z`](https://gitlab.com/gitlab-org/gitlab/-/releases) +1. Check for [required upgrade stops](#required-upgrade-stops). +1. Consult the [version-specific upgrade instructions](#version-specific-upgrading-instructions). +1. Upgrade GitLab accordingly. NOTE: When not explicitly specified, upgrade GitLab to the latest available patch @@ -209,6 +200,25 @@ be fixes for issues relating to the upgrade process. Specifically around a [major version](#upgrading-to-a-new-major-version), crucial database schema and migration patches may be included in the latest patch releases. +### Required upgrade stops + +Required upgrade stops are versions of GitLab that you must upgrade to before upgrading to later versions. Required +upgrade stops allow required background migrations to finish. + +During GitLab 16.x, we are scheduling two or three required upgrade stops. We will give at least two milestones of +notice when we schedule a required upgrade stop. + +The first planned required upgrade stop is scheduled for GitLab 16.3. If nothing is introduced requiring an upgrade stop, +GitLab 16.3 will be treated as a regular upgrade. + +### Earlier GitLab versions + +For information on upgrading to earlier GitLab versions, see the [documentation archives](https://archives.docs.gitlab.com). +The versions of the documentation in the archives contain version-specific information for even earlier versions of GitLab. + +For example, the [documentation for GitLab 15.11](https://archives.docs.gitlab.com/15.11/ee/update/#upgrade-paths) +contains information on versions back to GitLab 12. + ## Upgrading between editions GitLab comes in two flavors: [Community Edition](https://about.gitlab.com/features/#community) which is MIT licensed, @@ -1393,8 +1403,6 @@ Other issues: **GitLab 13.2 or older** directly to 14.0, this is [unsupported](#upgrading-to-a-new-major-version). You should instead follow a [supported upgrade path](#upgrade-paths). - See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-gitlab-139-to-144). -- See [Custom Rack Attack initializers](#custom-rack-attack-initializers) if you persist your own custom Rack Attack - initializers during upgrades. #### Upgrading to later 14.Y releases @@ -1406,263 +1414,6 @@ Other issues: 1. [Batched background migrations must finish](background_migrations.md#batched-background-migrations) before you upgrade to a later version [and may take longer than usual](#1400). -### 13.12.0 - -- See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-gitlab-139-to-144). - -- Check the GitLab database [has no references to legacy storage](../administration/raketasks/storage.md#on-legacy-storage). - The GitLab 14.0 pre-install check causes the package update to fail if unmigrated data exists: - - ```plaintext - Checking for unmigrated data on legacy storage - - Legacy storage is no longer supported. Please migrate your data to hashed storage. - ``` - -### 13.11.0 - -- Git 2.31.x and later is required. We recommend you use the - [Git version provided by Gitaly](../install/installation.md#git). - -- See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-gitlab-139-to-144). - -- GitLab 13.11 includes a faulty background migration ([`RescheduleArtifactExpiryBackfillAgain`](https://gitlab.com/gitlab-org/gitlab/-/blob/ccc70031b843ff8cff1185988c2e472a521c2701/db/post_migrate/20210413132500_reschedule_artifact_expiry_backfill_again.rb)) - that incorrectly sets the `expire_at` column in the `ci_job_artifacts` database table. - Incorrect `expire_at` values can potentially cause data loss. - - To prevent this risk of data loss, you must remove the content of the `RescheduleArtifactExpiryBackfillAgain` - migration, which makes it a no-op migration. You can repeat the changes from the - [commit that makes the migration no-op in 14.9 and later](https://gitlab.com/gitlab-org/gitlab/-/blob/42c3dfc5a1c8181767bbb5c76e7c5fa6fefbbc2b/db/post_migrate/20210413132500_reschedule_artifact_expiry_backfill_again.rb). - For more information, see [how to disable a data migration](../development/database/deleting_migrations.md#how-to-disable-a-data-migration). - -### 13.10.0 - -See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-gitlab-139-to-144). - -### 13.9.0 - -- We've detected an issue [with a column rename](https://gitlab.com/gitlab-org/gitlab/-/issues/324160) - that prevents upgrades to GitLab 13.9.0, 13.9.1, 13.9.2, and 13.9.3 when following the zero-downtime steps. It is necessary - to perform the following additional steps for the zero-downtime upgrade: - - 1. Before running the final `sudo gitlab-rake db:migrate` command on the deploy node, - execute the following queries using the PostgreSQL console (or `sudo gitlab-psql`) - to drop the problematic triggers: - - ```sql - drop trigger trigger_e40a6f1858e6 on application_settings; - drop trigger trigger_0d588df444c8 on application_settings; - drop trigger trigger_1572cbc9a15f on application_settings; - drop trigger trigger_22a39c5c25f3 on application_settings; - ``` - - 1. Run the final migrations: - - ```shell - sudo gitlab-rake db:migrate - ``` - - If you have already run the final `sudo gitlab-rake db:migrate` command on the deploy node and have - encountered the [column rename issue](https://gitlab.com/gitlab-org/gitlab/-/issues/324160), you - see the following error: - - ```shell - -- remove_column(:application_settings, :asset_proxy_whitelist) - rake aborted! - StandardError: An error has occurred, all later migrations canceled: - PG::DependentObjectsStillExist: ERROR: cannot drop column asset_proxy_whitelist of table application_settings because other objects depend on it - DETAIL: trigger trigger_0d588df444c8 on table application_settings depends on column asset_proxy_whitelist of table application_settings - ``` - - To work around this bug, follow the previous steps to complete the upgrade. - More details are available [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/324160). - -- See [Maintenance mode issue in GitLab 13.9 to 14.4](#maintenance-mode-issue-in-gitlab-139-to-144). - -- For GitLab Enterprise Edition customers, we noticed an issue when [subscription expiration is upcoming, and you create new subgroups and projects](https://gitlab.com/gitlab-org/gitlab/-/issues/322546). If you fall under that category and get 500 errors, you can work around this issue: - - 1. SSH into you GitLab server, and open a Rails console: - - ```shell - sudo gitlab-rails console - ``` - - 1. Disable the following features: - - ```ruby - Feature.disable(:subscribable_subscription_banner) - Feature.disable(:subscribable_license_banner) - ``` - - 1. Restart Puma or Unicorn: - - ```shell - #For installations using Puma - sudo gitlab-ctl restart puma - - #For installations using Unicorn - sudo gitlab-ctl restart unicorn - ``` - -### 13.8.8 - -GitLab 13.8 includes a background migration to address [an issue with duplicate service records](https://gitlab.com/gitlab-org/gitlab/-/issues/290008). If duplicate services are present, this background migration must complete before a unique index is applied to the services table, which was [introduced in GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52563). Upgrades from GitLab 13.8 and earlier to later versions must include an intermediate upgrade to GitLab 13.8.8 and [must wait until the background migrations complete](background_migrations.md) before proceeding. - -If duplicate services are still present, an upgrade to 13.9.x or later results in a failed upgrade with the following error: - -```console -PG::UniqueViolation: ERROR: could not create unique index "index_services_on_project_id_and_type_unique" -DETAIL: Key (project_id, type)=(NNN, ServiceName) is duplicated. -``` - -### 13.6.0 - -Ruby 2.7.2 is required. GitLab does not start with Ruby 2.6.6 or older versions. - -The required Git version is Git v2.29 or later. - -GitLab 13.6 includes a -[background migration `BackfillJiraTrackerDeploymentType2`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46368) -that may remain stuck permanently in a **pending** state despite completion of work -due to a bug. - -To clean up this stuck job, run the following in the [GitLab Rails Console](../administration/operations/rails_console.md): - -```ruby -Gitlab::Database::BackgroundMigrationJob.pending.where(class_name: "BackfillJiraTrackerDeploymentType2").find_each do |job| - puts Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded("BackfillJiraTrackerDeploymentType2", job.arguments) -end -``` - -### 13.4.0 - -GitLab 13.4.0 includes a background migration to [move all remaining repositories in legacy storage to hashed storage](../administration/raketasks/storage.md#migrate-to-hashed-storage). There are [known issues with this migration](https://gitlab.com/gitlab-org/gitlab/-/issues/259605) which are fixed in GitLab 13.5.4 and later. If possible, skip 13.4.0 and upgrade to 13.5.4 or later instead. The migration can take quite a while to run, depending on how many repositories must be moved. Be sure to check that all background migrations have completed before upgrading further. - -### 13.3.0 - -The recommended Git version is Git v2.28. The minimum required version of Git -v2.24 remains the same. - -### 13.2.0 - -GitLab installations that have multiple web nodes must be -[upgraded to 13.1](#1310) before upgrading to 13.2 (and later) due to a -breaking change in Rails that can result in authorization issues. - -GitLab 13.2.0 [remediates](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35492) an [email verification bypass](https://about.gitlab.com/releases/2020/05/27/security-release-13-0-1-released/). -After upgrading, if some of your users are unexpectedly encountering 404 or 422 errors when signing in, -or "blocked" messages when using the command line, -their accounts may have been un-confirmed. -In that case, ask them to check their email for a re-confirmation link. -For more information, see our discussion of [Email confirmation issues](../user/upgrade_email_bypass.md). - -GitLab 13.2.0 relies on the `btree_gist` extension for PostgreSQL. For installations with an externally managed PostgreSQL setup, make sure to -[install the extension manually](https://www.postgresql.org/docs/11/sql-createextension.html) before upgrading GitLab if the database user for GitLab -is not a superuser. This is not necessary for installations using a GitLab managed PostgreSQL database. - -### 13.1.0 - -In 13.1.0, you must upgrade to either: - -- At least Git v2.24 (previously, the minimum required version was Git v2.22). -- The recommended Git v2.26. - -Failure to do so results in internal errors in the Gitaly service in some RPCs due -to the use of the new `--end-of-options` Git flag. - -Additionally, in GitLab 13.1.0, the version of -[Rails was upgraded from 6.0.3 to 6.0.3.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33454). -The Rails upgrade included a change to CSRF token generation which is -not backwards-compatible - GitLab servers with the new Rails version -generate CSRF tokens that are not recognizable by GitLab servers -with the older Rails version - which could cause non-GET requests to -fail for [multi-node GitLab installations](zero_downtime.md#multi-node--ha-deployment). - -So, if you are using multiple Rails servers and specifically upgrading from 13.0, -all servers must first be upgraded to 13.1.Z before upgrading to 13.2.0 or later: - -1. Ensure all GitLab web nodes are running GitLab 13.1.Z. -1. Enable the `global_csrf_token` feature flag to enable new - method of CSRF token generation: - - ```ruby - Feature.enable(:global_csrf_token) - ``` - -1. Only then, continue to upgrade to later versions of GitLab. - -#### Custom Rack Attack initializers - -From GitLab 13.1, custom Rack Attack initializers (`config/initializers/rack_attack.rb`) are replaced with initializers -supplied with GitLab during upgrades. You should use these GitLab-supplied initializers. - -If you persist your own Rack Attack initializers between upgrades, you might -[get `500` errors](https://gitlab.com/gitlab-org/gitlab/-/issues/334681) when [upgrading to GitLab 14.0 and later](#1400). - -For **self-compiled (source) installations**, the Rack Attack initializer on GitLab -was renamed from [`config/initializers/rack_attack_new.rb` to `config/initializers/rack_attack.rb`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33072). -The rename was part of [deprecating Rack Attack throttles on Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4750). - -If `rack_attack.rb` has been created on your installation, consider creating a backup before updating: - -```shell -cd /home/git/gitlab -cp config/initializers/rack_attack.rb config/initializers/rack_attack_backup.rb -``` - -### 12.10.0 - -- The final patch release (12.10.14) - [has a regression affecting maven package uploads](https://about.gitlab.com/releases/2020/07/06/critical-security-release-gitlab-13-1-3-released/#maven-package-upload-broken-in-121014). - If you use this feature and must stay on 12.10 while preparing to upgrade to 13.0: - - - Upgrade to 12.10.13 instead. - - Upgrade to 13.0.14 as soon as possible. - -- [GitLab 13.0 requires PostgreSQL 11](https://about.gitlab.com/releases/2020/05/22/gitlab-13-0-released/#postgresql-11-is-now-the-minimum-required-version-to-install-gitlab). - - - 12.10 is the final release that shipped with PostgreSQL 9.6, 10, and 11. - - You should make sure that your database is PostgreSQL 11 on GitLab 12.10 before upgrading to 13.0. This upgrade requires downtime. - -### 12.2.0 - -In 12.2.0, we enabled Rails' authenticated cookie encryption. Old sessions are -automatically upgraded. - -However, session cookie downgrades are not supported. So after upgrading to 12.2.0, -any downgrades would result to all sessions being invalidated and users are logged out. - -### 12.1.0 - -- If you are planning to upgrade from `12.0.Z` to `12.10.Z`, it is necessary to - perform an intermediary upgrade to `12.1.Z` before upgrading to `12.10.Z` to - avoid issues like [#215141](https://gitlab.com/gitlab-org/gitlab/-/issues/215141). - -- Support for MySQL was removed in GitLab 12.1. Existing users using GitLab with - MySQL/MariaDB should - [migrate to PostgreSQL](https://gitlab.com/gitlab-org/gitlab/-/blob/v15.6.0-ee/doc/update/mysql_to_postgresql.md) - before upgrading. - -### 12.0.0 - -In 12.0.0 we made various database related changes. These changes require that -users first upgrade to the latest 11.11 patch release. After upgraded to 11.11.Z, -users can upgrade to 12.0.Z. Failure to do so may result in database migrations -not being applied, which could lead to application errors. - -It is also required that you upgrade to 12.0.Z before moving to a later version -of 12.Y. - -Example 1: you are currently using GitLab 11.11.8, which is the latest patch -release for 11.11.Z. You can upgrade as usual to 12.0.Z. - -Example 2: you are currently using a version of GitLab 10.Y. To upgrade, first -upgrade to the last 10.Y release (10.8.7) then the last 11.Y release (11.11.8). -After upgraded to 11.11.8 you can safely upgrade to 12.0.Z. - -See our [documentation on upgrade paths](../policy/maintenance.md#upgrade-recommendations) -for more information. - ### User profile data loss bug in 15.9.x There is a database migration bug in 15.9.0, 15.9.1, and 15.9.2 that can cause data loss from the user profile fields `linkedin`, `twitter`, `skype`, `website_url`, `location`, and `organization`. diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md index d9641e18e8e..21f509ebd33 100644 --- a/doc/update/upgrading_from_source.md +++ b/doc/update/upgrading_from_source.md @@ -327,10 +327,6 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production ``` -NOTE: -If you get any errors concerning Rack attack, see the [13.1](index.md#custom-rack-attack-initializers) -specific changes. - ### 13. Update Gitaly If Gitaly is located on its own server, or you use Gitaly Cluster, see [Gitaly or Gitaly Cluster](zero_downtime.md#gitaly-or-gitaly-cluster) diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index af6c2ec5383..365894e6118 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -243,7 +243,7 @@ function download_chart() { function base_config_changed() { if [ -z "${CI_MERGE_REQUEST_IID}" ]; then return; fi - curl "${CI_API_V4_URL}/projects/${CI_MERGE_REQUEST_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/changes" | jq '.changes | any(.old_path == "scripts/review_apps/base-config.yaml")' + curl "${CI_API_V4_URL}/projects/${CI_MERGE_REQUEST_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/changes" | jq --arg path "${GITLAB_REVIEW_APP_BASE_CONFIG_FILE}" '.changes | any(.old_path == $path)' } function parse_gitaly_image_tag() { @@ -263,7 +263,7 @@ function deploy() { local base_config_file_ref="${CI_DEFAULT_BRANCH}" if [[ "$(base_config_changed)" == "true" ]]; then base_config_file_ref="${CI_COMMIT_SHA}"; fi - local base_config_file="${GITLAB_REPO_URL}/raw/${base_config_file_ref}/scripts/review_apps/base-config.yaml" + local base_config_file="${GITLAB_REPO_URL}/raw/${base_config_file_ref}/${GITLAB_REVIEW_APP_BASE_CONFIG_FILE}" echoinfo "Deploying ${release} to ${CI_ENVIRONMENT_URL} ..." true diff --git a/scripts/utils.sh b/scripts/utils.sh index 4ed56b2de1a..e19622d07c6 100644 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -416,3 +416,38 @@ function url_encode() { -e 's/}/%7d/g' \ -e 's/~/%7e/g' } + +# Download the local gems in `gems` and `vendor/gems` folders from the API. +# +# This is useful if you need to run bundle install while not doing a git clone of the gitlab-org/gitlab repo. +function download_local_gems() { + for folder_path in vendor/gems gems; do + local output="${folder_path}.tar.gz" + + # From https://docs.gitlab.com/ee/api/repositories.html#get-file-archive: + # + # This endpoint can be accessed without authentication if the repository is publicly accessible. + # For GitLab.com users, this endpoint has a rate limit threshold of 5 requests per minute. + # + # We don't want to set a token for public repo (e.g. gitlab-org/gitlab), as 5 requests/minute can + # potentially be reached with many pipelines running in parallel. + local private_token_header="" + if [[ "${CI_PROJECT_VISIBILITY}" != "public" ]]; then + private_token_header="Private-Token: ${PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE}" + fi + + echo "Downloading ${folder_path}" + + url=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/repository/archive + curl -f \ + --get \ + --header "${private_token_header}" \ + --output "${output}" \ + --data-urlencode "sha=${CI_COMMIT_SHA}" \ + --data-urlencode "path=${folder_path}" \ + "${url}" + + tar -zxf "${output}" --strip-component 1 + rm "${output}" + done +} diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb index ecb899a7ca2..0c50b7b2475 100644 --- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb +++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb @@ -112,6 +112,18 @@ RSpec.describe 'User creates branch and merge request on issue page', :js, featu expect(page).to have_selector('.ref-selector', text: branch_name) expect(page).to have_current_path project_tree_path(project, branch_name), ignore_query: true end + + context 'when source branch is non-default' do + let(:source_branch) { 'feature' } + + it 'creates a branch' do + select_dropdown_option('create-branch', branch_name, source_branch) + wait_for_requests + + expect(page).to have_selector('.ref-selector', text: branch_name) + expect(page).to have_current_path project_tree_path(project, branch_name), ignore_query: true + end + end end context 'when branch name is invalid' do @@ -231,12 +243,13 @@ RSpec.describe 'User creates branch and merge request on issue page', :js, featu private - def select_dropdown_option(option, branch_name = nil) + def select_dropdown_option(option, branch_name = nil, source_branch = nil) find('.create-mr-dropdown-wrap .dropdown-toggle').click find("li[data-value='#{option}']").click - if branch_name - find('.js-branch-name').set(branch_name) + if branch_name || source_branch + find('.js-branch-name').set(branch_name) if branch_name + find('.js-ref').set(source_branch) if source_branch # Javascript debounces AJAX calls. # So we have to wait until AJAX requests are started. diff --git a/spec/frontend/issues/create_merge_request_dropdown_spec.js b/spec/frontend/issues/create_merge_request_dropdown_spec.js index 21ae844e2dd..54dc07688df 100644 --- a/spec/frontend/issues/create_merge_request_dropdown_spec.js +++ b/spec/frontend/issues/create_merge_request_dropdown_spec.js @@ -51,31 +51,6 @@ describe('CreateMergeRequestDropdown', () => { }); }); - describe('updateCreatePaths', () => { - it('escapes branch names correctly', () => { - dropdown.createBranchPath = `${TEST_HOST}/branches?branch_name=some-branch&issue=42`; - dropdown.createMrPath = `${TEST_HOST}/create_merge_request?merge_request%5Bsource_branch%5D=test&merge_request%5Btarget_branch%5D=master&merge_request%5Bissue_iid%5D=42`; - - dropdown.updateCreatePaths('branch', 'contains#hash'); - - expect(dropdown.createBranchPath).toBe( - `${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`, - ); - - expect(dropdown.createMrPath).toBe( - `${TEST_HOST}/create_merge_request?merge_request%5Bsource_branch%5D=contains%23hash&merge_request%5Btarget_branch%5D=master&merge_request%5Bissue_iid%5D=42`, - ); - - expect(dropdown.wrapperEl.dataset.createBranchPath).toBe( - `${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`, - ); - - expect(dropdown.wrapperEl.dataset.createMrPath).toBe( - `${TEST_HOST}/create_merge_request?merge_request%5Bsource_branch%5D=contains%23hash&merge_request%5Btarget_branch%5D=master&merge_request%5Bissue_iid%5D=42`, - ); - }); - }); - describe('enable', () => { beforeEach(() => { dropdown.createMergeRequestButton.classList.add('disabled'); diff --git a/spec/frontend/vue_merge_request_widget/mock_data.js b/spec/frontend/vue_merge_request_widget/mock_data.js index 9da687c0ff8..5b3f533f34e 100644 --- a/spec/frontend/vue_merge_request_widget/mock_data.js +++ b/spec/frontend/vue_merge_request_widget/mock_data.js @@ -332,7 +332,7 @@ export default { base_path: 'blob_path', head_path: 'blob_path', }, - codequality_reports_path: 'codequality_reports.json', + codequality_reports_path: '', codequality_help_path: 'code_quality.html', target_branch_path: '/root/acets-app/branches/main', source_branch_path: '/root/acets-app/branches/daaaa', diff --git a/spec/frontend/vue_shared/components/groups_list/groups_list_item_spec.js b/spec/frontend/vue_shared/components/groups_list/groups_list_item_spec.js new file mode 100644 index 00000000000..877de4f4695 --- /dev/null +++ b/spec/frontend/vue_shared/components/groups_list/groups_list_item_spec.js @@ -0,0 +1,182 @@ +import { GlAvatarLabeled, GlIcon } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import GroupsListItem from '~/vue_shared/components/groups_list/groups_list_item.vue'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import { + VISIBILITY_TYPE_ICON, + VISIBILITY_LEVEL_INTERNAL_STRING, + GROUP_VISIBILITY_TYPE, +} from '~/visibility_level/constants'; +import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue'; +import { ACCESS_LEVEL_LABELS } from '~/access_level/constants'; +import { groups } from './mock_data'; + +describe('GroupsListItem', () => { + let wrapper; + + const [group] = groups; + + const defaultPropsData = { group }; + + const createComponent = ({ propsData = {} } = {}) => { + wrapper = mountExtended(GroupsListItem, { + propsData: { ...defaultPropsData, ...propsData }, + directives: { + GlTooltip: createMockDirective('gl-tooltip'), + }, + }); + }; + + const findAvatarLabeled = () => wrapper.findComponent(GlAvatarLabeled); + const findGroupDescription = () => wrapper.findByTestId('group-description'); + const findVisibilityIcon = () => findAvatarLabeled().findComponent(GlIcon); + + it('renders group avatar', () => { + createComponent(); + + const avatarLabeled = findAvatarLabeled(); + + expect(avatarLabeled.props()).toMatchObject({ + label: group.fullName, + labelLink: group.webUrl, + }); + + expect(avatarLabeled.attributes()).toMatchObject({ + 'entity-id': group.id.toString(), + 'entity-name': group.fullName, + shape: 'rect', + }); + }); + + it('renders visibility icon with tooltip', () => { + createComponent(); + + const icon = findAvatarLabeled().findComponent(GlIcon); + const tooltip = getBinding(icon.element, 'gl-tooltip'); + + expect(icon.props('name')).toBe(VISIBILITY_TYPE_ICON[VISIBILITY_LEVEL_INTERNAL_STRING]); + expect(tooltip.value).toBe(GROUP_VISIBILITY_TYPE[VISIBILITY_LEVEL_INTERNAL_STRING]); + }); + + it('renders subgroup count', () => { + createComponent(); + + const countWrapper = wrapper.findByTestId('subgroups-count'); + const tooltip = getBinding(countWrapper.element, 'gl-tooltip'); + + expect(tooltip.value).toBe(GroupsListItem.i18n.subgroups); + expect(countWrapper.text()).toBe(group.descendantGroupsCount.toString()); + expect(countWrapper.findComponent(GlIcon).props('name')).toBe('subgroup'); + }); + + it('renders projects count', () => { + createComponent(); + + const countWrapper = wrapper.findByTestId('projects-count'); + const tooltip = getBinding(countWrapper.element, 'gl-tooltip'); + + expect(tooltip.value).toBe(GroupsListItem.i18n.projects); + expect(countWrapper.text()).toBe(group.projectsCount.toString()); + expect(countWrapper.findComponent(GlIcon).props('name')).toBe('project'); + }); + + it('renders members count', () => { + createComponent(); + + const countWrapper = wrapper.findByTestId('members-count'); + const tooltip = getBinding(countWrapper.element, 'gl-tooltip'); + + expect(tooltip.value).toBe(GroupsListItem.i18n.directMembers); + expect(countWrapper.text()).toBe(group.groupMembersCount.toString()); + expect(countWrapper.findComponent(GlIcon).props('name')).toBe('users'); + }); + + describe('when visibility is not provided', () => { + it('does not render visibility icon', () => { + const { visibility, ...groupWithoutVisibility } = group; + createComponent({ + propsData: { + group: groupWithoutVisibility, + }, + }); + + expect(findVisibilityIcon().exists()).toBe(false); + }); + }); + + it('renders access role badge', () => { + createComponent(); + + expect(findAvatarLabeled().findComponent(UserAccessRoleBadge).text()).toBe( + ACCESS_LEVEL_LABELS[group.accessLevel.integerValue], + ); + }); + + describe('when group has a description', () => { + it('renders description', () => { + const descriptionHtml = '<p>Foo bar</p>'; + + createComponent({ + propsData: { + group: { + ...group, + descriptionHtml, + }, + }, + }); + + expect(findGroupDescription().element.innerHTML).toBe(descriptionHtml); + }); + }); + + describe('when group does not have a description', () => { + it('does not render description', () => { + createComponent({ + propsData: { + group: { + ...group, + descriptionHtml: null, + }, + }, + }); + + expect(findGroupDescription().exists()).toBe(false); + }); + }); + + describe('when `showGroupIcon` prop is `true`', () => { + describe('when `parent` attribute is `null`', () => { + it('shows group icon', () => { + createComponent({ propsData: { showGroupIcon: true } }); + + expect(wrapper.findByTestId('group-icon').exists()).toBe(true); + }); + }); + + describe('when `parent` attribute is set', () => { + it('shows subgroup icon', () => { + createComponent({ + propsData: { + showGroupIcon: true, + group: { + ...group, + parent: { + id: 'gid://gitlab/Group/35', + }, + }, + }, + }); + + expect(wrapper.findByTestId('subgroup-icon').exists()).toBe(true); + }); + }); + }); + + describe('when `showGroupIcon` prop is `false`', () => { + it('does not show group icon', () => { + createComponent(); + + expect(wrapper.findByTestId('group-icon').exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/vue_shared/components/groups_list/groups_list_spec.js b/spec/frontend/vue_shared/components/groups_list/groups_list_spec.js new file mode 100644 index 00000000000..c65aa347bcf --- /dev/null +++ b/spec/frontend/vue_shared/components/groups_list/groups_list_spec.js @@ -0,0 +1,34 @@ +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import GroupsList from '~/vue_shared/components/groups_list/groups_list.vue'; +import GroupsListItem from '~/vue_shared/components/groups_list/groups_list_item.vue'; +import { groups } from './mock_data'; + +describe('GroupsList', () => { + let wrapper; + + const defaultPropsData = { + groups, + }; + + const createComponent = () => { + wrapper = shallowMountExtended(GroupsList, { + propsData: defaultPropsData, + }); + }; + + it('renders list with `GroupsListItem` component', () => { + createComponent(); + + const groupsListItemWrappers = wrapper.findAllComponents(GroupsListItem).wrappers; + const expectedProps = groupsListItemWrappers.map((groupsListItemWrapper) => + groupsListItemWrapper.props(), + ); + + expect(expectedProps).toEqual( + defaultPropsData.groups.map((group) => ({ + group, + showGroupIcon: false, + })), + ); + }); +}); diff --git a/spec/frontend/vue_shared/components/groups_list/mock_data.js b/spec/frontend/vue_shared/components/groups_list/mock_data.js new file mode 100644 index 00000000000..0dad27f8311 --- /dev/null +++ b/spec/frontend/vue_shared/components/groups_list/mock_data.js @@ -0,0 +1,35 @@ +export const groups = [ + { + id: 1, + fullName: 'Gitlab Org', + parent: null, + webUrl: 'http://127.0.0.1:3000/groups/gitlab-org', + descriptionHtml: + '<p data-sourcepos="1:1-1:64" dir="auto">Dolorem dolorem omnis impedit cupiditate pariatur officia velit. Fusce eget orci a ipsum tempus vehicula. Donec rhoncus ante sed lacus pharetra, vitae imperdiet felis lobortis. Donec maximus dapibus orci, sit amet euismod dolor rhoncus vel. In nec mauris nibh.</p>', + avatarUrl: null, + descendantGroupsCount: 1, + projectsCount: 1, + groupMembersCount: 2, + visibility: 'internal', + accessLevel: { + integerValue: 10, + }, + }, + { + id: 2, + fullName: 'Gitlab Org / test subgroup', + parent: { + id: 1, + }, + webUrl: 'http://127.0.0.1:3000/groups/gitlab-org/test-subgroup', + descriptionHtml: '', + avatarUrl: null, + descendantGroupsCount: 4, + projectsCount: 4, + groupMembersCount: 4, + visibility: 'private', + accessLevel: { + integerValue: 20, + }, + }, +]; diff --git a/spec/lib/gitlab/tracking/standard_context_spec.rb b/spec/lib/gitlab/tracking/standard_context_spec.rb index e1ae362e797..c44cfdea1cd 100644 --- a/spec/lib/gitlab/tracking/standard_context_spec.rb +++ b/spec/lib/gitlab/tracking/standard_context_spec.rb @@ -3,10 +3,6 @@ require 'spec_helper' RSpec.describe Gitlab::Tracking::StandardContext do - let_it_be(:project) { create(:project) } - let_it_be(:namespace) { create(:namespace) } - let_it_be(:user) { create(:user) } - let(:snowplow_context) { subject.to_context } describe '#to_context' do @@ -62,21 +58,27 @@ RSpec.describe Gitlab::Tracking::StandardContext do expect(snowplow_context.to_json.dig(:data, :context_generated_at)).to eq(Time.current) end - context 'plan' do - context 'when namespace is not available' do - it 'is nil' do - expect(snowplow_context.to_json.dig(:data, :plan)).to be_nil - end - end + it 'contains standard properties' do + standard_properties = [:user_id, :project_id, :namespace_id, :plan] + expect(snowplow_context.to_json[:data].keys).to include(*standard_properties) + end - context 'when namespace is available' do - let(:namespace) { create(:namespace) } + context 'with standard properties' do + let(:user_id) { 1 } + let(:project_id) { 2 } + let(:namespace_id) { 3 } + let(:plan_name) { "plan name" } - subject { described_class.new(namespace_id: namespace.id, plan_name: namespace.actual_plan_name) } + subject do + described_class.new(user_id: user_id, project_id: project_id, namespace_id: namespace_id, plan_name: plan_name) + end - it 'contains plan name' do - expect(snowplow_context.to_json.dig(:data, :plan)).to eq(Plan::DEFAULT) - end + it 'holds the correct values', :aggregate_failures do + json_data = snowplow_context.to_json.fetch(:data) + expect(json_data[:user_id]).to eq(user_id) + expect(json_data[:project_id]).to eq(project_id) + expect(json_data[:namespace_id]).to eq(namespace_id) + expect(json_data[:plan]).to eq(plan_name) end end @@ -95,24 +97,12 @@ RSpec.describe Gitlab::Tracking::StandardContext do end context 'with incorrect argument type' do - subject { described_class.new(project_id: create(:group)) } + subject { described_class.new(project_id: "a string") } it 'does call `track_and_raise_for_dev_exception`' do expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception) snowplow_context end end - - it 'contains user id' do - expect(snowplow_context.to_json[:data].keys).to include(:user_id) - end - - it 'contains namespace and project ids' do - expect(snowplow_context.to_json[:data].keys).to include(:project_id, :namespace_id) - end - - it 'accepts just project id as integer' do - expect { described_class.new(project: 1).to_context }.not_to raise_error - end end end |