From 92d681f6f0214b54130ea4ed9794da0fbbe3d497 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 19 Apr 2024 09:13:38 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/ci/as-if-foss.gitlab-ci.yml | 2 +- .gitlab/ci/frontend.gitlab-ci.yml | 2 +- .gitlab/ci/global.gitlab-ci.yml | 2 +- .gitlab/ci/rules.gitlab-ci.yml | 4 +- .gitlab/ci/templates/gem.gitlab-ci.yml | 4 +- .../issues/show/components/app.vue | 7 + .../issues/show/components/issue_header.vue | 5 + .../issues/show/components/sticky_header.vue | 9 + .../deployment/deployment_view_button.vue | 66 +++--- .../vue_shared/components/imported_badge.vue | 40 ++++ .../show/components/issuable_header.vue | 9 + .../webhooks/components/webhook_form_app.vue | 7 +- ...item_sidebar_dropdown_widget_with_edit.vue | 3 +- .../work_item_assignees_with_edit.vue | 66 +++++- .../concerns/web_hooks/hook_actions.rb | 8 - app/helpers/search_helper.rb | 8 +- app/models/issue.rb | 3 + app/models/merge_request.rb | 6 +- .../ci_access/refresh_service.rb | 2 +- .../user_access/refresh_service.rb | 2 +- app/services/web_hook_service.rb | 1 - .../beta/custom_webhook_headers.yml | 9 - .../lazy_merge_request_committers.yml | 9 - db/docs/approval_policy_rules.yml | 4 +- db/docs/security_policies.yml | 4 +- ...agement_project_id_to_security_policies.rb | 15 ++ ...ment_project_id_fk_to_security_policies.rb | 17 ++ ...agement_project_id_on_security_policies.rb | 16 ++ ...ent_project_id_to_approval_policy_rules.rb | 15 ++ ..._project_id_fk_to_approval_policy_rules.rb | 17 ++ ...ent_project_id_on_approval_policy_rules.rb | 16 ++ db/schema_migrations/20240416123401 | 1 + db/schema_migrations/20240416123402 | 1 + db/schema_migrations/20240416123403 | 1 + db/schema_migrations/20240416123404 | 1 + db/schema_migrations/20240416123405 | 1 + db/schema_migrations/20240416123406 | 1 + db/structure.sql | 14 +- doc/ci/steps/index.md | 1 + doc/user/clusters/agent/ci_cd_workflow.md | 5 +- doc/user/clusters/agent/user_access.md | 5 +- doc/user/project/integrations/webhooks.md | 6 +- .../OpenTofu/Base.latest.gitlab-ci.yml | 2 +- .../ci/templates/Pages/Hugo.gitlab-ci.yml | 2 +- lib/gitlab/search/recent_items.rb | 2 +- locale/gitlab.pot | 9 + ...r_merges_only_if_pipeline_succeeds_spec.rb | 159 +++++++++++-- .../show/components/issue_header_spec.js | 2 + .../show/components/sticky_header_spec.js | 12 + .../deployment/deployment_view_button_spec.js | 22 +- .../components/imported_badge_spec.js | 49 ++++ .../show/components/issuable_header_spec.js | 16 ++ .../work_item_assignees_with_edit_spec.js | 79 ++++++- spec/helpers/search_helper_spec.rb | 218 +++++++++++------- ...new_tables_with_gitlab_main_schema_spec.rb | 2 - spec/models/issue_spec.rb | 21 ++ spec/models/merge_request_spec.rb | 44 ---- spec/services/web_hook_service_spec.rb | 13 -- 58 files changed, 772 insertions(+), 295 deletions(-) create mode 100644 app/assets/javascripts/vue_shared/components/imported_badge.vue delete mode 100644 config/feature_flags/beta/custom_webhook_headers.yml delete mode 100644 config/feature_flags/gitlab_com_derisk/lazy_merge_request_committers.yml create mode 100644 db/migrate/20240416123401_add_security_policy_management_project_id_to_security_policies.rb create mode 100644 db/migrate/20240416123402_add_security_policy_management_project_id_fk_to_security_policies.rb create mode 100644 db/migrate/20240416123403_add_index_security_policy_management_project_id_on_security_policies.rb create mode 100644 db/migrate/20240416123404_add_security_policy_management_project_id_to_approval_policy_rules.rb create mode 100644 db/migrate/20240416123405_add_security_policy_management_project_id_fk_to_approval_policy_rules.rb create mode 100644 db/migrate/20240416123406_add_index_security_policy_management_project_id_on_approval_policy_rules.rb create mode 100644 db/schema_migrations/20240416123401 create mode 100644 db/schema_migrations/20240416123402 create mode 100644 db/schema_migrations/20240416123403 create mode 100644 db/schema_migrations/20240416123404 create mode 100644 db/schema_migrations/20240416123405 create mode 100644 db/schema_migrations/20240416123406 create mode 100644 spec/frontend/vue_shared/components/imported_badge_spec.js diff --git a/.gitlab/ci/as-if-foss.gitlab-ci.yml b/.gitlab/ci/as-if-foss.gitlab-ci.yml index 074c7b12ab9..2b30ed5f9da 100644 --- a/.gitlab/ci/as-if-foss.gitlab-ci.yml +++ b/.gitlab/ci/as-if-foss.gitlab-ci.yml @@ -32,7 +32,7 @@ prepare-as-if-foss-branch: - git add -A # --allow-empty accounts for the edge case where FOSS matchess EE repository # and a merge request only contains EE related changes. - - git commit -m 'Update from merge request' --allow-empty # TODO: Mark which SHA we add + - git commit -m 'Update from merge request' --allow-empty # TODO: Mark which SHA we add - git push -f "${FOSS_REPOSITORY}" "${AS_IF_FOSS_BRANCH}" prepare-as-if-foss-env: diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index 2eb6e9428b3..62a32b9bc85 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -73,7 +73,7 @@ compile-test-assets: expire_in: 7d paths: - public/assets/ - - config/helpers/tailwind/ # Assets created during tailwind compilation + - config/helpers/tailwind/ # Assets created during tailwind compilation - "${WEBPACK_COMPILE_LOG_PATH}" when: always diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml index 71c5493dfbb..d58ca6da102 100644 --- a/.gitlab/ci/global.gitlab-ci.yml +++ b/.gitlab/ci/global.gitlab-ci.yml @@ -308,7 +308,7 @@ .ai-gateway-variables: variables: - AIGW_AUTH__BYPASS_EXTERNAL: True + AIGW_AUTH__BYPASS_EXTERNAL: true AIGW_VERTEX_TEXT_MODEL__PROJECT: $VERTEX_AI_PROJECT AIGW_VERTEX_TEXT_MODEL__JSON_KEY: $VERTEX_AI_CREDENTIALS AIGW_FASTAPI__DOCS_URL: "/docs" diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index a61357488ba..a7fb145b1fb 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1393,7 +1393,7 @@ changes: *frontend-dependency-patterns when: never - <<: *if-merge-request - changes: [".gitlab/ci/frontend.gitlab-ci.yml"] # When this file is modified, we run full Jest jobs + changes: [".gitlab/ci/frontend.gitlab-ci.yml"] # When this file is modified, we run full Jest jobs when: never - <<: *if-merge-request changes: *frontend-predictive-patterns @@ -2855,7 +2855,7 @@ .setup:rules:set-pipeline-name: rules: - - <<: *if-not-merge-request # This is only designed to run in a merge request + - <<: *if-not-merge-request # This is only designed to run in a merge request when: never - if: '$PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE == null' when: never diff --git a/.gitlab/ci/templates/gem.gitlab-ci.yml b/.gitlab/ci/templates/gem.gitlab-ci.yml index 3faf56daace..e366538e9e5 100644 --- a/.gitlab/ci/templates/gem.gitlab-ci.yml +++ b/.gitlab/ci/templates/gem.gitlab-ci.yml @@ -18,11 +18,11 @@ spec: - ".gitlab/ci/gitlab-gems.gitlab-ci.yml" - ".gitlab/ci/vendored-gems.gitlab-ci.yml" - ".gitlab/ci/templates/gem.gitlab-ci.yml" - # Ensure dependency updates don't fail child pipelines: https://gitlab.com/gitlab-org/gitlab/-/issues/417428 + # Ensure dependency updates don't fail child pipelines: https://gitlab.com/gitlab-org/gitlab/-/issues/417428 - "Gemfile.lock" - "gems/gem.gitlab-ci.yml" - "gems/gem-pg.gitlab-ci.yml" - # Ensure new cop in the monolith don't break internal gems Rubocop checks: https://gitlab.com/gitlab-org/gitlab/-/issues/419915 + # Ensure new cop in the monolith don't break internal gems Rubocop checks: https://gitlab.com/gitlab-org/gitlab/-/issues/419915 - ".rubocop.yml" - "rubocop/**/*" - ".rubocop_todo/**/*" diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue index 27646df506b..c4f25488085 100644 --- a/app/assets/javascripts/issues/show/components/app.vue +++ b/app/assets/javascripts/issues/show/components/app.vue @@ -149,6 +149,11 @@ export default { required: false, default: false, }, + isImported: { + type: Boolean, + required: false, + default: false, + }, isLocked: { type: Boolean, required: false, @@ -551,6 +556,7 @@ export default { v-if="shouldShowStickyHeader" :is-confidential="isConfidential" :is-hidden="isHidden" + :is-imported="isImported" :is-locked="isLocked" :issuable-status="issuableStatus" :issuable-type="issuableType" @@ -570,6 +576,7 @@ export default { :duplicated-to-issue-url="duplicatedToIssueUrl" :is-first-contribution="isFirstContribution" :is-hidden="isHidden" + :is-imported="isImported" :is-locked="isLocked" :issuable-state="issuableStatus" :issuable-type="issuableType" diff --git a/app/assets/javascripts/issues/show/components/issue_header.vue b/app/assets/javascripts/issues/show/components/issue_header.vue index 96eb8fbb3c7..20c72f06833 100644 --- a/app/assets/javascripts/issues/show/components/issue_header.vue +++ b/app/assets/javascripts/issues/show/components/issue_header.vue @@ -36,6 +36,10 @@ export default { type: Boolean, required: true, }, + isImported: { + type: Boolean, + required: true, + }, isLocked: { type: Boolean, required: true, @@ -105,6 +109,7 @@ export default { :created-at="createdAt" :is-first-contribution="isFirstContribution" :is-hidden="isHidden" + :is-imported="isImported" :issuable-state="issuableState" :issuable-type="issuableType" :service-desk-reply-to="serviceDeskReplyTo" diff --git a/app/assets/javascripts/issues/show/components/sticky_header.vue b/app/assets/javascripts/issues/show/components/sticky_header.vue index 18e37c4216c..6237101084d 100644 --- a/app/assets/javascripts/issues/show/components/sticky_header.vue +++ b/app/assets/javascripts/issues/show/components/sticky_header.vue @@ -4,6 +4,7 @@ import HiddenBadge from '~/issuable/components/hidden_badge.vue'; import LockedBadge from '~/issuable/components/locked_badge.vue'; import { issuableStatusText, STATUS_CLOSED, WORKSPACE_PROJECT } from '~/issues/constants'; import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; +import ImportedBadge from '~/vue_shared/components/imported_badge.vue'; export default { WORKSPACE_PROJECT, @@ -14,6 +15,7 @@ export default { GlIntersectionObserver, GlLink, HiddenBadge, + ImportedBadge, LockedBadge, }, props: { @@ -27,6 +29,11 @@ export default { required: false, default: false, }, + isImported: { + type: Boolean, + required: false, + default: false, + }, isLocked: { type: Boolean, required: false, @@ -89,6 +96,8 @@ export default { /> + + -import { - GlButtonGroup, - GlDropdown, - GlDropdownItem, - GlIcon, - GlLink, - GlSearchBoxByType, -} from '@gitlab/ui'; +import { GlButtonGroup, GlCollapsibleListbox, GlLink } from '@gitlab/ui'; import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; import ReviewAppLink from '../review_app_link.vue'; @@ -14,11 +7,8 @@ export default { name: 'DeploymentViewButton', components: { GlButtonGroup, - GlDropdown, - GlDropdownItem, - GlIcon, + GlCollapsibleListbox, GlLink, - GlSearchBoxByType, ReviewAppLink, }, directives: { @@ -48,7 +38,14 @@ export default { return this.deployment.changes && this.deployment.changes.length > 1; }, filteredChanges() { - return this.deployment?.changes?.filter((change) => change.path.includes(this.searchTerm)); + return this.deployment?.changes + ?.filter((change) => change.path.includes(this.searchTerm)) + .map((change) => ({ value: change.external_url, text: change.path })); + }, + }, + methods: { + search(searchTerm) { + this.searchTerm = searchTerm; }, }, }; @@ -62,33 +59,24 @@ export default { size="small" css-class="deploy-link js-deploy-url gl-display-inline" /> - - - - - - {{ change.path }} -

