Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									67d7b691b1
								
							
						
					
					
						commit
						22856b8780
					
				
							
								
								
									
										6
									
								
								Gemfile
								
								
								
								
							
							
						
						
									
										6
									
								
								Gemfile
								
								
								
								
							|  | @ -157,7 +157,7 @@ gem 'github-markup', '~> 1.7.0', require: 'github/markup' | ||||||
| gem 'commonmarker', '~> 0.21' | gem 'commonmarker', '~> 0.21' | ||||||
| gem 'kramdown', '~> 2.3.1' | gem 'kramdown', '~> 2.3.1' | ||||||
| gem 'RedCloth', '~> 4.3.2' | gem 'RedCloth', '~> 4.3.2' | ||||||
| gem 'gitlab-rdoc', '~> 6.3.2', require: 'rdoc' # We need this fork until rdoc releases a new version. See https://gitlab.com/gitlab-org/gitlab/-/issues/334695 | gem 'rdoc', '~> 6.3.2' | ||||||
| gem 'org-ruby', '~> 0.9.12' | gem 'org-ruby', '~> 0.9.12' | ||||||
| gem 'creole', '~> 0.5.0' | gem 'creole', '~> 0.5.0' | ||||||
| gem 'wikicloth', '0.8.1' | gem 'wikicloth', '0.8.1' | ||||||
|  | @ -197,7 +197,7 @@ gem 'acts-as-taggable-on', '~> 7.0' | ||||||
| # Background jobs | # Background jobs | ||||||
| gem 'sidekiq', '~> 5.2.7' | gem 'sidekiq', '~> 5.2.7' | ||||||
| gem 'sidekiq-cron', '~> 1.0' | gem 'sidekiq-cron', '~> 1.0' | ||||||
| gem 'redis-namespace', '~> 1.7.0' | gem 'redis-namespace', '~> 1.8.1' | ||||||
| gem 'gitlab-sidekiq-fetcher', '0.5.6', require: 'sidekiq-reliable-fetch' | gem 'gitlab-sidekiq-fetcher', '0.5.6', require: 'sidekiq-reliable-fetch' | ||||||
| 
 | 
 | ||||||
| # Cron Parser | # Cron Parser | ||||||
|  | @ -229,7 +229,7 @@ gem 'js_regex', '~> 3.4' | ||||||
| gem 'device_detector' | gem 'device_detector' | ||||||
| 
 | 
 | ||||||
| # Redis | # Redis | ||||||
| gem 'redis', '~> 4.0' | gem 'redis', '~> 4.1.4' | ||||||
| gem 'connection_pool', '~> 2.0' | gem 'connection_pool', '~> 2.0' | ||||||
| 
 | 
 | ||||||
| # Redis session store | # Redis session store | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								Gemfile.lock
								
								
								
								
							
							
						
						
									
										12
									
								
								Gemfile.lock
								
								
								
								
							|  | @ -499,7 +499,6 @@ GEM | ||||||
