diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-30 15:11:08 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-30 15:11:08 +0000 |
commit | 2cba3ab8e7b2d6b32be6910d15b53860f2c2140e (patch) | |
tree | 28bd2516c6030e038bb8ea1143cc6ec11f953fd0 | |
parent | a10d237d37e78cbe84f72fffaeff74dc73f1e68f (diff) | |
download | gitlab-ce-2cba3ab8e7b2d6b32be6910d15b53860f2c2140e.tar.gz |
Add latest changes from gitlab-org/gitlab@master
60 files changed, 471 insertions, 354 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 38115beec69..7e4f1a02646 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,7 +38,7 @@ workflow: when: never # For merge requests, create a pipeline. - if: '$CI_MERGE_REQUEST_IID' - # For `master` branch, create a pipeline (this includes on schedules, pushes, merges, etc.). + # For `$CI_DEFAULT_BRANCH` branch, create a pipeline (this includes on schedules, pushes, merges, etc.). - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' # For tags, create a pipeline. - if: '$CI_COMMIT_TAG' diff --git a/.gitlab/ci/cache-repo.gitlab-ci.yml b/.gitlab/ci/cache-repo.gitlab-ci.yml index 48724f7e65c..475cbca3156 100644 --- a/.gitlab/ci/cache-repo.gitlab-ci.yml +++ b/.gitlab/ci/cache-repo.gitlab-ci.yml @@ -1,4 +1,4 @@ -# Builds a cached .tar.gz of the master branch with full history and +# Builds a cached .tar.gz of the $CI_DEFAULT_BRANCH branch with full history and # uploads it to Google Cloud Storage. This archive is downloaded by a # script defined by a CI/CD variable named CI_PRE_CLONE_SCRIPT. This has # two benefits: diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index 910a58bcd0e..e739b046ff7 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -246,7 +246,7 @@ coverage-frontend: extends: - .default-retry - .yarn-cache - - .frontend:rules:ee-mr-and-master-only + - .frontend:rules:ee-mr-and-default-branch-only needs: ["jest"] stage: post-test before_script: diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index f4ea6e3ae01..6b5c617dccc 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -142,7 +142,7 @@ ############################ ####################################################### -# EE/FOSS: default refs (MRs, master, schedules) jobs # +# EE/FOSS: default refs (MRs, default branch, schedules) jobs # setup-test-env: extends: - .rails-job-base @@ -347,7 +347,7 @@ db:migrate:reset: db:check-schema: extends: - .db-job-base - - .rails:rules:ee-mr-and-master-only + - .rails:rules:ee-mr-and-default-branch-only script: - source scripts/schema_changed.sh @@ -538,11 +538,11 @@ rspec:feature-flags: run_timed_command "bundle exec scripts/used-feature-flags"; fi -# EE/FOSS: default refs (MRs, master, schedules) jobs # +# EE/FOSS: default refs (MRs, default branch, schedules) jobs # ####################################################### ################################################## -# EE: default refs (MRs, master, schedules) jobs # +# EE: default refs (MRs, default branch, schedules) jobs # rspec migration pg11-as-if-foss: extends: - .rspec-base-pg11-as-if-foss @@ -685,81 +685,81 @@ db:rollback geo: script: - bundle exec rake geo:db:migrate VERSION=20170627195211 - bundle exec rake geo:db:migrate -# EE: default refs (MRs, master, schedules) jobs # +# EE: default refs (MRs, default branch, schedules) jobs # ################################################## ########################################## -# EE/FOSS: master nightly scheduled jobs # +# EE/FOSS: default branch nightly scheduled jobs # rspec migration pg12: extends: - .rspec-base-pg12 - .rspec-base-migration - - .rails:rules:master-schedule-nightly--code-backstage + - .rails:rules:default-branch-schedule-nightly--code-backstage - .rspec-migration-parallel rspec unit pg12: extends: - .rspec-base-pg12 - - .rails:rules:master-schedule-nightly--code-backstage + - .rails:rules:default-branch-schedule-nightly--code-backstage - .rspec-unit-parallel rspec integration pg12: extends: - .rspec-base-pg12 - - .rails:rules:master-schedule-nightly--code-backstage + - .rails:rules:default-branch-schedule-nightly--code-backstage - .rspec-integration-parallel rspec system pg12: extends: - .rspec-base-pg12 - - .rails:rules:master-schedule-nightly--code-backstage + - .rails:rules:default-branch-schedule-nightly--code-backstage - .rspec-system-parallel -# EE/FOSS: master nightly scheduled jobs # +# EE/FOSS: default branch nightly scheduled jobs # ########################################## ##################################### -# EE: master nightly scheduled jobs # +# EE: default branch nightly scheduled jobs # rspec-ee migration pg12: extends: - .rspec-ee-base-pg12 - .rspec-base-migration - - .rails:rules:master-schedule-nightly--code-backstage-ee-only + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - .rspec-ee-migration-parallel rspec-ee unit pg12: extends: - .rspec-ee-base-pg12 - - .rails:rules:master-schedule-nightly--code-backstage-ee-only + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - .rspec-ee-unit-parallel rspec-ee integration pg12: extends: - .rspec-ee-base-pg12 - - .rails:rules:master-schedule-nightly--code-backstage-ee-only + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - .rspec-ee-integration-parallel rspec-ee system pg12: extends: - .rspec-ee-base-pg12 - - .rails:rules:master-schedule-nightly--code-backstage-ee-only + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - .rspec-ee-system-parallel rspec-ee unit pg12 geo: extends: - .rspec-ee-base-geo-pg12 - - .rails:rules:master-schedule-nightly--code-backstage-ee-only + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - .rspec-ee-unit-geo-parallel rspec-ee integration pg12 geo: extends: - .rspec-ee-base-geo-pg12 - - .rails:rules:master-schedule-nightly--code-backstage-ee-only + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only rspec-ee system pg12 geo: extends: - .rspec-ee-base-geo-pg12 - - .rails:rules:master-schedule-nightly--code-backstage-ee-only -# EE: master nightly scheduled jobs # + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only +# EE: default branch nightly scheduled jobs # ##################################### ################################################## diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 5464409254a..39522f7b60c 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -11,25 +11,25 @@ if: '$CI_PROJECT_NAME != "gitlab-foss" && $CI_PROJECT_NAME != "gitlab-ce" && $CI_PROJECT_NAME != "gitlabhq"' .if-default-refs: &if-default-refs - if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME == "main" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG || $FORCE_GITLAB_CI' + if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG || $FORCE_GITLAB_CI' -.if-master-refs: &if-master-refs - if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME == "main"' +.if-default-branch-refs: &if-default-branch-refs + if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH' -.if-master-push: &if-master-push - if: '($CI_COMMIT_BRANCH == "master" || $CI_COMMIT_REF_NAME == "main") && $CI_PIPELINE_SOURCE == "push"' +.if-default-branch-push: &if-default-branch-push + if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push"' -.if-master-schedule-2-hourly: &if-master-schedule-2-hourly - if: '($CI_COMMIT_BRANCH == "master" || $CI_COMMIT_REF_NAME == "main") && $CI_PIPELINE_SOURCE == "schedule" && $FREQUENCY == "2-hourly"' +.if-default-branch-schedule-2-hourly: &if-default-branch-schedule-2-hourly + if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $FREQUENCY == "2-hourly"' -.if-master-schedule-nightly: &if-master-schedule-nightly - if: '($CI_COMMIT_BRANCH == "master" || $CI_COMMIT_REF_NAME == "main") && $CI_PIPELINE_SOURCE == "schedule" && $FREQUENCY == "nightly"' +.if-default-branch-schedule-nightly: &if-default-branch-schedule-nightly + if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $FREQUENCY == "nightly"' .if-auto-deploy-branches: &if-auto-deploy-branches if: '$CI_COMMIT_BRANCH =~ /^\d+-\d+-auto-deploy-\d+$/' -.if-master-or-tag: &if-master-or-tag - if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME == "main" || $CI_COMMIT_TAG' +.if-default-branch-or-tag: &if-default-branch-or-tag + if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG' .if-merge-request: &if-merge-request if: '$CI_MERGE_REQUEST_IID' @@ -52,8 +52,8 @@ .if-dot-com-gitlab-org-schedule: &if-dot-com-gitlab-org-schedule if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org" && $CI_PIPELINE_SOURCE == "schedule"' -.if-dot-com-gitlab-org-master: &if-dot-com-gitlab-org-master - if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org" && ($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME == "main")' +.if-dot-com-gitlab-org-default-branch: &if-dot-com-gitlab-org-default-branch + if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH' .if-dot-com-gitlab-org-merge-request: &if-dot-com-gitlab-org-merge-request if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org" && $CI_MERGE_REQUEST_IID' @@ -293,7 +293,7 @@ ################ .shared:rules:update-cache: rules: - - <<: *if-master-schedule-2-hourly + - <<: *if-default-branch-schedule-2-hourly - <<: *if-security-schedule - <<: *if-merge-request-title-update-caches @@ -435,26 +435,26 @@ - <<: *if-merge-request changes: *frontend-patterns -.frontend:rules:ee-mr-and-master-only: +.frontend:rules:ee-mr-and-default-branch-only: rules: - <<: *if-not-ee when: never - <<: *if-merge-request changes: *code-backstage-patterns when: always - - <<: *if-master-refs + - <<: *if-default-branch-refs changes: *code-backstage-patterns .frontend:rules:qa-frontend-node: rules: - - <<: *if-master-refs + - <<: *if-default-branch-refs changes: *frontend-dependency-patterns - <<: *if-merge-request changes: *frontend-dependency-patterns .frontend:rules:qa-frontend-node-latest: rules: - - <<: *if-master-refs + - <<: *if-default-branch-refs changes: *frontend-dependency-patterns allow_failure: true - <<: *if-merge-request @@ -465,7 +465,7 @@ rules: - <<: *if-not-canonical-namespace when: never - - if: '$DANGER_GITLAB_API_TOKEN && $CI_MERGE_REQUEST_IID && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")' + - if: '$DANGER_GITLAB_API_TOKEN && $CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH' changes: *frontend-patterns allow_failure: true @@ -485,7 +485,7 @@ rules: - <<: *if-not-ee when: never - - <<: *if-master-schedule-2-hourly + - <<: *if-default-branch-schedule-2-hourly ############ # QA rules # @@ -825,14 +825,14 @@ - changes: *db-library-patterns - <<: *if-merge-request-title-run-all-rspec -.rails:rules:ee-mr-and-master-only: +.rails:rules:ee-mr-and-default-branch-only: rules: - <<: *if-not-ee when: never - <<: *if-merge-request-title-run-all-rspec - <<: *if-merge-request changes: *code-backstage-patterns - - <<: *if-master-refs + - <<: *if-default-branch-refs changes: *code-backstage-patterns .rails:rules:detect-tests: @@ -888,7 +888,7 @@ rules: - <<: *if-not-ee when: never - - <<: *if-master-schedule-nightly + - <<: *if-default-branch-schedule-nightly - <<: *if-merge-request-title-run-all-rspec .rails:rules:rspec-coverage: @@ -898,7 +898,7 @@ - <<: *if-merge-request changes: *code-backstage-patterns when: always - - <<: *if-master-schedule-2-hourly + - <<: *if-default-branch-schedule-2-hourly - <<: *if-merge-request-title-run-all-rspec when: always @@ -906,21 +906,21 @@ rules: - <<: *if-not-ee when: never - - <<: *if-master-schedule-2-hourly + - <<: *if-default-branch-schedule-2-hourly allow_failure: true - <<: *if-merge-request-title-run-all-rspec -.rails:rules:master-schedule-nightly--code-backstage: +.rails:rules:default-branch-schedule-nightly--code-backstage: rules: - - <<: *if-master-schedule-nightly + - <<: *if-default-branch-schedule-nightly - <<: *if-merge-request changes: [".gitlab/ci/rails.gitlab-ci.yml"] -.rails:rules:master-schedule-nightly--code-backstage-ee-only: +.rails:rules:default-branch-schedule-nightly--code-backstage-ee-only: rules: - <<: *if-not-ee when: never - - <<: *if-master-schedule-nightly + - <<: *if-default-branch-schedule-nightly - <<: *if-merge-request changes: [".gitlab/ci/rails.gitlab-ci.yml"] @@ -946,7 +946,7 @@ rules: - if: '$CODE_QUALITY_DISABLED' when: never - # - <<: *if-master-refs # To be done in a later iteration: https://gitlab.com/gitlab-org/gitlab/issues/31160#note_278188255 + # - <<: *if-default-branch-refs # To be done in a later iteration: https://gitlab.com/gitlab-org/gitlab/issues/31160#note_278188255 - <<: *if-default-refs changes: *code-backstage-patterns allow_failure: true @@ -955,7 +955,7 @@ rules: - if: '$SAST_DISABLED || $GITLAB_FEATURES !~ /\bsast\b/' when: never - # - <<: *if-master-refs # To be done in a later iteration: https://gitlab.com/gitlab-org/gitlab/issues/31160#note_278188255 + # - <<: *if-default-branch-refs # To be done in a later iteration: https://gitlab.com/gitlab-org/gitlab/issues/31160#note_278188255 - <<: *if-default-refs changes: *code-backstage-qa-patterns allow_failure: true @@ -964,7 +964,7 @@ rules: - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/' when: never - # - <<: *if-master-refs # To be done in a later iteration: https://gitlab.com/gitlab-org/gitlab/issues/31160#note_278188255 + # - <<: *if-default-branch-refs # To be done in a later iteration: https://gitlab.com/gitlab-org/gitlab/issues/31160#note_278188255 - <<: *if-default-refs changes: *code-backstage-qa-patterns allow_failure: true @@ -985,7 +985,7 @@ rules: - if: '$DAST_DISABLED || $GITLAB_FEATURES !~ /\bdast\b/' when: never - - <<: *if-master-schedule-nightly + - <<: *if-default-branch-schedule-nightly allow_failure: true .reports:rules:license_scanning: @@ -1119,13 +1119,13 @@ rules: - <<: *if-not-canonical-namespace when: never - - <<: *if-master-or-tag + - <<: *if-default-branch-or-tag changes: *code-backstage-qa-patterns when: on_success .setup:rules:dont-interrupt-me: rules: - - <<: *if-master-or-tag + - <<: *if-default-branch-or-tag allow_failure: true - <<: *if-auto-deploy-branches allow_failure: true diff --git a/.gitlab/issue_templates/Experiment Successful Cleanup.md b/.gitlab/issue_templates/Experiment Successful Cleanup.md index afe4793cdfc..42f26342342 100644 --- a/.gitlab/issue_templates/Experiment Successful Cleanup.md +++ b/.gitlab/issue_templates/Experiment Successful Cleanup.md @@ -15,5 +15,6 @@ The changes need to become an official part of the product. - [ ] Optional: Migrate experiment to a default enabled [feature flag](https://docs.gitlab.com/ee/development/feature_flags) for one milestone and add a changelog. Converting to a feature flag can be skipped at the ICs discretion if risk is deemed low with consideration to both SaaS and (if applicable) self managed - [ ] In the next milestone, [remove the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) if applicable - [ ] After the flag removal is deployed, [clean up the feature/experiment feature flags](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel +- [ ] Ensure the corresponding [Experiment Tracking](https://gitlab.com/groups/gitlab-org/-/boards/1352542?label_name[]=devops%3A%3Agrowth&label_name[]=growth%20experiment&label_name[]=experiment%20tracking) issue is updated /label ~"feature" ~"feature::maintenance" ~"workflow::scheduling" ~"growth experiment" ~"feature flag" diff --git a/.gitlab/issue_templates/experiment_tracking_template.md b/.gitlab/issue_templates/experiment_tracking_template.md index 432ae57e594..6d05932b3d6 100644 --- a/.gitlab/issue_templates/experiment_tracking_template.md +++ b/.gitlab/issue_templates/experiment_tracking_template.md @@ -81,6 +81,7 @@ If applicable, any groups/projects that are happy to have this feature turned on - [ ] Announce on the issue that the flag has been enabled - [ ] Remove experiment code and feature flag and add changelog entry - a separate [cleanup issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Experiment%20Successful%20Cleanup) might be required - [ ] After the flag removal is deployed, [clean up the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel +- [ ] Assign to the product manager to update the [knowledge base](https://about.gitlab.com/direction/growth/#growth-insights-knowledge-base) (if applicable) ## Rollback Steps diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index d4875d7e89c..ff042f763a0 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -177,9 +177,6 @@ Rails/SaveBang: - 'qa/qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_http_spec.rb' - 'qa/qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb' - 'spec/controllers/abuse_reports_controller_spec.rb' - - 'spec/controllers/admin/impersonations_controller_spec.rb' - - 'spec/controllers/admin/runners_controller_spec.rb' - - 'spec/controllers/admin/services_controller_spec.rb' - 'spec/controllers/boards/issues_controller_spec.rb' - 'spec/controllers/groups/milestones_controller_spec.rb' - 'spec/controllers/groups/runners_controller_spec.rb' diff --git a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue index 6fee40fb061..33e92bfab44 100644 --- a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue +++ b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue @@ -108,7 +108,7 @@ export default { show :target="target" placement="right" - trigger="manual" + triggers="manual" container="viewport" :css-classes="['suggest-gitlab-ci-yml', 'ml-4']" > diff --git a/app/assets/javascripts/boards/components/board_add_new_column.vue b/app/assets/javascripts/boards/components/board_add_new_column.vue index a77696b70cc..d4b559add6e 100644 --- a/app/assets/javascripts/boards/components/board_add_new_column.vue +++ b/app/assets/javascripts/boards/components/board_add_new_column.vue @@ -5,7 +5,6 @@ import BoardAddNewColumnForm from '~/boards/components/board_add_new_column_form import { ListType } from '~/boards/constants'; import boardsStore from '~/boards/stores/boards_store'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { isScopedLabel } from '~/lib/utils/common_utils'; export default { components: { @@ -20,17 +19,12 @@ export default { data() { return { selectedId: null, + selectedLabel: null, }; }, computed: { ...mapState(['labels', 'labelsLoading']), ...mapGetters(['getListByLabelId', 'shouldUseGraphQL']), - selectedLabel() { - if (!this.selectedId) { - return null; - } - return this.labels.find(({ id }) => id === this.selectedId); - }, columnForSelected() { return this.getListByLabelId(this.selectedId); }, @@ -83,8 +77,13 @@ export default { this.fetchLabels(searchTerm); }, - showScopedLabels(label) { - return this.scopedLabelsAvailable && isScopedLabel(label); + setSelectedItem(selectedId) { + const label = this.labels.find(({ id }) => id === selectedId); + if (!selectedId || !label) { + this.selectedLabel = null; + } else { + this.selectedLabel = { ...label }; + } }, }, }; @@ -116,6 +115,7 @@ export default { v-if="labels.length > 0" v-model="selectedId" class="gl-overflow-y-auto gl-px-5 gl-pt-3" + @change="setSelectedItem" > <label v-for="label in labels" diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js index 1fc2a684e95..dec3f87b03e 100644 --- a/app/assets/javascripts/diffs/store/getters.js +++ b/app/assets/javascripts/diffs/store/getters.js @@ -156,16 +156,16 @@ export const diffLines = (state) => (file, unifiedDiffComponents) => { ); }; -export function suggestionCommitMessage(state) { +export function suggestionCommitMessage(state, _, rootState) { return (values = {}) => computeSuggestionCommitMessage({ message: state.defaultSuggestionCommitMessage, values: { - branch_name: state.branchName, - project_path: state.projectPath, - project_name: state.projectName, - username: state.username, - user_full_name: state.userFullName, + branch_name: rootState.page.mrMetadata.branch_name, + project_path: rootState.page.mrMetadata.project_path, + project_name: rootState.page.mrMetadata.project_name, + username: rootState.page.mrMetadata.username, + user_full_name: rootState.page.mrMetadata.user_full_name, ...values, }, }); diff --git a/app/assets/javascripts/notes/components/note_attachment.vue b/app/assets/javascripts/notes/components/note_attachment.vue index b20facc4032..c49f3e2de99 100644 --- a/app/assets/javascripts/notes/components/note_attachment.vue +++ b/app/assets/javascripts/notes/components/note_attachment.vue @@ -24,7 +24,7 @@ export default { target="_blank" rel="noopener noreferrer" > - <img :src="attachment.url" class="note-image-attach" /> + <img :src="attachment.url" class="note-image-attach col-lg-4" /> </a> <div class="attachment"> <a diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 2d04354a99d..9d437531e6d 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -179,8 +179,9 @@ } input[type='submit'] { - @extend .btn-block; margin-bottom: 0; + display: block; + width: 100%; } .devise-errors { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 23e368a2e73..36d39c1a613 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -236,8 +236,8 @@ $tabs-holder-z-index: 250; } .label-branch { - @extend .ref-name; - + @include gl-font-monospace; + font-size: 95%; color: $gl-text-color; font-weight: normal; overflow: hidden; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index cb5050fc578..59768f4cda8 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -48,7 +48,6 @@ } .note-image-attach { - @extend .col-lg-4; margin-left: 45px; float: none; } diff --git a/app/assets/stylesheets/pages/notifications.scss b/app/assets/stylesheets/pages/notifications.scss index 33ab42b5511..298de33888d 100644 --- a/app/assets/stylesheets/pages/notifications.scss +++ b/app/assets/stylesheets/pages/notifications.scss @@ -1,8 +1,4 @@ .notification-list-item { - .dropdown-menu { - @extend .dropdown-menu-right; - } - @include media-breakpoint-down(sm) { .notification-dropdown { width: 100%; diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb index 2417b1e0771..a6ecd835527 100644 --- a/app/finders/group_members_finder.rb +++ b/app/finders/group_members_finder.rb @@ -21,28 +21,13 @@ class GroupMembersFinder < UnionFinder end def execute(include_relations: DEFAULT_RELATIONS) - group_members = group_members_list - relations = [] + return filter_members(group_members_list) if include_relations == [:direct] - return filter_members(group_members) if include_relations == [:direct] + groups = groups_by_relations(include_relations) + return GroupMember.none unless groups - relations << group_members if include_relations.include?(:direct) + members = all_group_members(groups).distinct_on_user_with_max_access_level - if include_relations.include?(:inherited) && group.parent - parents_members = relation_group_members(group.ancestors) - - relations << parents_members - end - - if include_relations.include?(:descendants) - descendant_members = relation_group_members(group.descendants) - - relations << descendant_members - end - - return GroupMember.none if relations.empty? - - members = find_union(relations, GroupMember) filter_members(members) end @@ -50,6 +35,25 @@ class GroupMembersFinder < UnionFinder attr_reader :user, :group + def groups_by_relations(include_relations) + case include_relations.sort + when [:inherited] + group.ancestors + when [:descendants] + group.descendants + when [:direct, :inherited] + group.self_and_ancestors + when [:descendants, :direct] + group.self_and_descendants + when [:descendants, :inherited] + find_union([group.ancestors, group.descendants], Group) + when [:descendants, :direct, :inherited] + group.self_and_hierarchy + else + nil + end + end + def filter_members(members) members = members.search(params[:search]) if params[:search].present? members = members.sort_by_attribute(params[:sort]) if params[:sort].present? @@ -69,17 +73,13 @@ class GroupMembersFinder < UnionFinder group.members end - def relation_group_members(relation) - all_group_members(relation).non_minimal_access + def all_group_members(groups) + members_of_groups(groups).non_minimal_access end - # rubocop: disable CodeReuse/ActiveRecord - def all_group_members(relation) - GroupMember.non_request - .where(source_id: relation.select(:id)) - .where.not(user_id: group.users.select(:id)) + def members_of_groups(groups) + GroupMember.non_request.of_groups(groups) end - # rubocop: enable CodeReuse/ActiveRecord end GroupMembersFinder.prepend_if_ee('EE::GroupMembersFinder') diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 423914139ba..446e6174df2 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -226,7 +226,7 @@ class Deployment < ApplicationRecord end def update_merge_request_metrics! - return unless environment.update_merge_request_metrics? && success? + return unless environment.production? && success? merge_requests = project.merge_requests .joins(:metrics) diff --git a/app/models/environment.rb b/app/models/environment.rb index d89909a71a2..96b44c9ac0a 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -11,8 +11,6 @@ class Environment < ApplicationRecord self.reactive_cache_hard_limit = 10.megabytes self.reactive_cache_work_type = :external_dependency - PRODUCTION_ENVIRONMENT_IDENTIFIERS = %w[prod production].freeze - belongs_to :project, required: true use_fast_destroy :all_deployments @@ -251,10 +249,6 @@ class Environment < ApplicationRecord last_deployment.try(:created_at) end - def update_merge_request_metrics? - PRODUCTION_ENVIRONMENT_IDENTIFIERS.include?(folder_name.downcase) - end - def ref_path "refs/#{Repository::REF_ENVIRONMENTS}/#{slug}" end diff --git a/app/models/member.rb b/app/models/member.rb index 38574d67cb6..3f6845cc454 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -137,6 +137,12 @@ class Member < ApplicationRecord scope :with_source_id, ->(source_id) { where(source_id: source_id) } scope :including_source, -> { includes(:source) } + scope :distinct_on_user_with_max_access_level, -> do + distinct_members = select('DISTINCT ON (user_id, invite_email) *') + .order('user_id, invite_email, access_level DESC, expires_at DESC, created_at ASC') + Member.from(distinct_members, :members) + end + scope :order_name_asc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'ASC')) } scope :order_name_desc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'DESC')) } scope :order_recent_sign_in, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.last_sign_in_at', 'DESC')) } diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml index 2fa595503e5..d08c3d5ba41 100644 --- a/app/views/events/event/_note.html.haml +++ b/app/views/events/event/_note.html.haml @@ -22,7 +22,7 @@ - if note.attachment.url - if note.attachment.image? = link_to note.attachment.url, target: '_blank' do - = image_tag note.attachment.url, class: 'note-image-attach' + = image_tag note.attachment.url, class: 'note-image-attach col-lg-4' - else = link_to note.attachment.url, target: '_blank', class: 'note-file-attach' do = sprite_icon("paperclip") diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml index e02f126d165..ee296258d04 100644 --- a/app/views/projects/merge_requests/conflicts/show.html.haml +++ b/app/views/projects/merge_requests/conflicts/show.html.haml @@ -1,4 +1,4 @@ -- page_title _("Merge Conflicts"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge Requests") +- page_title _("Merge Conflicts"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge requests") - add_page_specific_style 'page_bundles/merge_conflicts' = render "projects/merge_requests/mr_title" diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml index c7861f4a01a..7082bf4b8b0 100644 --- a/app/views/projects/merge_requests/creations/_new_compare.html.haml +++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml @@ -1,5 +1,5 @@ %h3.page-title - New Merge Request + New merge request = form_for [@project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form js-requires-input" } do |f| - if params[:nav_source].present? diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml index 3631ddd5c3e..a8facf1c6fd 100644 --- a/app/views/projects/merge_requests/creations/_new_submit.html.haml +++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml @@ -1,5 +1,5 @@ %h3.page-title - New Merge Request + New merge request = form_for [@project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f| = render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits, presenter: @mr_presenter = f.hidden_field :source_project_id diff --git a/app/views/projects/merge_requests/creations/new.html.haml b/app/views/projects/merge_requests/creations/new.html.haml index 0741b24a5a1..6a8894384df 100644 --- a/app/views/projects/merge_requests/creations/new.html.haml +++ b/app/views/projects/merge_requests/creations/new.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs _("Merge Requests"), project_merge_requests_path(@project) +- add_to_breadcrumbs _("Merge requests"), project_merge_requests_path(@project) - breadcrumb_title _("New") -- page_title _("New Merge Request") +- page_title _("New merge request") - add_page_specific_style 'page_bundles/pipelines' - add_page_specific_style 'page_bundles/ci_status' diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml index a4bb790ce0b..019015a4d86 100644 --- a/app/views/projects/merge_requests/edit.html.haml +++ b/app/views/projects/merge_requests/edit.html.haml @@ -1,5 +1,5 @@ -- page_title _("Edit"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge Requests") +- page_title _("Edit"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge requests") %h3.page-title - Edit Merge Request #{@merge_request.to_reference} + Edit merge request #{@merge_request.to_reference} = render 'form' diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 62a251c7015..22d78418c5b 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -3,7 +3,7 @@ - new_merge_request_path = project_new_merge_request_path(merge_project) if merge_project - issuable_type = 'merge_request' -- page_title _("Merge Requests") +- page_title _("Merge requests") - new_merge_request_email = @project.new_issuable_address(current_user, 'merge_request') = render 'projects/last_push' diff --git a/app/views/projects/merge_requests/invalid.html.haml b/app/views/projects/merge_requests/invalid.html.haml index df942c11883..f0bf5af7732 100644 --- a/app/views/projects/merge_requests/invalid.html.haml +++ b/app/views/projects/merge_requests/invalid.html.haml @@ -1,4 +1,4 @@ -- page_title "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge Requests") +- page_title "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge requests") - badge_css_classes = "badge gl-text-white" - badge_info_css_classes = "#{badge_css_classes} badge-info" - badge_inverse_css_classes = "#{badge_css_classes} badge-inverse" @@ -25,4 +25,4 @@ of internal error %strong - Please close Merge Request or change branches with existing one + Please close merge request or change branches with existing one diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index 36491b0d8b8..24594b732a4 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -1,8 +1,8 @@ - @gfm_form = true - @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}" -- add_to_breadcrumbs _("Merge Requests"), project_merge_requests_path(@project) +- add_to_breadcrumbs _("Merge requests"), project_merge_requests_path(@project) - breadcrumb_title @merge_request.to_reference -- page_title "#{@merge_request.title} (#{@merge_request.to_reference})", _("Merge Requests") +- page_title "#{@merge_request.title} (#{@merge_request.to_reference})", _("Merge requests") - page_description @merge_request.description_html - page_card_attributes @merge_request.card_attributes - suggest_changes_help_path = help_page_path('user/discussions/index.md', anchor: 'suggest-changes') diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index f1352be28e3..6549c86ab29 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -69,7 +69,7 @@ .note-attachment - if note.attachment.image? = link_to note.attachment.url, target: '_blank' do - = image_tag note.attachment.url, class: 'note-image-attach' + = image_tag note.attachment.url, class: 'note-image-attach col-lg-4' .attachment = link_to note.attachment.url, target: '_blank' do = sprite_icon('paperclip') diff --git a/changelogs/unreleased/273034-support-semver-on-generic-packages.yml b/changelogs/unreleased/273034-support-semver-on-generic-packages.yml new file mode 100644 index 00000000000..14fabea7c1b --- /dev/null +++ b/changelogs/unreleased/273034-support-semver-on-generic-packages.yml @@ -0,0 +1,5 @@ +--- +title: Relax version validation on generic packages +merge_request: 56755 +author: +type: changed diff --git a/changelogs/unreleased/fix-group_members_max_access_level.yml b/changelogs/unreleased/fix-group_members_max_access_level.yml new file mode 100644 index 00000000000..ee7408c3a51 --- /dev/null +++ b/changelogs/unreleased/fix-group_members_max_access_level.yml @@ -0,0 +1,5 @@ +--- +title: Fix derivation of effective permissions (access level) of group members +merge_request: 56677 +author: Jonas Wälter @wwwjon +type: fixed diff --git a/changelogs/unreleased/fix-obsolte-production-identifier.yml b/changelogs/unreleased/fix-obsolte-production-identifier.yml new file mode 100644 index 00000000000..13ec9ff2876 --- /dev/null +++ b/changelogs/unreleased/fix-obsolte-production-identifier.yml @@ -0,0 +1,5 @@ +--- +title: Fix inconsistent production environment definition on VSA +merge_request: 57557 +author: +type: fixed diff --git a/changelogs/unreleased/issue-220040-fix-rails-savebang-admin-controllers.yml b/changelogs/unreleased/issue-220040-fix-rails-savebang-admin-controllers.yml new file mode 100644 index 00000000000..ff9b464c403 --- /dev/null +++ b/changelogs/unreleased/issue-220040-fix-rails-savebang-admin-controllers.yml @@ -0,0 +1,5 @@ +--- +title: Fix Rails/SaveBang Rubocop offenses for admin controllers +merge_request: 57644 +author: Huzaifa Iftikhar @huzaifaiftikhar +type: fixed diff --git a/changelogs/unreleased/tor-defect-overview-placeholder-message-diffs-new-data-source.yml b/changelogs/unreleased/tor-defect-overview-placeholder-message-diffs-new-data-source.yml new file mode 100644 index 00000000000..d602bad9d4e --- /dev/null +++ b/changelogs/unreleased/tor-defect-overview-placeholder-message-diffs-new-data-source.yml @@ -0,0 +1,6 @@ +--- +title: Hydrate some of the variables in the Overview tab suggestion commit placeholder + by switching the Diffs data source for it +merge_request: 57419 +author: +type: changed diff --git a/config/feature_flags/development/ci_needs_optional.yml b/config/feature_flags/development/ci_needs_optional.yml index fcbe9bf6106..eacb0ab6d51 100644 --- a/config/feature_flags/development/ci_needs_optional.yml +++ b/config/feature_flags/development/ci_needs_optional.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323891 milestone: '13.10' type: development group: group::pipeline authoring -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/usage_data_o_pipeline_authoring_unique_users_pushing_mr_ciconfigfile.yml b/config/feature_flags/development/usage_data_o_pipeline_authoring_unique_users_pushing_mr_ciconfigfile.yml index 64e46689775..36fe28236ec 100644 --- a/config/feature_flags/development/usage_data_o_pipeline_authoring_unique_users_pushing_mr_ciconfigfile.yml +++ b/config/feature_flags/development/usage_data_o_pipeline_authoring_unique_users_pushing_mr_ciconfigfile.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/322166 milestone: '13.10' type: development group: group::pipeline authoring -default_enabled: false +default_enabled: true diff --git a/doc/api/members.md b/doc/api/members.md index b9070b8f305..b917ca98562 100644 --- a/doc/api/members.md +++ b/doc/api/members.md @@ -90,8 +90,8 @@ Example response: Gets a list of group or project members viewable by the authenticated user, including inherited members and permissions through ancestor groups. -WARNING: -Due to [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/249523), the users effective `access_level` may actually be higher than returned value when listing group members. +If a user is a member of this group or project and also of one or more ancestor groups, only its membership with the highest `access_level` is returned. +This represents the effective permission of the user. This function takes pagination parameters `page` and `per_page` to restrict the list of users. diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index d4dc8e7f498..5ed64210725 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -2149,10 +2149,11 @@ To download artifacts from a job in the current pipeline, use the basic form of #### Optional `needs` > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30680) in GitLab 13.10. -> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default. -> - It's disabled on GitLab.com. -> - It's not recommended for production use. -> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-optional-needs). **(FREE SELF)** +> - [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default. +> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/323891) in GitLab 13.11. +> - Enabled on GitLab.com. +> - Recommended for production use. +> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-optional-needs). **(FREE SELF)** WARNING: This feature might not be available to you. Check the **version history** note above for details. @@ -2191,10 +2192,10 @@ rspec: #### Enable or disable optional needs **(FREE SELF)** -Optional needs is under development and not ready for production use. It is -deployed behind a feature flag that is **disabled by default**. +Optional needs is under development but ready for production use. +It is deployed behind a feature flag that is **enabled by default**. [GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) -can enable it. +can opt to disable it. To enable it: diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md index 6c41d93d51f..ea1d76df7ba 100644 --- a/doc/user/analytics/value_stream_analytics.md +++ b/doc/user/analytics/value_stream_analytics.md @@ -103,14 +103,8 @@ In short, the Value Stream Analytics dashboard tracks data related to [GitLab fl ## How the production environment is identified -Value Stream Analytics identifies production environments by looking for project [environments](../../ci/yaml/README.md#environment) with a name matching any of these patterns: - -- `prod` or `prod/*` -- `production` or `production/*` - -These patterns are not case-sensitive. - -You can change the name of a project environment in your GitLab CI/CD configuration. +Value Stream Analytics identifies production environments based on +[the deployment tier of environments](../../ci/environments/index.md#deployment-tier-of-environments). ## Example workflow diff --git a/doc/user/packages/generic_packages/index.md b/doc/user/packages/generic_packages/index.md index 2507478345f..57d6245dd96 100644 --- a/doc/user/packages/generic_packages/index.md +++ b/doc/user/packages/generic_packages/index.md @@ -47,7 +47,7 @@ PUT /projects/:id/packages/generic/:package_name/:package_version/:file_name?sta | -------------------| --------------- | ---------| -------------------------------------------------------------------------------------------------------------------------------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](../../../api/README.md#namespaced-path-encoding). | | `package_name` | string | yes | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). -| `package_version` | string | yes | The package version. It can contain only numbers (`0-9`), and dots (`.`). Must be in the format of `X.Y.Z`, i.e. should match `/\A\d+\.\d+\.\d+\z/` regular expression. +| `package_version` | string | yes | The package version. The following regex validates this: `\A(\.?[\w\+-]+\.?)+\z`. You can test your version strings on [Rubular](https://rubular.com/r/aNCV0wG5K14uq8). | `file_name` | string | yes | The filename. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). | `status` | string | no | The package status. It can be `default` (default) or `hidden`. Hidden packages do not appear in the UI or [package API list endpoints](../../../api/packages.md). diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index 1082d63724c..488ba04f87c 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -181,7 +181,7 @@ module Gitlab end def generic_package_version_regex - /\A\d+\.\d+\.\d+\z/ + maven_version_regex end def generic_package_name_regex diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 45aa67b0b15..216c81f3514 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -20593,9 +20593,6 @@ msgstr "" msgid "New Label" msgstr "" -msgid "New Merge Request" -msgstr "" - msgid "New Milestone" msgstr "" diff --git a/spec/controllers/admin/impersonations_controller_spec.rb b/spec/controllers/admin/impersonations_controller_spec.rb index 326003acaf8..744c0712d6b 100644 --- a/spec/controllers/admin/impersonations_controller_spec.rb +++ b/spec/controllers/admin/impersonations_controller_spec.rb @@ -42,7 +42,7 @@ RSpec.describe Admin::ImpersonationsController do context "when the impersonator is not admin (anymore)" do before do impersonator.admin = false - impersonator.save + impersonator.save! end it "responds with status 404" do diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb index cba25dbff95..45ea8949bf2 100644 --- a/spec/controllers/admin/runners_controller_spec.rb +++ b/spec/controllers/admin/runners_controller_spec.rb @@ -125,7 +125,7 @@ RSpec.describe Admin::RunnersController do describe '#resume' do it 'marks the runner as active and ticks the queue' do - runner.update(active: false) + runner.update!(active: false) expect do post :resume, params: { id: runner.id } @@ -140,7 +140,7 @@ RSpec.describe Admin::RunnersController do describe '#pause' do it 'marks the runner as inactive and ticks the queue' do - runner.update(active: true) + runner.update!(active: true) expect do post :pause, params: { id: runner.id } diff --git a/spec/controllers/admin/services_controller_spec.rb b/spec/controllers/admin/services_controller_spec.rb index 8e78cc75369..d5ec9907b48 100644 --- a/spec/controllers/admin/services_controller_spec.rb +++ b/spec/controllers/admin/services_controller_spec.rb @@ -44,7 +44,7 @@ RSpec.describe Admin::ServicesController do describe "#update" do let(:project) { create(:project) } let!(:service_template) do - RedmineService.create( + RedmineService.create!( project: nil, active: false, template: true, diff --git a/spec/features/merge_request/user_creates_merge_request_spec.rb b/spec/features/merge_request/user_creates_merge_request_spec.rb index 6b0c7733ab4..119cf31098c 100644 --- a/spec/features/merge_request/user_creates_merge_request_spec.rb +++ b/spec/features/merge_request/user_creates_merge_request_spec.rb @@ -87,7 +87,7 @@ RSpec.describe "User creates a merge request", :js do click_button("Compare branches and continue") - expect(page).to have_css("h3.page-title", text: "New Merge Request") + expect(page).to have_css("h3.page-title", text: "New merge request") page.within("form#new_merge_request") do fill_in("Title", with: title) diff --git a/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb b/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb index 95e435a333e..d8b258bac47 100644 --- a/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb +++ b/spec/features/merge_request/user_sees_breadcrumb_links_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'New merge request breadcrumb' do it 'displays link to project merge requests and new merge request' do page.within '.breadcrumbs' do - expect(find_link('Merge Requests')[:href]).to end_with(project_merge_requests_path(project)) + expect(find_link('Merge requests')[:href]).to end_with(project_merge_requests_path(project)) expect(find_link('New')[:href]).to end_with(project_new_merge_request_path(project)) end end diff --git a/spec/features/projects/files/user_creates_directory_spec.rb b/spec/features/projects/files/user_creates_directory_spec.rb index f2074c78dba..46b93d738e1 100644 --- a/spec/features/projects/files/user_creates_directory_spec.rb +++ b/spec/features/projects/files/user_creates_directory_spec.rb @@ -77,7 +77,7 @@ RSpec.describe 'Projects > Files > User creates a directory', :js do it 'creates the directory in the new branch and redirect to the merge request' do expect(page).to have_content('new-feature') expect(page).to have_content('The directory has been successfully created') - expect(page).to have_content('New Merge Request') + expect(page).to have_content('New merge request') expect(page).to have_content('From new-feature into master') expect(page).to have_content('Add new directory') diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb index a87a05d4408..3238f6744f7 100644 --- a/spec/finders/group_members_finder_spec.rb +++ b/spec/finders/group_members_finder_spec.rb @@ -3,174 +3,180 @@ require 'spec_helper' RSpec.describe GroupMembersFinder, '#execute' do - let(:group) { create(:group) } - let(:nested_group) { create(:group, parent: group) } - let(:deeper_nested_group) { create(:group, parent: nested_group) } - let(:user1) { create(:user) } - let(:user2) { create(:user) } - let(:user3) { create(:user) } - let(:user4) { create(:user) } - let(:user5) { create(:user, :two_factor_via_otp) } - - it 'returns members for top-level group' do - member1 = group.add_maintainer(user1) - member2 = group.add_maintainer(user2) - member3 = group.add_maintainer(user3) - create(:group_member, :minimal_access, user: create(:user), source: group) - - result = described_class.new(group).execute - - expect(result.to_a).to match_array([member3, member2, member1]) + let(:group) { create(:group) } + let(:sub_group) { create(:group, parent: group) } + let(:sub_sub_group) { create(:group, parent: sub_group) } + let(:user1) { create(:user) } + let(:user2) { create(:user) } + let(:user3) { create(:user) } + let(:user4) { create(:user) } + let(:user5) { create(:user, :two_factor_via_otp) } + + let(:groups) do + { + group: group, + sub_group: sub_group, + sub_sub_group: sub_sub_group + } end - it 'returns members & inherited members for nested group by default' do - group.add_developer(user2) - nested_group.request_access(user4) - member1 = group.add_maintainer(user1) - member3 = nested_group.add_maintainer(user2) - member4 = nested_group.add_maintainer(user3) - - result = described_class.new(nested_group).execute - - expect(result.to_a).to match_array([member1, member3, member4]) + context 'relations' do + let!(:members) do + { + user1_sub_sub_group: create(:group_member, :maintainer, group: sub_sub_group, user: user1), + user1_sub_group: create(:group_member, :developer, group: sub_group, user: user1), + user1_group: create(:group_member, :reporter, group: group, user: user1), + user2_sub_sub_group: create(:group_member, :reporter, group: sub_sub_group, user: user2), + user2_sub_group: create(:group_member, :developer, group: sub_group, user: user2), + user2_group: create(:group_member, :maintainer, group: group, user: user2), + user3_sub_sub_group: create(:group_member, :developer, group: sub_sub_group, user: user3, expires_at: 1.day.from_now), + user3_sub_group: create(:group_member, :developer, group: sub_group, user: user3, expires_at: 2.days.from_now), + user3_group: create(:group_member, :reporter, group: group, user: user3), + user4_sub_sub_group: create(:group_member, :reporter, group: sub_sub_group, user: user4), + user4_sub_group: create(:group_member, :developer, group: sub_group, user: user4, expires_at: 1.day.from_now), + user4_group: create(:group_member, :developer, group: group, user: user4, expires_at: 2.days.from_now) + } + end + + using RSpec::Parameterized::TableSyntax + + where(:subject_relations, :subject_group, :expected_members) do + nil | :group | [:user1_group, :user2_group, :user3_group, :user4_group] + [:direct] | :group | [:user1_group, :user2_group, :user3_group, :user4_group] + [:inherited] | :group | [] + [:descendants] | :group | [:user1_sub_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group] + [:direct, :inherited] | :group | [:user1_group, :user2_group, :user3_group, :user4_group] + [:direct, :descendants] | :group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:descendants, :inherited] | :group | [:user1_sub_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group] + [:direct, :descendants, :inherited] | :group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + nil | :sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct] | :sub_group | [:user1_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group] + [:inherited] | :sub_group | [:user1_group, :user2_group, :user3_group, :user4_group] + [:descendants] | :sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group] + [:direct, :inherited] | :sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct, :descendants] | :sub_group | [:user1_sub_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group] + [:descendants, :inherited] | :sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_sub_group, :user4_group] + [:direct, :descendants, :inherited] | :sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + nil | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct] | :sub_sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group] + [:inherited] | :sub_sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:descendants] | :sub_sub_group | [] + [:direct, :inherited] | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct, :descendants] | :sub_sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group] + [:descendants, :inherited] | :sub_sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct, :descendants, :inherited] | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + end + + with_them do + it 'returns correct members' do + result = if subject_relations + described_class.new(groups[subject_group]).execute(include_relations: subject_relations) + else + described_class.new(groups[subject_group]).execute + end + + expect(result.to_a).to match_array(expected_members.map { |name| members[name] }) + end + end end - it 'does not return inherited members for nested group if requested' do - group.add_maintainer(user1) - group.add_developer(user2) - member2 = nested_group.add_maintainer(user2) - member3 = nested_group.add_maintainer(user3) + context 'search' do + it 'returns searched members if requested' do + group.add_maintainer(user2) + group.add_developer(user3) + member = group.add_maintainer(user1) - result = described_class.new(nested_group).execute(include_relations: [:direct]) + result = described_class.new(group, params: { search: user1.name }).execute - expect(result.to_a).to match_array([member2, member3]) - end + expect(result.to_a).to match_array([member]) + end - it 'returns only inherited members for nested group if requested' do - group.add_developer(user2) - nested_group.request_access(user4) - member1 = group.add_maintainer(user1) - nested_group.add_maintainer(user2) - nested_group.add_maintainer(user3) + it 'returns nothing if search only in inherited relation' do + group.add_maintainer(user2) + group.add_developer(user3) + group.add_maintainer(user1) - result = described_class.new(nested_group).execute(include_relations: [:inherited]) + result = described_class.new(group, params: { search: user1.name }).execute(include_relations: [:inherited]) - expect(result.to_a).to match_array([member1]) - end + expect(result.to_a).to match_array([]) + end - it 'does not return nil if `inherited only` relation is requested on root group' do - group.add_developer(user2) + it 'returns searched member only from sub_group if search only in inherited relation' do + group.add_maintainer(user2) + group.add_developer(user3) + sub_group.add_maintainer(create(:user, name: user1.name)) + member = group.add_maintainer(user1) - result = described_class.new(group).execute(include_relations: [:inherited]) + result = described_class.new(sub_group, params: { search: member.user.name }).execute(include_relations: [:inherited]) - expect(result).not_to be_nil + expect(result.to_a).to contain_exactly(member) + end end - it 'returns members for descendant groups if requested' do - member1 = group.add_maintainer(user2) - member2 = group.add_maintainer(user1) - nested_group.add_maintainer(user2) - member3 = nested_group.add_maintainer(user3) - member4 = nested_group.add_maintainer(user4) + context 'filter by two-factor' do + it 'returns members with two-factor auth if requested by owner' do + group.add_owner(user2) + group.add_maintainer(user1) + member = group.add_maintainer(user5) - result = described_class.new(group).execute(include_relations: [:direct, :descendants]) + result = described_class.new(group, user2, params: { two_factor: 'enabled' }).execute - expect(result.to_a).to match_array([member1, member2, member3, member4]) - end + expect(result.to_a).to contain_exactly(member) + end - it 'returns searched members if requested' do - group.add_maintainer(user2) - group.add_developer(user3) - member = group.add_maintainer(user1) + it 'returns members without two-factor auth if requested by owner' do + member1 = group.add_owner(user2) + member2 = group.add_maintainer(user1) + member_with_2fa = group.add_maintainer(user5) - result = described_class.new(group, params: { search: user1.name }).execute + result = described_class.new(group, user2, params: { two_factor: 'disabled' }).execute - expect(result.to_a).to match_array([member]) - end + expect(result.to_a).not_to include(member_with_2fa) + expect(result.to_a).to match_array([member1, member2]) + end - it 'returns nothing if search only in inherited relation' do - group.add_maintainer(user2) - group.add_developer(user3) - group.add_maintainer(user1) + it 'returns direct members with two-factor auth if requested by owner' do + group.add_owner(user1) + group.add_maintainer(user2) + sub_group.add_maintainer(user3) + member_with_2fa = sub_group.add_maintainer(user5) - result = described_class.new(group, params: { search: user1.name }).execute(include_relations: [:inherited]) + result = described_class.new(sub_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:direct]) - expect(result.to_a).to match_array([]) - end + expect(result.to_a).to match_array([member_with_2fa]) + end - it 'returns searched member only from nested_group if search only in inherited relation' do - group.add_maintainer(user2) - group.add_developer(user3) - nested_group.add_maintainer(create(:user, name: user1.name)) - member = group.add_maintainer(user1) + it 'returns inherited members with two-factor auth if requested by owner' do + group.add_owner(user1) + member_with_2fa = group.add_maintainer(user5) + sub_group.add_maintainer(user2) + sub_group.add_maintainer(user3) - result = described_class.new(nested_group, params: { search: member.user.name }).execute(include_relations: [:inherited]) + result = described_class.new(sub_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:inherited]) - expect(result.to_a).to contain_exactly(member) - end - - it 'returns members with two-factor auth if requested by owner' do - group.add_owner(user2) - group.add_maintainer(user1) - member = group.add_maintainer(user5) - - result = described_class.new(group, user2, params: { two_factor: 'enabled' }).execute + expect(result.to_a).to match_array([member_with_2fa]) + end - expect(result.to_a).to contain_exactly(member) - end - - it 'returns members without two-factor auth if requested by owner' do - member1 = group.add_owner(user2) - member2 = group.add_maintainer(user1) - member_with_2fa = group.add_maintainer(user5) + it 'returns direct members without two-factor auth if requested by owner' do + group.add_owner(user1) + group.add_maintainer(user2) + member3 = sub_group.add_maintainer(user3) + sub_group.add_maintainer(user5) - result = described_class.new(group, user2, params: { two_factor: 'disabled' }).execute + result = described_class.new(sub_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:direct]) - expect(result.to_a).not_to include(member_with_2fa) - expect(result.to_a).to match_array([member1, member2]) - end - - it 'returns direct members with two-factor auth if requested by owner' do - group.add_owner(user1) - group.add_maintainer(user2) - nested_group.add_maintainer(user3) - member_with_2fa = nested_group.add_maintainer(user5) - - result = described_class.new(nested_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:direct]) - - expect(result.to_a).to match_array([member_with_2fa]) - end - - it 'returns inherited members with two-factor auth if requested by owner' do - group.add_owner(user1) - member_with_2fa = group.add_maintainer(user5) - nested_group.add_maintainer(user2) - nested_group.add_maintainer(user3) - - result = described_class.new(nested_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:inherited]) - - expect(result.to_a).to match_array([member_with_2fa]) - end - - it 'returns direct members without two-factor auth if requested by owner' do - group.add_owner(user1) - group.add_maintainer(user2) - member3 = nested_group.add_maintainer(user3) - nested_group.add_maintainer(user5) - - result = described_class.new(nested_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:direct]) - - expect(result.to_a).to match_array([member3]) - end + expect(result.to_a).to match_array([member3]) + end - it 'returns inherited members without two-factor auth if requested by owner' do - member1 = group.add_owner(user1) - group.add_maintainer(user5) - nested_group.add_maintainer(user2) - nested_group.add_maintainer(user3) + it 'returns inherited members without two-factor auth if requested by owner' do + member1 = group.add_owner(user1) + group.add_maintainer(user5) + sub_group.add_maintainer(user2) + sub_group.add_maintainer(user3) - result = described_class.new(nested_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:inherited]) + result = described_class.new(sub_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:inherited]) - expect(result.to_a).to match_array([member1]) + expect(result.to_a).to match_array([member1]) + end end end diff --git a/spec/frontend/boards/components/board_add_new_column_spec.js b/spec/frontend/boards/components/board_add_new_column_spec.js index 60584eaf6cf..61f210f566b 100644 --- a/spec/frontend/boards/components/board_add_new_column_spec.js +++ b/spec/frontend/boards/components/board_add_new_column_spec.js @@ -1,3 +1,4 @@ +import { GlFormRadioGroup } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; @@ -12,6 +13,10 @@ Vue.use(Vuex); describe('Board card layout', () => { let wrapper; + const selectLabel = (id) => { + wrapper.findComponent(GlFormRadioGroup).vm.$emit('change', id); + }; + const createStore = ({ actions = {}, getters = {}, state = {} } = {}) => { return new Vuex.Store({ state: { @@ -57,6 +62,11 @@ describe('Board card layout', () => { }, }), ); + + // trigger change event + if (selectedId) { + selectLabel(selectedId); + } }; afterEach(() => { diff --git a/spec/frontend/diffs/store/getters_spec.js b/spec/frontend/diffs/store/getters_spec.js index 04606b48662..2e3a66d5b01 100644 --- a/spec/frontend/diffs/store/getters_spec.js +++ b/spec/frontend/diffs/store/getters_spec.js @@ -377,32 +377,40 @@ describe('Diffs Module Getters', () => { }); describe('suggestionCommitMessage', () => { + let rootState; + beforeEach(() => { Object.assign(localState, { defaultSuggestionCommitMessage: '%{branch_name}%{project_path}%{project_name}%{username}%{user_full_name}%{file_paths}%{suggestions_count}%{files_count}', - branchName: 'branch', - projectPath: '/path', - projectName: 'name', - username: 'user', - userFullName: 'user userton', }); + rootState = { + page: { + mrMetadata: { + branch_name: 'branch', + project_path: '/path', + project_name: 'name', + username: 'user', + user_full_name: 'user userton', + }, + }, + }; }); it.each` - specialState | output - ${{}} | ${'branch/pathnameuseruser userton%{file_paths}%{suggestions_count}%{files_count}'} - ${{ userFullName: null }} | ${'branch/pathnameuser%{user_full_name}%{file_paths}%{suggestions_count}%{files_count}'} - ${{ username: null }} | ${'branch/pathname%{username}user userton%{file_paths}%{suggestions_count}%{files_count}'} - ${{ projectName: null }} | ${'branch/path%{project_name}useruser userton%{file_paths}%{suggestions_count}%{files_count}'} - ${{ projectPath: null }} | ${'branch%{project_path}nameuseruser userton%{file_paths}%{suggestions_count}%{files_count}'} - ${{ branchName: null }} | ${'%{branch_name}/pathnameuseruser userton%{file_paths}%{suggestions_count}%{files_count}'} + specialState | output + ${{}} | ${'branch/pathnameuseruser userton%{file_paths}%{suggestions_count}%{files_count}'} + ${{ user_full_name: null }} | ${'branch/pathnameuser%{user_full_name}%{file_paths}%{suggestions_count}%{files_count}'} + ${{ username: null }} | ${'branch/pathname%{username}user userton%{file_paths}%{suggestions_count}%{files_count}'} + ${{ project_name: null }} | ${'branch/path%{project_name}useruser userton%{file_paths}%{suggestions_count}%{files_count}'} + ${{ project_path: null }} | ${'branch%{project_path}nameuseruser userton%{file_paths}%{suggestions_count}%{files_count}'} + ${{ branch_name: null }} | ${'%{branch_name}/pathnameuseruser userton%{file_paths}%{suggestions_count}%{files_count}'} `( 'provides the correct "base" default commit message based on state ($specialState)', ({ specialState, output }) => { - Object.assign(localState, specialState); + Object.assign(rootState.page.mrMetadata, specialState); - expect(getters.suggestionCommitMessage(localState)()).toBe(output); + expect(getters.suggestionCommitMessage(localState, null, rootState)()).toBe(output); }, ); @@ -417,7 +425,9 @@ describe('Diffs Module Getters', () => { `( "properly overrides state values ($stateOverrides) if they're provided", ({ stateOverrides, output }) => { - expect(getters.suggestionCommitMessage(localState)(stateOverrides)).toBe(output); + expect(getters.suggestionCommitMessage(localState, null, rootState)(stateOverrides)).toBe( + output, + ); }, ); @@ -431,7 +441,9 @@ describe('Diffs Module Getters', () => { `( "fills in any missing interpolations ($providedValues) when they're provided at the getter callsite", ({ providedValues, output }) => { - expect(getters.suggestionCommitMessage(localState)(providedValues)).toBe(output); + expect(getters.suggestionCommitMessage(localState, null, rootState)(providedValues)).toBe( + output, + ); }, ); }); diff --git a/spec/frontend/error_tracking/components/error_tracking_list_spec.js b/spec/frontend/error_tracking/components/error_tracking_list_spec.js index c6ce236af01..c0c542ae587 100644 --- a/spec/frontend/error_tracking/components/error_tracking_list_spec.js +++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js @@ -52,7 +52,6 @@ describe('ErrorTrackingList', () => { beforeEach(() => { actions = { - getErrorList: () => {}, startPolling: jest.fn(), restartPolling: jest.fn().mockName('restartPolling'), addRecentSearch: jest.fn(), diff --git a/spec/frontend/notes/components/note_body_spec.js b/spec/frontend/notes/components/note_body_spec.js index 4922de987fa..40251244423 100644 --- a/spec/frontend/notes/components/note_body_spec.js +++ b/spec/frontend/notes/components/note_body_spec.js @@ -81,14 +81,21 @@ describe('issue_note_body component', () => { state: { defaultSuggestionCommitMessage: '%{branch_name}%{project_path}%{project_name}%{username}%{user_full_name}%{file_paths}%{suggestions_count}%{files_count}', - branchName: 'branch', - projectPath: '/path', - projectName: 'name', - username: 'user', - userFullName: 'user userton', }, getters: { suggestionCommitMessage }, }, + page: { + namespaced: true, + state: { + mrMetadata: { + branch_name: 'branch', + project_path: '/path', + project_name: 'name', + username: 'user', + user_full_name: 'user userton', + }, + }, + }, }, }); diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index 1aca3dae41b..f62a3c74005 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -667,7 +667,14 @@ RSpec.describe Gitlab::Regex do it { is_expected.to match('1.2.3') } it { is_expected.to match('1.3.350') } - it { is_expected.not_to match('1.3.350-20201230123456') } + it { is_expected.to match('1.3.350-20201230123456') } + it { is_expected.to match('1.2.3-rc1') } + it { is_expected.to match('1.2.3g') } + it { is_expected.to match('1.2') } + it { is_expected.to match('1.2.bananas') } + it { is_expected.to match('v1.2.4-build') } + it { is_expected.to match('d50d836eb3de6177ce6c7a5482f27f9c2c84b672') } + it { is_expected.to match('this_is_a_string_only') } it { is_expected.not_to match('..1.2.3') } it { is_expected.not_to match(' 1.2.3') } it { is_expected.not_to match("1.2.3 \r\t") } diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index c0afc1d50f6..7398a343ef4 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -808,4 +808,30 @@ RSpec.describe Deployment do end end end + + describe '#update_merge_request_metrics!' do + let_it_be(:project) { create(:project, :repository) } + let(:environment) { build(:environment, environment_tier, project: project) } + let!(:deployment) { create(:deployment, :success, project: project, environment: environment) } + let!(:merge_request) { create(:merge_request, :simple, :merged_last_month, project: project) } + + context 'with production environment' do + let(:environment_tier) { :production } + + it 'updates merge request metrics for production-grade environment' do + expect { deployment.update_merge_request_metrics! } + .to change { merge_request.reload.metrics.first_deployed_to_production_at } + .from(nil).to(deployment.reload.finished_at) + end + end + + context 'with staging environment' do + let(:environment_tier) { :staging } + + it 'updates merge request metrics for production-grade environment' do + expect { deployment.update_merge_request_metrics! } + .not_to change { merge_request.reload.metrics.first_deployed_to_production_at } + end + end + end end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index e021a6cf6d3..759bb080172 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -302,6 +302,8 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do 'testing' | described_class.tiers[:testing] 'testing-prd' | described_class.tiers[:testing] 'acceptance-testing' | described_class.tiers[:testing] + 'production-test' | described_class.tiers[:testing] + 'test-production' | described_class.tiers[:testing] 'QC' | described_class.tiers[:testing] 'gstg' | described_class.tiers[:staging] 'staging' | described_class.tiers[:staging] @@ -315,6 +317,12 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do 'gprd-cny' | described_class.tiers[:production] 'production' | described_class.tiers[:production] 'Production' | described_class.tiers[:production] + 'PRODUCTION' | described_class.tiers[:production] + 'Production/eu' | described_class.tiers[:production] + 'production/eu' | described_class.tiers[:production] + 'PRODUCTION/EU' | described_class.tiers[:production] + 'productioneu' | described_class.tiers[:production] + 'production/www.gitlab.com' | described_class.tiers[:production] 'prod' | described_class.tiers[:production] 'PROD' | described_class.tiers[:production] 'Live' | described_class.tiers[:production] @@ -444,31 +452,6 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do end end - describe '#update_merge_request_metrics?' do - { - 'gprd' => false, - 'prod' => true, - 'prod-test' => false, - 'PROD' => true, - 'production' => true, - 'production-test' => false, - 'PRODUCTION' => true, - 'production/eu' => true, - 'PRODUCTION/EU' => true, - 'production/www.gitlab.com' => true, - 'productioneu' => false, - 'Production' => true, - 'Production/eu' => true, - 'test-production' => false - }.each do |name, expected_value| - it "returns #{expected_value} for #{name}" do - env = create(:environment, name: name) - - expect(env.update_merge_request_metrics?).to eq(expected_value), "Expected the name '#{name}' to result in #{expected_value}, but it didn't." - end - end - end - describe '#environment_type' do subject { environment.environment_type } diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index c41f466456f..05247cf9f04 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -413,6 +413,24 @@ RSpec.describe Member do it { is_expected.not_to include @blocked_developer } it { is_expected.not_to include @member_with_minimal_access } end + + describe '.distinct_on_user_with_max_access_level' do + let_it_be(:other_group) { create(:group) } + let_it_be(:member_with_lower_access_level) { create(:group_member, :developer, group: other_group, user: @owner_user) } + + subject { described_class.default_scoped.distinct_on_user_with_max_access_level.to_a } + + it { is_expected.not_to include member_with_lower_access_level } + it { is_expected.to include @owner } + it { is_expected.to include @maintainer } + it { is_expected.to include @invited_member } + it { is_expected.to include @accepted_invite_member } + it { is_expected.to include @requested_member } + it { is_expected.to include @accepted_request_member } + it { is_expected.to include @blocked_maintainer } + it { is_expected.to include @blocked_developer } + it { is_expected.to include @member_with_minimal_access } + end end describe "Delegate methods" do diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb index 2ea4c5d8211..cf52749a186 100644 --- a/spec/models/packages/package_spec.rb +++ b/spec/models/packages/package_spec.rb @@ -367,7 +367,14 @@ RSpec.describe Packages::Package, type: :model do it { is_expected.to validate_presence_of(:version) } it { is_expected.to allow_value('1.2.3').for(:version) } it { is_expected.to allow_value('1.3.350').for(:version) } - it { is_expected.not_to allow_value('1.3.350-20201230123456').for(:version) } + it { is_expected.to allow_value('1.3.350-20201230123456').for(:version) } + it { is_expected.to allow_value('1.2.3-rc1').for(:version) } + it { is_expected.to allow_value('1.2.3g').for(:version) } + it { is_expected.to allow_value('1.2').for(:version) } + it { is_expected.to allow_value('1.2.bananas').for(:version) } + it { is_expected.to allow_value('v1.2.4-build').for(:version) } + it { is_expected.to allow_value('d50d836eb3de6177ce6c7a5482f27f9c2c84b672').for(:version) } + it { is_expected.to allow_value('this_is_a_string_only').for(:version) } it { is_expected.not_to allow_value('..1.2.3').for(:version) } it { is_expected.not_to allow_value(' 1.2.3').for(:version) } it { is_expected.not_to allow_value("1.2.3 \r\t").for(:version) } diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb index 16d56b6cfbe..a5e40eec919 100644 --- a/spec/requests/api/generic_packages_spec.rb +++ b/spec/requests/api/generic_packages_spec.rb @@ -16,6 +16,7 @@ RSpec.describe API::GenericPackages do let_it_be(:project_deploy_token_ro) { create(:project_deploy_token, deploy_token: deploy_token_ro, project: project) } let_it_be(:deploy_token_wo) { create(:deploy_token, read_package_registry: false, write_package_registry: true) } let_it_be(:project_deploy_token_wo) { create(:project_deploy_token, deploy_token: deploy_token_wo, project: project) } + let(:user) { personal_access_token.user } let(:ci_build) { create(:ci_build, :running, user: user) } @@ -326,6 +327,34 @@ RSpec.describe API::GenericPackages do end end end + + context 'different versions' do + where(:version, :expected_status) do + '1.3.350-20201230123456' | :created + '1.2.3' | :created + '1.2.3g' | :created + '1.2' | :created + '1.2.bananas' | :created + 'v1.2.4-build' | :created + 'd50d836eb3de6177ce6c7a5482f27f9c2c84b672' | :created + '..1.2.3' | :bad_request + '1.2.3-4/../../' | :bad_request + '%2e%2e%2f1.2.3' | :bad_request + end + + with_them do + let(:expected_package_diff_count) { expected_status == :created ? 1 : 0 } + let(:headers) { workhorse_headers.merge(auth_header) } + + subject { upload_file(params, headers, package_version: version) } + + it "returns the #{params[:expected_status]}", :aggregate_failures do + expect { subject }.to change { project.packages.generic.count }.by(expected_package_diff_count) + + expect(response).to have_gitlab_http_status(expected_status) + end + end + end end end @@ -418,8 +447,8 @@ RSpec.describe API::GenericPackages do end end - def upload_file(params, request_headers, send_rewritten_field: true, package_name: 'mypackage', file_name: 'myfile.tar.gz') - url = "/projects/#{project.id}/packages/generic/#{package_name}/0.0.1/#{file_name}" + def upload_file(params, request_headers, send_rewritten_field: true, package_name: 'mypackage', package_version: '0.0.1', file_name: 'myfile.tar.gz') + url = "/projects/#{project.id}/packages/generic/#{package_name}/#{package_version}/#{file_name}" workhorse_finalize( api(url), |