summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-01 18:08:33 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-01 18:08:33 +0000
commit24d67ec55454fc6f4e8e80bf7c8dc5bc677e8514 (patch)
tree6ce73c2fd8ec96c6f043c4a1403c0991fd448b81 /app
parenta0fdcfcdd514c2af98f18cadfa75f8a6a85b4d2c (diff)
downloadgitlab-ce-24d67ec55454fc6f4e8e80bf7c8dc5bc677e8514.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue15
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue8
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue70
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue73
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js58
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_reports.vue17
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue10
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue2
-rw-r--r--app/assets/javascripts/pipelines/pipeline_test_details.js13
-rw-r--r--app/assets/javascripts/pipelines/stores/test_reports/index.js12
-rw-r--r--app/assets/javascripts/security_configuration/components/app.vue1
-rw-r--r--app/assets/javascripts/security_configuration/components/constants.js2
-rw-r--r--app/controllers/oauth/applications_controller.rb4
-rw-r--r--app/experiments/security_actions_continuous_onboarding_experiment.rb9
-rw-r--r--app/helpers/integrations_helper.rb2
-rw-r--r--app/helpers/learn_gitlab_helper.rb20
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/integration.rb4
-rw-r--r--app/models/integrations/base_chat_notification.rb4
-rw-r--r--app/models/integrations/jira.rb3
-rw-r--r--app/models/oauth_access_token.rb2
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/ci/external_pull_requests/create_pipeline_service.rb14
-rw-r--r--app/services/ci/job_artifacts/create_service.rb6
25 files changed, 177 insertions, 180 deletions
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue
new file mode 100644
index 00000000000..693dc6a15ad
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue
@@ -0,0 +1,15 @@
+<script>
+import { s__ } from '~/locale';
+
+export default {
+ name: 'IncludedInTrialIndicator',
+ i18n: {
+ trialOnly: s__('LearnGitlab|- Included in trial'),
+ },
+};
+</script>
+<template>
+ <span class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only">
+ {{ $options.i18n.trialOnly }}
+ </span>
+</template>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue
index db9ef4df8af..54e15b6552c 100644
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue
@@ -38,14 +38,16 @@ export default {
actionsData: this.actions,
};
},
- maxValue: Object.keys(ACTION_LABELS).length,
actionSections: Object.keys(ACTION_SECTIONS),
computed: {
+ maxValue() {
+ return Object.keys(this.actionsData).length;
+ },
progressValue() {
return Object.values(this.actionsData).filter((a) => a.completed).length;
},
progressPercentage() {
- return Math.round((this.progressValue / this.$options.maxValue) * 100);
+ return Math.round((this.progressValue / this.maxValue) * 100);
},
},
mounted() {
@@ -125,7 +127,7 @@ export default {
<template #percentSymbol>%</template>
</gl-sprintf>
</p>
- <gl-progress-bar :value="progressValue" :max="$options.maxValue" />
+ <gl-progress-bar :value="progressValue" :max="maxValue" />
</div>
<div class="row">
<div
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue
deleted file mode 100644
index 09cc0032871..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue
+++ /dev/null
@@ -1,70 +0,0 @@
-<script>
-import { GlLink, GlCard, GlIcon } from '@gitlab/ui';
-import { s__ } from '~/locale';
-
-export default {
- name: 'LearnGitlabInfoCard',
- components: { GlLink, GlCard, GlIcon },
- i18n: {
- trial: s__('Learn GitLab|Trial only'),
- },
- props: {
- title: {
- required: true,
- type: String,
- },
- description: {
- required: true,
- type: String,
- },
- actionLabel: {
- required: true,
- type: String,
- },
- url: {
- required: true,
- type: String,
- },
- completed: {
- required: true,
- type: Boolean,
- },
- svg: {
- required: true,
- type: String,
- },
- trialRequired: {
- default: false,
- required: false,
- type: Boolean,
- },
- },
-};
-</script>
-<template>
- <gl-card class="gl-pt-0">
- <div class="gl-text-right gl-h-5">
- <gl-icon
- v-if="completed"
- name="check-circle-filled"
- class="gl-text-green-500"
- :size="16"
- data-testid="completed-icon"
- />
- <span
- v-else-if="trialRequired"
- class="gl-text-gray-500 gl-font-sm gl-font-style-italic"
- data-testid="trial-only"
- >{{ $options.i18n.trial }}</span
- >
- </div>
- <div
- class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
- >
- <img :src="svg" :alt="actionLabel" />
- <h6>{{ title }}</h6>
- <p class="gl-font-sm gl-text-gray-700">{{ description }}</p>
- <gl-link :href="url" target="_blank" rel="noopener noreferrer" />
- </div>
- </gl-card>
-</template>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
index 1912477758b..4eab0cccb06 100644
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
@@ -6,6 +6,7 @@ import { isExperimentVariant } from '~/experimentation/utils';
import eventHub from '~/invite_members/event_hub';
import { s__, __ } from '~/locale';
import { ACTION_LABELS } from '../constants';
+import IncludedInTrialIndicator from './included_in_trial_indicator.vue';
export default {
name: 'LearnGitlabSectionLink',
@@ -15,12 +16,12 @@ export default {
GlButton,
GlPopover,
GitlabExperiment,
+ IncludedInTrialIndicator,
},
directives: {
GlTooltip,
},
i18n: {
- trialOnly: s__('LearnGitlab|Trial only'),
contactAdmin: s__('LearnGitlab|Contact your administrator to start a free Ultimate trial.'),
viewAdminList: s__('LearnGitlab|View administrator list'),
watchHow: __('Watch how'),
@@ -41,12 +42,6 @@ export default {
};
},
computed: {
- linkTitle() {
- return ACTION_LABELS[this.action].title;
- },
- trialOnly() {
- return ACTION_LABELS[this.action].trialRequired;
- },
showInviteModalLink() {
return (
this.action === 'userAdded' && isExperimentVariant('invite_for_help_continuous_onboarding')
@@ -55,49 +50,51 @@ export default {
openInNewTab() {
return ACTION_LABELS[this.action]?.openInNewTab === true || this.value.openInNewTab === true;
},
- linkToVideoTutorial() {
- return ACTION_LABELS[this.action].videoTutorial;
- },
},
methods: {
openModal() {
eventHub.$emit('openModal', { source: 'learn_gitlab' });
},
+ actionLabelValue(value) {
+ return ACTION_LABELS[this.action][value];
+ },
},
};
</script>
<template>
<div class="gl-mb-4">
- <div v-if="trialOnly" class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only">
- {{ $options.i18n.trialOnly }}
- </div>
<div class="flex align-items-center">
<span v-if="value.completed" class="gl-text-green-500">
<gl-icon name="check-circle-filled" :size="16" data-testid="completed-icon" />
- {{ linkTitle }}
+ {{ actionLabelValue('title') }}
+ <included-in-trial-indicator v-if="actionLabelValue('trialRequired')" />
</span>
- <gl-link
- v-else-if="showInviteModalLink"
- data-track-action="click_link"
- :data-track-label="linkTitle"
- data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding"
- data-testid="invite-for-help-continuous-onboarding-experiment-link"
- @click="openModal"
- >
- {{ linkTitle }}
- </gl-link>
- <gl-link
- v-else-if="value.enabled"
- :target="openInNewTab ? '_blank' : '_self'"
- :href="value.url"
- data-testid="uncompleted-learn-gitlab-link"
- data-track-action="click_link"
- :data-track-label="linkTitle"
- >
- {{ linkTitle }}
- </gl-link>
+ <div v-else-if="showInviteModalLink">
+ <gl-link
+ data-track-action="click_link"
+ :data-track-label="actionLabelValue('trackLabel')"
+ data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding"
+ data-testid="invite-for-help-continuous-onboarding-experiment-link"
+ @click="openModal"
+ >{{ actionLabelValue('title') }}</gl-link
+ >
+
+ <included-in-trial-indicator v-if="actionLabelValue('trialRequired')" />
+ </div>
+ <div v-else-if="value.enabled">
+ <gl-link
+ :target="openInNewTab ? '_blank' : '_self'"
+ :href="value.url"
+ data-testid="uncompleted-learn-gitlab-link"
+ data-track-action="click_link"
+ :data-track-label="actionLabelValue('trackLabel')"
+ >{{ actionLabelValue('title') }}</gl-link
+ >
+
+ <included-in-trial-indicator v-if="actionLabelValue('trialRequired')" />
+ </div>
<template v-else>
- <div data-testid="disabled-learn-gitlab-link">{{ linkTitle }}</div>
+ <div data-testid="disabled-learn-gitlab-link">{{ actionLabelValue('title') }}</div>
<gl-button
:id="popoverId"
category="tertiary"
@@ -127,19 +124,19 @@ export default {
<template #control></template>
<template #candidate>
<gl-button
- v-if="linkToVideoTutorial"
+ v-if="actionLabelValue('videoTutorial')"
v-gl-tooltip
category="tertiary"
icon="live-preview"
:title="$options.i18n.watchHow"
:aria-label="$options.i18n.watchHow"
- :href="linkToVideoTutorial"
+ :href="actionLabelValue('videoTutorial')"
target="_blank"
class="ml-auto"
size="small"
data-testid="video-tutorial-link"
data-track-action="click_video_link"
- :data-track-label="linkTitle"
+ :data-track-label="actionLabelValue('trackLabel')"
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
data-track-experiment="video_tutorials_continuous_onboarding"
/>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
index 05bacd9b350..cb1a0302d91 100644
--- a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
@@ -2,9 +2,10 @@ import { s__ } from '~/locale';
export const ACTION_LABELS = {
gitWrite: {
- title: s__('LearnGitLab|Create or import a repository'),
- actionLabel: s__('LearnGitLab|Create or import a repository'),
+ title: s__('LearnGitLab|Create a repository'),
+ actionLabel: s__('LearnGitLab|Create a repository'),
description: s__('LearnGitLab|Create or import your first repository into your new project.'),
+ trackLabel: 'create_a_repository',
section: 'workspace',
position: 1,
},
@@ -14,20 +15,23 @@ export const ACTION_LABELS = {
description: s__(
'LearnGitLab|GitLab works best as a team. Invite your colleague to enjoy all features.',
),
+ trackLabel: 'invite_your_colleagues',
section: 'workspace',
position: 0,
},
pipelineCreated: {
- title: s__('LearnGitLab|Set up CI/CD'),
- actionLabel: s__('LearnGitLab|Set-up CI/CD'),
+ title: s__("LearnGitLab|Set up your first project's CI/CD"),
+ actionLabel: s__('LearnGitLab|Set up CI/CD'),
description: s__('LearnGitLab|Save time by automating your integration and deployment tasks.'),
+ trackLabel: 'set_up_your_first_project_s_ci_cd',
section: 'workspace',
position: 2,
},
trialStarted: {
- title: s__('LearnGitLab|Start a free Ultimate trial'),
+ title: s__('LearnGitLab|Start a free trial of GitLab Ultimate'),
actionLabel: s__('LearnGitLab|Try GitLab Ultimate for free'),
description: s__('LearnGitLab|Try all GitLab features for 30 days, no credit card required.'),
+ trackLabel: 'start_a_free_trial_of_gitlab_ultimate',
section: 'workspace',
position: 3,
openInNewTab: true,
@@ -38,6 +42,7 @@ export const ACTION_LABELS = {
description: s__(
'LearnGitLab|Prevent unexpected changes to important assets by assigning ownership of files and paths.',
),
+ trackLabel: 'add_code_owners',
trialRequired: true,
section: 'workspace',
position: 4,
@@ -45,9 +50,10 @@ export const ACTION_LABELS = {
videoTutorial: 'https://vimeo.com/670896787',
},
requiredMrApprovalsEnabled: {
- title: s__('LearnGitLab|Add merge request approval'),
+ title: s__('LearnGitLab|Enable require merge approvals'),
actionLabel: s__('LearnGitLab|Enable require merge approvals'),
description: s__('LearnGitLab|Route code reviews to the right reviewers, every time.'),
+ trackLabel: 'enable_require_merge_approvals',
trialRequired: true,
section: 'workspace',
position: 5,
@@ -55,28 +61,52 @@ export const ACTION_LABELS = {
videoTutorial: 'https://vimeo.com/670904904',
},
mergeRequestCreated: {
- title: s__('LearnGitLab|Submit a merge request'),
+ title: s__('LearnGitLab|Submit a merge request (MR)'),
actionLabel: s__('LearnGitLab|Submit a merge request (MR)'),
description: s__('LearnGitLab|Review and edit proposed changes to source code.'),
+ trackLabel: 'submit_a_merge_request_mr',
section: 'plan',
position: 1,
},
- securityScanEnabled: {
- title: s__('LearnGitLab|Run a Security scan using CI/CD'),
- actionLabel: s__('LearnGitLab|Run a Security scan using CI/CD'),
- description: s__('LearnGitLab|Scan your code to uncover vulnerabilities before deploying.'),
- section: 'deploy',
- position: 1,
- },
issueCreated: {
title: s__('LearnGitLab|Create an issue'),
actionLabel: s__('LearnGitLab|Create an issue'),
description: s__(
'LearnGitLab|Create/import issues (tickets) to collaborate on ideas and plan work.',
),
+ trackLabel: 'create_an_issue',
section: 'plan',
position: 0,
},
+ securityScanEnabled: {
+ title: s__('LearnGitLab|Run a Security scan using CI/CD'),
+ actionLabel: s__('LearnGitLab|Run a Security scan using CI/CD'),
+ description: s__('LearnGitLab|Scan your code to uncover vulnerabilities before deploying.'),
+ trackLabel: 'run_a_security_scan_using_ci_cd',
+ section: 'deploy',
+ position: 1,
+ },
+ licenseScanningRun: {
+ title: s__('LearnGitLab|Scan dependencies for licenses'),
+ trackLabel: 'scan_dependencies_for_licenses',
+ trialRequired: true,
+ section: 'deploy',
+ position: 2,
+ },
+ secureDependencyScanningRun: {
+ title: s__('LearnGitLab|Scan dependencies for vulnerabilities'),
+ trackLabel: 'scan_dependencies_for_vulnerabilities',
+ trialRequired: true,
+ section: 'deploy',
+ position: 3,
+ },
+ secureDastRun: {
+ title: s__('LearnGitLab|Analyze your application for vulnerabilities with DAST'),
+ trackLabel: 'analyze_your_application_for_vulnerabilities_with_dast',
+ trialRequired: true,
+ section: 'deploy',
+ position: 4,
+ },
};
export const ACTION_SECTIONS = {
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue b/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue
index 58d072b0005..3fb46a4f128 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue
@@ -1,6 +1,7 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
+import createTestReportsStore from '../../stores/test_reports';
import EmptyState from './empty_state.vue';
import TestSuiteTable from './test_suite_table.vue';
import TestSummary from './test_summary.vue';
@@ -15,9 +16,10 @@ export default {
TestSummary,
TestSummaryTable,
},
+ inject: ['blobPath', 'summaryEndpoint', 'suiteEndpoint'],
computed: {
- ...mapState(['isLoading', 'selectedSuiteIndex', 'testReports']),
- ...mapGetters(['getSelectedSuite']),
+ ...mapState('testReports', ['isLoading', 'selectedSuiteIndex', 'testReports']),
+ ...mapGetters('testReports', ['getSelectedSuite']),
showSuite() {
return this.selectedSuiteIndex !== null;
},
@@ -27,10 +29,19 @@ export default {
},
},
created() {
+ this.$store.registerModule(
+ 'testReports',
+ createTestReportsStore({
+ blobPath: this.blobPath,
+ summaryEndpoint: this.summaryEndpoint,
+ suiteEndpoint: this.suiteEndpoint,
+ }),
+ );
+
this.fetchSummary();
},
methods: {
- ...mapActions([
+ ...mapActions('testReports', [
'fetchTestSuite',
'fetchSummary',
'setSelectedSuiteIndex',
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
index 1e481d37017..1f438c63fee 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
@@ -51,14 +51,18 @@ export default {
},
},
computed: {
- ...mapState(['pageInfo']),
- ...mapGetters(['getSuiteTests', 'getSuiteTestCount', 'getSuiteArtifactsExpired']),
+ ...mapState('testReports', ['pageInfo']),
+ ...mapGetters('testReports', [
+ 'getSuiteTests',
+ 'getSuiteTestCount',
+ 'getSuiteArtifactsExpired',
+ ]),
hasSuites() {
return this.getSuiteTests.length > 0;
},
},
methods: {
- ...mapActions(['setPage']),
+ ...mapActions('testReports', ['setPage']),
},
wrapSymbols: ['::', '#', '.', '_', '-', '/', '\\'],
i18n,
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
index 2b44ce57faa..8389c2a5104 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
@@ -19,7 +19,7 @@ export default {
},
},
computed: {
- ...mapGetters(['getTestSuites']),
+ ...mapGetters('testReports', ['getTestSuites']),
hasSuites() {
return this.getTestSuites.length > 0;
},
diff --git a/app/assets/javascripts/pipelines/pipeline_test_details.js b/app/assets/javascripts/pipelines/pipeline_test_details.js
index 27ab2418440..fe4ca8e9529 100644
--- a/app/assets/javascripts/pipelines/pipeline_test_details.js
+++ b/app/assets/javascripts/pipelines/pipeline_test_details.js
@@ -1,9 +1,10 @@
import Vue from 'vue';
+import Vuex from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
import Translate from '~/vue_shared/translate';
import TestReports from './components/test_reports/test_reports.vue';
-import createTestReportsStore from './stores/test_reports';
+Vue.use(Vuex);
Vue.use(Translate);
export const createTestDetails = (selector) => {
@@ -16,11 +17,6 @@ export const createTestDetails = (selector) => {
suiteEndpoint,
artifactsExpiredImagePath,
} = el?.dataset || {};
- const testReportsStore = createTestReportsStore({
- blobPath,
- summaryEndpoint,
- suiteEndpoint,
- });
// eslint-disable-next-line no-new
new Vue({
@@ -32,8 +28,11 @@ export const createTestDetails = (selector) => {
emptyStateImagePath,
artifactsExpiredImagePath,
hasTestReport: parseBoolean(hasTestReport),
+ blobPath,
+ summaryEndpoint,
+ suiteEndpoint,
},
- store: testReportsStore,
+ store: new Vuex.Store(),
render(createElement) {
return createElement('test-reports');
},
diff --git a/app/assets/javascripts/pipelines/stores/test_reports/index.js b/app/assets/javascripts/pipelines/stores/test_reports/index.js
index 64d4b8bafb1..f45a53f47b7 100644
--- a/app/assets/javascripts/pipelines/stores/test_reports/index.js
+++ b/app/assets/javascripts/pipelines/stores/test_reports/index.js
@@ -1,16 +1,14 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
import state from './state';
-Vue.use(Vuex);
-
-export default (initialState) =>
- new Vuex.Store({
+export default (initialState) => {
+ return {
+ namespaced: true,
actions,
getters,
mutations,
state: state(initialState),
- });
+ };
+};
diff --git a/app/assets/javascripts/security_configuration/components/app.vue b/app/assets/javascripts/security_configuration/components/app.vue
index 0e21d69310e..ecde9235e93 100644
--- a/app/assets/javascripts/security_configuration/components/app.vue
+++ b/app/assets/javascripts/security_configuration/components/app.vue
@@ -206,6 +206,7 @@ export default {
<template #features>
<feature-card
v-for="feature in augmentedSecurityFeatures"
+ :id="feature.anchor"
:key="feature.type"
data-testid="security-testing-card"
:feature="feature"
diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js
index e4d2bd08f50..6efaf08a178 100644
--- a/app/assets/javascripts/security_configuration/components/constants.js
+++ b/app/assets/javascripts/security_configuration/components/constants.js
@@ -194,6 +194,7 @@ export const securityFeatures = [
helpPath: DAST_HELP_PATH,
configurationHelpPath: DAST_CONFIG_HELP_PATH,
type: REPORT_TYPE_DAST,
+ anchor: 'dast',
},
{
name: DEPENDENCY_SCANNING_NAME,
@@ -201,6 +202,7 @@ export const securityFeatures = [
helpPath: DEPENDENCY_SCANNING_HELP_PATH,
configurationHelpPath: DEPENDENCY_SCANNING_CONFIG_HELP_PATH,
type: REPORT_TYPE_DEPENDENCY_SCANNING,
+ anchor: 'dependency-scanning',
},
{
name: CONTAINER_SCANNING_NAME,
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index f425e996c2b..a996bad3fac 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -53,7 +53,9 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
def set_index_vars
@applications = current_user.oauth_applications.load
- @authorized_tokens = current_user.oauth_authorized_tokens.preload(:application).order(created_at: :desc).load # rubocop: disable CodeReuse/ActiveRecord
+ @authorized_tokens = current_user.oauth_authorized_tokens
+ .latest_per_application
+ .preload_application
# Don't overwrite a value possibly set by `create`
@application ||= Doorkeeper::Application.new
diff --git a/app/experiments/security_actions_continuous_onboarding_experiment.rb b/app/experiments/security_actions_continuous_onboarding_experiment.rb
new file mode 100644
index 00000000000..6adfbedc744
--- /dev/null
+++ b/app/experiments/security_actions_continuous_onboarding_experiment.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class SecurityActionsContinuousOnboardingExperiment < ApplicationExperiment
+ def control_behavior
+ end
+
+ def candidate_behavior
+ end
+end
diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb
index 8d5523464c7..a1512d40235 100644
--- a/app/helpers/integrations_helper.rb
+++ b/app/helpers/integrations_helper.rb
@@ -216,7 +216,7 @@ module IntegrationsHelper
end
def fields_for_integration(integration)
- Integrations::FieldSerializer.new(integration: integration).represent(integration.global_fields).to_json
+ Integrations::FieldSerializer.new(integration: integration).represent(integration.form_fields).to_json
end
def integration_level(integration)
diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb
index 890f7f099df..421cf84f98c 100644
--- a/app/helpers/learn_gitlab_helper.rb
+++ b/app/helpers/learn_gitlab_helper.rb
@@ -4,6 +4,7 @@ module LearnGitlabHelper
IMAGE_PATH_PLAN = "learn_gitlab/section_plan.svg"
IMAGE_PATH_DEPLOY = "learn_gitlab/section_deploy.svg"
IMAGE_PATH_WORKSPACE = "learn_gitlab/section_workspace.svg"
+ LICENSE_SCANNING_RUN_URL = 'https://docs.gitlab.com/ee/user/compliance/license_compliance/index.html'
def learn_gitlab_enabled?(project)
return false unless current_user
@@ -64,7 +65,7 @@ module LearnGitlabHelper
git_write: project_path(project),
merge_request_created: project_merge_requests_path(project),
user_added: project_members_url(project),
- security_scan_enabled: project_security_configuration_path(project)
+ **deploy_section_action_urls(project)
)
end
@@ -72,6 +73,23 @@ module LearnGitlabHelper
LearnGitlab::Onboarding::ACTION_ISSUE_IDS.transform_values { |id| project_issue_url(learn_gitlab_project, id) }
end
+ def deploy_section_action_urls(project)
+ experiment(:security_actions_continuous_onboarding,
+ namespace: project.namespace,
+ user: current_user,
+ sticky_to: current_user
+ ) do |e|
+ e.control { { security_scan_enabled: project_security_configuration_path(project) } }
+ e.candidate do
+ {
+ license_scanning_run: LICENSE_SCANNING_RUN_URL,
+ secure_dependency_scanning_run: project_security_configuration_path(project, anchor: 'dependency-scanning'),
+ secure_dast_run: project_security_configuration_path(project, anchor: 'dast')
+ }
+ end
+ end.run
+ end
+
def learn_gitlab_project
@learn_gitlab_project ||= LearnGitlab::Project.new(current_user).project
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 18b6556039c..ced5fd31fd5 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -29,10 +29,6 @@ module Ci
return_exit_code: -> (build) { build.exit_codes_defined? }
}.freeze
- DEFAULT_RETRIES = {
- scheduler_failure: 2
- }.freeze
-
DEGRADATION_THRESHOLD_VARIABLE_NAME = 'DEGRADATION_THRESHOLD'
RUNNERS_STATUS_CACHE_EXPIRATION = 1.minute
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index d7889407849..c48e21cf061 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -27,8 +27,6 @@ module Ci
DEFAULT_CONFIG_PATH = CONFIG_EXTENSION
CANCELABLE_STATUSES = (Ci::HasStatus::CANCELABLE_STATUSES + ['manual']).freeze
- BridgeStatusError = Class.new(StandardError)
-
paginates_per 15
sha_attribute :source_sha
diff --git a/app/models/integration.rb b/app/models/integration.rb
index af8e8060d81..f5f701662e7 100644
--- a/app/models/integration.rb
+++ b/app/models/integration.rb
@@ -505,8 +505,8 @@ class Integration < ApplicationRecord
fields.reject { _1[:type] == 'password' }.pluck(:name)
end
- def global_fields
- fields
+ def form_fields
+ fields.reject { _1[:api_only] == true }
end
def configurable_events
diff --git a/app/models/integrations/base_chat_notification.rb b/app/models/integrations/base_chat_notification.rb
index 51f238d5eb9..c7992e4083c 100644
--- a/app/models/integrations/base_chat_notification.rb
+++ b/app/models/integrations/base_chat_notification.rb
@@ -139,8 +139,8 @@ module Integrations
supported_events.map { |event| event_channel_name(event) }
end
- def global_fields
- fields.reject { |field| field[:name].end_with?('channel') }
+ def form_fields
+ super.reject { |field| field[:name].end_with?('channel') }
end
def default_channel_placeholder
diff --git a/app/models/integrations/jira.rb b/app/models/integrations/jira.rb
index 125f52104d4..c9c9b9d59d6 100644
--- a/app/models/integrations/jira.rb
+++ b/app/models/integrations/jira.rb
@@ -71,11 +71,12 @@ module Integrations
non_empty_password_help: -> { s_('JiraService|Leave blank to use your current password or API token.') },
help: -> { s_('JiraService|Use a password for server version and an API token for cloud version.') }
+ field :jira_issue_transition_id, api_only: true
+
# TODO: we can probably just delegate as part of
# https://gitlab.com/gitlab-org/gitlab/issues/29404
# These fields are API only, so no field definition is required.
data_field :jira_issue_transition_automatic
- data_field :jira_issue_transition_id
data_field :project_key
data_field :issues_enabled
data_field :vulnerabilities_enabled
diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb
index 9789d8ed62b..20130f01d44 100644
--- a/app/models/oauth_access_token.rb
+++ b/app/models/oauth_access_token.rb
@@ -7,6 +7,8 @@ class OauthAccessToken < Doorkeeper::AccessToken
alias_attribute :user, :resource_owner
scope :distinct_resource_owner_counts, ->(applications) { where(application: applications).distinct.group(:application_id).count(:resource_owner_id) }
+ scope :latest_per_application, -> { select('distinct on(application_id) *').order(application_id: :desc, created_at: :desc) }
+ scope :preload_application, -> { preload(:application) }
def scopes=(value)
if value.is_a?(Array)
diff --git a/app/models/user.rb b/app/models/user.rb
index 20ff796aba0..2afd64358ef 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1626,7 +1626,7 @@ class User < ApplicationRecord
end
def oauth_authorized_tokens
- Doorkeeper::AccessToken.where(resource_owner_id: id, revoked_at: nil)
+ OauthAccessToken.where(resource_owner_id: id, revoked_at: nil)
end
# Returns the projects a user contributed to in the last year.
diff --git a/app/services/ci/external_pull_requests/create_pipeline_service.rb b/app/services/ci/external_pull_requests/create_pipeline_service.rb
index 66127c94d35..ffc129eccda 100644
--- a/app/services/ci/external_pull_requests/create_pipeline_service.rb
+++ b/app/services/ci/external_pull_requests/create_pipeline_service.rb
@@ -10,24 +10,12 @@ module Ci
return pull_request_not_open_error unless pull_request.open?
return pull_request_branch_error unless pull_request.actual_branch_head?
- create_pipeline_for(pull_request)
- end
-
- private
-
- def create_pipeline_for(pull_request)
Ci::ExternalPullRequests::CreatePipelineWorker.perform_async(
project.id, current_user.id, pull_request.id
)
end
- def create_params(pull_request)
- {
- ref: pull_request.source_ref,
- source_sha: pull_request.source_sha,
- target_sha: pull_request.target_sha
- }
- end
+ private
def pull_request_not_open_error
ServiceResponse.error(message: 'The pull request is not opened', payload: nil)
diff --git a/app/services/ci/job_artifacts/create_service.rb b/app/services/ci/job_artifacts/create_service.rb
index 635111130d6..f658661a650 100644
--- a/app/services/ci/job_artifacts/create_service.rb
+++ b/app/services/ci/job_artifacts/create_service.rb
@@ -5,8 +5,6 @@ module Ci
class CreateService < ::BaseService
include Gitlab::Utils::UsageData
- ArtifactsExistError = Class.new(StandardError)
-
LSIF_ARTIFACT_TYPE = 'lsif'
METRICS_REPORT_UPLOAD_EVENT_NAME = 'i_testing_metrics_report_artifact_uploaders'
@@ -74,10 +72,6 @@ module Ci
Ci::JobArtifact.max_artifact_size(type: type, project: project)
end
- def forbidden_type_error(type)
- error("#{type} artifacts are forbidden", :forbidden)
- end
-
def too_large_error
error('file size has reached maximum size limit', :payload_too_large)
end