- {{ change.external_url }} -

+ + + +import { GlBadge, GlTooltipDirective } from '@gitlab/ui'; +import { __, s__, sprintf } from '~/locale'; + +import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/issues/constants'; + +const importableTypeText = { + [TYPE_ISSUE]: __('issue'), + [TYPE_MERGE_REQUEST]: __('merge request'), +}; + +export default { + components: { + GlBadge, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + props: { + importableType: { + type: String, + required: false, + default: '', + }, + }, + computed: { + title() { + return sprintf(s__('BulkImport|This %{importable} was imported from another instance.'), { + importable: importableTypeText[this.importableType], + }); + }, + }, +}; + + + diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue index 1b95a2abdf9..fbc733ee21c 100644 --- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue +++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue @@ -7,6 +7,7 @@ import { issuableStatusText, STATUS_OPEN, STATUS_REOPENED } from '~/issues/const import { isExternal } from '~/lib/utils/url_utility'; import { __, n__, sprintf } from '~/locale'; import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; +import ImportedBadge from '~/vue_shared/components/imported_badge.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue'; @@ -20,6 +21,7 @@ export default { GlSprintf, HiddenBadge, LockedBadge, + ImportedBadge, TimeAgoTooltip, WorkItemTypeIcon, }, @@ -70,6 +72,11 @@ export default { required: false, default: false, }, + isImported: { + type: Boolean, + required: false, + default: false, + }, issuableType: { type: String, required: false, @@ -170,6 +177,8 @@ export default { /> + + -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import FormUrlApp from './form_url_app.vue'; import FormCustomHeaders from './form_custom_headers.vue'; @@ -8,7 +7,6 @@ export default { FormUrlApp, FormCustomHeaders, }, - mixins: [glFeatureFlagsMixin()], props: { initialUrl: { type: String, @@ -32,9 +30,6 @@ export default { diff --git a/app/assets/javascripts/work_items/components/shared/work_item_sidebar_dropdown_widget_with_edit.vue b/app/assets/javascripts/work_items/components/shared/work_item_sidebar_dropdown_widget_with_edit.vue index 909dff097eb..14276cac23a 100644 --- a/app/assets/javascripts/work_items/components/shared/work_item_sidebar_dropdown_widget_with_edit.vue +++ b/app/assets/javascripts/work_items/components/shared/work_item_sidebar_dropdown_widget_with_edit.vue @@ -207,6 +207,7 @@ export default { :multiple="multiSelect" :searchable="searchable" start-opened + block is-check-centered :infinite-scroll="infiniteScroll" :searching="loading" @@ -217,7 +218,7 @@ export default { :selected="localSelectedItem" :reset-button-label="resetButton" :infinite-scroll-loading="infiniteScrollLoading" - toggle-class="gl-w-full! work-item-sidebar-dropdown-toggle" + toggle-class="work-item-sidebar-dropdown-toggle" @reset="unassignValue" @search="debouncedSearchKeyUpdate" @select="handleItemClick" diff --git a/app/assets/javascripts/work_items/components/work_item_assignees_with_edit.vue b/app/assets/javascripts/work_items/components/work_item_assignees_with_edit.vue index eb3c8b8a8f1..a26a25935a3 100644 --- a/app/assets/javascripts/work_items/components/work_item_assignees_with_edit.vue +++ b/app/assets/javascripts/work_items/components/work_item_assignees_with_edit.vue @@ -67,7 +67,8 @@ export default { }, data() { return { - localAssigneeIds: this.assignees.map(({ id }) => id), + localAssigneeIds: [], + assigneeIdsToShowAtTopOfTheListbox: [], searchStarted: false, searchKey: '', users: [], @@ -111,14 +112,51 @@ export default { }, }, computed: { - shouldShowParticipants() { - return this.searchKey === ''; - }, searchUsers() { - const allUsers = this.shouldShowParticipants - ? unionBy(this.users, this.participants, 'id') - : this.users; - return allUsers.map((user) => ({ + // when there is no search text, then we show selected users first + // followed by participants, then all other users + if (this.searchKey === '') { + const alphabetizedUsers = unionBy(this.users, this.participants, 'id').sort( + sortNameAlphabetically, + ); + + if (alphabetizedUsers.length === 0) { + return []; + } + + const currentUser = alphabetizedUsers.find(({ id }) => id === this.currentUser?.id); + + const allUsers = unionBy([currentUser], alphabetizedUsers, 'id').map((user) => ({ + ...user, + value: user?.id, + text: user?.name, + })); + + const selectedUsers = + allUsers + .filter(({ id }) => this.assigneeIdsToShowAtTopOfTheListbox.includes(id)) + .sort(sortNameAlphabetically) || []; + + const unselectedUsers = allUsers.filter( + ({ id }) => !this.assigneeIdsToShowAtTopOfTheListbox.includes(id), + ); + + // don't show the selected section if it's empty + if (selectedUsers.length === 0) { + return allUsers.map((user) => ({ + ...user, + value: user?.id, + text: user?.name, + })); + } + + return [ + { options: selectedUsers, text: __('Selected') }, + { options: unselectedUsers, text: __('All users'), textSrOnly: true }, + ]; + } + + return this.users.map((user) => ({ ...user, value: user?.id, text: user?.name, @@ -174,8 +212,15 @@ export default { assignees: { handler(newVal) { this.localAssigneeIds = newVal.map(({ id }) => id); + this.assigneeIdsToShowAtTopOfTheListbox = this.localAssigneeIds; }, deep: true, + immediate: true, + }, + searchKey(newVal, oldVal) { + if (newVal === '' && oldVal !== '') { + this.assigneeIdsToShowAtTopOfTheListbox = this.localAssigneeIds; + } }, }, methods: { @@ -241,7 +286,8 @@ export default { this.searchStarted = true; }, onDropdownHide() { - this.setSearchKey('', false); + this.setSearchKey(''); + this.assigneeIdsToShowAtTopOfTheListbox = this.localAssigneeIds; }, }, }; @@ -271,7 +317,7 @@ export default { @dropdownHidden="onDropdownHide" >