|       openid_connect (~> 1.2) |       openid_connect (~> 1.2) | ||||||
|     gitlab-pg_query (2.0.4) |     gitlab-pg_query (2.0.4) | ||||||
|       google-protobuf (>= 3.17.1) |       google-protobuf (>= 3.17.1) | ||||||
|     gitlab-rdoc (6.3.2) |  | ||||||
|     gitlab-sidekiq-fetcher (0.5.6) |     gitlab-sidekiq-fetcher (0.5.6) | ||||||
|       sidekiq (~> 5) |       sidekiq (~> 5) | ||||||
|     gitlab-styles (6.2.0) |     gitlab-styles (6.2.0) | ||||||
|  | @ -1026,11 +1025,12 @@ GEM | ||||||
|       msgpack (>= 0.4.3) |       msgpack (>= 0.4.3) | ||||||
|       optimist (>= 3.0.0) |       optimist (>= 3.0.0) | ||||||
|     rchardet (1.8.0) |     rchardet (1.8.0) | ||||||
|  |     rdoc (6.3.2) | ||||||
|     re2 (1.2.0) |     re2 (1.2.0) | ||||||
|     recaptcha (4.13.1) |     recaptcha (4.13.1) | ||||||
|       json |       json | ||||||
|     recursive-open-struct (1.1.2) |     recursive-open-struct (1.1.2) | ||||||
|     redis (4.1.3) |     redis (4.1.4) | ||||||
|     redis-actionpack (5.2.0) |     redis-actionpack (5.2.0) | ||||||
|       actionpack (>= 5, < 7) |       actionpack (>= 5, < 7) | ||||||
|       redis-rack (>= 2.1.0, < 3) |       redis-rack (>= 2.1.0, < 3) | ||||||
|  | @ -1038,7 +1038,7 @@ GEM | ||||||
|     redis-activesupport (5.2.0) |     redis-activesupport (5.2.0) | ||||||
|       activesupport (>= 3, < 7) |       activesupport (>= 3, < 7) | ||||||
|       redis-store (>= 1.3, < 2) |       redis-store (>= 1.3, < 2) | ||||||
|     redis-namespace (1.7.0) |     redis-namespace (1.8.1) | ||||||
|       redis (>= 3.0.4) |       redis (>= 3.0.4) | ||||||
|     redis-rack (2.1.2) |     redis-rack (2.1.2) | ||||||
|       rack (>= 2.0.8, < 3) |       rack (>= 2.0.8, < 3) | ||||||
|  | @ -1497,7 +1497,6 @@ DEPENDENCIES | ||||||
|   gitlab-markup (~> 1.7.1) |   gitlab-markup (~> 1.7.1) | ||||||
|   gitlab-net-dns (~> 0.9.1) |   gitlab-net-dns (~> 0.9.1) | ||||||
|   gitlab-omniauth-openid-connect (~> 0.4.0) |   gitlab-omniauth-openid-connect (~> 0.4.0) | ||||||
|   gitlab-rdoc (~> 6.3.2) |  | ||||||
|   gitlab-sidekiq-fetcher (= 0.5.6) |   gitlab-sidekiq-fetcher (= 0.5.6) | ||||||
|   gitlab-styles (~> 6.2.0) |   gitlab-styles (~> 6.2.0) | ||||||
|   gitlab_chronic_duration (~> 0.10.6.2) |   gitlab_chronic_duration (~> 0.10.6.2) | ||||||
|  | @ -1607,10 +1606,11 @@ DEPENDENCIES | ||||||
|   rainbow (~> 3.0) |   rainbow (~> 3.0) | ||||||
|   rblineprof (~> 0.3.6) |   rblineprof (~> 0.3.6) | ||||||
|   rbtrace (~> 0.4) |   rbtrace (~> 0.4) | ||||||
|  |   rdoc (~> 6.3.2) | ||||||
|   re2 (~> 1.2.0) |   re2 (~> 1.2.0) | ||||||
|   recaptcha (~> 4.11) |   recaptcha (~> 4.11) | ||||||
|   redis (~> 4.0) |   redis (~> 4.1.4) | ||||||
|   redis-namespace (~> 1.7.0) |   redis-namespace (~> 1.8.1) | ||||||
|   redis-rails (~> 5.0.2) |   redis-rails (~> 5.0.2) | ||||||
|   request_store (~> 1.5) |   request_store (~> 1.5) | ||||||
|   responders (~> 3.0) |   responders (~> 3.0) | ||||||
|  |  | ||||||
|  | @ -75,15 +75,6 @@ export default { | ||||||
|     validProjectKey() { |     validProjectKey() { | ||||||
|       return !this.enableJiraIssues || Boolean(this.projectKey) || !this.validated; |       return !this.enableJiraIssues || Boolean(this.projectKey) || !this.validated; | ||||||
|     }, |     }, | ||||||
|     showJiraVulnerabilitiesOptions() { |  | ||||||
|       return this.showJiraVulnerabilitiesIntegration; |  | ||||||
|     }, |  | ||||||
|     showUltimateUpgrade() { |  | ||||||
|       return this.showJiraIssuesIntegration && !this.showJiraVulnerabilitiesIntegration; |  | ||||||
|     }, |  | ||||||
|     showPremiumUpgrade() { |  | ||||||
|       return !this.showJiraIssuesIntegration; |  | ||||||
|     }, |  | ||||||
|   }, |   }, | ||||||
|   created() { |   created() { | ||||||
|     eventHub.$on('validateForm', this.validateForm); |     eventHub.$on('validateForm', this.validateForm); | ||||||
|  | @ -128,23 +119,30 @@ export default { | ||||||
|               }} |               }} | ||||||
|             </template> |             </template> | ||||||
|           </gl-form-checkbox> |           </gl-form-checkbox> | ||||||
|           <jira-issue-creation-vulnerabilities |           <template v-if="enableJiraIssues"> | ||||||
|             v-if="enableJiraIssues" |             <jira-issue-creation-vulnerabilities | ||||||
|             :project-key="projectKey" |               :project-key="projectKey" | ||||||
|             :initial-is-enabled="initialEnableJiraVulnerabilities" |               :initial-is-enabled="initialEnableJiraVulnerabilities" | ||||||
|             :initial-issue-type-id="initialVulnerabilitiesIssuetype" |               :initial-issue-type-id="initialVulnerabilitiesIssuetype" | ||||||
|             :show-full-feature="showJiraVulnerabilitiesOptions" |               :show-full-feature="showJiraVulnerabilitiesIntegration" | ||||||
|             data-testid="jira-for-vulnerabilities" |               data-testid="jira-for-vulnerabilities" | ||||||
|             @request-get-issue-types="getJiraIssueTypes" |               @request-get-issue-types="getJiraIssueTypes" | ||||||
|           /> |             /> | ||||||
|  |             <jira-upgrade-cta | ||||||
|  |               v-if="!showJiraVulnerabilitiesIntegration" | ||||||
|  |               class="gl-mt-2 gl-ml-6" | ||||||
|  |               data-testid="ultimate-upgrade-cta" | ||||||
|  |               show-ultimate-message | ||||||
|  |               :upgrade-plan-path="upgradePlanPath" | ||||||
|  |             /> | ||||||
|  |           </template> | ||||||
|         </template> |         </template> | ||||||
|         <jira-upgrade-cta |         <jira-upgrade-cta | ||||||
|           v-if="showUltimateUpgrade || showPremiumUpgrade" |           v-else | ||||||
|           class="gl-mt-2" |           class="gl-mt-2" | ||||||
|           :class="{ 'gl-ml-6': showUltimateUpgrade }" |           data-testid="premium-upgrade-cta" | ||||||
|  |           show-premium-message | ||||||
|           :upgrade-plan-path="upgradePlanPath" |           :upgrade-plan-path="upgradePlanPath" | ||||||
|           :show-ultimate-message="showUltimateUpgrade" |  | ||||||
|           :show-premium-message="showPremiumUpgrade" |  | ||||||
|         /> |         /> | ||||||
|       </div> |       </div> | ||||||
|     </gl-form-group> |     </gl-form-group> | ||||||
|  |  | ||||||
|  | @ -65,6 +65,9 @@ export default { | ||||||
|       isLoadingLegacyViewer: false, |       isLoadingLegacyViewer: false, | ||||||
|       activeViewerType: SIMPLE_BLOB_VIEWER, |       activeViewerType: SIMPLE_BLOB_VIEWER, | ||||||
|       project: { |       project: { | ||||||
|  |         userPermissions: { | ||||||
|  |           pushCode: false, | ||||||
|  |         }, | ||||||
|         repository: { |         repository: { | ||||||
|           blobs: { |           blobs: { | ||||||
|             nodes: [ |             nodes: [ | ||||||
|  | @ -86,7 +89,6 @@ export default { | ||||||
|                 canLock: false, |                 canLock: false, | ||||||
|                 isLocked: false, |                 isLocked: false, | ||||||
|                 lockLink: '', |                 lockLink: '', | ||||||
|                 canModifyBlob: true, |  | ||||||
|                 forkPath: '', |                 forkPath: '', | ||||||
|                 simpleViewer: {}, |                 simpleViewer: {}, | ||||||
|                 richViewer: null, |                 richViewer: null, | ||||||
|  | @ -168,7 +170,7 @@ export default { | ||||||
|             :path="path" |             :path="path" | ||||||
|             :name="blobInfo.name" |             :name="blobInfo.name" | ||||||
|             :replace-path="blobInfo.replacePath" |             :replace-path="blobInfo.replacePath" | ||||||
|             :can-push-code="blobInfo.canModifyBlob" |             :can-push-code="project.userPermissions.pushCode" | ||||||
|           /> |           /> | ||||||
|         </template> |         </template> | ||||||
|       </blob-header> |       </blob-header> | ||||||
|  |  | ||||||
|  | @ -1,5 +1,8 @@ | ||||||
| query getBlobInfo($projectPath: ID!, $filePath: String!) { | query getBlobInfo($projectPath: ID!, $filePath: String!) { | ||||||
|   project(fullPath: $projectPath) { |   project(fullPath: $projectPath) { | ||||||
|  |     userPermissions { | ||||||
|  |       pushCode | ||||||
|  |     } | ||||||
|     repository { |     repository { | ||||||
|       blobs(paths: [$filePath]) { |       blobs(paths: [$filePath]) { | ||||||
|         nodes { |         nodes { | ||||||
|  | @ -15,7 +18,6 @@ query getBlobInfo($projectPath: ID!, $filePath: String!) { | ||||||
|           storedExternally |           storedExternally | ||||||
|           rawPath |           rawPath | ||||||
|           replacePath |           replacePath | ||||||
|           canModifyBlob |  | ||||||
|           simpleViewer { |           simpleViewer { | ||||||
|             fileType |             fileType | ||||||
|             tooLarge |             tooLarge | ||||||
|  |  | ||||||
|  | @ -56,24 +56,19 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .flash-alert { |   .flash-alert { | ||||||
|     background-color: $red-100; |     background-color: $red-50; | ||||||
|     color: $red-700; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .flash-notice { |   .flash-notice { | ||||||
|     background-color: $blue-100; |     background-color: $blue-50; | ||||||
|     color: $blue-700; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .flash-success { |   .flash-success { | ||||||
|     background-color: $theme-green-100; |     background-color: $green-50; | ||||||
|     color: $green-700; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .flash-warning { |   .flash-warning { | ||||||
|     background-color: $orange-50; |     background-color: $orange-50; | ||||||
|     color: $gray-900; |  | ||||||
|     cursor: default; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .flash-text, |   .flash-text, | ||||||
|  |  | ||||||
|  | @ -84,11 +84,7 @@ module Repositories | ||||||
| 
 | 
 | ||||||
|       return if Feature.enabled?(:disable_git_http_fetch_writes) |       return if Feature.enabled?(:disable_git_http_fetch_writes) | ||||||
| 
 | 
 | ||||||
|       if Feature.enabled?(:project_statistics_sync, project, default_enabled: true) |       Projects::FetchStatisticsIncrementService.new(project).execute | ||||||
|         Projects::FetchStatisticsIncrementService.new(project).execute |  | ||||||
|       else |  | ||||||
|         ProjectDailyStatisticsWorker.perform_async(project.id) # rubocop:disable CodeReuse/Worker |  | ||||||
|       end |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def access |     def access | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ module Ci | ||||||
|       secret_detection: 'gl-secret-detection-report.json', |       secret_detection: 'gl-secret-detection-report.json', | ||||||
|       dependency_scanning: 'gl-dependency-scanning-report.json', |       dependency_scanning: 'gl-dependency-scanning-report.json', | ||||||
|       container_scanning: 'gl-container-scanning-report.json', |       container_scanning: 'gl-container-scanning-report.json', | ||||||
|  |       cluster_image_scanning: 'gl-cluster-image-scanning-report.json', | ||||||
|       dast: 'gl-dast-report.json', |       dast: 'gl-dast-report.json', | ||||||
|       license_scanning: 'gl-license-scanning-report.json', |       license_scanning: 'gl-license-scanning-report.json', | ||||||
|       performance: 'performance.json', |       performance: 'performance.json', | ||||||
|  | @ -71,6 +72,7 @@ module Ci | ||||||
|       secret_detection: :raw, |       secret_detection: :raw, | ||||||
|       dependency_scanning: :raw, |       dependency_scanning: :raw, | ||||||
|       container_scanning: :raw, |       container_scanning: :raw, | ||||||
|  |       cluster_image_scanning: :raw, | ||||||
|       dast: :raw, |       dast: :raw, | ||||||
|       license_scanning: :raw, |       license_scanning: :raw, | ||||||
| 
 | 
 | ||||||
|  | @ -108,6 +110,7 @@ module Ci | ||||||
|       sast |       sast | ||||||
|       secret_detection |       secret_detection | ||||||
|       requirements |       requirements | ||||||
|  |       cluster_image_scanning | ||||||
|     ].freeze |     ].freeze | ||||||
| 
 | 
 | ||||||
|     TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze |     TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze | ||||||
|  | @ -212,7 +215,8 @@ module Ci | ||||||
|       coverage_fuzzing: 23, ## EE-specific |       coverage_fuzzing: 23, ## EE-specific | ||||||
|       browser_performance: 24, ## EE-specific |       browser_performance: 24, ## EE-specific | ||||||
|       load_performance: 25, ## EE-specific |       load_performance: 25, ## EE-specific | ||||||
|       api_fuzzing: 26 ## EE-specific |       api_fuzzing: 26, ## EE-specific | ||||||
|  |       cluster_image_scanning: 27 ## EE-specific | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     # `file_location` indicates where actual files are stored. |     # `file_location` indicates where actual files are stored. | ||||||
|  |  | ||||||
|  | @ -31,7 +31,8 @@ class UserCallout < ApplicationRecord | ||||||
|     pipeline_needs_banner: 29, |     pipeline_needs_banner: 29, | ||||||
|     pipeline_needs_hover_tip: 30, |     pipeline_needs_hover_tip: 30, | ||||||
|     web_ide_ci_environments_guidance: 31, |     web_ide_ci_environments_guidance: 31, | ||||||
|     security_configuration_upgrade_banner: 32 |     security_configuration_upgrade_banner: 32, | ||||||
|  |     cloud_licensing_subscription_activation_banner: 33  # EE-only | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   validates :user, presence: true |   validates :user, presence: true | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
|   %head |   %head | ||||||
|     %meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" } |     %meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" } | ||||||
|     %meta{ content: "width=device-width, initial-scale=1", name: "viewport" } |     %meta{ content: "width=device-width, initial-scale=1", name: "viewport" } | ||||||
|     %link{ href: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600", rel: "stylesheet", type: "text/css" } |     %link{ href: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600", rel: "stylesheet", type: "text/css", data: { premailer: 'ignore' } } | ||||||
|     %title= message.subject |     %title= message.subject | ||||||
|     :css |     :css | ||||||
|       /* CLIENT-SPECIFIC STYLES */ |       /* CLIENT-SPECIFIC STYLES */ | ||||||
|  |  | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| --- |  | ||||||
| name: project_statistics_sync |  | ||||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29636 |  | ||||||
| rollout_issue_url:  |  | ||||||
| milestone: '12.10' |  | ||||||
| type: development |  | ||||||
| group: group::source code |  | ||||||
| default_enabled: true |  | ||||||
|  | @ -9237,6 +9237,7 @@ four standard [pagination arguments](#connection-pagination-arguments): | ||||||
| | <a id="groupcustomemoji"></a>`customEmoji` | [`CustomEmojiConnection`](#customemojiconnection) | Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled. (see [Connections](#connections)) | | | <a id="groupcustomemoji"></a>`customEmoji` | [`CustomEmojiConnection`](#customemojiconnection) | Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled. (see [Connections](#connections)) | | ||||||
| | <a id="groupdescription"></a>`description` | [`String`](#string) | Description of the namespace. | | | <a id="groupdescription"></a>`description` | [`String`](#string) | Description of the namespace. | | ||||||
| | <a id="groupdescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | | <a id="groupdescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | ||||||
|  | | <a id="groupdora"></a>`dora` | [`Dora`](#dora) | The group's DORA metrics. | | ||||||
| | <a id="groupemailsdisabled"></a>`emailsDisabled` | [`Boolean`](#boolean) | Indicates if a group has email notifications disabled. | | | <a id="groupemailsdisabled"></a>`emailsDisabled` | [`Boolean`](#boolean) | Indicates if a group has email notifications disabled. | | ||||||
| | <a id="groupepicboards"></a>`epicBoards` | [`EpicBoardConnection`](#epicboardconnection) | Find epic boards. (see [Connections](#connections)) | | | <a id="groupepicboards"></a>`epicBoards` | [`EpicBoardConnection`](#epicboardconnection) | Find epic boards. (see [Connections](#connections)) | | ||||||
| | <a id="groupepicsenabled"></a>`epicsEnabled` | [`Boolean`](#boolean) | Indicates if Epics are enabled for namespace. | | | <a id="groupepicsenabled"></a>`epicsEnabled` | [`Boolean`](#boolean) | Indicates if Epics are enabled for namespace. | | ||||||
|  | @ -14681,6 +14682,7 @@ Iteration ID wildcard values. | ||||||
| | <a id="jobartifactfiletypearchive"></a>`ARCHIVE` | ARCHIVE job artifact file type. | | | <a id="jobartifactfiletypearchive"></a>`ARCHIVE` | ARCHIVE job artifact file type. | | ||||||
| | <a id="jobartifactfiletypebrowser_performance"></a>`BROWSER_PERFORMANCE` | BROWSER PERFORMANCE job artifact file type. | | | <a id="jobartifactfiletypebrowser_performance"></a>`BROWSER_PERFORMANCE` | BROWSER PERFORMANCE job artifact file type. | | ||||||
| | <a id="jobartifactfiletypecluster_applications"></a>`CLUSTER_APPLICATIONS` | CLUSTER APPLICATIONS job artifact file type. | | | <a id="jobartifactfiletypecluster_applications"></a>`CLUSTER_APPLICATIONS` | CLUSTER APPLICATIONS job artifact file type. | | ||||||
|  | | <a id="jobartifactfiletypecluster_image_scanning"></a>`CLUSTER_IMAGE_SCANNING` | CLUSTER IMAGE SCANNING job artifact file type. | | ||||||
| | <a id="jobartifactfiletypecobertura"></a>`COBERTURA` | COBERTURA job artifact file type. | | | <a id="jobartifactfiletypecobertura"></a>`COBERTURA` | COBERTURA job artifact file type. | | ||||||
| | <a id="jobartifactfiletypecodequality"></a>`CODEQUALITY` | CODE QUALITY job artifact file type. | | | <a id="jobartifactfiletypecodequality"></a>`CODEQUALITY` | CODE QUALITY job artifact file type. | | ||||||
| | <a id="jobartifactfiletypecontainer_scanning"></a>`CONTAINER_SCANNING` | CONTAINER SCANNING job artifact file type. | | | <a id="jobartifactfiletypecontainer_scanning"></a>`CONTAINER_SCANNING` | CONTAINER SCANNING job artifact file type. | | ||||||
|  | @ -15210,6 +15212,7 @@ Name of the feature that the callout is for. | ||||||
| | <a id="usercalloutfeaturenameenumactive_user_count_threshold"></a>`ACTIVE_USER_COUNT_THRESHOLD` | Callout feature name for active_user_count_threshold. | | | <a id="usercalloutfeaturenameenumactive_user_count_threshold"></a>`ACTIVE_USER_COUNT_THRESHOLD` | Callout feature name for active_user_count_threshold. | | ||||||
| | <a id="usercalloutfeaturenameenumbuy_pipeline_minutes_notification_dot"></a>`BUY_PIPELINE_MINUTES_NOTIFICATION_DOT` | Callout feature name for buy_pipeline_minutes_notification_dot. | | | <a id="usercalloutfeaturenameenumbuy_pipeline_minutes_notification_dot"></a>`BUY_PIPELINE_MINUTES_NOTIFICATION_DOT` | Callout feature name for buy_pipeline_minutes_notification_dot. | | ||||||
| | <a id="usercalloutfeaturenameenumcanary_deployment"></a>`CANARY_DEPLOYMENT` | Callout feature name for canary_deployment. | | | <a id="usercalloutfeaturenameenumcanary_deployment"></a>`CANARY_DEPLOYMENT` | Callout feature name for canary_deployment. | | ||||||
|  | | <a id="usercalloutfeaturenameenumcloud_licensing_subscription_activation_banner"></a>`CLOUD_LICENSING_SUBSCRIPTION_ACTIVATION_BANNER` | Callout feature name for cloud_licensing_subscription_activation_banner. | | ||||||
| | <a id="usercalloutfeaturenameenumcluster_security_warning"></a>`CLUSTER_SECURITY_WARNING` | Callout feature name for cluster_security_warning. | | | <a id="usercalloutfeaturenameenumcluster_security_warning"></a>`CLUSTER_SECURITY_WARNING` | Callout feature name for cluster_security_warning. | | ||||||
| | <a id="usercalloutfeaturenameenumcustomize_homepage"></a>`CUSTOMIZE_HOMEPAGE` | Callout feature name for customize_homepage. | | | <a id="usercalloutfeaturenameenumcustomize_homepage"></a>`CUSTOMIZE_HOMEPAGE` | Callout feature name for customize_homepage. | | ||||||
| | <a id="usercalloutfeaturenameenumeoa_bronze_plan_banner"></a>`EOA_BRONZE_PLAN_BANNER` | Callout feature name for eoa_bronze_plan_banner. | | | <a id="usercalloutfeaturenameenumeoa_bronze_plan_banner"></a>`EOA_BRONZE_PLAN_BANNER` | Callout feature name for eoa_bronze_plan_banner. | | ||||||
|  |  | ||||||
|  | @ -3064,6 +3064,18 @@ as artifacts. | ||||||
| The collected coverage fuzzing report uploads to GitLab as an artifact and is summarized in merge | The collected coverage fuzzing report uploads to GitLab as an artifact and is summarized in merge | ||||||
| requests and the pipeline view. It's also used to provide data for security dashboards. | requests and the pipeline view. It's also used to provide data for security dashboards. | ||||||
| 
 | 
 | ||||||
|  | ##### `artifacts:reports:cluster_image_scanning` **(ULTIMATE)** | ||||||
|  | 
 | ||||||
|  | > - Introduced in GitLab 14.1. | ||||||
|  | > - Requires GitLab Runner 14.1 and above. | ||||||
|  | 
 | ||||||
|  | The `cluster_image_scanning` report collects `CLUSTER_IMAGE_SCANNING` vulnerabilities | ||||||
|  | as artifacts. | ||||||
|  | 
 | ||||||
|  | The collected `CLUSTER_IMAGE_SCANNING` report uploads to GitLab as an artifact and | ||||||
|  | is summarized in the pipeline view. It's also used to provide data for security | ||||||
|  | dashboards. | ||||||
|  | 
 | ||||||
| ##### `artifacts:reports:dast` **(ULTIMATE)** | ##### `artifacts:reports:dast` **(ULTIMATE)** | ||||||
| 
 | 
 | ||||||
| > - Introduced in GitLab 11.5. | > - Introduced in GitLab 11.5. | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ module Gitlab | ||||||
|             %i[junit codequality sast secret_detection dependency_scanning container_scanning |             %i[junit codequality sast secret_detection dependency_scanning container_scanning | ||||||
|                dast performance browser_performance load_performance license_scanning metrics lsif |                dast performance browser_performance load_performance license_scanning metrics lsif | ||||||
|                dotenv cobertura terraform accessibility cluster_applications |                dotenv cobertura terraform accessibility cluster_applications | ||||||
|                requirements coverage_fuzzing api_fuzzing].freeze |                requirements coverage_fuzzing api_fuzzing cluster_image_scanning].freeze | ||||||
| 
 | 
 | ||||||
|           attributes ALLOWED_KEYS |           attributes ALLOWED_KEYS | ||||||
| 
 | 
 | ||||||
|  | @ -32,6 +32,7 @@ module Gitlab | ||||||
|               validates :secret_detection, array_of_strings_or_string: true |               validates :secret_detection, array_of_strings_or_string: true | ||||||
|               validates :dependency_scanning, array_of_strings_or_string: true |               validates :dependency_scanning, array_of_strings_or_string: true | ||||||
|               validates :container_scanning, array_of_strings_or_string: true |               validates :container_scanning, array_of_strings_or_string: true | ||||||
|  |               validates :cluster_image_scanning, array_of_strings_or_string: true | ||||||
|               validates :dast, array_of_strings_or_string: true |               validates :dast, array_of_strings_or_string: true | ||||||
|               validates :performance, array_of_strings_or_string: true |               validates :performance, array_of_strings_or_string: true | ||||||
|               validates :browser_performance, array_of_strings_or_string: true |               validates :browser_performance, array_of_strings_or_string: true | ||||||
|  |  | ||||||
|  | @ -34,18 +34,6 @@ RSpec.describe Repositories::GitHttpController do | ||||||
|           end |           end | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         context 'when project_statistics_sync feature flag is disabled' do |  | ||||||
|           before do |  | ||||||
|             stub_feature_flags(project_statistics_sync: false, disable_git_http_fetch_writes: false) |  | ||||||
|           end |  | ||||||
| 
 |  | ||||||
|           it 'updates project statistics async for projects' do |  | ||||||
|             expect(ProjectDailyStatisticsWorker).to receive(:perform_async) |  | ||||||
| 
 |  | ||||||
|             send_request |  | ||||||
|           end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         it 'updates project statistics sync for projects' do |         it 'updates project statistics sync for projects' do | ||||||
|           stub_feature_flags(disable_git_http_fetch_writes: false) |           stub_feature_flags(disable_git_http_fetch_writes: false) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -508,6 +508,14 @@ FactoryBot.define do | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |     trait :cluster_image_scanning do | ||||||
|  |       options do | ||||||
|  |         { | ||||||
|  |             artifacts: { reports: { cluster_image_scanning: 'gl-cluster-image-scanning-report.json' } } | ||||||
|  |         } | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|     trait :license_scanning do |     trait :license_scanning do | ||||||
|       options do |       options do | ||||||
|         { |         { | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ import { GlFormCheckbox, GlFormInput } from '@gitlab/ui'; | ||||||
| import { mountExtended } from 'helpers/vue_test_utils_helper'; | import { mountExtended } from 'helpers/vue_test_utils_helper'; | ||||||
| 
 | 
 | ||||||
| import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue'; | import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue'; | ||||||
| import JiraUpgradeCta from '~/integrations/edit/components/jira_upgrade_cta.vue'; |  | ||||||
| import eventHub from '~/integrations/edit/event_hub'; | import eventHub from '~/integrations/edit/event_hub'; | ||||||
| import { createStore } from '~/integrations/edit/store'; | import { createStore } from '~/integrations/edit/store'; | ||||||
| 
 | 
 | ||||||
|  | @ -14,6 +13,7 @@ describe('JiraIssuesFields', () => { | ||||||
|     editProjectPath: '/edit', |     editProjectPath: '/edit', | ||||||
|     showJiraIssuesIntegration: true, |     showJiraIssuesIntegration: true, | ||||||
|     showJiraVulnerabilitiesIntegration: true, |     showJiraVulnerabilitiesIntegration: true, | ||||||
|  |     upgradePlanPath: 'https://gitlab.com', | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   const createComponent = ({ isInheriting = false, props, ...options } = {}) => { |   const createComponent = ({ isInheriting = false, props, ...options } = {}) => { | ||||||
|  | @ -37,60 +37,79 @@ describe('JiraIssuesFields', () => { | ||||||
|   const findEnableCheckboxDisabled = () => |   const findEnableCheckboxDisabled = () => | ||||||
|     findEnableCheckbox().find('[type=checkbox]').attributes('disabled'); |     findEnableCheckbox().find('[type=checkbox]').attributes('disabled'); | ||||||
|   const findProjectKey = () => wrapper.findComponent(GlFormInput); |   const findProjectKey = () => wrapper.findComponent(GlFormInput); | ||||||
|   const findJiraUpgradeCta = () => wrapper.findComponent(JiraUpgradeCta); |   const findPremiumUpgradeCTA = () => wrapper.findByTestId('premium-upgrade-cta'); | ||||||
|  |   const findUltimateUpgradeCTA = () => wrapper.findByTestId('ultimate-upgrade-cta'); | ||||||
|   const findJiraForVulnerabilities = () => wrapper.findByTestId('jira-for-vulnerabilities'); |   const findJiraForVulnerabilities = () => wrapper.findByTestId('jira-for-vulnerabilities'); | ||||||
|   const setEnableCheckbox = async (isEnabled = true) => |   const setEnableCheckbox = async (isEnabled = true) => | ||||||
|     findEnableCheckbox().vm.$emit('input', isEnabled); |     findEnableCheckbox().vm.$emit('input', isEnabled); | ||||||
| 
 | 
 | ||||||
|   describe('jira issues call to action', () => { |  | ||||||
|     it('shows the premium message', () => { |  | ||||||
|       createComponent({ |  | ||||||
|         props: { showJiraIssuesIntegration: false }, |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       expect(findJiraUpgradeCta().props()).toMatchObject({ |  | ||||||
|         showPremiumMessage: true, |  | ||||||
|         showUltimateMessage: false, |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     it('shows the ultimate message', () => { |  | ||||||
|       createComponent({ |  | ||||||
|         props: { |  | ||||||
|           showJiraIssuesIntegration: true, |  | ||||||
|           showJiraVulnerabilitiesIntegration: false, |  | ||||||
|         }, |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       expect(findJiraUpgradeCta().props()).toMatchObject({ |  | ||||||
|         showPremiumMessage: false, |  | ||||||
|         showUltimateMessage: true, |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   describe('template', () => { |   describe('template', () => { | ||||||
|     describe('upgrade banner for non-Premium user', () => { |     describe.each` | ||||||
|       beforeEach(() => { |       showJiraIssuesIntegration | showJiraVulnerabilitiesIntegration | ||||||
|         createComponent({ props: { initialProjectKey: '', showJiraIssuesIntegration: false } }); |       ${false}                  | ${false} | ||||||
|       }); |       ${false}                  | ${true} | ||||||
|  |       ${true}                   | ${false} | ||||||
|  |       ${true}                   | ${true} | ||||||
|  |     `(
 | ||||||
|  |       'when `showJiraIssuesIntegration` is $jiraIssues and `showJiraVulnerabilitiesIntegration` is $jiraVulnerabilities', | ||||||
|  |       ({ showJiraIssuesIntegration, showJiraVulnerabilitiesIntegration }) => { | ||||||
|  |         beforeEach(() => { | ||||||
|  |           createComponent({ | ||||||
|  |             props: { | ||||||
|  |               showJiraIssuesIntegration, | ||||||
|  |               showJiraVulnerabilitiesIntegration, | ||||||
|  |             }, | ||||||
|  |           }); | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|       it('does not show checkbox and input field', () => { |         if (showJiraIssuesIntegration) { | ||||||
|         expect(findEnableCheckbox().exists()).toBe(false); |           it('renders checkbox and input field', () => { | ||||||
|         expect(findProjectKey().exists()).toBe(false); |             expect(findEnableCheckbox().exists()).toBe(true); | ||||||
|       }); |             expect(findEnableCheckboxDisabled()).toBeUndefined(); | ||||||
|     }); |             expect(findProjectKey().exists()).toBe(true); | ||||||
|  |           }); | ||||||
|  | 
 | ||||||
|  |           it('does not render the Premium CTA', () => { | ||||||
|  |             expect(findPremiumUpgradeCTA().exists()).toBe(false); | ||||||
|  |           }); | ||||||
|  | 
 | ||||||
|  |           if (!showJiraVulnerabilitiesIntegration) { | ||||||
|  |             it.each` | ||||||
|  |               scenario                                                                          | enableJiraIssues | ||||||
|  |               ${'when "Enable Jira issues" is checked, renders Ultimate upgrade CTA'}           | ${true} | ||||||
|  |               ${'when "Enable Jira issues" is unchecked, does not render Ultimate upgrade CTA'} | ${false} | ||||||
|  |             `('$scenario', async ({ enableJiraIssues }) => {
 | ||||||
|  |               if (enableJiraIssues) { | ||||||
|  |                 await setEnableCheckbox(); | ||||||
|  |               } | ||||||
|  |               expect(findUltimateUpgradeCTA().exists()).toBe(enableJiraIssues); | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         } else { | ||||||
|  |           it('does not render checkbox and input field', () => { | ||||||
|  |             expect(findEnableCheckbox().exists()).toBe(false); | ||||||
|  |             expect(findProjectKey().exists()).toBe(false); | ||||||
|  |           }); | ||||||
|  | 
 | ||||||
|  |           it('renders the Premium CTA', () => { | ||||||
|  |             const premiumUpgradeCTA = findPremiumUpgradeCTA(); | ||||||
|  | 
 | ||||||
|  |             expect(premiumUpgradeCTA.exists()).toBe(true); | ||||||
|  |             expect(premiumUpgradeCTA.props('upgradePlanPath')).toBe(defaultProps.upgradePlanPath); | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         it('does not render the Ultimate CTA', () => { | ||||||
|  |           expect(findUltimateUpgradeCTA().exists()).toBe(false); | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     describe('Enable Jira issues checkbox', () => { |     describe('Enable Jira issues checkbox', () => { | ||||||
|       beforeEach(() => { |       beforeEach(() => { | ||||||
|         createComponent({ props: { initialProjectKey: '' } }); |         createComponent({ props: { initialProjectKey: '' } }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       it('renders enabled checkbox', () => { |  | ||||||
|         expect(findEnableCheckbox().exists()).toBe(true); |  | ||||||
|         expect(findEnableCheckboxDisabled()).toBeUndefined(); |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       it('renders disabled project_key input', () => { |       it('renders disabled project_key input', () => { | ||||||
|         const projectKey = findProjectKey(); |         const projectKey = findProjectKey(); | ||||||
| 
 | 
 | ||||||
|  | @ -99,10 +118,6 @@ describe('JiraIssuesFields', () => { | ||||||
|         expect(projectKey.attributes('required')).toBeUndefined(); |         expect(projectKey.attributes('required')).toBeUndefined(); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       it('does not show upgrade banner', () => { |  | ||||||
|         expect(findJiraUpgradeCta().exists()).toBe(false); |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       // As per https://vuejs.org/v2/guide/forms.html#Checkbox-1,
 |       // As per https://vuejs.org/v2/guide/forms.html#Checkbox-1,
 | ||||||
|       // browsers don't include unchecked boxes in form submissions.
 |       // browsers don't include unchecked boxes in form submissions.
 | ||||||
|       it('includes issues_enabled as false even if unchecked', () => { |       it('includes issues_enabled as false even if unchecked', () => { | ||||||
|  |  | ||||||
|  | @ -37,7 +37,6 @@ const simpleMockData = { | ||||||
|   canLock: true, |   canLock: true, | ||||||
|   isLocked: false, |   isLocked: false, | ||||||
|   lockLink: 'some_file.js/lock', |   lockLink: 'some_file.js/lock', | ||||||
|   canModifyBlob: true, |  | ||||||
|   forkPath: 'some_file.js/fork', |   forkPath: 'some_file.js/fork', | ||||||
|   simpleViewer: { |   simpleViewer: { | ||||||
|     fileType: 'text', |     fileType: 'text', | ||||||
|  | @ -56,16 +55,26 @@ const richMockData = { | ||||||
|     renderError: null, |     renderError: null, | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|  | const userPermissionsMockData = { | ||||||
|  |   userPermissions: { | ||||||
|  |     pushCode: true, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| const localVue = createLocalVue(); | const localVue = createLocalVue(); | ||||||
| const mockAxios = new MockAdapter(axios); | const mockAxios = new MockAdapter(axios); | ||||||
| 
 | 
 | ||||||
| const createComponentWithApollo = (mockData) => { | const createComponentWithApollo = (mockData, mockPermissionData = true) => { | ||||||
|   localVue.use(VueApollo); |   localVue.use(VueApollo); | ||||||
| 
 | 
 | ||||||
|   const mockResolver = jest |   const mockResolver = jest.fn().mockResolvedValue({ | ||||||
|     .fn() |     data: { | ||||||
|     .mockResolvedValue({ data: { project: { repository: { blobs: { nodes: [mockData] } } } } }); |       project: { | ||||||
|  |         userPermissions: { pushCode: mockPermissionData }, | ||||||
|  |         repository: { blobs: { nodes: [mockData] } }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
| 
 | 
 | ||||||
|   const fakeApollo = createMockApollo([[blobInfoQuery, mockResolver]]); |   const fakeApollo = createMockApollo([[blobInfoQuery, mockResolver]]); | ||||||
| 
 | 
 | ||||||
|  | @ -276,13 +285,16 @@ describe('Blob content viewer component', () => { | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     describe('BlobButtonGroup', () => { |     describe('BlobButtonGroup', () => { | ||||||
|       const { name, path } = simpleMockData; |       const { name, path, replacePath } = simpleMockData; | ||||||
|  |       const { | ||||||
|  |         userPermissions: { pushCode }, | ||||||
|  |       } = userPermissionsMockData; | ||||||
| 
 | 
 | ||||||
|       it('renders component', async () => { |       it('renders component', async () => { | ||||||
|         window.gon.current_user_id = 1; |         window.gon.current_user_id = 1; | ||||||
| 
 | 
 | ||||||
|         fullFactory({ |         fullFactory({ | ||||||
|           mockData: { blobInfo: simpleMockData }, |           mockData: { blobInfo: simpleMockData, project: userPermissionsMockData }, | ||||||
|           stubs: { |           stubs: { | ||||||
|             BlobContent: true, |             BlobContent: true, | ||||||
|             BlobButtonGroup: true, |             BlobButtonGroup: true, | ||||||
|  | @ -294,6 +306,8 @@ describe('Blob content viewer component', () => { | ||||||
|         expect(findBlobButtonGroup().props()).toMatchObject({ |         expect(findBlobButtonGroup().props()).toMatchObject({ | ||||||
|           name, |           name, | ||||||
|           path, |           path, | ||||||
|  |           replacePath, | ||||||
|  |           canPushCode: pushCode, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Reports do | ||||||
|         :secret_detection | 'gl-secret-detection-report.json' |         :secret_detection | 'gl-secret-detection-report.json' | ||||||
|         :dependency_scanning | 'gl-dependency-scanning-report.json' |         :dependency_scanning | 'gl-dependency-scanning-report.json' | ||||||
|         :container_scanning | 'gl-container-scanning-report.json' |         :container_scanning | 'gl-container-scanning-report.json' | ||||||
|  |         :cluster_image_scanning | 'gl-cluster-image-scanning-report.json' | ||||||
|         :dast | 'gl-dast-report.json' |         :dast | 'gl-dast-report.json' | ||||||
|         :license_scanning | 'gl-license-scanning-report.json' |         :license_scanning | 'gl-license-scanning-report.json' | ||||||
|         :performance | 'performance.json' |         :performance | 'performance.json' | ||||||
|  |  | ||||||
|  | @ -1969,6 +1969,19 @@ RSpec.describe Notify do | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |   describe 'in product marketing', :mailer do | ||||||
|  |     let_it_be(:group) { create(:group) } | ||||||
|  | 
 | ||||||
|  |     let(:mail) { ActionMailer::Base.deliveries.last } | ||||||
|  | 
 | ||||||
|  |     it 'does not raise error' do | ||||||
|  |       described_class.in_product_marketing_email(user.id, group.id, :trial, 0).deliver | ||||||
|  | 
 | ||||||
|  |       expect(mail.subject).to eq('Go farther with GitLab') | ||||||
|  |       expect(mail.body.parts.first.to_s).to include('Start a GitLab Ultimate trial today in less than one minute, no credit card required.') | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   def expect_sender(user) |   def expect_sender(user) | ||||||
|     sender = subject.header[:from].addrs[0] |     sender = subject.header[:from].addrs[0] | ||||||
|     expect(sender.display_name).to eq("#{user.name} (@#{user.username})") |     expect(sender.display_name).to eq("#{user.name} (@#{user.username})") | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ RSpec.describe Ci::RetryBuildService do | ||||||
|        erased_at auto_canceled_by job_artifacts job_artifacts_archive |        erased_at auto_canceled_by job_artifacts job_artifacts_archive | ||||||
|        job_artifacts_metadata job_artifacts_trace job_artifacts_junit |        job_artifacts_metadata job_artifacts_trace job_artifacts_junit | ||||||
|        job_artifacts_sast job_artifacts_secret_detection job_artifacts_dependency_scanning |        job_artifacts_sast job_artifacts_secret_detection job_artifacts_dependency_scanning | ||||||
|        job_artifacts_container_scanning job_artifacts_dast |        job_artifacts_container_scanning job_artifacts_cluster_image_scanning job_artifacts_dast | ||||||
|        job_artifacts_license_scanning |        job_artifacts_license_scanning | ||||||
|        job_artifacts_performance job_artifacts_browser_performance job_artifacts_load_performance |        job_artifacts_performance job_artifacts_browser_performance job_artifacts_load_performance | ||||||
|        job_artifacts_lsif job_artifacts_terraform job_artifacts_cluster_applications |        job_artifacts_lsif job_artifacts_terraform job_artifacts_cluster_applications | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue