Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-08-12 21:11:09 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-08-12 21:11:09 +0300
commit60eaf3d90650086dedb6fd94d6169dc5ab1f8d1e (patch)
treeff943955f1c424787ba65ff7c1607c522c103115 /app
parent1c8734ca5c2981e62b9c1162851ed136de86bbbf (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/work_items/components/work_item_actions.vue55
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail.vue79
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue15
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item.fragment.graphql2
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_links.query.graphql1
-rw-r--r--app/graphql/mutations/ci/job/retry.rb11
-rw-r--r--app/graphql/types/ci/job_type.rb2
-rw-r--r--app/graphql/types/ci/variable_input_type.rb13
-rw-r--r--app/models/ci/build.rb11
-rw-r--r--app/models/ci/job_artifact.rb3
-rw-r--r--app/models/ci/processable.rb2
-rw-r--r--app/models/container_repository.rb42
-rw-r--r--app/services/ci/retry_job_service.rb12
13 files changed, 227 insertions, 21 deletions
diff --git a/app/assets/javascripts/work_items/components/work_item_actions.vue b/app/assets/javascripts/work_items/components/work_item_actions.vue
index 77002eeaf55..2130eb4b729 100644
--- a/app/assets/javascripts/work_items/components/work_item_actions.vue
+++ b/app/assets/javascripts/work_items/components/work_item_actions.vue
@@ -1,15 +1,24 @@
<script>
-import { GlDropdown, GlDropdownItem, GlModal, GlModalDirective } from '@gitlab/ui';
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownDivider,
+ GlModal,
+ GlModalDirective,
+} from '@gitlab/ui';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
export default {
i18n: {
deleteTask: s__('WorkItem|Delete task'),
+ enableTaskConfidentiality: s__('WorkItem|Turn on confidentiality'),
+ disableTaskConfidentiality: s__('WorkItem|Turn off confidentiality'),
},
components: {
GlDropdown,
GlDropdownItem,
+ GlDropdownDivider,
GlModal,
},
directives: {
@@ -22,14 +31,33 @@ export default {
required: false,
default: null,
},
+ canUpdate: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
canDelete: {
type: Boolean,
required: false,
default: false,
},
+ isConfidential: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isParentConfidential: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
- emits: ['deleteWorkItem'],
+ emits: ['deleteWorkItem', 'toggleWorkItemConfidentiality'],
methods: {
+ handleToggleWorkItemConfidentiality() {
+ this.track('click_toggle_work_item_confidentiality');
+ this.$emit('toggleWorkItemConfidentiality', !this.isConfidential);
+ },
handleDeleteWorkItem() {
this.track('click_delete_work_item');
this.$emit('deleteWorkItem');
@@ -44,7 +72,7 @@ export default {
</script>
<template>
- <div v-if="canDelete">
+ <div>
<gl-dropdown
icon="ellipsis_v"
text-sr-only
@@ -53,9 +81,24 @@ export default {
no-caret
right
>
- <gl-dropdown-item v-gl-modal="'work-item-confirm-delete'">{{
- $options.i18n.deleteTask
- }}</gl-dropdown-item>
+ <template v-if="canUpdate && !isParentConfidential">
+ <gl-dropdown-item
+ data-testid="confidentiality-toggle-action"
+ @click="handleToggleWorkItemConfidentiality"
+ >{{
+ isConfidential
+ ? $options.i18n.disableTaskConfidentiality
+ : $options.i18n.enableTaskConfidentiality
+ }}</gl-dropdown-item
+ >
+ <gl-dropdown-divider />
+ </template>
+ <gl-dropdown-item
+ v-if="canDelete"
+ v-gl-modal="'work-item-confirm-delete'"
+ data-testid="delete-action"
+ >{{ $options.i18n.deleteTask }}</gl-dropdown-item
+ >
</gl-dropdown>
<gl-modal
modal-id="work-item-confirm-delete"
diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue
index 23e2842743a..44c9ef9e44b 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -1,5 +1,13 @@
<script>
-import { GlAlert, GlSkeletonLoader, GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
+import {
+ GlAlert,
+ GlSkeletonLoader,
+ GlLoadingIcon,
+ GlIcon,
+ GlBadge,
+ GlButton,
+ GlTooltipDirective,
+} from '@gitlab/ui';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import {
@@ -11,8 +19,12 @@ import {
WIDGET_TYPE_HIERARCHY,
WORK_ITEM_VIEWED_STORAGE_KEY,
} from '../constants';
+
import workItemQuery from '../graphql/work_item.query.graphql';
import workItemTitleSubscription from '../graphql/work_item_title.subscription.graphql';
+import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
+import updateWorkItemTaskMutation from '../graphql/update_work_item_task.mutation.graphql';
+
import WorkItemActions from './work_item_actions.vue';
import WorkItemState from './work_item_state.vue';
import WorkItemTitle from './work_item_title.vue';
@@ -29,7 +41,9 @@ export default {
},
components: {
GlAlert,
+ GlBadge,
GlButton,
+ GlLoadingIcon,
GlSkeletonLoader,
GlIcon,
WorkItemAssignees,
@@ -65,6 +79,7 @@ export default {
error: undefined,
workItem: {},
showInfoBanner: true,
+ updateInProgress: false,
};
},
apollo: {
@@ -125,6 +140,9 @@ export default {
parentWorkItem() {
return this.workItemHierarchy?.parent;
},
+ parentWorkItemConfidentiality() {
+ return this.parentWorkItem?.confidential;
+ },
parentUrl() {
return `../../issues/${this.parentWorkItem?.iid}`;
},
@@ -138,6 +156,54 @@ export default {
dismissBanner() {
this.showInfoBanner = false;
},
+ toggleConfidentiality(confidentialStatus) {
+ this.updateInProgress = true;
+ let updateMutation = updateWorkItemMutation;
+ let inputVariables = {
+ id: this.workItemId,
+ confidential: confidentialStatus,
+ };
+
+ if (this.parentWorkItem) {
+ updateMutation = updateWorkItemTaskMutation;
+ inputVariables = {
+ id: this.parentWorkItem.id,
+ taskData: {
+ id: this.workItemId,
+ confidential: confidentialStatus,
+ },
+ };
+ }
+
+ this.$apollo
+ .mutate({
+ mutation: updateMutation,
+ variables: {
+ input: inputVariables,
+ },
+ })
+ .then(
+ ({
+ data: {
+ workItemUpdate: { errors, workItem, task },
+ },
+ }) => {
+ if (errors?.length) {
+ throw new Error(errors[0]);
+ }
+
+ this.$emit('workItemUpdated', {
+ confidential: workItem?.confidential || task?.confidential,
+ });
+ },
+ )
+ .catch((error) => {
+ this.error = error.message;
+ })
+ .finally(() => {
+ this.updateInProgress = false;
+ });
+ },
},
WORK_ITEM_VIEWED_STORAGE_KEY,
};
@@ -156,7 +222,7 @@ export default {
</gl-skeleton-loader>
</div>
<template v-else>
- <div class="gl-display-flex gl-align-items-center">
+ <div class="gl-display-flex gl-align-items-center" data-testid="work-item-body">
<ul
v-if="parentWorkItem"
class="list-unstyled gl-display-flex gl-mr-auto gl-max-w-26 gl-md-max-w-50p gl-min-w-0 gl-mb-0"
@@ -187,10 +253,19 @@ export default {
data-testid="work-item-type"
>{{ workItemType }}</span
>
+ <gl-loading-icon v-if="updateInProgress" :inline="true" class="gl-mr-3" />
+ <gl-badge v-if="workItem.confidential" variant="warning" icon="eye-slash" class="gl-mr-3">{{
+ __('Confidential')
+ }}</gl-badge>
<work-item-actions
+ v-if="canUpdate || canDelete"
:work-item-id="workItem.id"
:can-delete="canDelete"
+ :can-update="canUpdate"
+ :is-confidential="workItem.confidential"
+ :is-parent-confidential="parentWorkItemConfidentiality"
@deleteWorkItem="$emit('deleteWorkItem')"
+ @toggleWorkItemConfidentiality="toggleConfidentiality"
@error="error = $event"
/>
<gl-button
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
index aa9cd2bbe4b..372bf7ec749 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
@@ -1,5 +1,5 @@
<script>
-import { GlButton, GlBadge, GlIcon, GlLoadingIcon } from '@gitlab/ui';
+import { GlButton, GlBadge, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { produce } from 'immer';
import { s__ } from '~/locale';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
@@ -30,6 +30,9 @@ export default {
WorkItemLinksMenu,
WorkItemDetailModal,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
inject: ['projectPath'],
props: {
workItemId: {
@@ -284,7 +287,15 @@ export default {
class="gl-relative gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row gl-overflow-break-word gl-min-w-0 gl-bg-white gl-mb-3 gl-py-3 gl-px-4 gl-border gl-border-gray-100 gl-rounded-base gl-line-height-32"
data-testid="links-child"
>
- <div class="gl-overflow-hidden">
+ <div class="gl-overflow-hidden gl-display-flex gl-align-items-center">
+ <gl-icon
+ v-if="child.confidential"
+ v-gl-tooltip.top
+ name="eye-slash"
+ class="gl-mr-3 gl-text-orange-500"
+ data-testid="confidential-icon"
+ :title="__('Confidential')"
+ />
<gl-icon :name="$options.WIDGET_TYPE_TASK_ICON" class="gl-mr-3 gl-text-gray-700" />
<gl-button
:href="childPath(child.id)"
diff --git a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql
index 7d08cbaca48..dacc1527a43 100644
--- a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql
@@ -5,6 +5,7 @@ fragment WorkItem on WorkItem {
title
state
description
+ confidential
workItemType {
id
name
@@ -34,6 +35,7 @@ fragment WorkItem on WorkItem {
id
iid
title
+ confidential
}
children {
nodes {
diff --git a/app/assets/javascripts/work_items/graphql/work_item_links.query.graphql b/app/assets/javascripts/work_items/graphql/work_item_links.query.graphql
index c30c907baeb..df62ca1c143 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_links.query.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item_links.query.graphql
@@ -20,6 +20,7 @@ query workItemQuery($id: WorkItemID!) {
children {
nodes {
id
+ confidential
workItemType {
id
}
diff --git a/app/graphql/mutations/ci/job/retry.rb b/app/graphql/mutations/ci/job/retry.rb
index 50e9c51c9e7..bfb9b902cc5 100644
--- a/app/graphql/mutations/ci/job/retry.rb
+++ b/app/graphql/mutations/ci/job/retry.rb
@@ -11,13 +11,20 @@ module Mutations
null: true,
description: 'Job after the mutation.'
+ argument :variables, [::Types::Ci::VariableInputType],
+ required: false,
+ default_value: [],
+ replace_null_with_default: true,
+ description: 'Variables to use when retrying a manual job.'
+
authorize :update_build
- def resolve(id:)
+ def resolve(id:, variables:)
job = authorized_find!(id: id)
project = job.project
+ variables = variables.map(&:to_h)
- response = ::Ci::RetryJobService.new(project, current_user).execute(job)
+ response = ::Ci::RetryJobService.new(project, current_user).execute(job, variables: variables)
if response.success?
{
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index 3444c76da08..4ea9a016e74 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -194,7 +194,7 @@ module Types
end
def manual_variables
- if object.manual? && object.respond_to?(:job_variables)
+ if object.action? && object.respond_to?(:job_variables)
object.job_variables
else
[]
diff --git a/app/graphql/types/ci/variable_input_type.rb b/app/graphql/types/ci/variable_input_type.rb
new file mode 100644
index 00000000000..193ca6ffe4e
--- /dev/null
+++ b/app/graphql/types/ci/variable_input_type.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class VariableInputType < BaseInputObject
+ graphql_name 'CiVariableInput'
+ description 'Attributes for defining a CI/CD variable.'
+
+ argument :key, GraphQL::Types::String, description: 'Name of the variable.'
+ argument :value, GraphQL::Types::String, description: 'Value of the variable.'
+ end
+ end
+end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index b267ea8f93f..b5dcb863e5b 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -1161,6 +1161,17 @@ module Ci
end
end
+ def clone(current_user:, new_job_variables_attributes: [])
+ new_build = super
+
+ if action? && new_job_variables_attributes.any?
+ new_build.job_variables = []
+ new_build.job_variables_attributes = new_job_variables_attributes
+ end
+
+ new_build
+ end
+
protected
def run_status_commit_hooks!
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 8b41ade90df..71d33f0bb63 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -22,7 +22,8 @@ module Ci
accessibility: %w[accessibility],
coverage: %w[cobertura],
codequality: %w[codequality],
- terraform: %w[terraform]
+ terraform: %w[terraform],
+ sbom: %w[cyclonedx]
}.freeze
DEFAULT_FILE_NAMES = {
diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb
index f666629c8fd..a2ff49077be 100644
--- a/app/models/ci/processable.rb
+++ b/app/models/ci/processable.rb
@@ -101,7 +101,7 @@ module Ci
:merge_train_pipeline?,
to: :pipeline
- def clone(current_user:)
+ def clone(current_user:, new_job_variables_attributes: [])
new_attributes = self.class.clone_accessors.to_h do |attribute|
[attribute, public_send(attribute)] # rubocop:disable GitlabSecurity/PublicSend
end
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
index 1aca763d57f..e10452c1081 100644
--- a/app/models/container_repository.rb
+++ b/app/models/container_repository.rb
@@ -19,6 +19,8 @@ class ContainerRepository < ApplicationRecord
MIGRATION_PHASE_1_STARTED_AT = Date.new(2021, 11, 4).freeze
MIGRATION_PHASE_1_ENDED_AT = Date.new(2022, 01, 23).freeze
+ MAX_TAGS_PAGES = 2000
+
TooManyImportsError = Class.new(StandardError)
belongs_to :project
@@ -377,6 +379,10 @@ class ContainerRepository < ApplicationRecord
migration_retries_count >= ContainerRegistry::Migration.max_retries - 1
end
+ def migrated?
+ MIGRATION_PHASE_1_ENDED_AT < self.created_at || import_done?
+ end
+
def last_import_step_done_at
[migration_pre_import_done_at, migration_import_done_at, migration_aborted_at, migration_skipped_at].compact.max
end
@@ -427,6 +433,32 @@ class ContainerRepository < ApplicationRecord
end
end
+ def each_tags_page(page_size: 100, &block)
+ raise ArgumentError, 'not a migrated repository' unless migrated?
+ raise ArgumentError, 'block not given' unless block
+
+ # dummy uri to initialize the loop
+ next_page_uri = URI('')
+ page_count = 0
+
+ while next_page_uri && page_count < MAX_TAGS_PAGES
+ last = Rack::Utils.parse_nested_query(next_page_uri.query)['last']
+ current_page = gitlab_api_client.tags(self.path, page_size: page_size, last: last)
+
+ if current_page&.key?(:response_body)
+ yield transform_tags_page(current_page[:response_body])
+ next_page_uri = current_page.dig(:pagination, :next, :uri)
+ else
+ # no current page. Break the loop
+ next_page_uri = nil
+ end
+
+ page_count += 1
+ end
+
+ raise 'too many pages requested' if page_count >= MAX_TAGS_PAGES
+ end
+
def tags_count
return 0 unless manifest && manifest['tags']
@@ -559,6 +591,16 @@ class ContainerRepository < ApplicationRecord
self.migration_skipped_reason = reason
finish_import
end
+
+ def transform_tags_page(tags_response_body)
+ return [] unless tags_response_body
+
+ tags_response_body.map do |raw_tag|
+ tag = ContainerRegistry::Tag.new(self, raw_tag['name'])
+ tag.force_created_at_from_iso8601(raw_tag['created_at'])
+ tag
+ end
+ end
end
ContainerRepository.prepend_mod_with('ContainerRepository')
diff --git a/app/services/ci/retry_job_service.rb b/app/services/ci/retry_job_service.rb
index e0ced3d0197..25bda8a6380 100644
--- a/app/services/ci/retry_job_service.rb
+++ b/app/services/ci/retry_job_service.rb
@@ -4,10 +4,10 @@ module Ci
class RetryJobService < ::BaseService
include Gitlab::Utils::StrongMemoize
- def execute(job)
+ def execute(job, variables: [])
if job.retryable?
job.ensure_scheduling_type!
- new_job = retry_job(job)
+ new_job = retry_job(job, variables: variables)
ServiceResponse.success(payload: { job: new_job })
else
@@ -19,7 +19,7 @@ module Ci
end
# rubocop: disable CodeReuse/ActiveRecord
- def clone!(job)
+ def clone!(job, variables: [])
# Cloning a job requires a strict type check to ensure
# the attributes being used for the clone are taken straight
# from the model and not overridden by other abstractions.
@@ -27,7 +27,7 @@ module Ci
check_access!(job)
- new_job = job.clone(current_user: current_user)
+ new_job = job.clone(current_user: current_user, new_job_variables_attributes: variables)
new_job.run_after_commit do
::Ci::CopyCrossDatabaseAssociationsService.new.execute(job, new_job)
@@ -55,8 +55,8 @@ module Ci
def check_assignable_runners!(job); end
- def retry_job(job)
- clone!(job).tap do |new_job|
+ def retry_job(job, variables: [])
+ clone!(job, variables: variables).tap do |new_job|
check_assignable_runners!(new_job) if new_job.is_a?(Ci::Build)
next if new_job.failed?