Add latest changes from gitlab-org/gitlab@master
|  | @ -11,7 +11,6 @@ Layout/SpaceInsideParens: | |||
|     - 'spec/lib/error_tracking/sentry_client/projects_spec.rb' | ||||
|     - 'spec/lib/error_tracking/sentry_client/repo_spec.rb' | ||||
|     - 'spec/lib/feature/gitaly_spec.rb' | ||||
|     - 'spec/lib/gitlab/app_text_logger_spec.rb' | ||||
|     - 'spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb' | ||||
|     - 'spec/lib/gitlab/ci/config/entry/reports_spec.rb' | ||||
|     - 'spec/lib/gitlab/ci/config/entry/trigger_spec.rb' | ||||
|  |  | |||
|  | @ -1087,7 +1087,6 @@ RSpec/FeatureCategory: | |||
|     - 'ee/spec/services/boards/lists/update_service_spec.rb' | ||||
|     - 'ee/spec/services/boards/update_service_spec.rb' | ||||
|     - 'ee/spec/services/boards/user_preferences/update_service_spec.rb' | ||||
|     - 'ee/spec/services/branches/delete_service_spec.rb' | ||||
|     - 'ee/spec/services/external_status_checks/create_service_spec.rb' | ||||
|     - 'ee/spec/services/projects/alerting/notify_service_spec.rb' | ||||
|     - 'ee/spec/services/projects/cleanup_service_spec.rb' | ||||
|  |  | |||
|  | @ -1843,7 +1843,6 @@ Style/InlineDisableAnnotation: | |||
|     - 'ee/spec/serializers/vulnerabilities/issue_link_entity_spec.rb' | ||||
|     - 'ee/spec/serializers/vulnerabilities/merge_request_link_entity_spec.rb' | ||||
|     - 'ee/spec/services/app_sec/fuzzing/api/ci_configuration_create_service_spec.rb' | ||||
|     - 'ee/spec/services/branches/delete_service_spec.rb' | ||||
|     - 'ee/spec/services/ee/branches/delete_service_spec.rb' | ||||
|     - 'ee/spec/services/ee/resource_events/change_iteration_service_spec.rb' | ||||
|     - 'ee/spec/services/geo/file_registry_removal_service_spec.rb' | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ | |||
| {"name":"apollo_upload_server","version":"2.1.6","platform":"ruby","checksum":"dcec4072258e6518b0b82e03b485efbddde946813543c14184fc81952d6bcdb2"}, | ||||
| {"name":"app_store_connect","version":"0.29.0","platform":"ruby","checksum":"01d7a923825a4221892099acb5a72f86f6ee7d8aa95815d3c459ba6816ea430f"}, | ||||
| {"name":"arr-pm","version":"0.0.12","platform":"ruby","checksum":"fdff482f75239239201f4d667d93424412639aad0b3b0ad4d827e7c637e0ad39"}, | ||||
| {"name":"asciidoctor","version":"2.0.18","platform":"ruby","checksum":"bbd1e1d16deed8db94bf9624b9f4474fac32d9ca7225d377f076c08d9adde387"}, | ||||
| {"name":"asciidoctor","version":"2.0.23","platform":"ruby","checksum":"52208807f237dfa0ca29882f8b13d60b820496116ad191cf197ca56f2b7fddf3"}, | ||||
| {"name":"asciidoctor-include-ext","version":"0.4.0","platform":"ruby","checksum":"406adb9d2fbfc25536609ca13b787ed704dc06a4e49d6709b83f3bad578f7878"}, | ||||
| {"name":"asciidoctor-kroki","version":"0.10.0","platform":"ruby","checksum":"8e4225d88f120e2e7b5d3f5ddb67c5e69496d7344a16c57db5036ac900123062"}, | ||||
| {"name":"asciidoctor-plantuml","version":"0.0.16","platform":"ruby","checksum":"407e47cd1186ded5ccc75f0c812e5524c26c571d542247c5132abb8f47bd1793"}, | ||||
|  |  | |||
|  | @ -316,7 +316,7 @@ GEM | |||
|       activesupport (>= 6.0.0) | ||||
|       jwt (>= 1.4, <= 2.5.0) | ||||
|     arr-pm (0.0.12) | ||||
|     asciidoctor (2.0.18) | ||||
|     asciidoctor (2.0.23) | ||||
|     asciidoctor-include-ext (0.4.0) | ||||
|       asciidoctor (>= 1.5.6, < 3.0.0) | ||||
|     asciidoctor-kroki (0.10.0) | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ | |||
| {"name":"apollo_upload_server","version":"2.1.6","platform":"ruby","checksum":"dcec4072258e6518b0b82e03b485efbddde946813543c14184fc81952d6bcdb2"}, | ||||
| {"name":"app_store_connect","version":"0.29.0","platform":"ruby","checksum":"01d7a923825a4221892099acb5a72f86f6ee7d8aa95815d3c459ba6816ea430f"}, | ||||
| {"name":"arr-pm","version":"0.0.12","platform":"ruby","checksum":"fdff482f75239239201f4d667d93424412639aad0b3b0ad4d827e7c637e0ad39"}, | ||||
| {"name":"asciidoctor","version":"2.0.18","platform":"ruby","checksum":"bbd1e1d16deed8db94bf9624b9f4474fac32d9ca7225d377f076c08d9adde387"}, | ||||
| {"name":"asciidoctor","version":"2.0.23","platform":"ruby","checksum":"52208807f237dfa0ca29882f8b13d60b820496116ad191cf197ca56f2b7fddf3"}, | ||||
| {"name":"asciidoctor-include-ext","version":"0.4.0","platform":"ruby","checksum":"406adb9d2fbfc25536609ca13b787ed704dc06a4e49d6709b83f3bad578f7878"}, | ||||
| {"name":"asciidoctor-kroki","version":"0.10.0","platform":"ruby","checksum":"8e4225d88f120e2e7b5d3f5ddb67c5e69496d7344a16c57db5036ac900123062"}, | ||||
| {"name":"asciidoctor-plantuml","version":"0.0.16","platform":"ruby","checksum":"407e47cd1186ded5ccc75f0c812e5524c26c571d542247c5132abb8f47bd1793"}, | ||||
|  | @ -342,7 +342,7 @@ | |||
| {"name":"io-event","version":"1.6.5","platform":"ruby","checksum":"5da4c044ac5f411563da1a4743d28c8d30d7802e29370db42139a52b807b4ce2"}, | ||||
| {"name":"ipaddr","version":"1.2.5","platform":"ruby","checksum":"4e679c71d6d8ed99f925487082f70f9a958de155591caa0e7f6cef9aa160f17a"}, | ||||
| {"name":"ipaddress","version":"0.8.3","platform":"ruby","checksum":"85640c4f9194c26937afc8c78e3074a8e7c97d5d1210358d1440f01034d006f5"}, | ||||
| {"name":"irb","version":"1.14.0","platform":"ruby","checksum":"53d805013bbd194874b8c13a56aca6aebcd11dd79166d88724f8a434fedde615"}, | ||||
| {"name":"irb","version":"1.14.1","platform":"ruby","checksum":"5975003b58d36efaf492380baa982ceedf5aed36967a4d5b40996bc5c66e80f8"}, | ||||
| {"name":"jaeger-client","version":"1.1.0","platform":"ruby","checksum":"cb5e9b9bbee6ee8d6a82d03d947a5b04543d8c0a949c22e484254f18d8a458a8"}, | ||||
| {"name":"jaro_winkler","version":"1.5.6","platform":"java","checksum":"3262aea433861fec3179184e9adc1933cca8bc15665957a143b56816f1a22f74"}, | ||||
| {"name":"jaro_winkler","version":"1.5.6","platform":"ruby","checksum":"007db7805527ada1cc12f2547676181d63b0a504ec4dd7a9a2eb2424521ccd81"}, | ||||
|  |  | |||
|  | @ -325,7 +325,7 @@ GEM | |||
|       activesupport (>= 6.0.0) | ||||
|       jwt (>= 1.4, <= 2.5.0) | ||||
|     arr-pm (0.0.12) | ||||
|     asciidoctor (2.0.18) | ||||
|     asciidoctor (2.0.23) | ||||
|     asciidoctor-include-ext (0.4.0) | ||||
|       asciidoctor (>= 1.5.6, < 3.0.0) | ||||
|     asciidoctor-kroki (0.10.0) | ||||
|  | @ -1022,7 +1022,7 @@ GEM | |||
|     io-event (1.6.5) | ||||
|     ipaddr (1.2.5) | ||||
|     ipaddress (0.8.3) | ||||
|     irb (1.14.0) | ||||
|     irb (1.14.1) | ||||
|       rdoc (>= 4.0.0) | ||||
|       reline (>= 0.4.2) | ||||
|     jaeger-client (1.1.0) | ||||
|  |  | |||
|  | @ -91,6 +91,11 @@ export default { | |||
|       // Don't do anything if this happened on a no trigger element | ||||
|       if (e.target.closest('.js-no-trigger')) return; | ||||
| 
 | ||||
|       if (e.target.closest('.js-no-trigger-title') && (e.ctrlKey || e.metaKey || e.button === 1)) { | ||||
|         return; | ||||
|       } | ||||
|       e.preventDefault(); | ||||
| 
 | ||||
|       const isMultiSelect = e.ctrlKey || e.metaKey; | ||||
|       if (isMultiSelect && gon?.features?.boardMultiSelect) { | ||||
|         this.toggleBoardItemMultiSelection(this.item); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import { GlLabel, GlTooltipDirective, GlIcon, GlLoadingIcon } from '@gitlab/ui'; | |||
| import { sortBy } from 'lodash'; | ||||
| import boardCardInner from 'ee_else_ce/boards/mixins/board_card_inner'; | ||||
| import { isScopedLabel, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; | ||||
| import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; | ||||
| import { updateHistory, queryToObject } from '~/lib/utils/url_utility'; | ||||
| import { sprintf, __, n__ } from '~/locale'; | ||||
| import isShowingLabelsQuery from '~/graphql_shared/client/is_showing_labels.query.graphql'; | ||||
|  | @ -36,7 +37,7 @@ export default { | |||
|   directives: { | ||||
|     GlTooltip: GlTooltipDirective, | ||||
|   }, | ||||
|   mixins: [boardCardInner], | ||||
|   mixins: [boardCardInner, glFeatureFlagsMixin()], | ||||
|   inject: [ | ||||
|     'allowSubEpics', | ||||
|     'rootPath', | ||||
|  | @ -260,8 +261,13 @@ export default { | |||
|         <a | ||||
|           :href="item.path || item.webUrl || ''" | ||||
|           :title="item.title" | ||||
|           :class="{ '!gl-text-gray-400': isLoading }" | ||||
|           class="js-no-trigger gl-text-primary hover:gl-text-gray-900" | ||||
|           :class="{ | ||||
|             '!gl-text-gray-400': isLoading, | ||||
|             'js-no-trigger': !glFeatures.issuesListDrawer, | ||||
|             'js-no-trigger-title': glFeatures.issuesListDrawer, | ||||
|           }" | ||||
|           class="gl-text-primary hover:gl-text-gray-900" | ||||
|           data-testid="board-card-title-link" | ||||
|           @mousemove.stop | ||||
|           >{{ item.title }}</a | ||||
|         > | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ import produce from 'immer'; | |||
| import Draggable from 'vuedraggable'; | ||||
| import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue'; | ||||
| import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue'; | ||||
| import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; | ||||
| import WorkItemDrawer from '~/work_items/components/work_item_drawer.vue'; | ||||
| import { s__ } from '~/locale'; | ||||
| import { defaultSortableOptions, DRAG_DELAY } from '~/sortable/constants'; | ||||
|  | @ -39,7 +38,6 @@ export default { | |||
|     GlAlert, | ||||
|     WorkItemDrawer, | ||||
|   }, | ||||
|   mixins: [glFeatureFlagsMixin()], | ||||
|   inject: [ | ||||
|     'boardType', | ||||
|     'canAdminList', | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ import { GlDrawer } from '@gitlab/ui'; | |||
| import { DRAWER_Z_INDEX } from '~/lib/utils/constants'; | ||||
| import { getContentWrapperHeight } from '~/lib/utils/dom_utils'; | ||||
| import getMergeRequestReviewers from '~/sidebar/queries/get_merge_request_reviewers.query.graphql'; | ||||
| import ReviewersContainer from './reviewers_container.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   apollo: { | ||||
|  | @ -23,7 +22,6 @@ export default { | |||
|   }, | ||||
|   components: { | ||||
|     GlDrawer, | ||||
|     ReviewersContainer, | ||||
|     ApprovalSummary: () => | ||||
|       import('ee_component/merge_requests/components/reviewers/approval_summary.vue'), | ||||
|     ApprovalRulesWrapper: () => | ||||
|  | @ -65,14 +63,12 @@ export default { | |||
|       <h4 class="gl-my-0">{{ __('Assign reviewers') }}</h4> | ||||
|     </template> | ||||
|     <template #header> | ||||
|       <approval-summary /> | ||||
|       <approval-summary class="gl-mt-3" /> | ||||
|     </template> | ||||
|     <reviewers-container | ||||
|     <approval-rules-wrapper | ||||
|       :reviewers="reviewers" | ||||
|       :loading-reviewers="loadingReviewers" | ||||
|       @request-review="(params) => $emit('request-review', params)" | ||||
|       @remove-reviewer="(params) => $emit('remove-reviewer', params)" | ||||
|       @request-review="(data) => $emit('request-review', data)" | ||||
|       @remove-reviewer="(data) => $emit('remove-reviewer', data)" | ||||
|     /> | ||||
|     <approval-rules-wrapper :reviewers="reviewers" /> | ||||
|   </gl-drawer> | ||||
| </template> | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ import userPermissionsQuery from './queries/user_permissions.query.graphql'; | |||
| 
 | ||||
| export default { | ||||
|   apollo: { | ||||
|     // eslint-disable-next-line @gitlab/vue-no-undef-apollo-properties | ||||
|     userPermissions: { | ||||
|       query: userPermissionsQuery, | ||||
|       variables() { | ||||
|  | @ -50,6 +49,7 @@ export default { | |||
|       searching: false, | ||||
|       fetchedUsers: [], | ||||
|       currentSelectedReviewers: this.selectedReviewers.map((r) => r.username), | ||||
|       userPermissions: {}, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|  | @ -141,18 +141,16 @@ export default { | |||
| 
 | ||||
| <template> | ||||
|   <update-reviewers | ||||
|     v-if="userPermissions && userPermissions.adminMergeRequest" | ||||
|     v-if="userPermissions.adminMergeRequest" | ||||
|     :selected-reviewers="currentSelectedReviewers" | ||||
|   > | ||||
|     <template #default="{ loading, updateReviewers }"> | ||||
|       <gl-collapsible-listbox | ||||
|         v-model="currentSelectedReviewers" | ||||
|         icon="plus" | ||||
|         :toggle-text="$options.i18n.selectReviewer" | ||||
|         :toggle-text="__('Edit')" | ||||
|         toggle-class="!gl-text-primary" | ||||
|         :header-text="$options.i18n.selectReviewer" | ||||
|         :reset-button-label="$options.i18n.unassign" | ||||
|         text-sr-only | ||||
|         category="tertiary" | ||||
|         no-caret | ||||
|         size="small" | ||||
|  |  | |||
|  | @ -1,115 +0,0 @@ | |||
| <script> | ||||
| import { GlEmptyState, GlButton } from '@gitlab/ui'; | ||||
| import noReviewersAssignedSvg from '@gitlab/svgs/dist/illustrations/add-user-sm.svg?url'; | ||||
| import UncollapsedReviewerList from '~/sidebar/components/reviewers/uncollapsed_reviewer_list.vue'; | ||||
| import { sprintf, __, n__ } from '~/locale'; | ||||
| import ReviewerDropdown from '~/merge_requests/components/reviewers/reviewer_dropdown.vue'; | ||||
| import UpdateReviewers from './update_reviewers.vue'; | ||||
| import userPermissionsQuery from './queries/user_permissions.query.graphql'; | ||||
| 
 | ||||
| export default { | ||||
|   apollo: { | ||||
|     userPermissions: { | ||||
|       query: userPermissionsQuery, | ||||
|       variables() { | ||||
|         return { | ||||
|           fullPath: this.projectPath, | ||||
|           iid: this.issuableIid, | ||||
|         }; | ||||
|       }, | ||||
|       update: (data) => data.project?.mergeRequest?.userPermissions || {}, | ||||
|     }, | ||||
|   }, | ||||
|   components: { | ||||
|     GlEmptyState, | ||||
|     GlButton, | ||||
|     UncollapsedReviewerList, | ||||
|     UpdateReviewers, | ||||
|     ReviewerDropdown, | ||||
|   }, | ||||
|   inject: ['projectPath', 'issuableIid'], | ||||
|   props: { | ||||
|     reviewers: { | ||||
|       type: Array, | ||||
|       required: true, | ||||
|     }, | ||||
|     loadingReviewers: { | ||||
|       type: Boolean, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       userPermissions: {}, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     currentUser() { | ||||
|       return [gon.current_username]; | ||||
|     }, | ||||
|     relativeUrlRoot() { | ||||
|       return gon.relative_url_root ?? ''; | ||||
|     }, | ||||
|     reviewersTitle() { | ||||
|       if (this.reviewers.length === 0) { | ||||
|         return sprintf(__('%{count} Reviewers'), { count: this.reviewers.length }); | ||||
|       } | ||||
| 
 | ||||
|       return sprintf(n__('%{count} Reviewer', '%{count} Reviewers', this.reviewers.length), { | ||||
|         count: this.reviewers.length, | ||||
|       }); | ||||
|     }, | ||||
|   }, | ||||
|   noReviewersAssignedSvg, | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div> | ||||
|     <div class="gl-mb-3 gl-flex gl-w-full gl-items-center gl-font-bold gl-leading-20"> | ||||
|       <template v-if="loadingReviewers"> | ||||
|         <div class="gl-animate-skeleton-loader gl-h-4 gl-w-20 gl-rounded-base"></div> | ||||
|         <div class="gl-animate-skeleton-loader gl-ml-auto gl-h-4 gl-w-4 gl-rounded-base"></div> | ||||
|       </template> | ||||
|       <template v-else> | ||||
|         {{ reviewersTitle }} | ||||
|         <reviewer-dropdown :selected-reviewers="reviewers" class="gl-ml-auto" /> | ||||
|       </template> | ||||
|     </div> | ||||
|     <div v-if="loadingReviewers"> | ||||
|       <div class="gl-animate-skeleton-loader gl-mb-3 gl-h-4 !gl-max-w-20 gl-rounded-base"></div> | ||||
|       <div class="gl-animate-skeleton-loader gl-mb-3 gl-h-4 !gl-max-w-20 gl-rounded-base"></div> | ||||
|       <div class="gl-animate-skeleton-loader gl-h-4 !gl-max-w-20 gl-rounded-base"></div> | ||||
|     </div> | ||||
|     <uncollapsed-reviewer-list | ||||
|       v-else-if="reviewers.length" | ||||
|       :is-editable="userPermissions.adminMergeRequest" | ||||
|       :root-path="relativeUrlRoot" | ||||
|       :users="reviewers" | ||||
|       issuable-type="merge_request" | ||||
|       @request-review="(params) => $emit('request-review', params)" | ||||
|       @remove-reviewer="(params) => $emit('remove-reviewer', params)" | ||||
|     /> | ||||
|     <gl-empty-state v-else :svg-path="$options.noReviewersAssignedSvg" :svg-height="70"> | ||||
|       <template #description> | ||||
|         <p class="gl-mb-3 gl-font-normal">{{ __('No reviewers assigned') }}</p> | ||||
|         <update-reviewers | ||||
|           v-if="userPermissions.adminMergeRequest" | ||||
|           :selected-reviewers="currentUser" | ||||
|         > | ||||
|           <template #default="{ loading, updateReviewers }"> | ||||
|             <gl-button | ||||
|               category="tertiary" | ||||
|               size="small" | ||||
|               :loading="loading" | ||||
|               data-testid="assign-yourself-button" | ||||
|               @click="updateReviewers" | ||||
|             > | ||||
|               {{ __('Assign yourself') }} | ||||
|             </gl-button> | ||||
|           </template> | ||||
|         </update-reviewers> | ||||
|       </template> | ||||
|     </gl-empty-state> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -1,19 +1,17 @@ | |||
| <script> | ||||
| // NOTE! For the first iteration, we are simply copying the implementation of Assignees | ||||
| // It will soon be overhauled in Issue https://gitlab.com/gitlab-org/gitlab/-/issues/233736 | ||||
| import { MountingPortal } from 'portal-vue'; | ||||
| import { GlLoadingIcon, GlButton, GlTooltipDirective } from '@gitlab/ui'; | ||||
| import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; | ||||
| import { n__, s__ } from '~/locale'; | ||||
| import ReviewerDrawer from '~/merge_requests/components/reviewers/reviewer_drawer.vue'; | ||||
| import ReviewerDropdown from '~/merge_requests/components/reviewers/reviewer_dropdown.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'ReviewerTitle', | ||||
|   components: { | ||||
|     MountingPortal, | ||||
|     GlLoadingIcon, | ||||
|     GlButton, | ||||
|     ReviewerDrawer, | ||||
|     ReviewerDropdown, | ||||
|   }, | ||||
|   directives: { | ||||
|     Tooltip: GlTooltipDirective, | ||||
|  | @ -33,11 +31,11 @@ export default { | |||
|       type: Boolean, | ||||
|       required: true, | ||||
|     }, | ||||
|     reviewers: { | ||||
|       type: Array, | ||||
|       required: false, | ||||
|       default: () => [], | ||||
|     }, | ||||
|   data() { | ||||
|     return { | ||||
|       drawerOpen: false, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     reviewerTitle() { | ||||
|  | @ -45,17 +43,8 @@ export default { | |||
|       return n__('Reviewer', `%d Reviewers`, reviewers); | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     toggleDrawerOpen(drawerOpen = !this.drawerOpen) { | ||||
|       if (!this.glFeatures.reviewerAssignDrawer) return; | ||||
| 
 | ||||
|       this.drawerOpen = drawerOpen; | ||||
|     }, | ||||
|   }, | ||||
|   i18n: { | ||||
|     addEditReviewers: s__('MergeRequest|Add or edit reviewers'), | ||||
|     changeReviewer: s__('MergeRequest|Change reviewer'), | ||||
|     quickAdd: s__('MergeRequest|Quick add reviewers'), | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | @ -66,33 +55,25 @@ export default { | |||
|     {{ reviewerTitle }} | ||||
|     <gl-loading-icon v-if="loading" size="sm" inline class="align-bottom" /> | ||||
|     <template v-if="editable"> | ||||
|       <reviewer-dropdown | ||||
|         v-if="glFeatures.reviewerAssignDrawer" | ||||
|         class="gl-ml-auto" | ||||
|         :selected-reviewers="reviewers" | ||||
|       /> | ||||
|       <gl-button | ||||
|         v-else | ||||
|         v-tooltip.hover | ||||
|         :title=" | ||||
|           glFeatures.reviewerAssignDrawer | ||||
|             ? $options.i18n.addEditReviewers | ||||
|             : $options.i18n.changeReviewer | ||||
|         " | ||||
|         class="hide-collapsed gl-float-right gl-ml-auto" | ||||
|         :class="{ 'js-sidebar-dropdown-toggle edit-link': !glFeatures.reviewerAssignDrawer }" | ||||
|         :title="$options.i18n.changeReviewer" | ||||
|         class="hide-collapsed js-sidebar-dropdown-toggle edit-link gl-float-right gl-ml-auto" | ||||
|         data-track-action="click_edit_button" | ||||
|         data-track-label="right_sidebar" | ||||
|         data-track-property="reviewer" | ||||
|         :data-testid="glFeatures.reviewerAssignDrawer ? 'drawer-toggle' : 'reviewers-edit-button'" | ||||
|         data-testid="reviewers-edit-button" | ||||
|         category="tertiary" | ||||
|         size="small" | ||||
|         @click="toggleDrawerOpen(!drawerOpen)" | ||||
|       > | ||||
|         {{ __('Edit') }} | ||||
|       </gl-button> | ||||
|     </template> | ||||
|     <mounting-portal v-if="glFeatures.reviewerAssignDrawer" mount-to="#js-reviewer-drawer-portal"> | ||||
|       <reviewer-drawer | ||||
|         :open="drawerOpen" | ||||
|         @request-review="(params) => $emit('request-review', params)" | ||||
|         @remove-reviewer="(data) => $emit('remove-reviewer', data)" | ||||
|         @close="toggleDrawerOpen(false)" | ||||
|       /> | ||||
|     </mounting-portal> | ||||
|   </div> | ||||
| </template> | ||||
|  |  | |||
|  | @ -2,11 +2,15 @@ | |||
| // NOTE! For the first iteration, we are simply copying the implementation of Assignees | ||||
| // It will soon be overhauled in Issue https://gitlab.com/gitlab-org/gitlab/-/issues/233736 | ||||
| import Vue from 'vue'; | ||||
| import { MountingPortal } from 'portal-vue'; | ||||
| import { GlButton } from '@gitlab/ui'; | ||||
| import { createAlert } from '~/alert'; | ||||
| import { TYPE_ISSUE } from '~/issues/constants'; | ||||
| import { __ } from '~/locale'; | ||||
| import { isGid, getIdFromGraphQLId } from '~/graphql_shared/utils'; | ||||
| import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; | ||||
| import { fetchUserCounts } from '~/super_sidebar/user_counts_fetch'; | ||||
| import ReviewerDrawer from '~/merge_requests/components/reviewers/reviewer_drawer.vue'; | ||||
| import eventHub from '../../event_hub'; | ||||
| import getMergeRequestReviewersQuery from '../../queries/get_merge_request_reviewers.query.graphql'; | ||||
| import mergeRequestReviewersUpdatedSubscription from '../../queries/merge_request_reviewers.subscription.graphql'; | ||||
|  | @ -18,14 +22,21 @@ export const state = Vue.observable({ | |||
|   issuable: {}, | ||||
|   loading: false, | ||||
|   initialLoading: true, | ||||
|   drawerOpen: false, | ||||
| }); | ||||
| 
 | ||||
| export default { | ||||
|   name: 'SidebarReviewers', | ||||
|   components: { | ||||
|     MountingPortal, | ||||
|     GlButton, | ||||
|     ReviewerTitle, | ||||
|     Reviewers, | ||||
|     ReviewerDrawer, | ||||
|     ApprovalSummary: () => | ||||
|       import('ee_component/merge_requests/components/reviewers/approval_summary.vue'), | ||||
|   }, | ||||
|   mixins: [glFeatureFlagsMixin()], | ||||
|   props: { | ||||
|     mediator: { | ||||
|       type: Object, | ||||
|  | @ -132,12 +143,14 @@ export default { | |||
|     eventHub.$on('sidebar.addReviewer', this.addReviewer); | ||||
|     eventHub.$on('sidebar.removeAllReviewers', this.removeAllReviewers); | ||||
|     eventHub.$on('sidebar.saveReviewers', this.saveReviewers); | ||||
|     eventHub.$on('sidebar.toggleReviewerDrawer', this.toggleDrawerOpen); | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
|     eventHub.$off('sidebar.removeReviewer', this.removeReviewer); | ||||
|     eventHub.$off('sidebar.addReviewer', this.addReviewer); | ||||
|     eventHub.$off('sidebar.removeAllReviewers', this.removeAllReviewers); | ||||
|     eventHub.$off('sidebar.saveReviewers', this.saveReviewers); | ||||
|     eventHub.$off('sidebar.toggleReviewerDrawer', this.toggleDrawerOpen); | ||||
|   }, | ||||
|   methods: { | ||||
|     reviewBySelf() { | ||||
|  | @ -178,6 +191,11 @@ export default { | |||
|         event.done(); | ||||
|       } | ||||
|     }, | ||||
|     toggleDrawerOpen(drawerOpen = !this.drawerOpen) { | ||||
|       if (!this.glFeatures.reviewerAssignDrawer) return; | ||||
| 
 | ||||
|       this.drawerOpen = drawerOpen; | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | @ -185,12 +203,24 @@ export default { | |||
| <template> | ||||
|   <div> | ||||
|     <reviewer-title | ||||
|       :reviewers="reviewers" | ||||
|       :number-of-reviewers="reviewers.length" | ||||
|       :loading="isLoading" | ||||
|       :editable="canUpdate" | ||||
|       @request-review="requestReview" | ||||
|       @remove-reviewer="removeReviewerById" | ||||
|     /> | ||||
|     <approval-summary v-if="glFeatures.reviewerAssignDrawer" short-text class="gl-mb-2"> | ||||
|       <gl-button | ||||
|         size="small" | ||||
|         category="tertiary" | ||||
|         variant="confirm" | ||||
|         class="gl-ml-3" | ||||
|         @click="toggleDrawerOpen()" | ||||
|       > | ||||
|         {{ __('Assign') }} | ||||
|       </gl-button> | ||||
|     </approval-summary> | ||||
|     <reviewers | ||||
|       v-if="!initialLoading" | ||||
|       :root-path="relativeUrlRoot" | ||||
|  | @ -202,5 +232,13 @@ export default { | |||
|       @assign-self="reviewBySelf" | ||||
|       @remove-reviewer="removeReviewerById" | ||||
|     /> | ||||
|     <mounting-portal v-if="glFeatures.reviewerAssignDrawer" mount-to="#js-reviewer-drawer-portal"> | ||||
|       <reviewer-drawer | ||||
|         :open="drawerOpen" | ||||
|         @request-review="requestReview" | ||||
|         @remove-reviewer="removeReviewerById" | ||||
|         @close="toggleDrawerOpen(false)" | ||||
|       /> | ||||
|     </mounting-portal> | ||||
|   </div> | ||||
| </template> | ||||
|  |  | |||
|  | @ -122,7 +122,7 @@ export default { | |||
|       this.loadingStates[userId] = LOADING_STATE; | ||||
|       this.$emit('remove-reviewer', { | ||||
|         userId, | ||||
|         callback: () => this.requestRemovalComplete(userId), | ||||
|         done: () => this.requestRemovalComplete(userId), | ||||
|       }); | ||||
|     }, | ||||
|     requestReviewComplete(userId, success) { | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #import "~/graphql_shared/fragments/user.fragment.graphql" | ||||
| #import "~/graphql_shared/fragments/user_availability.fragment.graphql" | ||||
| #import "ee_else_ce/sidebar/queries/reviewer_applicable_approval_rules.fragment.graphql" | ||||
| 
 | ||||
| query mergeRequestReviewers($fullPath: ID!, $iid: String!) { | ||||
|   workspace: project(fullPath: $fullPath) { | ||||
|  | @ -15,6 +16,8 @@ query mergeRequestReviewers($fullPath: ID!, $iid: String!) { | |||
|             canUpdate | ||||
|             approved | ||||
|             reviewState | ||||
| 
 | ||||
|             ...ReviewersApplicableApprovalRules | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #import "~/graphql_shared/fragments/user.fragment.graphql" | ||||
| #import "~/graphql_shared/fragments/user_availability.fragment.graphql" | ||||
| #import "ee_else_ce/sidebar/queries/reviewer_applicable_approval_rules.fragment.graphql" | ||||
| 
 | ||||
| subscription mergeRequestReviewersUpdated($issuableId: IssuableID!) { | ||||
|   mergeRequestReviewersUpdated(issuableId: $issuableId) { | ||||
|  | @ -14,6 +15,8 @@ subscription mergeRequestReviewersUpdated($issuableId: IssuableID!) { | |||
|             canUpdate | ||||
|             approved | ||||
|             reviewState | ||||
| 
 | ||||
|             ...ReviewersApplicableApprovalRules | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  |  | |||
|  | @ -0,0 +1,4 @@ | |||
| fragment ReviewersApplicableApprovalRules on UserMergeRequestInteraction { | ||||
|   # Overriden in EE | ||||
|   approved | ||||
| } | ||||
|  | @ -66,11 +66,17 @@ export default { | |||
|       required: false, | ||||
|       default: false, | ||||
|     }, | ||||
|     actionButtons: { | ||||
|       type: Array, | ||||
|       required: false, | ||||
|       default: () => [], | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       hasApprovalAuthError: false, | ||||
|       isApproving: false, | ||||
|       userPermissions: {}, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|  | @ -276,6 +282,7 @@ export default { | |||
|       :collapsed="collapsed" | ||||
|       :expand-details-tooltip="__('Expand eligible approvers')" | ||||
|       :collapse-details-tooltip="__('Collapse eligible approvers')" | ||||
|       :actions="actionButtons" | ||||
|       @toggle="() => $emit('toggle')" | ||||
|     > | ||||
|       <template v-if="isLoading">{{ $options.FETCH_LOADING }}</template> | ||||
|  |  | |||
|  | @ -12,6 +12,8 @@ export const COMPONENTS = { | |||
|     import('ee_component/vue_merge_request_widget/components/checks/locked_paths.vue'), | ||||
|   locked_lfs_files: () => | ||||
|     import('ee_component/vue_merge_request_widget/components/checks/locked_paths.vue'), | ||||
|   not_approved: () => | ||||
|     import('ee_component/vue_merge_request_widget/components/checks/not_approved.vue'), | ||||
| }; | ||||
| 
 | ||||
| export const FAILURE_REASONS = { | ||||
|  |  | |||
|  | @ -103,6 +103,7 @@ export default { | |||
|           class="media-body gl-leading-normal" | ||||
|         > | ||||
|           <slot></slot> | ||||
|         </div> | ||||
|         <div | ||||
|           :class="{ | ||||
|             'state-container-action-buttons gl-flex-wrap lg:gl-justify-end': !actions.length, | ||||
|  | @ -114,7 +115,6 @@ export default { | |||
|             <actions v-if="actions.length" :tertiary-buttons="actions" /> | ||||
|           </slot> | ||||
|         </div> | ||||
|         </div> | ||||
|         <div | ||||
|           v-if="isCollapsible" | ||||
|           :class="{ 'md:gl-hidden': !collapseOnDesktop }" | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ module Import | |||
|   class SourceUsersController < ApplicationController | ||||
|     prepend_before_action :check_feature_flag! | ||||
| 
 | ||||
|     before_action :source_user | ||||
|     before_action :check_source_user_valid! | ||||
| 
 | ||||
|     respond_to :html | ||||
|  | @ -53,7 +52,7 @@ module Import | |||
|     strong_memoize_attr :source_user | ||||
| 
 | ||||
|     def check_feature_flag! | ||||
|       not_found unless Feature.enabled?(:importer_user_mapping, current_user) | ||||
|       not_found unless Feature.enabled?(:importer_user_mapping, source_user.reassigned_by_user) | ||||
|     end | ||||
| 
 | ||||
|     def banner(partial) | ||||
|  |  | |||
|  | @ -38,6 +38,8 @@ module ViteHelper | |||
|       options[:host] = URI::HTTP.build(host: ViteRuby.config.host, port: ViteRuby.config.port).to_s | ||||
|     end | ||||
| 
 | ||||
|     options[:extname] = false | ||||
| 
 | ||||
|     stylesheet_link_tag( | ||||
|       ViteRuby.instance.manifest.path_for("stylesheets/styles.#{path}.scss", type: :stylesheet), | ||||
|       **options | ||||
|  |  | |||
|  | @ -19,11 +19,6 @@ module Import | |||
|     def perform(import_source_user_id, _params = {}) | ||||
|       @import_source_user = Import::SourceUser.find_by_id(import_source_user_id) | ||||
| 
 | ||||
|       return unless Feature.enabled?( | ||||
|         :importer_user_mapping, | ||||
|         User.actor_from_id(import_source_user&.reassigned_by_user_id) | ||||
|       ) | ||||
| 
 | ||||
|       return unless import_source_user_valid? | ||||
| 
 | ||||
|       Import::ReassignPlaceholderUserRecordsService.new(import_source_user).execute | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| --- | ||||
| migration_job_name: BackfillApprovalGroupRulesProtectedBranchesGroupId | ||||
| description: Backfills sharding key `approval_group_rules_protected_branches.group_id` from `approval_group_rules`. | ||||
| description: Backfills sharding key `approval_group_rules_protected_branches.group_id` | ||||
|   from `approval_group_rules`. | ||||
| feature_category: source_code_management | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/159584 | ||||
| milestone: '17.2' | ||||
| queued_migration_version: 20240716135032 | ||||
| finalized_by: # version of the migration that finalized this BBM | ||||
| finalized_by: '20240926232010' | ||||
|  |  | |||
|  | @ -0,0 +1,21 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class FinalizeBackfillApprovalGroupRulesProtectedBranchesGroupId < Gitlab::Database::Migration[2.2] | ||||
|   milestone '17.5' | ||||
| 
 | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   restrict_gitlab_migration gitlab_schema: :gitlab_main_cell | ||||
| 
 | ||||
|   def up | ||||
|     ensure_batched_background_migration_is_finished( | ||||
|       job_class_name: 'BackfillApprovalGroupRulesProtectedBranchesGroupId', | ||||
|       table_name: :approval_group_rules_protected_branches, | ||||
|       column_name: :id, | ||||
|       job_arguments: [:group_id, :approval_group_rules, :group_id, :approval_group_rule_id], | ||||
|       finalize: true | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   def down; end | ||||
| end | ||||
|  | @ -0,0 +1 @@ | |||
| 33eb34ed1ba55339802c399e74aec4318a5e83fdbdd744e32632adbf6a452b93 | ||||
|  | @ -19,8 +19,11 @@ These options are in lowercase. | |||
| 
 | ||||
| ### Target Types | ||||
| 
 | ||||
| > - Support for epics [introduced](https://gitlab.com/groups/gitlab-org/-/epics/13056) in GitLab 17.3. | ||||
| 
 | ||||
| Available target types for the `target_type` parameter are: | ||||
| 
 | ||||
| - `epic` | ||||
| - `issue` | ||||
| - `milestone` | ||||
| - `merge_request` | ||||
|  | @ -31,7 +34,7 @@ Available target types for the `target_type` parameter are: | |||
| - `user` | ||||
| 
 | ||||
| These options are in lowercase. | ||||
| Events associated with epics are not available using the API. | ||||
| Some epic features like child items, linked items, start dates, due dates, and health statuses are not available using the API. | ||||
| Some discussions on merge requests may be of type `DiscussionNote`. These are not available using the API. | ||||
| 
 | ||||
| ### Date formatting | ||||
|  |  | |||
| Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB | 
|  | @ -22,7 +22,7 @@ environment, reducing the effort to assess the impact of changes. Thus, when we | |||
| and it will immediately be clear that the application can still be properly built and deployed. After all, you can _see_ it | ||||
| running! | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| However, looking at the freshly deployed code to check whether it still looks and behaves as | ||||
| expected is repetitive manual work, which means it is a prime candidate for automation. This is | ||||
|  |  | |||
| Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB | 
| Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB | 
| Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB | 
| Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB | 
| Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB | 
| Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB | 
| Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB | 
| Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB | 
| Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB | 
| Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB | 
| Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB | 
| Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB | 
|  | @ -127,7 +127,7 @@ They can be added per project by navigating to the project's **Settings** > **CI | |||
| To the field **KEY**, add the name `SSH_PRIVATE_KEY`, and to the **VALUE** field, paste the private key you've copied earlier. | ||||
| We'll use this variable in the `.gitlab-ci.yml` later, to easily connect to our remote server as the deployer user without entering its password. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| We also need to add the public key to **Project** > **Settings** > **Repository** as a [deploy key](../../../user/project/deploy_keys/index.md), which gives us the ability to access our repository from the server through the SSH protocol. | ||||
| 
 | ||||
|  | @ -140,7 +140,7 @@ cat ~/.ssh/id_rsa.pub | |||
| 
 | ||||
| To the field **Title**, add any name you want, and paste the public key into the **Key** field. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| Now, let's clone our repository on the server just to make sure the `deployer` user has access to the repository. | ||||
| 
 | ||||
|  | @ -442,7 +442,7 @@ Now that we have our `Dockerfile` let's build and push it to our [GitLab contain | |||
| 
 | ||||
| In your GitLab project repository, go to the **Registry** tab. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| You may need to enable the container registry for your project to see this tab. You'll find it under your project's **Settings > General > Visibility, project features, permissions**. | ||||
| 
 | ||||
|  | @ -464,7 +464,7 @@ docker push registry.gitlab.com/<USERNAME>/laravel-sample | |||
| 
 | ||||
| Congratulations! You just pushed the first Docker image to the GitLab Registry, and if you refresh the page you should be able to see it: | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| You can also [use GitLab CI/CD](https://about.gitlab.com/blog/2016/05/23/gitlab-container-registry/#use-with-gitlab-ci) to build and push your Docker images, rather than doing that on your machine. | ||||
| 
 | ||||
|  | @ -624,41 +624,41 @@ You may also want to add another job for [staging environment](https://about.git | |||
| We have prepared everything we need to test and deploy our app with GitLab CI/CD. | ||||
| To do that, commit and push `.gitlab-ci.yml` to the `main` branch. It will trigger a pipeline, which you can watch live under your project's **Pipelines**. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| Here we see our **Test** and **Deploy** stages. | ||||
| The **Test** stage has the `unit_test` build running. | ||||
| Select it to see the runner's output. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| After our code passed through the pipeline successfully, we can deploy to our production server by selecting **Run** (**{play}**) on the right side. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| After the deploy pipeline passes successfully, go to **Pipelines > Environments**. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| If something doesn't work as expected, you can roll back to the latest working version of your app. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| By selecting the external link icon specified on the right side, GitLab opens the production website. | ||||
| Our deployment successfully was done and we can see the application is live. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| In the case that you're interested to know how is the application directory structure on the production server after deployment, here are three directories named `current`, `releases` and `storage`. | ||||
| As you know, the `current` directory is a symbolic link that points to the latest release. | ||||
| The `.env` file consists of our Laravel environment variables. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| If you go to the `current` directory, you should see the application's content. | ||||
| As you see, the `.env` is pointing to the `/var/www/app/.env` file and also `storage` is pointing to the `/var/www/app/storage/` directory. | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| ## Conclusion | ||||
| 
 | ||||
|  |  | |||
|  | @ -52,7 +52,7 @@ To enable the plugin: | |||
| 1. In your IDE, on the top bar, select your IDE's name, then select **Settings**. | ||||
| 1. On the left sidebar, select **Plugins**. | ||||
| 1. Select the **GitLab Duo** plugin, and select **Install**. | ||||
| 1. Select **OK**. | ||||
| 1. Select **OK** or **Save**. | ||||
| 
 | ||||
| To configure the plugin in your IDE after you enable it: | ||||
| 
 | ||||
|  | @ -65,7 +65,7 @@ To configure the plugin in your IDE after you enable it: | |||
| 1. For **GitLab Personal Access Token**, paste in the personal access token you created. The token is not displayed, | ||||
|    nor is it accessible to others. | ||||
| 1. Select **Verify setup**. | ||||
| 1. Select **OK**. | ||||
| 1. Select **OK** or **Save**. | ||||
| 
 | ||||
| ### Enable experimental or beta features | ||||
| 
 | ||||
|  | @ -110,7 +110,7 @@ To use a custom SSL certificate with GitLab Duo: | |||
| 1. On the left sidebar, expand **Tools**, then select **GitLab Duo**. | ||||
| 1. Under **Connection**, enter the **URL to GitLab instance**. | ||||
| 1. To verify your connection, select **Verify setup**. | ||||
| 1. Select **OK**. | ||||
| 1. Select **OK** or **Save**. | ||||
| 
 | ||||
| If your IDE detects a non-trusted SSL certificate: | ||||
| 
 | ||||
|  | @ -161,18 +161,7 @@ From the IDE: | |||
| 1. Select **Integrate with 1Password CLI**. | ||||
| 1. Optional. For **Secret reference**, paste the secret reference you copied from 1Password. | ||||
| 1. Optional. To verify your credentials, select **Verify setup**. | ||||
| 1. Select **OK**. | ||||
| 
 | ||||
| ## Toggle sending open tabs as context | ||||
| 
 | ||||
| By default, the Code Suggestions use the files open in your IDE for context. | ||||
| To enable or disable this feature in your IDE: | ||||
| 
 | ||||
| 1. Go to your IDE's top menu bar and select **Settings**. | ||||
| 1. On the left sidebar, expand **Tools**, then select **GitLab Duo**. | ||||
| 1. Expand **GitLab Language Server**. | ||||
| 1. Under **Code Completion**, select or clear **Send open tabs as context**. | ||||
| 1. Select **OK**. | ||||
| 1. Select **OK** or **Save**. | ||||
| 
 | ||||
| ## Report issues with the plugin | ||||
| 
 | ||||
|  | @ -195,6 +184,7 @@ built-in error reporting tool: | |||
| 
 | ||||
| ## Related topics | ||||
| 
 | ||||
| - [Code Suggestions](../../user/project/repository/code_suggestions/index.md) | ||||
| - [About the Create:Editor Extensions Group](https://handbook.gitlab.com/handbook/engineering/development/dev/create/editor-extensions/) | ||||
| - [Open issues for this plugin](https://gitlab.com/gitlab-org/editor-extensions/gitlab-jetbrains-plugin/-/issues/) | ||||
| - [Plugin documentation](https://gitlab.com/gitlab-org/editor-extensions/gitlab-jetbrains-plugin/-/blob/main/README.md) | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ To enable debug logs in JetBrains: | |||
| 1. On the top bar, go to **Help > Diagnostic Tools > Debug Log Settings**, or | ||||
|    search for the action by going to **Help > Find Action > Debug log settings**. | ||||
| 1. Add this line: `com.gitlab.plugin` | ||||
| 1. Select **OK**. | ||||
| 1. Select **OK** or **Save**. | ||||
| 
 | ||||
| The debug logs are available in the `idea.log` log file. | ||||
| 
 | ||||
|  | @ -66,8 +66,7 @@ To do this: | |||
| 1. Confirm your default browser trusts the **URL to GitLab instance** you're using. | ||||
| 1. Enable the **Ignore certificate errors** option. | ||||
| 1. Select **Verify setup**. | ||||
| 1. Select **Apply**. | ||||
| 1. Select **OK**. | ||||
| 1. Select **OK** or **Save**. | ||||
| 
 | ||||
| ## Error: `Failed to check token` | ||||
| 
 | ||||
|  | @ -79,4 +78,4 @@ GitLab Language Server process are invalid. To re-enable Code Suggestions: | |||
| 1. Under **Connection**, select **Verify setup**. | ||||
| 1. Update your **Connection** details as needed. | ||||
| 1. Select **Verify setup**, and confirm that authentication succeeds. | ||||
| 1. Select **OK**. | ||||
| 1. Select **OK** or **Save**. | ||||
|  |  | |||
| After Width: | Height: | Size: 3.7 KiB | 
|  | @ -114,15 +114,6 @@ This extension brings the GitLab features you use every day directly into your V | |||
| 
 | ||||
| For detailed information on these features, refer to the [GitLab Workflow extension documentation](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/README.md). | ||||
| 
 | ||||
| ## Troubleshooting | ||||
| 
 | ||||
| If you encounter any issues or have feature requests: | ||||
| 
 | ||||
| 1. Check the [extension documentation](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/README.md) | ||||
|    for known issues and solutions. | ||||
| 1. Report bugs or request features in the | ||||
|    [`gitlab-vscode-extension` issue queue](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues). | ||||
| 
 | ||||
| ## Related topics | ||||
| 
 | ||||
| - [Download the GitLab Workflow extension](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow) | ||||
|  |  | |||
|  | @ -0,0 +1,68 @@ | |||
| --- | ||||
| stage: Create | ||||
| group: Editor Extensions | ||||
| info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments | ||||
| --- | ||||
| 
 | ||||
| # Using the VS Code extension with self-signed certificates | ||||
| 
 | ||||
| You can still use the GitLab Workflow extension for VS Code even if your GitLab instance uses a self-signed SSL certificate. | ||||
| 
 | ||||
| If you also use a proxy to connect to your GitLab instance, let us know in | ||||
| [issue 314](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/314). If you still have connection problems | ||||
| after completing these steps, review [epic 6244](https://gitlab.com/groups/gitlab-org/-/epics/6244), which links to | ||||
| all existing SSL issues for the GitLab Workflow extension. | ||||
| 
 | ||||
| ## Use the extension with a self-signed CA | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - Your GitLab instance uses a certificate signed with a self-signed certificate authority (CA). | ||||
| 
 | ||||
| 1. Ensure your CA certificate is correctly added to your system for the extension to work. VS Code reads | ||||
|    the system certificate store, and changes all node `http` requests to trust the certificates: | ||||
| 
 | ||||
|    ```mermaid | ||||
|    graph LR; | ||||
|    A[Self-signed CA] -- signed --> B[Your GitLab instance certificate] | ||||
|    ``` | ||||
| 
 | ||||
|    For more information, see [Self-signed certificate error when installing Python support in WSL](https://github.com/microsoft/vscode/issues/131836#issuecomment-909983815) in the Visual Studio Code issue queue. | ||||
| 
 | ||||
| 1. In your VS Code `settings.json`, set `"http.systemCertificates": true`. The default value is `true`, so you might not need to change this value. | ||||
| 1. Follow the instructions for your operating system: | ||||
| 
 | ||||
| ### Windows | ||||
| 
 | ||||
| NOTE: | ||||
| These instructions were tested on Windows 10 and VS Code 1.60.0. | ||||
| 
 | ||||
| Make sure you can see your self-signed CA in your certificate store: | ||||
| 
 | ||||
| 1. Open the command prompt. | ||||
| 1. Run `certmgr`. | ||||
| 1. Make sure you see your certificate in **Trusted Root Certification Authorities > Certificates**. | ||||
| 
 | ||||
| ### Linux | ||||
| 
 | ||||
| NOTE: | ||||
| These instructions were tested on Arch Linux `5.14.3-arch1-1` and VS Code 1.60.0. | ||||
| 
 | ||||
| 1. Use your operating system's tools to confirm you can add our self-signed CA to your system: | ||||
|    - `update-ca-trust` (Fedora, RHEL, CentOS) | ||||
|    - `update-ca-certificates` (Ubuntu, Debian, OpenSUSE, SLES) | ||||
|    - `trust` (Arch) | ||||
| 1. Confirm the CA certificate is in `/etc/ssl/certs/ca-certificates.crt` or `/etc/ssl/certs/ca-bundle.crt`. | ||||
|    VS Code [checks this location](https://github.com/microsoft/vscode/issues/131836#issuecomment-909983815). | ||||
| 
 | ||||
| ### MacOS | ||||
| 
 | ||||
| NOTE: | ||||
| These instructions are untested, but likely work as intended. If you can confirm this setup, | ||||
| create a documentation issue with more information. | ||||
| 
 | ||||
| Make sure you see the self-signed CA in your keychain: | ||||
| 
 | ||||
| 1. Go to **Finder > Applications > Utilities > Keychain Access**. | ||||
| 1. In the left-hand column, select **System**. | ||||
| 1. Your self-signed CA certificate should be on the list. | ||||
|  | @ -0,0 +1,123 @@ | |||
| --- | ||||
| stage: Create | ||||
| group: Editor Extensions | ||||
| info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments | ||||
| --- | ||||
| 
 | ||||
| # Troubleshooting the GitLab Workflow extension for VS Code | ||||
| 
 | ||||
| If you encounter any issues with the GitLab Workflow extension for VS Code, or have feature requests for it: | ||||
| 
 | ||||
| 1. Check the [extension documentation](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/README.md) | ||||
|    for known issues and solutions. | ||||
| 1. Report bugs or request features in the | ||||
|    [`gitlab-vscode-extension` issue queue](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues). | ||||
| 
 | ||||
| ## Enable debug logs | ||||
| 
 | ||||
| Both the VS Code Extension and the GitLab Language Server provide logs that can help you troubleshoot. To enable debug logging: | ||||
| 
 | ||||
| 1. In VS Code, on the top bar, go to **Code > Preferences > Settings**. | ||||
| 1. On the top right corner, select **Open Settings (JSON)** to edit your `settings.json` file. | ||||
| 1. Add this line, or edit it if it already exists: | ||||
| 
 | ||||
|    ```json | ||||
|    "gitlab.debug": true, | ||||
|    ``` | ||||
| 
 | ||||
| 1. Save your changes. | ||||
| 
 | ||||
| ### View log files | ||||
| 
 | ||||
| To view debug logs from either the VS Code Extension or the GitLab Language Server: | ||||
| 
 | ||||
| 1. Use the command `GitLab: Show Extension Logs` to view the output panel. | ||||
| 1. In the upper right corner of the output panel, select either **GitLab Workflow** or | ||||
|    **GitLab Language Server** from the dropdown list. | ||||
| 
 | ||||
| ## Error: `407 Access Denied` failure with a proxy | ||||
| 
 | ||||
| If you use an authenticated proxy, you might encounter an error like `407 Access Denied (authentication_failed)`: | ||||
| 
 | ||||
| ```plaintext | ||||
| Request failed: Can't add GitLab account for https://gitlab.com. Check your instance URL and network connection. | ||||
| Fetching resource from https://gitlab.com/api/v4/personal_access_tokens/self failed | ||||
| ``` | ||||
| 
 | ||||
| GitLab Duo Code Suggestions does not support authenticated proxies. For the proposed feature, | ||||
| see [issue 1234](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/1234) in the extension's project. | ||||
| 
 | ||||
| ## Configure self-signed certificates | ||||
| 
 | ||||
| To use self-signed certificates to connect to your GitLab instance, configure them using these settings. | ||||
| These settings are community contributions, because the GitLab team uses a public CA. None of the fields are required. | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - You're not using the [`http.proxy` setting](https://code.visualstudio.com/docs/setup/network#_legacy-proxy-server-support) | ||||
|   in VS Code. For more information, see [issue 314](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/314). | ||||
| 
 | ||||
| | Setting name | Default | Information | | ||||
| | ------------ | :-----: | ----------- | | ||||
| | `gitlab.ca`  | null    | Deprecated. See [the SSL setup guide](ssl.md) for more information on how to set up your self-signed CA. | | ||||
| | `gitlab.cert`| null    | Unsupported. See [epic 6244](https://gitlab.com/groups/gitlab-org/-/epics/6244). If your self-managed GitLab instance requires a custom certificate or key pair, set this option to point to your certificate file. See `gitlab.certKey`. | | ||||
| | `gitlab.certKey`| null    | Unsupported. See [epic 6244](https://gitlab.com/groups/gitlab-org/-/epics/6244). If your self-managed GitLab instance requires a custom certificate or key pair, set this option to point to your certificate key file. See `gitlab.cert`. | | ||||
| | `gitlab.ignoreCertificateErrors` | false   | Unsupported. See [epic 6244](https://gitlab.com/groups/gitlab-org/-/epics/6244). If you use a self-managed GitLab instance with no SSL certificate, or have certificate issues that prevent you from using the extension, set this option to `true` to ignore certificate errors. | | ||||
| 
 | ||||
| ## Disable code suggestions | ||||
| 
 | ||||
| Code completion is enabled by default. To disable it: | ||||
| 
 | ||||
| 1. In VS Code, on the left sidebar, select **Extensions > GitLab Workflow**. | ||||
| 1. Select **Manage** (**{settings}**) **> Extension Settings**. | ||||
| 1. In **GitLab > Duo Code Suggestions**, clear **Enable GitLab Duo Code Suggestions**. | ||||
| 
 | ||||
| ## Disable streaming of code generation results | ||||
| 
 | ||||
| By default, code generation streams AI-generated code. Streaming sends generated code | ||||
| to your editor incrementally, rather than waiting for the full code snippet to generate. | ||||
| This allows for a more interactive and responsive experience. | ||||
| 
 | ||||
| If you prefer to see code generation results only when they are complete, you can turn off streaming. | ||||
| Disabling streaming means that code generation requests might be perceived | ||||
| as taking longer to resolve. To disable streaming: | ||||
| 
 | ||||
| 1. In VS Code, on the top bar, go to **Code > Settings > Settings**. | ||||
| 1. On the top right corner, select **Open Settings (JSON)** to edit your `settings.json` file: | ||||
| 
 | ||||
|     | ||||
| 1. In your `settings.json` file, add this line, or set it to `false` it already exists: | ||||
| 
 | ||||
|    ```json | ||||
|    "gitlab.featureFlags.streamCodeGenerations": false, | ||||
|    ``` | ||||
| 
 | ||||
| 1. Save your changes. | ||||
| 
 | ||||
| ## Error: Direct connection fails | ||||
| 
 | ||||
| > - Direct connection [introduced](https://gitlab.com/groups/gitlab-org/-/epics/13252) in GitLab 17.2. | ||||
| 
 | ||||
| To reduce latency, the Workflow extension tries to send suggestion completion requests directly to GitLab Cloud Connector, | ||||
| bypassing the GitLab instance. This network connection does not use the proxy and certificate settings of the VS Code extension. | ||||
| 
 | ||||
| If your GitLab instance doesn't support direct connections, or your network prevents the extension from connecting to | ||||
| GitLab Cloud Connector, you might see these warnings in your logs: | ||||
| 
 | ||||
| ```plaintext | ||||
| Failed to fetch direct connection details from GitLab instance. | ||||
| Code suggestion requests will be sent to GitLab instance. | ||||
| ``` | ||||
| 
 | ||||
| This error means your instance either doesn't support direct connections, or is misconfigured. | ||||
| To fix the problem, see the [troubleshooting guide for Code Suggestions](../../user/project/repository/code_suggestions/troubleshooting.md). | ||||
| 
 | ||||
| If you see this error, the extension can't connect to GitLab Cloud Connector, and is reverting to use your GitLab instance: | ||||
| 
 | ||||
| ```plaintext | ||||
| Direct connection for code suggestions failed. | ||||
| Code suggestion requests will be sent to your GitLab instance. | ||||
| ``` | ||||
| 
 | ||||
| The indirect connection through your GitLab instance is about 100 ms slower, but otherwise works the same. | ||||
| This issue is often caused by network connection problems, like with your LAN firewall or proxy settings. | ||||
|  | @ -108,11 +108,9 @@ you might write something like: | |||
| AI is non-deterministic, so you may not get the same suggestion every time with the same input. | ||||
| To generate quality code, write clear, descriptive, specific tasks. | ||||
| 
 | ||||
| ### Best practice examples | ||||
| 
 | ||||
| For use cases and best practices, follow the [GitLab Duo examples documentation](../../../gitlab_duo_examples.md). | ||||
| 
 | ||||
| #### Use open tabs as context | ||||
| ## Open tabs as context | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/464767) in GitLab 17.2 [with a flag](../../../../administration/feature_flags.md) named `advanced_context_resolver`. Disabled by default. | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/462750) in GitLab 17.2 [with a flag](../../../../administration/feature_flags.md) named `code_suggestions_context`. Disabled by default. | ||||
|  | @ -125,49 +123,80 @@ FLAG: | |||
| The availability of this feature is controlled by a feature flag. | ||||
| For more information, see the history. | ||||
| 
 | ||||
| For better results from GitLab Duo Code Suggestions, ensure that Open Tabs Context is enabled in your IDE settings. | ||||
| This feature uses the contents of the files currently open in your IDE to get more | ||||
| accurate and relevant results from Code Suggestions. Like prompt engineering, these files | ||||
| To get more accurate and relevant results from Code Suggestions and code generation, you can use | ||||
| the contents of the files open in tabs in your IDE. Similar to prompt engineering, these files | ||||
| give GitLab Duo more information about the standards and practices in your code project. | ||||
| 
 | ||||
| To get the most benefit from using your open tabs as context, open the files relevant to the code | ||||
| you want to create, including configuration files. When you start work in a new file, | ||||
| Code Suggestions offers you suggestions in the new file. | ||||
| ### Enable open tabs as context | ||||
| 
 | ||||
| By default, Code Suggestions uses the open files in your IDE for context when making suggestions. | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - Requires GitLab 17.2 and later. Earlier GitLab versions that support Code Suggestions | ||||
|   cannot weight the content of open tabs more heavily than other files in your project. | ||||
| - Requires a [supported code language](#advanced-context-supported-languages). | ||||
| - You must have GitLab 17.2 or later. Earlier GitLab versions that support Code Suggestions | ||||
|   cannot weigh the content of open tabs more heavily than other files in your project. | ||||
| - GitLab Duo Code Suggestions must be enabled for your project. | ||||
| - Use a [supported code language](#advanced-context-supported-languages): | ||||
|   - Code completion: All configured languages. | ||||
|   - Code generation: Go, Java, JavaScript, Kotlin, Python, Ruby, Rust, TypeScript (`.ts` and `.tsx` files), | ||||
|     Vue, and YAML. | ||||
| - For Visual Studio Code, you must have GitLab Workflow extension version 4.14.2 or later. | ||||
| 
 | ||||
| 1. Open the files you want to provide for context. Advanced Context uses the most recently | ||||
|    opened or changed files for context. If you don’t want a file sent as additional context, close it. | ||||
| 1. To fine-tune your Code Generation results, add code comments to your file that explain | ||||
|    what you want to build. Code Generation treats your code comments like chat. Your code comments | ||||
|    update the `user_instruction`, and then improve the next results you receive. | ||||
| To confirm that open tabs are used as context: | ||||
| 
 | ||||
| As you work, GitLab Duo provides Code Suggestions that use your other open files | ||||
| (within [truncation limits](#truncation-of-file-content)) | ||||
| as extra context. | ||||
| ::Tabs | ||||
| 
 | ||||
| :::TabTitle Visual Studio Code | ||||
| 
 | ||||
| 1. On the top bar, go to **Code > Settings > Extensions**. | ||||
| 1. Search for GitLab Workflow in the list, and select the gear icon. | ||||
| 1. Select **Extension Settings**. | ||||
| 1. In your **User** settings, under **GitLab › Duo Code Suggestions: Open Tabs Context**, | ||||
|    select **Enable Open Tabs Context to improve Code Suggestions by sharing context across open tabs**. | ||||
| 
 | ||||
| :::TabTitle JetBrains IDEs | ||||
| 
 | ||||
| 1. Go to your IDE's top menu bar and select **Settings**. | ||||
| 1. On the left sidebar, expand **Tools**, then select **GitLab Duo**. | ||||
| 1. Expand **GitLab Language Server**. | ||||
| 1. Under **Code Completion**, select **Send open tabs as context**. | ||||
| 1. Select **OK** or **Save**. | ||||
| 
 | ||||
| ::EndTabs | ||||
| 
 | ||||
| ### Use open tabs as context | ||||
| 
 | ||||
| Open the files you want to provide for context: | ||||
| 
 | ||||
| - Open tabs uses the most recently opened or changed files. | ||||
| - If you do not want a file used as additional context, close that file. | ||||
| 
 | ||||
| When you start working in a file, GitLab Duo uses your open files | ||||
| as extra context, within [truncation limits](#truncation-of-file-content). | ||||
| 
 | ||||
| You can adjust your code generation results by adding code comments to your file | ||||
| that explain what you want to build. Code generation treats your code comments | ||||
| like chat. Your code comments update the `user_instruction`, and then improve | ||||
| the next results you receive. | ||||
| 
 | ||||
| To learn about the code that builds the prompt, see these files: | ||||
| 
 | ||||
| - **Code Generation**: | ||||
| - **Code generation**: | ||||
|   [`ee/lib/api/code_suggestions.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/api/code_suggestions.rb#L76) | ||||
|   in the `gitlab` repository. | ||||
| - **Code Completion**: | ||||
| - **Code completion**: | ||||
|   [`ai_gateway/code_suggestions/processing/completions.py`](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/blob/fcb3f485a8f047a86a8166aad81f93b6d82106a7/ai_gateway/code_suggestions/processing/completions.py#L273) | ||||
|   in the `modelops` repository. | ||||
| 
 | ||||
| We'd love your feedback about the Advanced Context feature in | ||||
| Provide feedback about this feature in | ||||
| [issue 258](https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/issues/258). | ||||
| 
 | ||||
| ### Advanced Context supported languages | ||||
| ## Advanced Context supported languages | ||||
| 
 | ||||
| The Advanced Context feature supports these languages: | ||||
| 
 | ||||
| - Code Completion: all configured languages. | ||||
| - Code Generation: Go, Java, JavaScript, Kotlin, Python, Ruby, Rust, TypeScript (`.ts` and `.tsx` files), Vue, and YAML. | ||||
| - Code completion: all configured languages. | ||||
| - Code generation: Go, Java, JavaScript, Kotlin, Python, Ruby, Rust, TypeScript (`.ts` and `.tsx` files), Vue, and YAML. | ||||
| 
 | ||||
| ## Response time | ||||
| 
 | ||||
|  |  | |||
|  | @ -145,57 +145,6 @@ To do this: | |||
| 
 | ||||
| ::EndTabs | ||||
| 
 | ||||
| ## Use open tabs as context | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/issues/206) in GitLab 17.2. | ||||
| 
 | ||||
| To enhance the accuracy and relevance of GitLab Duo Code Suggestions, enable the use of | ||||
| open tabs as context in your IDE settings. This feature uses the contents of files most recently | ||||
| opened or changed in your IDE to provide more tailored code suggestions, within certain truncation limits. | ||||
| This extra context gives you: | ||||
| 
 | ||||
| - More accurate and relevant code suggestions | ||||
| - Better alignment with your project's standards and practices | ||||
| - Improved context for new file creation | ||||
| 
 | ||||
| Open tabs as context supports these languages: | ||||
| 
 | ||||
| - Code Completion: All configured languages. | ||||
| - Code Generation: Go, Java, JavaScript, Kotlin, Python, Ruby, Rust, TypeScript (`.ts` and `.tsx` files), | ||||
|   Vue, and YAML. | ||||
| 
 | ||||
| ## Enable open tabs as context | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - Requires GitLab 17.1 or later. | ||||
| - For GitLab self-managed instances, enable the `code_suggestions_context`and the | ||||
|   `advanced_context_resolver` [feature flags](../../../feature_flags.md). | ||||
| - GitLab Duo Code Suggestions enabled for your project | ||||
| - For Visual Studio Code, requires the GitLab Workflow extension, version 4.14.2 or later. | ||||
| 
 | ||||
| ::Tabs | ||||
| 
 | ||||
| :::TabTitle Visual Studio Code | ||||
| 
 | ||||
| 1. Install the [GitLab Workflow extension](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow) | ||||
|    from the Visual Studio Marketplace. | ||||
| 1. Configure the extension following the | ||||
|    [setup instructions](https://gitlab.com/gitlab-org/gitlab-vscode-extension#extension-settings). | ||||
| 1. Enable the feature by toggling the `gitlab.duoCodeSuggestions.enabledSupportedLanguages` setting. | ||||
| 
 | ||||
| :::TabTitle JetBrains IDEs | ||||
| 
 | ||||
| For installation instructions for JetBrains IDEs, see the | ||||
| [GitLab JetBrains Plugin documentation](https://gitlab.com/gitlab-org/editor-extensions/gitlab-jetbrains-plugin#toggle-sending-open-tabs-as-context). | ||||
| 
 | ||||
| ::EndTabs | ||||
| 
 | ||||
| When you're ready to start coding: | ||||
| 
 | ||||
| 1. Open relevant files, including configuration files, to provide better context. | ||||
| 1. Close any files you don't want to be used as context. | ||||
| 
 | ||||
| ## View multiple code suggestions | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/1325) in GitLab 17.1. | ||||
|  |  | |||
|  | @ -685,14 +685,6 @@ msgid_plural "%{count} Participants" | |||
| msgstr[0] "" | ||||
| msgstr[1] "" | ||||
| 
 | ||||
| msgid "%{count} Reviewer" | ||||
| msgid_plural "%{count} Reviewers" | ||||
| msgstr[0] "" | ||||
| msgstr[1] "" | ||||
| 
 | ||||
| msgid "%{count} Reviewers" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "%{count} approval required from %{name}" | ||||
| msgid_plural "%{count} approvals required from %{name}" | ||||
| msgstr[0] "" | ||||
|  | @ -754,9 +746,6 @@ msgstr "" | |||
| msgid "%{count} of %{total}" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "%{count} optional %{label}." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "%{count} project" | ||||
| msgid_plural "%{count} projects" | ||||
| msgstr[0] "" | ||||
|  | @ -7592,9 +7581,6 @@ msgstr "" | |||
| msgid "Assign to me" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Assign yourself" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Assigned" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -33890,6 +33876,9 @@ msgstr "" | |||
| msgid "MergeChecks|All threads must be resolved" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "MergeChecks|Assign reviewers" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "MergeChecks|Enable \"Pipelines must succeed\" first." | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -34052,9 +34041,6 @@ msgstr "" | |||
| msgid "MergeRequests|started a thread on commit %{linkStart}%{commitDisplay}%{linkEnd}" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "MergeRequest|Add or edit reviewers" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "MergeRequest|Awaiting review" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -34094,9 +34080,6 @@ msgstr "" | |||
| msgid "MergeRequest|No files found" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "MergeRequest|Quick add reviewers" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "MergeRequest|Remove reviewer" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -36290,9 +36273,6 @@ msgstr "" | |||
| msgid "No results found." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "No reviewers assigned" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "No runner executable" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -38094,6 +38074,9 @@ msgstr "" | |||
| msgid "Optional" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Optional approvals" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab." | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -46165,6 +46148,11 @@ msgstr "" | |||
| msgid "Requirement|Requirements have become work items and the legacy requirement IDs are being deprecated. Update your links to reference this item's new ID %{id}. %{linkStart}Learn more%{linkEnd}." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Requires %d approval" | ||||
| msgid_plural "Requires %d approvals" | ||||
| msgstr[0] "" | ||||
| msgstr[1] "" | ||||
| 
 | ||||
| msgid "Requires %d approval from eligible users." | ||||
| msgid_plural "Requires %d approvals from eligible users." | ||||
| msgstr[0] "" | ||||
|  |  | |||
|  | @ -96,15 +96,27 @@ describe('Board card', () => { | |||
|   }); | ||||
| 
 | ||||
|   describe('when GlLabel is clicked in BoardCardInner', () => { | ||||
|     it("doesn't call setSelectedBoardItemsMutation", () => { | ||||
|     it("doesn't call setSelectedBoardItemsMutation", async () => { | ||||
|       mountComponent(); | ||||
| 
 | ||||
|       wrapper.findComponent(GlLabel).trigger('mouseup'); | ||||
|       await wrapper.findComponent(GlLabel).trigger('mouseup'); | ||||
| 
 | ||||
|       expect(mockSetSelectedBoardItemsResolver).toHaveBeenCalledTimes(0); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('when issuable title is clicked in BoardCardInner and issuesListDrawer feature is enabled', () => { | ||||
|     it('calls mockSetSelectedBoardItemsResolver', async () => { | ||||
|       mountComponent({ provide: { glFeatures: { issuesListDrawer: true } } }); | ||||
| 
 | ||||
|       await wrapper.findByTestId('board-card-title-link').trigger('click'); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(mockSetActiveBoardItemResolver).toHaveBeenCalledTimes(1); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   it('should not highlight the card by default', () => { | ||||
|     mountComponent(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,134 +0,0 @@ | |||
| import Vue from 'vue'; | ||||
| import VueApollo from 'vue-apollo'; | ||||
| import { GlEmptyState, GlButton } from '@gitlab/ui'; | ||||
| import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; | ||||
| import createMockApollo from 'helpers/mock_apollo_helper'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import UpdateReviewers from '~/merge_requests/components/reviewers/update_reviewers.vue'; | ||||
| import ReviewersContainer from '~/merge_requests/components/reviewers/reviewers_container.vue'; | ||||
| import UncollapsedReviewerList from '~/sidebar/components/reviewers/uncollapsed_reviewer_list.vue'; | ||||
| import userPermissionsQuery from '~/merge_requests/components/reviewers/queries/user_permissions.query.graphql'; | ||||
| import setReviewersMutation from '~/merge_requests/components/reviewers/queries/set_reviewers.mutation.graphql'; | ||||
| 
 | ||||
| let wrapper; | ||||
| let setReviewersMutationHandler; | ||||
| 
 | ||||
| Vue.use(VueApollo); | ||||
| 
 | ||||
| function createComponent(propsData = {}, adminMergeRequest = true) { | ||||
|   setReviewersMutationHandler = jest.fn().mockResolvedValue({ | ||||
|     data: { mergeRequestSetReviewers: { errors: null } }, | ||||
|   }); | ||||
| 
 | ||||
|   const apolloProvider = createMockApollo([ | ||||
|     [ | ||||
|       userPermissionsQuery, | ||||
|       jest.fn().mockResolvedValue({ | ||||
|         data: { | ||||
|           project: { id: 1, mergeRequest: { id: 1, userPermissions: { adminMergeRequest } } }, | ||||
|         }, | ||||
|       }), | ||||
|     ], | ||||
|     [setReviewersMutation, setReviewersMutationHandler], | ||||
|   ]); | ||||
| 
 | ||||
|   wrapper = shallowMountExtended(ReviewersContainer, { | ||||
|     apolloProvider, | ||||
|     propsData: { | ||||
|       reviewers: [], | ||||
|       loadingReviewers: false, | ||||
|       ...propsData, | ||||
|     }, | ||||
|     provide: { | ||||
|       projectPath: 'gitlab-org/gitlab', | ||||
|       issuableIid: '1', | ||||
|     }, | ||||
|     stubs: { | ||||
|       GlEmptyState, | ||||
|       UpdateReviewers, | ||||
|     }, | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| const findEmptyState = () => wrapper.findComponent(GlEmptyState); | ||||
| const findAssignButton = () => wrapper.findComponent(GlButton); | ||||
| const findReviewersList = () => wrapper.findComponent(UncollapsedReviewerList); | ||||
| const findUpdateReviewers = () => wrapper.findComponent(UpdateReviewers); | ||||
| 
 | ||||
| describe('Reviewers container component', () => { | ||||
|   beforeEach(() => { | ||||
|     window.gon = { current_username: 'root' }; | ||||
|   }); | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     window.gon = {}; | ||||
|   }); | ||||
| 
 | ||||
|   describe('when no reviewers exist', () => { | ||||
|     it('renders empty state', () => { | ||||
|       createComponent(); | ||||
| 
 | ||||
|       expect(findEmptyState().exists()).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     describe('when user has permission to add reviewers', () => { | ||||
|       it('renders empty state with add reviewers button', async () => { | ||||
|         createComponent(); | ||||
| 
 | ||||
|         await waitForPromises(); | ||||
| 
 | ||||
|         expect(findAssignButton().exists()).toBe(true); | ||||
|       }); | ||||
| 
 | ||||
|       it('sets current user as update-reviewers component prop', async () => { | ||||
|         createComponent(); | ||||
| 
 | ||||
|         await waitForPromises(); | ||||
| 
 | ||||
|         expect(findUpdateReviewers().props('selectedReviewers')).toEqual( | ||||
|           expect.arrayContaining(['root']), | ||||
|         ); | ||||
|       }); | ||||
| 
 | ||||
|       it('adds current user as reviewer', async () => { | ||||
|         createComponent(); | ||||
| 
 | ||||
|         await waitForPromises(); | ||||
| 
 | ||||
|         findAssignButton().vm.$emit('click'); | ||||
| 
 | ||||
|         await waitForPromises(); | ||||
| 
 | ||||
|         expect(setReviewersMutationHandler).toHaveBeenCalledWith( | ||||
|           expect.objectContaining({ | ||||
|             reviewerUsernames: ['root'], | ||||
|           }), | ||||
|         ); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('when user does not have permission to add reviewers', () => { | ||||
|       it('renders empty state with add reviewers button', async () => { | ||||
|         createComponent({}, false); | ||||
| 
 | ||||
|         await waitForPromises(); | ||||
| 
 | ||||
|         expect(findAssignButton().exists()).toBe(false); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders reviewers list component', async () => { | ||||
|     createComponent({ reviewers: ['test-reviewer'] }); | ||||
| 
 | ||||
|     await waitForPromises(); | ||||
| 
 | ||||
|     expect(findReviewersList().exists()).toBe(true); | ||||
|     expect(findReviewersList().props()).toEqual( | ||||
|       expect.objectContaining({ | ||||
|         users: ['test-reviewer'], | ||||
|         issuableType: 'merge_request', | ||||
|       }), | ||||
|     ); | ||||
|   }); | ||||
| }); | ||||
|  | @ -2,9 +2,7 @@ import Vue from 'vue'; | |||
| import VueApollo from 'vue-apollo'; | ||||
| import { GlLoadingIcon } from '@gitlab/ui'; | ||||
| import { mountExtended } from 'helpers/vue_test_utils_helper'; | ||||
| import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; | ||||
| import { mockTracking, triggerEvent } from 'helpers/tracking_helper'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import createMockApollo from 'helpers/mock_apollo_helper'; | ||||
| import Component from '~/sidebar/components/reviewers/reviewer_title.vue'; | ||||
| import getMergeRequestReviewers from '~/sidebar/queries/get_merge_request_reviewers.query.graphql'; | ||||
|  | @ -16,7 +14,6 @@ describe('ReviewerTitle component', () => { | |||
|   let wrapper; | ||||
| 
 | ||||
|   const findEditButton = () => wrapper.findByTestId('reviewers-edit-button'); | ||||
|   const findDrawerToggle = () => wrapper.findByTestId('drawer-toggle'); | ||||
| 
 | ||||
|   const createComponent = (props, { reviewerAssignDrawer = false } = {}) => { | ||||
|     const apolloProvider = createMockApollo([ | ||||
|  | @ -126,44 +123,4 @@ describe('ReviewerTitle component', () => { | |||
| 
 | ||||
|     expect(findEditButton().attributes('title')).toBe('Change reviewer'); | ||||
|   }); | ||||
| 
 | ||||
|   describe('when reviewerAssignDrawer is enabled', () => { | ||||
|     beforeEach(() => { | ||||
|       setHTMLFixture('<div id="js-reviewer-drawer-portal"></div>'); | ||||
|     }); | ||||
| 
 | ||||
|     afterEach(() => { | ||||
|       resetHTMLFixture(); | ||||
|     }); | ||||
| 
 | ||||
|     it('sets title for drawer toggle as `Add or edit reviewers`', async () => { | ||||
|       wrapper = createComponent( | ||||
|         { | ||||
|           editable: true, | ||||
|         }, | ||||
|         { reviewerAssignDrawer: true }, | ||||
|       ); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(findDrawerToggle().attributes('title')).toBe('Add or edit reviewers'); | ||||
|     }); | ||||
| 
 | ||||
|     it('clicking toggle opens reviewer drawer', async () => { | ||||
|       wrapper = createComponent( | ||||
|         { | ||||
|           editable: true, | ||||
|         }, | ||||
|         { reviewerAssignDrawer: true }, | ||||
|       ); | ||||
| 
 | ||||
|       expect(document.querySelector('.gl-drawer')).toBe(null); | ||||
| 
 | ||||
|       findDrawerToggle().vm.$emit('click'); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(document.querySelector('.gl-drawer')).not.toBe(null); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -33,6 +33,12 @@ describe('sidebar reviewers', () => { | |||
|         changing: false, | ||||
|         ...props, | ||||
|       }, | ||||
|       provide: { | ||||
|         projectPath: 'projectPath', | ||||
|         issuableId: 1, | ||||
|         issuableIid: 1, | ||||
|         multipleApprovalRulesAvailable: false, | ||||
|       }, | ||||
|       // Attaching to document is required because this component emits something from the parent element :/
 | ||||
|       attachTo: document.body, | ||||
|     }); | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ RSpec.describe Gitlab::AppTextLogger do | |||
|   let(:string_message) { 'Information' } | ||||
| 
 | ||||
|   it 'logs a hash as string' do | ||||
|     expect(subject.format_message('INFO', Time.now, nil, hash_message )).to include(hash_message.to_s) | ||||
|     expect(subject.format_message('INFO', Time.now, nil, hash_message)).to include(hash_message.to_s) | ||||
|   end | ||||
| 
 | ||||
|   it 'logs a string unchanged' do | ||||
|  |  | |||
|  | @ -938,17 +938,19 @@ RSpec.describe Group, feature_category: :groups_and_projects do | |||
|       group.destroy! | ||||
|     end | ||||
| 
 | ||||
|     let!(:group_1) { create(:group, name: 'Y group') } | ||||
|     let!(:group_2) { create(:group, name: 'J group', created_at: 2.days.ago, updated_at: 1.day.ago) } | ||||
|     let!(:group_3) { create(:group, name: 'A group') } | ||||
|     let!(:group_4) { create(:group, name: 'F group', created_at: 1.day.ago, updated_at: 1.day.ago) } | ||||
|     let!(:group_1) { create(:group, id: 10, name: 'Y group') } | ||||
|     let!(:group_2) { create(:group, id: 11, name: 'J group', created_at: 2.days.ago, updated_at: 1.day.ago) } | ||||
|     let!(:group_3) { create(:group, id: 12, name: 'A group') } | ||||
|     let!(:group_4) { create(:group, id: 13, name: 'F group', created_at: 1.day.ago, updated_at: 1.day.ago) } | ||||
| 
 | ||||
|     subject { described_class.with_statistics.with_route.sort_by_attribute(sort) } | ||||
| 
 | ||||
|     context 'when sort by is not provided (id desc by default)' do | ||||
|     context 'when sort by is not provided' do | ||||
|       let(:sort) { nil } | ||||
| 
 | ||||
|       it { is_expected.to eq([group_1, group_2, group_3, group_4]) } | ||||
|       it 'results are not ordered' do | ||||
|         is_expected.to contain_exactly(group_1, group_2, group_3, group_4) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when sort by name_asc' do | ||||
|  | @ -975,30 +977,18 @@ RSpec.describe Group, feature_category: :groups_and_projects do | |||
|       it { is_expected.to eq([group_1, group_2, group_3, group_4].sort_by(&:path).reverse) } | ||||
|     end | ||||
| 
 | ||||
|     context 'when sort by recently_created' do | ||||
|     context 'when sort by created_desc' do | ||||
|       let(:sort) { 'created_desc' } | ||||
| 
 | ||||
|       it { is_expected.to eq([group_3, group_1, group_4, group_2]) } | ||||
|     end | ||||
| 
 | ||||
|     context 'when sort by oldest_created' do | ||||
|     context 'when sort by created_asc' do | ||||
|       let(:sort) { 'created_asc' } | ||||
| 
 | ||||
|       it { is_expected.to eq([group_2, group_4, group_1, group_3]) } | ||||
|     end | ||||
| 
 | ||||
|     context 'when sort by latest_activity' do | ||||
|       let(:sort) { 'latest_activity_desc' } | ||||
| 
 | ||||
|       it { is_expected.to eq([group_1, group_2, group_3, group_4]) } | ||||
|     end | ||||
| 
 | ||||
|     context 'when sort by oldest_activity' do | ||||
|       let(:sort) { 'latest_activity_asc' } | ||||
| 
 | ||||
|       it { is_expected.to eq([group_1, group_2, group_3, group_4]) } | ||||
|     end | ||||
| 
 | ||||
|     context 'when sort by storage_size_desc' do | ||||
|       let!(:project_1) do | ||||
|         create(:project, | ||||
|  |  | |||
|  | @ -2072,7 +2072,6 @@ | |||
| - './ee/spec/services/boards/lists/update_service_spec.rb' | ||||
| - './ee/spec/services/boards/update_service_spec.rb' | ||||
| - './ee/spec/services/boards/user_preferences/update_service_spec.rb' | ||||
| - './ee/spec/services/branches/delete_service_spec.rb' | ||||
| - './ee/spec/services/ci/audit_variable_change_service_spec.rb' | ||||
| - './ee/spec/services/ci_cd/github_integration_setup_service_spec.rb' | ||||
| - './ee/spec/services/ci_cd/github_setup_service_spec.rb' | ||||
|  |  | |||
|  | @ -51,18 +51,6 @@ RSpec.describe Import::ReassignPlaceholderUserRecordsWorker, feature_category: : | |||
|         it_behaves_like 'an invalid source user' | ||||
|       end | ||||
| 
 | ||||
|       context 'when the feature flag is disabled' do | ||||
|         before do | ||||
|           stub_feature_flags(importer_user_mapping: false) | ||||
|         end | ||||
| 
 | ||||
|         it 'does not enqueue service to map records to real users' do | ||||
|           expect(Import::ReassignPlaceholderUserRecordsService).not_to receive(:new) | ||||
| 
 | ||||
|           perform_multiple(job_args) | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       it 'queues a DeletePlaceholderUserWorker with the source user ID' do | ||||
|         expect(Import::DeletePlaceholderUserWorker) | ||||
|           .to receive(:perform_async).with(import_source_user.id) | ||||
|  |  | |||