Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-03-07 06:09:47 +00:00
parent 3e0178f80c
commit b8ff7c8f92
41 changed files with 565 additions and 219 deletions

View File

@ -289,7 +289,7 @@ export default {
</div>
</fieldset>
<template v-else>
<p class="gl-m-0 gl-py-1">
<p class="gl-m-0 gl-pb-1">
{{ $options.i18n.startDate }}:
<span data-testid="start-date-value" :class="{ 'gl-text-secondary': !startDate }">
{{ startDateValue }}

View File

@ -260,18 +260,19 @@ export default {
</span>
</template>
<template #readonly>
<gl-label
v-for="label in localLabels"
:key="label.id"
class="gl-mr-2 gl-mb-2"
:title="label.title"
:description="label.description"
:background-color="label.color"
:scoped="scopedLabel(label)"
:show-close-button="canUpdate"
:target="labelFilterUrl(label)"
@close="removeLabel(label)"
/>
<div class="gl-display-flex gl-gap-2 gl-flex-wrap gl-mt-1">
<gl-label
v-for="label in localLabels"
:key="label.id"
:title="label.title"
:description="label.description"
:background-color="label.color"
:scoped="scopedLabel(label)"
:show-close-button="canUpdate"
:target="labelFilterUrl(label)"
@close="removeLabel(label)"
/>
</div>
</template>
</work-item-sidebar-dropdown-widget-with-edit>
</template>

View File

@ -259,7 +259,7 @@ class Milestone < ApplicationRecord
if project
"#{project.to_reference_base(from, full: full)}#{reference}"
else
reference
"#{group.to_reference_base(from, full: full)}#{reference}"
end
end

View File

@ -1001,6 +1001,7 @@ class Project < ApplicationRecord
def reference_pattern
%r{
(?<!#{Gitlab::PathRegex::PATH_START_CHAR})
(?<absolute_path>/)?
((?<namespace>#{Gitlab::PathRegex::FULL_NAMESPACE_FORMAT_REGEX})/)?
(?<project>#{Gitlab::PathRegex::PROJECT_PATH_FORMAT_REGEX})
}xo

View File

@ -1,6 +1,6 @@
---
description: Counts Download Payload button clicks
category: InternalEventTracking
internal_events: true
action: usage_data_download_payload_clicked
identifiers:
- user

View File

@ -22,19 +22,20 @@ All event definitions are stored in the following directories:
Each event is defined in a separate YAML file consisting of the following fields:
| Field | Required | Additional information |
|------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `description` | yes | A description of the event. |
| `category` | yes | Always InternalEventTracking (only different for legacy events). |
| `action` | yes | A unique name for the event. Only lowercase, numbers, and underscores are allowed. Use the format `<operation>_<target_of_operation>_<where/when>`. <br/><br/> Ex: `publish_go_module_to_the_registry_from_pipeline` <br/>`<operation> = publish`<br/>`<target> = go_module`<br/>`<when/where> = to_the_registry_from_pipeline`. |
| `identifiers` | no | A list of identifiers sent with the event. Can be set to one or more of `project`, `user`, or `namespace`. |
| `product_section` | yes | The [section](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/sections.yml). |
| `product_stage` | no | The [stage](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml) for the event. |
| `product_group` | yes | The [group](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml) that owns the event. |
| `milestone` | no | The milestone when the event is introduced. |
| `introduced_by_url` | no | The URL to the merge request that introduced the event. |
| `distributions` | yes | The [distributions](https://handbook.gitlab.com/handbook/marketing/brand-and-product-marketing/product-and-solution-marketing/tiers/#definitions) where the tracked feature is available. Can be set to one or more of `ce` or `ee`. |
| `tiers` | yes | The [tiers](https://handbook.gitlab.com/handbook/marketing/brand-and-product-marketing/product-and-solution-marketing/tiers/) where the tracked feature is available. Can be set to one or more of `free`, `premium`, or `ultimate`. |
| Field | Required | Additional information |
|---------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `description` | yes | A description of the event. |
| `internal_events` | no | Always `true` for events used in Internal Events. |
| `category` | no | Required for legacy events. Should not be used for Internal Events. |
| `action` | yes | A unique name for the event. Only lowercase, numbers, and underscores are allowed. Use the format `<operation>_<target_of_operation>_<where/when>`. <br/><br/> Ex: `publish_go_module_to_the_registry_from_pipeline` <br/>`<operation> = publish`<br/>`<target> = go_module`<br/>`<when/where> = to_the_registry_from_pipeline`. |
| `identifiers` | no | A list of identifiers sent with the event. Can be set to one or more of `project`, `user`, or `namespace`. |
| `product_section` | yes | The [section](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/sections.yml). |
| `product_stage` | no | The [stage](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml) for the event. |
| `product_group` | yes | The [group](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml) that owns the event. |
| `milestone` | no | The milestone when the event is introduced. |
| `introduced_by_url` | no | The URL to the merge request that introduced the event. |
| `distributions` | yes | The [distributions](https://handbook.gitlab.com/handbook/marketing/brand-and-product-marketing/product-and-solution-marketing/tiers/#definitions) where the tracked feature is available. Can be set to one or more of `ce` or `ee`. |
| `tiers` | yes | The [tiers](https://handbook.gitlab.com/handbook/marketing/brand-and-product-marketing/product-and-solution-marketing/tiers/) where the tracked feature is available. Can be set to one or more of `free`, `premium`, or `ultimate`. |
### Example event definition
@ -42,7 +43,7 @@ This is an example YAML file for an internal event:
```yaml
description: A user visited a product analytics dashboard
category: InternalEventTracking
internal_events: true
action: visit_product_analytics_dashboard
identifiers:
- project

View File

@ -712,9 +712,9 @@ GitLab Flavored Markdown recognizes the following:
| Label by name (one word) | `~bug` | `namespace/project~bug` | `project~bug` |
| Label by name (multiple words) | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` |
| Label by name (scoped) | `~"priority::high"` | `namespace/project~"priority::high"` | `project~"priority::high"` |
| Project milestone by ID | `%123` | `namespace/project%123` | `project%123` |
| Milestone by name (one word) | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` |
| Milestone by name (multiple words) | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` |
| Project milestone by ID <sup>2</sup> | `%123` | `namespace/project%123` | `project%123` |
| Milestone by name (one word) <sup>2</sup> | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` |
| Milestone by name (multiple words) <sup>2</sup> | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` |
| Commit (specific) | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` |
| Commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` |
| Repository file reference | `[README](doc/README.md)` | | |
@ -722,14 +722,14 @@ GitLab Flavored Markdown recognizes the following:
| [Alert](../operations/incident_management/alerts.md) | `^alert#123` | `namespace/project^alert#123` | `project^alert#123` |
| [Contact](crm/index.md#contacts) | `[contact:test@example.com]` | | |
<ol>
<li>
<small>
<a href="https://gitlab.com/gitlab-org/gitlab/-/issues/384885">Introduced</a> in GitLab 16.9. Iteration cadence references are always rendered following the format <code>[cadence:&lt;ID>]</code>.
For example, the text reference <code>[cadence:"plan"]</code> renders as <code>[cadence:1]</code> if the referenced iterations cadence's ID is <code>1</code>.
</small>
</li>
</ol>
**Footnotes:**
1. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384885) in GitLab 16.9.
Iteration cadence references are always rendered following the format `[cadence:<ID>]`.
For example, the text reference `[cadence:"plan"]` renders as `[cadence:1]` if the referenced
iterations cadence's ID is `1`.
1. For milestones, prepend a `/` before `namespace/project` to specify the exact milestone,
removing any possible ambiguity.
For example, referencing an issue by using `#123` formats the output as a link
to issue number 123 with text `#123`. Likewise, a link to issue number 123 is

View File

@ -17,6 +17,13 @@ module Gitlab
# @attr [Float] duration
Result = Struct.new(:stdout, :stderr, :status, :duration, keyword_init: true)
# Result data structure from running a command in single pipeline mode
#
# @attr [String] stderr
# @attr [Process::Status] status
# @attr [Float] duration
SinglePipelineResult = Struct.new(:stderr, :status, :duration, keyword_init: true)
# @example Usage
# Shell.new('echo', 'Some amazing output').capture
# @param [Array<String>] cmd_args
@ -36,6 +43,33 @@ module Gitlab
Result.new(stdout: stdout, stderr: stderr, status: status, duration: duration)
end
# Run single command in pipeline mode with optional input or output redirection
#
# @param [IO|String|Array] input stdin redirection
# @param [IO|String|Array] output stdout redirection
# @return [Command::SinglePipelineResult]
def run_single_pipeline!(input: nil, output: nil)
start = Time.now
# Open3 writes on `err_write` and we receive from `err_read`
err_read, err_write = IO.pipe
# Pipeline accepts custom {Process.spawn} options
# stderr capture is always performed, stdin and stdout redirection
# are performed only when either `input` or `output` are present
options = { err: err_write } # redirect stderr to IO pipe
options[:in] = input if input # redirect stdin
options[:out] = output if output # redirect stdout
status_list = Open3.pipeline(cmd_args, **options)
duration = Time.now - start
err_write.close # close the pipe before reading
stderr = err_read.read
err_read.close # close after reading to avoid leaking file descriptors
SinglePipelineResult.new(stderr: stderr, status: status_list[0], duration: duration)
end
end
end
end

View File

@ -1,6 +1,6 @@
---
description: <%= args.last %>
category: InternalEventTracking
internal_events: true
action: <%= event %>
label_description:
property_description:

View File

@ -725,7 +725,7 @@ module API
Gitlab::AppLogger.warn("Redis tracking event failed for event: #{event_name}, message: #{error.message}")
end
def track_event(event_name, user:, send_snowplow_event: true, namespace_id: nil, project_id: nil)
def track_event(event_name, user:, send_snowplow_event: true, namespace_id: nil, project_id: nil, additional_properties: Gitlab::InternalEvents::DEFAULT_ADDITIONAL_PROPERTIES)
return unless user.present?
namespace = Namespace.find(namespace_id) if namespace_id
@ -734,6 +734,7 @@ module API
Gitlab::InternalEvents.track_event(
event_name,
send_snowplow_event: send_snowplow_event,
additional_properties: additional_properties,
user: user,
namespace: namespace,
project: project

View File

@ -104,13 +104,16 @@ module API
event_name = params[:event]
namespace_id = params[:namespace_id]
project_id = params[:project_id]
default_additional_properties = Gitlab::InternalEvents::DEFAULT_ADDITIONAL_PROPERTIES
additional_properties = params.fetch(:additional_properties, default_additional_properties)
track_event(
event_name,
send_snowplow_event: false,
user: current_user,
namespace_id: namespace_id,
project_id: project_id
project_id: project_id,
additional_properties: additional_properties.symbolize_keys
)
status :ok

View File

@ -5,6 +5,9 @@ module Backup
FILE_NAME_SUFFIX = '_gitlab_backup.tar'
MANIFEST_NAME = 'backup_information.yml'
# Use the content from stdin instead of an actual filepath (used by tar as input or output)
USE_STDIN = '-'
attr_reader :progress, :remote_storage, :options
def initialize(progress, backup_tasks: nil)
@ -237,10 +240,20 @@ module Backup
Dir.chdir(backup_path) do
# create archive
puts_time "Creating backup archive: #{tar_file} ... ".color(:blue)
tar_utils = ::Gitlab::Backup::Cli::Utils::Tar.new
tar_command = tar_utils.pack_cmd(
archive_file: USE_STDIN,
target_directory: backup_path,
target: backup_contents)
# Set file permissions on open to prevent chmod races.
archive_permissions = Gitlab.config.backup.archive_permissions
tar_system_options = { out: [tar_file, 'w', archive_permissions] }
if Kernel.system('tar', '-cf', '-', *backup_contents, tar_system_options)
archive_file = [tar_file, 'w', archive_permissions]
result = tar_command.run_single_pipeline!(output: archive_file)
if result.status.success?
puts_time "Creating backup archive: #{tar_file} ... ".color(:blue) + 'done'.color(:green)
else
puts_time "Creating archive #{tar_file} failed".color(:red)

View File

@ -10,8 +10,8 @@ module Backup
DEFAULT_EXCLUDE = ['lost+found'].freeze
# Use the content from a PIPE instead of an actual filepath (used by tar as input or output)
USE_PIPE_INSTEAD_OF_FILE = '-'
# Use the content from stdin instead of an actual filepath (used by tar as input or output)
USE_STDIN = '-'
attr_reader :excludes
@ -54,7 +54,7 @@ module Backup
archive_file = [backup_tarball, 'w', 0o600]
tar_command = tar_utils.pack_cmd(
archive_file: USE_PIPE_INSTEAD_OF_FILE,
archive_file: USE_STDIN,
target_directory: backup_files_realpath,
target: '.',
excludes: excludes)
@ -64,7 +64,7 @@ module Backup
else
archive_file = [backup_tarball, 'w', 0o600]
tar_command = tar_utils.pack_cmd(
archive_file: USE_PIPE_INSTEAD_OF_FILE,
archive_file: USE_STDIN,
target_directory: storage_realpath,
target: '.',
excludes: excludes)
@ -91,7 +91,7 @@ module Backup
archive_file = backup_tarball.to_s
tar_command = tar_utils.extract_cmd(
archive_file: USE_PIPE_INSTEAD_OF_FILE,
archive_file: USE_STDIN,
target_directory: storage_realpath)
result = shell_pipeline.new(decompress_command, tar_command).run_pipeline!(input: archive_file)

View File

@ -184,7 +184,7 @@ module Banzai
parent_path = if parent_type == :group
reference_cache.full_group_path(namespace_ref)
else
reference_cache.full_project_path(namespace_ref, project_ref)
reference_cache.full_project_path(namespace_ref, project_ref, matches)
end
parent = from_ref_cached(parent_path)

View File

@ -109,7 +109,7 @@ module Banzai
parent = project || group
if project || full_path_ref?(matches)
project_path = reference_cache.full_project_path(matches[:namespace], matches[:project])
project_path = reference_cache.full_project_path(matches[:namespace], matches[:project], matches)
parent_from_ref = from_ref_cached(project_path)
reference = parent_from_ref.to_human_reference(parent)

View File

@ -11,17 +11,24 @@ module Banzai
def parent_records(parent, ids)
return Milestone.none unless valid_context?(parent)
milestone_iids = ids.map { |y| y[:milestone_iid] }.compact
unless milestone_iids.empty?
iid_relation = find_milestones(parent, true).where(iid: milestone_iids)
relation = []
# We need to handle relative and absolute paths separately
milestones_absolute_indexed = ids.group_by { |id| id[:absolute_path] }
milestones_absolute_indexed.each do |absolute_path, fitered_ids|
milestone_iids = fitered_ids&.pluck(:milestone_iid)&.compact
if milestone_iids.present?
relation << find_milestones(parent, true, absolute_path: absolute_path).where(iid: milestone_iids)
end
milestone_names = fitered_ids&.pluck(:milestone_name)&.compact
if milestone_names.present?
relation << find_milestones(parent, false, absolute_path: absolute_path).where(name: milestone_names)
end
end
milestone_names = ids.map { |y| y[:milestone_name] }.compact
unless milestone_names.empty?
milestone_relation = find_milestones(parent, false).where(name: milestone_names)
end
relation = [iid_relation, milestone_relation].compact
relation.compact!
return Milestone.none if relation.all?(Milestone.none)
Milestone.from_union(relation).includes(:project, :group)
@ -44,12 +51,14 @@ module Banzai
# or the milestone_name, but not both. But below, we have both pieces of information.
# But it's accounted for in `find_object`
def parse_symbol(symbol, match_data)
absolute_path = !!match_data&.named_captures&.fetch('absolute_path')
if symbol
# when parsing links, there is no `match_data[:milestone_iid]`, but `symbol`
# holds the iid
{ milestone_iid: symbol.to_i, milestone_name: nil }
{ milestone_iid: symbol.to_i, milestone_name: nil, absolute_path: absolute_path }
else
{ milestone_iid: match_data[:milestone_iid]&.to_i, milestone_name: match_data[:milestone_name]&.tr('"', '') }
{ milestone_iid: match_data[:milestone_iid]&.to_i, milestone_name: match_data[:milestone_name]&.tr('"', ''), absolute_path: absolute_path }
end
end
@ -97,27 +106,27 @@ module Banzai
escape_with_placeholders(unescaped_html, milestones)
end
def find_milestones(parent, find_by_iid = false)
finder_params = milestone_finder_params(parent, find_by_iid)
def find_milestones(parent, find_by_iid = false, absolute_path: false)
finder_params = milestone_finder_params(parent, find_by_iid, absolute_path)
MilestonesFinder.new(finder_params).execute
end
def milestone_finder_params(parent, find_by_iid)
def milestone_finder_params(parent, find_by_iid, absolute_path)
{ order: nil, state: 'all' }.tap do |params|
params[:project_ids] = parent.id if project_context?(parent)
# We don't support IID lookups because IIDs can clash between
# group/project milestones and group/subgroup milestones.
params[:group_ids] = group_and_ancestors_ids(parent) unless find_by_iid
params[:group_ids] = group_and_ancestors_ids(parent, absolute_path) unless find_by_iid
end
end
def group_and_ancestors_ids(parent)
def group_and_ancestors_ids(parent, absolute_path)
if group_context?(parent)
parent.self_and_ancestors.select(:id)
absolute_path ? parent.id : parent.self_and_ancestors.select(:id)
elsif project_context?(parent)
parent.group&.self_and_ancestors&.select(:id)
absolute_path ? nil : parent.group&.self_and_ancestors&.select(:id)
end
end

View File

@ -45,11 +45,17 @@ module Banzai
end
end
def full_project_path(namespace, project_ref)
def full_project_path(namespace, project_ref, matches = nil)
return current_parent_path unless project_ref
namespace_ref = namespace || current_project_namespace_path
"#{namespace_ref}/#{project_ref}"
matched_absolute_path = matches&.named_captures&.fetch('absolute_path')
namespace ||= current_project_namespace_path unless matched_absolute_path
full_path = []
full_path << '/' if matched_absolute_path
full_path << "#{namespace}/" if namespace
full_path << project_ref
full_path.join
end
def full_group_path(group_ref)
@ -80,10 +86,10 @@ module Banzai
next unless pattern
prepare_doc_for_scan.to_enum(:scan, pattern).each do
parent_path = if parent_type == :project
full_project_path($~[:namespace], $~[:project])
else
parent_path = if parent_type == :group
full_group_path($~[:group])
else
full_project_path($~[:namespace], $~[:project], $~)
end
ident = filter.identifier($~)
@ -101,7 +107,7 @@ module Banzai
@per_reference ||= {}
@per_reference[parent_type] ||= begin
refs = references_per_parent.keys
refs = references_per_parent.keys.compact
parent_ref = {}
# if we already have a parent, no need to query it again
@ -117,7 +123,12 @@ module Banzai
refs -= [ref] if parent_ref[ref]
end
find_for_paths(refs).index_by(&:full_path).merge(parent_ref)
absolute_paths = refs.filter_map { |ref| ref if ref[0] == '/' }
relative_paths = refs - absolute_paths
find_for_paths(relative_paths, false).index_by(&:full_path)
.merge(find_for_paths(absolute_paths, true).index_by { |object| "/#{object.full_path}" })
.merge(parent_ref)
end
end
@ -140,41 +151,60 @@ module Banzai
end
# Returns projects for the given paths.
def find_for_paths(paths)
def find_for_paths(paths, absolute_path = false)
return [] if paths.empty?
if Gitlab::SafeRequestStore.active?
cache = refs_cache
to_query = paths - cache.keys
unless to_query.empty?
records = objects_for_paths(to_query)
found = []
records.each do |record|
ref = record.full_path
get_or_set_cache(cache, ref) { record }
found << ref
end
not_found = to_query - found
not_found.each do |ref|
get_or_set_cache(cache, ref) { nil }
end
end
cache.slice(*paths).values.compact
cached_objects_for_paths(paths, absolute_path)
else
objects_for_paths(paths)
objects_for_paths(paths, absolute_path)
end
end
def objects_for_paths(paths)
def cached_objects_for_paths(paths, absolute_path)
cache = refs_cache
to_query = paths - cache.keys
unless to_query.empty?
records = objects_for_paths(to_query, absolute_path)
found = []
records.each do |record|
ref = absolute_path ? "/#{record.full_path}" : record.full_path
get_or_set_cache(cache, ref) { record }
found << ref
end
not_found = to_query - found
not_found.each do |ref|
get_or_set_cache(cache, ref) { nil }
end
end
cache.slice(*paths).values.compact
end
def objects_for_paths(paths, absolute_path)
search_paths = absolute_path ? paths.pluck(1..-1) : paths
klass = parent_type.to_s.camelize.constantize
result = klass.where_full_path_in(paths)
result = klass.where_full_path_in(search_paths)
return result if parent_type == :group
return unless parent_type == :project
result.includes(namespace: :route)
projects = result.includes(namespace: :route)
.allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/420046")
return projects unless absolute_path
# If we make it to here, then we're handling absolute path(s).
# Which means we need to also search groups as well as projects.
# Possible future optimization might be to use Route along the lines of:
# Routable.where_full_path_in(paths).includes(:source)
# See `routable.rb`
groups = Group.where_full_path_in(search_paths)
projects.to_a + groups.to_a
end
def refs_cache

View File

@ -232,6 +232,7 @@ semgrep-sast:
- '**/*.html'
- '**/*.scala'
- '**/*.sc'
- '**/*.php'
sobelow-sast:
extends: .sast-analyzer

View File

@ -287,6 +287,7 @@ semgrep-sast:
- '**/*.cs'
- '**/*.scala'
- '**/*.sc'
- '**/*.php'
- if: $CI_OPEN_MERGE_REQUESTS # Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
when: never
- if: $CI_COMMIT_BRANCH # If there's no open merge request, add it to a *branch* pipeline instead.
@ -303,6 +304,7 @@ semgrep-sast:
- '**/*.cs'
- '**/*.scala'
- '**/*.sc'
- '**/*.php'
sobelow-sast:
extends: .sast-analyzer

View File

@ -12,13 +12,16 @@ module Gitlab
property: [String],
value: [Integer, Float]
}.freeze
DEFAULT_ADDITIONAL_PROPERTIES = {}.freeze
class << self
include Gitlab::Tracking::Helpers
include Gitlab::Utils::StrongMemoize
include Gitlab::UsageDataCounters::RedisCounter
def track_event(event_name, category: nil, send_snowplow_event: true, additional_properties: {}, **kwargs)
def track_event(
event_name, category: nil, send_snowplow_event: true,
additional_properties: DEFAULT_ADDITIONAL_PROPERTIES, **kwargs)
raise UnknownEventError, "Unknown event: #{event_name}" unless EventDefinitions.known_event?(event_name)
validate_properties!(additional_properties, kwargs)

View File

@ -45234,6 +45234,9 @@ msgstr ""
msgid "SecurityOrchestration|Merge request approval policy"
msgstr ""
msgid "SecurityOrchestration|Merge result policy syntax changes"
msgstr ""
msgid "SecurityOrchestration|New merge request approval policy"
msgstr ""
@ -45410,6 +45413,9 @@ msgstr ""
msgid "SecurityOrchestration|Select users"
msgstr ""
msgid "SecurityOrchestration|Several merge result policy criteria have been deprecated. Policies using these elements will not work after GitLab 17.0 (May 10, 2024). You must edit these policies to remove the deprecated criteria."
msgstr ""
msgid "SecurityOrchestration|Severity is %{severity}."
msgstr ""
@ -45443,6 +45449,9 @@ msgstr ""
msgid "SecurityOrchestration|Summary"
msgstr ""
msgid "SecurityOrchestration|Summary of syntax changes:"
msgstr ""
msgid "SecurityOrchestration|The %{oldNameStart}Scan result policy%{oldNameEnd} is now called the %{newNameStart}Merge request approval policy%{newNameEnd} to better align with its purpose."
msgstr ""
@ -45635,12 +45644,21 @@ msgstr ""
msgid "SecurityOrchestration|have no fix available"
msgstr ""
msgid "SecurityOrchestration|match_on_inclusion is replaced by %{linkStart}match_on_inclusion_license%{linkEnd}"
msgstr ""
msgid "SecurityOrchestration|more than %{allowed}"
msgstr ""
msgid "SecurityOrchestration|newly_deprecated is replaced by %{firstLinkStart}New::Needs Triage%{firstLinkEnd} and %{secondLinkStart}New::Dismissed%{secondLinkEnd}"
msgstr ""
msgid "SecurityOrchestration|or from:"
msgstr ""
msgid "SecurityOrchestration|project.networkpolicies will be removed (GraphQL API associated with the network policies)"
msgstr ""
msgid "SecurityOrchestration|projects with compliance frameworks"
msgstr ""

View File

@ -3,6 +3,7 @@
module InternalEventsCli
NEW_EVENT_FIELDS = [
:description,
:internal_events,
:category,
:action,
:label_description,
@ -21,11 +22,11 @@ module InternalEventsCli
].freeze
EVENT_DEFAULTS = {
internal_events: true,
product_section: nil,
product_stage: nil,
product_group: nil,
introduced_by_url: 'TODO',
category: 'InternalEventTracking'
introduced_by_url: 'TODO'
}.freeze
Event = Struct.new(*NEW_EVENT_FIELDS, keyword_init: true) do

View File

@ -18,7 +18,9 @@ module InternalEventsCli
options.sort_by do |option|
category = events.dig(option[:value], 'category')
event_sort_param(category, option[:name])
internal_events = events.dig(option[:value], 'internal_events')
event_sort_param(internal_events, category, option[:name])
end
end
@ -37,23 +39,18 @@ module InternalEventsCli
end
def format_event_name(event)
case event.category
when 'InternalEventTracking', 'default'
if event.internal_events || event.category == 'default'
event.action
else
"#{event.category}:#{event.action}"
end
end
def event_sort_param(category, name)
case category
when 'InternalEventTracking'
"0#{name}"
when 'default'
"1#{name}"
else
"2#{category}#{name}"
end
def event_sort_param(internal_events, category, name)
return "0#{name}" if internal_events
return "1#{name}" if category == 'default'
"2#{category}#{name}"
end
def get_existing_events_for_paths(event_paths)

View File

@ -82,7 +82,7 @@ module InternalEventsCli
Find one? Create a new metric for the event.
Otherwise? Create a new event.
If you find a relevant event that has a different category from 'InternalEventTracking', it can be migrated to
If you find a relevant event that does not have the property `internal_events: true`, it can be migrated to
Internal Events. See https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html
TEXT

View File

@ -1,6 +1,6 @@
---
description: Internal Event CLI is opened
category: InternalEventTracking
internal_events: true
action: internal_events_cli_opened
product_section: analytics
product_stage: monitor

View File

@ -1,6 +1,6 @@
---
description: Engineer uses Internal Event CLI to define a new event
category: InternalEventTracking
internal_events: true
action: internal_events_cli_used
identifiers:
- project

View File

@ -1,6 +1,6 @@
---
description: random event string
category: InternalEventTracking
internal_events: true
action: random_name
identifiers:
- project

View File

@ -1,6 +1,6 @@
---
description: Engineer closes Internal Event CLI
category: InternalEventTracking
internal_events: true
action: internal_events_cli_closed
identifiers:
- project

View File

@ -866,6 +866,7 @@ RSpec.describe API::Helpers, feature_category: :shared do
expect(Gitlab::InternalEvents).to receive(:track_event).with(
event_name,
send_snowplow_event: true,
additional_properties: {},
user: user,
namespace: namespace,
project: project
@ -882,6 +883,7 @@ RSpec.describe API::Helpers, feature_category: :shared do
expect(Gitlab::InternalEvents).to receive(:track_event).with(
event_name,
send_snowplow_event: false,
additional_properties: {},
user: user,
namespace: namespace,
project: project
@ -895,6 +897,24 @@ RSpec.describe API::Helpers, feature_category: :shared do
)
end
it 'passes additional_properties on to InternalEvents.track_event' do
expect(Gitlab::InternalEvents).to receive(:track_event).with(
event_name,
send_snowplow_event: true,
additional_properties: { label: 'label2' },
user: user,
namespace: namespace,
project: project
)
helper.track_event(event_name,
user: user,
namespace_id: namespace.id,
project_id: project.id,
additional_properties: { label: 'label2' }
)
end
it 'tracks an exception and renders 422 for unknown event', :aggregate_failures do
expect(Gitlab::InternalEvents).to receive(:track_event).and_raise(Gitlab::InternalEvents::UnknownEventError, "Unknown event: #{unknown_event}")

View File

@ -194,21 +194,10 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
let(:backup_id) { "1546300800_2019_01_01_#{Gitlab::VERSION}" }
let(:full_backup_id) { backup_id }
let(:pack_tar_file) { "#{backup_id}_gitlab_backup.tar" }
let(:pack_tar_system_options) { { out: [pack_tar_file, 'w', Gitlab.config.backup.archive_permissions] } }
let(:pack_tar_cmdline) { ['tar', '-cf', '-', *expected_backup_contents, pack_tar_system_options] }
let(:lfs) do
Backup::Tasks::Lfs.new(progress: progress, options: options)
.tap { |task| allow(task).to receive(:target).and_return(target1) }
end
let(:lfs) { Backup::Tasks::Lfs.new(progress: progress, options: options) }
let(:pages) { Backup::Tasks::Pages.new(progress: progress, options: options) }
let(:pages) do
Backup::Tasks::Pages.new(progress: progress, options: options)
.tap { |task| allow(task).to receive(:target).and_return(target2) }
end
let(:target1) { instance_double(Backup::Targets::Target) }
let(:target2) { instance_double(Backup::Targets::Target) }
let(:backup_tasks) do
{ 'lfs' => lfs, 'pages' => pages }
end
@ -217,10 +206,6 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
stub_env('INCREMENTAL', incremental_env)
allow(ApplicationRecord.connection).to receive(:reconnect!)
allow(Gitlab::BackupLogger).to receive(:info)
allow(Kernel).to receive(:system).and_return(true)
allow(target1).to receive(:dump).with(backup_path.join('lfs.tar.gz'), backup_id)
allow(target2).to receive(:dump).with(backup_path.join('pages.tar.gz'), backup_id)
end
it 'creates a backup tar' do
@ -228,7 +213,8 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
subject.create # rubocop:disable Rails/SaveBang
end
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
expect(File).to exist(backup_path.join(pack_tar_file))
expect(FileUtils).to have_received(:rm_rf).with(backup_path.join('backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(backup_path.join('tmp'))
end
@ -243,15 +229,15 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
it 'uses the given value as tar file name' do
subject.create # rubocop:disable Rails/SaveBang
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
expect(File).to exist(backup_path.join('custom_gitlab_backup.tar'))
end
context 'tar fails' do
before do
expect(Kernel).to receive(:system).with(*pack_tar_cmdline).and_return(false)
end
it 'logs a failure' do
allow(Open3).to receive(:pipeline).and_return(
[instance_double(Process::Status, success?: false, exitstatus: 1)]
)
expect do
subject.create # rubocop:disable Rails/SaveBang
end.to raise_error(Backup::Error, 'Backup failed')
@ -265,14 +251,13 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
context 'when SKIP env is set' do
let(:expected_backup_contents) { %w[backup_information.yml lfs.tar.gz] }
before do
stub_env('SKIP', 'pages')
end
it 'executes tar' do
subject.create # rubocop:disable Rails/SaveBang
stub_env('SKIP', 'pages')
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
expect(lfs).to receive(:target).and_call_original
expect(pages).not_to receive(:target)
subject.create # rubocop:disable Rails/SaveBang
end
end
@ -281,16 +266,15 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
let(:pages) do
Backup::Tasks::Pages.new(progress: progress, options: options)
.tap do |task|
allow(task).to receive_messages(target: target2, destination_optional: true)
allow(task).to receive(:destination_optional).and_return(true)
end
end
it 'executes tar' do
allow(pages).to receive_message_chain(:target, :dump)
expect(File).to receive(:exist?).with(backup_path.join('pages.tar.gz')).and_return(false)
subject.create # rubocop:disable Rails/SaveBang
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
end
end
@ -312,9 +296,12 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
before do
allow(Gitlab::BackupLogger).to receive(:info)
allow(Dir).to receive(:chdir).and_yield
allow(Dir).to receive(:glob).and_return(files)
allow(FileUtils).to receive(:rm)
files.each do |bkp|
FileUtils.touch(backup_path.join(bkp))
end
allow(FileUtils).to receive(:rm).and_call_original
allow(Time).to receive(:now).and_return(Time.zone.parse('2016-1-1'))
end
@ -326,7 +313,9 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
it 'removes no files' do
expect(FileUtils).not_to have_received(:rm)
files.each do |bkp|
expect(File).to exist(backup_path.join(bkp))
end
end
it 'prints a skipped message' do
@ -350,7 +339,9 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
it 'removes no files' do
expect(FileUtils).not_to have_received(:rm)
files.each do |bkp|
expect(File).to exist(backup_path.join(bkp))
end
end
it 'prints a done message' do
@ -367,7 +358,9 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
it 'removes no files' do
expect(FileUtils).not_to have_received(:rm)
files.each do |bkp|
expect(File).to exist(backup_path.join(bkp))
end
end
it 'prints a done message' do
@ -643,11 +636,12 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
it 'creates a non-tarred backup' do
expect(subject).not_to receive(:pack)
travel_to(backup_time) do
subject.create # rubocop:disable Rails/SaveBang
end
expect(Kernel).not_to have_received(:system).with(*pack_tar_cmdline)
expect(subject.send(:backup_information).to_h).to include(
backup_id: backup_id,
backup_created_at: backup_time.localtime,
@ -752,12 +746,14 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
it 'unpacks and packs the backup' do
expect(subject).to receive(:unpack).and_call_original
expect(subject).to receive(:pack).and_call_original
travel_to(backup_time) do
subject.create # rubocop:disable Rails/SaveBang
end
expect(Kernel).to have_received(:system).with(*unpack_tar_cmdline)
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
expect(FileUtils).to have_received(:rm_rf).with(backup_path.join('backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(backup_path.join('tmp'))
end
@ -777,11 +773,11 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
context 'tar fails' do
before do
expect(Kernel).to receive(:system).with(*pack_tar_cmdline).and_return(false)
end
it 'logs a failure' do
allow(Open3).to receive(:pipeline).and_return(
[instance_double(Process::Status, success?: false, exitstatus: 1)]
)
expect do
subject.create # rubocop:disable Rails/SaveBang
end.to raise_error(Backup::Error, 'Backup failed')
@ -845,12 +841,14 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
it 'unpacks and packs the backup' do
expect(subject).to receive(:unpack).and_call_original
expect(subject).to receive(:pack).and_call_original
travel_to(backup_time) do
subject.create # rubocop:disable Rails/SaveBang
end
expect(Kernel).to have_received(:system).with(*unpack_tar_cmdline)
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
expect(FileUtils).to have_received(:rm_rf).with(backup_path.join('backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(backup_path.join('tmp'))
end
@ -873,7 +871,9 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
context 'tar fails' do
before do
expect(Kernel).to receive(:system).with(*pack_tar_cmdline).and_return(false)
allow(Open3).to receive(:pipeline).and_return(
[instance_double(Process::Status, success?: false, exitstatus: 1)]
)
end
it 'logs a failure' do

View File

@ -323,6 +323,18 @@ RSpec.describe Banzai::Filter::References::MilestoneReferenceFilter, feature_cat
end
end
shared_examples 'absolute references' do
it 'supports absolute reference' do
absolute_reference = "/#{reference}"
result = reference_filter("See #{absolute_reference}")
expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone))
expect(result.css('a').first.attr('data-original')).to eq absolute_reference
expect(result.content).to eq "See %#{milestone.title}"
end
end
shared_context 'project milestones' do
let(:reference) { milestone.to_reference(format: :iid) }
@ -341,6 +353,10 @@ RSpec.describe Banzai::Filter::References::MilestoneReferenceFilter, feature_cat
let(:resource) { milestone }
let(:resource_text) { "#{resource.class.reference_prefix}#{resource.title}" }
end
it_behaves_like 'absolute references' do
let(:reference) { milestone.to_reference(format: :iid, full: true) }
end
end
shared_context 'group milestones' do
@ -357,6 +373,10 @@ RSpec.describe Banzai::Filter::References::MilestoneReferenceFilter, feature_cat
let(:resource_text) { "#{resource.class.reference_prefix}#{resource.title}" }
end
it_behaves_like 'absolute references' do
let(:reference) { milestone.to_reference(format: :name, full: true) }
end
it 'does not support references by IID' do
doc = reference_filter("See #{Milestone.reference_prefix}#{milestone.iid}")
@ -411,6 +431,10 @@ RSpec.describe Banzai::Filter::References::MilestoneReferenceFilter, feature_cat
expect(reference_filter(act, context).to_html).to eq exp
end
it_behaves_like 'absolute references' do
let(:reference) { "#{project.full_path}%#{milestone.iid}" }
end
end
context 'when group milestone' do
@ -420,7 +444,7 @@ RSpec.describe Banzai::Filter::References::MilestoneReferenceFilter, feature_cat
let(:sub_group) { create(:group, parent: group) }
let(:sub_group_milestone) { create(:milestone, title: 'sub_group_milestone', group: sub_group) }
it 'links to a valid reference of subgroup and group milestones' do
it 'links to valid references of subgroup and group milestones' do
[group_milestone, sub_group_milestone].each do |milestone|
reference = "%#{milestone.title}"
@ -429,6 +453,18 @@ RSpec.describe Banzai::Filter::References::MilestoneReferenceFilter, feature_cat
expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone))
end
end
it 'links to valid absolute references of subgroup and group milestones' do
[group_milestone, sub_group_milestone].each do |milestone|
reference = "/#{milestone.group.full_path}%#{milestone.title}"
result = reference_filter("See #{reference}", { project: nil, group: sub_group })
expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone))
expect(result.css('a').first.attr('data-original')).to eq reference
expect(result.content).to eq "See %#{milestone.title}"
end
end
end
it 'ignores internal references' do
@ -450,6 +486,36 @@ RSpec.describe Banzai::Filter::References::MilestoneReferenceFilter, feature_cat
expect(links[1].attr('href')).to eq(urls.milestone_url(group_milestone))
end
end
context 'when referencing both project and group milestones using absolute references' do
let(:milestone) { create(:milestone, project: project) }
let(:group_milestone) { create(:milestone, title: 'group_milestone', group: project.group) }
it 'links to valid references' do
doc = reference_filter("See /#{milestone.to_reference(full: true)} and /#{group_milestone.to_reference(full: true)}", context)
links = doc.css('a')
expect(links.length).to eq(2)
expect(links[0].attr('href')).to eq(urls.milestone_url(milestone))
expect(links[1].attr('href')).to eq(urls.milestone_url(group_milestone))
end
end
context 'when referencing both group and subgroup milestones using absolute references' do
let(:subgroup) { create(:group, :public, parent: group) }
let(:group_milestone) { create(:milestone, title: 'group_milestone', group: group) }
let(:subgroup_milestone) { create(:milestone, title: 'group_milestone', group: subgroup) }
let(:context) { { project: project, group: nil } }
it 'links to valid references' do
doc = reference_filter("See /#{group_milestone.to_reference(full: true)} and /#{subgroup_milestone.to_reference(full: true)}", context)
links = doc.css('a')
expect(links.length).to eq(2)
expect(links[0].attr('href')).to eq(urls.milestone_url(group_milestone))
expect(links[1].attr('href')).to eq(urls.milestone_url(subgroup_milestone))
end
end
end
context 'when milestone is open' do

View File

@ -3,11 +3,14 @@
require 'spec_helper'
RSpec.describe Banzai::Filter::References::ReferenceCache, feature_category: :team_planning do
let_it_be(:project) { create(:project) }
let_it_be(:group) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:project2) { create(:project) }
let_it_be(:issue1) { create(:issue, project: project) }
let_it_be(:issue2) { create(:issue, project: project) }
let_it_be(:issue3) { create(:issue, project: project2) }
let_it_be(:issue4) { create(:issue, project: project2) }
let_it_be(:doc) { Nokogiri::HTML.fragment("#{issue1.to_reference} #{issue2.to_reference} #{issue3.to_reference(full: true)}") }
let_it_be(:result) { {} }
let_it_be(:filter_class) { Banzai::Filter::References::IssueReferenceFilter }
@ -63,17 +66,49 @@ RSpec.describe Banzai::Filter::References::ReferenceCache, feature_category: :te
describe '#parent_per_reference' do
it 'returns a Hash containing projects grouped per parent paths' do
expect(cache.parent_per_reference).to match({ project.full_path => project, project2.full_path => project2 })
expect(cache.parent_per_reference).to include({ project.full_path => project, project2.full_path => project2 })
end
end
describe '#records_per_parent' do
it 'returns a Hash containing projects grouped per parent paths' do
it 'returns a Hash containing records grouped per parent' do
expect(cache.records_per_parent).to match({ project => { issue1.iid => issue1, issue2.iid => issue2 },
project2 => { issue3.iid => issue3 } })
end
end
end
context 'when the cache is loaded with absolute references' do
it 'loads references grouped per parent path and absolute references' do
milestone1 = create(:milestone, group: group)
milestone2 = create(:milestone, group: subgroup)
milestone3 = create(:milestone, project: project)
doc_milestone = Nokogiri::HTML.fragment("/#{milestone1.to_reference(full: true)} /#{milestone2.to_reference(full: true)} #{milestone3.to_reference(full: true)}")
filter_milestone = Banzai::Filter::References::MilestoneReferenceFilter.new(doc_milestone, project: project)
cache_milestone = described_class.new(filter_milestone, { project: project }, {})
cache_milestone.load_reference_cache(filter_milestone.nodes)
expect(cache_milestone.references_per_parent).to match({
"/#{group.full_path}" => [{ milestone_iid: nil, milestone_name: milestone1.title, absolute_path: true }].to_set,
"/#{subgroup.full_path}" => [{ milestone_iid: nil, milestone_name: milestone2.title, absolute_path: true }].to_set,
project.full_path => [{ milestone_iid: nil, milestone_name: milestone3.title, absolute_path: false }].to_set
})
expect(cache_milestone.parent_per_reference).to match({
"/#{group.full_path}" => group,
"/#{subgroup.full_path}" => subgroup,
project.full_path => project
})
expect(cache_milestone.records_per_parent).to match({
group => { { milestone_iid: milestone1.iid, milestone_name: milestone1.title } => milestone1 },
subgroup => { { milestone_iid: milestone2.iid, milestone_name: milestone2.title } => milestone2 },
project => { { milestone_iid: milestone3.iid, milestone_name: milestone3.title } => milestone3 }
})
end
end
end
describe '#initialize_reference_cache' do
@ -105,8 +140,8 @@ RSpec.describe Banzai::Filter::References::ReferenceCache, feature_category: :te
end
describe '#find_for_paths' do
def find_for_paths(paths)
cache.send(:find_for_paths, paths)
def find_for_paths(paths, absolute_path = false)
cache.send(:find_for_paths, paths, absolute_path)
end
context 'with RequestStore disabled' do
@ -117,6 +152,14 @@ RSpec.describe Banzai::Filter::References::ReferenceCache, feature_category: :te
it 'return an empty array for paths that do not exist' do
expect(find_for_paths(['nonexistent/project'])).to eq([])
end
it 'finds group and project by absolute path' do
project_path = "/#{project.full_path}"
group_path = "/#{subgroup.full_path}"
nonexistent_path = '/nonexistent/project'
expect(find_for_paths([project_path, group_path, nonexistent_path], true)).to match_array([project, subgroup])
end
end
context 'with RequestStore enabled', :request_store do
@ -166,9 +209,16 @@ RSpec.describe Banzai::Filter::References::ReferenceCache, feature_category: :te
expect(cache.full_project_path('something', 'cool')).to eq 'something/cool'
end
it 'returns uses default namespace and project ref when namespace nil' do
it 'returns default namespace and project ref when namespace nil' do
expect(cache.full_project_path(nil, 'cool')).to eq "#{project.namespace.full_path}/cool"
end
it 'returns absolute paths when matched to an absolute path' do
match = "/something/cool".match(Project.reference_pattern)
expect(cache.full_project_path('something', 'cool', match)).to eq '/something/cool'
expect(cache.full_project_path(nil, 'cool', match)).to eq '/cool'
end
end
describe '#full_group_path' do

View File

@ -63,7 +63,7 @@ RSpec.describe Gitlab::Analytics::InternalEventsGenerator, :silence_stdout, feat
let(:identifiers) { %w[project user namespace] }
let(:event_definition) do
{
"category" => "InternalEventTracking",
"internal_events" => true,
"action" => event,
"description" => description,
"product_section" => section,

View File

@ -36,7 +36,7 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin
it 'has no duplicated actions in InternalEventTracking events', :aggregate_failures do
definitions_by_action = described_class
.definitions
.select { |d| d.attributes[:internal_events] || d.attributes[:category] == 'InternalEventTracking' }
.select { |d| d.attributes[:internal_events] }
.group_by { |d| d.attributes[:action] }
definitions_by_action.each do |action, definitions|
@ -45,6 +45,17 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin
end
end
it 'only has internal events without category', :aggregate_failures do
internal_events = described_class
.definitions
.select { |d| d.attributes[:internal_events] }
internal_events.each do |event|
expect(event.attributes[:category]).to be_nil,
"Event definition with internal_events: true should not have a category: #{event.path}"
end
end
it 'has event definitions for all events used in Internal Events metric definitions', :aggregate_failures do
from_metric_definitions = Gitlab::Usage::MetricDefinition.definitions
.values

View File

@ -470,7 +470,7 @@ RSpec.describe Milestone, feature_category: :team_planning do
end
it 'does supports cross-project references within a group' do
expect(milestone.to_reference(another_project, format: :name)).to eq '%"milestone"'
expect(milestone.to_reference(another_project, format: :name)).to eq "#{group.full_path}%\"milestone\""
end
it 'raises an error when using iid format' do

View File

@ -1491,6 +1491,26 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
end
end
end
describe '#reference_pattern' do
it 'matches a normal reference' do
reference = project.to_reference
match = reference.match(described_class.reference_pattern)
expect(match[:namespace]).to eq project.namespace.full_path
expect(match[:project]).to eq project.path
expect(match[:absolute_path]).to eq nil
end
it 'matches an absolute reference' do
reference = "/#{project.to_reference}"
match = reference.match(described_class.reference_pattern)
expect(match[:namespace]).to eq project.namespace.full_path
expect(match[:project]).to eq project.path
expect(match[:absolute_path]).to eq '/'
end
end
end
describe '#to_reference_base' do

View File

@ -216,6 +216,12 @@ RSpec.describe API::UsageData, feature_category: :service_ping do
context 'with authentication' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project) }
let_it_be(:additional_properties) do
{
label: 'label3',
property: 'admin'
}
end
before do
stub_application_setting(usage_ping_enabled: true)
@ -225,12 +231,43 @@ RSpec.describe API::UsageData, feature_category: :service_ping do
context 'with correct params' do
it 'returns status ok' do
expect(Gitlab::InternalEvents).to receive(:track_event)
.with(known_event, send_snowplow_event: false, user: user, namespace: namespace, project: project)
.with(
known_event,
send_snowplow_event: false,
user: user,
namespace: namespace,
project: project,
additional_properties: additional_properties
)
post api(endpoint, user), params: { event: known_event, namespace_id: namespace.id, project_id: project.id }
params = {
event: known_event,
namespace_id: namespace.id,
project_id: project.id,
additional_properties: additional_properties
}
post api(endpoint, user), params: params
expect(response).to have_gitlab_http_status(:ok)
end
context 'with no additional_properties' do
it 'returns status ok' do
expect(Gitlab::InternalEvents).to receive(:track_event)
.with(
known_event,
send_snowplow_event: false,
user: user,
namespace: namespace,
project: project,
additional_properties: {}
)
post api(endpoint, user), params: { event: known_event, namespace_id: namespace.id, project_id: project.id }
expect(response).to have_gitlab_http_status(:ok)
end
end
end
end
end

View File

@ -133,13 +133,13 @@ RSpec.describe Cli, feature_category: :service_ping do
context 'when creating a metric from multiple events' do
let(:events) do
[{
action: '00_event1', category: 'InternalEventTracking',
action: '00_event1', internal_events: true,
product_section: 'dev', product_stage: 'plan', product_group: 'optimize'
}, {
action: '00_event2', category: 'InternalEventTracking',
action: '00_event2', internal_events: true,
product_section: 'dev', product_stage: 'create', product_group: 'ide'
}, {
action: '00_event3', category: 'InternalEventTracking',
action: '00_event3', internal_events: true,
product_section: 'dev', product_stage: 'create', product_group: 'source_code'
}]
end
@ -213,8 +213,7 @@ RSpec.describe Cli, feature_category: :service_ping do
context 'when product group for event no longer exists' do
let(:event) do
{
action: '00_event1', category: 'InternalEventTracking',
product_section: 'other', product_stage: 'other', product_group: 'other'
action: '00_event1', product_section: 'other', product_stage: 'other', product_group: 'other'
}
end
@ -344,7 +343,7 @@ RSpec.describe Cli, feature_category: :service_ping do
end
context 'when all metrics already exist' do
let(:event) { { action: '00_event1', category: 'InternalEventTracking' } }
let(:event) { { action: '00_event1' } }
let(:metric) { { options: { 'events' => ['00_event1'] }, events: [{ 'name' => '00_event1' }] } }
let(:files) do

View File

@ -228,7 +228,7 @@ module GitalySetup
Gitlab::SetupHelper::Gitaly.create_configuration(
gitaly_dir,
{ 'default' => storage_path, 'test_second_storage' => second_storage_path },
{ 'default' => storage_path },
force: true,
options: {
runtime_dir: runtime_dir,
@ -237,7 +237,7 @@ module GitalySetup
)
Gitlab::SetupHelper::Gitaly.create_configuration(
gitaly_dir,
{ 'default' => storage_path, 'test_second_storage' => second_storage_path },
{ 'test_second_storage' => second_storage_path },
force: true,
options: {
runtime_dir: runtime_dir,

View File

@ -6,7 +6,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.0
github.com/BurntSushi/toml v1.3.2
github.com/alecthomas/chroma/v2 v2.12.0
github.com/aws/aws-sdk-go v1.50.7
github.com/aws/aws-sdk-go v1.50.27
github.com/disintegration/imaging v1.6.2
github.com/getsentry/raven-go v0.2.0
github.com/golang-jwt/jwt/v5 v5.2.1
@ -30,19 +30,19 @@ require (
golang.org/x/net v0.21.0
golang.org/x/oauth2 v0.16.0
golang.org/x/tools v0.18.0
google.golang.org/grpc v1.61.0
google.golang.org/grpc v1.62.0
google.golang.org/protobuf v1.32.0
honnef.co/go/tools v0.4.7
)
require (
cloud.google.com/go v0.110.10 // indirect
cloud.google.com/go v0.112.0 // indirect
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.5 // indirect
cloud.google.com/go/monitoring v1.16.3 // indirect
cloud.google.com/go/monitoring v1.17.0 // indirect
cloud.google.com/go/profiler v0.1.0 // indirect
cloud.google.com/go/storage v1.35.1 // indirect
cloud.google.com/go/storage v1.36.0 // indirect
cloud.google.com/go/trace v1.10.4 // indirect
contrib.go.opencensus.io/exporter/stackdriver v0.13.14 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 // indirect
@ -63,6 +63,9 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@ -107,6 +110,11 @@ require (
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
go.opentelemetry.io/otel v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect
@ -114,13 +122,13 @@ require (
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.4.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/api v0.151.0 // indirect
google.golang.org/api v0.155.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -24,8 +24,8 @@ cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSU
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y=
cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic=
cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@ -40,8 +40,8 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI=
cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
cloud.google.com/go/monitoring v1.16.3 h1:mf2SN9qSoBtIgiMA4R/y4VADPWZA7VCNJA079qLaZQ8=
cloud.google.com/go/monitoring v1.16.3/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw=
cloud.google.com/go/monitoring v1.17.0 h1:blrdvF0MkPPivSO041ihul7rFMhXdVp8Uq7F59DKXTU=
cloud.google.com/go/monitoring v1.17.0/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw=
cloud.google.com/go/profiler v0.1.0 h1:MG/rxKC1MztRfEWMGYKFISxyZak5hNh29f0A/z2tvWk=
cloud.google.com/go/profiler v0.1.0/go.mod h1:D7S7LV/zKbRWkOzYL1b5xytpqt8Ikd/v/yvf1/Tx2pQ=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
@ -53,8 +53,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w=
cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8=
cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
cloud.google.com/go/trace v1.10.4 h1:2qOAuAzNezwW3QN+t41BtkDJOG42HywL73q8x/f6fnM=
cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY=
contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpXy/0+JIb1M8KjigCJzx7+4=
@ -94,8 +94,8 @@ github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurG
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/aws/aws-sdk-go v1.44.256/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.50.7 h1:odKb+uneeGgF2jgAerKjFzpljiyZxleV4SHB7oBK+YA=
github.com/aws/aws-sdk-go v1.50.7/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go v1.50.27 h1:96ifhrSuja+AzdP3W/T2337igqVQ2FcSIJYkk+0rCeA=
github.com/aws/aws-sdk-go v1.50.27/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs=
github.com/aws/aws-sdk-go-v2/config v1.26.1 h1:z6DqMxclFGL3Zfo+4Q0rLnAZ6yVkzCRxhRMsiRQnD1o=
@ -141,6 +141,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@ -160,6 +161,9 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
@ -171,6 +175,11 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
@ -456,6 +465,17 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
@ -689,8 +709,8 @@ golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -785,8 +805,8 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.151.0 h1:FhfXLO/NFdJIzQtCqjpysWwqKk8AzGWBUhMIx67cVDU=
google.golang.org/api v0.151.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg=
google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA=
google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -849,12 +869,12 @@ google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB5KrjI2q0wCOWEOIh0OEsleqakHJg=
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY=
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY=
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@ -881,8 +901,8 @@ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=