Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-06 09:07:05 +00:00
parent 67d7b691b1
commit 22856b8780
21 changed files with 171 additions and 126 deletions

View File

@ -157,7 +157,7 @@ gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'commonmarker', '~> 0.21'
gem 'kramdown', '~> 2.3.1'
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 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
@ -197,7 +197,7 @@ gem 'acts-as-taggable-on', '~> 7.0'
# Background jobs
gem 'sidekiq', '~> 5.2.7'
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'
# Cron Parser
@ -229,7 +229,7 @@ gem 'js_regex', '~> 3.4'
gem 'device_detector'
# Redis
gem 'redis', '~> 4.0'
gem 'redis', '~> 4.1.4'
gem 'connection_pool', '~> 2.0'
# Redis session store

View File

@ -499,7 +499,6 @@ GEM
openid_connect (~> 1.2)
gitlab-pg_query (2.0.4)
google-protobuf (>= 3.17.1)
gitlab-rdoc (6.3.2)
gitlab-sidekiq-fetcher (0.5.6)
sidekiq (~> 5)
gitlab-styles (6.2.0)
@ -1026,11 +1025,12 @@ GEM
msgpack (>= 0.4.3)
optimist (>= 3.0.0)
rchardet (1.8.0)
rdoc (6.3.2)
re2 (1.2.0)
recaptcha (4.13.1)
json
recursive-open-struct (1.1.2)
redis (4.1.3)
redis (4.1.4)
redis-actionpack (5.2.0)
actionpack (>= 5, < 7)
redis-rack (>= 2.1.0, < 3)
@ -1038,7 +1038,7 @@ GEM
redis-activesupport (5.2.0)
activesupport (>= 3, < 7)
redis-store (>= 1.3, < 2)
redis-namespace (1.7.0)
redis-namespace (1.8.1)
redis (>= 3.0.4)
redis-rack (2.1.2)
rack (>= 2.0.8, < 3)
@ -1497,7 +1497,6 @@ DEPENDENCIES
gitlab-markup (~> 1.7.1)
gitlab-net-dns (~> 0.9.1)
gitlab-omniauth-openid-connect (~> 0.4.0)
gitlab-rdoc (~> 6.3.2)
gitlab-sidekiq-fetcher (= 0.5.6)
gitlab-styles (~> 6.2.0)
gitlab_chronic_duration (~> 0.10.6.2)
@ -1607,10 +1606,11 @@ DEPENDENCIES
rainbow (~> 3.0)
rblineprof (~> 0.3.6)
rbtrace (~> 0.4)
rdoc (~> 6.3.2)
re2 (~> 1.2.0)
recaptcha (~> 4.11)
redis (~> 4.0)
redis-namespace (~> 1.7.0)
redis (~> 4.1.4)
redis-namespace (~> 1.8.1)
redis-rails (~> 5.0.2)
request_store (~> 1.5)
responders (~> 3.0)

View File

@ -75,15 +75,6 @@ export default {
validProjectKey() {
return !this.enableJiraIssues || Boolean(this.projectKey) || !this.validated;
},
showJiraVulnerabilitiesOptions() {
return this.showJiraVulnerabilitiesIntegration;
},
showUltimateUpgrade() {
return this.showJiraIssuesIntegration && !this.showJiraVulnerabilitiesIntegration;
},
showPremiumUpgrade() {
return !this.showJiraIssuesIntegration;
},
},
created() {
eventHub.$on('validateForm', this.validateForm);
@ -128,23 +119,30 @@ export default {
}}
</template>
</gl-form-checkbox>
<jira-issue-creation-vulnerabilities
v-if="enableJiraIssues"
:project-key="projectKey"
:initial-is-enabled="initialEnableJiraVulnerabilities"
:initial-issue-type-id="initialVulnerabilitiesIssuetype"
:show-full-feature="showJiraVulnerabilitiesOptions"
data-testid="jira-for-vulnerabilities"
@request-get-issue-types="getJiraIssueTypes"
/>
<template v-if="enableJiraIssues">
<jira-issue-creation-vulnerabilities
:project-key="projectKey"
:initial-is-enabled="initialEnableJiraVulnerabilities"
:initial-issue-type-id="initialVulnerabilitiesIssuetype"
:show-full-feature="showJiraVulnerabilitiesIntegration"
data-testid="jira-for-vulnerabilities"
@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>
<jira-upgrade-cta
v-if="showUltimateUpgrade || showPremiumUpgrade"
v-else
class="gl-mt-2"
:class="{ 'gl-ml-6': showUltimateUpgrade }"
data-testid="premium-upgrade-cta"
show-premium-message
:upgrade-plan-path="upgradePlanPath"
:show-ultimate-message="showUltimateUpgrade"
:show-premium-message="showPremiumUpgrade"
/>
</div>
</gl-form-group>

