Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-10-23 18:08:31 +00:00
parent 9086e66ee7
commit 7e1e5ca371
115 changed files with 1194 additions and 361 deletions

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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"]

View File

@ -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:

View File

@ -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"

View File

@ -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:

View File

@ -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:

View File

@ -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.

View File

@ -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'

View File

@ -1 +1 @@
2.6.6
2.7.2

View File

@ -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+

View File

@ -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';

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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) }

View File

@ -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:

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
---
title: Add support for search and inclusion of project labels within Group Labels API
merge_request: 44415
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Add total count to Terraform state GraphQL API
merge_request: 45798
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Store test failure data when build finishes
merge_request: 45027
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Change permanent routable redirect to 301
merge_request: 45980
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Update to Ruby v2.7.2
merge_request: 44223
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Add repository_read_only column to Snippets
merge_request: 45868
author:
type: added

View File

@ -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"

View File

@ -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

View File

@ -254,6 +254,8 @@
- 1
- - propagate_integration_inherit
- 1
- - propagate_integration_inherit_descendant
- 1
- - propagate_integration_project
- 1
- - propagate_service_template

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
1673018885366e92eb47f5fc705ea8251c2db49b5c14b788e84b10d8db91af48

View File

@ -0,0 +1 @@
18ccd2059d9a19a51ea0162c46a1293e280759daffa54ba58ba5e431ee7aba93

View File

@ -0,0 +1 @@
e266655483655e1ecbb4f65594ef5b985c3f0449231755f589f3e293e28c9f6b

View File

@ -0,0 +1 @@
d62928276708c26656070f803ea6271be74a1fe9802877258d4a8cf19df32d09

View File

@ -0,0 +1 @@
809d93d367ff9310063904ee3c266914311ef54e8c7f9d6d7fd924d25890bf19

View File

@ -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;

View File

@ -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
![audit log](img/audit_log.png)
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

View File

@ -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.
"""

View File

@ -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.",

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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.

View File

@ -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).

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -185,6 +185,7 @@ excluded_attributes:
- :secret
- :encrypted_secret_token
- :encrypted_secret_token_iv
- :repository_read_only
merge_request_diff:
- :external_diff
- :stored_externally

View File

@ -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 ""

View File

@ -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"]

View File

@ -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!

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 }

View File

@ -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)

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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', {}])

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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