Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
971f05815d
commit
c47ade2adb
41 changed files with 580 additions and 119 deletions
|
@ -87,20 +87,22 @@ gemnasium-python-dependency_scanning:
|
||||||
|
|
||||||
# Analyze dependencies for malicious behavior
|
# Analyze dependencies for malicious behavior
|
||||||
# See https://gitlab.com/gitlab-com/gl-security/security-research/package-hunter
|
# See https://gitlab.com/gitlab-com/gl-security/security-research/package-hunter
|
||||||
package_hunter:
|
.package_hunter-base:
|
||||||
extends:
|
extends:
|
||||||
- .default-retry
|
- .default-retry
|
||||||
- .reports:rules:package_hunter
|
|
||||||
stage: test
|
stage: test
|
||||||
image:
|
image:
|
||||||
name: registry.gitlab.com/gitlab-com/gl-security/security-research/package-hunter-cli:latest
|
name: registry.gitlab.com/gitlab-com/gl-security/security-research/package-hunter-cli:latest
|
||||||
entrypoint: [""]
|
entrypoint: [""]
|
||||||
|
variables:
|
||||||
|
DEBUG: '*'
|
||||||
|
HTR_user: '$PACKAGE_HUNTER_USER'
|
||||||
|
HTR_pass: '$PACKAGE_HUNTER_PASS'
|
||||||
needs: []
|
needs: []
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
script:
|
before_script:
|
||||||
- rm -r spec locale .git app/assets/images doc/
|
- rm -r spec locale .git app/assets/images doc/
|
||||||
- cd .. && tar -I "gzip --best" -cf gitlab.tgz gitlab/
|
- 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:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- gl-dependency-scanning-report.json
|
- gl-dependency-scanning-report.json
|
||||||
|
@ -108,6 +110,20 @@ package_hunter:
|
||||||
dependency_scanning: gl-dependency-scanning-report.json
|
dependency_scanning: gl-dependency-scanning-report.json
|
||||||
expire_in: 1 week
|
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:
|
license_scanning:
|
||||||
extends: .default-retry
|
extends: .default-retry
|
||||||
needs: []
|
needs: []
|
||||||
|
|
|
@ -1099,7 +1099,7 @@
|
||||||
- <<: *if-default-branch-schedule-nightly
|
- <<: *if-default-branch-schedule-nightly
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
.reports:rules:package_hunter:
|
.reports:rules:package_hunter-yarn:
|
||||||
rules:
|
rules:
|
||||||
- if: "$PACKAGE_HUNTER_USER == null || $PACKAGE_HUNTER_USER == ''"
|
- if: "$PACKAGE_HUNTER_USER == null || $PACKAGE_HUNTER_USER == ''"
|
||||||
when: never
|
when: never
|
||||||
|
@ -1107,6 +1107,14 @@
|
||||||
- <<: *if-merge-request
|
- <<: *if-merge-request
|
||||||
changes: ["yarn.lock"]
|
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:
|
.reports:rules:license_scanning:
|
||||||
rules:
|
rules:
|
||||||
- if: '$LICENSE_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\blicense_scanning\b/'
|
- 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
|
## Maintainer checklist
|
||||||
|
|
||||||
- [ ] Correct milestone is applied and the title is matching across all backports
|
- [ ] 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.**
|
- [ ] Assigned to `@gitlab-release-tools-bot` with passing CI pipelines.
|
||||||
|
|
||||||
/label ~security
|
/label ~security
|
||||||
|
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -164,7 +164,7 @@ gem 'wikicloth', '0.8.1'
|
||||||
gem 'asciidoctor', '~> 2.0.10'
|
gem 'asciidoctor', '~> 2.0.10'
|
||||||
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
|
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
|
||||||
gem 'asciidoctor-plantuml', '~> 0.0.12'
|
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 'rouge', '~> 3.26.0'
|
||||||
gem 'truncato', '~> 0.7.11'
|
gem 'truncato', '~> 0.7.11'
|
||||||
gem 'bootstrap_form', '~> 4.2.0'
|
gem 'bootstrap_form', '~> 4.2.0'
|
||||||
|
|
|
@ -92,10 +92,10 @@ GEM
|
||||||
faraday_middleware (~> 1.0)
|
faraday_middleware (~> 1.0)
|
||||||
faraday_middleware-multi_json (~> 0.0)
|
faraday_middleware-multi_json (~> 0.0)
|
||||||
oauth2 (~> 1.4)
|
oauth2 (~> 1.4)
|
||||||
asciidoctor (2.0.12)
|
asciidoctor (2.0.15)
|
||||||
asciidoctor-include-ext (0.3.1)
|
asciidoctor-include-ext (0.3.1)
|
||||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||||
asciidoctor-kroki (0.4.0)
|
asciidoctor-kroki (0.5.0)
|
||||||
asciidoctor (~> 2.0)
|
asciidoctor (~> 2.0)
|
||||||
asciidoctor-plantuml (0.0.12)
|
asciidoctor-plantuml (0.0.12)
|
||||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||||
|
@ -1409,7 +1409,7 @@ DEPENDENCIES
|
||||||
asana (~> 0.10.3)
|
asana (~> 0.10.3)
|
||||||
asciidoctor (~> 2.0.10)
|
asciidoctor (~> 2.0.10)
|
||||||
asciidoctor-include-ext (~> 0.3.1)
|
asciidoctor-include-ext (~> 0.3.1)
|
||||||
asciidoctor-kroki (~> 0.4.0)
|
asciidoctor-kroki (~> 0.5.0)
|
||||||
asciidoctor-plantuml (~> 0.0.12)
|
asciidoctor-plantuml (~> 0.0.12)
|
||||||
atlassian-jwt (~> 0.2.0)
|
atlassian-jwt (~> 0.2.0)
|
||||||
attr_encrypted (~> 3.1.0)
|
attr_encrypted (~> 3.1.0)
|
||||||
|
|
|
@ -93,9 +93,8 @@ export default {
|
||||||
tooltip: '',
|
tooltip: '',
|
||||||
attrs: {
|
attrs: {
|
||||||
'data-qa-selector': 'edit_button',
|
'data-qa-selector': 'edit_button',
|
||||||
'data-track-event': 'click_edit',
|
'data-track-action': 'click_consolidated_edit',
|
||||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
'data-track-label': 'edit',
|
||||||
'data-track-label': 'Edit',
|
|
||||||
},
|
},
|
||||||
...handleOptions,
|
...handleOptions,
|
||||||
};
|
};
|
||||||
|
@ -127,9 +126,8 @@ export default {
|
||||||
tooltip: '',
|
tooltip: '',
|
||||||
attrs: {
|
attrs: {
|
||||||
'data-qa-selector': 'web_ide_button',
|
'data-qa-selector': 'web_ide_button',
|
||||||
'data-track-event': 'click_edit_ide',
|
'data-track-action': 'click_consolidated_edit_ide',
|
||||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
'data-track-label': 'web_ide',
|
||||||
'data-track-label': 'Web IDE',
|
|
||||||
},
|
},
|
||||||
...handleOptions,
|
...handleOptions,
|
||||||
};
|
};
|
||||||
|
|
|
@ -65,7 +65,7 @@ module BlobHelper
|
||||||
return unless blob = readable_blob(options, path, project, ref)
|
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]}"
|
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)
|
if Feature.enabled?(:web_ide_primary_edit, project.group)
|
||||||
common_classes += " btn-inverted"
|
common_classes += " btn-inverted"
|
||||||
|
@ -85,7 +85,7 @@ module BlobHelper
|
||||||
return unless blob
|
return unless blob
|
||||||
|
|
||||||
common_classes = 'btn gl-button btn-confirm ide-edit-button gl-ml-3'
|
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)
|
unless Feature.enabled?(:web_ide_primary_edit, project.group)
|
||||||
common_classes += " btn-inverted"
|
common_classes += " btn-inverted"
|
||||||
|
|
|
@ -1,32 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module IntegrationsHelper
|
module IntegrationsHelper
|
||||||
def integration_event_description(event)
|
def integration_event_description(integration, event)
|
||||||
case event
|
case integration
|
||||||
when "push", "push_events"
|
when Integrations::Jira
|
||||||
s_("ProjectService|Trigger event for pushes to the repository.")
|
jira_integration_event_description(event)
|
||||||
when "tag_push", "tag_push_events"
|
when Integrations::Teamcity
|
||||||
s_("ProjectService|Trigger event for new tags pushed to the repository.")
|
teamcity_integration_event_description(event)
|
||||||
when "note", "note_events"
|
else
|
||||||
s_("ProjectService|Trigger event for new comments.")
|
default_integration_event_description(event)
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -144,6 +126,53 @@ module IntegrationsHelper
|
||||||
|
|
||||||
private
|
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)
|
def trigger_events_for_integration(integration)
|
||||||
ServiceEventSerializer.new(service: integration).represent(integration.configurable_events).to_json
|
ServiceEventSerializer.new(service: integration).represent(integration.configurable_events).to_json
|
||||||
end
|
end
|
||||||
|
|
|
@ -172,10 +172,6 @@ class Integration < ApplicationRecord
|
||||||
'push'
|
'push'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.event_description(event)
|
|
||||||
IntegrationsHelper.integration_event_description(event)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.find_or_create_templates
|
def self.find_or_create_templates
|
||||||
create_nonexistent_templates
|
create_nonexistent_templates
|
||||||
for_template
|
for_template
|
||||||
|
|
|
@ -577,15 +577,6 @@ module Integrations
|
||||||
data_fields.deployment_server!
|
data_fields.deployment_server!
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -29,15 +29,6 @@ module Integrations
|
||||||
def supported_events
|
def supported_events
|
||||||
%w(push merge_request)
|
%w(push merge_request)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def compose_service_hook
|
def compose_service_hook
|
||||||
|
|
|
@ -14,7 +14,7 @@ class ServiceEventEntity < Grape::Entity
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :description do |event|
|
expose :description do |event|
|
||||||
IntegrationsHelper.integration_event_description(event)
|
IntegrationsHelper.integration_event_description(integration, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :field, if: -> (_, _) { event_field } do
|
expose :field, if: -> (_, _) { event_field } do
|
||||||
|
|
27
app/services/service_ping/build_payload_service.rb
Normal file
27
app/services/service_ping/build_payload_service.rb
Normal file
|
@ -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
|
```shell
|
||||||
RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:packages:migrate
|
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
|
- Portuguese, Brazilian
|
||||||
- Paulo George Gomes Bezerra - [GitLab](https://gitlab.com/paulobezerra), [CrowdIn](https://crowdin.com/profile/paulogomes.rep)
|
- 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)
|
- 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
|
- Romanian
|
||||||
- Proofreaders needed.
|
- Proofreaders needed.
|
||||||
- Russian
|
- 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.
|
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.
|
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
|
### Select which repositories to import
|
||||||
|
|
||||||
After you have authorized access to your GitHub repositories, you are redirected to the GitHub importer page and
|
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
|
INTEGRATIONS = integrations.freeze
|
||||||
|
|
||||||
integration_classes.each do |service|
|
integration_classes.each do |integration|
|
||||||
event_names = service.try(:event_names) || next
|
event_names = integration.try(:event_names) || next
|
||||||
event_names.each do |event_name|
|
event_names.each do |event_name|
|
||||||
INTEGRATIONS[service.to_param.tr("_", "-")] << {
|
INTEGRATIONS[integration.to_param.tr("_", "-")] << {
|
||||||
required: false,
|
required: false,
|
||||||
name: event_name.to_sym,
|
name: event_name.to_sym,
|
||||||
type: String,
|
type: String,
|
||||||
desc: service.event_description(event_name)
|
desc: IntegrationsHelper.integration_event_description(integration, event_name)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,6 +44,52 @@ module Gitlab
|
||||||
# TODO: Switch to individual job interval (prereq: https://gitlab.com/gitlab-org/gitlab/-/issues/328801)
|
# TODO: Switch to individual job interval (prereq: https://gitlab.com/gitlab-org/gitlab/-/issues/328801)
|
||||||
duration.to_f / batched_migration.interval
|
duration.to_f / batched_migration.interval
|
||||||
end
|
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
|
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
|
# 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.
|
# 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`.
|
# 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)
|
def select_caught_up_hosts(location)
|
||||||
all_hosts = @host_list.hosts
|
all_hosts = @host_list.hosts
|
||||||
valid_hosts = all_hosts.select { |host| host.caught_up?(location) }
|
valid_hosts = all_hosts.select { |host| host.caught_up?(location) }
|
||||||
|
@ -201,6 +202,7 @@ module Gitlab
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Could be removed when `:load_balancing_refine_load_balancer_methods` FF is rolled out
|
||||||
def set_consistent_hosts_for_request(hosts)
|
def set_consistent_hosts_for_request(hosts)
|
||||||
RequestStore[VALID_HOSTS_CACHE_KEY] = hosts
|
RequestStore[VALID_HOSTS_CACHE_KEY] = hosts
|
||||||
end
|
end
|
||||||
|
|
|
@ -53,8 +53,14 @@ module Gitlab
|
||||||
# write location. If no such location exists, err on the side of caution.
|
# write location. If no such location exists, err on the side of caution.
|
||||||
return false unless location
|
return false unless location
|
||||||
|
|
||||||
load_balancer.select_caught_up_hosts(location).tap do |selected|
|
if ::Feature.enabled?(:load_balancing_refine_load_balancer_methods)
|
||||||
unstick(namespace, id) if selected
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -16,14 +16,20 @@ module Gitlab
|
||||||
# end
|
# end
|
||||||
class << self
|
class << self
|
||||||
def start(&block)
|
def start(&block)
|
||||||
|
return @metric_start&.call unless block_given?
|
||||||
|
|
||||||
@metric_start = block
|
@metric_start = block
|
||||||
end
|
end
|
||||||
|
|
||||||
def finish(&block)
|
def finish(&block)
|
||||||
|
return @metric_finish&.call unless block_given?
|
||||||
|
|
||||||
@metric_finish = block
|
@metric_finish = block
|
||||||
end
|
end
|
||||||
|
|
||||||
def relation(&block)
|
def relation(&block)
|
||||||
|
return @metric_relation&.call unless block_given?
|
||||||
|
|
||||||
@metric_relation = block
|
@metric_relation = block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,15 +38,21 @@ module Gitlab
|
||||||
@column = column
|
@column = column
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def value
|
def value
|
||||||
|
start, finish = get_or_cache_batch_ids
|
||||||
|
|
||||||
method(self.class.metric_operation)
|
method(self.class.metric_operation)
|
||||||
.call(relation,
|
.call(relation,
|
||||||
self.class.column,
|
self.class.column,
|
||||||
start: self.class.metric_start&.call,
|
start: start,
|
||||||
finish: self.class.metric_finish&.call)
|
finish: finish)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_sql
|
def to_sql
|
||||||
|
@ -73,6 +85,22 @@ module Gitlab
|
||||||
raise "Unknown time frame: #{time_frame} for DatabaseMetric"
|
raise "Unknown time frame: #{time_frame} for DatabaseMetric"
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31905,6 +31905,12 @@ msgstr ""
|
||||||
msgid "Team domain"
|
msgid "Team domain"
|
||||||
msgstr ""
|
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"
|
msgid "Telephone number"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
"@gitlab/favicon-overlay": "2.0.0",
|
"@gitlab/favicon-overlay": "2.0.0",
|
||||||
"@gitlab/svgs": "1.202.0",
|
"@gitlab/svgs": "1.202.0",
|
||||||
"@gitlab/tributejs": "1.0.0",
|
"@gitlab/tributejs": "1.0.0",
|
||||||
"@gitlab/ui": "30.2.0",
|
"@gitlab/ui": "30.2.1",
|
||||||
"@gitlab/visual-review-tools": "1.6.1",
|
"@gitlab/visual-review-tools": "1.6.1",
|
||||||
"@rails/actioncable": "6.1.3-2",
|
"@rails/actioncable": "6.1.3-2",
|
||||||
"@rails/ujs": "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>
|
</div>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -176,7 +176,6 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
|
||||||
|
|
||||||
<!---->
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@ -304,7 +303,6 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
|
||||||
|
|
||||||
<!---->
|
<!---->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -15,8 +15,8 @@ const ACTION_EDIT = {
|
||||||
tooltip: '',
|
tooltip: '',
|
||||||
attrs: {
|
attrs: {
|
||||||
'data-qa-selector': 'edit_button',
|
'data-qa-selector': 'edit_button',
|
||||||
'data-track-event': 'click_edit',
|
'data-track-action': 'click_consolidated_edit',
|
||||||
'data-track-label': 'Edit',
|
'data-track-label': 'edit',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const ACTION_EDIT_CONFIRM_FORK = {
|
const ACTION_EDIT_CONFIRM_FORK = {
|
||||||
|
@ -32,8 +32,8 @@ const ACTION_WEB_IDE = {
|
||||||
text: 'Web IDE',
|
text: 'Web IDE',
|
||||||
attrs: {
|
attrs: {
|
||||||
'data-qa-selector': 'web_ide_button',
|
'data-qa-selector': 'web_ide_button',
|
||||||
'data-track-event': 'click_edit_ide',
|
'data-track-action': 'click_consolidated_edit_ide',
|
||||||
'data-track-label': 'Web IDE',
|
'data-track-label': 'web_ide',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const ACTION_WEB_IDE_CONFIRM_FORK = {
|
const ACTION_WEB_IDE_CONFIRM_FORK = {
|
||||||
|
|
|
@ -67,8 +67,8 @@ RSpec.describe BlobHelper do
|
||||||
it 'passes on primary tracking attributes' do
|
it 'passes on primary tracking attributes' do
|
||||||
parsed_link = Capybara.string(link).find_link('Edit')
|
parsed_link = Capybara.string(link).find_link('Edit')
|
||||||
|
|
||||||
expect(parsed_link[:'data-track-event']).to eq("click_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-label']).to eq("edit")
|
||||||
expect(parsed_link[:'data-track-property']).to eq(nil)
|
expect(parsed_link[:'data-track-property']).to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -85,8 +85,8 @@ RSpec.describe BlobHelper do
|
||||||
it 'passes on secondary tracking attributes' do
|
it 'passes on secondary tracking attributes' do
|
||||||
parsed_link = Capybara.string(link).find_link('Edit')
|
parsed_link = Capybara.string(link).find_link('Edit')
|
||||||
|
|
||||||
expect(parsed_link[:'data-track-event']).to eq("click_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-label']).to eq("edit")
|
||||||
expect(parsed_link[:'data-track-property']).to eq("secondary")
|
expect(parsed_link[:'data-track-property']).to eq("secondary")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -332,8 +332,8 @@ RSpec.describe BlobHelper do
|
||||||
it 'passes on secondary tracking attributes' do
|
it 'passes on secondary tracking attributes' do
|
||||||
parsed_link = Capybara.string(link).find_link('Web IDE')
|
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-action']).to eq("click_edit_ide")
|
||||||
expect(parsed_link[:'data-track-label']).to eq("Web IDE")
|
expect(parsed_link[:'data-track-label']).to eq("web_ide")
|
||||||
expect(parsed_link[:'data-track-property']).to eq("secondary")
|
expect(parsed_link[:'data-track-property']).to eq("secondary")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -350,8 +350,8 @@ RSpec.describe BlobHelper do
|
||||||
it 'passes on primary tracking attributes' do
|
it 'passes on primary tracking attributes' do
|
||||||
parsed_link = Capybara.string(link).find_link('Web IDE')
|
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-action']).to eq("click_edit_ide")
|
||||||
expect(parsed_link[:'data-track-label']).to eq("Web IDE")
|
expect(parsed_link[:'data-track-label']).to eq("web_ide")
|
||||||
expect(parsed_link[:'data-track-property']).to eq(nil)
|
expect(parsed_link[:'data-track-property']).to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,22 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe IntegrationsHelper do
|
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
|
describe '#integration_form_data' do
|
||||||
let(:fields) do
|
let(:fields) do
|
||||||
[
|
[
|
||||||
|
|
|
@ -124,4 +124,73 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -325,10 +325,22 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true, selects hosts, and unsticks if any secondary has caught up' do
|
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(lb).to receive(:select_up_to_date_host).and_return(true)
|
||||||
expect(described_class).to receive(:unstick).with(:project, 42)
|
expect(described_class).to receive(:unstick).with(:project, 42)
|
||||||
expect(described_class.select_caught_up_replicas(:project, 42)).to be true
|
expect(described_class.select_caught_up_replicas(:project, 42)).to be true
|
||||||
end
|
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)
|
||||||
|
expect(described_class.select_caught_up_replicas(:project, 42)).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ RSpec.describe Gitlab::Kroki do
|
||||||
|
|
||||||
describe '.formats' do
|
describe '.formats' do
|
||||||
def default_formats
|
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
|
end
|
||||||
|
|
||||||
subject { described_class.formats(Gitlab::CurrentSettings) }
|
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
|
47
spec/services/service_ping/build_payload_service_spec.rb
Normal file
47
spec/services/service_ping/build_payload_service_spec.rb
Normal file
|
@ -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
|
21
spec/support/matchers/usage_metric_matchers.rb
Normal file
21
spec/support/matchers/usage_metric_matchers.rb
Normal file
|
@ -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"
|
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
|
||||||
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
||||||
|
|
||||||
"@gitlab/ui@30.2.0":
|
"@gitlab/ui@30.2.1":
|
||||||
version "30.2.0"
|
version "30.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-30.2.0.tgz#eceec947f901cca9507a1ac8b3bd0031a5dcacf9"
|
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-30.2.1.tgz#be582413712cd2372ff01279e47579f5785591d4"
|
||||||
integrity sha512-rYG3HyUHZQyum9+6OKvp45r9b9E/wzAl8rpFyIIZMg6a14JPfsGhdjXqycWlLxf3TAsbTD6MtjQm/z/I8J6V8g==
|
integrity sha512-Pv2w5ZSR7+G5zcaRjvf8KPs7wQRBqAXXNvKOw2pg/aZsRoK+JfN1neNmfeaqO6K/k1TJyiP6inDXAvhU8SqmOg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/standalone" "^7.0.0"
|
"@babel/standalone" "^7.0.0"
|
||||||
bootstrap-vue "2.18.1"
|
bootstrap-vue "2.18.1"
|
||||||
|
|
Loading…
Reference in a new issue