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-07-25 15:09:06 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-25 15:09:06 +0300
commitcc1e91be1cd930f58baebb89f2ff1893045a2aea (patch)
treead35ee30978a657f17d87fcd569f35337b96da12 /app
parent012ed4e4f69ab58f9d9b58140865a734fa5a9c88 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/boards/components/board_card_inner.vue1
-rw-r--r--app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js5
-rw-r--r--app/assets/javascripts/linked_resources/index.js2
-rw-r--r--app/assets/javascripts/test_utils/index.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/README.md64
-rw-r--r--app/assets/stylesheets/framework/files.scss4
-rw-r--r--app/controllers/profiles/personal_access_tokens_controller.rb2
-rw-r--r--app/models/ci/pipeline.rb76
-rw-r--r--app/models/personal_access_token.rb4
-rw-r--r--app/models/project.rb7
-rw-r--r--app/serializers/group_access_token_entity.rb26
-rw-r--r--app/serializers/group_access_token_serializer.rb7
-rw-r--r--app/serializers/personal_access_token_entity.rb11
-rw-r--r--app/serializers/personal_access_token_serializer.rb7
-rw-r--r--app/serializers/project_access_token_entity.rb27
-rw-r--r--app/serializers/project_access_token_serializer.rb7
-rw-r--r--app/services/ci/destroy_pipeline_service.rb2
-rw-r--r--app/workers/all_queues.yml9
-rw-r--r--app/workers/ci/cancel_pipeline_worker.rb25
19 files changed, 262 insertions, 26 deletions
diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue
index a632f5ae0ed..45b53b13a7f 100644
--- a/app/assets/javascripts/boards/components/board_card_inner.vue
+++ b/app/assets/javascripts/boards/components/board_card_inner.vue
@@ -367,6 +367,7 @@ export default {
:img-alt="avatarUrlTitle(assignee)"
:img-src="avatarUrl(assignee)"
:img-size="24"
+ img-css-classes="avatar gl-mr-0!"
class="js-no-trigger"
tooltip-placement="bottom"
>
diff --git a/app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js b/app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js
index e4ad0bf8e76..bc3cb163c39 100644
--- a/app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js
+++ b/app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js
@@ -1,3 +1,4 @@
+import { KeyMod, KeyCode } from 'monaco-editor';
import { debounce } from 'lodash';
import { BLOB_PREVIEW_ERROR } from '~/blob_edit/constants';
import createFlash from '~/flash';
@@ -158,8 +159,8 @@ export class EditorMarkdownPreviewExtension {
if (instance.getAction(EXTENSION_MARKDOWN_PREVIEW_ACTION_ID)) return;
const actionBasis = {
keybindings: [
- // eslint-disable-next-line no-bitwise,no-undef
- monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_P),
+ // eslint-disable-next-line no-bitwise
+ KeyMod.chord(KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_P),
],
contextMenuGroupId: 'navigation',
contextMenuOrder: 1.5,
diff --git a/app/assets/javascripts/linked_resources/index.js b/app/assets/javascripts/linked_resources/index.js
index 802364acff9..6d799d30b4b 100644
--- a/app/assets/javascripts/linked_resources/index.js
+++ b/app/assets/javascripts/linked_resources/index.js
@@ -27,8 +27,8 @@ export default function initLinkedResources() {
render: (createElement) =>
createElement('resource-links-block', {
props: {
- issuableId,
helpPath,
+ issuableId: parseInt(issuableId, 10),
canAddResourceLinks: parseBoolean(canAddResourceLinks),
},
}),
diff --git a/app/assets/javascripts/test_utils/index.js b/app/assets/javascripts/test_utils/index.js
index 2727485fb95..369cf9714e8 100644
--- a/app/assets/javascripts/test_utils/index.js
+++ b/app/assets/javascripts/test_utils/index.js
@@ -1,8 +1,10 @@
+import { editor } from 'monaco-editor';
import { Sortable } from 'sortablejs';
import simulateDrag from './simulate_drag';
import simulateInput from './simulate_input';
// Export to global space for rspec to use
+window.localMonaco = editor;
window.simulateDrag = simulateDrag;
window.simulateInput = simulateInput;
window.Sortable = Sortable;
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/README.md b/app/assets/javascripts/vue_merge_request_widget/components/extensions/README.md
new file mode 100644
index 00000000000..e5bb4e1af4c
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/README.md
@@ -0,0 +1,64 @@
+### Widget Extensions
+
+#### Telemetry
+
+Telemetry is enabled by default in all widget extensions.
+
+However, telemetry events are not reported until they have been marked as a "known event" with a Metric Dictionary.
+
+If telemetry metrics are desired when adding a widget extension, it is important to also create known events.
+
+The following steps are needed to generate these known events for a single widget:
+
+1. Widgets should be named `Widget${CamelName}`.
+ - For example: a widget for "Test Reports" should be `WidgetTestReports`
+1. "Compute" the widget name slug by converting the `${CamelName}` to lower-, snake-case.
+ - The above example would be `test_reports`
+1. Add the new widget name slug to `lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb` in the `WIDGETS` list.
+1. Ensure the GDK is running (`gdk start`)
+1. Generate known events on the command line with the following command. Replace `test_reports` with your appropriate name slug.
+ ```
+ bundle exec rails generate gitlab:usage_metric_definition \
+ counts.code_review.i_merge_request_widget_test_reports_count_view \
+ counts.code_review.i_merge_request_widget_test_reports_count_full_report_clicked \
+ counts.code_review.i_merge_request_widget_test_reports_count_expand \
+ counts.code_review.i_merge_request_widget_test_reports_count_expand_success \
+ counts.code_review.i_merge_request_widget_test_reports_count_expand_warning \
+ counts.code_review.i_merge_request_widget_test_reports_count_expand_failed \
+ --dir=all
+ ```
+1. Modify each newly generated file so that they match the existing files for MR Widget Extension telemetry.
+ - You can find existing examples by doing a glob search like so: `metrics/**/*_i_code_review_merge_request_widget_*`
+ - Roughly-speaking, each file should have these values:
+ 1. `description` = A plain English description of this value. Please see existing widget extension telemetry files for examples.
+ 1. `product_section` = `dev`
+ 1. `product_stage` = `create`
+ 1. `product_group` = `code_review`
+ 1. `product_category` = `code_review`
+ 1. `introduced_by_url` = `'[your MR]'`
+ 1. `options.events` = (the event in the command from above that generated this file, like `i_code_review_merge_request_widget_test_reports_count_view`)
+ - This is how the telemetry events are linked to "metrics" so this is probably one of the more important values
+ 1. `data_source` = `redis`
+ 1. `data_category` = `optional`
+1. Repeat steps 5 and 6 for the HLL metrics. Replace `test_reports` with your appropriate name slug.
+ ```
+ bundle exec rails generate gitlab:usage_metric_definition:redis_hll code_review \
+ i_code_review_merge_request_widget_test_reports_view \
+ i_code_review_merge_request_widget_test_reports_full_report_clicked \
+ i_code_review_merge_request_widget_test_reports_expand \
+ i_code_review_merge_request_widget_test_reports_expand_success \
+ i_code_review_merge_request_widget_test_reports_expand_warning \
+ i_code_review_merge_request_widget_test_reports_expand_failed \
+ --class_name=RedisHLLMetric
+ ```
+ - In step 6 for HLL, change the `data_source` to `redis_hll`.
+1. Add each of the HLL metrics to `lib/gitlab/usage_data_counters/known_events/code_review_events.yml`
+ 1. `name` = [the event]
+ 1. `redis_slot` = `code_review`
+ 1. `category` = `code_review`
+ 1. `aggregation` = `weekly`
+1. Add each event to the appropriate aggregates in `config/metrics/aggregates/code_review.yml`
+
+##### New Events
+
+If you are adding a new event to our known events, it will need to be included in `lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb`. Update the list of `KNOWN_EVENTS` with the new event(s).
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 41265de0ca0..00ae509ad15 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -576,10 +576,10 @@ span.idiff {
}
}
-// *:nth-of-type(1n+5) - makes sure we do not render elements 5+ right away when
+// *:nth-of-type(1n+30) - makes sure we do not render elements 30+ right away when
// viewing a file. Even though the HTML is injected in the DOM, as long as we do
// not render those elements, the browser doesn't need to spend resources
// calculating and repainting what's hidden.
-.file-holder [data-loading] .file-content *:nth-of-type(1n+5) {
+.file-holder [data-loading] .file-content *:nth-of-type(1n+30) {
@include gl-display-none;
}
diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb
index a8d8e1e38a3..07d786ab060 100644
--- a/app/controllers/profiles/personal_access_tokens_controller.rb
+++ b/app/controllers/profiles/personal_access_tokens_controller.rb
@@ -58,7 +58,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
end
def active_personal_access_tokens
- tokens = finder(state: 'active', sort: 'expires_at_asc').execute
+ tokens = finder(state: 'active', sort: 'expires_at_asc_id_desc').execute
if Feature.enabled?('access_token_pagination')
tokens = tokens.page(page)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 0c1cc6eb1b0..3a8e68c58a6 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -102,6 +102,7 @@ module Ci
has_one :chat_data, class_name: 'Ci::PipelineChatData'
has_many :triggered_pipelines, through: :sourced_pipelines, source: :pipeline
+ # Only includes direct and not nested children
has_many :child_pipelines, -> { merge(Ci::Sources::Pipeline.same_project) }, through: :sourced_pipelines, source: :pipeline
has_one :triggered_by_pipeline, through: :source_pipeline, source: :source_pipeline
has_one :parent_pipeline, -> { merge(Ci::Sources::Pipeline.same_project) }, through: :source_pipeline, source: :source_pipeline
@@ -592,26 +593,20 @@ module Ci
canceled? && auto_canceled_by_id?
end
- def cancel_running(retries: 1)
- preloaded_relations = [:project, :pipeline, :deployment, :taggings]
+ # Cancel a pipelines cancelable jobs and optionally it's child pipelines cancelable jobs
+ # retries - # of times to retry if errors
+ # cascade_to_children - if true cancels all related child pipelines for parent child pipelines
+ # auto_canceled_by_pipeline_id - store the pipeline_id of the pipeline that triggered cancellation
+ # execute_async - if true cancel the children asyncronously
+ def cancel_running(retries: 1, cascade_to_children: true, auto_canceled_by_pipeline_id: nil, execute_async: true)
+ update(auto_canceled_by_id: auto_canceled_by_pipeline_id) if auto_canceled_by_pipeline_id
- retry_lock(cancelable_statuses, retries, name: 'ci_pipeline_cancel_running') do |cancelables|
- cancelables.find_in_batches do |batch|
- Preloaders::CommitStatusPreloader.new(batch).execute(preloaded_relations)
+ cancel_jobs(cancelable_statuses, retries: retries, auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id)
- batch.each do |job|
- yield(job) if block_given?
- job.cancel
- end
- end
- end
- end
-
- def auto_cancel_running(pipeline, retries: 1)
- update(auto_canceled_by: pipeline)
-
- cancel_running(retries: retries) do |job|
- job.auto_canceled_by = pipeline
+ if cascade_to_children && project.cascade_cancel_pipelines_enabled?
+ # cancel any bridges that could spin up new child pipelines
+ cancel_jobs(bridges_in_self_and_descendants.cancelable, retries: retries, auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id)
+ cancel_children(auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id, execute_async: execute_async)
end
end
@@ -953,6 +948,10 @@ module Ci
Ci::Build.latest.where(pipeline: self_and_descendants)
end
+ def bridges_in_self_and_descendants
+ Ci::Bridge.latest.where(pipeline: self_and_descendants)
+ end
+
def environments_in_self_and_descendants(deployment_status: nil)
# We limit to 100 unique environments for application safety.
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/340781#note_699114700
@@ -986,6 +985,11 @@ module Ci
object_hierarchy(project_condition: :same).base_and_descendants
end
+ # With only parent-child pipelines
+ def all_child_pipelines
+ object_hierarchy(project_condition: :same).descendants
+ end
+
def self_and_descendants_complete?
self_and_descendants.all?(&:complete?)
end
@@ -1323,6 +1327,42 @@ module Ci
private
+ def cancel_jobs(jobs, retries: 1, auto_canceled_by_pipeline_id: nil)
+ retry_lock(jobs, retries, name: 'ci_pipeline_cancel_running') do |statuses|
+ preloaded_relations = [:project, :pipeline, :deployment, :taggings]
+
+ statuses.find_in_batches do |status_batch|
+ relation = CommitStatus.where(id: status_batch)
+ Preloaders::CommitStatusPreloader.new(relation).execute(preloaded_relations)
+
+ relation.each do |job|
+ job.auto_canceled_by_id = auto_canceled_by_pipeline_id if auto_canceled_by_pipeline_id
+ job.cancel
+ end
+ end
+ end
+ end
+
+ # For parent child-pipelines only (not multi-project)
+ def cancel_children(auto_canceled_by_pipeline_id: nil, execute_async: true)
+ all_child_pipelines.each do |child_pipeline|
+ if execute_async
+ ::Ci::CancelPipelineWorker.perform_async(
+ child_pipeline.id,
+ auto_canceled_by_pipeline_id
+ )
+ else
+ child_pipeline.cancel_running(
+ # cascade_to_children is false because we iterate through children
+ # we also cancel bridges prior to prevent more children
+ cascade_to_children: false,
+ execute_async: execute_async,
+ auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id
+ )
+ end
+ end
+ end
+
def add_message(severity, content)
messages.build(severity: severity, content: content)
end
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 68ba3d6eab4..31e5e6f73bf 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -33,6 +33,7 @@ class PersonalAccessToken < ApplicationRecord
scope :preload_users, -> { preload(:user) }
scope :order_expires_at_asc, -> { reorder(expires_at: :asc) }
scope :order_expires_at_desc, -> { reorder(expires_at: :desc) }
+ scope :order_expires_at_asc_id_desc, -> { reorder(expires_at: :asc, id: :desc) }
scope :project_access_token, -> { includes(:user).where(user: { user_type: :project_bot }) }
scope :owner_is_human, -> { includes(:user).where(user: { user_type: :human }) }
@@ -77,7 +78,8 @@ class PersonalAccessToken < ApplicationRecord
super.merge(
{
'expires_at_asc' => -> { order_expires_at_asc },
- 'expires_at_desc' => -> { order_expires_at_desc }
+ 'expires_at_desc' => -> { order_expires_at_desc },
+ 'expires_at_asc_id_desc' => -> { order_expires_at_asc_id_desc }
}
)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index c8d7afdd46f..39a7541e5d3 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1041,6 +1041,13 @@ class Project < ApplicationRecord
def emails_enabled?
!emails_disabled?
end
+
+ def cascade_cancel_pipelines_enabled?
+ strong_memoize(:cascade_cancel_pipelines_enabled) do
+ Feature.enabled?(:ci_parent_pipeline_cancels_children, self)
+ end
+ end
+
override :lfs_enabled?
def lfs_enabled?
return namespace.lfs_enabled? if self[:lfs_enabled].nil?
diff --git a/app/serializers/group_access_token_entity.rb b/app/serializers/group_access_token_entity.rb
new file mode 100644
index 00000000000..e832eef1188
--- /dev/null
+++ b/app/serializers/group_access_token_entity.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+# rubocop: disable Gitlab/NamespacedClass
+class GroupAccessTokenEntity < API::Entities::PersonalAccessToken
+ include Gitlab::Routing
+
+ expose :revoke_path do |token, options|
+ group = options.fetch(:group)
+
+ next unless group
+
+ revoke_group_settings_access_token_path(
+ id: token,
+ group_id: group.path)
+ end
+
+ expose :access_level do |token, options|
+ group = options.fetch(:group)
+
+ next unless group
+ next unless token.user
+
+ group.member(token.user)&.access_level
+ end
+end
+# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/group_access_token_serializer.rb b/app/serializers/group_access_token_serializer.rb
new file mode 100644
index 00000000000..55f6de77844
--- /dev/null
+++ b/app/serializers/group_access_token_serializer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+# rubocop: disable Gitlab/NamespacedClass
+class GroupAccessTokenSerializer < BaseSerializer
+ entity GroupAccessTokenEntity
+end
+# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/personal_access_token_entity.rb b/app/serializers/personal_access_token_entity.rb
new file mode 100644
index 00000000000..acd06fecd12
--- /dev/null
+++ b/app/serializers/personal_access_token_entity.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+# rubocop: disable Gitlab/NamespacedClass
+class PersonalAccessTokenEntity < API::Entities::PersonalAccessToken
+ include Gitlab::Routing
+
+ expose :revoke_path do |token, options|
+ revoke_profile_personal_access_token_path(token)
+ end
+end
+# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/personal_access_token_serializer.rb b/app/serializers/personal_access_token_serializer.rb
new file mode 100644
index 00000000000..0a59fa117f9
--- /dev/null
+++ b/app/serializers/personal_access_token_serializer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+# rubocop: disable Gitlab/NamespacedClass
+class PersonalAccessTokenSerializer < BaseSerializer
+ entity PersonalAccessTokenEntity
+end
+# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/project_access_token_entity.rb b/app/serializers/project_access_token_entity.rb
new file mode 100644
index 00000000000..b317057c952
--- /dev/null
+++ b/app/serializers/project_access_token_entity.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+# rubocop: disable Gitlab/NamespacedClass
+class ProjectAccessTokenEntity < API::Entities::PersonalAccessToken
+ include Gitlab::Routing
+
+ expose :revoke_path do |token, options|
+ project = options.fetch(:project)
+
+ next unless project
+
+ revoke_namespace_project_settings_access_token_path(
+ id: token,
+ namespace_id: project.namespace.path,
+ project_id: project.path)
+ end
+
+ expose :access_level do |token, options|
+ project = options.fetch(:project)
+
+ next unless project
+ next unless token.user
+
+ project.member(token.user)&.access_level
+ end
+end
+# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/project_access_token_serializer.rb b/app/serializers/project_access_token_serializer.rb
new file mode 100644
index 00000000000..97db088cf64
--- /dev/null
+++ b/app/serializers/project_access_token_serializer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+# rubocop: disable Gitlab/NamespacedClass
+class ProjectAccessTokenSerializer < BaseSerializer
+ entity ProjectAccessTokenEntity
+end
+# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/services/ci/destroy_pipeline_service.rb b/app/services/ci/destroy_pipeline_service.rb
index d85e52e1312..1c563396162 100644
--- a/app/services/ci/destroy_pipeline_service.rb
+++ b/app/services/ci/destroy_pipeline_service.rb
@@ -7,7 +7,7 @@ module Ci
Ci::ExpirePipelineCacheService.new.execute(pipeline, delete: true)
- pipeline.cancel_running if pipeline.cancelable?
+ pipeline.cancel_running(cascade_to_children: true, execute_async: false) if pipeline.cancelable?
# The pipeline, the builds, job and pipeline artifacts all get destroyed here.
# Ci::Pipeline#destroy triggers fast destroy on job_artifacts and
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index a1a5248c060..22503531008 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -2109,6 +2109,15 @@
:weight: 2
:idempotent: false
:tags: []
+- :name: ci_cancel_pipeline
+ :worker_name: Ci::CancelPipelineWorker
+ :feature_category: :continuous_integration
+ :has_external_dependencies: false
+ :urgency: :high
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: ci_delete_objects
:worker_name: Ci::DeleteObjectsWorker
:feature_category: :continuous_integration
diff --git a/app/workers/ci/cancel_pipeline_worker.rb b/app/workers/ci/cancel_pipeline_worker.rb
new file mode 100644
index 00000000000..147839a0625
--- /dev/null
+++ b/app/workers/ci/cancel_pipeline_worker.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Ci
+ class CancelPipelineWorker
+ include ApplicationWorker
+
+ # lots of updates to ci_builds
+ data_consistency :always
+ feature_category :continuous_integration
+ idempotent!
+ deduplicate :until_executed
+ urgency :high
+
+ def perform(pipeline_id, auto_canceled_by_pipeline_id)
+ ::Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline|
+ pipeline.cancel_running(
+ # cascade_to_children is false because we iterate through children
+ # we also cancel bridges prior to prevent more children
+ cascade_to_children: false,
+ auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id
+ )
+ end
+ end
+ end
+end