View File

@ -65,6 +65,9 @@ export default {
isLoadingLegacyViewer: false,
activeViewerType: SIMPLE_BLOB_VIEWER,
project: {
userPermissions: {
pushCode: false,
},
repository: {
blobs: {
nodes: [
@ -86,7 +89,6 @@ export default {
canLock: false,
isLocked: false,
lockLink: '',
canModifyBlob: true,
forkPath: '',
simpleViewer: {},
richViewer: null,
@ -168,7 +170,7 @@ export default {
:path="path"
:name="blobInfo.name"
:replace-path="blobInfo.replacePath"
:can-push-code="blobInfo.canModifyBlob"
:can-push-code="project.userPermissions.pushCode"
/>
</template>
</blob-header>

View File

@ -1,5 +1,8 @@
query getBlobInfo($projectPath: ID!, $filePath: String!) {
project(fullPath: $projectPath) {
userPermissions {
pushCode
}
repository {
blobs(paths: [$filePath]) {
nodes {
@ -15,7 +18,6 @@ query getBlobInfo($projectPath: ID!, $filePath: String!) {
storedExternally
rawPath
replacePath
canModifyBlob
simpleViewer {
fileType
tooLarge

View File

@ -56,24 +56,19 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
}
.flash-alert {
background-color: $red-100;
color: $red-700;
background-color: $red-50;
}
.flash-notice {
background-color: $blue-100;
color: $blue-700;
background-color: $blue-50;
}
.flash-success {
background-color: $theme-green-100;
color: $green-700;
background-color: $green-50;
}
.flash-warning {
background-color: $orange-50;
color: $gray-900;
cursor: default;
}
.flash-text,

View File

@ -84,11 +84,7 @@ module Repositories
return if Feature.enabled?(:disable_git_http_fetch_writes)
if Feature.enabled?(:project_statistics_sync, project, default_enabled: true)
Projects::FetchStatisticsIncrementService.new(project).execute
else
ProjectDailyStatisticsWorker.perform_async(project.id) # rubocop:disable CodeReuse/Worker
end
Projects::FetchStatisticsIncrementService.new(project).execute
end
def access

View File

@ -33,6 +33,7 @@ module Ci
secret_detection: 'gl-secret-detection-report.json',
dependency_scanning: 'gl-dependency-scanning-report.json',
container_scanning: 'gl-container-scanning-report.json',
cluster_image_scanning: 'gl-cluster-image-scanning-report.json',
dast: 'gl-dast-report.json',
license_scanning: 'gl-license-scanning-report.json',
performance: 'performance.json',
@ -71,6 +72,7 @@ module Ci
secret_detection: :raw,
dependency_scanning: :raw,
container_scanning: :raw,
cluster_image_scanning: :raw,
dast: :raw,
license_scanning: :raw,
@ -108,6 +110,7 @@ module Ci
sast
secret_detection
requirements
cluster_image_scanning
].freeze
TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze
@ -212,7 +215,8 @@ module Ci
coverage_fuzzing: 23, ## EE-specific
browser_performance: 24, ## 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.

View File

@ -31,7 +31,8 @@ class UserCallout < ApplicationRecord
pipeline_needs_banner: 29,
pipeline_needs_hover_tip: 30,
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

View File

@ -3,7 +3,7 @@
%head
%meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" }
%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
:css
/* CLIENT-SPECIFIC STYLES */

View File

@ -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

View File

@ -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="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="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="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. |
@ -14681,6 +14682,7 @@ Iteration ID wildcard values.
| <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="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="jobartifactfiletypecodequality"></a>`CODEQUALITY` | CODE QUALITY 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="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="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="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. |

View File

@ -3064,6 +3064,18 @@ as artifacts.
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.
##### `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)**
> - Introduced in GitLab 11.5.

View File

@ -15,7 +15,7 @@ module Gitlab
%i[junit codequality sast secret_detection dependency_scanning container_scanning
dast performance browser_performance load_performance license_scanning metrics lsif
dotenv cobertura terraform accessibility cluster_applications
requirements coverage_fuzzing api_fuzzing].freeze
requirements coverage_fuzzing api_fuzzing cluster_image_scanning].freeze
attributes ALLOWED_KEYS
@ -32,6 +32,7 @@ module Gitlab
validates :secret_detection, 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 :cluster_image_scanning, array_of_strings_or_string: true
validates :dast, array_of_strings_or_string: true
validates :performance, array_of_strings_or_string: true
validates :browser_performance, array_of_strings_or_string: true

View File

@ -34,18 +34,6 @@ RSpec.describe Repositories::GitHttpController do
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
stub_feature_flags(disable_git_http_fetch_writes: false)

View File

@ -508,6 +508,14 @@ FactoryBot.define do
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
options do
{

View File

@ -2,7 +2,6 @@ import { GlFormCheckbox, GlFormInput } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
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 { createStore } from '~/integrations/edit/store';
@ -14,6 +13,7 @@ describe('JiraIssuesFields', () => {
editProjectPath: '/edit',
showJiraIssuesIntegration: true,
showJiraVulnerabilitiesIntegration: true,
upgradePlanPath: 'https://gitlab.com',
};
const createComponent = ({ isInheriting = false, props, ...options } = {}) => {
@ -37,60 +37,79 @@ describe('JiraIssuesFields', () => {
const findEnableCheckboxDisabled = () =>
findEnableCheckbox().find('[type=checkbox]').attributes('disabled');
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 setEnableCheckbox = async (isEnabled = true) =>
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('upgrade banner for non-Premium user', () => {
beforeEach(() => {
createComponent({ props: { initialProjectKey: '', showJiraIssuesIntegration: false } });
});
describe.each`
showJiraIssuesIntegration | showJiraVulnerabilitiesIntegration
${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', () => {
expect(findEnableCheckbox().exists()).toBe(false);
expect(findProjectKey().exists()).toBe(false);
});
});
if (showJiraIssuesIntegration) {
it('renders checkbox and input field', () => {
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', () => {
beforeEach(() => {
createComponent({ props: { initialProjectKey: '' } });
});
it('renders enabled checkbox', () => {
expect(findEnableCheckbox().exists()).toBe(true);
expect(findEnableCheckboxDisabled()).toBeUndefined();
});
it('renders disabled project_key input', () => {
const projectKey = findProjectKey();
@ -99,10 +118,6 @@ describe('JiraIssuesFields', () => {
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,
// browsers don't include unchecked boxes in form submissions.
it('includes issues_enabled as false even if unchecked', () => {

View File

@ -37,7 +37,6 @@ const simpleMockData = {
canLock: true,
isLocked: false,
lockLink: 'some_file.js/lock',
canModifyBlob: true,
forkPath: 'some_file.js/fork',
simpleViewer: {
fileType: 'text',
@ -56,16 +55,26 @@ const richMockData = {
renderError: null,
},
};
const userPermissionsMockData = {
userPermissions: {
pushCode: true,
},
};
const localVue = createLocalVue();
const mockAxios = new MockAdapter(axios);
const createComponentWithApollo = (mockData) => {
const createComponentWithApollo = (mockData, mockPermissionData = true) => {
localVue.use(VueApollo);
const mockResolver = jest
.fn()
.mockResolvedValue({ data: { project: { repository: { blobs: { nodes: [mockData] } } } } });
const mockResolver = jest.fn().mockResolvedValue({
data: {
project: {
userPermissions: { pushCode: mockPermissionData },
repository: { blobs: { nodes: [mockData] } },
},
},
});
const fakeApollo = createMockApollo([[blobInfoQuery, mockResolver]]);
@ -276,13 +285,16 @@ describe('Blob content viewer component', () => {
});
describe('BlobButtonGroup', () => {
const { name, path } = simpleMockData;
const { name, path, replacePath } = simpleMockData;
const {
userPermissions: { pushCode },
} = userPermissionsMockData;
it('renders component', async () => {
window.gon.current_user_id = 1;
fullFactory({
mockData: { blobInfo: simpleMockData },
mockData: { blobInfo: simpleMockData, project: userPermissionsMockData },
stubs: {
BlobContent: true,
BlobButtonGroup: true,
@ -294,6 +306,8 @@ describe('Blob content viewer component', () => {
expect(findBlobButtonGroup().props()).toMatchObject({
name,
path,
replacePath,
canPushCode: pushCode,
});
});

View File

@ -40,6 +40,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Reports do
:secret_detection | 'gl-secret-detection-report.json'
:dependency_scanning | 'gl-dependency-scanning-report.json'
:container_scanning | 'gl-container-scanning-report.json'
:cluster_image_scanning | 'gl-cluster-image-scanning-report.json'
:dast | 'gl-dast-report.json'
:license_scanning | 'gl-license-scanning-report.json'
:performance | 'performance.json'

View File

@ -1969,6 +1969,19 @@ RSpec.describe Notify do
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)
sender = subject.header[:from].addrs[0]
expect(sender.display_name).to eq("#{user.name} (@#{user.username})")

View File

@ -39,7 +39,7 @@ RSpec.describe Ci::RetryBuildService do
erased_at auto_canceled_by job_artifacts job_artifacts_archive
job_artifacts_metadata job_artifacts_trace job_artifacts_junit
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_performance job_artifacts_browser_performance job_artifacts_load_performance
job_artifacts_lsif job_artifacts_terraform job_artifacts_cluster_applications