Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									a3487798ae
								
							
						
					
					
						commit
						3d42e098d9
					
				|  | @ -1,18 +1,63 @@ | |||
| <script> | ||||
| import { GlDropdown, GlDropdownItem, GlTooltipDirective as GlTooltip } from '@gitlab/ui'; | ||||
| import { GlTooltip, GlDisclosureDropdown } from '@gitlab/ui'; | ||||
| import { uniqueId } from 'lodash'; | ||||
| 
 | ||||
| import { __ } from '~/locale'; | ||||
| 
 | ||||
| export default { | ||||
|   components: { | ||||
|     GlDropdown, | ||||
|     GlDropdownItem, | ||||
|   }, | ||||
|   directives: { | ||||
|     GlDisclosureDropdown, | ||||
|     GlTooltip, | ||||
|   }, | ||||
|   inject: ['tiptapEditor'], | ||||
|   data() { | ||||
|     return { | ||||
|       isActive: {}, | ||||
|       toggleId: uniqueId('dropdown-toggle-btn-'), | ||||
|       items: [ | ||||
|         { | ||||
|           text: __('Comment'), | ||||
|           action: () => this.insert('comment'), | ||||
|         }, | ||||
|         { | ||||
|           text: __('Code block'), | ||||
|           action: () => this.insert('codeBlock'), | ||||
|         }, | ||||
|         { | ||||
|           text: __('Details block'), | ||||
|           action: () => this.insertList('details', 'detailsContent'), | ||||
|         }, | ||||
|         { | ||||
|           text: __('Bullet list'), | ||||
|           action: () => this.insertList('bulletList', 'listItem'), | ||||
|           wrapperClass: 'gl-sm-display-none!', | ||||
|         }, | ||||
|         { | ||||
|           text: __('Ordered list'), | ||||
|           action: () => this.insertList('orderedList', 'listItem'), | ||||
|           wrapperClass: 'gl-sm-display-none!', | ||||
|         }, | ||||
|         { | ||||
|           text: __('Task list'), | ||||
|           action: () => this.insertList('taskList', 'taskItem'), | ||||
|           wrapperClass: 'gl-sm-display-none!', | ||||
|         }, | ||||
|         { | ||||
|           text: __('Horizontal rule'), | ||||
|           action: () => this.execute('setHorizontalRule', 'horizontalRule'), | ||||
|         }, | ||||
|         { | ||||
|           text: __('Mermaid diagram'), | ||||
|           action: () => this.insert('diagram', { language: 'mermaid' }), | ||||
|         }, | ||||
|         { | ||||
|           text: __('PlantUML diagram'), | ||||
|           action: () => this.insert('diagram', { language: 'plantuml' }), | ||||
|         }, | ||||
|         { | ||||
|           text: __('Table of contents'), | ||||
|           action: () => this.execute('insertTableOfContents', 'tableOfContents'), | ||||
|         }, | ||||
|       ], | ||||
|     }; | ||||
|   }, | ||||
|   methods: { | ||||
|  | @ -46,47 +91,17 @@ export default { | |||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <gl-dropdown | ||||
|     v-gl-tooltip | ||||
|     size="small" | ||||
|     category="tertiary" | ||||
|     icon="plus" | ||||
|     :text="__('More')" | ||||
|     :title="__('More')" | ||||
|     text-sr-only | ||||
|     class="content-editor-dropdown" | ||||
|     right | ||||
|     lazy | ||||
|   > | ||||
|     <gl-dropdown-item @click="insert('comment')"> | ||||
|       {{ __('Comment') }} | ||||
|     </gl-dropdown-item> | ||||
|     <gl-dropdown-item @click="insert('codeBlock')"> | ||||
|       {{ __('Code block') }} | ||||
|     </gl-dropdown-item> | ||||
|     <gl-dropdown-item @click="insertList('details', 'detailsContent')"> | ||||
|       {{ __('Details block') }} | ||||
|     </gl-dropdown-item> | ||||
|     <gl-dropdown-item class="gl-sm-display-none!" @click="insertList('bulletList', 'listItem')"> | ||||
|       {{ __('Bullet list') }} | ||||
|     </gl-dropdown-item> | ||||
|     <gl-dropdown-item class="gl-sm-display-none!" @click="insertList('orderedList', 'listItem')"> | ||||
|       {{ __('Ordered list') }} | ||||
|     </gl-dropdown-item> | ||||
|     <gl-dropdown-item class="gl-sm-display-none!" @click="insertList('taskList', 'taskItem')"> | ||||
|       {{ __('Task list') }} | ||||
|     </gl-dropdown-item> | ||||
|     <gl-dropdown-item @click="execute('setHorizontalRule', 'horizontalRule')"> | ||||
|       {{ __('Horizontal rule') }} | ||||
|     </gl-dropdown-item> | ||||
|     <gl-dropdown-item @click="insert('diagram', { language: 'mermaid' })"> | ||||
|       {{ __('Mermaid diagram') }} | ||||
|     </gl-dropdown-item> | ||||
|     <gl-dropdown-item @click="insert('diagram', { language: 'plantuml' })"> | ||||
|       {{ __('PlantUML diagram') }} | ||||
|     </gl-dropdown-item> | ||||
|     <gl-dropdown-item @click="execute('insertTableOfContents', 'tableOfContents')"> | ||||
|       {{ __('Table of contents') }} | ||||
|     </gl-dropdown-item> | ||||
|   </gl-dropdown> | ||||
|   <div class="gl-display-inline-flex gl-vertical-align-middle"> | ||||
|     <gl-disclosure-dropdown | ||||
|       :items="items" | ||||
|       :toggle-id="toggleId" | ||||
|       size="small" | ||||
|       category="tertiary" | ||||
|       icon="plus" | ||||
|       :toggle-text="__('More options')" | ||||
|       text-sr-only | ||||
|       right | ||||
|     /> | ||||
|     <gl-tooltip :target="toggleId" placement="top">{{ __('More options') }}</gl-tooltip> | ||||
|   </div> | ||||
| </template> | ||||
|  |  | |||
|  | @ -1,76 +0,0 @@ | |||
| <script> | ||||
| import { GlAlert, GlSprintf, GlLink, GlButton } from '@gitlab/ui'; | ||||
| import { s__ } from '~/locale'; | ||||
| import Tracking from '~/tracking'; | ||||
| import { UPGRADE_DOCS_URL, ABOUT_RELEASES_PAGE } from '../constants'; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'SecurityPatchUpgradeAlert', | ||||
|   i18n: { | ||||
|     alertTitle: s__('VersionCheck|Critical security upgrade available'), | ||||
|     alertBody: s__( | ||||
|       'VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}', | ||||
|     ), | ||||
|     learnMore: s__('VersionCheck|Learn more about this critical security release.'), | ||||
|     primaryButtonText: s__('VersionCheck|Upgrade now'), | ||||
|   }, | ||||
|   components: { | ||||
|     GlAlert, | ||||
|     GlSprintf, | ||||
|     GlLink, | ||||
|     GlButton, | ||||
|   }, | ||||
|   mixins: [Tracking.mixin()], | ||||
|   props: { | ||||
|     currentVersion: { | ||||
|       type: String, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.track('render', { | ||||
|       label: 'security_patch_upgrade_alert', | ||||
|       property: this.currentVersion, | ||||
|     }); | ||||
|   }, | ||||
|   methods: { | ||||
|     trackLearnMoreClick() { | ||||
|       this.track('click_link', { | ||||
|         label: 'security_patch_upgrade_alert_learn_more', | ||||
|         property: this.currentVersion, | ||||
|       }); | ||||
|     }, | ||||
|     trackUpgradeNowClick() { | ||||
|       this.track('click_link', { | ||||
|         label: 'security_patch_upgrade_alert_upgrade_now', | ||||
|         property: this.currentVersion, | ||||
|       }); | ||||
|     }, | ||||
|   }, | ||||
|   UPGRADE_DOCS_URL, | ||||
|   ABOUT_RELEASES_PAGE, | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <gl-alert :title="$options.i18n.alertTitle" variant="danger" :dismissible="false"> | ||||
|     <gl-sprintf :message="$options.i18n.alertBody"> | ||||
|       <template #currentVersion> | ||||
|         <span class="gl-font-weight-bold">{{ currentVersion }}</span> | ||||
|       </template> | ||||
|       <template #link> | ||||
|         <gl-link :href="$options.ABOUT_RELEASES_PAGE" @click="trackLearnMoreClick">{{ | ||||
|           $options.i18n.learnMore | ||||
|         }}</gl-link> | ||||
|       </template> | ||||
|     </gl-sprintf> | ||||
|     <template #actions> | ||||
|       <gl-button | ||||
|         :href="$options.UPGRADE_DOCS_URL" | ||||
|         variant="confirm" | ||||
|         @click="trackUpgradeNowClick" | ||||
|         >{{ $options.i18n.primaryButtonText }}</gl-button | ||||
|       > | ||||
|     </template> | ||||
|   </gl-alert> | ||||
| </template> | ||||
|  | @ -1,7 +1,6 @@ | |||
| import Vue from 'vue'; | ||||
| import { parseBoolean, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; | ||||
| import GitlabVersionCheckBadge from './components/gitlab_version_check_badge.vue'; | ||||
| import SecurityPatchUpgradeAlert from './components/security_patch_upgrade_alert.vue'; | ||||
| import SecurityPatchUpgradeAlertModal from './components/security_patch_upgrade_alert_modal.vue'; | ||||
| 
 | ||||
| const mountGitlabVersionCheckBadge = (el) => { | ||||
|  | @ -33,25 +32,6 @@ const mountGitlabVersionCheckBadge = (el) => { | |||
|   } | ||||
| }; | ||||
| 
 | ||||
| const mountSecurityPatchUpgradeAlert = (el) => { | ||||
|   const { currentVersion } = el.dataset; | ||||
| 
 | ||||
|   try { | ||||
|     return new Vue({ | ||||
|       el, | ||||
|       render(createElement) { | ||||
|         return createElement(SecurityPatchUpgradeAlert, { | ||||
|           props: { | ||||
|             currentVersion, | ||||
|           }, | ||||
|         }); | ||||
|       }, | ||||
|     }); | ||||
|   } catch { | ||||
|     return null; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const mountSecurityPatchUpgradeAlertModal = (el) => { | ||||
|   const { currentVersion, version } = el.dataset; | ||||
| 
 | ||||
|  | @ -78,16 +58,11 @@ const mountSecurityPatchUpgradeAlertModal = (el) => { | |||
| export default () => { | ||||
|   const renderedApps = []; | ||||
| 
 | ||||
|   const securityPatchUpgradeAlert = document.getElementById('js-security-patch-upgrade-alert'); | ||||
|   const securityPatchUpgradeAlertModal = document.getElementById( | ||||
|     'js-security-patch-upgrade-alert-modal', | ||||
|   ); | ||||
|   const versionCheckBadges = [...document.querySelectorAll('.js-gitlab-version-check-badge')]; | ||||
| 
 | ||||
|   if (securityPatchUpgradeAlert) { | ||||
|     renderedApps.push(mountSecurityPatchUpgradeAlert(securityPatchUpgradeAlert)); | ||||
|   } | ||||
| 
 | ||||
|   if (securityPatchUpgradeAlertModal) { | ||||
|     renderedApps.push(mountSecurityPatchUpgradeAlertModal(securityPatchUpgradeAlertModal)); | ||||
|   } | ||||
|  |  | |||
|  | @ -0,0 +1,258 @@ | |||
| <script> | ||||
| import { | ||||
|   GlAlert, | ||||
|   GlButton, | ||||
|   GlCard, | ||||
|   GlFormInput, | ||||
|   GlLink, | ||||
|   GlLoadingIcon, | ||||
|   GlSprintf, | ||||
|   GlToggle, | ||||
| } from '@gitlab/ui'; | ||||
| import { createAlert } from '~/flash'; | ||||
| import { __, s__ } from '~/locale'; | ||||
| import { helpPagePath } from '~/helpers/help_page_helper'; | ||||
| import inboundAddProjectCIJobTokenScopeMutation from '../graphql/mutations/inbound_add_project_ci_job_token_scope.mutation.graphql'; | ||||
| import inboundRemoveProjectCIJobTokenScopeMutation from '../graphql/mutations/inbound_remove_project_ci_job_token_scope.mutation.graphql'; | ||||
| import inboundUpdateCIJobTokenScopeMutation from '../graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql'; | ||||
| import inboundGetCIJobTokenScopeQuery from '../graphql/queries/inbound_get_ci_job_token_scope.query.graphql'; | ||||
| import inboundGetProjectsWithCIJobTokenScopeQuery from '../graphql/queries/inbound_get_projects_with_ci_job_token_scope.query.graphql'; | ||||
| import TokenProjectsTable from './token_projects_table.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   i18n: { | ||||
|     toggleLabelTitle: s__('CICD|Allow access to this project with a CI_JOB_TOKEN'), | ||||
|     toggleHelpText: s__( | ||||
|       `CICD|Manage which projects can use their CI_JOB_TOKEN to access this project. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}`, | ||||
|     ), | ||||
|     cardHeaderTitle: s__( | ||||
|       'CICD|Allow CI job tokens from the following projects to access this project', | ||||
|     ), | ||||
|     settingDisabledMessage: s__( | ||||
|       'CICD|Enable feature to allow job token access by the following projects.', | ||||
|     ), | ||||
|     addProject: __('Add project'), | ||||
|     cancel: __('Cancel'), | ||||
|     addProjectPlaceholder: __('Paste project path (i.e. gitlab-org/gitlab)'), | ||||
|     projectsFetchError: __('There was a problem fetching the projects'), | ||||
|     scopeFetchError: __('There was a problem fetching the job token scope value'), | ||||
|   }, | ||||
|   fields: [ | ||||
|     { | ||||
|       key: 'project', | ||||
|       label: __('Project with access'), | ||||
|       thClass: 'gl-border-t-none!', | ||||
|       columnClass: 'gl-w-40p', | ||||
|     }, | ||||
|     { | ||||
|       key: 'namespace', | ||||
|       label: __('Namespace'), | ||||
|       thClass: 'gl-border-t-none!', | ||||
|       columnClass: 'gl-w-40p', | ||||
|     }, | ||||
|     { | ||||
|       key: 'actions', | ||||
|       label: '', | ||||
|       tdClass: 'gl-text-right', | ||||
|       thClass: 'gl-border-t-none!', | ||||
|       columnClass: 'gl-w-10p', | ||||
|     }, | ||||
|   ], | ||||
|   components: { | ||||
|     GlAlert, | ||||
|     GlButton, | ||||
|     GlCard, | ||||
|     GlFormInput, | ||||
|     GlLink, | ||||
|     GlLoadingIcon, | ||||
|     GlSprintf, | ||||
|     GlToggle, | ||||
|     TokenProjectsTable, | ||||
|   }, | ||||
|   inject: { | ||||
|     fullPath: { | ||||
|       default: '', | ||||
|     }, | ||||
|   }, | ||||
|   apollo: { | ||||
|     inboundJobTokenScopeEnabled: { | ||||
|       query: inboundGetCIJobTokenScopeQuery, | ||||
|       variables() { | ||||
|         return { | ||||
|           fullPath: this.fullPath, | ||||
|         }; | ||||
|       }, | ||||
|       update({ project }) { | ||||
|         return project.ciCdSettings.inboundJobTokenScopeEnabled; | ||||
|       }, | ||||
|       error() { | ||||
|         createAlert({ message: this.$options.i18n.scopeFetchError }); | ||||
|       }, | ||||
|     }, | ||||
|     projects: { | ||||
|       query: inboundGetProjectsWithCIJobTokenScopeQuery, | ||||
|       variables() { | ||||
|         return { | ||||
|           fullPath: this.fullPath, | ||||
|         }; | ||||
|       }, | ||||
|       update({ project }) { | ||||
|         return project?.ciJobTokenScope?.inboundAllowlist?.nodes ?? []; | ||||
|       }, | ||||
|       error() { | ||||
|         createAlert({ message: this.$options.i18n.projectsFetchError }); | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       inboundJobTokenScopeEnabled: null, | ||||
|       targetProjectPath: '', | ||||
|       projects: [], | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     isProjectPathEmpty() { | ||||
|       return this.targetProjectPath === ''; | ||||
|     }, | ||||
|     ciJobTokenHelpPage() { | ||||
|       return helpPagePath('ci/jobs/ci_job_token'); | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     async updateCIJobTokenScope() { | ||||
|       try { | ||||
|         const { | ||||
|           data: { | ||||
|             ciCdSettingsUpdate: { errors }, | ||||
|           }, | ||||
|         } = await this.$apollo.mutate({ | ||||
|           mutation: inboundUpdateCIJobTokenScopeMutation, | ||||
|           variables: { | ||||
|             input: { | ||||
|               fullPath: this.fullPath, | ||||
|               inboundJobTokenScopeEnabled: this.inboundJobTokenScopeEnabled, | ||||
|             }, | ||||
|           }, | ||||
|         }); | ||||
| 
 | ||||
|         if (errors.length) { | ||||
|           throw new Error(errors[0]); | ||||
|         } | ||||
|       } catch (error) { | ||||
|         this.inboundJobTokenScopeEnabled = !this.inboundJobTokenScopeEnabled; | ||||
|         createAlert({ message: error.message }); | ||||
|       } | ||||
|     }, | ||||
|     async addProject() { | ||||
|       try { | ||||
|         const { | ||||
|           data: { | ||||
|             ciJobTokenScopeAddProject: { errors }, | ||||
|           }, | ||||
|         } = await this.$apollo.mutate({ | ||||
|           mutation: inboundAddProjectCIJobTokenScopeMutation, | ||||
|           variables: { | ||||
|             projectPath: this.fullPath, | ||||
|             targetProjectPath: this.targetProjectPath, | ||||
|           }, | ||||
|         }); | ||||
| 
 | ||||
|         if (errors.length) { | ||||
|           throw new Error(errors[0]); | ||||
|         } | ||||
|       } catch (error) { | ||||
|         createAlert({ message: error.message }); | ||||
|       } finally { | ||||
|         this.clearTargetProjectPath(); | ||||
|         this.getProjects(); | ||||
|       } | ||||
|     }, | ||||
|     async removeProject(removeTargetPath) { | ||||
|       try { | ||||
|         const { | ||||
|           data: { | ||||
|             ciJobTokenScopeRemoveProject: { errors }, | ||||
|           }, | ||||
|         } = await this.$apollo.mutate({ | ||||
|           mutation: inboundRemoveProjectCIJobTokenScopeMutation, | ||||
|           variables: { | ||||
|             projectPath: this.fullPath, | ||||
|             targetProjectPath: removeTargetPath, | ||||
|           }, | ||||
|         }); | ||||
| 
 | ||||
|         if (errors.length) { | ||||
|           throw new Error(errors[0]); | ||||
|         } | ||||
|       } catch (error) { | ||||
|         createAlert({ message: error.message }); | ||||
|       } finally { | ||||
|         this.getProjects(); | ||||
|       } | ||||
|     }, | ||||
|     clearTargetProjectPath() { | ||||
|       this.targetProjectPath = ''; | ||||
|     }, | ||||
|     getProjects() { | ||||
|       this.$apollo.queries.projects.refetch(); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <div> | ||||
|     <gl-loading-icon v-if="$apollo.loading" size="lg" class="gl-mt-5" /> | ||||
|     <template v-else> | ||||
|       <gl-toggle | ||||
|         v-model="inboundJobTokenScopeEnabled" | ||||
|         :label="$options.i18n.toggleLabelTitle" | ||||
|         @change="updateCIJobTokenScope" | ||||
|       > | ||||
|         <template #help> | ||||
|           <gl-sprintf :message="$options.i18n.toggleHelpText"> | ||||
|             <template #link="{ content }"> | ||||
|               <gl-link :href="ciJobTokenHelpPage" class="inline-link" target="_blank"> | ||||
|                 {{ content }} | ||||
|               </gl-link> | ||||
|             </template> | ||||
|           </gl-sprintf> | ||||
|         </template> | ||||
|       </gl-toggle> | ||||
| 
 | ||||
|       <div> | ||||
|         <gl-card class="gl-mt-5 gl-mb-3"> | ||||
|           <template #header> | ||||
|             <h5 class="gl-my-0">{{ $options.i18n.cardHeaderTitle }}</h5> | ||||
|           </template> | ||||
|           <template #default> | ||||
|             <gl-form-input | ||||
|               v-model="targetProjectPath" | ||||
|               :placeholder="$options.i18n.addProjectPlaceholder" | ||||
|             /> | ||||
|           </template> | ||||
|           <template #footer> | ||||
|             <gl-button variant="confirm" :disabled="isProjectPathEmpty" @click="addProject"> | ||||
|               {{ $options.i18n.addProject }} | ||||
|             </gl-button> | ||||
|             <gl-button @click="clearTargetProjectPath">{{ $options.i18n.cancel }}</gl-button> | ||||
|           </template> | ||||
|         </gl-card> | ||||
|         <gl-alert | ||||
|           v-if="!inboundJobTokenScopeEnabled" | ||||
|           class="gl-mb-3" | ||||
|           variant="warning" | ||||
|           :dismissible="false" | ||||
|           :show-icon="false" | ||||
|         > | ||||
|           {{ $options.i18n.settingDisabledMessage }} | ||||
|         </gl-alert> | ||||
|         <token-projects-table | ||||
|           :projects="projects" | ||||
|           :table-fields="$options.fields" | ||||
|           @removeProject="removeProject" | ||||
|         /> | ||||
|       </div> | ||||
|     </template> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -17,7 +17,6 @@ import removeProjectCIJobTokenScopeMutation from '../graphql/mutations/remove_pr | |||
| import updateCIJobTokenScopeMutation from '../graphql/mutations/update_ci_job_token_scope.mutation.graphql'; | ||||
| import getCIJobTokenScopeQuery from '../graphql/queries/get_ci_job_token_scope.query.graphql'; | ||||
| import getProjectsWithCIJobTokenScopeQuery from '../graphql/queries/get_projects_with_ci_job_token_scope.query.graphql'; | ||||
| import OptInJwt from './opt_in_jwt.vue'; | ||||
| import TokenProjectsTable from './token_projects_table.vue'; | ||||
| 
 | ||||
| export default { | ||||
|  | @ -36,6 +35,27 @@ export default { | |||
|     projectsFetchError: __('There was a problem fetching the projects'), | ||||
|     scopeFetchError: __('There was a problem fetching the job token scope value'), | ||||
|   }, | ||||
|   fields: [ | ||||
|     { | ||||
|       key: 'project', | ||||
|       label: __('Project that can be accessed'), | ||||
|       thClass: 'gl-border-t-none!', | ||||
|       columnClass: 'gl-w-40p', | ||||
|     }, | ||||
|     { | ||||
|       key: 'namespace', | ||||
|       label: __('Namespace'), | ||||
|       thClass: 'gl-border-t-none!', | ||||
|       columnClass: 'gl-w-40p', | ||||
|     }, | ||||
|     { | ||||
|       key: 'actions', | ||||
|       label: '', | ||||
|       tdClass: 'gl-text-right', | ||||
|       thClass: 'gl-border-t-none!', | ||||
|       columnClass: 'gl-w-10p', | ||||
|     }, | ||||
|   ], | ||||
|   components: { | ||||
|     GlAlert, | ||||
|     GlButton, | ||||
|  | @ -45,7 +65,6 @@ export default { | |||
|     GlLoadingIcon, | ||||
|     GlSprintf, | ||||
|     GlToggle, | ||||
|     OptInJwt, | ||||
|     TokenProjectsTable, | ||||
|   }, | ||||
|   inject: { | ||||
|  | @ -230,9 +249,12 @@ export default { | |||
|         > | ||||
|           {{ $options.i18n.settingDisabledMessage }} | ||||
|         </gl-alert> | ||||
|         <token-projects-table :projects="projects" @removeProject="removeProject" /> | ||||
|         <token-projects-table | ||||
|           :projects="projects" | ||||
|           :table-fields="$options.fields" | ||||
|           @removeProject="removeProject" | ||||
|         /> | ||||
|       </div> | ||||
|       <opt-in-jwt /> | ||||
|     </template> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -0,0 +1,27 @@ | |||
| <script> | ||||
| import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; | ||||
| import OutboundTokenAccess from './outbound_token_access.vue'; | ||||
| import InboundTokenAccess from './inbound_token_access.vue'; | ||||
| import OptInJwt from './opt_in_jwt.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   components: { | ||||
|     OutboundTokenAccess, | ||||
|     InboundTokenAccess, | ||||
|     OptInJwt, | ||||
|   }, | ||||
|   mixins: [glFeatureFlagMixin()], | ||||
|   computed: { | ||||
|     inboundTokenAccessEnabled() { | ||||
|       return this.glFeatures.ciInboundJobTokenScope; | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <div> | ||||
|     <outbound-token-access /> | ||||
|     <inbound-token-access v-if="inboundTokenAccessEnabled" class="gl-pt-5" /> | ||||
|     <opt-in-jwt /> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -1,32 +1,11 @@ | |||
| <script> | ||||
| import { GlButton, GlTable } from '@gitlab/ui'; | ||||
| import { __, s__ } from '~/locale'; | ||||
| import { s__ } from '~/locale'; | ||||
| 
 | ||||
| export default { | ||||
|   i18n: { | ||||
|     emptyText: s__('CI/CD|No projects have been added to the scope'), | ||||
|   }, | ||||
|   fields: [ | ||||
|     { | ||||
|       key: 'project', | ||||
|       label: __('Projects that can be accessed'), | ||||
|       thClass: 'gl-border-t-none!', | ||||
|       columnClass: 'gl-w-40p', | ||||
|     }, | ||||
|     { | ||||
|       key: 'namespace', | ||||
|       label: __('Namespace'), | ||||
|       thClass: 'gl-border-t-none!', | ||||
|       columnClass: 'gl-w-40p', | ||||
|     }, | ||||
|     { | ||||
|       key: 'actions', | ||||
|       label: '', | ||||
|       tdClass: 'gl-text-right', | ||||
|       thClass: 'gl-border-t-none!', | ||||
|       columnClass: 'gl-w-10p', | ||||
|     }, | ||||
|   ], | ||||
|   components: { | ||||
|     GlButton, | ||||
|     GlTable, | ||||
|  | @ -41,6 +20,10 @@ export default { | |||
|       type: Array, | ||||
|       required: true, | ||||
|     }, | ||||
|     tableFields: { | ||||
|       type: Array, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     removeProject(project) { | ||||
|  | @ -52,7 +35,7 @@ export default { | |||
| <template> | ||||
|   <gl-table | ||||
|     :items="projects" | ||||
|     :fields="$options.fields" | ||||
|     :fields="tableFields" | ||||
|     :tbody-tr-attr="{ 'data-testid': 'projects-token-table-row' }" | ||||
|     :empty-text="$options.i18n.emptyText" | ||||
|     show-empty | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| mutation inboundAddProjectCIJobTokenScope($projectPath: ID!, $targetProjectPath: ID!) { | ||||
|   ciJobTokenScopeAddProject( | ||||
|     input: { projectPath: $projectPath, targetProjectPath: $targetProjectPath, direction: INBOUND } | ||||
|   ) { | ||||
|     errors | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| mutation inboundRemoveProjectCIJobTokenScope($projectPath: ID!, $targetProjectPath: ID!) { | ||||
|   ciJobTokenScopeRemoveProject( | ||||
|     input: { projectPath: $projectPath, targetProjectPath: $targetProjectPath, direction: INBOUND } | ||||
|   ) { | ||||
|     errors | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,8 @@ | |||
| mutation inboundUpdateCIJobTokenScope($input: CiCdSettingsUpdateInput!) { | ||||
|   ciCdSettingsUpdate(input: $input) { | ||||
|     ciCdSettings { | ||||
|       inboundJobTokenScopeEnabled | ||||
|     } | ||||
|     errors | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,8 @@ | |||
| query inboundGetCIJobTokenScope($fullPath: ID!) { | ||||
|   project(fullPath: $fullPath) { | ||||
|     id | ||||
|     ciCdSettings { | ||||
|       inboundJobTokenScopeEnabled | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,18 @@ | |||
| query inboundGetProjectsWithCIJobTokenScope($fullPath: ID!) { | ||||
|   project(fullPath: $fullPath) { | ||||
|     id | ||||
|     ciJobTokenScope { | ||||
|       inboundAllowlist { | ||||
|         nodes { | ||||
|           id | ||||
|           name | ||||
|           namespace { | ||||
|             id | ||||
|             fullPath | ||||
|           } | ||||
|           fullPath | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -1,7 +1,7 @@ | |||
| import Vue from 'vue'; | ||||
| import VueApollo from 'vue-apollo'; | ||||
| import createDefaultClient from '~/lib/graphql'; | ||||
| import TokenAccess from './components/token_access.vue'; | ||||
| import TokenAccessApp from './components/token_access_app.vue'; | ||||
| 
 | ||||
| Vue.use(VueApollo); | ||||
| 
 | ||||
|  | @ -25,7 +25,7 @@ export const initTokenAccess = (containerId = 'js-ci-token-access-app') => { | |||
|       fullPath, | ||||
|     }, | ||||
|     render(createElement) { | ||||
|       return createElement(TokenAccess); | ||||
|       return createElement(TokenAccessApp); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
|  |  | |||
|  | @ -7,6 +7,8 @@ class Explore::GroupsController < Explore::ApplicationController | |||
|   urgency :low | ||||
| 
 | ||||
|   def index | ||||
|     render_group_tree GroupsFinder.new(nil).execute | ||||
|     user = Feature.enabled?(:generic_explore_groups, current_user, type: :experiment) ? nil : current_user | ||||
| 
 | ||||
|     render_group_tree GroupsFinder.new(user).execute | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ module Projects | |||
| 
 | ||||
|       before_action do | ||||
|         push_frontend_feature_flag(:ci_remove_character_limitation_raw_masked_var, type: :development) | ||||
|         push_frontend_feature_flag(:ci_inbound_job_token_scope, @project) | ||||
|       end | ||||
| 
 | ||||
|       helper_method :highlight_badge | ||||
|  |  | |||
|  | @ -3,8 +3,6 @@ | |||
| module VersionCheckHelper | ||||
|   include Gitlab::Utils::StrongMemoize | ||||
| 
 | ||||
|   SECURITY_ALERT_SEVERITY = 'danger' | ||||
| 
 | ||||
|   def show_version_check? | ||||
|     return false unless Gitlab::CurrentSettings.version_check_enabled | ||||
|     return false if User.single_user&.requires_usage_stats_consent? | ||||
|  | @ -18,9 +16,9 @@ module VersionCheckHelper | |||
|   strong_memoize_attr :gitlab_version_check | ||||
| 
 | ||||
|   def show_security_patch_upgrade_alert? | ||||
|     return false unless Feature.enabled?(:critical_security_alert) && show_version_check? && gitlab_version_check | ||||
|     return false unless show_version_check? && gitlab_version_check | ||||
| 
 | ||||
|     gitlab_version_check['severity'] === SECURITY_ALERT_SEVERITY | ||||
|     Gitlab::Utils.to_boolean(gitlab_version_check['critical_vulnerability']) | ||||
|   end | ||||
| 
 | ||||
|   def link_to_version | ||||
|  |  | |||
|  | @ -696,22 +696,14 @@ class Repository | |||
|   end | ||||
| 
 | ||||
|   def head_tree(skip_flat_paths: true) | ||||
|     if Feature.enabled?(:optimized_head_tree) | ||||
|       return if empty? || root_ref.nil? | ||||
|     return if empty? || root_ref.nil? | ||||
| 
 | ||||
|       @head_tree ||= Tree.new(self, root_ref, nil, skip_flat_paths: skip_flat_paths) | ||||
|     elsif head_commit | ||||
|       @head_tree ||= Tree.new(self, head_commit.sha, nil, skip_flat_paths: skip_flat_paths) | ||||
|     end | ||||
|     @head_tree ||= Tree.new(self, root_ref, nil, skip_flat_paths: skip_flat_paths) | ||||
|   end | ||||
| 
 | ||||
|   def tree(sha = :head, path = nil, recursive: false, skip_flat_paths: true, pagination_params: nil) | ||||
|     if sha == :head | ||||
|       if Feature.enabled?(:optimized_head_tree) | ||||
|         return if empty? || root_ref.nil? | ||||
|       else | ||||
|         return unless head_commit | ||||
|       end | ||||
|       return if empty? || root_ref.nil? | ||||
| 
 | ||||
|       if path.nil? | ||||
|         return head_tree(skip_flat_paths: skip_flat_paths) | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| - return unless show_security_patch_upgrade_alert? | ||||
| 
 | ||||
| #js-security-patch-upgrade-alert{ data: { "current_version": Gitlab.version_info } } | ||||
| #js-security-patch-upgrade-alert-modal{ data: { "current_version": Gitlab.version_info, "version": gitlab_version_check.to_json } } | ||||
|  |  | |||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: critical_security_alert | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108732 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/387719 | ||||
| milestone: '15.8' | ||||
| type: development | ||||
| group: group::distribution | ||||
| default_enabled: false | ||||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: optimized_head_tree | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110248 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/389448 | ||||
| milestone: '15.9' | ||||
| type: development | ||||
| group: group::source code | ||||
| default_enabled: false | ||||
|  | @ -0,0 +1,8 @@ | |||
| --- | ||||
| name: generic_explore_groups | ||||
| introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103019" | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381564 | ||||
| milestone: '15.6' | ||||
| type: experiment | ||||
| group: group::source code | ||||
| default_enabled: true | ||||
|  | @ -1,15 +1,22 @@ | |||
| # REQUIRED FIELDS | ||||
| # | ||||
| - title: "Support for Praefect custom metrics endpoint configuration" | ||||
|   announcement_milestone: "15.9"  # (required) The milestone when this feature was first announced as deprecated. | ||||
|   removal_milestone: "16.0"  # (required) The milestone when this feature is planned to be removed | ||||
|   breaking_change: true  # (required) Change to false if this is not a breaking change. | ||||
|   reporter: mjwood  # (required) GitLab username of the person reporting the change | ||||
|   stage: Gitaly  # (required) String value of the stage that the feature was created in. e.g., Growth | ||||
|   issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/390266  # (required) Link to the deprecation issue in GitLab | ||||
|   body: |  # (required) Do not modify this line, instead modify the lines below. | ||||
|     Support for using the `prometheus_exclude_database_from_default_metrics` configuration value is deprecated because using it is non-performant. Instead, all metrics | ||||
|     that scrape the Praefect database will be exported to the `/db_metrics` endpoint. This may require updating your metrics collection targets. | ||||
|   announcement_milestone: "15.9" | ||||
|   removal_milestone: "16.0" | ||||
|   breaking_change: true | ||||
|   reporter: mjwood | ||||
|   stage: Gitaly | ||||
|   issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/390266 | ||||
|   body: | | ||||
|     Support for using the `prometheus_exclude_database_from_default_metrics` configuration value is deprecated in GitLab | ||||
|     15.9 and will be removed in GitLab 16.0. We are removing this configuration value because using it is non-performant. | ||||
|     This change means the following metrics will become unavailable on `/metrics`: | ||||
| 
 | ||||
|     - `gitaly_praefect_unavailable_repositories`. | ||||
|     - `gitaly_praefect_verification_queue_depth`. | ||||
|     - `gitaly_praefect_replication_queue_depth`. | ||||
| 
 | ||||
|     This may require updating your metrics collection targets to also scrape `/db_metrics`. | ||||
| # | ||||
| # OPTIONAL END OF SUPPORT FIELDS | ||||
| # | ||||
|  |  | |||
|  | @ -1,10 +1,11 @@ | |||
| --- | ||||
| table_name: ar_internal_metadata | ||||
| classes: [] | ||||
| classes: | ||||
| - ActiveRecord::InternalMetadata | ||||
| feature_categories: | ||||
| - database | ||||
| description: >- | ||||
|   An internal table used by ActiveRecord to store information about how the database was migrated. | ||||
| description: An internal table used by ActiveRecord to store information about how | ||||
|   the database was migrated. | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685 | ||||
| milestone: '0.8' | ||||
| gitlab_schema: gitlab_internal | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| --- | ||||
| table_name: dast_profiles_tags | ||||
| classes: | ||||
|   - Dast::ScannerProfileTag | ||||
| - Dast::ProfileTag | ||||
| feature_categories: | ||||
|   - dynamic_application_security_testing | ||||
| - dynamic_application_security_testing | ||||
| description: Join Table for Runner tags and DAST Profiles | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108371 | ||||
| milestone: '15.8' | ||||
|  |  | |||
|  | @ -1,9 +1,8 @@ | |||
| --- | ||||
| table_name: dast_scanner_profiles_tags | ||||
| classes: | ||||
|   - Dast::ScannerProfileTag | ||||
| classes: [] | ||||
| feature_categories: | ||||
|   - dynamic_application_security_testing | ||||
| - dynamic_application_security_testing | ||||
| description: Join Table for Runner tags and DAST Scanner Profiles | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104909 | ||||
| milestone: '15.7' | ||||
|  |  | |||
|  | @ -668,7 +668,7 @@ variables: | |||
| 
 | ||||
| test: | ||||
|   script: | ||||
|     - pwd | ||||
|     - pwd -P | ||||
| ``` | ||||
| 
 | ||||
| The `$CI_CONCURRENT_PROJECT_ID` should be used in conjunction with `$CI_PROJECT_PATH` | ||||
|  | @ -680,7 +680,7 @@ variables: | |||
| 
 | ||||
| test: | ||||
|   script: | ||||
|     - pwd | ||||
|     - pwd -P | ||||
| ``` | ||||
| 
 | ||||
| #### Nested paths | ||||
|  |  | |||
|  | @ -109,9 +109,7 @@ shared runner resources. | |||
| The fair usage queue algorithm assigns jobs based on the projects that have the | ||||
| fewest number of jobs already running on shared runners. | ||||
| 
 | ||||
| **Example 1** | ||||
| 
 | ||||
| If these jobs are in the queue: | ||||
| For example, if these jobs are in the queue: | ||||
| 
 | ||||
| - Job 1 for Project 1 | ||||
| - Job 2 for Project 1 | ||||
|  | @ -120,7 +118,7 @@ If these jobs are in the queue: | |||
| - Job 5 for Project 2 | ||||
| - Job 6 for Project 3 | ||||
| 
 | ||||
| The fair usage algorithm assigns jobs in this order: | ||||
| When several CI/CD jobs run concurrently, the fair usage algorithm assigns jobs in this order: | ||||
| 
 | ||||
| 1. Job 1 is first, because it has the lowest job number from projects with no running jobs (that is, all projects). | ||||
| 1. Job 4 is next, because 4 is now the lowest job number from projects with no running jobs (Project 1 has a job running). | ||||
|  | @ -129,20 +127,7 @@ The fair usage algorithm assigns jobs in this order: | |||
| 1. Job 5 is next, because Project 1 now has 2 jobs running and Job 5 is the lowest remaining job number between Projects 2 and 3. | ||||
| 1. Finally is Job 3... because it's the only job left. | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| **Example 2** | ||||
| 
 | ||||
| If these jobs are in the queue: | ||||
| 
 | ||||
| - Job 1 for Project 1 | ||||
| - Job 2 for Project 1 | ||||
| - Job 3 for Project 1 | ||||
| - Job 4 for Project 2 | ||||
| - Job 5 for Project 2 | ||||
| - Job 6 for Project 3 | ||||
| 
 | ||||
| The fair usage algorithm assigns jobs in this order: | ||||
| When only one job runs at a time, the fair usage algorithm assigns jobs in this order: | ||||
| 
 | ||||
| 1. Job 1 is chosen first, because it has the lowest job number from projects with no running jobs (that is, all projects). | ||||
| 1. We finish Job 1. | ||||
|  |  | |||
|  | @ -179,8 +179,15 @@ WARNING: | |||
| This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). | ||||
| Review the details carefully before upgrading. | ||||
| 
 | ||||
| Support for using the `prometheus_exclude_database_from_default_metrics` configuration value is deprecated because using it is non-performant. Instead, all metrics | ||||
| that scrape the Praefect database will be exported to the `/db_metrics` endpoint. This may require updating your metrics collection targets. | ||||
| Support for using the `prometheus_exclude_database_from_default_metrics` configuration value is deprecated in GitLab | ||||
| 15.9 and will be removed in GitLab 16.0. We are removing this configuration value because using it is non-performant. | ||||
| This change means the following metrics will become unavailable on `/metrics`: | ||||
| 
 | ||||
| - `gitaly_praefect_unavailable_repositories`. | ||||
| - `gitaly_praefect_verification_queue_depth`. | ||||
| - `gitaly_praefect_replication_queue_depth`. | ||||
| 
 | ||||
| This may require updating your metrics collection targets to also scrape `/db_metrics`. | ||||
| 
 | ||||
| </div> | ||||
| </div> | ||||
|  |  | |||
|  | @ -275,11 +275,11 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap | |||
| 
 | ||||
| ### 15.7.2 | ||||
| 
 | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.7.1 | ||||
| 
 | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.7.0 | ||||
| 
 | ||||
|  | @ -320,23 +320,31 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap | |||
| 
 | ||||
|   Sites that have configured `max_concurrency` will not be affected by this change. | ||||
|   [Read more about the Sidekiq concurrency setting](../administration/sidekiq/extra_sidekiq_processes.md#concurrency). | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.6.6 | ||||
| 
 | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.6.5 | ||||
| 
 | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.6.4 | ||||
| 
 | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6, and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.6.3 | ||||
| 
 | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.6.2 | ||||
| 
 | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.6.1 | ||||
| 
 | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.6.0 | ||||
| 
 | ||||
|  | @ -356,7 +364,7 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap | |||
|   This issue was [fixed in GitLab 15.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105375) and backported | ||||
|   to GitLab 15.6.2. The issue can also be worked around: | ||||
|   [read about how to create these indexes](https://gitlab.com/gitlab-org/gitlab/-/issues/378343#note_1199863087). | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.x and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.7.3 or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| - Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover. | ||||
| 
 | ||||
| ### 15.5.0 | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,6 +13,21 @@ GitLab uses the SSH protocol to securely communicate with Git. | |||
| When you use SSH keys to authenticate to the GitLab remote server, | ||||
| you don't need to supply your username and password each time. | ||||
| 
 | ||||
| ## What are SSH keys | ||||
| 
 | ||||
| SSH uses two keys, a public key and a private key. | ||||
| 
 | ||||
| - The public key can be distributed. | ||||
| - The private key should be protected. | ||||
| 
 | ||||
| When you need to copy or upload your SSH public key, make sure you do not accidentally copy or upload your private key instead. | ||||
| 
 | ||||
| You cannot expose data by uploading your public key. For example, you can use your public key to | ||||
| [sign commits](project/repository/ssh_signed_commits/index.md), | ||||
| which makes your use of GitLab and your data even more secure. | ||||
| 
 | ||||
| For details, see [Asymmetric cryptography, also known as public-key cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography). | ||||
| 
 | ||||
| ## Prerequisites | ||||
| 
 | ||||
| To use SSH to communicate with GitLab, you need: | ||||
|  |  | |||
|  | @ -485,7 +485,7 @@ namespace :gitlab do | |||
|                 outdated = true | ||||
|               end | ||||
| 
 | ||||
|               if existing_metadata['classes'].sort != table_metadata['classes'].sort | ||||
|               if existing_metadata['classes'] && existing_metadata['classes'].sort != table_metadata['classes'].sort | ||||
|                 existing_metadata['classes'] = table_metadata['classes'] | ||||
|                 outdated = true | ||||
|               end | ||||
|  | @ -514,10 +514,9 @@ namespace :gitlab do | |||
|         File.join(path, sub_directory, "#{source_name}.yml") | ||||
|       end | ||||
| 
 | ||||
|       # Temporary disable this, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85760#note_998452069 | ||||
|       # Rake::Task['db:migrate'].enhance do | ||||
|       #   Rake::Task['gitlab:db:dictionary:generate'].invoke if Rails.env.development? | ||||
|       # end | ||||
|       Rake::Task['db:migrate'].enhance do | ||||
|         Rake::Task['gitlab:db:dictionary:generate'].invoke if Rails.env.development? | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -7783,6 +7783,12 @@ msgstr "" | |||
| msgid "CICD|Add an existing project to the scope" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CICD|Allow CI job tokens from the following projects to access this project" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CICD|Allow access to this project with a CI_JOB_TOKEN" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CICD|An error occurred while update the setting. Please try again." | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -7807,6 +7813,9 @@ msgstr "" | |||
| msgid "CICD|Deployment strategy" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CICD|Enable feature to allow job token access by the following projects." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CICD|Enable feature to limit job token access to the following projects." | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -7822,6 +7831,9 @@ msgstr "" | |||
| msgid "CICD|Limit JSON Web Token (JWT) access" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CICD|Manage which projects can use their CI_JOB_TOKEN to access this project. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CICD|Select the projects that can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -12766,9 +12778,6 @@ msgstr "" | |||
| msgid "DastProfiles|Manage profiles" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "DastProfiles|Manage site profiles" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "DastProfiles|Minimum = 0 (no timeout enabled), Maximum = 2880 minutes" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -27387,9 +27396,6 @@ msgstr "" | |||
| msgid "Months" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "More" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "More Details" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -27414,6 +27420,9 @@ msgstr "" | |||
| msgid "More information." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "More options" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "More than %{number_commits_distance} commits different with %{default_branch}" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -33014,6 +33023,9 @@ msgstr "" | |||
| msgid "Project slug" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Project that can be accessed" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Project uploads" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -33026,6 +33038,9 @@ msgstr "" | |||
| msgid "Project was not found or you do not have permission to add this project to Security Dashboards." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Project with access" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Project: %{name}" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -33890,9 +33905,6 @@ msgstr "" | |||
| msgid "Projects shared with %{group_name}" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Projects that can be accessed" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Projects to index" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -46657,9 +46669,6 @@ msgstr "" | |||
| msgid "VersionCheck|%{details}" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "VersionCheck|Critical security upgrade available" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "VersionCheck|Important notice - Critical security release" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -46687,9 +46696,6 @@ msgstr "" | |||
| msgid "VersionCheck|You are currently on version %{currentVersion}! We strongly recommend upgrading your GitLab installation to one of the following versions immediately: %{latestStableVersions}." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "VersionCheck|You are currently on version %{currentVersion}. We strongly recommend upgrading your GitLab installation. %{link}" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "VersionCheck|Your GitLab Version" | ||||
| msgstr "" | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,8 +56,8 @@ | |||
|     "@gitlab/at.js": "1.5.7", | ||||
|     "@gitlab/favicon-overlay": "2.0.0", | ||||
|     "@gitlab/fonts": "^1.2.0", | ||||
|     "@gitlab/svgs": "3.18.0", | ||||
|     "@gitlab/ui": "55.0.1", | ||||
|     "@gitlab/svgs": "3.20.0", | ||||
|     "@gitlab/ui": "55.1.0", | ||||
|     "@gitlab/visual-review-tools": "1.7.3", | ||||
|     "@gitlab/web-ide": "0.0.1-dev-20230120231236", | ||||
|     "@rails/actioncable": "6.1.4-7", | ||||
|  |  | |||
|  | @ -40,4 +40,12 @@ RSpec.describe Explore::GroupsController do | |||
|   end | ||||
| 
 | ||||
|   it_behaves_like 'explore groups' | ||||
| 
 | ||||
|   context 'generic_explore_groups flag is disabled' do | ||||
|     before do | ||||
|       stub_feature_flags(generic_explore_groups: false) | ||||
|     end | ||||
| 
 | ||||
|     it_behaves_like 'explore groups' | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -1,10 +1,9 @@ | |||
| import { GlDropdown } from '@gitlab/ui'; | ||||
| import { GlDisclosureDropdown } from '@gitlab/ui'; | ||||
| import { mountExtended } from 'helpers/vue_test_utils_helper'; | ||||
| import ToolbarMoreDropdown from '~/content_editor/components/toolbar_more_dropdown.vue'; | ||||
| import Diagram from '~/content_editor/extensions/diagram'; | ||||
| import HorizontalRule from '~/content_editor/extensions/horizontal_rule'; | ||||
| import eventHubFactory from '~/helpers/event_hub_factory'; | ||||
| import { stubComponent } from 'helpers/stub_component'; | ||||
| import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils'; | ||||
| 
 | ||||
| describe('content_editor/components/toolbar_more_dropdown', () => { | ||||
|  | @ -25,14 +24,11 @@ describe('content_editor/components/toolbar_more_dropdown', () => { | |||
|         tiptapEditor, | ||||
|         eventHub, | ||||
|       }, | ||||
|       stubs: { | ||||
|         GlDropdown: stubComponent(GlDropdown), | ||||
|       }, | ||||
|       propsData, | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   const findDropdown = () => wrapper.findComponent(GlDropdown); | ||||
|   const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown); | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     buildEditor(); | ||||
|  | @ -60,7 +56,7 @@ describe('content_editor/components/toolbar_more_dropdown', () => { | |||
| 
 | ||||
|     beforeEach(async () => { | ||||
|       commands = mockChainedCommands(tiptapEditor, [command, 'focus', 'run']); | ||||
|       btn = wrapper.findByRole('menuitem', { name }); | ||||
|       btn = wrapper.findByRole('button', { name }); | ||||
|     }); | ||||
| 
 | ||||
|     it(`inserts a ${contentType}`, async () => { | ||||
|  | @ -76,12 +72,11 @@ describe('content_editor/components/toolbar_more_dropdown', () => { | |||
|   }); | ||||
| 
 | ||||
|   describe('a11y tests', () => { | ||||
|     it('sets text, title, and text-sr-only properties to the table button dropdown', () => { | ||||
|     it('sets toggleText and text-sr-only properties to the table button dropdown', () => { | ||||
|       expect(findDropdown().props()).toMatchObject({ | ||||
|         text: 'More', | ||||
|         textSrOnly: true, | ||||
|         toggleText: 'More options', | ||||
|       }); | ||||
|       expect(findDropdown().attributes('title')).toBe('More'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -1,84 +0,0 @@ | |||
| import { GlAlert, GlButton, GlLink, GlSprintf } from '@gitlab/ui'; | ||||
| import { shallowMount } from '@vue/test-utils'; | ||||
| import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; | ||||
| import SecurityPatchUpgradeAlert from '~/gitlab_version_check/components/security_patch_upgrade_alert.vue'; | ||||
| import { UPGRADE_DOCS_URL, ABOUT_RELEASES_PAGE } from '~/gitlab_version_check/constants'; | ||||
| 
 | ||||
| describe('SecurityPatchUpgradeAlert', () => { | ||||
|   let wrapper; | ||||
|   let trackingSpy; | ||||
| 
 | ||||
|   const defaultProps = { | ||||
|     currentVersion: '99.9', | ||||
|   }; | ||||
| 
 | ||||
|   const createComponent = () => { | ||||
|     trackingSpy = mockTracking(undefined, undefined, jest.spyOn); | ||||
| 
 | ||||
|     wrapper = shallowMount(SecurityPatchUpgradeAlert, { | ||||
|       propsData: { | ||||
|         ...defaultProps, | ||||
|       }, | ||||
|       stubs: { | ||||
|         GlAlert, | ||||
|         GlSprintf, | ||||
|       }, | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     unmockTracking(); | ||||
|   }); | ||||
| 
 | ||||
|   const findGlAlert = () => wrapper.findComponent(GlAlert); | ||||
|   const findGlButton = () => wrapper.findComponent(GlButton); | ||||
|   const findGlLink = () => wrapper.findComponent(GlLink); | ||||
| 
 | ||||
|   describe('template', () => { | ||||
|     beforeEach(() => { | ||||
|       createComponent(); | ||||
|     }); | ||||
| 
 | ||||
|     it('renders non-dismissible GlAlert with version information', () => { | ||||
|       expect(findGlAlert().text()).toContain( | ||||
|         `You are currently on version ${defaultProps.currentVersion}.`, | ||||
|       ); | ||||
|       expect(findGlAlert().props('dismissible')).toBe(false); | ||||
|     }); | ||||
| 
 | ||||
|     it('tracks render security_patch_upgrade_alert correctly', () => { | ||||
|       expect(trackingSpy).toHaveBeenCalledWith(undefined, 'render', { | ||||
|         label: 'security_patch_upgrade_alert', | ||||
|         property: defaultProps.currentVersion, | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('renders GlLink with correct text and link', () => { | ||||
|       expect(findGlLink().text()).toBe('Learn more about this critical security release.'); | ||||
|       expect(findGlLink().attributes('href')).toBe(ABOUT_RELEASES_PAGE); | ||||
|     }); | ||||
| 
 | ||||
|     it('tracks click security_patch_upgrade_alert_learn_more when link is clicked', async () => { | ||||
|       await findGlLink().vm.$emit('click'); | ||||
| 
 | ||||
|       expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', { | ||||
|         label: 'security_patch_upgrade_alert_learn_more', | ||||
|         property: defaultProps.currentVersion, | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('renders GlButton with correct text and link', () => { | ||||
|       expect(findGlButton().text()).toBe('Upgrade now'); | ||||
|       expect(findGlButton().attributes('href')).toBe(UPGRADE_DOCS_URL); | ||||
|     }); | ||||
| 
 | ||||
|     it('tracks click security_patch_upgrade_alert_upgrade_now when button is clicked', async () => { | ||||
|       await findGlButton().vm.$emit('click'); | ||||
| 
 | ||||
|       expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', { | ||||
|         label: 'security_patch_upgrade_alert_upgrade_now', | ||||
|         property: defaultProps.currentVersion, | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | @ -7,9 +7,6 @@ import { | |||
|   VERSION_CHECK_BADGE_FIXTURE, | ||||
|   VERSION_CHECK_BADGE_FINDER, | ||||
|   VERSION_BADGE_TEXT, | ||||
|   SECURITY_PATCH_FIXTURE, | ||||
|   SECURITY_PATCH_FINDER, | ||||
|   SECURITY_PATCH_TEXT, | ||||
|   SECURITY_MODAL_FIXTURE, | ||||
|   SECURITY_MODAL_FINDER, | ||||
|   SECURITY_MODAL_TEXT, | ||||
|  | @ -29,15 +26,13 @@ describe('initGitlabVersionCheck', () => { | |||
|   }); | ||||
| 
 | ||||
|   describe.each` | ||||
|     description                                                     | fixture                                                                               | finders                                                                       | componentTexts | ||||
|     ${'with no version check elements'}                             | ${'<div></div>'}                                                                      | ${[]}                                                                         | ${[]} | ||||
|     ${'with version check badge el but no prop data'}               | ${VERSION_CHECK_BADGE_NO_PROP_FIXTURE}                                                | ${[VERSION_CHECK_BADGE_FINDER]}                                               | ${[undefined]} | ||||
|     ${'with version check badge el but no severity data'}           | ${VERSION_CHECK_BADGE_NO_SEVERITY_FIXTURE}                                            | ${[VERSION_CHECK_BADGE_FINDER]}                                               | ${[undefined]} | ||||
|     ${'with version check badge el and version data'}               | ${VERSION_CHECK_BADGE_FIXTURE}                                                        | ${[VERSION_CHECK_BADGE_FINDER]}                                               | ${[VERSION_BADGE_TEXT]} | ||||
|     ${'with security patch el'}                                     | ${SECURITY_PATCH_FIXTURE}                                                             | ${[SECURITY_PATCH_FINDER]}                                                    | ${[SECURITY_PATCH_TEXT]} | ||||
|     ${'with security patch and version badge els'}                  | ${`${SECURITY_PATCH_FIXTURE}${VERSION_CHECK_BADGE_FIXTURE}`}                          | ${[SECURITY_PATCH_FINDER, VERSION_CHECK_BADGE_FINDER]}                        | ${[SECURITY_PATCH_TEXT, VERSION_BADGE_TEXT]} | ||||
|     ${'with security modal el'}                                     | ${SECURITY_MODAL_FIXTURE}                                                             | ${[SECURITY_MODAL_FINDER]}                                                    | ${[SECURITY_MODAL_TEXT]} | ||||
|     ${'with security modal, security patch, and version badge els'} | ${`${SECURITY_PATCH_FIXTURE}${SECURITY_MODAL_FIXTURE}${VERSION_CHECK_BADGE_FIXTURE}`} | ${[SECURITY_PATCH_FINDER, SECURITY_MODAL_FINDER, VERSION_CHECK_BADGE_FINDER]} | ${[SECURITY_PATCH_TEXT, SECURITY_MODAL_TEXT, VERSION_BADGE_TEXT]} | ||||
|     description                                           | fixture                                                      | finders                                                | componentTexts | ||||
|     ${'with no version check elements'}                   | ${'<div></div>'}                                             | ${[]}                                                  | ${[]} | ||||
|     ${'with version check badge el but no prop data'}     | ${VERSION_CHECK_BADGE_NO_PROP_FIXTURE}                       | ${[VERSION_CHECK_BADGE_FINDER]}                        | ${[undefined]} | ||||
|     ${'with version check badge el but no severity data'} | ${VERSION_CHECK_BADGE_NO_SEVERITY_FIXTURE}                   | ${[VERSION_CHECK_BADGE_FINDER]}                        | ${[undefined]} | ||||
|     ${'with version check badge el and version data'}     | ${VERSION_CHECK_BADGE_FIXTURE}                               | ${[VERSION_CHECK_BADGE_FINDER]}                        | ${[VERSION_BADGE_TEXT]} | ||||
|     ${'with security modal el'}                           | ${SECURITY_MODAL_FIXTURE}                                    | ${[SECURITY_MODAL_FINDER]}                             | ${[SECURITY_MODAL_TEXT]} | ||||
|     ${'with security modal and version badge els'}        | ${`${SECURITY_MODAL_FIXTURE}${VERSION_CHECK_BADGE_FIXTURE}`} | ${[SECURITY_MODAL_FINDER, VERSION_CHECK_BADGE_FINDER]} | ${[SECURITY_MODAL_TEXT, VERSION_BADGE_TEXT]} | ||||
|   `('$description', ({ fixture, finders, componentTexts }) => {
 | ||||
|     beforeEach(() => { | ||||
|       createApp(fixture); | ||||
|  |  | |||
|  | @ -9,12 +9,6 @@ export const VERSION_CHECK_BADGE_FINDER = '[data-testid="badge-click-wrapper"]'; | |||
| 
 | ||||
| export const VERSION_BADGE_TEXT = 'Up to date'; | ||||
| 
 | ||||
| export const SECURITY_PATCH_FIXTURE = `<div id="js-security-patch-upgrade-alert" data-current-version="15.1"></div>`; | ||||
| 
 | ||||
| export const SECURITY_PATCH_FINDER = 'h2'; | ||||
| 
 | ||||
| export const SECURITY_PATCH_TEXT = 'Critical security upgrade available'; | ||||
| 
 | ||||
| export const SECURITY_MODAL_FIXTURE = `<div id="js-security-patch-upgrade-alert-modal" data-current-version="15.1" data-version='{ "details": "test details", "latest-stable-versions": "[]" }'></div>`; | ||||
| 
 | ||||
| export const SECURITY_MODAL_FINDER = '[data-testid="alert-modal-title"]'; | ||||
|  |  | |||
|  | @ -0,0 +1,311 @@ | |||
| import { GlAlert, GlFormInput, GlToggle, GlLoadingIcon } from '@gitlab/ui'; | ||||
| import Vue from 'vue'; | ||||
| import VueApollo from 'vue-apollo'; | ||||
| import createMockApollo from 'helpers/mock_apollo_helper'; | ||||
| import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import { createAlert } from '~/flash'; | ||||
| import InboundTokenAccess from '~/token_access/components/inbound_token_access.vue'; | ||||
| import inboundAddProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/inbound_add_project_ci_job_token_scope.mutation.graphql'; | ||||
| import inboundRemoveProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/inbound_remove_project_ci_job_token_scope.mutation.graphql'; | ||||
| import inboundUpdateCIJobTokenScopeMutation from '~/token_access/graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql'; | ||||
| import inboundGetCIJobTokenScopeQuery from '~/token_access/graphql/queries/inbound_get_ci_job_token_scope.query.graphql'; | ||||
| import inboundGetProjectsWithCIJobTokenScopeQuery from '~/token_access/graphql/queries/inbound_get_projects_with_ci_job_token_scope.query.graphql'; | ||||
| import { | ||||
|   inboundJobTokenScopeEnabledResponse, | ||||
|   inboundJobTokenScopeDisabledResponse, | ||||
|   inboundProjectsWithScopeResponse, | ||||
|   inboundAddProjectSuccessResponse, | ||||
|   inboundRemoveProjectSuccess, | ||||
|   inboundUpdateScopeSuccessResponse, | ||||
| } from './mock_data'; | ||||
| 
 | ||||
| const projectPath = 'root/my-repo'; | ||||
| const message = 'An error occurred'; | ||||
| const error = new Error(message); | ||||
| 
 | ||||
| Vue.use(VueApollo); | ||||
| 
 | ||||
| jest.mock('~/flash'); | ||||
| 
 | ||||
| describe('TokenAccess component', () => { | ||||
|   let wrapper; | ||||
| 
 | ||||
|   const inboundJobTokenScopeEnabledResponseHandler = jest | ||||
|     .fn() | ||||
|     .mockResolvedValue(inboundJobTokenScopeEnabledResponse); | ||||
|   const inboundJobTokenScopeDisabledResponseHandler = jest | ||||
|     .fn() | ||||
|     .mockResolvedValue(inboundJobTokenScopeDisabledResponse); | ||||
|   const inboundProjectsWithScopeResponseHandler = jest | ||||
|     .fn() | ||||
|     .mockResolvedValue(inboundProjectsWithScopeResponse); | ||||
|   const inboundAddProjectSuccessResponseHandler = jest | ||||
|     .fn() | ||||
|     .mockResolvedValue(inboundAddProjectSuccessResponse); | ||||
|   const inboundRemoveProjectSuccessHandler = jest | ||||
|     .fn() | ||||
|     .mockResolvedValue(inboundRemoveProjectSuccess); | ||||
|   const inboundUpdateScopeSuccessResponseHandler = jest | ||||
|     .fn() | ||||
|     .mockResolvedValue(inboundUpdateScopeSuccessResponse); | ||||
|   const failureHandler = jest.fn().mockRejectedValue(error); | ||||
| 
 | ||||
|   const findToggle = () => wrapper.findComponent(GlToggle); | ||||
|   const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); | ||||
|   const findAddProjectBtn = () => wrapper.findByRole('button', { name: 'Add project' }); | ||||
|   const findCancelBtn = () => wrapper.findByRole('button', { name: 'Cancel' }); | ||||
|   const findProjectInput = () => wrapper.findComponent(GlFormInput); | ||||
|   const findRemoveProjectBtn = () => wrapper.findByRole('button', { name: 'Remove access' }); | ||||
|   const findTokenDisabledAlert = () => wrapper.findComponent(GlAlert); | ||||
| 
 | ||||
|   const createMockApolloProvider = (requestHandlers) => { | ||||
|     return createMockApollo(requestHandlers); | ||||
|   }; | ||||
| 
 | ||||
|   const createComponent = (requestHandlers, mountFn = shallowMountExtended) => { | ||||
|     wrapper = mountFn(InboundTokenAccess, { | ||||
|       provide: { | ||||
|         fullPath: projectPath, | ||||
|       }, | ||||
|       apolloProvider: createMockApolloProvider(requestHandlers), | ||||
|       data() { | ||||
|         return { | ||||
|           targetProjectPath: 'root/test', | ||||
|         }; | ||||
|       }, | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   describe('loading state', () => { | ||||
|     it('shows loading state while waiting on query to resolve', async () => { | ||||
|       createComponent([ | ||||
|         [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|         [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|       ]); | ||||
| 
 | ||||
|       expect(findLoadingIcon().exists()).toBe(true); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(findLoadingIcon().exists()).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('fetching projects and scope', () => { | ||||
|     it('fetches projects and scope correctly', () => { | ||||
|       const expectedVariables = { | ||||
|         fullPath: 'root/my-repo', | ||||
|       }; | ||||
| 
 | ||||
|       createComponent([ | ||||
|         [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|         [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|       ]); | ||||
| 
 | ||||
|       expect(inboundJobTokenScopeEnabledResponseHandler).toHaveBeenCalledWith(expectedVariables); | ||||
|       expect(inboundProjectsWithScopeResponseHandler).toHaveBeenCalledWith(expectedVariables); | ||||
|     }); | ||||
| 
 | ||||
|     it('handles fetch projects error correctly', async () => { | ||||
|       createComponent([ | ||||
|         [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|         [inboundGetProjectsWithCIJobTokenScopeQuery, failureHandler], | ||||
|       ]); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(createAlert).toHaveBeenCalledWith({ | ||||
|         message: 'There was a problem fetching the projects', | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('handles fetch scope error correctly', async () => { | ||||
|       createComponent([ | ||||
|         [inboundGetCIJobTokenScopeQuery, failureHandler], | ||||
|         [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|       ]); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(createAlert).toHaveBeenCalledWith({ | ||||
|         message: 'There was a problem fetching the job token scope value', | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('toggle', () => { | ||||
|     it('the toggle is on and the alert is hidden', async () => { | ||||
|       createComponent([ | ||||
|         [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|         [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|       ]); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(findToggle().props('value')).toBe(true); | ||||
|       expect(findTokenDisabledAlert().exists()).toBe(false); | ||||
|     }); | ||||
| 
 | ||||
|     it('the toggle is off and the alert is visible', async () => { | ||||
|       createComponent([ | ||||
|         [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeDisabledResponseHandler], | ||||
|         [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|       ]); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(findToggle().props('value')).toBe(false); | ||||
|       expect(findTokenDisabledAlert().exists()).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     describe('update ci job token scope', () => { | ||||
|       it('calls inboundUpdateCIJobTokenScopeMutation mutation', async () => { | ||||
|         createComponent( | ||||
|           [ | ||||
|             [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|             [inboundUpdateCIJobTokenScopeMutation, inboundUpdateScopeSuccessResponseHandler], | ||||
|           ], | ||||
|           mountExtended, | ||||
|         ); | ||||
| 
 | ||||
|         await waitForPromises(); | ||||
| 
 | ||||
|         expect(findToggle().props('value')).toBe(true); | ||||
| 
 | ||||
|         findToggle().vm.$emit('change', false); | ||||
| 
 | ||||
|         await waitForPromises(); | ||||
| 
 | ||||
|         expect(findToggle().props('value')).toBe(false); | ||||
|         expect(inboundUpdateScopeSuccessResponseHandler).toHaveBeenCalledWith({ | ||||
|           input: { | ||||
|             fullPath: 'root/my-repo', | ||||
|             inboundJobTokenScopeEnabled: false, | ||||
|           }, | ||||
|         }); | ||||
|       }); | ||||
| 
 | ||||
|       it('handles update scope error correctly', async () => { | ||||
|         createComponent( | ||||
|           [ | ||||
|             [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeDisabledResponseHandler], | ||||
|             [inboundUpdateCIJobTokenScopeMutation, failureHandler], | ||||
|           ], | ||||
|           mountExtended, | ||||
|         ); | ||||
| 
 | ||||
|         await waitForPromises(); | ||||
| 
 | ||||
|         expect(findToggle().props('value')).toBe(false); | ||||
| 
 | ||||
|         findToggle().vm.$emit('change', true); | ||||
| 
 | ||||
|         await waitForPromises(); | ||||
| 
 | ||||
|         expect(findToggle().props('value')).toBe(false); | ||||
|         expect(createAlert).toHaveBeenCalledWith({ message }); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('add project', () => { | ||||
|     it('calls add project mutation', async () => { | ||||
|       createComponent( | ||||
|         [ | ||||
|           [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|           [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|           [inboundAddProjectCIJobTokenScopeMutation, inboundAddProjectSuccessResponseHandler], | ||||
|         ], | ||||
|         mountExtended, | ||||
|       ); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       findAddProjectBtn().trigger('click'); | ||||
| 
 | ||||
|       expect(inboundAddProjectSuccessResponseHandler).toHaveBeenCalledWith({ | ||||
|         projectPath, | ||||
|         targetProjectPath: 'root/test', | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('add project handles error correctly', async () => { | ||||
|       createComponent( | ||||
|         [ | ||||
|           [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|           [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|           [inboundAddProjectCIJobTokenScopeMutation, failureHandler], | ||||
|         ], | ||||
|         mountExtended, | ||||
|       ); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       findAddProjectBtn().trigger('click'); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(createAlert).toHaveBeenCalledWith({ message }); | ||||
|     }); | ||||
| 
 | ||||
|     it('clicking cancel clears target path', async () => { | ||||
|       createComponent( | ||||
|         [ | ||||
|           [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|           [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|         ], | ||||
|         mountExtended, | ||||
|       ); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(findProjectInput().element.value).toBe('root/test'); | ||||
| 
 | ||||
|       await findCancelBtn().trigger('click'); | ||||
| 
 | ||||
|       expect(findProjectInput().element.value).toBe(''); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('remove project', () => { | ||||
|     it('calls remove project mutation', async () => { | ||||
|       createComponent( | ||||
|         [ | ||||
|           [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|           [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|           [inboundRemoveProjectCIJobTokenScopeMutation, inboundRemoveProjectSuccessHandler], | ||||
|         ], | ||||
|         mountExtended, | ||||
|       ); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       findRemoveProjectBtn().trigger('click'); | ||||
| 
 | ||||
|       expect(inboundRemoveProjectSuccessHandler).toHaveBeenCalledWith({ | ||||
|         projectPath, | ||||
|         targetProjectPath: 'root/ci-project', | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('remove project handles error correctly', async () => { | ||||
|       createComponent( | ||||
|         [ | ||||
|           [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler], | ||||
|           [inboundGetProjectsWithCIJobTokenScopeQuery, inboundProjectsWithScopeResponseHandler], | ||||
|           [inboundRemoveProjectCIJobTokenScopeMutation, failureHandler], | ||||
|         ], | ||||
|         mountExtended, | ||||
|       ); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       findRemoveProjectBtn().trigger('click'); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
| 
 | ||||
|       expect(createAlert).toHaveBeenCalledWith({ message }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | @ -106,6 +106,21 @@ export const mockProjects = [ | |||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| export const mockFields = [ | ||||
|   { | ||||
|     key: 'project', | ||||
|     label: 'Project with access', | ||||
|   }, | ||||
|   { | ||||
|     key: 'namespace', | ||||
|     label: 'Namespace', | ||||
|   }, | ||||
|   { | ||||
|     key: 'actions', | ||||
|     label: '', | ||||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| export const optInJwtQueryResponse = (optInJwt) => ({ | ||||
|   data: { | ||||
|     project: { | ||||
|  | @ -131,3 +146,84 @@ export const optInJwtMutationResponse = (optInJwt) => ({ | |||
|     }, | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| export const inboundJobTokenScopeEnabledResponse = { | ||||
|   data: { | ||||
|     project: { | ||||
|       id: '1', | ||||
|       ciCdSettings: { | ||||
|         inboundJobTokenScopeEnabled: true, | ||||
|         __typename: 'ProjectCiCdSetting', | ||||
|       }, | ||||
|       __typename: 'Project', | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const inboundJobTokenScopeDisabledResponse = { | ||||
|   data: { | ||||
|     project: { | ||||
|       id: '1', | ||||
|       ciCdSettings: { | ||||
|         inboundJobTokenScopeEnabled: false, | ||||
|         __typename: 'ProjectCiCdSetting', | ||||
|       }, | ||||
|       __typename: 'Project', | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const inboundProjectsWithScopeResponse = { | ||||
|   data: { | ||||
|     project: { | ||||
|       __typename: 'Project', | ||||
|       id: '1', | ||||
|       ciJobTokenScope: { | ||||
|         __typename: 'CiJobTokenScopeType', | ||||
|         inboundAllowlist: { | ||||
|           __typename: 'ProjectConnection', | ||||
|           nodes: [ | ||||
|             { | ||||
|               __typename: 'Project', | ||||
|               fullPath: 'root/ci-project', | ||||
|               id: 'gid://gitlab/Project/23', | ||||
|               name: 'ci-project', | ||||
|               namespace: { id: 'gid://gitlab/Namespaces::UserNamespace/1', fullPath: 'root' }, | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const inboundAddProjectSuccessResponse = { | ||||
|   data: { | ||||
|     ciJobTokenScopeAddProject: { | ||||
|       errors: [], | ||||
|       __typename: 'CiJobTokenScopeAddProjectPayload', | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const inboundRemoveProjectSuccess = { | ||||
|   data: { | ||||
|     ciJobTokenScopeRemoveProject: { | ||||
|       errors: [], | ||||
|       __typename: 'CiJobTokenScopeRemoveProjectPayload', | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const inboundUpdateScopeSuccessResponse = { | ||||
|   data: { | ||||
|     ciCdSettingsUpdate: { | ||||
|       ciCdSettings: { | ||||
|         inboundJobTokenScopeEnabled: false, | ||||
|         __typename: 'ProjectCiCdSetting', | ||||
|       }, | ||||
|       errors: [], | ||||
|       __typename: 'CiCdSettingsUpdatePayload', | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
|  |  | |||
|  | @ -5,8 +5,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; | |||
| import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; | ||||
| import waitForPromises from 'helpers/wait_for_promises'; | ||||
| import { createAlert } from '~/flash'; | ||||
| import OptInJwt from '~/token_access/components/opt_in_jwt.vue'; | ||||
| import TokenAccess from '~/token_access/components/token_access.vue'; | ||||
| import OutboundTokenAccess from '~/token_access/components/outbound_token_access.vue'; | ||||
| import addProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/add_project_ci_job_token_scope.mutation.graphql'; | ||||
| import removeProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/remove_project_ci_job_token_scope.mutation.graphql'; | ||||
| import updateCIJobTokenScopeMutation from '~/token_access/graphql/mutations/update_ci_job_token_scope.mutation.graphql'; | ||||
|  | @ -41,7 +40,6 @@ describe('TokenAccess component', () => { | |||
|   const failureHandler = jest.fn().mockRejectedValue(error); | ||||
| 
 | ||||
|   const findToggle = () => wrapper.findComponent(GlToggle); | ||||
|   const findOptInJwt = () => wrapper.findComponent(OptInJwt); | ||||
|   const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); | ||||
|   const findAddProjectBtn = () => wrapper.findByRole('button', { name: 'Add project' }); | ||||
|   const findRemoveProjectBtn = () => wrapper.findByRole('button', { name: 'Remove access' }); | ||||
|  | @ -52,7 +50,7 @@ describe('TokenAccess component', () => { | |||
|   }; | ||||
| 
 | ||||
|   const createComponent = (requestHandlers, mountFn = shallowMountExtended) => { | ||||
|     wrapper = mountFn(TokenAccess, { | ||||
|     wrapper = mountFn(OutboundTokenAccess, { | ||||
|       provide: { | ||||
|         fullPath: projectPath, | ||||
|       }, | ||||
|  | @ -65,10 +63,6 @@ describe('TokenAccess component', () => { | |||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     wrapper.destroy(); | ||||
|   }); | ||||
| 
 | ||||
|   describe('loading state', () => { | ||||
|     it('shows loading state while waiting on query to resolve', async () => { | ||||
|       createComponent([ | ||||
|  | @ -84,21 +78,6 @@ describe('TokenAccess component', () => { | |||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('template', () => { | ||||
|     beforeEach(async () => { | ||||
|       createComponent([ | ||||
|         [getCIJobTokenScopeQuery, enabledJobTokenScopeHandler], | ||||
|         [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScopeHandler], | ||||
|       ]); | ||||
| 
 | ||||
|       await waitForPromises(); | ||||
|     }); | ||||
| 
 | ||||
|     it('renders the opt in jwt component', () => { | ||||
|       expect(findOptInJwt().exists()).toBe(true); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('fetching projects and scope', () => { | ||||
|     it('fetches projects and scope correctly', () => { | ||||
|       const expectedVariables = { | ||||
|  | @ -0,0 +1,47 @@ | |||
| import { shallowMount } from '@vue/test-utils'; | ||||
| import OutboundTokenAccess from '~/token_access/components/outbound_token_access.vue'; | ||||
| import InboundTokenAccess from '~/token_access/components/inbound_token_access.vue'; | ||||
| import OptInJwt from '~/token_access/components/opt_in_jwt.vue'; | ||||
| import TokenAccessApp from '~/token_access/components/token_access_app.vue'; | ||||
| 
 | ||||
| describe('TokenAccessApp component', () => { | ||||
|   let wrapper; | ||||
| 
 | ||||
|   const findOutboundTokenAccess = () => wrapper.findComponent(OutboundTokenAccess); | ||||
|   const findInboundTokenAccess = () => wrapper.findComponent(InboundTokenAccess); | ||||
|   const findOptInJwt = () => wrapper.findComponent(OptInJwt); | ||||
| 
 | ||||
|   const createComponent = (flagState = false) => { | ||||
|     wrapper = shallowMount(TokenAccessApp, { | ||||
|       provide: { | ||||
|         glFeatures: { ciInboundJobTokenScope: flagState }, | ||||
|       }, | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   describe('default', () => { | ||||
|     beforeEach(() => { | ||||
|       createComponent(); | ||||
|     }); | ||||
| 
 | ||||
|     it('renders the opt in jwt component', () => { | ||||
|       expect(findOptInJwt().exists()).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     it('renders the outbound token access component', () => { | ||||
|       expect(findOutboundTokenAccess().exists()).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     it('does not render the inbound token access component', () => { | ||||
|       expect(findInboundTokenAccess().exists()).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('with feature flag enabled', () => { | ||||
|     it('renders the inbound token access component', () => { | ||||
|       createComponent(true); | ||||
| 
 | ||||
|       expect(findInboundTokenAccess().exists()).toBe(true); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | @ -1,7 +1,7 @@ | |||
| import { GlTable, GlButton } from '@gitlab/ui'; | ||||
| import { mountExtended } from 'helpers/vue_test_utils_helper'; | ||||
| import TokenProjectsTable from '~/token_access/components/token_projects_table.vue'; | ||||
| import { mockProjects } from './mock_data'; | ||||
| import { mockProjects, mockFields } from './mock_data'; | ||||
| 
 | ||||
| describe('Token projects table', () => { | ||||
|   let wrapper; | ||||
|  | @ -12,6 +12,7 @@ describe('Token projects table', () => { | |||
|         fullPath: 'root/ci-project', | ||||
|       }, | ||||
|       propsData: { | ||||
|         tableFields: mockFields, | ||||
|         projects: mockProjects, | ||||
|       }, | ||||
|     }); | ||||
|  | @ -28,10 +29,6 @@ describe('Token projects table', () => { | |||
|     createComponent(); | ||||
|   }); | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     wrapper.destroy(); | ||||
|   }); | ||||
| 
 | ||||
|   it('displays a table', () => { | ||||
|     expect(findTable().exists()).toBe(true); | ||||
|   }); | ||||
|  |  | |||
|  | @ -49,26 +49,29 @@ RSpec.describe VersionCheckHelper do | |||
| 
 | ||||
|   describe '#show_security_patch_upgrade_alert?' do | ||||
|     describe 'return conditions' do | ||||
|       where(:feature_enabled, :show_version_check, :gitlab_version_check, :result) do | ||||
|       where(:show_version_check, :gitlab_version_check, :result) do | ||||
|         [ | ||||
|           [false, false, nil, false], | ||||
|           [false, false, { "severity" => "success" }, false], | ||||
|           [false, false, { "severity" => "danger" }, false], | ||||
|           [false, true, nil, false], | ||||
|           [false, true, { "severity" => "success" }, false], | ||||
|           [false, true, { "severity" => "danger" }, false], | ||||
|           [true, false, nil, false], | ||||
|           [true, false, { "severity" => "success" }, false], | ||||
|           [true, false, { "severity" => "danger" }, false], | ||||
|           [true, true, nil, false], | ||||
|           [true, true, { "severity" => "success" }, false], | ||||
|           [true, true, { "severity" => "danger" }, true] | ||||
|           [false, nil, false], | ||||
|           [false, { "severity" => "success" }, false], | ||||
|           [false, { "severity" => "danger" }, false], | ||||
|           [false, { "severity" => "danger", "critical_vulnerability" => 'some text' }, false], | ||||
|           [false, { "severity" => "danger", "critical_vulnerability" => 'false' }, false], | ||||
|           [false, { "severity" => "danger", "critical_vulnerability" => false }, false], | ||||
|           [false, { "severity" => "danger", "critical_vulnerability" => 'true' }, false], | ||||
|           [false, { "severity" => "danger", "critical_vulnerability" => true }, false], | ||||
|           [true, nil, false], | ||||
|           [true, { "severity" => "success" }, nil], | ||||
|           [true, { "severity" => "danger" }, nil], | ||||
|           [true, { "severity" => "danger", "critical_vulnerability" => 'some text' }, nil], | ||||
|           [true, { "severity" => "danger", "critical_vulnerability" => 'false' }, false], | ||||
|           [true, { "severity" => "danger", "critical_vulnerability" => false }, false], | ||||
|           [true, { "severity" => "danger", "critical_vulnerability" => 'true' }, true], | ||||
|           [true, { "severity" => "danger", "critical_vulnerability" => true }, true] | ||||
|         ] | ||||
|       end | ||||
| 
 | ||||
|       with_them do | ||||
|         before do | ||||
|           stub_feature_flags(critical_security_alert: feature_enabled) | ||||
|           allow(helper).to receive(:show_version_check?).and_return(show_version_check) | ||||
|           allow(helper).to receive(:gitlab_version_check).and_return(gitlab_version_check) | ||||
|         end | ||||
|  |  | |||
|  | @ -2775,16 +2775,6 @@ RSpec.describe Repository, feature_category: :source_code_management do | |||
|       it 'returns a Tree' do | ||||
|         expect(repository.head_tree).to be_an_instance_of(Tree) | ||||
|       end | ||||
| 
 | ||||
|       context 'when feature flag "optimized_head_tree" is disabled' do | ||||
|         before do | ||||
|           stub_feature_flags(optimized_head_tree: false) | ||||
|         end | ||||
| 
 | ||||
|         it 'returns a Tree' do | ||||
|           expect(repository.head_tree).to be_an_instance_of(Tree) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'with a non-existing repository' do | ||||
|  | @ -2793,18 +2783,6 @@ RSpec.describe Repository, feature_category: :source_code_management do | |||
| 
 | ||||
|         expect(repository.head_tree).to be_nil | ||||
|       end | ||||
| 
 | ||||
|       context 'when feature flag "optimized_head_tree" is disabled' do | ||||
|         before do | ||||
|           stub_feature_flags(optimized_head_tree: false) | ||||
|         end | ||||
| 
 | ||||
|         it 'returns nil' do | ||||
|           expect(repository).to receive(:head_commit).and_return(nil) | ||||
| 
 | ||||
|           expect(repository.head_tree).to be_nil | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  | @ -2817,33 +2795,16 @@ RSpec.describe Repository, feature_category: :source_code_management do | |||
|     let(:pagination_params) { nil } | ||||
| 
 | ||||
|     context 'using a non-existing repository' do | ||||
|       context 'when feature flag "optimized_head_tree" is enabled' do | ||||
|         before do | ||||
|           allow(repository).to receive(:root_ref).and_return(nil) | ||||
|         end | ||||
| 
 | ||||
|         it { is_expected.to be_nil } | ||||
| 
 | ||||
|         context 'when path is defined' do | ||||
|           let(:path) { 'README.md' } | ||||
| 
 | ||||
|           it { is_expected.to be_nil } | ||||
|         end | ||||
|       before do | ||||
|         allow(repository).to receive(:root_ref).and_return(nil) | ||||
|       end | ||||
| 
 | ||||
|       context 'when feature flag "optimized_head_tree" is disabled' do | ||||
|         before do | ||||
|           stub_feature_flags(optimized_head_tree: false) | ||||
|           allow(repository).to receive(:head_commit).and_return(nil) | ||||
|         end | ||||
|       it { is_expected.to be_nil } | ||||
| 
 | ||||
|       context 'when path is defined' do | ||||
|         let(:path) { 'README.md' } | ||||
| 
 | ||||
|         it { is_expected.to be_nil } | ||||
| 
 | ||||
|         context 'when path is defined' do | ||||
|           let(:path) { 'README.md' } | ||||
| 
 | ||||
|           it { is_expected.to be_nil } | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,10 +9,6 @@ RSpec.describe 'shared/gitlab_version/_security_patch_upgrade_alert' do | |||
|       render | ||||
|     end | ||||
| 
 | ||||
|     it 'renders the security patch upgrade alert' do | ||||
|       expect(rendered).to have_selector('#js-security-patch-upgrade-alert') | ||||
|     end | ||||
| 
 | ||||
|     it 'renders the security patch upgrade alert modal' do | ||||
|       expect(rendered).to have_selector('#js-security-patch-upgrade-alert-modal') | ||||
|     end | ||||
|  |  | |||
							
								
								
									
										16
									
								
								yarn.lock
								
								
								
								
							
							
						
						
									
										16
									
								
								yarn.lock
								
								
								
								
							|  | @ -1342,15 +1342,15 @@ | |||
|     stylelint-declaration-strict-value "1.8.0" | ||||
|     stylelint-scss "4.2.0" | ||||
| 
 | ||||
| "@gitlab/svgs@3.18.0": | ||||
|   version "3.18.0" | ||||
|   resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.18.0.tgz#d89364feb42404a35824a54d518b51af8c1e900f" | ||||
|   integrity sha512-ni9TmhusXpt/3k/DZzovMEUeB/6UTXiDpuujI8HDBqR4Mwlah6FBco5ZfolkW6YjFL0YvtcLWhnwZA0iM3hfMw== | ||||
| "@gitlab/svgs@3.20.0": | ||||
|   version "3.20.0" | ||||
|   resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.20.0.tgz#4ee4f2f24304d13ccce58f82c2ecd87e556f35b4" | ||||
|   integrity sha512-nYTF4j5kon4XbBr/sAzuubgxjIne9+RTZLmSrSaL9FL4eyuv9aa7YMCcOrlIbYX5jlSYlcD+ck2F2M1sqXXOBA== | ||||
| 
 | ||||
| "@gitlab/ui@55.0.1": | ||||
|   version "55.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-55.0.1.tgz#3d5c22b864d64435eff01fb39e4edb6e1b8a0432" | ||||
|   integrity sha512-vPwmGhc73HTSBGoMi4jL+Wf11w/1o62pwXuYSYNOs2xis1xMLJiaPBGXNKDFliC5AaRDXAIxCiC5CMJZH3qXMg== | ||||
| "@gitlab/ui@55.1.0": | ||||
|   version "55.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-55.1.0.tgz#a2ea6951365c495df7acdd1351b1660771607b67" | ||||
|   integrity sha512-0E+l76jNsK3BPqQmbuTKAvC4RfjQfpaLvmlShe8wxrnMrS0IKsse43RST0ttV+mhkOVfac0me8pDhN4ijSm7Tw== | ||||
|   dependencies: | ||||
|     "@popperjs/core" "^2.11.2" | ||||
|     bootstrap-vue "2.20.1" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue