From b8ff7c8f9213769b001700c6cfe27da89fac3875 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 7 Mar 2024 06:09:47 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../work_item_due_date_with_edit.vue | 2 +- .../components/work_item_labels_with_edit.vue | 25 ++--- app/models/milestone.rb | 2 +- app/models/project.rb | 1 + .../usage_data_download_payload_clicked.yml | 2 +- .../event_definition_guide.md | 29 +++--- doc/user/markdown.md | 22 ++--- .../lib/gitlab/backup/cli/shell/command.rb | 34 +++++++ .../event_definition.yml | 2 +- lib/api/helpers.rb | 3 +- lib/api/usage_data.rb | 5 +- lib/backup/manager.rb | 17 +++- lib/backup/targets/files.rb | 10 +- .../references/abstract_reference_filter.rb | 2 +- .../references/label_reference_filter.rb | 2 +- .../references/milestone_reference_filter.rb | 45 +++++---- .../filter/references/reference_cache.rb | 96 ++++++++++++------- .../ci/templates/Jobs/SAST.gitlab-ci.yml | 1 + .../templates/Jobs/SAST.latest.gitlab-ci.yml | 2 + lib/gitlab/internal_events.rb | 5 +- locale/gitlab.pot | 18 ++++ scripts/internal_events/cli/event.rb | 5 +- .../cli/helpers/event_options.rb | 21 ++-- scripts/internal_events/cli/text.rb | 2 +- .../events/ee_event_without_identifiers.yml | 2 +- .../events/event_with_identifiers.yml | 2 +- .../events/keyboard_smashed_event.yml | 2 +- .../secondary_event_with_identifiers.yml | 2 +- spec/lib/api/helpers_spec.rb | 20 ++++ spec/lib/backup/manager_spec.rb | 92 +++++++++--------- .../milestone_reference_filter_spec.rb | 68 ++++++++++++- .../filter/references/reference_cache_spec.rb | 62 ++++++++++-- .../internal_events_generator_spec.rb | 2 +- .../gitlab/tracking/event_definition_spec.rb | 13 ++- spec/models/milestone_spec.rb | 2 +- spec/models/project_spec.rb | 20 ++++ spec/requests/api/usage_data_spec.rb | 41 +++++++- spec/scripts/internal_events/cli_spec.rb | 11 +-- spec/support/helpers/gitaly_setup.rb | 4 +- workhorse/go.mod | 28 ++++-- workhorse/go.sum | 60 ++++++++---- 41 files changed, 565 insertions(+), 219 deletions(-) diff --git a/app/assets/javascripts/work_items/components/work_item_due_date_with_edit.vue b/app/assets/javascripts/work_items/components/work_item_due_date_with_edit.vue index a85bb99e06d..e8dc6a56607 100644 --- a/app/assets/javascripts/work_items/components/work_item_due_date_with_edit.vue +++ b/app/assets/javascripts/work_items/components/work_item_due_date_with_edit.vue @@ -289,7 +289,7 @@ export default { diff --git a/app/models/milestone.rb b/app/models/milestone.rb index c23921f28bd..f31873c6b85 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -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 diff --git a/app/models/project.rb b/app/models/project.rb index de9cc4996ac..68ac32a4360 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1001,6 +1001,7 @@ class Project < ApplicationRecord def reference_pattern %r{ (?/)? ((?#{Gitlab::PathRegex::FULL_NAMESPACE_FORMAT_REGEX})/)? (?#{Gitlab::PathRegex::PROJECT_PATH_FORMAT_REGEX}) }xo diff --git a/config/events/usage_data_download_payload_clicked.yml b/config/events/usage_data_download_payload_clicked.yml index 30a612c29d5..1ff1b75c450 100644 --- a/config/events/usage_data_download_payload_clicked.yml +++ b/config/events/usage_data_download_payload_clicked.yml @@ -1,6 +1,6 @@ --- description: Counts Download Payload button clicks -category: InternalEventTracking +internal_events: true action: usage_data_download_payload_clicked identifiers: - user diff --git a/doc/development/internal_analytics/internal_event_instrumentation/event_definition_guide.md b/doc/development/internal_analytics/internal_event_instrumentation/event_definition_guide.md index 62acdddd2b9..6cd1e3d6aa3 100644 --- a/doc/development/internal_analytics/internal_event_instrumentation/event_definition_guide.md +++ b/doc/development/internal_analytics/internal_event_instrumentation/event_definition_guide.md @@ -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 `__`.

Ex: `publish_go_module_to_the_registry_from_pipeline`
` = publish`
` = go_module`
` = 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 `__`.

Ex: `publish_go_module_to_the_registry_from_pipeline`
` = publish`
` = go_module`
` = 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 diff --git a/doc/user/markdown.md b/doc/user/markdown.md index b56c01970e6..ac7d9c64c02 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -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 2 | `%123` | `namespace/project%123` | `project%123` | +| Milestone by name (one word) 2 | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` | +| Milestone by name (multiple words) 2 | `%"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]` | | | -
    -
  1. - - Introduced 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. - -
  2. -
+**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:]`. + 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 diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/shell/command.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/shell/command.rb index d7f727f92ed..9089a5d4804 100644 --- a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/shell/command.rb +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/shell/command.rb @@ -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] 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 diff --git a/generator_templates/gitlab_internal_events/event_definition.yml b/generator_templates/gitlab_internal_events/event_definition.yml index 928b3138687..713a089d8b5 100644 --- a/generator_templates/gitlab_internal_events/event_definition.yml +++ b/generator_templates/gitlab_internal_events/event_definition.yml @@ -1,6 +1,6 @@ --- description: <%= args.last %> -category: InternalEventTracking +internal_events: true action: <%= event %> label_description: property_description: diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 9b2c6c37fd6..324a9364761 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -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 diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb index 1510e431dc5..26b9218ec65 100644 --- a/lib/api/usage_data.rb +++ b/lib/api/usage_data.rb @@ -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 diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index b190ec72190..17f3fa8689b 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -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) diff --git a/lib/backup/targets/files.rb b/lib/backup/targets/files.rb index 928bd3ab1c8..2e71cb80d29 100644 --- a/lib/backup/targets/files.rb +++ b/lib/backup/targets/files.rb @@ -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) diff --git a/lib/banzai/filter/references/abstract_reference_filter.rb b/lib/banzai/filter/references/abstract_reference_filter.rb index c3c5103106b..a8014937ea5 100644 --- a/lib/banzai/filter/references/abstract_reference_filter.rb +++ b/lib/banzai/filter/references/abstract_reference_filter.rb @@ -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) diff --git a/lib/banzai/filter/references/label_reference_filter.rb b/lib/banzai/filter/references/label_reference_filter.rb index 6020c7b7f58..170fdd92c67 100644 --- a/lib/banzai/filter/references/label_reference_filter.rb +++ b/lib/banzai/filter/references/label_reference_filter.rb @@ -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) diff --git a/lib/banzai/filter/references/milestone_reference_filter.rb b/lib/banzai/filter/references/milestone_reference_filter.rb index 77658f72d34..af8050cf8d0 100644 --- a/lib/banzai/filter/references/milestone_reference_filter.rb +++ b/lib/banzai/filter/references/milestone_reference_filter.rb @@ -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 diff --git a/lib/banzai/filter/references/reference_cache.rb b/lib/banzai/filter/references/reference_cache.rb index 9bddd0c376d..3878d0a3bf1 100644 --- a/lib/banzai/filter/references/reference_cache.rb +++ b/lib/banzai/filter/references/reference_cache.rb @@ -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 diff --git a/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml index d567ab2a141..47b1688e5ed 100644 --- a/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml @@ -232,6 +232,7 @@ semgrep-sast: - '**/*.html' - '**/*.scala' - '**/*.sc' + - '**/*.php' sobelow-sast: extends: .sast-analyzer diff --git a/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml index 88d10f8b235..1fe45119713 100644 --- a/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml @@ -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 diff --git a/lib/gitlab/internal_events.rb b/lib/gitlab/internal_events.rb index e50303240c1..b50658f46da 100644 --- a/lib/gitlab/internal_events.rb +++ b/lib/gitlab/internal_events.rb @@ -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) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a5c55849158..32bf2248d07 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -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 "" diff --git a/scripts/internal_events/cli/event.rb b/scripts/internal_events/cli/event.rb index d98aa8a6bd1..fecc490b4cc 100755 --- a/scripts/internal_events/cli/event.rb +++ b/scripts/internal_events/cli/event.rb @@ -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 diff --git a/scripts/internal_events/cli/helpers/event_options.rb b/scripts/internal_events/cli/helpers/event_options.rb index 723eda9eeb3..704663fed6d 100755 --- a/scripts/internal_events/cli/helpers/event_options.rb +++ b/scripts/internal_events/cli/helpers/event_options.rb @@ -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) diff --git a/scripts/internal_events/cli/text.rb b/scripts/internal_events/cli/text.rb index e524b879d38..e7609a013a6 100755 --- a/scripts/internal_events/cli/text.rb +++ b/scripts/internal_events/cli/text.rb @@ -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 diff --git a/spec/fixtures/scripts/internal_events/events/ee_event_without_identifiers.yml b/spec/fixtures/scripts/internal_events/events/ee_event_without_identifiers.yml index 07f606fbe33..482ca841d37 100644 --- a/spec/fixtures/scripts/internal_events/events/ee_event_without_identifiers.yml +++ b/spec/fixtures/scripts/internal_events/events/ee_event_without_identifiers.yml @@ -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 diff --git a/spec/fixtures/scripts/internal_events/events/event_with_identifiers.yml b/spec/fixtures/scripts/internal_events/events/event_with_identifiers.yml index 5050953920d..59572589de1 100644 --- a/spec/fixtures/scripts/internal_events/events/event_with_identifiers.yml +++ b/spec/fixtures/scripts/internal_events/events/event_with_identifiers.yml @@ -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 diff --git a/spec/fixtures/scripts/internal_events/events/keyboard_smashed_event.yml b/spec/fixtures/scripts/internal_events/events/keyboard_smashed_event.yml index c0ccbc03af7..1f9b6e5e86c 100644 --- a/spec/fixtures/scripts/internal_events/events/keyboard_smashed_event.yml +++ b/spec/fixtures/scripts/internal_events/events/keyboard_smashed_event.yml @@ -1,6 +1,6 @@ --- description: random event string -category: InternalEventTracking +internal_events: true action: random_name identifiers: - project diff --git a/spec/fixtures/scripts/internal_events/events/secondary_event_with_identifiers.yml b/spec/fixtures/scripts/internal_events/events/secondary_event_with_identifiers.yml index 4e2e77e0c5c..41720dc68b2 100644 --- a/spec/fixtures/scripts/internal_events/events/secondary_event_with_identifiers.yml +++ b/spec/fixtures/scripts/internal_events/events/secondary_event_with_identifiers.yml @@ -1,6 +1,6 @@ --- description: Engineer closes Internal Event CLI -category: InternalEventTracking +internal_events: true action: internal_events_cli_closed identifiers: - project diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb index b9c90e30e05..2c751d0a24f 100644 --- a/spec/lib/api/helpers_spec.rb +++ b/spec/lib/api/helpers_spec.rb @@ -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}") diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb index aec2f48dc4e..697c3a3a5f0 100644 --- a/spec/lib/backup/manager_spec.rb +++ b/spec/lib/backup/manager_spec.rb @@ -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 diff --git a/spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb index e778f07227c..49d591d23bf 100644 --- a/spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb @@ -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 diff --git a/spec/lib/banzai/filter/references/reference_cache_spec.rb b/spec/lib/banzai/filter/references/reference_cache_spec.rb index e45b798823a..7fc9f0f2652 100644 --- a/spec/lib/banzai/filter/references/reference_cache_spec.rb +++ b/spec/lib/banzai/filter/references/reference_cache_spec.rb @@ -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 diff --git a/spec/lib/generators/gitlab/analytics/internal_events_generator_spec.rb b/spec/lib/generators/gitlab/analytics/internal_events_generator_spec.rb index 2d9356ca96d..4156d768875 100644 --- a/spec/lib/generators/gitlab/analytics/internal_events_generator_spec.rb +++ b/spec/lib/generators/gitlab/analytics/internal_events_generator_spec.rb @@ -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, diff --git a/spec/lib/gitlab/tracking/event_definition_spec.rb b/spec/lib/gitlab/tracking/event_definition_spec.rb index fc74079fcd2..88ac55c8b08 100644 --- a/spec/lib/gitlab/tracking/event_definition_spec.rb +++ b/spec/lib/gitlab/tracking/event_definition_spec.rb @@ -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 diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 79b663807b4..a429a7d9cef 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -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 diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 312857e05e3..5f51277b9c0 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -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 diff --git a/spec/requests/api/usage_data_spec.rb b/spec/requests/api/usage_data_spec.rb index ca25eb685a3..c728706f3f8 100644 --- a/spec/requests/api/usage_data_spec.rb +++ b/spec/requests/api/usage_data_spec.rb @@ -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 diff --git a/spec/scripts/internal_events/cli_spec.rb b/spec/scripts/internal_events/cli_spec.rb index 4ea87d3a5a3..553b4a4708c 100644 --- a/spec/scripts/internal_events/cli_spec.rb +++ b/spec/scripts/internal_events/cli_spec.rb @@ -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 diff --git a/spec/support/helpers/gitaly_setup.rb b/spec/support/helpers/gitaly_setup.rb index d1d61bb127e..6272798aa15 100644 --- a/spec/support/helpers/gitaly_setup.rb +++ b/spec/support/helpers/gitaly_setup.rb @@ -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, diff --git a/workhorse/go.mod b/workhorse/go.mod index eb5ab59bca3..0e0e68f23ea 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -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 ) diff --git a/workhorse/go.sum b/workhorse/go.sum index a6697e635d2..3f89873e36c 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -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=