Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9086e66ee7
commit
7e1e5ca371
|
|
@ -17,7 +17,7 @@ stages:
|
|||
# in cases where jobs require Docker-in-Docker, the job
|
||||
# definition must be extended with `.use-docker-in-docker`
|
||||
default:
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
|
||||
tags:
|
||||
- gitlab-org
|
||||
# All jobs are interruptible by default
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
cloud-native-image:
|
||||
extends: .cng:rules
|
||||
image: ruby:2.6-alpine
|
||||
image: ruby:2.7-alpine
|
||||
dependencies: []
|
||||
stage: post-test
|
||||
variables:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
extends:
|
||||
- .default-retry
|
||||
- .docs:rules:review-docs
|
||||
image: ruby:2.6-alpine
|
||||
image: ruby:2.7-alpine
|
||||
stage: review
|
||||
needs: []
|
||||
variables:
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
extends:
|
||||
- .frontend-base
|
||||
- .assets-compile-cache
|
||||
image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-git-2.28-lfs-2.9-node-12.18-yarn-1.22-graphicsmagick-1.3.34
|
||||
image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-git-2.28-lfs-2.9-node-12.18-yarn-1.22-graphicsmagick-1.3.34
|
||||
variables:
|
||||
WEBPACK_VENDOR_DLL: "true"
|
||||
stage: prepare
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
.rails-cache:
|
||||
cache:
|
||||
key: "rails-v2"
|
||||
key: "rails-v3"
|
||||
paths:
|
||||
- vendor/ruby/
|
||||
- vendor/gitaly-ruby/
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
.static-analysis-cache:
|
||||
cache:
|
||||
key: "static-analysis-v1"
|
||||
key: "static-analysis-v2"
|
||||
paths:
|
||||
- vendor/ruby/
|
||||
- node_modules/
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
.qa-cache:
|
||||
cache:
|
||||
key: "qa-v1"
|
||||
key: "qa-v2"
|
||||
paths:
|
||||
- qa/vendor/ruby/
|
||||
policy: pull
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
policy: pull
|
||||
|
||||
.use-pg11:
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
|
||||
services:
|
||||
- name: postgres:11.6
|
||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
|
||||
.use-pg12:
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
|
||||
services:
|
||||
- name: postgres:12
|
||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||
|
|
@ -89,7 +89,7 @@
|
|||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
|
||||
.use-pg11-ee:
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
|
||||
services:
|
||||
- name: postgres:11.6
|
||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
|
||||
.use-pg12-ee:
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
|
||||
services:
|
||||
- name: postgres:12
|
||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
before_script:
|
||||
- '[ "$FOSS_ONLY" = "1" ] && rm -rf ee/ qa/spec/ee/ qa/qa/specs/features/ee/ qa/qa/ee/ qa/qa/ee.rb'
|
||||
- cd qa/
|
||||
- bundle install --clean --jobs=$(nproc) --path=vendor --retry=3 --quiet
|
||||
- gem install bundler -v 1.17.3
|
||||
- bundle install --clean --jobs=$(nproc) --path=vendor --retry=3 --without=development --quiet
|
||||
- bundle check
|
||||
|
||||
qa:internal:
|
||||
|
|
@ -47,7 +48,7 @@ update-qa-cache:
|
|||
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
|
||||
|
||||
.package-and-qa-base:
|
||||
image: ruby:2.6-alpine
|
||||
image: ruby:2.7-alpine
|
||||
stage: qa
|
||||
retry: 0
|
||||
script:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
######################
|
||||
#######################
|
||||
# rspec job base specs
|
||||
.rails-job-base:
|
||||
extends:
|
||||
|
|
@ -181,6 +181,7 @@ update-coverage-cache:
|
|||
- .shared:rules:update-cache
|
||||
stage: prepare
|
||||
script:
|
||||
- run_timed_command "gem install bundler -v 1.17.3"
|
||||
- run_timed_command "bundle install --jobs=$(nproc) --path=vendor --retry=3 --quiet --without default development test production puma unicorn kerberos metrics omnibus ed25519"
|
||||
cache:
|
||||
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
|
||||
|
|
@ -358,6 +359,7 @@ rspec:coverage:
|
|||
- memory-static
|
||||
- memory-on-boot
|
||||
script:
|
||||
- run_timed_command "gem install bundler -v 1.17.3"
|
||||
- run_timed_command "bundle install --jobs=$(nproc) --path=vendor --retry=3 --quiet --without default development test production puma unicorn kerberos metrics omnibus ed25519"
|
||||
- run_timed_command "bundle exec scripts/merge-simplecov"
|
||||
- run_timed_command "bundle exec scripts/gather-test-memory-data"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ review-build-cng:
|
|||
extends:
|
||||
- .default-retry
|
||||
- .review:rules:review-build-cng
|
||||
image: ruby:2.6-alpine
|
||||
image: ruby:2.7-alpine
|
||||
stage: review-prepare
|
||||
before_script:
|
||||
- source ./scripts/utils.sh
|
||||
|
|
@ -122,7 +122,7 @@ review-stop:
|
|||
extends:
|
||||
- .default-retry
|
||||
- .use-docker-in-docker
|
||||
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.6
|
||||
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7
|
||||
stage: qa
|
||||
# This is needed so that manual jobs with needs don't block the pipeline.
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/199979.
|
||||
|
|
@ -199,7 +199,7 @@ review-performance:
|
|||
parallel-spec-reports:
|
||||
extends:
|
||||
- .review:rules:mr-only-manual
|
||||
image: ruby:2.6-alpine
|
||||
image: ruby:2.7-alpine
|
||||
stage: post-qa
|
||||
dependencies: ["review-qa-all"]
|
||||
variables:
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ no_ee_check:
|
|||
verify-tests-yml:
|
||||
extends:
|
||||
- .setup:rules:verify-tests-yml
|
||||
image: ruby:2.6-alpine
|
||||
image: ruby:2.7-alpine
|
||||
stage: test
|
||||
needs: []
|
||||
script:
|
||||
|
|
@ -61,7 +61,7 @@ verify-tests-yml:
|
|||
- scripts/verify-tff-mapping
|
||||
|
||||
.detect-test-base:
|
||||
image: ruby:2.6-alpine
|
||||
image: ruby:2.7-alpine
|
||||
needs: []
|
||||
stage: prepare
|
||||
script:
|
||||
|
|
|
|||
|
|
@ -55,6 +55,14 @@ All reviewers can help ensure accuracy, clarity, completeness, and adherence to
|
|||
|
||||
For more information about labels, see [Technical Writing workflows - Labels](https://about.gitlab.com/handbook/engineering/ux/technical-writing/workflow/#labels).
|
||||
|
||||
For suggestions that you are confident don't need to be reviewed, change them locally
|
||||
and push a commit directly to save others from unneeded reviews. For example:
|
||||
|
||||
- Clear typos, like `this is a typpo`.
|
||||
- Minor issues, like single quotes instead of double quotes, Oxford commas, and periods.
|
||||
|
||||
For more information, see our documentation on [Merging a merge request](https://docs.gitlab.com/ee/development/code_review.html#merging-a-merge-request).
|
||||
|
||||
**3. Maintainer**
|
||||
|
||||
1. [ ] Review by assigned maintainer, who can always request/require the above reviews. Maintainer's review can occur before or after a technical writer review.
|
||||
|
|
|
|||
|
|
@ -1269,7 +1269,6 @@ FactoryBot/InlineAssociation:
|
|||
- 'ee/spec/factories/resource_weight_events.rb'
|
||||
- 'ee/spec/factories/vulnerabilities/feedback.rb'
|
||||
- 'spec/factories/atlassian_identities.rb'
|
||||
- 'spec/factories/audit_events.rb'
|
||||
- 'spec/factories/design_management/design_at_version.rb'
|
||||
- 'spec/factories/design_management/designs.rb'
|
||||
- 'spec/factories/design_management/versions.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2.6.6
|
||||
2.7.2
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the
|
|||
GitLab is a Ruby on Rails application that runs on the following software:
|
||||
|
||||
- Ubuntu/Debian/CentOS/RHEL/OpenSUSE
|
||||
- Ruby (MRI) 2.6.6
|
||||
- Ruby (MRI) 2.7.2
|
||||
- Git 2.24+
|
||||
- Redis 4.0+
|
||||
- PostgreSQL 11+
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
@import './pages/admin';
|
||||
@import './pages/alert_management/details';
|
||||
@import './pages/alert_management/severity-icons';
|
||||
@import './pages/branches';
|
||||
@import './pages/builds';
|
||||
@import './pages/ci_projects';
|
||||
|
|
|
|||
|
|
@ -2,26 +2,26 @@
|
|||
.incident-management-list,
|
||||
.alert-management-details {
|
||||
.icon-critical {
|
||||
color: $red-800;
|
||||
@include gl-text-red-800;
|
||||
}
|
||||
|
||||
.icon-high {
|
||||
color: $red-600;
|
||||
@include gl-text-red-600;
|
||||
}
|
||||
|
||||
.icon-medium {
|
||||
color: $orange-400;
|
||||
@include gl-text-orange-400;
|
||||
}
|
||||
|
||||
.icon-low {
|
||||
color: $orange-300;
|
||||
@include gl-text-orange-300;
|
||||
}
|
||||
|
||||
.icon-info {
|
||||
color: $blue-400;
|
||||
@include gl-text-blue-400;
|
||||
}
|
||||
|
||||
.icon-unknown {
|
||||
color: $gray-200;
|
||||
@include gl-text-gray-200;
|
||||
}
|
||||
}
|
||||
|
|
@ -105,10 +105,6 @@
|
|||
content: '\f110';
|
||||
}
|
||||
|
||||
.fa-trash-o::before {
|
||||
content: '\f014';
|
||||
}
|
||||
|
||||
.fa-caret-right::before {
|
||||
content: '\f0da';
|
||||
}
|
||||
|
|
@ -117,18 +113,10 @@
|
|||
content: '\f077';
|
||||
}
|
||||
|
||||
.fa-bug::before {
|
||||
content: '\f188';
|
||||
}
|
||||
|
||||
.fa-exclamation-circle::before {
|
||||
content: '\f06a';
|
||||
}
|
||||
|
||||
.fa-bell::before {
|
||||
content: '\f0f3';
|
||||
}
|
||||
|
||||
.fa-file-o::before {
|
||||
content: '\f016';
|
||||
}
|
||||
|
|
@ -141,10 +129,6 @@
|
|||
content: '\f111';
|
||||
}
|
||||
|
||||
.fa-git::before {
|
||||
content: '\f1d3';
|
||||
}
|
||||
|
||||
.fa-thumb-tack::before {
|
||||
content: '\f08d';
|
||||
}
|
||||
|
|
@ -153,38 +137,6 @@
|
|||
content: '\f06d';
|
||||
}
|
||||
|
||||
.fa-pause::before {
|
||||
content: '\f04c';
|
||||
}
|
||||
|
||||
.fa-play::before {
|
||||
content: '\f04b';
|
||||
}
|
||||
|
||||
.fa-share::before {
|
||||
content: '\f064';
|
||||
}
|
||||
|
||||
.fa-book::before {
|
||||
content: '\f02d';
|
||||
}
|
||||
|
||||
.fa-times-circle::before {
|
||||
content: '\f057';
|
||||
}
|
||||
|
||||
.fa-skype::before {
|
||||
content: '\f17e';
|
||||
}
|
||||
|
||||
.fa-linkedin-square::before {
|
||||
content: '\f08c';
|
||||
}
|
||||
|
||||
.fa-twitter-square::before {
|
||||
content: '\f081';
|
||||
}
|
||||
|
||||
.fa-file-pdf-o::before {
|
||||
content: '\f1c1';
|
||||
}
|
||||
|
|
@ -217,6 +169,14 @@
|
|||
content: '\f1c8';
|
||||
}
|
||||
|
||||
.fa-square-o::before {
|
||||
content: '\f096';
|
||||
}
|
||||
|
||||
.fa-check-square-o::before {
|
||||
content: '\f046';
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
|
|
|
|||
|
|
@ -1,24 +1,26 @@
|
|||
@import 'mixins_and_variables_and_functions';
|
||||
|
||||
.alert-management-details {
|
||||
@include media-breakpoint-down(xs) {
|
||||
.alert-details-incident-button {
|
||||
width: 100%;
|
||||
@include gl-w-full;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-sidebar-mobile-button {
|
||||
right: 0;
|
||||
@include gl-right-0;
|
||||
}
|
||||
|
||||
.dropdown-menu-toggle {
|
||||
&:hover {
|
||||
background-color: $white;
|
||||
@include gl-bg-white;
|
||||
}
|
||||
}
|
||||
|
||||
.assignee-dropdown-item {
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@include gl-display-flex;
|
||||
@include gl-align-items-center;
|
||||
|
||||
&::before {
|
||||
top: 50% !important;
|
||||
|
|
@ -26,7 +28,9 @@
|
|||
|
||||
&.is-active {
|
||||
&:last-child {
|
||||
border-bottom: 1px solid $gray-100;
|
||||
@include gl-border-b-gray-100;
|
||||
@include gl-border-b-1;
|
||||
@include gl-border-b-solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ module RoutableActions
|
|||
flash[:notice] = "#{routable.class.to_s.titleize} '#{requested_full_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path."
|
||||
end
|
||||
|
||||
redirect_to build_canonical_path(routable)
|
||||
redirect_to build_canonical_path(routable), status: :moved_permanently
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ module Types
|
|||
|
||||
authorize :read_terraform_state
|
||||
|
||||
connection_type_class(Types::CountableConnectionType)
|
||||
|
||||
field :id, GraphQL::ID_TYPE,
|
||||
null: false,
|
||||
description: 'ID of the Terraform state'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
class TestCase < ApplicationRecord
|
||||
extend Gitlab::Ci::Model
|
||||
|
||||
validates :project, :key_hash, presence: true
|
||||
|
||||
has_many :test_case_failures, class_name: 'Ci::TestCaseFailure'
|
||||
|
||||
belongs_to :project
|
||||
|
||||
scope :by_project_and_keys, -> (project, keys) { where(project_id: project.id, key_hash: keys) }
|
||||
|
||||
class << self
|
||||
def find_or_create_by_batch(project, test_case_keys)
|
||||
# Insert records first. Existing ones will be skipped.
|
||||
insert_all(test_case_attrs(project, test_case_keys))
|
||||
|
||||
# Find all matching records now that we are sure they all are persisted.
|
||||
by_project_and_keys(project, test_case_keys)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def test_case_attrs(project, test_case_keys)
|
||||
# NOTE: Rails 6.1 will add support for insert_all on relation so that
|
||||
# we will be able to do project.test_cases.insert_all.
|
||||
test_case_keys.map do |hashed_key|
|
||||
{ project_id: project.id, key_hash: hashed_key }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
class TestCaseFailure < ApplicationRecord
|
||||
extend Gitlab::Ci::Model
|
||||
|
||||
validates :test_case, :build, :failed_at, presence: true
|
||||
|
||||
belongs_to :test_case, class_name: "Ci::TestCase", foreign_key: :test_case_id
|
||||
belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id
|
||||
end
|
||||
end
|
||||
|
|
@ -274,6 +274,17 @@ class Service < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def self.inherited_descendants_from_self_or_ancestors_from(integration)
|
||||
inherit_from_ids =
|
||||
where(type: integration.type, group: integration.group.self_and_ancestors)
|
||||
.or(where(type: integration.type, instance: true)).select(:id)
|
||||
|
||||
from_union([
|
||||
where(type: integration.type, inherit_from_id: inherit_from_ids, group: integration.group.descendants),
|
||||
where(type: integration.type, inherit_from_id: inherit_from_ids, project: Project.in_namespace(integration.group.self_and_descendants))
|
||||
])
|
||||
end
|
||||
|
||||
def activated?
|
||||
active
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ module Admin
|
|||
include PropagateService
|
||||
|
||||
def propagate
|
||||
update_inherited_integrations
|
||||
|
||||
if integration.instance?
|
||||
update_inherited_integrations
|
||||
create_integration_for_groups_without_integration if Feature.enabled?(:group_level_integrations)
|
||||
create_integration_for_projects_without_integration
|
||||
else
|
||||
update_inherited_descendant_integrations
|
||||
create_integration_for_groups_without_integration_belonging_to_group
|
||||
create_integration_for_projects_without_integration_belonging_to_group
|
||||
end
|
||||
|
|
@ -18,34 +18,39 @@ module Admin
|
|||
|
||||
private
|
||||
|
||||
# rubocop: disable Cop/InBatches
|
||||
def update_inherited_integrations
|
||||
Service.by_type(integration.type).inherit_from_id(integration.id).each_batch(of: BATCH_SIZE) do |services|
|
||||
min_id, max_id = services.pick("MIN(services.id), MAX(services.id)")
|
||||
PropagateIntegrationInheritWorker.perform_async(integration.id, min_id, max_id)
|
||||
end
|
||||
propagate_integrations(
|
||||
Service.by_type(integration.type).inherit_from_id(integration.id),
|
||||
PropagateIntegrationInheritWorker
|
||||
)
|
||||
end
|
||||
|
||||
def update_inherited_descendant_integrations
|
||||
propagate_integrations(
|
||||
Service.inherited_descendants_from_self_or_ancestors_from(integration),
|
||||
PropagateIntegrationInheritDescendantWorker
|
||||
)
|
||||
end
|
||||
# rubocop: enable Cop/InBatches
|
||||
|
||||
def create_integration_for_groups_without_integration
|
||||
Group.without_integration(integration).each_batch(of: BATCH_SIZE) do |groups|
|
||||
min_id, max_id = groups.pick("MIN(namespaces.id), MAX(namespaces.id)")
|
||||
PropagateIntegrationGroupWorker.perform_async(integration.id, min_id, max_id)
|
||||
end
|
||||
propagate_integrations(
|
||||
Group.without_integration(integration),
|
||||
PropagateIntegrationGroupWorker
|
||||
)
|
||||
end
|
||||
|
||||
def create_integration_for_groups_without_integration_belonging_to_group
|
||||
integration.group.descendants.without_integration(integration).each_batch(of: BATCH_SIZE) do |groups|
|
||||
min_id, max_id = groups.pick("MIN(namespaces.id), MAX(namespaces.id)")
|
||||
PropagateIntegrationGroupWorker.perform_async(integration.id, min_id, max_id)
|
||||
end
|
||||
propagate_integrations(
|
||||
integration.group.descendants.without_integration(integration),
|
||||
PropagateIntegrationGroupWorker
|
||||
)
|
||||
end
|
||||
|
||||
def create_integration_for_projects_without_integration_belonging_to_group
|
||||
Project.without_integration(integration).in_namespace(integration.group.self_and_descendants).each_batch(of: BATCH_SIZE) do |projects|
|
||||
min_id, max_id = projects.pick("MIN(projects.id), MAX(projects.id)")
|
||||
PropagateIntegrationProjectWorker.perform_async(integration.id, min_id, max_id)
|
||||
end
|
||||
propagate_integrations(
|
||||
Project.without_integration(integration).in_namespace(integration.group.self_and_descendants),
|
||||
PropagateIntegrationProjectWorker
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class BulkCreateIntegrationService
|
|||
if integration.template?
|
||||
integration.to_service_hash
|
||||
else
|
||||
integration.to_service_hash.tap { |json| json['inherit_from_id'] = integration.id }
|
||||
integration.to_service_hash.tap { |json| json['inherit_from_id'] = integration.inherit_from_id || integration.id }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class BulkUpdateIntegrationService
|
|||
attr_reader :integration, :batch
|
||||
|
||||
def service_hash
|
||||
integration.to_service_hash.tap { |json| json['inherit_from_id'] = integration.id }
|
||||
integration.to_service_hash.tap { |json| json['inherit_from_id'] = integration.inherit_from_id || integration.id }
|
||||
end
|
||||
|
||||
def data_fields_hash
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
class TestCasesService
|
||||
MAX_TRACKABLE_FAILURES = 200
|
||||
|
||||
def execute(build)
|
||||
return unless Feature.enabled?(:test_failure_history, build.project)
|
||||
return unless build.has_test_reports?
|
||||
return unless build.project.default_branch_or_master == build.ref
|
||||
|
||||
test_suite = generate_test_suite_report(build)
|
||||
|
||||
track_failures(build, test_suite)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_test_suite_report(build)
|
||||
build.collect_test_reports!(Gitlab::Ci::Reports::TestReports.new)
|
||||
end
|
||||
|
||||
def track_failures(build, test_suite)
|
||||
return if test_suite.failed_count > MAX_TRACKABLE_FAILURES
|
||||
|
||||
test_suite.failed.keys.each_slice(100) do |keys|
|
||||
Ci::TestCase.transaction do
|
||||
test_cases = Ci::TestCase.find_or_create_by_batch(build.project, keys)
|
||||
Ci::TestCaseFailure.insert_all(test_case_failures(test_cases, build))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_case_failures(test_cases, build)
|
||||
test_cases.map do |test_case|
|
||||
{
|
||||
test_case_id: test_case.id,
|
||||
build_id: build.id,
|
||||
failed_at: build.finished_at
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -21,9 +21,16 @@ module Admin
|
|||
attr_reader :integration
|
||||
|
||||
def create_integration_for_projects_without_integration
|
||||
Project.without_integration(integration).each_batch(of: BATCH_SIZE) do |projects|
|
||||
min_id, max_id = projects.pick("MIN(projects.id), MAX(projects.id)")
|
||||
PropagateIntegrationProjectWorker.perform_async(integration.id, min_id, max_id)
|
||||
propagate_integrations(
|
||||
Project.without_integration(integration),
|
||||
PropagateIntegrationProjectWorker
|
||||
)
|
||||
end
|
||||
|
||||
def propagate_integrations(relation, worker_class)
|
||||
relation.each_batch(of: BATCH_SIZE) do |records|
|
||||
min_id, max_id = records.pick("MIN(#{relation.table_name}.id), MAX(#{relation.table_name}.id)")
|
||||
worker_class.perform_async(integration.id, min_id, max_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
.devops
|
||||
.devops-header
|
||||
%h2.devops-header-title{ class: "devops-#{score_level(@metric.average_percentage_score)}-score" }
|
||||
= number_to_percentage(@metric.average_percentage_score, precision: 1)
|
||||
.devops-header-subtitle
|
||||
= _('DevOps')
|
||||
%br
|
||||
= _('Score')
|
||||
= link_to sprite_icon('question-o', css_class: 'devops-header-icon'), help_page_path('user/admin_area/analytics/dev_ops_report')
|
||||
|
||||
.devops-cards.board-card-container
|
||||
- @metric.cards.each do |card|
|
||||
= render 'card', card: card
|
||||
|
||||
.devops-steps.d-none.d-lg-block
|
||||
- @metric.idea_to_production_steps.each_with_index do |step, index|
|
||||
.devops-step{ class: "devops-#{score_level(step.percentage_score)}-score" }
|
||||
= custom_icon("i2p_step_#{index + 1}")
|
||||
%h4.devops-step-title
|
||||
= step.title
|
||||
|
|
@ -12,23 +12,4 @@
|
|||
- elsif @metric.blank?
|
||||
= render 'no_data'
|
||||
- else
|
||||
.devops
|
||||
.devops-header
|
||||
%h2.devops-header-title{ class: "devops-#{score_level(@metric.average_percentage_score)}-score" }
|
||||
= number_to_percentage(@metric.average_percentage_score, precision: 1)
|
||||
.devops-header-subtitle
|
||||
= _('DevOps')
|
||||
%br
|
||||
= _('Score')
|
||||
= link_to sprite_icon('question-o', css_class: 'devops-header-icon'), help_page_path('user/admin_area/analytics/dev_ops_report')
|
||||
|
||||
.devops-cards.board-card-container
|
||||
- @metric.cards.each do |card|
|
||||
= render 'card', card: card
|
||||
|
||||
.devops-steps.d-none.d-lg-block
|
||||
- @metric.idea_to_production_steps.each_with_index do |step, index|
|
||||
.devops-step{ class: "devops-#{score_level(step.percentage_score)}-score" }
|
||||
= custom_icon("i2p_step_#{index + 1}")
|
||||
%h4.devops-step-title
|
||||
= step.title
|
||||
= render 'report'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
- add_to_breadcrumbs s_('AlertManagement|Alerts'), project_alert_management_index_path(@project)
|
||||
- page_title s_('AlertManagement|Alert detail')
|
||||
- add_page_specific_style 'page_bundles/alert_management_details'
|
||||
|
||||
#js-alert_details{ data: alert_management_detail_data(@project, @alert_id) }
|
||||
|
|
|
|||
|
|
@ -1847,6 +1847,14 @@
|
|||
:weight: 1
|
||||
:idempotent: true
|
||||
:tags: []
|
||||
- :name: propagate_integration_inherit_descendant
|
||||
:feature_category: :integrations
|
||||
:has_external_dependencies:
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
:weight: 1
|
||||
:idempotent: true
|
||||
:tags: []
|
||||
- :name: propagate_integration_project
|
||||
:feature_category: :integrations
|
||||
:has_external_dependencies:
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
BuildCoverageWorker.new.perform(build.id)
|
||||
Ci::BuildReportResultWorker.new.perform(build.id)
|
||||
|
||||
# TODO: As per https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/194, it may be
|
||||
# best to avoid creating more workers that we have no intention of calling async.
|
||||
# Change the previous worker calls on top to also just call the service directly.
|
||||
Ci::TestCasesService.new.execute(build)
|
||||
|
||||
# We execute these async as these are independent operations.
|
||||
BuildHooksWorker.perform_async(build.id)
|
||||
ExpirePipelineCacheWorker.perform_async(build.pipeline_id) if build.pipeline.cacheable?
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PropagateIntegrationInheritDescendantWorker
|
||||
include ApplicationWorker
|
||||
|
||||
feature_category :integrations
|
||||
idempotent!
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def perform(integration_id, min_id, max_id)
|
||||
integration = Service.find_by_id(integration_id)
|
||||
return unless integration
|
||||
|
||||
batch = Service.inherited_descendants_from_self_or_ancestors_from(integration).where(id: min_id..max_id)
|
||||
|
||||
BulkUpdateIntegrationService.new(integration, batch).execute
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
|
@ -11,9 +11,9 @@ class PropagateIntegrationInheritWorker
|
|||
integration = Service.find_by_id(integration_id)
|
||||
return unless integration
|
||||
|
||||
services = Service.where(id: min_id..max_id).by_type(integration.type).inherit_from_id(integration.id)
|
||||
batch = Service.where(id: min_id..max_id).by_type(integration.type).inherit_from_id(integration.id)
|
||||
|
||||
BulkUpdateIntegrationService.new(integration, services).execute
|
||||
BulkUpdateIntegrationService.new(integration, batch).execute
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add support for search and inclusion of project labels within Group Labels API
|
||||
merge_request: 44415
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add total count to Terraform state GraphQL API
|
||||
merge_request: 45798
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Store test failure data when build finishes
|
||||
merge_request: 45027
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change permanent routable redirect to 301
|
||||
merge_request: 45980
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update to Ruby v2.7.2
|
||||
merge_request: 44223
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add repository_read_only column to Snippets
|
||||
merge_request: 45868
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -197,6 +197,7 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/reports.css"
|
||||
config.assets.precompile << "page_bundles/xterm.css"
|
||||
config.assets.precompile << "page_bundles/wiki.css"
|
||||
config.assets.precompile << "page_bundles/alert_management_details.css"
|
||||
config.assets.precompile << "lazy_bundles/cropper.css"
|
||||
config.assets.precompile << "performance_bar.css"
|
||||
config.assets.precompile << "lib/ace.js"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
name: test_failure_history
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45027
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/268249
|
||||
type: development
|
||||
group: group::testing
|
||||
default_enabled: false
|
||||
|
|
@ -254,6 +254,8 @@
|
|||
- 1
|
||||
- - propagate_integration_inherit
|
||||
- 1
|
||||
- - propagate_integration_inherit_descendant
|
||||
- 1
|
||||
- - propagate_integration_project
|
||||
- 1
|
||||
- - propagate_service_template
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateCiTestCases < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
unless table_exists?(:ci_test_cases)
|
||||
create_table :ci_test_cases do |t|
|
||||
t.bigint :project_id, null: false
|
||||
t.text :key_hash, null: false
|
||||
|
||||
t.index [:project_id, :key_hash], unique: true
|
||||
# NOTE: FK for projects will be added on a separate migration as per guidelines
|
||||
end
|
||||
end
|
||||
|
||||
add_text_limit :ci_test_cases, :key_hash, 64
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table :ci_test_cases
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateCiTestCaseFailures < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
create_table :ci_test_case_failures do |t|
|
||||
t.datetime_with_timezone :failed_at
|
||||
t.bigint :test_case_id, null: false
|
||||
t.bigint :build_id, null: false
|
||||
|
||||
t.index [:test_case_id, :failed_at, :build_id], name: 'index_test_case_failures_unique_columns', unique: true, order: { failed_at: :desc }
|
||||
t.index :build_id
|
||||
t.foreign_key :ci_test_cases, column: :test_case_id, on_delete: :cascade
|
||||
# NOTE: FK for ci_builds will be added on a separate migration as per guidelines
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table :ci_test_case_failures
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddProjectsFkToCiTestCases < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :ci_test_cases, :projects, column: :project_id, on_delete: :cascade
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key :ci_test_cases, column: :project_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCiBuildsFkToCiTestCaseFailures < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :ci_test_case_failures, :ci_builds, column: :build_id, on_delete: :cascade
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key :ci_test_case_failures, column: :build_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddRepositoryReadOnlyToSnippets < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :snippets, :repository_read_only, :boolean, default: false, null: false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
1673018885366e92eb47f5fc705ea8251c2db49b5c14b788e84b10d8db91af48
|
||||
|
|
@ -0,0 +1 @@
|
|||
18ccd2059d9a19a51ea0162c46a1293e280759daffa54ba58ba5e431ee7aba93
|
||||
|
|
@ -0,0 +1 @@
|
|||
e266655483655e1ecbb4f65594ef5b985c3f0449231755f589f3e293e28c9f6b
|
||||
|
|
@ -0,0 +1 @@
|
|||
d62928276708c26656070f803ea6271be74a1fe9802877258d4a8cf19df32d09
|
||||
|
|
@ -0,0 +1 @@
|
|||
809d93d367ff9310063904ee3c266914311ef54e8c7f9d6d7fd924d25890bf19
|
||||
|
|
@ -10656,6 +10656,38 @@ CREATE SEQUENCE ci_subscriptions_projects_id_seq
|
|||
|
||||
ALTER SEQUENCE ci_subscriptions_projects_id_seq OWNED BY ci_subscriptions_projects.id;
|
||||
|
||||
CREATE TABLE ci_test_case_failures (
|
||||
id bigint NOT NULL,
|
||||
failed_at timestamp with time zone,
|
||||
test_case_id bigint NOT NULL,
|
||||
build_id bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ci_test_case_failures_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE ci_test_case_failures_id_seq OWNED BY ci_test_case_failures.id;
|
||||
|
||||
CREATE TABLE ci_test_cases (
|
||||
id bigint NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
key_hash text NOT NULL,
|
||||
CONSTRAINT check_dd3c5d1c15 CHECK ((char_length(key_hash) <= 64))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ci_test_cases_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE ci_test_cases_id_seq OWNED BY ci_test_cases.id;
|
||||
|
||||
CREATE TABLE ci_trigger_requests (
|
||||
id integer NOT NULL,
|
||||
trigger_id integer NOT NULL,
|
||||
|
|
@ -16157,7 +16189,8 @@ CREATE TABLE snippets (
|
|||
description_html text,
|
||||
encrypted_secret_token character varying(255),
|
||||
encrypted_secret_token_iv character varying(255),
|
||||
secret boolean DEFAULT false NOT NULL
|
||||
secret boolean DEFAULT false NOT NULL,
|
||||
repository_read_only boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE snippets_id_seq
|
||||
|
|
@ -17534,6 +17567,10 @@ ALTER TABLE ONLY ci_stages ALTER COLUMN id SET DEFAULT nextval('ci_stages_id_seq
|
|||
|
||||
ALTER TABLE ONLY ci_subscriptions_projects ALTER COLUMN id SET DEFAULT nextval('ci_subscriptions_projects_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY ci_test_case_failures ALTER COLUMN id SET DEFAULT nextval('ci_test_case_failures_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY ci_test_cases ALTER COLUMN id SET DEFAULT nextval('ci_test_cases_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY ci_trigger_requests ALTER COLUMN id SET DEFAULT nextval('ci_trigger_requests_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY ci_triggers ALTER COLUMN id SET DEFAULT nextval('ci_triggers_id_seq'::regclass);
|
||||
|
|
@ -18574,6 +18611,12 @@ ALTER TABLE ONLY ci_stages
|
|||
ALTER TABLE ONLY ci_subscriptions_projects
|
||||
ADD CONSTRAINT ci_subscriptions_projects_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY ci_test_case_failures
|
||||
ADD CONSTRAINT ci_test_case_failures_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY ci_test_cases
|
||||
ADD CONSTRAINT ci_test_cases_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY ci_trigger_requests
|
||||
ADD CONSTRAINT ci_trigger_requests_pkey PRIMARY KEY (id);
|
||||
|
||||
|
|
@ -20238,6 +20281,10 @@ CREATE INDEX index_ci_subscriptions_projects_on_upstream_project_id ON ci_subscr
|
|||
|
||||
CREATE UNIQUE INDEX index_ci_subscriptions_projects_unique_subscription ON ci_subscriptions_projects USING btree (downstream_project_id, upstream_project_id);
|
||||
|
||||
CREATE INDEX index_ci_test_case_failures_on_build_id ON ci_test_case_failures USING btree (build_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_ci_test_cases_on_project_id_and_key_hash ON ci_test_cases USING btree (project_id, key_hash);
|
||||
|
||||
CREATE INDEX index_ci_trigger_requests_on_commit_id ON ci_trigger_requests USING btree (commit_id);
|
||||
|
||||
CREATE INDEX index_ci_trigger_requests_on_trigger_id_and_id ON ci_trigger_requests USING btree (trigger_id, id DESC);
|
||||
|
|
@ -21740,6 +21787,8 @@ CREATE UNIQUE INDEX index_terraform_states_on_project_id_and_name ON terraform_s
|
|||
|
||||
CREATE UNIQUE INDEX index_terraform_states_on_uuid ON terraform_states USING btree (uuid);
|
||||
|
||||
CREATE UNIQUE INDEX index_test_case_failures_unique_columns ON ci_test_case_failures USING btree (test_case_id, failed_at DESC, build_id);
|
||||
|
||||
CREATE INDEX index_timelogs_on_issue_id ON timelogs USING btree (issue_id);
|
||||
|
||||
CREATE INDEX index_timelogs_on_merge_request_id ON timelogs USING btree (merge_request_id);
|
||||
|
|
@ -22328,6 +22377,9 @@ ALTER TABLE ONLY clusters_applications_runners
|
|||
ALTER TABLE ONLY design_management_designs_versions
|
||||
ADD CONSTRAINT fk_03c671965c FOREIGN KEY (design_id) REFERENCES design_management_designs(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY ci_test_cases
|
||||
ADD CONSTRAINT fk_0526c30ded FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY issues
|
||||
ADD CONSTRAINT fk_05f1e72feb FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
|
|
@ -22841,6 +22893,9 @@ ALTER TABLE ONLY ci_sources_pipelines
|
|||
ALTER TABLE ONLY geo_event_log
|
||||
ADD CONSTRAINT fk_d5af95fcd9 FOREIGN KEY (lfs_object_deleted_event_id) REFERENCES geo_lfs_object_deleted_events(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY ci_test_case_failures
|
||||
ADD CONSTRAINT fk_d69404d827 FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY lists
|
||||
ADD CONSTRAINT fk_d6cf4279f7 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
@ -24134,6 +24189,9 @@ ALTER TABLE ONLY merge_request_blocks
|
|||
ALTER TABLE ONLY protected_branch_unprotect_access_levels
|
||||
ADD CONSTRAINT fk_rails_e9eb8dc025 FOREIGN KEY (protected_branch_id) REFERENCES protected_branches(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY ci_test_case_failures
|
||||
ADD CONSTRAINT fk_rails_eab6349715 FOREIGN KEY (test_case_id) REFERENCES ci_test_cases(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY alert_management_alert_user_mentions
|
||||
ADD CONSTRAINT fk_rails_eb2de0cdef FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
|||
|
|
@ -96,9 +96,9 @@ From there, you can see the following actions:
|
|||
- Permission to approve merge requests by authors was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9)
|
||||
- Number of required approvals was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9)
|
||||
- Added or removed users and groups from project approval groups ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213603) in GitLab 13.2)
|
||||
- Project CI/CD variable added, removed, or protected status changed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30857) in GitLab 13.4.
|
||||
- Project CI/CD variable added, removed, or protected status changed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30857) in GitLab 13.4)
|
||||
|
||||
Project events can also be accessed via the [Project Audit Events API](../api/audit_events.md#project-audit-events)
|
||||
Project events can also be accessed via the [Project Audit Events API](../api/audit_events.md#project-audit-events).
|
||||
|
||||
### Instance events **(PREMIUM ONLY)**
|
||||
|
||||
|
|
@ -113,8 +113,8 @@ To view the server-wide administrator log, visit **Admin Area > Monitoring > Aud
|
|||
In addition to the group and project events, the following user actions are also
|
||||
recorded:
|
||||
|
||||
- Failed Logins
|
||||
- Sign-in events and the authentication type (such as standard, LDAP, or OmniAuth)
|
||||
- Failed sign-ins
|
||||
- Added SSH key
|
||||
- Added or removed email
|
||||
- Changed password
|
||||
|
|
@ -134,7 +134,7 @@ the filter dropdown box. You can further filter by specific group, project, or u
|
|||
|
||||

|
||||
|
||||
Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events)
|
||||
Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events).
|
||||
|
||||
### Missing events
|
||||
|
||||
|
|
|
|||
|
|
@ -2307,6 +2307,11 @@ type ClusterAgent {
|
|||
The connection type for ClusterAgent.
|
||||
"""
|
||||
type ClusterAgentConnection {
|
||||
"""
|
||||
Total count of collection
|
||||
"""
|
||||
count: Int!
|
||||
|
||||
"""
|
||||
A list of edges.
|
||||
"""
|
||||
|
|
@ -2389,6 +2394,11 @@ type ClusterAgentToken {
|
|||
The connection type for ClusterAgentToken.
|
||||
"""
|
||||
type ClusterAgentTokenConnection {
|
||||
"""
|
||||
Total count of collection
|
||||
"""
|
||||
count: Int!
|
||||
|
||||
"""
|
||||
A list of edges.
|
||||
"""
|
||||
|
|
@ -8725,7 +8735,12 @@ type Group {
|
|||
"""
|
||||
Represents vulnerable project counts for each grade
|
||||
"""
|
||||
vulnerabilityGrades: [VulnerableProjectsByGrade!]!
|
||||
vulnerabilityGrades(
|
||||
"""
|
||||
Include grades belonging to subgroups
|
||||
"""
|
||||
includeSubgroups: Boolean = false
|
||||
): [VulnerableProjectsByGrade!]!
|
||||
|
||||
"""
|
||||
Vulnerability scanners reported on the project vulnerabilties of the group and its subgroups
|
||||
|
|
@ -18949,6 +18964,11 @@ type TerraformState {
|
|||
The connection type for TerraformState.
|
||||
"""
|
||||
type TerraformStateConnection {
|
||||
"""
|
||||
Total count of collection
|
||||
"""
|
||||
count: Int!
|
||||
|
||||
"""
|
||||
A list of edges.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -6183,6 +6183,24 @@
|
|||
"name": "ClusterAgentConnection",
|
||||
"description": "The connection type for ClusterAgent.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "count",
|
||||
"description": "Total count of collection",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "edges",
|
||||
"description": "A list of edges.",
|
||||
|
|
@ -6442,6 +6460,24 @@
|
|||
"name": "ClusterAgentTokenConnection",
|
||||
"description": "The connection type for ClusterAgentToken.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "count",
|
||||
"description": "Total count of collection",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "edges",
|
||||
"description": "A list of edges.",
|
||||
|
|
@ -23634,7 +23670,16 @@
|
|||
"name": "vulnerabilityGrades",
|
||||
"description": "Represents vulnerable project counts for each grade",
|
||||
"args": [
|
||||
|
||||
{
|
||||
"name": "includeSubgroups",
|
||||
"description": "Include grades belonging to subgroups",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": "false"
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
|
|
@ -54902,6 +54947,24 @@
|
|||
"name": "TerraformStateConnection",
|
||||
"description": "The connection type for TerraformState.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "count",
|
||||
"description": "Total count of collection",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "edges",
|
||||
"description": "A list of edges.",
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ GET /groups/:id/labels
|
|||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `with_counts` | boolean | no | Whether or not to include issue and merge request counts. Defaults to `false`. _([Introduced in GitLab 12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31543))_ |
|
||||
| `include_ancestor_groups` | boolean | no | Include ancestor groups. Defaults to `true`. |
|
||||
| `include_descendant_groups` | boolean | no | Include descendant groups. Defaults to `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259024) in GitLab 13.6 |
|
||||
| `only_group_labels` | boolean | no | Toggle to include only group labels or also project labels. Defaults to `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259024) in GitLab 13.6 |
|
||||
| `search` | string | no | Keyword to filter labels by. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259024) in GitLab 13.6 |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/labels?with_counts=true"
|
||||
|
|
@ -75,6 +78,8 @@ GET /groups/:id/labels/:label_id
|
|||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `label_id` | integer or string | yes | The ID or title of a group's label. |
|
||||
| `include_ancestor_groups` | boolean | no | Include ancestor groups. Defaults to `true`. |
|
||||
| `include_descendant_groups` | boolean | no | Include descendant groups. Defaults to `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259024) in GitLab 13.6 |
|
||||
| `only_group_labels` | boolean | no | Toggle to include only group labels or also project labels. Defaults to `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259024) in GitLab 13.6 |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/labels/bug"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ GET /projects/:id/labels
|
|||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `with_counts` | boolean | no | Whether or not to include issue and merge request counts. Defaults to `false`. _([Introduced in GitLab 12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31543))_ |
|
||||
| `include_ancestor_groups` | boolean | no | Include ancestor groups. Defaults to `true`. |
|
||||
| `search` | string | no | Keyword to filter labels by. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259024) in GitLab 13.6 |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels?with_counts=true"
|
||||
|
|
|
|||
|
|
@ -243,9 +243,9 @@ Download Ruby and compile it:
|
|||
|
||||
```shell
|
||||
mkdir /tmp/ruby && cd /tmp/ruby
|
||||
curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.6.tar.gz
|
||||
echo '2d78048e293817f38d4ede4ebc7873013e97bb0b ruby-2.6.6.tar.gz' | shasum -c - && tar xzf ruby-2.6.6.tar.gz
|
||||
cd ruby-2.6.6
|
||||
curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.gz
|
||||
echo 'cb9731a17487e0ad84037490a6baf8bfa31a09e8 ruby-2.7.2.tar.gz' | shasum -c - && tar xzf ruby-2.7.2.tar.gz
|
||||
cd ruby-2.7.2
|
||||
|
||||
./configure --disable-install-rdoc
|
||||
make
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ For example, if the dashboard time range is set to 8 hours, the value of
|
|||
|
||||
[Variables can be defined](../../../operations/metrics/dashboards/yaml.md#templating-templating-properties) in a custom dashboard YAML file.
|
||||
|
||||
Variable names are case-sensitive.
|
||||
|
||||
## Query variables from URL
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214500) in GitLab 13.0.
|
||||
|
|
|
|||
|
|
@ -775,11 +775,11 @@ or using the appropriate [`ASDF_<tool>_VERSION`](https://asdf-vm.com/#/core-conf
|
|||
activate the appropriate version.
|
||||
|
||||
For example, the following `.tool-versions` file will activate version `12.16.3` of [Node.js](https://nodejs.org/)
|
||||
and version `2.6.6` of [Ruby](https://www.ruby-lang.org/).
|
||||
and version `2.7.2` of [Ruby](https://www.ruby-lang.org/).
|
||||
|
||||
```plaintext
|
||||
nodejs 12.16.3
|
||||
ruby 2.6.6
|
||||
ruby 2.7.2
|
||||
```
|
||||
|
||||
The next example shows how to activate the same versions of the tools mentioned above by using environment variables defined in your
|
||||
|
|
@ -792,7 +792,7 @@ include:
|
|||
license_scanning:
|
||||
variables:
|
||||
ASDF_NODEJS_VERSION: '12.16.3'
|
||||
ASDF_RUBY_VERSION: '2.6.6'
|
||||
ASDF_RUBY_VERSION: '2.7.2'
|
||||
```
|
||||
|
||||
A full list of variables can be found in [environment variables](#available-variables).
|
||||
|
|
|
|||
|
|
@ -80,7 +80,10 @@ By default, groups created in:
|
|||
- GitLab 12.2 or later allow both Owners and Maintainers to create subgroups.
|
||||
- GitLab 12.1 or earlier only allow Owners to create subgroups.
|
||||
|
||||
This setting can be for any group by an Owner or Administrator.
|
||||
The setting can be changed for any group by:
|
||||
|
||||
- A group owner. Select the group, and navigate to **Settings > General > Permissions, LFS, 2FA**.
|
||||
- An administrator. Navigate to **Admin Area > Overview > Groups**, select the group, and choose **Edit**.
|
||||
|
||||
For more information check the
|
||||
[permissions table](../../permissions.md#group-members-permissions). For a list
|
||||
|
|
|
|||
|
|
@ -20,10 +20,16 @@ module API
|
|||
desc: 'Include issue and merge request counts'
|
||||
optional :include_ancestor_groups, type: Boolean, default: true,
|
||||
desc: 'Include ancestor groups'
|
||||
optional :include_descendant_groups, type: Boolean, default: false,
|
||||
desc: 'Include descendant groups. This feature was added in GitLab 13.6'
|
||||
optional :only_group_labels, type: Boolean, default: true,
|
||||
desc: 'Toggle to include only group labels or also project labels. This feature was added in GitLab 13.6'
|
||||
optional :search, type: String,
|
||||
desc: 'Keyword to filter labels by. This feature was added in GitLab 13.6'
|
||||
use :pagination
|
||||
end
|
||||
get ':id/labels' do
|
||||
get_labels(user_group, Entities::GroupLabel, include_ancestor_groups: params[:include_ancestor_groups])
|
||||
get_labels(user_group, Entities::GroupLabel, declared_params)
|
||||
end
|
||||
|
||||
desc 'Get a single label' do
|
||||
|
|
@ -33,9 +39,13 @@ module API
|
|||
params do
|
||||
optional :include_ancestor_groups, type: Boolean, default: true,
|
||||
desc: 'Include ancestor groups'
|
||||
optional :include_descendant_groups, type: Boolean, default: false,
|
||||
desc: 'Include descendant groups. This feature was added in GitLab 13.6'
|
||||
optional :only_group_labels, type: Boolean, default: true,
|
||||
desc: 'Toggle to include only group labels or also project labels. This feature was added in GitLab 13.6'
|
||||
end
|
||||
get ':id/labels/:name' do
|
||||
get_label(user_group, Entities::GroupLabel, include_ancestor_groups: params[:include_ancestor_groups])
|
||||
get_label(user_group, Entities::GroupLabel, declared_params)
|
||||
end
|
||||
|
||||
desc 'Create a new label' do
|
||||
|
|
|
|||
|
|
@ -89,16 +89,15 @@ module API
|
|||
@project ||= find_project!(params[:id])
|
||||
end
|
||||
|
||||
def available_labels_for(label_parent, include_ancestor_groups: true)
|
||||
search_params = { include_ancestor_groups: include_ancestor_groups }
|
||||
|
||||
def available_labels_for(label_parent, params = { include_ancestor_groups: true, only_group_labels: true })
|
||||
if label_parent.is_a?(Project)
|
||||
search_params[:project_id] = label_parent.id
|
||||
params.delete(:only_group_labels)
|
||||
params[:project_id] = label_parent.id
|
||||
else
|
||||
search_params.merge!(group_id: label_parent.id, only_group_labels: true)
|
||||
params[:group_id] = label_parent.id
|
||||
end
|
||||
|
||||
LabelsFinder.new(current_user, search_params).execute
|
||||
LabelsFinder.new(current_user, params).execute
|
||||
end
|
||||
|
||||
def find_user(id)
|
||||
|
|
|
|||
|
|
@ -28,23 +28,23 @@ module API
|
|||
at_least_one_of :new_name, :color, :description
|
||||
end
|
||||
|
||||
def find_label(parent, id_or_title, include_ancestor_groups: true)
|
||||
labels = available_labels_for(parent, include_ancestor_groups: include_ancestor_groups)
|
||||
def find_label(parent, id_or_title, params = { include_ancestor_groups: true })
|
||||
labels = available_labels_for(parent, params)
|
||||
label = labels.find_by_id(id_or_title) || labels.find_by_title(id_or_title)
|
||||
|
||||
label || not_found!('Label')
|
||||
end
|
||||
|
||||
def get_labels(parent, entity, include_ancestor_groups: true)
|
||||
present paginate(available_labels_for(parent, include_ancestor_groups: include_ancestor_groups)),
|
||||
def get_labels(parent, entity, params = {})
|
||||
present paginate(available_labels_for(parent, params)),
|
||||
with: entity,
|
||||
current_user: current_user,
|
||||
parent: parent,
|
||||
with_counts: params[:with_counts]
|
||||
end
|
||||
|
||||
def get_label(parent, entity, include_ancestor_groups: true)
|
||||
label = find_label(parent, params_id_or_title, include_ancestor_groups: include_ancestor_groups)
|
||||
def get_label(parent, entity, params = {})
|
||||
label = find_label(parent, params_id_or_title, params)
|
||||
|
||||
present label, with: entity, current_user: current_user, parent: parent
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@ module API
|
|||
desc: 'Include issue and merge request counts'
|
||||
optional :include_ancestor_groups, type: Boolean, default: true,
|
||||
desc: 'Include ancestor groups'
|
||||
optional :search, type: String,
|
||||
desc: 'Keyword to filter labels by. This feature was added in GitLab 13.6'
|
||||
use :pagination
|
||||
end
|
||||
get ':id/labels' do
|
||||
get_labels(user_project, Entities::ProjectLabel, include_ancestor_groups: params[:include_ancestor_groups])
|
||||
get_labels(user_project, Entities::ProjectLabel, declared_params)
|
||||
end
|
||||
|
||||
desc 'Get a single label' do
|
||||
|
|
@ -34,7 +36,7 @@ module API
|
|||
desc: 'Include ancestor groups'
|
||||
end
|
||||
get ':id/labels/:name' do
|
||||
get_label(user_project, Entities::ProjectLabel, include_ancestor_groups: params[:include_ancestor_groups])
|
||||
get_label(user_project, Entities::ProjectLabel, declared_params)
|
||||
end
|
||||
|
||||
desc 'Create a new label' do
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ excluded_attributes:
|
|||
- :secret
|
||||
- :encrypted_secret_token
|
||||
- :encrypted_secret_token_iv
|
||||
- :repository_read_only
|
||||
merge_request_diff:
|
||||
- :external_diff
|
||||
- :stored_externally
|
||||
|
|
|
|||
|
|
@ -7791,6 +7791,9 @@ msgstr ""
|
|||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
msgid "Created on %{created_at}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Created on:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -15306,6 +15309,9 @@ msgstr ""
|
|||
msgid "Last used"
|
||||
msgstr ""
|
||||
|
||||
msgid "Last used %{last_used_at} ago"
|
||||
msgstr ""
|
||||
|
||||
msgid "Last used on:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -22938,6 +22944,9 @@ msgstr ""
|
|||
msgid "SSH host keys are not available on this system. Please use %{ssh_keyscan} command or contact your GitLab administrator for more information."
|
||||
msgstr ""
|
||||
|
||||
msgid "SSH key"
|
||||
msgstr ""
|
||||
|
||||
msgid "SSH keys allow you to establish a secure connection between your computer and GitLab."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -23046,6 +23055,9 @@ msgstr ""
|
|||
msgid "Scopes can't be blank"
|
||||
msgstr ""
|
||||
|
||||
msgid "Scopes: %{scope_list}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Score"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -26244,6 +26256,12 @@ msgstr ""
|
|||
msgid "The following %{user} can also push to this branch: %{branch}"
|
||||
msgstr ""
|
||||
|
||||
msgid "The following Personal Access Token was revoked by an administrator, %{username}."
|
||||
msgstr ""
|
||||
|
||||
msgid "The following SSH key was deleted by an administrator, %{username}."
|
||||
msgstr ""
|
||||
|
||||
msgid "The following items will NOT be exported:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -30138,6 +30156,15 @@ msgstr ""
|
|||
msgid "You can always edit this later"
|
||||
msgstr ""
|
||||
|
||||
msgid "You can create a new %{link}."
|
||||
msgstr ""
|
||||
|
||||
msgid "You can create a new Personal Access Token by visiting %{link}"
|
||||
msgstr ""
|
||||
|
||||
msgid "You can create a new SSH key by visiting %{link}"
|
||||
msgstr ""
|
||||
|
||||
msgid "You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -30582,6 +30609,9 @@ msgstr ""
|
|||
msgid "Your License"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your Personal Access Token was revoked"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your Personal Access Tokens will expire in %{days_to_expire} days or less"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -30597,6 +30627,9 @@ msgstr ""
|
|||
msgid "Your Public Email will be displayed on your public profile."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your SSH key was deleted"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your SSH keys (%{count})"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM ruby:2.6-stretch
|
||||
FROM ruby:2.7-buster
|
||||
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
|
||||
|
||||
ENV DEBIAN_FRONTEND="noninteractive"
|
||||
|
|
@ -65,7 +65,7 @@ COPY VERSION ./ee/app/models/license.r[b] /home/gitlab/ee/app/models/
|
|||
COPY ./lib/gitlab.rb /home/gitlab/lib/
|
||||
COPY ./lib/gitlab/utils.rb /home/gitlab/lib/gitlab/
|
||||
COPY ./INSTALLATION_TYPE ./VERSION /home/gitlab/
|
||||
RUN cd /home/gitlab/qa/ && bundle install --jobs=$(nproc) --retry=3 --quiet
|
||||
RUN cd /home/gitlab/qa/ && gem install bundler:1.17.3 && bundle install --jobs=$(nproc) --retry=3 --without=development --quiet
|
||||
COPY ./qa /home/gitlab/qa
|
||||
|
||||
ENTRYPOINT ["bin/test"]
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ module QA
|
|||
end
|
||||
|
||||
def executor_image
|
||||
@executor_image || 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.6'
|
||||
@executor_image || 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
|
||||
end
|
||||
|
||||
def fabricate_via_api!
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ module QA
|
|||
@name = name || "qa-runner-#{SecureRandom.hex(4)}"
|
||||
@run_untagged = true
|
||||
@executor = :shell
|
||||
@executor_image = 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.6'
|
||||
@executor_image = 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
|
||||
|
||||
super()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ export USE_BUNDLE_INSTALL=${USE_BUNDLE_INSTALL:-true}
|
|||
export BUNDLE_INSTALL_FLAGS=${BUNDLE_INSTALL_FLAGS:-"--without=production development --jobs=$(nproc) --path=vendor --retry=3 --quiet"}
|
||||
|
||||
if [ "$USE_BUNDLE_INSTALL" != "false" ]; then
|
||||
# This is for backwards compatibility for Gitaly
|
||||
run_timed_command "gem install bundler:1.17.3"
|
||||
bundle --version
|
||||
run_timed_command "bundle install --clean ${BUNDLE_INSTALL_FLAGS}"
|
||||
run_timed_command "bundle check"
|
||||
|
|
|
|||
|
|
@ -52,14 +52,14 @@ RSpec.describe Projects::IssuesController do
|
|||
get :index, params: { namespace_id: project.namespace, project_id: project }
|
||||
|
||||
expect(response).to redirect_to(project_issues_path(new_project))
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(response).to have_gitlab_http_status(:moved_permanently)
|
||||
end
|
||||
|
||||
it 'redirects from an old issue correctly' do
|
||||
get :show, params: { namespace_id: project.namespace, project_id: project, id: issue }
|
||||
|
||||
expect(response).to redirect_to(project_issue_path(new_project, issue))
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(response).to have_gitlab_http_status(:moved_permanently)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1869,7 +1869,7 @@ RSpec.describe Projects::IssuesController do
|
|||
}
|
||||
|
||||
expect(response).to redirect_to(designs_project_issue_path(new_project, issue))
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(response).to have_gitlab_http_status(:moved_permanently)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ RSpec.describe Projects::MergeRequestsController do
|
|||
}
|
||||
|
||||
expect(response).to redirect_to(project_merge_request_path(new_project, merge_request))
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(response).to have_gitlab_http_status(:moved_permanently)
|
||||
end
|
||||
|
||||
it 'redirects from an old merge request commits correctly' do
|
||||
|
|
@ -173,7 +173,7 @@ RSpec.describe Projects::MergeRequestsController do
|
|||
}
|
||||
|
||||
expect(response).to redirect_to(commits_project_merge_request_path(new_project, merge_request))
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(response).to have_gitlab_http_status(:moved_permanently)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ RSpec.describe Projects::Registry::RepositoriesController do
|
|||
end
|
||||
|
||||
it 'tracks the event' do
|
||||
expect(Gitlab::Tracking).to receive(:event).with(anything, 'list_repositories', {})
|
||||
expect(Gitlab::Tracking).to receive(:event).with(anything, 'list_repositories')
|
||||
|
||||
go_to_index(format: :json)
|
||||
end
|
||||
|
|
@ -133,7 +133,7 @@ RSpec.describe Projects::Registry::RepositoriesController do
|
|||
end
|
||||
|
||||
it 'tracks the event' do
|
||||
expect(Gitlab::Tracking).to receive(:event).with(anything, 'delete_repository', {})
|
||||
expect(Gitlab::Tracking).to receive(:event).with(anything, 'delete_repository')
|
||||
allow(DeleteContainerRepositoryWorker).to receive(:perform_async).with(user.id, repository.id)
|
||||
|
||||
delete_repository(repository)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ RSpec.describe Projects::Registry::TagsController do
|
|||
end
|
||||
|
||||
it 'tracks the event' do
|
||||
expect(Gitlab::Tracking).to receive(:event).with(anything, 'list_tags', {})
|
||||
expect(Gitlab::Tracking).to receive(:event).with(anything, 'list_tags')
|
||||
|
||||
get_tags
|
||||
end
|
||||
|
|
@ -109,7 +109,7 @@ RSpec.describe Projects::Registry::TagsController do
|
|||
|
||||
it 'tracks the event' do
|
||||
expect_delete_tags(%w[test.])
|
||||
expect(controller).to receive(:track_event).with(:delete_tag, {})
|
||||
expect(controller).to receive(:track_event).with(:delete_tag)
|
||||
|
||||
destroy_tag('test.')
|
||||
end
|
||||
|
|
@ -150,7 +150,7 @@ RSpec.describe Projects::Registry::TagsController do
|
|||
|
||||
it 'tracks the event' do
|
||||
expect_delete_tags(tags)
|
||||
expect(Gitlab::Tracking).to receive(:event).with(anything, 'delete_tag_bulk', {})
|
||||
expect(Gitlab::Tracking).to receive(:event).with(anything, 'delete_tag_bulk')
|
||||
|
||||
bulk_destroy_tags(tags)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ FactoryBot.define do
|
|||
factory :audit_event, class: 'AuditEvent', aliases: [:user_audit_event] do
|
||||
user
|
||||
|
||||
transient { target_user { create(:user) } }
|
||||
transient { target_user { association(:user) } }
|
||||
|
||||
entity_type { 'User' }
|
||||
entity_id { target_user.id }
|
||||
|
|
@ -27,7 +27,7 @@ FactoryBot.define do
|
|||
end
|
||||
|
||||
trait :project_event do
|
||||
transient { target_project { create(:project) } }
|
||||
transient { target_project { association(:project) } }
|
||||
|
||||
entity_type { 'Project' }
|
||||
entity_id { target_project.id }
|
||||
|
|
@ -50,7 +50,7 @@ FactoryBot.define do
|
|||
end
|
||||
|
||||
trait :group_event do
|
||||
transient { target_group { create(:group) } }
|
||||
transient { target_group { association(:group) } }
|
||||
|
||||
entity_type { 'Group' }
|
||||
entity_id { target_group.id }
|
||||
|
|
|
|||
|
|
@ -332,6 +332,12 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
trait :test_reports_with_duplicate_failed_test_names do
|
||||
after(:build) do |build|
|
||||
build.job_artifacts << create(:ci_job_artifact, :junit_with_duplicate_failed_test_names, job: build)
|
||||
end
|
||||
end
|
||||
|
||||
trait :accessibility_reports do
|
||||
after(:build) do |build|
|
||||
build.job_artifacts << create(:ci_job_artifact, :accessibility, job: build)
|
||||
|
|
|
|||
|
|
@ -109,6 +109,16 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
trait :junit_with_duplicate_failed_test_names do
|
||||
file_type { :junit }
|
||||
file_format { :gzip }
|
||||
|
||||
after(:build) do |artifact, evaluator|
|
||||
artifact.file = fixture_file_upload(
|
||||
Rails.root.join('spec/fixtures/junit/junit_with_duplicate_failed_test_names.xml.gz'), 'application/x-gzip')
|
||||
end
|
||||
end
|
||||
|
||||
trait :junit_with_ant do
|
||||
file_type { :junit }
|
||||
file_format { :gzip }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :report_test_case, class: 'Gitlab::Ci::Reports::TestCase' do
|
||||
suite_name { "rspec" }
|
||||
name { "test-1" }
|
||||
classname { "trace" }
|
||||
file { "spec/trace_spec.rb" }
|
||||
execution_time { 1.23 }
|
||||
status { Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS }
|
||||
system_output { nil }
|
||||
attachment { nil }
|
||||
association :job, factory: :ci_build
|
||||
|
||||
trait :failed do
|
||||
status { Gitlab::Ci::Reports::TestCase::STATUS_FAILED }
|
||||
system_output { "Failure/Error: is_expected.to eq(300) expected: 300 got: -100" }
|
||||
end
|
||||
|
||||
trait :failed_with_attachment do
|
||||
status { Gitlab::Ci::Reports::TestCase::STATUS_FAILED }
|
||||
attachment { "some/path.png" }
|
||||
end
|
||||
|
||||
skip_create
|
||||
|
||||
initialize_with do
|
||||
new(
|
||||
suite_name: suite_name,
|
||||
name: name,
|
||||
classname: classname,
|
||||
file: file,
|
||||
execution_time: execution_time,
|
||||
status: status,
|
||||
system_output: system_output,
|
||||
attachment: attachment,
|
||||
job: job
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,41 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :test_case, class: 'Gitlab::Ci::Reports::TestCase' do
|
||||
suite_name { "rspec" }
|
||||
name { "test-1" }
|
||||
classname { "trace" }
|
||||
file { "spec/trace_spec.rb" }
|
||||
execution_time { 1.23 }
|
||||
status { Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS }
|
||||
system_output { nil }
|
||||
attachment { nil }
|
||||
association :job, factory: :ci_build
|
||||
|
||||
trait :failed do
|
||||
status { Gitlab::Ci::Reports::TestCase::STATUS_FAILED }
|
||||
system_output { "Failure/Error: is_expected.to eq(300) expected: 300 got: -100" }
|
||||
end
|
||||
|
||||
trait :failed_with_attachment do
|
||||
status { Gitlab::Ci::Reports::TestCase::STATUS_FAILED }
|
||||
attachment { "some/path.png" }
|
||||
end
|
||||
|
||||
skip_create
|
||||
|
||||
initialize_with do
|
||||
new(
|
||||
suite_name: suite_name,
|
||||
name: name,
|
||||
classname: classname,
|
||||
file: file,
|
||||
execution_time: execution_time,
|
||||
status: status,
|
||||
system_output: system_output,
|
||||
attachment: attachment,
|
||||
job: job
|
||||
)
|
||||
end
|
||||
factory :ci_test_case, class: 'Ci::TestCase' do
|
||||
project
|
||||
key_hash { Digest::SHA256.hexdigest(SecureRandom.hex) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :ci_test_case_failure, class: 'Ci::TestCaseFailure' do
|
||||
build factory: :ci_build
|
||||
test_case factory: :ci_test_case
|
||||
failed_at { Time.current }
|
||||
end
|
||||
end
|
||||
|
|
@ -12,6 +12,9 @@ RSpec.describe 'Profile account page', :js do
|
|||
describe 'when I delete my account' do
|
||||
before do
|
||||
visit profile_account_path
|
||||
|
||||
# Scroll page to the bottom to make Delete account button visible
|
||||
execute_script('window.scrollTo(0, document.body.scrollHeight)')
|
||||
end
|
||||
|
||||
it { expect(page).to have_content('Delete account') }
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -36,7 +36,7 @@ RSpec.describe Resolvers::ReleaseResolver do
|
|||
let(:args) { {} }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { resolve_release }.to raise_error(ArgumentError, "missing keyword: tag_name")
|
||||
expect { resolve_release }.to raise_error(ArgumentError, "missing keyword: :tag_name")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ RSpec.describe API::Helpers do
|
|||
|
||||
describe '#track_event' do
|
||||
it "creates a gitlab tracking event" do
|
||||
expect(Gitlab::Tracking).to receive(:event).with('foo', 'my_event', {})
|
||||
expect(Gitlab::Tracking).to receive(:event).with('foo', 'my_event')
|
||||
|
||||
subject.track_event('my_event', category: 'foo')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ RSpec.describe Gitlab::Ci::Reports::TestCase do
|
|||
|
||||
context 'when required params are given' do
|
||||
let(:job) { build(:ci_build) }
|
||||
let(:params) { attributes_for(:test_case).merge!(job: job) }
|
||||
let(:params) { attributes_for(:report_test_case).merge!(job: job) }
|
||||
|
||||
it 'initializes an instance', :aggregate_failures do
|
||||
expect { test_case }.not_to raise_error
|
||||
|
|
@ -31,7 +31,7 @@ RSpec.describe Gitlab::Ci::Reports::TestCase do
|
|||
|
||||
shared_examples 'param is missing' do |param|
|
||||
let(:job) { build(:ci_build) }
|
||||
let(:params) { attributes_for(:test_case).merge!(job: job) }
|
||||
let(:params) { attributes_for(:report_test_case).merge!(job: job) }
|
||||
|
||||
it 'raises an error' do
|
||||
params.delete(param)
|
||||
|
|
@ -55,7 +55,7 @@ RSpec.describe Gitlab::Ci::Reports::TestCase do
|
|||
context 'when attachment is present' do
|
||||
let_it_be(:job) { create(:ci_build) }
|
||||
|
||||
let(:attachment_test_case) { build(:test_case, :failed_with_attachment, job: job) }
|
||||
let(:attachment_test_case) { build(:report_test_case, :failed_with_attachment, job: job) }
|
||||
|
||||
it "initializes the attachment if present" do
|
||||
expect(attachment_test_case.attachment).to eq("some/path.png")
|
||||
|
|
@ -71,7 +71,7 @@ RSpec.describe Gitlab::Ci::Reports::TestCase do
|
|||
end
|
||||
|
||||
context 'when attachment is missing' do
|
||||
let(:test_case) { build(:test_case) }
|
||||
let(:test_case) { build(:report_test_case) }
|
||||
|
||||
it '#has_attachment?' do
|
||||
expect(test_case.has_attachment?).to be_falsy
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ RSpec.describe Gitlab::Ci::Reports::TestReports do
|
|||
end
|
||||
|
||||
describe '#with_attachment' do
|
||||
let(:test_case) { build(:test_case, :failed) }
|
||||
let(:test_case) { build(:report_test_case, :failed) }
|
||||
|
||||
subject { test_reports.with_attachment! }
|
||||
|
||||
|
|
@ -126,8 +126,8 @@ RSpec.describe Gitlab::Ci::Reports::TestReports do
|
|||
end
|
||||
|
||||
context 'when test suites contain an attachment' do
|
||||
let(:test_case_succes) { build(:test_case) }
|
||||
let(:test_case_with_attachment) { build(:test_case, :failed_with_attachment) }
|
||||
let(:test_case_succes) { build(:report_test_case) }
|
||||
let(:test_case_with_attachment) { build(:report_test_case, :failed_with_attachment) }
|
||||
|
||||
before do
|
||||
test_reports.get_suite('rspec').add_test_case(test_case_succes)
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do
|
|||
subject { test_suite.with_attachment! }
|
||||
|
||||
context 'when test cases do not contain an attachment' do
|
||||
let(:test_case) { build(:test_case, :failed)}
|
||||
let(:test_case) { build(:report_test_case, :failed)}
|
||||
|
||||
before do
|
||||
test_suite.add_test_case(test_case)
|
||||
|
|
@ -103,7 +103,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do
|
|||
end
|
||||
|
||||
context 'when test cases contain an attachment' do
|
||||
let(:test_case_with_attachment) { build(:test_case, :failed_with_attachment)}
|
||||
let(:test_case_with_attachment) { build(:report_test_case, :failed_with_attachment)}
|
||||
|
||||
before do
|
||||
test_suite.add_test_case(test_case_with_attachment)
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
|
|||
context 'when unknown keyword is specified' do
|
||||
it 'raises error' do
|
||||
expect { described_class.new(key: variable_key, value: 'abc', files: true) }
|
||||
.to raise_error ArgumentError, 'unknown keyword: files'
|
||||
.to raise_error ArgumentError, 'unknown keyword: :files'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when required keywords are not specified' do
|
||||
it 'raises error' do
|
||||
expect { described_class.new(key: variable_key) }
|
||||
.to raise_error ArgumentError, 'missing keyword: value'
|
||||
.to raise_error ArgumentError, 'missing keyword: :value'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ RSpec.describe Gitlab::Config::Entry::Simplifiable do
|
|||
end
|
||||
|
||||
it 'attemps to load a first strategy' do
|
||||
expect(first).to receive(:new).with('something', anything)
|
||||
expect(first).to receive(:new).with('something')
|
||||
|
||||
entry.new('something')
|
||||
end
|
||||
|
|
@ -53,7 +53,7 @@ RSpec.describe Gitlab::Config::Entry::Simplifiable do
|
|||
end
|
||||
|
||||
it 'attemps to load a second strategy' do
|
||||
expect(second).to receive(:new).with('test', anything)
|
||||
expect(second).to receive(:new).with('test')
|
||||
|
||||
entry.new('test')
|
||||
end
|
||||
|
|
@ -68,7 +68,7 @@ RSpec.describe Gitlab::Config::Entry::Simplifiable do
|
|||
end
|
||||
|
||||
it 'instantiates an unknown strategy' do
|
||||
expect(unknown).to receive(:new).with('test', anything)
|
||||
expect(unknown).to receive(:new).with('test')
|
||||
|
||||
entry.new('test')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ RSpec.describe Gitlab::Tracking::IncidentManagement do
|
|||
context 'param without label' do
|
||||
let(:params) { { create_issue: '1' } }
|
||||
|
||||
it_behaves_like 'a tracked event', "enabled_issue_auto_creation_on_alerts", {}
|
||||
it_behaves_like 'a tracked event', "enabled_issue_auto_creation_on_alerts"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::TestCaseFailure do
|
||||
describe 'relationships' do
|
||||
it { is_expected.to belong_to(:build) }
|
||||
it { is_expected.to belong_to(:test_case) }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
subject { build(:ci_test_case_failure) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:test_case) }
|
||||
it { is_expected.to validate_presence_of(:build) }
|
||||
it { is_expected.to validate_presence_of(:failed_at) }
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::TestCase do
|
||||
describe 'relationships' do
|
||||
it { is_expected.to belong_to(:project) }
|
||||
it { is_expected.to have_many(:test_case_failures) }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
subject { build(:ci_test_case) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:project) }
|
||||
it { is_expected.to validate_presence_of(:key_hash) }
|
||||
end
|
||||
|
||||
describe '.find_or_create_by_batch' do
|
||||
it 'finds or creates records for the given test case keys', :aggregate_failures do
|
||||
project = create(:project)
|
||||
existing_tc = create(:ci_test_case, project: project)
|
||||
new_key = Digest::SHA256.hexdigest(SecureRandom.hex)
|
||||
keys = [existing_tc.key_hash, new_key]
|
||||
|
||||
result = described_class.find_or_create_by_batch(project, keys)
|
||||
|
||||
expect(result.map(&:key_hash)).to match_array([existing_tc.key_hash, new_key])
|
||||
expect(result).to all(be_persisted)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -32,7 +32,7 @@ RSpec.describe OptionallySearch do
|
|||
it 'delegates to the search method' do
|
||||
expect(model)
|
||||
.to receive(:search)
|
||||
.with('foo', {})
|
||||
.with('foo')
|
||||
.and_call_original
|
||||
|
||||
expect(model.optionally_search('foo')).to eq(['foo', {}])
|
||||
|
|
|
|||
|
|
@ -252,12 +252,17 @@ RSpec.describe Member do
|
|||
end
|
||||
|
||||
describe '.last_ten_days_excluding_today' do
|
||||
let_it_be(:created_today) { create(:group_member, created_at: Date.today.beginning_of_day) }
|
||||
let_it_be(:created_yesterday) { create(:group_member, created_at: 1.day.ago) }
|
||||
let_it_be(:created_eleven_days_ago) { create(:group_member, created_at: 11.days.ago) }
|
||||
let_it_be(:now) { Time.current }
|
||||
let_it_be(:created_today) { create(:group_member, created_at: now.beginning_of_day) }
|
||||
let_it_be(:created_yesterday) { create(:group_member, created_at: now - 1.day) }
|
||||
let_it_be(:created_eleven_days_ago) { create(:group_member, created_at: now - 11.days) }
|
||||
|
||||
subject { described_class.last_ten_days_excluding_today }
|
||||
|
||||
before do
|
||||
travel_to now
|
||||
end
|
||||
|
||||
it { is_expected.to include(created_yesterday) }
|
||||
it { is_expected.not_to include(created_today, created_eleven_days_ago) }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -599,6 +599,23 @@ RSpec.describe Service do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.inherited_descendants_from_self_or_ancestors_from' do
|
||||
let_it_be(:subgroup1) { create(:group, parent: group) }
|
||||
let_it_be(:subgroup2) { create(:group, parent: group) }
|
||||
let_it_be(:project1) { create(:project, group: subgroup1) }
|
||||
let_it_be(:project2) { create(:project, group: subgroup2) }
|
||||
let_it_be(:group_integration) { create(:prometheus_service, group: group, project: nil) }
|
||||
let_it_be(:subgroup_integration1) { create(:prometheus_service, group: subgroup1, project: nil, inherit_from_id: group_integration.id) }
|
||||
let_it_be(:subgroup_integration2) { create(:prometheus_service, group: subgroup2, project: nil) }
|
||||
let_it_be(:project_integration1) { create(:prometheus_service, group: nil, project: project1, inherit_from_id: group_integration.id) }
|
||||
let_it_be(:project_integration2) { create(:prometheus_service, group: nil, project: project2, inherit_from_id: subgroup_integration2.id) }
|
||||
|
||||
it 'returns the groups and projects inheriting from integration ancestors', :aggregate_failures do
|
||||
expect(described_class.inherited_descendants_from_self_or_ancestors_from(group_integration)).to eq([subgroup_integration1, project_integration1])
|
||||
expect(described_class.inherited_descendants_from_self_or_ancestors_from(subgroup_integration2)).to eq([project_integration2])
|
||||
end
|
||||
end
|
||||
|
||||
describe "{property}_changed?" do
|
||||
let(:service) do
|
||||
BambooService.create(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'query terraform states' do
|
||||
include GraphqlHelpers
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:terraform_state) { create(:terraform_state, :locked, project: project) }
|
||||
|
||||
let(:query) do
|
||||
graphql_query_for(:project, { fullPath: project.full_path },
|
||||
%{
|
||||
terraformStates {
|
||||
count
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
lockedAt
|
||||
createdAt
|
||||
updatedAt
|
||||
|
||||
lockedByUser {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
let(:current_user) { project.creator }
|
||||
let(:data) { graphql_data.dig('project', 'terraformStates') }
|
||||
|
||||
before do
|
||||
post_graphql(query, current_user: current_user)
|
||||
end
|
||||
|
||||
it 'returns terraform state data', :aggregate_failures do
|
||||
state = data.dig('nodes', 0)
|
||||
|
||||
expect(state['id']).to eq(terraform_state.to_global_id.to_s)
|
||||
expect(state['name']).to eq(terraform_state.name)
|
||||
expect(state['lockedAt']).to eq(terraform_state.locked_at.strftime('%Y-%m-%dT%H:%M:%SZ'))
|
||||
expect(state['createdAt']).to eq(terraform_state.created_at.strftime('%Y-%m-%dT%H:%M:%SZ'))
|
||||
expect(state['updatedAt']).to eq(terraform_state.updated_at.strftime('%Y-%m-%dT%H:%M:%SZ'))
|
||||
expect(state.dig('lockedByUser', 'id')).to eq(terraform_state.locked_by_user.to_global_id.to_s)
|
||||
end
|
||||
|
||||
it 'returns count of terraform states' do
|
||||
count = data.dig('count')
|
||||
expect(count).to be(project.terraform_states.size)
|
||||
end
|
||||
|
||||
context 'unauthorized users' do
|
||||
let(:current_user) { nil }
|
||||
|
||||
it { expect(data).to be_nil }
|
||||
end
|
||||
end
|
||||
|
|
@ -7,60 +7,97 @@ RSpec.describe API::GroupLabels do
|
|||
let(:group) { create(:group) }
|
||||
let(:subgroup) { create(:group, parent: group) }
|
||||
let!(:group_member) { create(:group_member, group: group, user: user) }
|
||||
let!(:group_label1) { create(:group_label, title: 'feature', group: group) }
|
||||
let!(:group_label1) { create(:group_label, title: 'feature-label', group: group) }
|
||||
let!(:group_label2) { create(:group_label, title: 'bug', group: group) }
|
||||
let!(:subgroup_label) { create(:group_label, title: 'support', group: subgroup) }
|
||||
let!(:subgroup_label) { create(:group_label, title: 'support-label', group: subgroup) }
|
||||
|
||||
describe 'GET :id/labels' do
|
||||
it 'returns all available labels for the group' do
|
||||
get api("/groups/#{group.id}/labels", user)
|
||||
context 'get current group labels' do
|
||||
let(:request) { get api("/groups/#{group.id}/labels", user) }
|
||||
let(:expected_labels) { [group_label1.name, group_label2.name] }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response).to all(match_schema('public_api/v4/labels/label'))
|
||||
expect(json_response.size).to eq(2)
|
||||
expect(json_response.map {|r| r['name'] }).to contain_exactly('feature', 'bug')
|
||||
end
|
||||
it_behaves_like 'fetches labels'
|
||||
|
||||
context 'when the with_counts parameter is set' do
|
||||
it 'includes counts in the response' do
|
||||
get api("/groups/#{group.id}/labels", user), params: { with_counts: true }
|
||||
context 'when search param is provided' do
|
||||
let(:request) { get api("/groups/#{group.id}/labels?search=lab", user) }
|
||||
let(:expected_labels) { [group_label1.name] }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response).to all(match_schema('public_api/v4/labels/label_with_counts'))
|
||||
expect(json_response.size).to eq(2)
|
||||
expect(json_response.map { |r| r['open_issues_count'] }).to contain_exactly(0, 0)
|
||||
it_behaves_like 'fetches labels'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET :subgroup_id/labels' do
|
||||
context 'when the include_ancestor_groups parameter is not set' do
|
||||
it 'returns all available labels for the group and ancestor groups' do
|
||||
get api("/groups/#{subgroup.id}/labels", user)
|
||||
context 'when the with_counts parameter is set' do
|
||||
it 'includes counts in the response' do
|
||||
get api("/groups/#{group.id}/labels", user), params: { with_counts: true }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response).to all(match_schema('public_api/v4/labels/label'))
|
||||
expect(json_response.size).to eq(3)
|
||||
expect(json_response.map {|r| r['name'] }).to contain_exactly('feature', 'bug', 'support')
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response).to all(match_schema('public_api/v4/labels/label_with_counts'))
|
||||
expect(json_response.size).to eq(2)
|
||||
expect(json_response.map { |r| r['open_issues_count'] }).to contain_exactly(0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when include_descendant_groups param is provided' do
|
||||
let!(:project) { create(:project, group: group) }
|
||||
let!(:project_label1) { create(:label, title: 'project-label1', project: project, priority: 3) }
|
||||
let!(:project_label2) { create(:label, title: 'project-bug', project: project) }
|
||||
|
||||
let(:request) { get api("/groups/#{group.id}/labels", user), params: { include_descendant_groups: true } }
|
||||
let(:expected_labels) { [group_label1.name, group_label2.name, subgroup_label.name] }
|
||||
|
||||
it_behaves_like 'fetches labels'
|
||||
|
||||
context 'when search param is provided' do
|
||||
let(:request) { get api("/groups/#{group.id}/labels", user), params: { search: 'lab', include_descendant_groups: true } }
|
||||
let(:expected_labels) { [group_label1.name, subgroup_label.name] }
|
||||
|
||||
it_behaves_like 'fetches labels'
|
||||
end
|
||||
|
||||
context 'when only_group_labels param is false' do
|
||||
let(:request) { get api("/groups/#{group.id}/labels", user), params: { include_descendant_groups: true, only_group_labels: false } }
|
||||
let(:expected_labels) { [group_label1.name, group_label2.name, subgroup_label.name, project_label1.name, project_label2.name] }
|
||||
|
||||
it_behaves_like 'fetches labels'
|
||||
|
||||
context 'when search param is provided' do
|
||||
let(:request) { get api("/groups/#{group.id}/labels", user), params: { search: 'lab', include_descendant_groups: true, only_group_labels: false } }
|
||||
let(:expected_labels) { [group_label1.name, subgroup_label.name, project_label1.name] }
|
||||
|
||||
it_behaves_like 'fetches labels'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the include_ancestor_groups parameter is set to false' do
|
||||
it 'returns all available labels for the group but not for ancestor groups' do
|
||||
get api("/groups/#{subgroup.id}/labels", user), params: { include_ancestor_groups: false }
|
||||
describe 'with subgroup labels' do
|
||||
context 'when the include_ancestor_groups parameter is not set' do
|
||||
let(:request) { get api("/groups/#{subgroup.id}/labels", user) }
|
||||
let(:expected_labels) { [group_label1.name, group_label2.name, subgroup_label.name] }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response).to all(match_schema('public_api/v4/labels/label'))
|
||||
expect(json_response.size).to eq(1)
|
||||
expect(json_response.map {|r| r['name'] }).to contain_exactly('support')
|
||||
it_behaves_like 'fetches labels'
|
||||
|
||||
context 'when search param is provided' do
|
||||
let(:request) { get api("/groups/#{subgroup.id}/labels?search=lab", user) }
|
||||
let(:expected_labels) { [group_label1.name, subgroup_label.name] }
|
||||
|
||||
it_behaves_like 'fetches labels'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the include_ancestor_groups parameter is set to false' do
|
||||
let(:request) { get api("/groups/#{subgroup.id}/labels", user), params: { include_ancestor_groups: false } }
|
||||
let(:expected_labels) { [subgroup_label.name] }
|
||||
|
||||
it_behaves_like 'fetches labels'
|
||||
|
||||
context 'when search param is provided' do
|
||||
let(:request) { get api("/groups/#{subgroup.id}/labels?search=lab", user), params: { include_ancestor_groups: false } }
|
||||
let(:expected_labels) { [subgroup_label.name] }
|
||||
|
||||
it_behaves_like 'fetches labels'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -223,7 +260,7 @@ RSpec.describe API::GroupLabels do
|
|||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(subgroup.labels[0].name).to eq('New Label')
|
||||
expect(group_label1.name).to eq('feature')
|
||||
expect(group_label1.name).to eq(group_label1.title)
|
||||
end
|
||||
|
||||
it 'returns 404 if label does not exist' do
|
||||
|
|
@ -278,7 +315,7 @@ RSpec.describe API::GroupLabels do
|
|||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(subgroup.labels[0].name).to eq('New Label')
|
||||
expect(group_label1.name).to eq('feature')
|
||||
expect(group_label1.name).to eq(group_label1.title)
|
||||
end
|
||||
|
||||
it 'returns 404 if label does not exist' do
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue