Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
971f05815d
commit
c47ade2adb
|
@ -87,20 +87,22 @@ gemnasium-python-dependency_scanning:
|
|||
|
||||
# Analyze dependencies for malicious behavior
|
||||
# See https://gitlab.com/gitlab-com/gl-security/security-research/package-hunter
|
||||
package_hunter:
|
||||
.package_hunter-base:
|
||||
extends:
|
||||
- .default-retry
|
||||
- .reports:rules:package_hunter
|
||||
stage: test
|
||||
image:
|
||||
name: registry.gitlab.com/gitlab-com/gl-security/security-research/package-hunter-cli:latest
|
||||
entrypoint: [""]
|
||||
variables:
|
||||
DEBUG: '*'
|
||||
HTR_user: '$PACKAGE_HUNTER_USER'
|
||||
HTR_pass: '$PACKAGE_HUNTER_PASS'
|
||||
needs: []
|
||||
allow_failure: true
|
||||
script:
|
||||
before_script:
|
||||
- rm -r spec locale .git app/assets/images doc/
|
||||
- cd .. && tar -I "gzip --best" -cf gitlab.tgz gitlab/
|
||||
- DEBUG=* HTR_user=$PACKAGE_HUNTER_USER HTR_pass=$PACKAGE_HUNTER_PASS node /usr/src/app/cli.js analyze --format gitlab gitlab.tgz | tee $CI_PROJECT_DIR/gl-dependency-scanning-report.json
|
||||
artifacts:
|
||||
paths:
|
||||
- gl-dependency-scanning-report.json
|
||||
|
@ -108,6 +110,20 @@ package_hunter:
|
|||
dependency_scanning: gl-dependency-scanning-report.json
|
||||
expire_in: 1 week
|
||||
|
||||
package_hunter-yarn:
|
||||
extends:
|
||||
- .package_hunter-base
|
||||
- .reports:rules:package_hunter-yarn
|
||||
script:
|
||||
- node /usr/src/app/cli.js analyze --format gitlab --manager yarn gitlab.tgz | tee $CI_PROJECT_DIR/gl-dependency-scanning-report.json
|
||||
|
||||
package_hunter-bundler:
|
||||
extends:
|
||||
- .package_hunter-base
|
||||
- .reports:rules:package_hunter-bundler
|
||||
script:
|
||||
- node /usr/src/app/cli.js analyze --format gitlab --manager bundler gitlab.tgz | tee $CI_PROJECT_DIR/gl-dependency-scanning-report.json
|
||||
|
||||
license_scanning:
|
||||
extends: .default-retry
|
||||
needs: []
|
||||
|
|
|
@ -1099,7 +1099,7 @@
|
|||
- <<: *if-default-branch-schedule-nightly
|
||||
allow_failure: true
|
||||
|
||||
.reports:rules:package_hunter:
|
||||
.reports:rules:package_hunter-yarn:
|
||||
rules:
|
||||
- if: "$PACKAGE_HUNTER_USER == null || $PACKAGE_HUNTER_USER == ''"
|
||||
when: never
|
||||
|
@ -1107,6 +1107,14 @@
|
|||
- <<: *if-merge-request
|
||||
changes: ["yarn.lock"]
|
||||
|
||||
.reports:rules:package_hunter-bundler:
|
||||
rules:
|
||||
- if: "$PACKAGE_HUNTER_USER == null || $PACKAGE_HUNTER_USER == ''"
|
||||
when: never
|
||||
- <<: *if-default-branch-schedule-2-hourly
|
||||
- <<: *if-merge-request
|
||||
changes: ["Gemfile.lock"]
|
||||
|
||||
.reports:rules:license_scanning:
|
||||
rules:
|
||||
- if: '$LICENSE_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\blicense_scanning\b/'
|
||||
|
|
|
@ -30,8 +30,8 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
|
|||
|
||||
## Maintainer checklist
|
||||
|
||||
- [ ] Correct milestone is applied and the title is matching across all backports
|
||||
- [ ] Assigned to `@gitlab-release-tools-bot` with passing CI pipelines and **when all backports including the MR targeting master are ready.**
|
||||
- [ ] Correct milestone is applied and the title is matching across all backports.
|
||||
- [ ] Assigned to `@gitlab-release-tools-bot` with passing CI pipelines.
|
||||
|
||||
/label ~security
|
||||
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -164,7 +164,7 @@ gem 'wikicloth', '0.8.1'
|
|||
gem 'asciidoctor', '~> 2.0.10'
|
||||
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
|
||||
gem 'asciidoctor-plantuml', '~> 0.0.12'
|
||||
gem 'asciidoctor-kroki', '~> 0.4.0', require: false
|
||||
gem 'asciidoctor-kroki', '~> 0.5.0', require: false
|
||||
gem 'rouge', '~> 3.26.0'
|
||||
gem 'truncato', '~> 0.7.11'
|
||||
gem 'bootstrap_form', '~> 4.2.0'
|
||||
|
|
|
@ -92,10 +92,10 @@ GEM
|
|||
faraday_middleware (~> 1.0)
|
||||
faraday_middleware-multi_json (~> 0.0)
|
||||
oauth2 (~> 1.4)
|
||||
asciidoctor (2.0.12)
|
||||
asciidoctor (2.0.15)
|
||||
asciidoctor-include-ext (0.3.1)
|
||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
asciidoctor-kroki (0.4.0)
|
||||
asciidoctor-kroki (0.5.0)
|
||||
asciidoctor (~> 2.0)
|
||||
asciidoctor-plantuml (0.0.12)
|
||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
|
@ -1409,7 +1409,7 @@ DEPENDENCIES
|
|||
asana (~> 0.10.3)
|
||||
asciidoctor (~> 2.0.10)
|
||||
asciidoctor-include-ext (~> 0.3.1)
|
||||
asciidoctor-kroki (~> 0.4.0)
|
||||
asciidoctor-kroki (~> 0.5.0)
|
||||
asciidoctor-plantuml (~> 0.0.12)
|
||||
atlassian-jwt (~> 0.2.0)
|
||||
attr_encrypted (~> 3.1.0)
|
||||
|
|
|
@ -93,9 +93,8 @@ export default {
|
|||
tooltip: '',
|
||||
attrs: {
|
||||
'data-qa-selector': 'edit_button',
|
||||
'data-track-event': 'click_edit',
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
'data-track-label': 'Edit',
|
||||
'data-track-action': 'click_consolidated_edit',
|
||||
'data-track-label': 'edit',
|
||||
},
|
||||
...handleOptions,
|
||||
};
|
||||
|
@ -127,9 +126,8 @@ export default {
|
|||
tooltip: '',
|
||||
attrs: {
|
||||
'data-qa-selector': 'web_ide_button',
|
||||
'data-track-event': 'click_edit_ide',
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
'data-track-label': 'Web IDE',
|
||||
'data-track-action': 'click_consolidated_edit_ide',
|
||||
'data-track-label': 'web_ide',
|
||||
},
|
||||
...handleOptions,
|
||||
};
|
||||
|
|
|
@ -65,7 +65,7 @@ module BlobHelper
|
|||
return unless blob = readable_blob(options, path, project, ref)
|
||||
|
||||
common_classes = "btn gl-button btn-confirm js-edit-blob gl-ml-3 #{options[:extra_class]}"
|
||||
data = { track_event: 'click_edit', track_label: 'Edit' }
|
||||
data = { track_action: 'click_edit', track_label: 'edit' }
|
||||
|
||||
if Feature.enabled?(:web_ide_primary_edit, project.group)
|
||||
common_classes += " btn-inverted"
|
||||
|
@ -85,7 +85,7 @@ module BlobHelper
|
|||
return unless blob
|
||||
|
||||
common_classes = 'btn gl-button btn-confirm ide-edit-button gl-ml-3'
|
||||
data = { track_event: 'click_edit_ide', track_label: 'Web IDE' }
|
||||
data = { track_action: 'click_edit_ide', track_label: 'web_ide' }
|
||||
|
||||
unless Feature.enabled?(:web_ide_primary_edit, project.group)
|
||||
common_classes += " btn-inverted"
|
||||
|
|
|
@ -1,32 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module IntegrationsHelper
|
||||
def integration_event_description(event)
|
||||
case event
|
||||
when "push", "push_events"
|
||||
s_("ProjectService|Trigger event for pushes to the repository.")
|
||||
when "tag_push", "tag_push_events"
|
||||
s_("ProjectService|Trigger event for new tags pushed to the repository.")
|
||||
when "note", "note_events"
|
||||
s_("ProjectService|Trigger event for new comments.")
|
||||
when "confidential_note", "confidential_note_events"
|
||||
s_("ProjectService|Trigger event for new comments on confidential issues.")
|
||||
when "issue", "issue_events"
|
||||
s_("ProjectService|Trigger event when an issue is created, updated, or closed.")
|
||||
when "confidential_issue", "confidential_issue_events"
|
||||
s_("ProjectService|Trigger event when a confidential issue is created, updated, or closed.")
|
||||
when "merge_request", "merge_request_events"
|
||||
s_("ProjectService|Trigger event when a merge request is created, updated, or merged.")
|
||||
when "pipeline", "pipeline_events"
|
||||
s_("ProjectService|Trigger event when a pipeline status changes.")
|
||||
when "wiki_page", "wiki_page_events"
|
||||
s_("ProjectService|Trigger event when a wiki page is created or updated.")
|
||||
when "commit", "commit_events"
|
||||
s_("ProjectService|Trigger event when a commit is created or updated.")
|
||||
when "deployment"
|
||||
s_("ProjectService|Trigger event when a deployment starts or finishes.")
|
||||
when "alert"
|
||||
s_("ProjectService|Trigger event when a new, unique alert is recorded.")
|
||||
def integration_event_description(integration, event)
|
||||
case integration
|
||||
when Integrations::Jira
|
||||
jira_integration_event_description(event)
|
||||
when Integrations::Teamcity
|
||||
teamcity_integration_event_description(event)
|
||||
else
|
||||
default_integration_event_description(event)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -144,6 +126,53 @@ module IntegrationsHelper
|
|||
|
||||
private
|
||||
|
||||
def jira_integration_event_description(event)
|
||||
case event
|
||||
when "merge_request", "merge_request_events"
|
||||
s_("JiraService|Jira comments are created when an issue is referenced in a merge request.")
|
||||
when "commit", "commit_events"
|
||||
s_("JiraService|Jira comments are created when an issue is referenced in a commit.")
|
||||
end
|
||||
end
|
||||
|
||||
def teamcity_integration_event_description(event)
|
||||
case event
|
||||
when 'push', 'push_events'
|
||||
s_('TeamcityIntegration|Trigger TeamCity CI after every push to the repository, except branch delete')
|
||||
when 'merge_request', 'merge_request_events'
|
||||
s_('TeamcityIntegration|Trigger TeamCity CI after a merge request has been created or updated')
|
||||
end
|
||||
end
|
||||
|
||||
def default_integration_event_description(event)
|
||||
case event
|
||||
when "push", "push_events"
|
||||
s_("ProjectService|Trigger event for pushes to the repository.")
|
||||
when "tag_push", "tag_push_events"
|
||||
s_("ProjectService|Trigger event for new tags pushed to the repository.")
|
||||
when "note", "note_events"
|
||||
s_("ProjectService|Trigger event for new comments.")
|
||||
when "confidential_note", "confidential_note_events"
|
||||
s_("ProjectService|Trigger event for new comments on confidential issues.")
|
||||
when "issue", "issue_events"
|
||||
s_("ProjectService|Trigger event when an issue is created, updated, or closed.")
|
||||
when "confidential_issue", "confidential_issue_events"
|
||||
s_("ProjectService|Trigger event when a confidential issue is created, updated, or closed.")
|
||||
when "merge_request", "merge_request_events"
|
||||
s_("ProjectService|Trigger event when a merge request is created, updated, or merged.")
|
||||
when "pipeline", "pipeline_events"
|
||||
s_("ProjectService|Trigger event when a pipeline status changes.")
|
||||
when "wiki_page", "wiki_page_events"
|
||||
s_("ProjectService|Trigger event when a wiki page is created or updated.")
|
||||
when "commit", "commit_events"
|
||||
s_("ProjectService|Trigger event when a commit is created or updated.")
|
||||
when "deployment"
|
||||
s_("ProjectService|Trigger event when a deployment starts or finishes.")
|
||||
when "alert"
|
||||
s_("ProjectService|Trigger event when a new, unique alert is recorded.")
|
||||
end
|
||||
end
|
||||
|
||||
def trigger_events_for_integration(integration)
|
||||
ServiceEventSerializer.new(service: integration).represent(integration.configurable_events).to_json
|
||||
end
|
||||
|
|
|
@ -172,10 +172,6 @@ class Integration < ApplicationRecord
|
|||
'push'
|
||||
end
|
||||
|
||||
def self.event_description(event)
|
||||
IntegrationsHelper.integration_event_description(event)
|
||||
end
|
||||
|
||||
def self.find_or_create_templates
|
||||
create_nonexistent_templates
|
||||
for_template
|
||||
|
|
|
@ -577,15 +577,6 @@ module Integrations
|
|||
data_fields.deployment_server!
|
||||
end
|
||||
end
|
||||
|
||||
def self.event_description(event)
|
||||
case event
|
||||
when "merge_request", "merge_request_events"
|
||||
s_("JiraService|Jira comments are created when an issue is referenced in a merge request.")
|
||||
when "commit", "commit_events"
|
||||
s_("JiraService|Jira comments are created when an issue is referenced in a commit.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -29,15 +29,6 @@ module Integrations
|
|||
def supported_events
|
||||
%w(push merge_request)
|
||||
end
|
||||
|
||||
def event_description(event)
|
||||
case event
|
||||
when 'push', 'push_events'
|
||||
'TeamCity CI will be triggered after every push to the repository except branch delete'
|
||||
when 'merge_request', 'merge_request_events'
|
||||
'TeamCity CI will be triggered after a merge request has been created or updated'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def compose_service_hook
|
||||
|
|
|
@ -14,7 +14,7 @@ class ServiceEventEntity < Grape::Entity
|
|||
end
|
||||
|
||||
expose :description do |event|
|
||||
IntegrationsHelper.integration_event_description(event)
|
||||
IntegrationsHelper.integration_event_description(integration, event)
|
||||
end
|
||||
|
||||
expose :field, if: -> (_, _) { event_field } do
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ServicePing
|
||||
class BuildPayloadService
|
||||
def execute
|
||||
return {} unless allowed_to_report?
|
||||
|
||||
raw_payload
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def allowed_to_report?
|
||||
product_intelligence_enabled? && !User.single_user&.requires_usage_stats_consent?
|
||||
end
|
||||
|
||||
def product_intelligence_enabled?
|
||||
::Gitlab::CurrentSettings.usage_ping_enabled?
|
||||
end
|
||||
|
||||
def raw_payload
|
||||
@raw_payload ||= ::Gitlab::UsageData.data(force_refresh: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ServicePing::BuildPayloadService.prepend_mod_with('ServicePing::BuildPayloadService')
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: load_balancing_refine_load_balancer_methods
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65356
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/335109
|
||||
milestone: '14.1'
|
||||
type: development
|
||||
group: group::memory
|
||||
default_enabled: false
|
|
@ -1,20 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Ensure that locked attributes can not be changed using a counter.
|
||||
# TODO: this can be removed once `asciidoctor` gem is > 2.0.12
|
||||
# and https://github.com/asciidoctor/asciidoctor/issues/3939 is merged
|
||||
module Asciidoctor
|
||||
module DocumentPatch
|
||||
def counter(name, seed = nil)
|
||||
return @parent_document.counter(name, seed) if @parent_document # rubocop: disable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
unless attribute_locked? name
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Asciidoctor::Document
|
||||
prepend Asciidoctor::DocumentPatch
|
||||
end
|
|
@ -237,3 +237,17 @@ For installations from source:
|
|||
```shell
|
||||
RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:packages:migrate
|
||||
```
|
||||
|
||||
You can optionally track progress and verify that all packages migrated successfully.
|
||||
|
||||
From the [PostgreSQL console](https://docs.gitlab.com/omnibus/settings/database.html#connecting-to-the-bundled-postgresql-database)
|
||||
(`sudo gitlab-psql -d gitlabhq_production` for Omnibus GitLab), verify that `objectstg` below (where
|
||||
`file_store=2`) has the count of all packages:
|
||||
|
||||
```shell
|
||||
gitlabhq_production=# SELECT count(*) AS total, sum(case when file_store = '1' then 1 else 0 end) AS filesystem, sum(case when file_store = '2' then 1 else 0 end) AS objectstg FROM packages_package_files;
|
||||
|
||||
total | filesystem | objectstg
|
||||
------+------------+-----------
|
||||
34 | 0 | 34
|
||||
```
|
||||
|
|
|
@ -93,6 +93,7 @@ are very appreciative of the work done by translators and proofreaders!
|
|||
- Portuguese, Brazilian
|
||||
- Paulo George Gomes Bezerra - [GitLab](https://gitlab.com/paulobezerra), [CrowdIn](https://crowdin.com/profile/paulogomes.rep)
|
||||
- André Gama - [GitLab](https://gitlab.com/andregamma), [CrowdIn](https://crowdin.com/profile/ToeOficial)
|
||||
- Eduardo Addad de Oliveira - [GitLab](https://gitlab.com/eduardoaddad), [CrowdIn](https://crowdin.com/profile/eduardoaddad)
|
||||
- Romanian
|
||||
- Proofreaders needed.
|
||||
- Russian
|
||||
|
|
|
@ -134,6 +134,9 @@ If you are not using the GitHub integration, you can still perform an authorizat
|
|||
1. Hit the **List Your GitHub Repositories** button and wait while GitLab reads your repositories' information.
|
||||
Once done, you'll be taken to the importer page to select the repositories to import.
|
||||
|
||||
To use a newer personal access token in imports after previously performing these steps, sign out of
|
||||
your GitLab account and sign in again, or revoke the older personal access token in GitHub.
|
||||
|
||||
### Select which repositories to import
|
||||
|
||||
After you have authorized access to your GitHub repositories, you are redirected to the GitHub importer page and
|
||||
|
|
|
@ -23,14 +23,14 @@ module API
|
|||
|
||||
INTEGRATIONS = integrations.freeze
|
||||
|
||||
integration_classes.each do |service|
|
||||
event_names = service.try(:event_names) || next
|
||||
integration_classes.each do |integration|
|
||||
event_names = integration.try(:event_names) || next
|
||||
event_names.each do |event_name|
|
||||
INTEGRATIONS[service.to_param.tr("_", "-")] << {
|
||||
INTEGRATIONS[integration.to_param.tr("_", "-")] << {
|
||||
required: false,
|
||||
name: event_name.to_sym,
|
||||
type: String,
|
||||
desc: service.event_description(event_name)
|
||||
desc: IntegrationsHelper.integration_event_description(integration, event_name)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,6 +44,52 @@ module Gitlab
|
|||
# TODO: Switch to individual job interval (prereq: https://gitlab.com/gitlab-org/gitlab/-/issues/328801)
|
||||
duration.to_f / batched_migration.interval
|
||||
end
|
||||
|
||||
def split_and_retry!
|
||||
with_lock do
|
||||
raise 'Only failed jobs can be split' unless failed?
|
||||
|
||||
new_batch_size = batch_size / 2
|
||||
|
||||
raise 'Job cannot be split further' if new_batch_size < 1
|
||||
|
||||
batching_strategy = batched_migration.batch_class.new
|
||||
next_batch_bounds = batching_strategy.next_batch(
|
||||
batched_migration.table_name,
|
||||
batched_migration.column_name,
|
||||
batch_min_value: min_value,
|
||||
batch_size: new_batch_size
|
||||
)
|
||||
midpoint = next_batch_bounds.last
|
||||
|
||||
# We don't want the midpoint to go over the existing max_value because
|
||||
# those IDs would already be in the next batched migration job.
|
||||
# This could happen when a lot of records in the current batch are deleted.
|
||||
#
|
||||
# In this case, we just lower the batch size so that future calls to this
|
||||
# method could eventually split the job if it continues to fail.
|
||||
if midpoint >= max_value
|
||||
update!(batch_size: new_batch_size, status: :pending)
|
||||
else
|
||||
old_max_value = max_value
|
||||
|
||||
update!(
|
||||
batch_size: new_batch_size,
|
||||
max_value: midpoint,
|
||||
attempts: 0,
|
||||
status: :pending,
|
||||
started_at: nil,
|
||||
finished_at: nil,
|
||||
metrics: {}
|
||||
)
|
||||
|
||||
new_record = dup
|
||||
new_record.min_value = midpoint.next
|
||||
new_record.max_value = old_max_value
|
||||
new_record.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -165,6 +165,7 @@ module Gitlab
|
|||
# while we only need a single host: https://gitlab.com/gitlab-org/gitlab/-/issues/326125#note_615271604
|
||||
# Also, shuffling the list afterwards doesn't seem to be necessary.
|
||||
# This may be improved by merging this method with `select_up_to_date_host`.
|
||||
# Could be removed when `:load_balancing_refine_load_balancer_methods` FF is rolled out
|
||||
def select_caught_up_hosts(location)
|
||||
all_hosts = @host_list.hosts
|
||||
valid_hosts = all_hosts.select { |host| host.caught_up?(location) }
|
||||
|
@ -201,6 +202,7 @@ module Gitlab
|
|||
true
|
||||
end
|
||||
|
||||
# Could be removed when `:load_balancing_refine_load_balancer_methods` FF is rolled out
|
||||
def set_consistent_hosts_for_request(hosts)
|
||||
RequestStore[VALID_HOSTS_CACHE_KEY] = hosts
|
||||
end
|
||||
|
|
|
@ -53,10 +53,16 @@ module Gitlab
|
|||
# write location. If no such location exists, err on the side of caution.
|
||||
return false unless location
|
||||
|
||||
if ::Feature.enabled?(:load_balancing_refine_load_balancer_methods)
|
||||
load_balancer.select_up_to_date_host(location).tap do |selected|
|
||||
unstick(namespace, id) if selected
|
||||
end
|
||||
else
|
||||
load_balancer.select_caught_up_hosts(location).tap do |selected|
|
||||
unstick(namespace, id) if selected
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Sticks to the primary if necessary, otherwise unsticks an object (if
|
||||
# it was previously stuck to the primary).
|
||||
|
|
|
@ -16,14 +16,20 @@ module Gitlab
|
|||
# end
|
||||
class << self
|
||||
def start(&block)
|
||||
return @metric_start&.call unless block_given?
|
||||
|
||||
@metric_start = block
|
||||
end
|
||||
|
||||
def finish(&block)
|
||||
return @metric_finish&.call unless block_given?
|
||||
|
||||
@metric_finish = block
|
||||
end
|
||||
|
||||
def relation(&block)
|
||||
return @metric_relation&.call unless block_given?
|
||||
|
||||
@metric_relation = block
|
||||
end
|
||||
|
||||
|
@ -32,15 +38,21 @@ module Gitlab
|
|||
@column = column
|
||||
end
|
||||
|
||||
attr_reader :metric_operation, :metric_relation, :metric_start, :metric_finish, :column
|
||||
def cache_start_and_finish_as(cache_key)
|
||||
@cache_key = cache_key
|
||||
end
|
||||
|
||||
attr_reader :metric_operation, :metric_relation, :metric_start, :metric_finish, :column, :cache_key
|
||||
end
|
||||
|
||||
def value
|
||||
start, finish = get_or_cache_batch_ids
|
||||
|
||||
method(self.class.metric_operation)
|
||||
.call(relation,
|
||||
self.class.column,
|
||||
start: self.class.metric_start&.call,
|
||||
finish: self.class.metric_finish&.call)
|
||||
start: start,
|
||||
finish: finish)
|
||||
end
|
||||
|
||||
def to_sql
|
||||
|
@ -73,6 +85,22 @@ module Gitlab
|
|||
raise "Unknown time frame: #{time_frame} for DatabaseMetric"
|
||||
end
|
||||
end
|
||||
|
||||
def get_or_cache_batch_ids
|
||||
return [self.class.start, self.class.finish] unless self.class.cache_key.present?
|
||||
|
||||
key_name = "metric_instrumentation/#{self.class.cache_key}"
|
||||
|
||||
start = Gitlab::Cache.fetch_once("#{key_name}_minimum_id", expires_in: 1.day) do
|
||||
self.class.start
|
||||
end
|
||||
|
||||
finish = Gitlab::Cache.fetch_once("#{key_name}_maximum_id", expires_in: 1.day) do
|
||||
self.class.finish
|
||||
end
|
||||
|
||||
[start, finish]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31905,6 +31905,12 @@ msgstr ""
|
|||
msgid "Team domain"
|
||||
msgstr ""
|
||||
|
||||
msgid "TeamcityIntegration|Trigger TeamCity CI after a merge request has been created or updated"
|
||||
msgstr ""
|
||||
|
||||
msgid "TeamcityIntegration|Trigger TeamCity CI after every push to the repository, except branch delete"
|
||||
msgstr ""
|
||||
|
||||
msgid "Telephone number"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "1.202.0",
|
||||
"@gitlab/tributejs": "1.0.0",
|
||||
"@gitlab/ui": "30.2.0",
|
||||
"@gitlab/ui": "30.2.1",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "6.1.3-2",
|
||||
"@rails/ujs": "6.1.3-2",
|
||||
|
|
|
@ -156,7 +156,6 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ
|
|||
|
||||
<!---->
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -176,7 +176,6 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
|
|||
|
||||
<!---->
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -304,7 +303,6 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
|
|||
|
||||
<!---->
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -15,8 +15,8 @@ const ACTION_EDIT = {
|
|||
tooltip: '',
|
||||
attrs: {
|
||||
'data-qa-selector': 'edit_button',
|
||||
'data-track-event': 'click_edit',
|
||||
'data-track-label': 'Edit',
|
||||
'data-track-action': 'click_consolidated_edit',
|
||||
'data-track-label': 'edit',
|
||||
},
|
||||
};
|
||||
const ACTION_EDIT_CONFIRM_FORK = {
|
||||
|
@ -32,8 +32,8 @@ const ACTION_WEB_IDE = {
|
|||
text: 'Web IDE',
|
||||
attrs: {
|
||||
'data-qa-selector': 'web_ide_button',
|
||||
'data-track-event': 'click_edit_ide',
|
||||
'data-track-label': 'Web IDE',
|
||||
'data-track-action': 'click_consolidated_edit_ide',
|
||||
'data-track-label': 'web_ide',
|
||||
},
|
||||
};
|
||||
const ACTION_WEB_IDE_CONFIRM_FORK = {
|
||||
|
|
|
@ -67,8 +67,8 @@ RSpec.describe BlobHelper do
|
|||
it 'passes on primary tracking attributes' do
|
||||
parsed_link = Capybara.string(link).find_link('Edit')
|
||||
|
||||
expect(parsed_link[:'data-track-event']).to eq("click_edit")
|
||||
expect(parsed_link[:'data-track-label']).to eq("Edit")
|
||||
expect(parsed_link[:'data-track-action']).to eq("click_edit")
|
||||
expect(parsed_link[:'data-track-label']).to eq("edit")
|
||||
expect(parsed_link[:'data-track-property']).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
@ -85,8 +85,8 @@ RSpec.describe BlobHelper do
|
|||
it 'passes on secondary tracking attributes' do
|
||||
parsed_link = Capybara.string(link).find_link('Edit')
|
||||
|
||||
expect(parsed_link[:'data-track-event']).to eq("click_edit")
|
||||
expect(parsed_link[:'data-track-label']).to eq("Edit")
|
||||
expect(parsed_link[:'data-track-action']).to eq("click_edit")
|
||||
expect(parsed_link[:'data-track-label']).to eq("edit")
|
||||
expect(parsed_link[:'data-track-property']).to eq("secondary")
|
||||
end
|
||||
end
|
||||
|
@ -332,8 +332,8 @@ RSpec.describe BlobHelper do
|
|||
it 'passes on secondary tracking attributes' do
|
||||
parsed_link = Capybara.string(link).find_link('Web IDE')
|
||||
|
||||
expect(parsed_link[:'data-track-event']).to eq("click_edit_ide")
|
||||
expect(parsed_link[:'data-track-label']).to eq("Web IDE")
|
||||
expect(parsed_link[:'data-track-action']).to eq("click_edit_ide")
|
||||
expect(parsed_link[:'data-track-label']).to eq("web_ide")
|
||||
expect(parsed_link[:'data-track-property']).to eq("secondary")
|
||||
end
|
||||
end
|
||||
|
@ -350,8 +350,8 @@ RSpec.describe BlobHelper do
|
|||
it 'passes on primary tracking attributes' do
|
||||
parsed_link = Capybara.string(link).find_link('Web IDE')
|
||||
|
||||
expect(parsed_link[:'data-track-event']).to eq("click_edit_ide")
|
||||
expect(parsed_link[:'data-track-label']).to eq("Web IDE")
|
||||
expect(parsed_link[:'data-track-action']).to eq("click_edit_ide")
|
||||
expect(parsed_link[:'data-track-label']).to eq("web_ide")
|
||||
expect(parsed_link[:'data-track-property']).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,22 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe IntegrationsHelper do
|
||||
describe '#integration_event_description' do
|
||||
subject(:description) { helper.integration_event_description(integration, 'merge_request_events') }
|
||||
|
||||
context 'when integration is Jira' do
|
||||
let(:integration) { Integrations::Jira.new }
|
||||
|
||||
it { is_expected.to include('Jira') }
|
||||
end
|
||||
|
||||
context 'when integration is Team City' do
|
||||
let(:integration) { Integrations::Teamcity.new }
|
||||
|
||||
it { is_expected.to include('TeamCity') }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#integration_form_data' do
|
||||
let(:fields) do
|
||||
[
|
||||
|
|
|
@ -124,4 +124,73 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#split_and_retry!' do
|
||||
let!(:job) { create(:batched_background_migration_job, batch_size: 10, min_value: 6, max_value: 15, status: :failed) }
|
||||
|
||||
it 'splits the job into two and marks them as pending' do
|
||||
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
|
||||
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
|
||||
end
|
||||
|
||||
expect { job.split_and_retry! }.to change { described_class.count }.by(1)
|
||||
|
||||
expect(job).to have_attributes(
|
||||
min_value: 6,
|
||||
max_value: 10,
|
||||
batch_size: 5,
|
||||
status: 'pending',
|
||||
attempts: 0,
|
||||
started_at: nil,
|
||||
finished_at: nil,
|
||||
metrics: {}
|
||||
)
|
||||
|
||||
new_job = described_class.last
|
||||
|
||||
expect(new_job).to have_attributes(
|
||||
batched_background_migration_id: job.batched_background_migration_id,
|
||||
min_value: 11,
|
||||
max_value: 15,
|
||||
batch_size: 5,
|
||||
status: 'pending',
|
||||
attempts: 0,
|
||||
started_at: nil,
|
||||
finished_at: nil,
|
||||
metrics: {}
|
||||
)
|
||||
expect(new_job.created_at).not_to eq(job.created_at)
|
||||
end
|
||||
|
||||
context 'when job is not failed' do
|
||||
let!(:job) { create(:batched_background_migration_job, status: :succeeded) }
|
||||
|
||||
it 'raises an exception' do
|
||||
expect { job.split_and_retry! }.to raise_error 'Only failed jobs can be split'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when batch size is already 1' do
|
||||
let!(:job) { create(:batched_background_migration_job, batch_size: 1, status: :failed) }
|
||||
|
||||
it 'raises an exception' do
|
||||
expect { job.split_and_retry! }.to raise_error 'Job cannot be split further'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when computed midpoint is larger than the max value of the batch' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
|
||||
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 16])
|
||||
end
|
||||
end
|
||||
|
||||
it 'lowers the batch size and marks the job as pending' do
|
||||
expect { job.split_and_retry! }.not_to change { described_class.count }
|
||||
|
||||
expect(job.batch_size).to eq(5)
|
||||
expect(job.status).to eq('pending')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -324,6 +324,17 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do
|
|||
.with(:project, 42).and_return('foo')
|
||||
end
|
||||
|
||||
it 'returns true, selects hosts, and unsticks if any secondary has caught up' do
|
||||
expect(lb).to receive(:select_up_to_date_host).and_return(true)
|
||||
expect(described_class).to receive(:unstick).with(:project, 42)
|
||||
expect(described_class.select_caught_up_replicas(:project, 42)).to be true
|
||||
end
|
||||
|
||||
context 'when :load_balancing_refine_load_balancer_methods FF is disabled' do
|
||||
before do
|
||||
stub_feature_flags(load_balancing_refine_load_balancer_methods: false)
|
||||
end
|
||||
|
||||
it 'returns true, selects hosts, and unsticks if any secondary has caught up' do
|
||||
expect(lb).to receive(:select_caught_up_hosts).and_return(true)
|
||||
expect(described_class).to receive(:unstick).with(:project, 42)
|
||||
|
@ -332,3 +343,4 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ RSpec.describe Gitlab::Kroki do
|
|||
|
||||
describe '.formats' do
|
||||
def default_formats
|
||||
%w[bytefield c4plantuml ditaa erd graphviz nomnoml plantuml svgbob umlet vega vegalite wavedrom].freeze
|
||||
%w[bytefield c4plantuml ditaa erd graphviz nomnoml pikchr plantuml svgbob umlet vega vegalite wavedrom].freeze
|
||||
end
|
||||
|
||||
subject { described_class.formats(Gitlab::CurrentSettings) }
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
|
||||
subject do
|
||||
described_class.tap do |m|
|
||||
m.relation { Issue }
|
||||
m.operation :count
|
||||
m.start { m.relation.minimum(:id) }
|
||||
m.finish { m.relation.maximum(:id) }
|
||||
end.new(time_frame: 'all')
|
||||
end
|
||||
|
||||
describe '#value' do
|
||||
let_it_be(:issue_1) { create(:issue) }
|
||||
let_it_be(:issue_2) { create(:issue) }
|
||||
let_it_be(:issue_3) { create(:issue) }
|
||||
let_it_be(:issues) { Issue.all }
|
||||
|
||||
before do
|
||||
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
|
||||
end
|
||||
|
||||
it 'calculates a correct result' do
|
||||
expect(subject.value).to eq(3)
|
||||
end
|
||||
|
||||
it 'does not cache the result of start and finish', :request_store, :use_clean_rails_redis_caching do
|
||||
expect(Gitlab::Cache).not_to receive(:fetch_once)
|
||||
expect(subject).to receive(:count).with(any_args, hash_including(start: issues.min_by(&:id).id, finish: issues.max_by(&:id).id)).and_call_original
|
||||
|
||||
subject.value
|
||||
|
||||
expect(Rails.cache.read('metric_instrumentation/special_issue_count_minimum_id')).to eq(nil)
|
||||
expect(Rails.cache.read('metric_instrumentation/special_issue_count_maximum_id')).to eq(nil)
|
||||
end
|
||||
|
||||
context 'with start and finish not called' do
|
||||
subject do
|
||||
described_class.tap do |m|
|
||||
m.relation { Issue }
|
||||
m.operation :count
|
||||
end.new(time_frame: 'all')
|
||||
end
|
||||
|
||||
it 'calculates a correct result' do
|
||||
expect(subject.value).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with cache_start_and_finish_as called' do
|
||||
subject do
|
||||
described_class.tap do |m|
|
||||
m.relation { Issue }
|
||||
m.operation :count
|
||||
m.start { m.relation.minimum(:id) }
|
||||
m.finish { m.relation.maximum(:id) }
|
||||
m.cache_start_and_finish_as :special_issue_count
|
||||
end.new(time_frame: 'all')
|
||||
end
|
||||
|
||||
it 'caches using the key name passed', :request_store, :use_clean_rails_redis_caching do
|
||||
expect(Gitlab::Cache).to receive(:fetch_once).with('metric_instrumentation/special_issue_count_minimum_id', any_args).and_call_original
|
||||
expect(Gitlab::Cache).to receive(:fetch_once).with('metric_instrumentation/special_issue_count_maximum_id', any_args).and_call_original
|
||||
expect(subject).to receive(:count).with(any_args, hash_including(start: issues.min_by(&:id).id, finish: issues.max_by(&:id).id)).and_call_original
|
||||
|
||||
subject.value
|
||||
|
||||
expect(Rails.cache.read('metric_instrumentation/special_issue_count_minimum_id')).to eq(issues.min_by(&:id).id)
|
||||
expect(Rails.cache.read('metric_instrumentation/special_issue_count_maximum_id')).to eq(issues.max_by(&:id).id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ServicePing::BuildPayloadService do
|
||||
describe '#execute', :without_license do
|
||||
subject(:service_ping_payload) { described_class.new.execute }
|
||||
|
||||
include_context 'stubbed service ping metrics definitions' do
|
||||
let(:subscription_metrics) do
|
||||
[
|
||||
metric_attributes('active_user_count', "Subscription")
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when usage_ping_enabled setting is false' do
|
||||
before do
|
||||
# Gitlab::CurrentSettings.usage_ping_enabled? == false
|
||||
stub_config_setting(usage_ping_enabled: false)
|
||||
end
|
||||
|
||||
it 'returns empty service ping payload' do
|
||||
expect(service_ping_payload).to eq({})
|
||||
end
|
||||
end
|
||||
|
||||
context 'when usage_ping_enabled setting is true' do
|
||||
before do
|
||||
# Gitlab::CurrentSettings.usage_ping_enabled? == true
|
||||
stub_config_setting(usage_ping_enabled: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'complete service ping payload'
|
||||
|
||||
context 'with require stats consent enabled' do
|
||||
before do
|
||||
allow(User).to receive(:single_user).and_return(double(:user, requires_usage_stats_consent?: true))
|
||||
end
|
||||
|
||||
it 'returns empty service ping payload' do
|
||||
expect(service_ping_payload).to eq({})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec::Matchers.define :have_usage_metric do |key_path|
|
||||
match do |payload|
|
||||
payload = payload.deep_stringify_keys
|
||||
|
||||
key_path.split('.').each do |part|
|
||||
break false unless payload&.has_key?(part)
|
||||
|
||||
payload = payload[part]
|
||||
end
|
||||
end
|
||||
|
||||
failure_message do
|
||||
"Payload does not contain metric with key path: '#{key_path}'"
|
||||
end
|
||||
|
||||
failure_message_when_negated do
|
||||
"Payload contains restricted metric with key path: '#{key_path}'"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_context 'stubbed service ping metrics definitions' do
|
||||
include UsageDataHelpers
|
||||
|
||||
let(:metrics_definitions) { standard_metrics + subscription_metrics + operational_metrics + optional_metrics }
|
||||
let(:standard_metrics) do
|
||||
[
|
||||
metric_attributes('uuid', "Standard")
|
||||
]
|
||||
end
|
||||
|
||||
let(:operational_metrics) do
|
||||
[
|
||||
metric_attributes('counts.merge_requests', "Operational"),
|
||||
metric_attributes('counts.todos', "Operational")
|
||||
]
|
||||
end
|
||||
|
||||
let(:optional_metrics) do
|
||||
[
|
||||
metric_attributes('counts.boards', "Optional"),
|
||||
metric_attributes('gitaly.filesystems', '').except('data_category')
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
stub_usage_data_connections
|
||||
stub_object_store_settings
|
||||
|
||||
allow(Gitlab::Usage::MetricDefinition).to(
|
||||
receive(:definitions)
|
||||
.and_return(metrics_definitions.to_h { |definition| [definition['key_path'], Gitlab::Usage::MetricDefinition.new('', definition.symbolize_keys)] })
|
||||
)
|
||||
end
|
||||
|
||||
def metric_attributes(key_path, category)
|
||||
{
|
||||
'key_path' => key_path,
|
||||
'data_category' => category
|
||||
}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'complete service ping payload' do
|
||||
it_behaves_like 'service ping payload with all expected metrics' do
|
||||
let(:expected_metrics) do
|
||||
standard_metrics + subscription_metrics + operational_metrics + optional_metrics
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'service ping payload with all expected metrics' do
|
||||
specify do
|
||||
aggregate_failures do
|
||||
expected_metrics.each do |metric|
|
||||
is_expected.to have_usage_metric metric['key_path']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'service ping payload without restricted metrics' do
|
||||
specify do
|
||||
aggregate_failures do
|
||||
restricted_metrics.each do |metric|
|
||||
is_expected.not_to have_usage_metric metric['key_path']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -908,10 +908,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
|
||||
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
||||
|
||||
"@gitlab/ui@30.2.0":
|
||||
version "30.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-30.2.0.tgz#eceec947f901cca9507a1ac8b3bd0031a5dcacf9"
|
||||
integrity sha512-rYG3HyUHZQyum9+6OKvp45r9b9E/wzAl8rpFyIIZMg6a14JPfsGhdjXqycWlLxf3TAsbTD6MtjQm/z/I8J6V8g==
|
||||
"@gitlab/ui@30.2.1":
|
||||
version "30.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-30.2.1.tgz#be582413712cd2372ff01279e47579f5785591d4"
|
||||
integrity sha512-Pv2w5ZSR7+G5zcaRjvf8KPs7wQRBqAXXNvKOw2pg/aZsRoK+JfN1neNmfeaqO6K/k1TJyiP6inDXAvhU8SqmOg==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
bootstrap-vue "2.18.1"
|
||||
|
|
Loading…
Reference in New Issue