Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									bf000ffcdf
								
							
						
					
					
						commit
						fbc7e1f503
					
				|  | @ -1,17 +1,46 @@ | ||||||
| <script> | <script> | ||||||
| import { GlButton } from '@gitlab/ui'; | import { GlButton, GlDropdown, GlDropdownItem, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui'; | ||||||
| import { mapActions, mapGetters, mapState } from 'vuex'; | import { mapActions, mapGetters, mapState } from 'vuex'; | ||||||
| import Dropdown from './dropdown.vue'; | import { __ } from '~/locale'; | ||||||
|  | 
 | ||||||
|  | const barLabel = __('File templates'); | ||||||
|  | const templateListDropdownLabel = __('Choose a template...'); | ||||||
|  | const templateTypesDropdownLabel = __('Choose a type...'); | ||||||
|  | const undoButtonText = __('Undo'); | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
|  |   i18n: { | ||||||
|  |     barLabel, | ||||||
|  |     templateListDropdownLabel, | ||||||
|  |     templateTypesDropdownLabel, | ||||||
|  |     undoButtonText, | ||||||
|  |   }, | ||||||
|   components: { |   components: { | ||||||
|     Dropdown, |  | ||||||
|     GlButton, |     GlButton, | ||||||
|  |     GlDropdown, | ||||||
|  |     GlDropdownItem, | ||||||
|  |     GlLoadingIcon, | ||||||
|  |     GlSearchBoxByType, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       search: '', | ||||||
|  |     }; | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     ...mapGetters(['activeFile']), |     ...mapGetters(['activeFile']), | ||||||
|     ...mapGetters('fileTemplates', ['templateTypes']), |     ...mapGetters('fileTemplates', ['templateTypes']), | ||||||
|     ...mapState('fileTemplates', ['selectedTemplateType', 'updateSuccess']), |     ...mapState('fileTemplates', [ | ||||||
|  |       'selectedTemplateType', | ||||||
|  |       'updateSuccess', | ||||||
|  |       'templates', | ||||||
|  |       'isLoading', | ||||||
|  |     ]), | ||||||
|  |     filteredTemplateTypes() { | ||||||
|  |       return this.templates.filter((t) => { | ||||||
|  |         return t.name.toLowerCase().includes(this.search.toLowerCase()); | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|     showTemplatesDropdown() { |     showTemplatesDropdown() { | ||||||
|       return Object.keys(this.selectedTemplateType).length > 0; |       return Object.keys(this.selectedTemplateType).length > 0; | ||||||
|     }, |     }, | ||||||
|  | @ -26,6 +55,7 @@ export default { | ||||||
|     ...mapActions('fileTemplates', [ |     ...mapActions('fileTemplates', [ | ||||||
|       'setSelectedTemplateType', |       'setSelectedTemplateType', | ||||||
|       'fetchTemplate', |       'fetchTemplate', | ||||||
|  |       'fetchTemplateTypes', | ||||||
|       'undoFileTemplate', |       'undoFileTemplate', | ||||||
|     ]), |     ]), | ||||||
|     setInitialType() { |     setInitialType() { | ||||||
|  | @ -50,27 +80,46 @@ export default { | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <div |   <div | ||||||
|     class="d-flex align-items-center ide-file-templates qa-file-templates-bar gl-relative gl-z-index-1" |     class="gl-display-flex gl-align-items-center ide-file-templates qa-file-templates-bar gl-relative gl-z-index-1" | ||||||
|   > |   > | ||||||
|     <strong class="gl-mr-3"> {{ __('File templates') }} </strong> |     <strong class="gl-mr-3"> {{ $options.i18n.barLabel }} </strong> | ||||||
|     <dropdown |     <gl-dropdown | ||||||
|       :data="templateTypes" |       class="gl-mr-6 qa-file-templates-bar" | ||||||
|       :label="selectedTemplateType.name || __('Choose a type...')" |       :text="selectedTemplateType.name || $options.i18n.templateTypesDropdownLabel" | ||||||
|       class="mr-2" |     > | ||||||
|       @click="selectTemplateType" |       <gl-dropdown-item | ||||||
|     /> |         v-for="template in templateTypes" | ||||||
|     <dropdown |         :key="template.key" | ||||||
|  |         @click.prevent="selectTemplateType(template)" | ||||||
|  |       > | ||||||
|  |         {{ template.name }} | ||||||
|  |       </gl-dropdown-item> | ||||||
|  |     </gl-dropdown> | ||||||
|  |     <gl-dropdown | ||||||
|       v-if="showTemplatesDropdown" |       v-if="showTemplatesDropdown" | ||||||
|       :label="__('Choose a template...')" |       class="gl-mr-6 qa-file-template-dropdown" | ||||||
|       :is-async-data="true" |       :text="$options.i18n.templateListDropdownLabel" | ||||||
|       :searchable="true" |       @show="fetchTemplateTypes" | ||||||
|       :title="__('File templates')" |     > | ||||||
|       class="mr-2 qa-file-template-dropdown" |       <template #header> | ||||||
|       @click="selectTemplate" |         <gl-search-box-by-type v-model.trim="search" /> | ||||||
|     /> |       </template> | ||||||
|  |       <div> | ||||||
|  |         <gl-loading-icon v-if="isLoading" /> | ||||||
|  |         <template v-else> | ||||||
|  |           <gl-dropdown-item | ||||||
|  |             v-for="template in filteredTemplateTypes" | ||||||
|  |             :key="template.key" | ||||||
|  |             @click="selectTemplate(template)" | ||||||
|  |           > | ||||||
|  |             {{ template.name }} | ||||||
|  |           </gl-dropdown-item> | ||||||
|  |         </template> | ||||||
|  |       </div> | ||||||
|  |     </gl-dropdown> | ||||||
|     <transition name="fade"> |     <transition name="fade"> | ||||||
|       <gl-button v-show="updateSuccess" category="secondary" variant="default" @click="undo"> |       <gl-button v-show="updateSuccess" category="secondary" variant="default" @click="undo"> | ||||||
|         {{ __('Undo') }} |         {{ $options.i18n.undoButtonText }} | ||||||
|       </gl-button> |       </gl-button> | ||||||
|     </transition> |     </transition> | ||||||
|   </div> |   </div> | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/con | ||||||
| import { | import { | ||||||
|   CREATED_DESC, |   CREATED_DESC, | ||||||
|   i18n, |   i18n, | ||||||
|  |   ISSUE_REFERENCE, | ||||||
|   MAX_LIST_SIZE, |   MAX_LIST_SIZE, | ||||||
|   PAGE_SIZE, |   PAGE_SIZE, | ||||||
|   PARAM_STATE, |   PARAM_STATE, | ||||||
|  | @ -219,11 +220,13 @@ export default { | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     queryVariables() { |     queryVariables() { | ||||||
|  |       const isIidSearch = ISSUE_REFERENCE.test(this.searchQuery); | ||||||
|       return { |       return { | ||||||
|         fullPath: this.fullPath, |         fullPath: this.fullPath, | ||||||
|  |         iid: isIidSearch ? this.searchQuery.slice(1) : undefined, | ||||||
|         isProject: this.isProject, |         isProject: this.isProject, | ||||||
|         isSignedIn: this.isSignedIn, |         isSignedIn: this.isSignedIn, | ||||||
|         search: this.searchQuery, |         search: isIidSearch ? undefined : this.searchQuery, | ||||||
|         sort: this.sortKey, |         sort: this.sortKey, | ||||||
|         state: this.state, |         state: this.state, | ||||||
|         ...this.pageParams, |         ...this.pageParams, | ||||||
|  |  | ||||||
|  | @ -52,6 +52,7 @@ export const i18n = { | ||||||
|   upvotes: __('Upvotes'), |   upvotes: __('Upvotes'), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | export const ISSUE_REFERENCE = /^#\d+$/; | ||||||
| export const MAX_LIST_SIZE = 10; | export const MAX_LIST_SIZE = 10; | ||||||
| export const PAGE_SIZE = 20; | export const PAGE_SIZE = 20; | ||||||
| export const PAGE_SIZE_MANUAL = 100; | export const PAGE_SIZE_MANUAL = 100; | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ query getIssues( | ||||||
|   $isProject: Boolean = false |   $isProject: Boolean = false | ||||||
|   $isSignedIn: Boolean = false |   $isSignedIn: Boolean = false | ||||||
|   $fullPath: ID! |   $fullPath: ID! | ||||||
|  |   $iid: String | ||||||
|   $search: String |   $search: String | ||||||
|   $sort: IssueSort |   $sort: IssueSort | ||||||
|   $state: IssuableState |   $state: IssuableState | ||||||
|  | @ -29,6 +30,7 @@ query getIssues( | ||||||
|     id |     id | ||||||
|     issues( |     issues( | ||||||
|       includeSubgroups: true |       includeSubgroups: true | ||||||
|  |       iid: $iid | ||||||
|       search: $search |       search: $search | ||||||
|       sort: $sort |       sort: $sort | ||||||
|       state: $state |       state: $state | ||||||
|  | @ -59,6 +61,7 @@ query getIssues( | ||||||
|   project(fullPath: $fullPath) @include(if: $isProject) { |   project(fullPath: $fullPath) @include(if: $isProject) { | ||||||
|     id |     id | ||||||
|     issues( |     issues( | ||||||
|  |       iid: $iid | ||||||
|       search: $search |       search: $search | ||||||
|       sort: $sort |       sort: $sort | ||||||
|       state: $state |       state: $state | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| query getIssuesCount( | query getIssuesCount( | ||||||
|   $isProject: Boolean = false |   $isProject: Boolean = false | ||||||
|   $fullPath: ID! |   $fullPath: ID! | ||||||
|  |   $iid: String | ||||||
|   $search: String |   $search: String | ||||||
|   $assigneeId: String |   $assigneeId: String | ||||||
|   $assigneeUsernames: [String!] |   $assigneeUsernames: [String!] | ||||||
|  | @ -20,6 +21,7 @@ query getIssuesCount( | ||||||
|     openedIssues: issues( |     openedIssues: issues( | ||||||
|       includeSubgroups: true |       includeSubgroups: true | ||||||
|       state: opened |       state: opened | ||||||
|  |       iid: $iid | ||||||
|       search: $search |       search: $search | ||||||
|       assigneeId: $assigneeId |       assigneeId: $assigneeId | ||||||
|       assigneeUsernames: $assigneeUsernames |       assigneeUsernames: $assigneeUsernames | ||||||
|  | @ -37,6 +39,7 @@ query getIssuesCount( | ||||||
|     closedIssues: issues( |     closedIssues: issues( | ||||||
|       includeSubgroups: true |       includeSubgroups: true | ||||||
|       state: closed |       state: closed | ||||||
|  |       iid: $iid | ||||||
|       search: $search |       search: $search | ||||||
|       assigneeId: $assigneeId |       assigneeId: $assigneeId | ||||||
|       assigneeUsernames: $assigneeUsernames |       assigneeUsernames: $assigneeUsernames | ||||||
|  | @ -54,6 +57,7 @@ query getIssuesCount( | ||||||
|     allIssues: issues( |     allIssues: issues( | ||||||
|       includeSubgroups: true |       includeSubgroups: true | ||||||
|       state: all |       state: all | ||||||
|  |       iid: $iid | ||||||
|       search: $search |       search: $search | ||||||
|       assigneeId: $assigneeId |       assigneeId: $assigneeId | ||||||
|       assigneeUsernames: $assigneeUsernames |       assigneeUsernames: $assigneeUsernames | ||||||
|  | @ -73,6 +77,7 @@ query getIssuesCount( | ||||||
|     id |     id | ||||||
|     openedIssues: issues( |     openedIssues: issues( | ||||||
|       state: opened |       state: opened | ||||||
|  |       iid: $iid | ||||||
|       search: $search |       search: $search | ||||||
|       assigneeId: $assigneeId |       assigneeId: $assigneeId | ||||||
|       assigneeUsernames: $assigneeUsernames |       assigneeUsernames: $assigneeUsernames | ||||||
|  | @ -91,6 +96,7 @@ query getIssuesCount( | ||||||
|     } |     } | ||||||
|     closedIssues: issues( |     closedIssues: issues( | ||||||
|       state: closed |       state: closed | ||||||
|  |       iid: $iid | ||||||
|       search: $search |       search: $search | ||||||
|       assigneeId: $assigneeId |       assigneeId: $assigneeId | ||||||
|       assigneeUsernames: $assigneeUsernames |       assigneeUsernames: $assigneeUsernames | ||||||
|  | @ -109,6 +115,7 @@ query getIssuesCount( | ||||||
|     } |     } | ||||||
|     allIssues: issues( |     allIssues: issues( | ||||||
|       state: all |       state: all | ||||||
|  |       iid: $iid | ||||||
|       search: $search |       search: $search | ||||||
|       assigneeId: $assigneeId |       assigneeId: $assigneeId | ||||||
|       assigneeUsernames: $assigneeUsernames |       assigneeUsernames: $assigneeUsernames | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| export const TRACK_TOGGLE_TRAINING_PROVIDER_ACTION = 'toggle_security_training_provider'; | export const TRACK_TOGGLE_TRAINING_PROVIDER_ACTION = 'toggle_security_training_provider'; | ||||||
| export const TRACK_TOGGLE_TRAINING_PROVIDER_LABEL = 'update_security_training_provider'; | export const TRACK_TOGGLE_TRAINING_PROVIDER_LABEL = 'update_security_training_provider'; | ||||||
| export const TRACK_CLICK_TRAINING_LINK = 'click_security_training_link'; | export const TRACK_CLICK_TRAINING_LINK_ACTION = 'click_security_training_link'; | ||||||
| export const TRACK_PROVIDER_LEARN_MORE_CLICK_ACTION = 'click_link'; | export const TRACK_PROVIDER_LEARN_MORE_CLICK_ACTION = 'click_link'; | ||||||
| export const TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL = 'security_training_provider'; | export const TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL = 'security_training_provider'; | ||||||
| export const TRACK_TRAINING_LOADED_ACTION = 'security_training_link_loaded'; | export const TRACK_TRAINING_LOADED_ACTION = 'security_training_link_loaded'; | ||||||
|  |  | ||||||
|  | @ -9,10 +9,10 @@ module Types | ||||||
| 
 | 
 | ||||||
|         authorize :read_package |         authorize :read_package | ||||||
| 
 | 
 | ||||||
|  |         field :icon_url, GraphQL::Types::String, null: true, description: 'Icon URL of the Nuget package.' | ||||||
|         field :id, ::Types::GlobalIDType[::Packages::Nuget::Metadatum], null: false, description: 'ID of the metadatum.' |         field :id, ::Types::GlobalIDType[::Packages::Nuget::Metadatum], null: false, description: 'ID of the metadatum.' | ||||||
|         field :license_url, GraphQL::Types::String, null: true, description: 'License URL of the Nuget package.' |         field :license_url, GraphQL::Types::String, null: true, description: 'License URL of the Nuget package.' | ||||||
|         field :project_url, GraphQL::Types::String, null: true, description: 'Project URL of the Nuget package.' |         field :project_url, GraphQL::Types::String, null: true, description: 'Project URL of the Nuget package.' | ||||||
|         field :icon_url, GraphQL::Types::String, null: true, description: 'Icon URL of the Nuget package.' |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -7,9 +7,9 @@ module Types | ||||||
|       description 'Represents a package dependency link' |       description 'Represents a package dependency link' | ||||||
|       authorize :read_package |       authorize :read_package | ||||||
| 
 | 
 | ||||||
|       field :id, ::Types::GlobalIDType[::Packages::DependencyLink], null: false, description: 'ID of the dependency link.' |  | ||||||
|       field :dependency_type, Types::Packages::PackageDependencyTypeEnum, null: false, description: 'Dependency type.' |  | ||||||
|       field :dependency, Types::Packages::PackageDependencyType, null: true, description: 'Dependency.' |       field :dependency, Types::Packages::PackageDependencyType, null: true, description: 'Dependency.' | ||||||
|  |       field :dependency_type, Types::Packages::PackageDependencyTypeEnum, null: false, description: 'Dependency type.' | ||||||
|  |       field :id, ::Types::GlobalIDType[::Packages::DependencyLink], null: false, description: 'ID of the dependency link.' | ||||||
|       field :metadata, Types::Packages::DependencyLinkMetadataType, null: true, description: 'Dependency link metadata.' |       field :metadata, Types::Packages::DependencyLinkMetadataType, null: true, description: 'Dependency link metadata.' | ||||||
| 
 | 
 | ||||||
|       # NOTE: This method must be kept in sync with the union |       # NOTE: This method must be kept in sync with the union | ||||||
|  |  | ||||||
|  | @ -7,17 +7,17 @@ module Types | ||||||
|       description 'Represents a package file' |       description 'Represents a package file' | ||||||
|       authorize :read_package |       authorize :read_package | ||||||
| 
 | 
 | ||||||
|       field :id, ::Types::GlobalIDType[::Packages::PackageFile], null: false, description: 'ID of the file.' |  | ||||||
|       field :created_at, Types::TimeType, null: false, description: 'Created date.' |       field :created_at, Types::TimeType, null: false, description: 'Created date.' | ||||||
|       field :updated_at, Types::TimeType, null: false, description: 'Updated date.' |  | ||||||
|       field :size, GraphQL::Types::String, null: false, description: 'Size of the package file.' |  | ||||||
|       field :file_name, GraphQL::Types::String, null: false, description: 'Name of the package file.' |  | ||||||
|       field :download_path, GraphQL::Types::String, null: false, description: 'Download path of the package file.' |       field :download_path, GraphQL::Types::String, null: false, description: 'Download path of the package file.' | ||||||
|       field :file_md5, GraphQL::Types::String, null: true, description: 'Md5 of the package file.' |       field :file_md5, GraphQL::Types::String, null: true, description: 'Md5 of the package file.' | ||||||
|       field :file_sha1, GraphQL::Types::String, null: true, description: 'Sha1 of the package file.' |  | ||||||
|       field :file_sha256, GraphQL::Types::String, null: true, description: 'Sha256 of the package file.' |  | ||||||
|       field :file_metadata, Types::Packages::FileMetadataType, null: true, |       field :file_metadata, Types::Packages::FileMetadataType, null: true, | ||||||
|         description: 'File metadata.' |         description: 'File metadata.' | ||||||
|  |       field :file_name, GraphQL::Types::String, null: false, description: 'Name of the package file.' | ||||||
|  |       field :file_sha1, GraphQL::Types::String, null: true, description: 'Sha1 of the package file.' | ||||||
|  |       field :file_sha256, GraphQL::Types::String, null: true, description: 'Sha256 of the package file.' | ||||||
|  |       field :id, ::Types::GlobalIDType[::Packages::PackageFile], null: false, description: 'ID of the file.' | ||||||
|  |       field :size, GraphQL::Types::String, null: false, description: 'Size of the package file.' | ||||||
|  |       field :updated_at, Types::TimeType, null: false, description: 'Updated date.' | ||||||
| 
 | 
 | ||||||
|       # NOTE: This method must be kept in sync with the union |       # NOTE: This method must be kept in sync with the union | ||||||
|       # type: `Types::Packages::FileMetadataType`. |       # type: `Types::Packages::FileMetadataType`. | ||||||
|  |  | ||||||
|  | @ -7,9 +7,9 @@ module Types | ||||||
|       description 'Represents a package tag' |       description 'Represents a package tag' | ||||||
|       authorize :read_package |       authorize :read_package | ||||||
| 
 | 
 | ||||||
|  |       field :created_at, Types::TimeType, null: false, description: 'Created date.' | ||||||
|       field :id, GraphQL::Types::ID, null: false, description: 'ID of the tag.' |       field :id, GraphQL::Types::ID, null: false, description: 'ID of the tag.' | ||||||
|       field :name, GraphQL::Types::String, null: false, description: 'Name of the tag.' |       field :name, GraphQL::Types::String, null: false, description: 'Name of the tag.' | ||||||
|       field :created_at, Types::TimeType, null: false, description: 'Created date.' |  | ||||||
|       field :updated_at, Types::TimeType, null: false, description: 'Updated date.' |       field :updated_at, Types::TimeType, null: false, description: 'Updated date.' | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -13,23 +13,23 @@ module Types | ||||||
|       field :id, ::Types::GlobalIDType[::Packages::Package], null: false, |       field :id, ::Types::GlobalIDType[::Packages::Package], null: false, | ||||||
|             description: 'ID of the package.' |             description: 'ID of the package.' | ||||||
| 
 | 
 | ||||||
|       field :name, GraphQL::Types::String, null: false, description: 'Name of the package.' |       field :can_destroy, GraphQL::Types::Boolean, null: false, description: 'Whether the user can destroy the package.' | ||||||
|       field :created_at, Types::TimeType, null: false, description: 'Date of creation.' |       field :created_at, Types::TimeType, null: false, description: 'Date of creation.' | ||||||
|       field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.' |       field :metadata, Types::Packages::MetadataType, null: true, | ||||||
|       field :version, GraphQL::Types::String, null: true, description: 'Version string.' |         description: 'Package metadata.' | ||||||
|  |       field :name, GraphQL::Types::String, null: false, description: 'Name of the package.' | ||||||
|       field :package_type, Types::Packages::PackageTypeEnum, null: false, description: 'Package type.' |       field :package_type, Types::Packages::PackageTypeEnum, null: false, description: 'Package type.' | ||||||
|       field :tags, Types::Packages::PackageTagType.connection_type, null: true, description: 'Package tags.' |  | ||||||
|       field :project, Types::ProjectType, null: false, description: 'Project where the package is stored.' |  | ||||||
|       field :pipelines, Types::Ci::PipelineType.connection_type, null: true, |       field :pipelines, Types::Ci::PipelineType.connection_type, null: true, | ||||||
|             description: 'Pipelines that built the package.', |             description: 'Pipelines that built the package.', | ||||||
|             deprecated: { reason: 'Due to scalability concerns, this field is going to be removed', milestone: '14.6' } |             deprecated: { reason: 'Due to scalability concerns, this field is going to be removed', milestone: '14.6' } | ||||||
|       field :metadata, Types::Packages::MetadataType, null: true, |       field :project, Types::ProjectType, null: false, description: 'Project where the package is stored.' | ||||||
|         description: 'Package metadata.' |       field :status, Types::Packages::PackageStatusEnum, null: false, description: 'Package status.' | ||||||
|  |       field :tags, Types::Packages::PackageTagType.connection_type, null: true, description: 'Package tags.' | ||||||
|  |       field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.' | ||||||
|  |       field :version, GraphQL::Types::String, null: true, description: 'Version string.' | ||||||
|       field :versions, ::Types::Packages::PackageType.connection_type, null: true, |       field :versions, ::Types::Packages::PackageType.connection_type, null: true, | ||||||
|         description: 'Other versions of the package.', |         description: 'Other versions of the package.', | ||||||
|         deprecated: { reason: 'This field is now only returned in the PackageDetailsType', milestone: '13.11' } |         deprecated: { reason: 'This field is now only returned in the PackageDetailsType', milestone: '13.11' } | ||||||
|       field :status, Types::Packages::PackageStatusEnum, null: false, description: 'Package status.' |  | ||||||
|       field :can_destroy, GraphQL::Types::Boolean, null: false, description: 'Whether the user can destroy the package.' |  | ||||||
| 
 | 
 | ||||||
|       def project |       def project | ||||||
|         Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find |         Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find | ||||||
|  |  | ||||||
|  | @ -9,23 +9,23 @@ module Types | ||||||
|     field :commit_count, GraphQL::Types::Float, null: false, |     field :commit_count, GraphQL::Types::Float, null: false, | ||||||
|           description: 'Commit count of the project.' |           description: 'Commit count of the project.' | ||||||
| 
 | 
 | ||||||
|     field :storage_size, GraphQL::Types::Float, null: false, |  | ||||||
|           description: 'Storage size of the project in bytes.' |  | ||||||
|     field :repository_size, GraphQL::Types::Float, null: false, |  | ||||||
|           description: 'Repository size of the project in bytes.' |  | ||||||
|     field :lfs_objects_size, GraphQL::Types::Float, null: false, |  | ||||||
|           description: 'Large File Storage (LFS) object size of the project in bytes.' |  | ||||||
|     field :build_artifacts_size, GraphQL::Types::Float, null: false, |     field :build_artifacts_size, GraphQL::Types::Float, null: false, | ||||||
|           description: 'Build artifacts size of the project in bytes.' |           description: 'Build artifacts size of the project in bytes.' | ||||||
|  |     field :lfs_objects_size, GraphQL::Types::Float, null: false, | ||||||
|  |           description: 'Large File Storage (LFS) object size of the project in bytes.' | ||||||
|     field :packages_size, GraphQL::Types::Float, null: false, |     field :packages_size, GraphQL::Types::Float, null: false, | ||||||
|           description: 'Packages size of the project in bytes.' |           description: 'Packages size of the project in bytes.' | ||||||
|     field :wiki_size, GraphQL::Types::Float, null: true, |  | ||||||
|           description: 'Wiki size of the project in bytes.' |  | ||||||
|     field :snippets_size, GraphQL::Types::Float, null: true, |  | ||||||
|           description: 'Snippets size of the project in bytes.' |  | ||||||
|     field :pipeline_artifacts_size, GraphQL::Types::Float, null: true, |     field :pipeline_artifacts_size, GraphQL::Types::Float, null: true, | ||||||
|           description: 'CI Pipeline artifacts size in bytes.' |           description: 'CI Pipeline artifacts size in bytes.' | ||||||
|  |     field :repository_size, GraphQL::Types::Float, null: false, | ||||||
|  |           description: 'Repository size of the project in bytes.' | ||||||
|  |     field :snippets_size, GraphQL::Types::Float, null: true, | ||||||
|  |           description: 'Snippets size of the project in bytes.' | ||||||
|  |     field :storage_size, GraphQL::Types::Float, null: false, | ||||||
|  |           description: 'Storage size of the project in bytes.' | ||||||
|     field :uploads_size, GraphQL::Types::Float, null: true, |     field :uploads_size, GraphQL::Types::Float, null: true, | ||||||
|           description: 'Uploads size of the project in bytes.' |           description: 'Uploads size of the project in bytes.' | ||||||
|  |     field :wiki_size, GraphQL::Types::Float, null: true, | ||||||
|  |           description: 'Wiki size of the project in bytes.' | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -11,21 +11,21 @@ module Types | ||||||
|     field :id, GraphQL::Types::ID, null: false, |     field :id, GraphQL::Types::ID, null: false, | ||||||
|           description: 'ID of the project.' |           description: 'ID of the project.' | ||||||
| 
 | 
 | ||||||
|  |     field :ci_config_path_or_default, GraphQL::Types::String, null: false, | ||||||
|  |           description: 'Path of the CI configuration file.' | ||||||
|     field :full_path, GraphQL::Types::ID, null: false, |     field :full_path, GraphQL::Types::ID, null: false, | ||||||
|           description: 'Full path of the project.' |           description: 'Full path of the project.' | ||||||
|     field :path, GraphQL::Types::String, null: false, |     field :path, GraphQL::Types::String, null: false, | ||||||
|           description: 'Path of the project.' |           description: 'Path of the project.' | ||||||
|     field :ci_config_path_or_default, GraphQL::Types::String, null: false, |  | ||||||
|           description: 'Path of the CI configuration file.' |  | ||||||
| 
 | 
 | ||||||
|     field :sast_ci_configuration, Types::CiConfiguration::Sast::Type, null: true, |     field :sast_ci_configuration, Types::CiConfiguration::Sast::Type, null: true, | ||||||
|       calls_gitaly: true, |       calls_gitaly: true, | ||||||
|       description: 'SAST CI configuration for the project.' |       description: 'SAST CI configuration for the project.' | ||||||
| 
 | 
 | ||||||
|     field :name_with_namespace, GraphQL::Types::String, null: false, |  | ||||||
|           description: 'Full name of the project with its namespace.' |  | ||||||
|     field :name, GraphQL::Types::String, null: false, |     field :name, GraphQL::Types::String, null: false, | ||||||
|           description: 'Name of the project (without namespace).' |           description: 'Name of the project (without namespace).' | ||||||
|  |     field :name_with_namespace, GraphQL::Types::String, null: false, | ||||||
|  |           description: 'Full name of the project with its namespace.' | ||||||
| 
 | 
 | ||||||
|     field :description, GraphQL::Types::String, null: true, |     field :description, GraphQL::Types::String, null: true, | ||||||
|           description: 'Short description of the project.' |           description: 'Short description of the project.' | ||||||
|  | @ -37,17 +37,17 @@ module Types | ||||||
|     field :topics, [GraphQL::Types::String], null: true, |     field :topics, [GraphQL::Types::String], null: true, | ||||||
|           description: 'List of project topics.' |           description: 'List of project topics.' | ||||||
| 
 | 
 | ||||||
|     field :ssh_url_to_repo, GraphQL::Types::String, null: true, |  | ||||||
|           description: 'URL to connect to the project via SSH.' |  | ||||||
|     field :http_url_to_repo, GraphQL::Types::String, null: true, |     field :http_url_to_repo, GraphQL::Types::String, null: true, | ||||||
|           description: 'URL to connect to the project via HTTPS.' |           description: 'URL to connect to the project via HTTPS.' | ||||||
|  |     field :ssh_url_to_repo, GraphQL::Types::String, null: true, | ||||||
|  |           description: 'URL to connect to the project via SSH.' | ||||||
|     field :web_url, GraphQL::Types::String, null: true, |     field :web_url, GraphQL::Types::String, null: true, | ||||||
|           description: 'Web URL of the project.' |           description: 'Web URL of the project.' | ||||||
| 
 | 
 | ||||||
|     field :star_count, GraphQL::Types::Int, null: false, |  | ||||||
|           description: 'Number of times the project has been starred.' |  | ||||||
|     field :forks_count, GraphQL::Types::Int, null: false, calls_gitaly: true, # 4 times |     field :forks_count, GraphQL::Types::Int, null: false, calls_gitaly: true, # 4 times | ||||||
|           description: 'Number of times the project has been forked.' |           description: 'Number of times the project has been forked.' | ||||||
|  |     field :star_count, GraphQL::Types::Int, null: false, | ||||||
|  |           description: 'Number of times the project has been starred.' | ||||||
| 
 | 
 | ||||||
|     field :created_at, Types::TimeType, null: true, |     field :created_at, Types::TimeType, null: true, | ||||||
|           description: 'Timestamp of the project creation.' |           description: 'Timestamp of the project creation.' | ||||||
|  | @ -60,12 +60,12 @@ module Types | ||||||
|     field :visibility, GraphQL::Types::String, null: true, |     field :visibility, GraphQL::Types::String, null: true, | ||||||
|           description: 'Visibility of the project.' |           description: 'Visibility of the project.' | ||||||
| 
 | 
 | ||||||
|     field :shared_runners_enabled, GraphQL::Types::Boolean, null: true, |  | ||||||
|           description: 'Indicates if shared runners are enabled for the project.' |  | ||||||
|     field :lfs_enabled, GraphQL::Types::Boolean, null: true, |     field :lfs_enabled, GraphQL::Types::Boolean, null: true, | ||||||
|           description: 'Indicates if the project has Large File Storage (LFS) enabled.' |           description: 'Indicates if the project has Large File Storage (LFS) enabled.' | ||||||
|     field :merge_requests_ff_only_enabled, GraphQL::Types::Boolean, null: true, |     field :merge_requests_ff_only_enabled, GraphQL::Types::Boolean, null: true, | ||||||
|           description: 'Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded.' |           description: 'Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded.' | ||||||
|  |     field :shared_runners_enabled, GraphQL::Types::Boolean, null: true, | ||||||
|  |           description: 'Indicates if shared runners are enabled for the project.' | ||||||
| 
 | 
 | ||||||
|     field :service_desk_enabled, GraphQL::Types::Boolean, null: true, |     field :service_desk_enabled, GraphQL::Types::Boolean, null: true, | ||||||
|           description: 'Indicates if the project has service desk enabled.' |           description: 'Indicates if the project has service desk enabled.' | ||||||
|  | @ -85,33 +85,33 @@ module Types | ||||||
|     field :open_issues_count, GraphQL::Types::Int, null: true, |     field :open_issues_count, GraphQL::Types::Int, null: true, | ||||||
|           description: 'Number of open issues for the project.' |           description: 'Number of open issues for the project.' | ||||||
| 
 | 
 | ||||||
|  |     field :allow_merge_on_skipped_pipeline, GraphQL::Types::Boolean, null: true, | ||||||
|  |           description: 'If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of the project can also be merged with skipped jobs.' | ||||||
|  |     field :autoclose_referenced_issues, GraphQL::Types::Boolean, null: true, | ||||||
|  |           description: 'Indicates if issues referenced by merge requests and commits within the default branch are closed automatically.' | ||||||
|     field :import_status, GraphQL::Types::String, null: true, |     field :import_status, GraphQL::Types::String, null: true, | ||||||
|           description: 'Status of import background job of the project.' |           description: 'Status of import background job of the project.' | ||||||
|     field :jira_import_status, GraphQL::Types::String, null: true, |     field :jira_import_status, GraphQL::Types::String, null: true, | ||||||
|           description: 'Status of Jira import background job of the project.' |           description: 'Status of Jira import background job of the project.' | ||||||
|     field :only_allow_merge_if_pipeline_succeeds, GraphQL::Types::Boolean, null: true, |  | ||||||
|           description: 'Indicates if merge requests of the project can only be merged with successful jobs.' |  | ||||||
|     field :allow_merge_on_skipped_pipeline, GraphQL::Types::Boolean, null: true, |  | ||||||
|           description: 'If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of the project can also be merged with skipped jobs.' |  | ||||||
|     field :request_access_enabled, GraphQL::Types::Boolean, null: true, |  | ||||||
|           description: 'Indicates if users can request member access to the project.' |  | ||||||
|     field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::Types::Boolean, null: true, |     field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::Types::Boolean, null: true, | ||||||
|           description: 'Indicates if merge requests of the project can only be merged when all the discussions are resolved.' |           description: 'Indicates if merge requests of the project can only be merged when all the discussions are resolved.' | ||||||
|  |     field :only_allow_merge_if_pipeline_succeeds, GraphQL::Types::Boolean, null: true, | ||||||
|  |           description: 'Indicates if merge requests of the project can only be merged with successful jobs.' | ||||||
|     field :printing_merge_request_link_enabled, GraphQL::Types::Boolean, null: true, |     field :printing_merge_request_link_enabled, GraphQL::Types::Boolean, null: true, | ||||||
|           description: 'Indicates if a link to create or view a merge request should display after a push to Git repositories of the project from the command line.' |           description: 'Indicates if a link to create or view a merge request should display after a push to Git repositories of the project from the command line.' | ||||||
|     field :remove_source_branch_after_merge, GraphQL::Types::Boolean, null: true, |     field :remove_source_branch_after_merge, GraphQL::Types::Boolean, null: true, | ||||||
|           description: 'Indicates if `Delete source branch` option should be enabled by default for all new merge requests of the project.' |           description: 'Indicates if `Delete source branch` option should be enabled by default for all new merge requests of the project.' | ||||||
|     field :autoclose_referenced_issues, GraphQL::Types::Boolean, null: true, |     field :request_access_enabled, GraphQL::Types::Boolean, null: true, | ||||||
|           description: 'Indicates if issues referenced by merge requests and commits within the default branch are closed automatically.' |           description: 'Indicates if users can request member access to the project.' | ||||||
|     field :suggestion_commit_message, GraphQL::Types::String, null: true, |  | ||||||
|           description: 'Commit message used to apply merge request suggestions.' |  | ||||||
|     field :squash_read_only, GraphQL::Types::Boolean, null: false, method: :squash_readonly?, |     field :squash_read_only, GraphQL::Types::Boolean, null: false, method: :squash_readonly?, | ||||||
|           description: 'Indicates if `squashReadOnly` is enabled.' |           description: 'Indicates if `squashReadOnly` is enabled.' | ||||||
|  |     field :suggestion_commit_message, GraphQL::Types::String, null: true, | ||||||
|  |           description: 'Commit message used to apply merge request suggestions.' | ||||||
| 
 | 
 | ||||||
|     field :namespace, Types::NamespaceType, null: true, |  | ||||||
|           description: 'Namespace of the project.' |  | ||||||
|     field :group, Types::GroupType, null: true, |     field :group, Types::GroupType, null: true, | ||||||
|           description: 'Group of the project.' |           description: 'Group of the project.' | ||||||
|  |     field :namespace, Types::NamespaceType, null: true, | ||||||
|  |           description: 'Namespace of the project.' | ||||||
| 
 | 
 | ||||||
|     field :statistics, Types::ProjectStatisticsType, |     field :statistics, Types::ProjectStatisticsType, | ||||||
|           null: true, |           null: true, | ||||||
|  |  | ||||||
|  | @ -9,11 +9,11 @@ module Types | ||||||
| 
 | 
 | ||||||
|         field :key, GraphQL::Types::String, null: false, |         field :key, GraphQL::Types::String, null: false, | ||||||
|               description: 'Key of the Jira project.' |               description: 'Key of the Jira project.' | ||||||
|  |         field :name, GraphQL::Types::String, null: true, | ||||||
|  |               description: 'Name of the Jira project.' | ||||||
|         field :project_id, GraphQL::Types::Int, null: false, |         field :project_id, GraphQL::Types::Int, null: false, | ||||||
|               description: 'ID of the Jira project.', |               description: 'ID of the Jira project.', | ||||||
|               method: :id |               method: :id | ||||||
|         field :name, GraphQL::Types::String, null: true, |  | ||||||
|               description: 'Name of the Jira project.' |  | ||||||
|       end |       end | ||||||
|       # rubocop:enable Graphql/AuthorizeTypes |       # rubocop:enable Graphql/AuthorizeTypes | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  | @ -10,15 +10,16 @@ module IssuableLink | ||||||
|   extend ActiveSupport::Concern |   extend ActiveSupport::Concern | ||||||
| 
 | 
 | ||||||
|   TYPE_RELATES_TO = 'relates_to' |   TYPE_RELATES_TO = 'relates_to' | ||||||
|   TYPE_BLOCKS = 'blocks' |   TYPE_BLOCKS = 'blocks' ## EE-only. Kept here to be used on link_type enum. | ||||||
|   # we don't store is_blocked_by in the db but need it for displaying the relation |  | ||||||
|   # from the target |  | ||||||
|   TYPE_IS_BLOCKED_BY = 'is_blocked_by' |  | ||||||
| 
 | 
 | ||||||
|   class_methods do |   class_methods do | ||||||
|     def inverse_link_type(type) |     def inverse_link_type(type) | ||||||
|       type |       type | ||||||
|     end |     end | ||||||
|  | 
 | ||||||
|  |     def issuable_type | ||||||
|  |       raise NotImplementedError | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   included do |   included do | ||||||
|  | @ -44,12 +45,11 @@ module IssuableLink | ||||||
|       return unless source && target |       return unless source && target | ||||||
| 
 | 
 | ||||||
|       if self.class.base_class.find_by(source: target, target: source) |       if self.class.base_class.find_by(source: target, target: source) | ||||||
|         errors.add(:source, "is already related to this #{issuable_type}") |         errors.add(:source, "is already related to this #{self.class.issuable_type}") | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 |  | ||||||
|     def issuable_type |  | ||||||
|       raise NotImplementedError |  | ||||||
|     end |  | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  | 
 | ||||||
|  | IssuableLink.prepend_mod_with('IssuableLink') | ||||||
|  | IssuableLink::ClassMethods.prepend_mod_with('IssuableLink::ClassMethods') | ||||||
|  |  | ||||||
|  | @ -10,10 +10,10 @@ class IssueLink < ApplicationRecord | ||||||
|   scope :for_source_issue, ->(issue) { where(source_id: issue.id) } |   scope :for_source_issue, ->(issue) { where(source_id: issue.id) } | ||||||
|   scope :for_target_issue, ->(issue) { where(target_id: issue.id) } |   scope :for_target_issue, ->(issue) { where(target_id: issue.id) } | ||||||
| 
 | 
 | ||||||
|   private |   class << self | ||||||
| 
 |     def issuable_type | ||||||
|   def issuable_type |       :issue | ||||||
|     :issue |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,7 +18,6 @@ swap: | ||||||
|   file name: filename |   file name: filename | ||||||
|   filesystem: file system |   filesystem: file system | ||||||
|   info: information |   info: information | ||||||
|   need to: must |  | ||||||
|   repo: repository |   repo: repository | ||||||
|   timezone: time zone |   timezone: time zone | ||||||
|   utilize: use |   utilize: use | ||||||
|  |  | ||||||
|  | @ -5,9 +5,12 @@ | ||||||
| # | # | ||||||
| # For a list of all options, see https://docs.errata.ai/vale/styles | # For a list of all options, see https://docs.errata.ai/vale/styles | ||||||
| extends: substitution | extends: substitution | ||||||
| message: 'Be concise: "%s" is less wordy than "%s".' | message: '%s "%s".' | ||||||
| link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html | link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html | ||||||
| level: suggestion | level: suggestion | ||||||
| ignorecase: true | ignorecase: true | ||||||
| swap: | swap: | ||||||
|   in order to: to |   in order to: "Be concise: use 'to' rather than" | ||||||
|  |   needs? to: "Rewrite the sentence, or use 'must', instead of" | ||||||
|  |   note that: "Be concise: rewrite the sentence to not use" | ||||||
|  |   please: "Remove this word from the sentence: " | ||||||
|  |  | ||||||
|  | @ -192,7 +192,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o | ||||||
|    for more details. |    for more details. | ||||||
| 
 | 
 | ||||||
|    NOTE: |    NOTE: | ||||||
|    If you need to use `0.0.0.0` or `*` as the listen_address, you also need to add |    If you need to use `0.0.0.0` or `*` as the listen_address, you also must add | ||||||
|    `127.0.0.1/32` to the `postgresql['md5_auth_cidr_addresses']` setting, to allow Rails to connect through |    `127.0.0.1/32` to the `postgresql['md5_auth_cidr_addresses']` setting, to allow Rails to connect through | ||||||
|    `127.0.0.1`. For more information, see [omnibus-5258](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5258). |    `127.0.0.1`. For more information, see [omnibus-5258](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5258). | ||||||
| 
 | 
 | ||||||
|  | @ -378,7 +378,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o | ||||||
| 1. Configure PostgreSQL: | 1. Configure PostgreSQL: | ||||||
| 
 | 
 | ||||||
|    This step is similar to how we configured the **primary** instance. |    This step is similar to how we configured the **primary** instance. | ||||||
|    We need to enable this, even if using a single node. |    We must enable this, even if using a single node. | ||||||
| 
 | 
 | ||||||
|    Edit `/etc/gitlab/gitlab.rb` and add the following, replacing the IP |    Edit `/etc/gitlab/gitlab.rb` and add the following, replacing the IP | ||||||
|    addresses with addresses appropriate to your network configuration: |    addresses with addresses appropriate to your network configuration: | ||||||
|  | @ -407,7 +407,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o | ||||||
|    ``` |    ``` | ||||||
| 
 | 
 | ||||||
|    For external PostgreSQL instances, see [additional instructions](external_database.md). |    For external PostgreSQL instances, see [additional instructions](external_database.md). | ||||||
|    If you bring a former **primary** node back online to serve as a **secondary** node, then you also need to remove `roles(['geo_primary_role'])` or `geo_primary_role['enable'] = true`. |    If you bring a former **primary** node back online to serve as a **secondary** node, then you also must remove `roles(['geo_primary_role'])` or `geo_primary_role['enable'] = true`. | ||||||
| 
 | 
 | ||||||
| 1. Reconfigure GitLab for the changes to take effect: | 1. Reconfigure GitLab for the changes to take effect: | ||||||
| 
 | 
 | ||||||
|  | @ -472,7 +472,7 @@ data before running `pg_basebackup`. | ||||||
|      initial replication to take under an hour. |      initial replication to take under an hour. | ||||||
|    - Pass `--sslmode=disable` to skip PostgreSQL TLS authentication altogether |    - Pass `--sslmode=disable` to skip PostgreSQL TLS authentication altogether | ||||||
|      (for example, you know the network path is secure, or you are using a site-to-site |      (for example, you know the network path is secure, or you are using a site-to-site | ||||||
|      VPN). This is **not** safe over the public Internet! |      VPN). It is **not** safe over the public Internet! | ||||||
|    - You can read more details about each `sslmode` in the |    - You can read more details about each `sslmode` in the | ||||||
|      [PostgreSQL documentation](https://www.postgresql.org/docs/12/libpq-ssl.html#LIBPQ-SSL-PROTECTION); |      [PostgreSQL documentation](https://www.postgresql.org/docs/12/libpq-ssl.html#LIBPQ-SSL-PROTECTION); | ||||||
|      the instructions above are carefully written to ensure protection against |      the instructions above are carefully written to ensure protection against | ||||||
|  | @ -480,7 +480,7 @@ data before running `pg_basebackup`. | ||||||
|    - Change the `--slot-name` to the name of the replication slot |    - Change the `--slot-name` to the name of the replication slot | ||||||
|      to be used on the **primary** database. The script attempts to create the |      to be used on the **primary** database. The script attempts to create the | ||||||
|      replication slot automatically if it does not exist. |      replication slot automatically if it does not exist. | ||||||
|    - If you're repurposing an old server into a Geo **secondary** node, you need to |    - If you're repurposing an old server into a Geo **secondary** node, you must | ||||||
|      add `--force` to the command line. |      add `--force` to the command line. | ||||||
|    - When not in a production machine you can disable backup step if you |    - When not in a production machine you can disable backup step if you | ||||||
|      really sure this is what you want by adding `--skip-backup` |      really sure this is what you want by adding `--skip-backup` | ||||||
|  | @ -547,7 +547,7 @@ FATAL:  could not connect to the primary server: FATAL:  password authentication | ||||||
| 
 | 
 | ||||||
| On all GitLab Geo **secondary** servers: | On all GitLab Geo **secondary** servers: | ||||||
| 
 | 
 | ||||||
| 1. The first step isn't necessary from a configuration perspective, since the hashed `'sql_replication_password'` | 1. The first step isn't necessary from a configuration perspective, because the hashed `'sql_replication_password'` | ||||||
|    is not used on the GitLab Geo **secondary**. However in the event that **secondary** needs to be promoted |    is not used on the GitLab Geo **secondary**. However in the event that **secondary** needs to be promoted | ||||||
|    to the GitLab Geo **primary**, make sure to match the `'sql_replication_password'` in the secondary |    to the GitLab Geo **primary**, make sure to match the `'sql_replication_password'` in the secondary | ||||||
|    server configuration. |    server configuration. | ||||||
|  | @ -612,7 +612,7 @@ and other database best practices. | ||||||
| 
 | 
 | ||||||
| ##### Step 1. Configure Patroni permanent replication slot on the primary site | ##### Step 1. Configure Patroni permanent replication slot on the primary site | ||||||
| 
 | 
 | ||||||
| To set up database replication with Patroni on a secondary node, we need to | To set up database replication with Patroni on a secondary node, we must | ||||||
| configure a _permanent replication slot_ on the primary node's Patroni cluster, | configure a _permanent replication slot_ on the primary node's Patroni cluster, | ||||||
| and ensure password authentication is used. | and ensure password authentication is used. | ||||||
| 
 | 
 | ||||||
|  | @ -678,7 +678,7 @@ Leader instance**: | ||||||
| ##### Step 2. Configure the internal load balancer on the primary site | ##### Step 2. Configure the internal load balancer on the primary site | ||||||
| 
 | 
 | ||||||
| To avoid reconfiguring the Standby Leader on the secondary site whenever a new | To avoid reconfiguring the Standby Leader on the secondary site whenever a new | ||||||
| Leader is elected on the primary site, we need to set up a TCP internal load | Leader is elected on the primary site, we must set up a TCP internal load | ||||||
| balancer which gives a single endpoint for connecting to the Patroni | balancer which gives a single endpoint for connecting to the Patroni | ||||||
| cluster's Leader. | cluster's Leader. | ||||||
| 
 | 
 | ||||||
|  | @ -860,7 +860,7 @@ For each Patroni instance on the secondary site: | ||||||
| 
 | 
 | ||||||
| ### Migrating from repmgr to Patroni | ### Migrating from repmgr to Patroni | ||||||
| 
 | 
 | ||||||
| 1. Before migrating, it is recommended that there is no replication lag between the primary and secondary sites and that replication is paused. In GitLab 13.2 and later, you can pause and resume replication with `gitlab-ctl geo-replication-pause` and `gitlab-ctl geo-replication-resume` on a Geo secondary database node. | 1. Before migrating, we recommend that there is no replication lag between the primary and secondary sites and that replication is paused. In GitLab 13.2 and later, you can pause and resume replication with `gitlab-ctl geo-replication-pause` and `gitlab-ctl geo-replication-resume` on a Geo secondary database node. | ||||||
| 1. Follow the [instructions to migrate repmgr to Patroni](../../postgresql/replication_and_failover.md#switching-from-repmgr-to-patroni). When configuring Patroni on each primary site database node, add `patroni['replication_slots'] = { '<slot_name>' => 'physical' }` | 1. Follow the [instructions to migrate repmgr to Patroni](../../postgresql/replication_and_failover.md#switching-from-repmgr-to-patroni). When configuring Patroni on each primary site database node, add `patroni['replication_slots'] = { '<slot_name>' => 'physical' }` | ||||||
| to `gitlab.rb` where `<slot_name>` is the name of the replication slot for your Geo secondary. This ensures that Patroni recognizes the replication slot as permanent and not drop it upon restarting. | to `gitlab.rb` where `<slot_name>` is the name of the replication slot for your Geo secondary. This ensures that Patroni recognizes the replication slot as permanent and not drop it upon restarting. | ||||||
| 1. If database replication to the secondary was paused before migration, resume replication once Patroni is confirmed working on the primary. | 1. If database replication to the secondary was paused before migration, resume replication once Patroni is confirmed working on the primary. | ||||||
|  | @ -869,7 +869,7 @@ to `gitlab.rb` where `<slot_name>` is the name of the replication slot for your | ||||||
| 
 | 
 | ||||||
| Before the introduction of Patroni, Geo had no Omnibus support for HA setups on the secondary node. | Before the introduction of Patroni, Geo had no Omnibus support for HA setups on the secondary node. | ||||||
| 
 | 
 | ||||||
| With Patroni it's now possible to support that. In order to migrate the existing PostgreSQL to Patroni: | With Patroni it's now possible to support that. To migrate the existing PostgreSQL to Patroni: | ||||||
| 
 | 
 | ||||||
| 1. Make sure you have a Consul cluster setup on the secondary (similar to how you set it up on the primary). | 1. Make sure you have a Consul cluster setup on the secondary (similar to how you set it up on the primary). | ||||||
| 1. [Configure a permanent replication slot](#step-1-configure-patroni-permanent-replication-slot-on-the-primary-site). | 1. [Configure a permanent replication slot](#step-1-configure-patroni-permanent-replication-slot-on-the-primary-site). | ||||||
|  | @ -894,7 +894,7 @@ A production-ready and secure setup requires at least three Consul nodes, two | ||||||
| Patroni nodes and one PgBouncer node on the secondary site. | Patroni nodes and one PgBouncer node on the secondary site. | ||||||
| 
 | 
 | ||||||
| Because of [omnibus-6587](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6587), Consul can't track multiple | Because of [omnibus-6587](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6587), Consul can't track multiple | ||||||
| services, so these need to be different than the nodes used for the Standby Cluster database. | services, so these must be different than the nodes used for the Standby Cluster database. | ||||||
| 
 | 
 | ||||||
| Be sure to use [password credentials](../../postgresql/replication_and_failover.md#database-authorization-for-patroni) | Be sure to use [password credentials](../../postgresql/replication_and_failover.md#database-authorization-for-patroni) | ||||||
| and other database best practices. | and other database best practices. | ||||||
|  | @ -1037,7 +1037,7 @@ For each node running the `gitlab-rails`, `sidekiq`, and `geo-logcursor` service | ||||||
|    sudo -i |    sudo -i | ||||||
|    ``` |    ``` | ||||||
| 
 | 
 | ||||||
| 1. Edit `/etc/gitlab/gitlab.rb` and add the following attributes. You may have other attributes set, but the following need to be set. | 1. Edit `/etc/gitlab/gitlab.rb` and add the following attributes. You may have other attributes set, but the following must be set. | ||||||
| 
 | 
 | ||||||
|    ```ruby |    ```ruby | ||||||
|    # Tracking database settings |    # Tracking database settings | ||||||
|  |  | ||||||
|  | @ -182,7 +182,7 @@ file for the environment, since it isn't generated dynamically. | ||||||
| ## Troubleshooting | ## Troubleshooting | ||||||
| 
 | 
 | ||||||
| If your SSH traffic is [slow](https://github.com/linux-pam/linux-pam/issues/270) | If your SSH traffic is [slow](https://github.com/linux-pam/linux-pam/issues/270) | ||||||
| or causing high CPU load, be sure to check the size of `/var/log/btmp`, and ensure it is rotated on a regular basis. | or causing high CPU load, be sure to check the size of `/var/log/btmp`, and ensure it is rotated on a regular basis or after reaching a certain size. | ||||||
| If this file is very large, GitLab SSH fast lookup can cause the bottleneck to be hit more frequently, thus decreasing performance even further. | If this file is very large, GitLab SSH fast lookup can cause the bottleneck to be hit more frequently, thus decreasing performance even further. | ||||||
| If you are able to, you may consider disabling [`UsePAM` in your `sshd_config`](https://linux.die.net/man/5/sshd_config) to avoid reading `/var/log/btmp` altogether. | If you are able to, you may consider disabling [`UsePAM` in your `sshd_config`](https://linux.die.net/man/5/sshd_config) to avoid reading `/var/log/btmp` altogether. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -174,15 +174,15 @@ Work required to achieve our next CI/CD scaling target is tracked in the | ||||||
| 
 | 
 | ||||||
| 1. ✓ Migrate primary keys to big integers on GitLab.com. | 1. ✓ Migrate primary keys to big integers on GitLab.com. | ||||||
| 1. ✓ Implement the new architecture of builds queuing on GitLab.com. | 1. ✓ Implement the new architecture of builds queuing on GitLab.com. | ||||||
| 1. Make the new builds queuing architecture generally available. | 1. [Make the new builds queuing architecture generally available](https://gitlab.com/groups/gitlab-org/-/epics/6954). | ||||||
| 1. Partition CI/CD data using time-decay pattern. | 1. [Partition CI/CD data using time-decay pattern](../ci_data_decay/). | ||||||
| 
 | 
 | ||||||
| ## Status | ## Status | ||||||
| 
 | 
 | ||||||
| |-------------|--------------| | |-------------|--------------| | ||||||
| | Created at  | 21.01.2021   | | | Created at  | 21.01.2021   | | ||||||
| | Approved at | 26.04.2021   | | | Approved at | 26.04.2021   | | ||||||
| | Updated at  | 06.12.2021   | | | Updated at  | 28.02.2022   | | ||||||
| 
 | 
 | ||||||
| Status: In progress. | Status: In progress. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 45 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 17 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 13 KiB | 
|  | @ -62,7 +62,8 @@ You can configure a webhook for a group or a project. | ||||||
| 
 | 
 | ||||||
| ## Test a webhook | ## Test a webhook | ||||||
| 
 | 
 | ||||||
| You can trigger a webhook manually, to ensure it's working properly. | You can trigger a webhook manually, to ensure it's working properly. You can also send | ||||||
|  | a test request to re-enable a [disabled webhook](#re-enable-disabled-webhooks). | ||||||
| 
 | 
 | ||||||
| For example, to test `push events`, your project should have at least one commit. The webhook uses this commit in the webhook. | For example, to test `push events`, your project should have at least one commit. The webhook uses this commit in the webhook. | ||||||
| 
 | 
 | ||||||
|  | @ -72,6 +73,8 @@ To test a webhook: | ||||||
| 1. Scroll down to the list of configured webhooks. | 1. Scroll down to the list of configured webhooks. | ||||||
| 1. From the **Test** dropdown list, select the type of event to test. | 1. From the **Test** dropdown list, select the type of event to test. | ||||||
| 
 | 
 | ||||||
|  | You can also test a webhook from its edit page. | ||||||
|  | 
 | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
| ## Create an example webhook receiver | ## Create an example webhook receiver | ||||||
|  | @ -143,7 +146,32 @@ GitLab webhooks, keep in mind the following: | ||||||
|   GitLab assumes the hook failed and retries it. |   GitLab assumes the hook failed and retries it. | ||||||
|   Most HTTP libraries take care of the response for you automatically but if |   Most HTTP libraries take care of the response for you automatically but if | ||||||
|   you are writing a low-level hook, this is important to remember. |   you are writing a low-level hook, this is important to remember. | ||||||
| - GitLab ignores the HTTP status code returned by your endpoint. | - GitLab usually ignores the HTTP status code returned by your endpoint, | ||||||
|  |   unless the `web_hooks_disable_failed` feature flag is set. | ||||||
|  | 
 | ||||||
|  | ### Failing webhooks | ||||||
|  | 
 | ||||||
|  | > - Introduced in GitLab 13.12 [with a flag](../../../administration/feature_flags.md) named `web_hooks_disable_failed`. Disabled by default. | ||||||
|  | > - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/329849) in GitLab 14.9. | ||||||
|  | 
 | ||||||
|  | FLAG: | ||||||
|  | On self-managed GitLab, by default this feature is not available. To make it available, | ||||||
|  | ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `web_hooks_disable_failed`. | ||||||
|  | The feature is not ready for production use. | ||||||
|  | 
 | ||||||
|  | If a webhook fails repeatedly, it may be disabled automatically. | ||||||
|  | 
 | ||||||
|  | Webhooks that return response codes in the `5xx` range are understood to be failing | ||||||
|  | intermittently, and are temporarily disabled. This lasts initially | ||||||
|  | for 10 minutes. If the hook continues to fail, the back-off period is | ||||||
|  | extended on each retry, up to a maximum disabled period of 24 hours. | ||||||
|  | 
 | ||||||
|  | Webhooks that return failure codes in the `4xx` range are understood to be | ||||||
|  | misconfigured, and these are disabled until you manually re-enable | ||||||
|  | them. These webhooks are not automatically retried. | ||||||
|  | 
 | ||||||
|  | See [troubleshooting](#troubleshoot-webhooks) for information on | ||||||
|  | how to see if a webhook is disabled, and how to re-enable it. | ||||||
| 
 | 
 | ||||||
| ## How image URLs are displayed in the webhook body | ## How image URLs are displayed in the webhook body | ||||||
| 
 | 
 | ||||||
|  | @ -186,6 +214,13 @@ To view the table: | ||||||
| 
 | 
 | ||||||
| 1. In your project, on the left sidebar, select **Settings > Webhooks**. | 1. In your project, on the left sidebar, select **Settings > Webhooks**. | ||||||
| 1. Scroll down to the webhooks. | 1. Scroll down to the webhooks. | ||||||
|  | 1. Each [failing webhook](#failing-webhooks) has a badge listing it as: | ||||||
|  | 
 | ||||||
|  |    - **Failed to connect** if it is misconfigured, and needs manual intervention to re-enable it. | ||||||
|  |    - **Fails to connect** if it is temporarily disabled and will retry later. | ||||||
|  |    | ||||||
|  |     | ||||||
|  |     | ||||||
| 1. Select **Edit** for the webhook you want to view. | 1. Select **Edit** for the webhook you want to view. | ||||||
| 
 | 
 | ||||||
| The table includes the following details about each request: | The table includes the following details about each request: | ||||||
|  | @ -233,3 +268,17 @@ determined by [CAcert.org](http://www.cacert.org/). | ||||||
| 
 | 
 | ||||||
| If that is not the case, consider using [SSL Checker](https://www.sslshopper.com/ssl-checker.html) to identify faults. | If that is not the case, consider using [SSL Checker](https://www.sslshopper.com/ssl-checker.html) to identify faults. | ||||||
| Missing intermediate certificates are common causes of verification failure. | Missing intermediate certificates are common causes of verification failure. | ||||||
|  | 
 | ||||||
|  | ### Re-enable disabled webhooks | ||||||
|  | 
 | ||||||
|  | If a webhook is failing, a banner displays at the top of the edit page explaining | ||||||
|  | why it is disabled, and when it will be automatically re-enabled. For example: | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | In the case of a failed webhook, an error banner is displayed: | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | To re-enable a failing or failed webhook, [send a test request](#test-a-webhook). If the test | ||||||
|  | request succeeds, the webhook is re-enabled. | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ type: reference | ||||||
| 
 | 
 | ||||||
| # GitLab Advanced Search **(PREMIUM)** | # GitLab Advanced Search **(PREMIUM)** | ||||||
| 
 | 
 | ||||||
| > - Moved to GitLab Premium in 13.9. | > Moved to GitLab Premium in 13.9. | ||||||
| 
 | 
 | ||||||
| NOTE: | NOTE: | ||||||
| This is the user documentation. To configure the Advanced Search, | This is the user documentation. To configure the Advanced Search, | ||||||
|  | @ -147,8 +147,8 @@ its performance: | ||||||
| 
 | 
 | ||||||
| FLAG: | FLAG: | ||||||
| On self-managed GitLab, by default this feature is not available. To make it available, | On self-managed GitLab, by default this feature is not available. To make it available, | ||||||
|    ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `prevent_abusive_searches`. | ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `prevent_abusive_searches`. | ||||||
|    The feature is not ready for production use. | The feature is not ready for production use. | ||||||
| 
 | 
 | ||||||
| To prevent abusive searches, such as searches that may result in a Distributed Denial of Service (DDoS), Global Search ignores, logs, and | To prevent abusive searches, such as searches that may result in a Distributed Denial of Service (DDoS), Global Search ignores, logs, and | ||||||
| doesn't return any results for searches considered abusive according to the following criteria, if `prevent_abusive_searches` feature flag is enabled: | doesn't return any results for searches considered abusive according to the following criteria, if `prevent_abusive_searches` feature flag is enabled: | ||||||
|  |  | ||||||
|  | @ -516,6 +516,14 @@ RSpec.describe 'Filter issues', :js do | ||||||
|         expect_no_issues_list |         expect_no_issues_list | ||||||
|         expect_filtered_search_input(search) |         expect_filtered_search_input(search) | ||||||
|       end |       end | ||||||
|  | 
 | ||||||
|  |       it 'filters issues by issue reference' do | ||||||
|  |         search = '#1' | ||||||
|  |         input_filtered_search(search) | ||||||
|  | 
 | ||||||
|  |         expect_issues_list_count(1) | ||||||
|  |         expect_filtered_search_input(search) | ||||||
|  |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'searched text with other filters' do |     context 'searched text with other filters' do | ||||||
|  |  | ||||||
|  | @ -36,7 +36,7 @@ describe('IDE file templates bar component', () => { | ||||||
|     it('calls setSelectedTemplateType when clicking item', () => { |     it('calls setSelectedTemplateType when clicking item', () => { | ||||||
|       jest.spyOn(vm, 'setSelectedTemplateType').mockImplementation(); |       jest.spyOn(vm, 'setSelectedTemplateType').mockImplementation(); | ||||||
| 
 | 
 | ||||||
|       vm.$el.querySelector('.dropdown-content button').click(); |       vm.$el.querySelector('.dropdown-menu button').click(); | ||||||
| 
 | 
 | ||||||
|       expect(vm.setSelectedTemplateType).toHaveBeenCalledWith({ |       expect(vm.setSelectedTemplateType).toHaveBeenCalledWith({ | ||||||
|         name: '.gitlab-ci.yml', |         name: '.gitlab-ci.yml', | ||||||
|  | @ -64,10 +64,10 @@ describe('IDE file templates bar component', () => { | ||||||
|       expect(vm.$el.querySelectorAll('.dropdown')[1].textContent).toContain('Choose a template'); |       expect(vm.$el.querySelectorAll('.dropdown')[1].textContent).toContain('Choose a template'); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('calls fetchTemplate on click', () => { |     it('calls fetchTemplate on dropdown open', () => { | ||||||
|       jest.spyOn(vm, 'fetchTemplate').mockImplementation(); |       jest.spyOn(vm, 'fetchTemplate').mockImplementation(); | ||||||
| 
 | 
 | ||||||
|       vm.$el.querySelectorAll('.dropdown-content')[1].querySelector('button').click(); |       vm.$el.querySelectorAll('.dropdown-menu')[1].querySelector('button').click(); | ||||||
| 
 | 
 | ||||||
|       expect(vm.fetchTemplate).toHaveBeenCalledWith({ |       expect(vm.fetchTemplate).toHaveBeenCalledWith({ | ||||||
|         name: 'test', |         name: 'test', | ||||||
|  | @ -85,7 +85,7 @@ describe('IDE file templates bar component', () => { | ||||||
|   it('calls undoFileTemplate when clicking undo button', () => { |   it('calls undoFileTemplate when clicking undo button', () => { | ||||||
|     jest.spyOn(vm, 'undoFileTemplate').mockImplementation(); |     jest.spyOn(vm, 'undoFileTemplate').mockImplementation(); | ||||||
| 
 | 
 | ||||||
|     vm.$el.querySelector('.btn-default').click(); |     vm.$el.querySelector('.btn-default-secondary').click(); | ||||||
| 
 | 
 | ||||||
|     expect(vm.undoFileTemplate).toHaveBeenCalled(); |     expect(vm.undoFileTemplate).toHaveBeenCalled(); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  | @ -21,8 +21,6 @@ RSpec.describe IssuableLink do | ||||||
|   describe '.inverse_link_type' do |   describe '.inverse_link_type' do | ||||||
|     it 'returns the inverse type of link' do |     it 'returns the inverse type of link' do | ||||||
|       expect(test_class.inverse_link_type('relates_to')).to eq('relates_to') |       expect(test_class.inverse_link_type('relates_to')).to eq('relates_to') | ||||||
|       expect(test_class.inverse_link_type('is_blocked_by')).to eq('is_blocked_by') |  | ||||||
|       expect(test_class.inverse_link_type('blocks')).to eq('blocks') |  | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,6 +10,10 @@ RSpec.describe IssueLink do | ||||||
|     let(:issuable_link_factory) { :issue_link } |     let(:issuable_link_factory) { :issue_link } | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |   describe '.issuable_type' do | ||||||
|  |     it { expect(described_class.issuable_type).to eq(:issue) } | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   describe 'Scopes' do |   describe 'Scopes' do | ||||||
|     let_it_be(:issue1) { create(:issue) } |     let_it_be(:issue1) { create(:issue) } | ||||||
|     let_it_be(:issue2) { create(:issue) } |     let_it_be(:issue2) { create(:issue) } | ||||||
|  |  | ||||||
|  | @ -217,6 +217,7 @@ RSpec.describe Tooling::Danger::ProjectHelper do | ||||||
|       'ee/lib/ee/gitlab/integrations/sti_type.rb' | [:integrations_be, :backend] |       'ee/lib/ee/gitlab/integrations/sti_type.rb' | [:integrations_be, :backend] | ||||||
|       'ee/lib/ee/api/helpers/integrations_helpers.rb' | [:integrations_be, :backend] |       'ee/lib/ee/api/helpers/integrations_helpers.rb' | [:integrations_be, :backend] | ||||||
|       'ee/app/serializers/integrations/jira_serializers/issue_entity.rb' | [:integrations_be, :backend] |       'ee/app/serializers/integrations/jira_serializers/issue_entity.rb' | [:integrations_be, :backend] | ||||||
|  |       'app/serializers/jira_connect/app_data_serializer.rb' | [:integrations_be, :backend] | ||||||
|       'lib/api/github/entities.rb' | [:integrations_be, :backend] |       'lib/api/github/entities.rb' | [:integrations_be, :backend] | ||||||
|       'lib/api/v3/github.rb' | [:integrations_be, :backend] |       'lib/api/v3/github.rb' | [:integrations_be, :backend] | ||||||
|       'app/models/clusters/integrations/elastic_stack.rb' | [:backend] |       'app/models/clusters/integrations/elastic_stack.rb' | [:backend] | ||||||
|  |  | ||||||
|  | @ -53,7 +53,7 @@ module Tooling | ||||||
|         %r{\A( |         %r{\A( | ||||||
|           ((ee|jh)/)?app/((?!.*clusters)(?!.*alert_management)(?!.*views)(?!.*assets).+/)?integration.+ | |           ((ee|jh)/)?app/((?!.*clusters)(?!.*alert_management)(?!.*views)(?!.*assets).+/)?integration.+ | | ||||||
|           ((ee|jh)/)?app/((?!.*search).+/)?project_service.+ | |           ((ee|jh)/)?app/((?!.*search).+/)?project_service.+ | | ||||||
|           ((ee|jh)/)?app/(models|helpers|workers|services|controllers)/(.+/)?(jira_connect.+|.*hook.+) | |           ((ee|jh)/)?app/(models|helpers|workers|services|serializers|controllers)/(.+/)?(jira_connect.+|.*hook.+) | | ||||||
|           ((ee|jh)/)?app/controllers/(.+/)?oauth/jira/.+ | |           ((ee|jh)/)?app/controllers/(.+/)?oauth/jira/.+ | | ||||||
|           ((ee|jh)/)?app/services/(.+/)?jira.+ | |           ((ee|jh)/)?app/services/(.+/)?jira.+ | | ||||||
|           ((ee|jh)/)?app/workers/(.+/)?(propagate_integration.+|irker_worker\.rb) | |           ((ee|jh)/)?app/workers/(.+/)?(propagate_integration.+|irker_worker\.rb) | | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue