Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0d0fdf863c
commit
1f84ff323d
20 changed files with 291 additions and 59 deletions
|
@ -73,8 +73,7 @@ populate-qa-tests-var:
|
||||||
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
|
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
|
||||||
stage: prepare
|
stage: prepare
|
||||||
script:
|
script:
|
||||||
- tooling/bin/qa/check_if_qa_only_spec_changes ${CHANGES_FILE} ${ONLY_QA_CHANGES_FILE}
|
- export QA_TESTS=$(scripts/determine-qa-tests --files $CHANGES_FILE --labels $CI_MERGE_REQUEST_LABELS)
|
||||||
- '[ -f $ONLY_QA_CHANGES_FILE ] && export QA_TESTS="`cat $ONLY_QA_CHANGES_FILE`"'
|
|
||||||
- 'echo "QA_TESTS=$QA_TESTS" >> qa_tests_var.env'
|
- 'echo "QA_TESTS=$QA_TESTS" >> qa_tests_var.env'
|
||||||
- 'echo "QA_TESTS: $QA_TESTS"'
|
- 'echo "QA_TESTS: $QA_TESTS"'
|
||||||
artifacts:
|
artifacts:
|
||||||
|
@ -83,11 +82,9 @@ populate-qa-tests-var:
|
||||||
dotenv: qa_tests_var.env
|
dotenv: qa_tests_var.env
|
||||||
paths:
|
paths:
|
||||||
- ${CHANGES_FILE}
|
- ${CHANGES_FILE}
|
||||||
- ${ONLY_QA_CHANGES_FILE}
|
|
||||||
- qa_tests_var.env
|
- qa_tests_var.env
|
||||||
variables:
|
variables:
|
||||||
CHANGES_FILE: tmp/changed_files.txt
|
CHANGES_FILE: tmp/changed_files.txt
|
||||||
ONLY_QA_CHANGES_FILE: tmp/qa_only_changed_files.txt
|
|
||||||
needs:
|
needs:
|
||||||
- detect-tests
|
- detect-tests
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,9 @@
|
||||||
.if-dot-com-gitlab-org-and-security-merge-request: &if-dot-com-gitlab-org-and-security-merge-request
|
.if-dot-com-gitlab-org-and-security-merge-request: &if-dot-com-gitlab-org-and-security-merge-request
|
||||||
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/security$)/ && $CI_MERGE_REQUEST_IID'
|
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/security$)/ && $CI_MERGE_REQUEST_IID'
|
||||||
|
|
||||||
|
.if-dot-com-gitlab-org-and-security-merge-request-and-qa-tests-specified: &if-dot-com-gitlab-org-and-security-merge-request-and-qa-tests-specified
|
||||||
|
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/security$)/ && $CI_MERGE_REQUEST_IID && $QA_TESTS'
|
||||||
|
|
||||||
.if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-qa: &if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-qa
|
.if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-qa: &if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-qa
|
||||||
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/security$)/ && $CI_MERGE_REQUEST_IID && $QA_MANUAL_FF_PACKAGE_AND_QA'
|
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/security$)/ && $CI_MERGE_REQUEST_IID && $QA_MANUAL_FF_PACKAGE_AND_QA'
|
||||||
|
|
||||||
|
@ -943,6 +946,9 @@
|
||||||
- <<: *if-dot-com-gitlab-org-and-security-merge-request
|
- <<: *if-dot-com-gitlab-org-and-security-merge-request
|
||||||
changes: *qa-patterns
|
changes: *qa-patterns
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
- <<: *if-dot-com-gitlab-org-and-security-merge-request-and-qa-tests-specified
|
||||||
|
changes: *code-patterns
|
||||||
|
allow_failure: true
|
||||||
- <<: *if-dot-com-gitlab-org-and-security-merge-request
|
- <<: *if-dot-com-gitlab-org-and-security-merge-request
|
||||||
changes: *code-patterns
|
changes: *code-patterns
|
||||||
when: manual
|
when: manual
|
||||||
|
|
|
@ -6547,7 +6547,6 @@ Layout/LineLength:
|
||||||
- 'spec/workers/users/deactivate_dormant_users_worker_spec.rb'
|
- 'spec/workers/users/deactivate_dormant_users_worker_spec.rb'
|
||||||
- 'spec/workers/web_hooks/destroy_worker_spec.rb'
|
- 'spec/workers/web_hooks/destroy_worker_spec.rb'
|
||||||
- 'tooling/bin/find_changes'
|
- 'tooling/bin/find_changes'
|
||||||
- 'tooling/bin/qa/check_if_qa_only_spec_changes'
|
|
||||||
- 'tooling/danger/product_intelligence.rb'
|
- 'tooling/danger/product_intelligence.rb'
|
||||||
- 'tooling/danger/project_helper.rb'
|
- 'tooling/danger/project_helper.rb'
|
||||||
- 'tooling/danger/specs.rb'
|
- 'tooling/danger/specs.rb'
|
||||||
|
|
|
@ -1251,7 +1251,6 @@ Style/IfUnlessModifier:
|
||||||
- 'spec/views/projects/edit.html.haml_spec.rb'
|
- 'spec/views/projects/edit.html.haml_spec.rb'
|
||||||
- 'spec/views/shared/access_tokens/_table.html.haml_spec.rb'
|
- 'spec/views/shared/access_tokens/_table.html.haml_spec.rb'
|
||||||
- 'spec/workers/analytics/usage_trends/counter_job_worker_spec.rb'
|
- 'spec/workers/analytics/usage_trends/counter_job_worker_spec.rb'
|
||||||
- 'tooling/bin/qa/check_if_qa_only_spec_changes'
|
|
||||||
- 'tooling/danger/product_intelligence.rb'
|
- 'tooling/danger/product_intelligence.rb'
|
||||||
- 'tooling/lib/tooling/image.rb'
|
- 'tooling/lib/tooling/image.rb'
|
||||||
- 'tooling/lib/tooling/test_map_packer.rb'
|
- 'tooling/lib/tooling/test_map_packer.rb'
|
||||||
|
|
|
@ -109,5 +109,4 @@ Style/RedundantRegexpEscape:
|
||||||
- 'spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb'
|
- 'spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb'
|
||||||
- 'spec/uploaders/personal_file_uploader_spec.rb'
|
- 'spec/uploaders/personal_file_uploader_spec.rb'
|
||||||
- 'spec/views/help/index.html.haml_spec.rb'
|
- 'spec/views/help/index.html.haml_spec.rb'
|
||||||
- 'tooling/bin/qa/check_if_qa_only_spec_changes'
|
|
||||||
- 'tooling/danger/project_helper.rb'
|
- 'tooling/danger/project_helper.rb'
|
||||||
|
|
|
@ -58,7 +58,9 @@ class UsersController < ApplicationController
|
||||||
# Get all keys of a user(params[:username]) in a text format
|
# Get all keys of a user(params[:username]) in a text format
|
||||||
# Helpful for sysadmins to put in respective servers
|
# Helpful for sysadmins to put in respective servers
|
||||||
def ssh_keys
|
def ssh_keys
|
||||||
render plain: user.all_ssh_keys.join("\n")
|
keys = user.all_ssh_keys.join("\n")
|
||||||
|
keys << "\n" unless keys.empty?
|
||||||
|
render plain: keys
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity
|
def activity
|
||||||
|
@ -74,7 +76,9 @@ class UsersController < ApplicationController
|
||||||
|
|
||||||
# Get all gpg keys of a user(params[:username]) in a text format
|
# Get all gpg keys of a user(params[:username]) in a text format
|
||||||
def gpg_keys
|
def gpg_keys
|
||||||
render plain: user.gpg_keys.select(&:verified?).map(&:key).join("\n")
|
keys = user.gpg_keys.filter_map { |gpg_key| gpg_key.key if gpg_key.verified? }.join("\n")
|
||||||
|
keys << "\n" unless keys.empty?
|
||||||
|
render plain: keys
|
||||||
end
|
end
|
||||||
|
|
||||||
def groups
|
def groups
|
||||||
|
|
|
@ -316,7 +316,12 @@ class IssuableFinder
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
def by_project(items)
|
def by_project(items)
|
||||||
if params.project? || params.projects
|
# When finding issues for multiple projects it's more efficient
|
||||||
|
# to use a JOIN instead of running a sub-query
|
||||||
|
# See https://gitlab.com/gitlab-org/gitlab/-/commit/8591cc02be6b12ed60f763a5e0147f2cbbca99e1
|
||||||
|
if params.projects.is_a?(ActiveRecord::Relation)
|
||||||
|
items.merge(params.projects.reorder(nil)).join_project
|
||||||
|
elsif params.projects
|
||||||
items.of_projects(params.projects).references_project
|
items.of_projects(params.projects).references_project
|
||||||
else
|
else
|
||||||
items.none
|
items.none
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
- else
|
- else
|
||||||
= _('Change your password or recover your current one')
|
= _('Change your password or recover your current one')
|
||||||
= form_for @user, url: profile_password_path, method: :put, html: {class: "update-password"} do |f|
|
= form_for @user, url: profile_password_path, method: :put, html: {class: "update-password"} do |f|
|
||||||
= form_errors(@user)
|
= form_errors(@user, pajamas_alert: true)
|
||||||
|
|
||||||
- unless @user.password_automatically_set?
|
- unless @user.password_automatically_set?
|
||||||
.form-group
|
.form-group
|
||||||
|
|
|
@ -31,22 +31,19 @@
|
||||||
%ul
|
%ul
|
||||||
- if label.project_label? && label.project.group && can?(current_user, :admin_label, label.project.group)
|
- if label.project_label? && label.project.group && can?(current_user, :admin_label, label.project.group)
|
||||||
%li
|
%li
|
||||||
%button.js-promote-project-label-button.gl-button.btn.btn-default-tertiary{ disabled: true, type: 'button',
|
= render Pajamas::ButtonComponent.new(category: :tertiary,
|
||||||
data: { url: promote_project_label_path(label.project, label),
|
button_options: { class: 'js-promote-project-label-button', data: { url: promote_project_label_path(label.project, label), label_title: label.title, label_color: label.color, label_text_color: label.text_color, group_name: label.project.group.name } } ) do
|
||||||
label_title: label.title,
|
|
||||||
label_color: label.color,
|
|
||||||
label_text_color: label.text_color,
|
|
||||||
group_name: label.project.group.name } }
|
|
||||||
= _('Promote to group label')
|
= _('Promote to group label')
|
||||||
%li
|
%li
|
||||||
%span
|
%span
|
||||||
%button.text-danger.js-delete-label-modal-button{ type: 'button', data: { label_name: label.name, subject_name: label.subject_name, destroy_path: label.destroy_path } }
|
= render Pajamas::ButtonComponent.new(category: :tertiary,
|
||||||
|
button_options: { class: 'text-danger js-delete-label-modal-button', data: { label_name: label.name, subject_name: label.subject_name, destroy_path: label.destroy_path } } ) do
|
||||||
= _('Delete')
|
= _('Delete')
|
||||||
- if current_user
|
- if current_user
|
||||||
%li.gl-display-inline-block.label-subscription.js-label-subscription.gl-ml-3
|
%li.gl-display-inline-block.label-subscription.js-label-subscription.gl-ml-3
|
||||||
- if label.can_subscribe_to_label_in_different_levels?
|
- if label.can_subscribe_to_label_in_different_levels?
|
||||||
%button.js-unsubscribe-button.gl-button.btn.btn-default.gl-w-full{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
|
= render Pajamas::ButtonComponent.new(button_options: { class: "js-unsubscribe-button #{('hidden' if status.unsubscribed?)}", data: { url: toggle_subscription_path, toggle: 'tooltip', container: 'body' }, title: tooltip_title } ) do
|
||||||
%span.gl-button-text= _('Unsubscribe')
|
= _('Unsubscribe')
|
||||||
.dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) }
|
.dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) }
|
||||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'gl-w-full', data: { toggle: 'dropdown' } }) do
|
= render Pajamas::ButtonComponent.new(button_options: { class: 'gl-w-full', data: { toggle: 'dropdown' } }) do
|
||||||
= _('Subscribe')
|
= _('Subscribe')
|
||||||
|
@ -54,11 +51,11 @@
|
||||||
.dropdown-menu.dropdown-open-left
|
.dropdown-menu.dropdown-open-left
|
||||||
%ul
|
%ul
|
||||||
%li
|
%li
|
||||||
%button.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_project_label_path(@project, label) } }
|
= render Pajamas::ButtonComponent.new(category: :tertiary, button_options: { class: "js-subscribe-button #{('hidden' unless status.unsubscribed?)}", data: { status: status, url: toggle_subscription_project_label_path(@project, label) } } ) do
|
||||||
%span.gl-button-text= _('Subscribe at project level')
|
= _('Subscribe at project level')
|
||||||
%li
|
%li
|
||||||
%button.js-subscribe-button.js-group-level{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } }
|
= render Pajamas::ButtonComponent.new(category: :tertiary, button_options: { class: "js-subscribe-button js-group-level #{('hidden' unless status.unsubscribed?)}", data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } } ) do
|
||||||
%span.gl-button-text= _('Subscribe at group level')
|
= _('Subscribe at group level')
|
||||||
- else
|
- else
|
||||||
%button.gl-button.js-subscribe-button.btn.btn-default.gl-w-full{ data: { status: status, url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
|
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-subscribe-button gl-w-full', data: { status: status, url: toggle_subscription_path, toggle: 'tooltip', container: 'body' }, title: tooltip_title } ) do
|
||||||
%span.gl-button-text= label_subscription_toggle_button_text(label, @project)
|
= label_subscription_toggle_button_text(label, @project)
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveTmpIndexOnRoutesNamespaceIdColumn < Gitlab::Database::Migration[2.0]
|
||||||
|
INDEX_NAME = 'tmp_index_for_namespace_id_migration_on_routes'
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
remove_concurrent_index_by_name :routes, INDEX_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_concurrent_index :routes,
|
||||||
|
:id,
|
||||||
|
where: "routes.namespace_id is null and routes.source_type = 'Namespace'",
|
||||||
|
name: INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ValidateNotNullConstraintOnRoutesNamespaceIdColumn < Gitlab::Database::Migration[2.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
validate_not_null_constraint :routes, :namespace_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_not_null_constraint :routes, :namespace_id
|
||||||
|
add_not_null_constraint :routes, :namespace_id, validate: false
|
||||||
|
end
|
||||||
|
end
|
1
db/schema_migrations/20220621082245
Normal file
1
db/schema_migrations/20220621082245
Normal file
|
@ -0,0 +1 @@
|
||||||
|
290ff026a11ac5eadd71fb9fb3ba21bed535d148c56b3176f115e973cdb41369
|
1
db/schema_migrations/20220621092245
Normal file
1
db/schema_migrations/20220621092245
Normal file
|
@ -0,0 +1 @@
|
||||||
|
c414443040bb168009bcbe00f874b1f474c1d1dcf563e621cfdf641f21846d59
|
|
@ -20349,7 +20349,8 @@ CREATE TABLE routes (
|
||||||
created_at timestamp without time zone,
|
created_at timestamp without time zone,
|
||||||
updated_at timestamp without time zone,
|
updated_at timestamp without time zone,
|
||||||
name character varying,
|
name character varying,
|
||||||
namespace_id bigint
|
namespace_id bigint,
|
||||||
|
CONSTRAINT check_af84c6c93f CHECK ((namespace_id IS NOT NULL))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE SEQUENCE routes_id_seq
|
CREATE SEQUENCE routes_id_seq
|
||||||
|
@ -24302,9 +24303,6 @@ ALTER TABLE ONLY chat_teams
|
||||||
ALTER TABLE vulnerability_scanners
|
ALTER TABLE vulnerability_scanners
|
||||||
ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID;
|
ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID;
|
||||||
|
|
||||||
ALTER TABLE routes
|
|
||||||
ADD CONSTRAINT check_af84c6c93f CHECK ((namespace_id IS NOT NULL)) NOT VALID;
|
|
||||||
|
|
||||||
ALTER TABLE sprints
|
ALTER TABLE sprints
|
||||||
ADD CONSTRAINT check_ccd8a1eae0 CHECK ((start_date IS NOT NULL)) NOT VALID;
|
ADD CONSTRAINT check_ccd8a1eae0 CHECK ((start_date IS NOT NULL)) NOT VALID;
|
||||||
|
|
||||||
|
@ -29997,8 +29995,6 @@ CREATE INDEX tmp_index_for_namespace_id_migration_on_group_members ON members US
|
||||||
|
|
||||||
CREATE INDEX tmp_index_for_namespace_id_migration_on_project_members ON members USING btree (id) WHERE ((member_namespace_id IS NULL) AND ((type)::text = 'ProjectMember'::text));
|
CREATE INDEX tmp_index_for_namespace_id_migration_on_project_members ON members USING btree (id) WHERE ((member_namespace_id IS NULL) AND ((type)::text = 'ProjectMember'::text));
|
||||||
|
|
||||||
CREATE INDEX tmp_index_for_namespace_id_migration_on_routes ON routes USING btree (id) WHERE ((namespace_id IS NULL) AND ((source_type)::text = 'Namespace'::text));
|
|
||||||
|
|
||||||
CREATE INDEX tmp_index_for_null_project_namespace_id ON projects USING btree (id) WHERE (project_namespace_id IS NULL);
|
CREATE INDEX tmp_index_for_null_project_namespace_id ON projects USING btree (id) WHERE (project_namespace_id IS NULL);
|
||||||
|
|
||||||
CREATE INDEX tmp_index_for_project_namespace_id_migration_on_routes ON routes USING btree (id) WHERE ((namespace_id IS NULL) AND ((source_type)::text = 'Project'::text));
|
CREATE INDEX tmp_index_for_project_namespace_id_migration_on_routes ON routes USING btree (id) WHERE ((namespace_id IS NULL) AND ((source_type)::text = 'Project'::text));
|
||||||
|
|
|
@ -41232,6 +41232,9 @@ msgstr ""
|
||||||
msgid "UsageQuota|Something went wrong while fetching project storage statistics"
|
msgid "UsageQuota|Something went wrong while fetching project storage statistics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "UsageQuota|Something went wrong while loading usage details"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "UsageQuota|Storage"
|
msgid "UsageQuota|Storage"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
106
scripts/determine-qa-tests
Executable file
106
scripts/determine-qa-tests
Executable file
|
@ -0,0 +1,106 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'optparse'
|
||||||
|
|
||||||
|
# This script returns end-to-end tests or test directories. The returned value is stored in QA_TESTS
|
||||||
|
# variable for executing only those tests.
|
||||||
|
|
||||||
|
class DetermineQATests # rubocop:disable Gitlab/NamespacedClass
|
||||||
|
def initialize(options)
|
||||||
|
@changed_files = options.delete(:changed_files)
|
||||||
|
@mr_labels = options.delete(:mr_labels) || []
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
# If only e2e test files have changed, run only those tests
|
||||||
|
qa_tests = if has_qa_spec_only_changes?
|
||||||
|
changed_files
|
||||||
|
|
||||||
|
# If only non qa files have changed, use the devops MR label to run the files in the related directory
|
||||||
|
# However, if a feature flag file has changed, do not return any specific test/test directory
|
||||||
|
elsif has_non_qa_only_changes? && mr_labels.any? && !has_dev_ops_feature_flag_changes?
|
||||||
|
devops_stage = devops_stage_from_mr_labels
|
||||||
|
|
||||||
|
qa_spec_directories_for_devops_stage(devops_stage) if devops_stage
|
||||||
|
end
|
||||||
|
|
||||||
|
trim_path(qa_tests).join(' ') if qa_tests
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :changed_files, :mr_labels
|
||||||
|
|
||||||
|
# Are the changed files only qa specs?
|
||||||
|
#
|
||||||
|
# @return [Boolean] whether the changes files are only qa specs
|
||||||
|
def has_qa_spec_only_changes?
|
||||||
|
changed_files.all? { |file_path| file_path =~ %r{^qa/qa/specs/features/} }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Are the changed files only outside the qa directory?
|
||||||
|
#
|
||||||
|
# @return [Boolean] whether the changes files are outside of qa directory
|
||||||
|
def has_non_qa_only_changes?
|
||||||
|
changed_files.none? { |file_path| file_path =~ %r{^qa/} }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Are the changed files for development and ops feature flags?
|
||||||
|
#
|
||||||
|
# @return [Boolean] whether the changes files are for development and ops feature flags
|
||||||
|
def has_dev_ops_feature_flag_changes?
|
||||||
|
changed_files.any? { |file_path| file_path =~ %r{/feature_flags/(development|ops)/.*\.yml} }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Remove the leading `qa/` from the file or directory paths
|
||||||
|
#
|
||||||
|
# @param [Array] paths Array of file or directory paths
|
||||||
|
# @return [Array] Array of files or directories with the first occurance of `qa/` removed
|
||||||
|
def trim_path(paths)
|
||||||
|
paths.map { |path| path.delete_prefix("qa/") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extract devops stage from MR labels
|
||||||
|
#
|
||||||
|
# @return [String] a devops stage
|
||||||
|
def devops_stage_from_mr_labels
|
||||||
|
mr_labels.find { |label| label =~ /^devops::/ }&.delete_prefix('devops::')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get qa spec directories for devops stage
|
||||||
|
#
|
||||||
|
# @param [String] devops_stage a devops stage
|
||||||
|
# @return [Array] qa spec directories
|
||||||
|
def qa_spec_directories_for_devops_stage(devops_stage)
|
||||||
|
Dir.glob("qa/qa/specs/**/*/").select { |dir| dir =~ %r{\d+_#{devops_stage}/$} }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if $0 == __FILE__
|
||||||
|
options = {}
|
||||||
|
|
||||||
|
OptionParser.new do |opts|
|
||||||
|
opts.on("-f", "--files CHANGED_FILES_PATH", String,
|
||||||
|
"A path to a file containing a list of changed files") do |value|
|
||||||
|
changed_files_path = value
|
||||||
|
abort("ERROR: The specified changed files path does not exist") unless File.exist?(changed_files_path)
|
||||||
|
|
||||||
|
changed_files = File.read(changed_files_path).split(' ')
|
||||||
|
abort("ERROR: There are no changed files") if changed_files.empty?
|
||||||
|
|
||||||
|
options[:changed_files] = changed_files
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-l", "--labels MR_LABELS", String, "A comma separated list of MR labels") do |value|
|
||||||
|
options[:mr_labels] = Array(value&.split(',')).compact
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-h", "--help", "Prints this help") do
|
||||||
|
puts opts
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
end.parse!
|
||||||
|
|
||||||
|
puts DetermineQATests.new(options).execute
|
||||||
|
end
|
|
@ -25,7 +25,7 @@ RSpec.describe 'Profile > Password' do
|
||||||
it 'shows an error message' do
|
it 'shows an error message' do
|
||||||
fill_passwords('mypassword', 'mypassword2')
|
fill_passwords('mypassword', 'mypassword2')
|
||||||
|
|
||||||
page.within('.alert-danger') do
|
page.within('.gl-alert-danger') do
|
||||||
expect(page).to have_content("Password confirmation doesn't match Password")
|
expect(page).to have_content("Password confirmation doesn't match Password")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -236,14 +236,14 @@ RSpec.describe UsersController do
|
||||||
let!(:deploy_key) { create(:deploy_key, user: user) }
|
let!(:deploy_key) { create(:deploy_key, user: user) }
|
||||||
|
|
||||||
shared_examples_for 'renders all public keys' do
|
shared_examples_for 'renders all public keys' do
|
||||||
it 'renders all non-deploy keys separated with a new line with text/plain content type without the comment key' do
|
it 'renders all non-deploy keys terminated with a new line with text/plain content type without the comment key' do
|
||||||
get "/#{user.username}.keys"
|
get "/#{user.username}.keys"
|
||||||
|
|
||||||
expect(response).to be_successful
|
expect(response).to be_successful
|
||||||
expect(response.media_type).to eq("text/plain")
|
expect(response.media_type).to eq("text/plain")
|
||||||
|
|
||||||
expect(response.body).not_to eq('')
|
expect(response.body).not_to eq('')
|
||||||
expect(response.body).to eq(user.all_ssh_keys.join("\n"))
|
expect(response.body).to eq(user.all_ssh_keys.map { |key| key + "\n" }.join)
|
||||||
|
|
||||||
expect(response.body).to include(key.key.sub(' dummy@gitlab.com', ''))
|
expect(response.body).to include(key.key.sub(' dummy@gitlab.com', ''))
|
||||||
expect(response.body).to include(another_key.key.sub(' dummy@gitlab.com', ''))
|
expect(response.body).to include(another_key.key.sub(' dummy@gitlab.com', ''))
|
||||||
|
@ -308,7 +308,7 @@ RSpec.describe UsersController do
|
||||||
let!(:another_gpg_key) { create(:another_gpg_key, user: user.reload) }
|
let!(:another_gpg_key) { create(:another_gpg_key, user: user.reload) }
|
||||||
|
|
||||||
shared_examples_for 'renders all verified GPG keys' do
|
shared_examples_for 'renders all verified GPG keys' do
|
||||||
it 'renders all verified keys separated with a new line with text/plain content type' do
|
it 'renders all verified keys terminated with a new line with text/plain content type' do
|
||||||
get "/#{user.username}.gpg"
|
get "/#{user.username}.gpg"
|
||||||
|
|
||||||
expect(response).to be_successful
|
expect(response).to be_successful
|
||||||
|
@ -316,7 +316,7 @@ RSpec.describe UsersController do
|
||||||
expect(response.media_type).to eq("text/plain")
|
expect(response.media_type).to eq("text/plain")
|
||||||
|
|
||||||
expect(response.body).not_to eq('')
|
expect(response.body).not_to eq('')
|
||||||
expect(response.body).to eq(user.gpg_keys.select(&:verified?).map(&:key).join("\n"))
|
expect(response.body).to eq(user.gpg_keys.filter_map { |gpg_key| gpg_key.key + "\n" if gpg_key.verified? }.join)
|
||||||
|
|
||||||
expect(response.body).to include(gpg_key.key)
|
expect(response.body).to include(gpg_key.key)
|
||||||
expect(response.body).to include(another_gpg_key.key)
|
expect(response.body).to include(another_gpg_key.key)
|
||||||
|
|
109
spec/scripts/determine-qa-tests_spec.rb
Normal file
109
spec/scripts/determine-qa-tests_spec.rb
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
|
load File.expand_path('../../scripts/determine-qa-tests', __dir__)
|
||||||
|
|
||||||
|
RSpec.describe 'scripts/determine-qa-tests' do
|
||||||
|
describe DetermineQATests do
|
||||||
|
describe '.execute' do
|
||||||
|
let(:qa_spec_files) do
|
||||||
|
%w[qa/qa/specs/features/browser_ui/1_manage/test1.rb
|
||||||
|
qa/qa/specs/features/browser_ui/1_manage/user/test2.rb]
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:qa_spec_and_non_spec_files) do
|
||||||
|
%w[qa/qa/specs/features/browser_ui/1_manage/test1.rb
|
||||||
|
qa/qa/page/admin/menu.rb]
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:non_qa_files) do
|
||||||
|
%w[rubocop/code_reuse_helpers.rb
|
||||||
|
app/components/diffs/overflow_warning_component.rb]
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:non_qa_and_feature_flag_files) do
|
||||||
|
%w[rubocop/code_reuse_helpers.rb
|
||||||
|
app/components/diffs/overflow_warning_component.rb
|
||||||
|
config/feature_flags/development/access_token_ajax.yml]
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:qa_spec_and_non_qa_files) do
|
||||||
|
%w[rubocop/code_reuse_helpers.rb
|
||||||
|
app/components/diffs/overflow_warning_component.rb
|
||||||
|
qa/qa/specs/features/browser_ui/1_manage/test1.rb]
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:qa_non_spec_and_non_qa_files) do
|
||||||
|
%w[rubocop/code_reuse_helpers.rb
|
||||||
|
app/components/diffs/overflow_warning_component.rb
|
||||||
|
qa/qa/page/admin/menu.rb]
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'determine qa tests' do
|
||||||
|
context 'when only qa spec files have changed' do
|
||||||
|
it 'returns only the changed qa specs' do
|
||||||
|
subject = described_class.new({ changed_files: qa_spec_files }.merge(labels))
|
||||||
|
|
||||||
|
expect(subject.execute).to eql qa_spec_files.map { |path| path.delete_prefix("qa/") }.join(' ')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when qa spec and non spec files have changed' do
|
||||||
|
it 'does not return any specs' do
|
||||||
|
subject = described_class.new({ changed_files: qa_spec_and_non_spec_files }.merge(labels))
|
||||||
|
expect(subject.execute).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when non-qa and feature flag files have changed' do
|
||||||
|
it 'does not return any specs' do
|
||||||
|
subject = described_class.new({ changed_files: non_qa_and_feature_flag_files }.merge(labels))
|
||||||
|
expect(subject.execute).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when qa spec and non-qa files have changed' do
|
||||||
|
it 'does not return any specs' do
|
||||||
|
subject = described_class.new({ changed_files: qa_spec_and_non_qa_files }.merge(labels))
|
||||||
|
expect(subject.execute).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when qa non-spec and non-qa files have changed' do
|
||||||
|
it 'does not return any specs' do
|
||||||
|
subject = described_class.new({ changed_files: qa_non_spec_and_non_qa_files }.merge(labels))
|
||||||
|
expect(subject.execute).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a devops label is not specified' do
|
||||||
|
let(:labels) { { mr_labels: ['type::feature'] } }
|
||||||
|
|
||||||
|
it_behaves_like 'determine qa tests'
|
||||||
|
|
||||||
|
context 'when only non-qa files have changed' do
|
||||||
|
it 'does not return any specs' do
|
||||||
|
subject = described_class.new({ changed_files: non_qa_files })
|
||||||
|
expect(subject.execute).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a devops label is specified' do
|
||||||
|
let(:labels) { { mr_labels: %w[devops::manage type::feature] } }
|
||||||
|
|
||||||
|
it_behaves_like 'determine qa tests'
|
||||||
|
|
||||||
|
context 'when only non-qa files have changed' do
|
||||||
|
it 'returns the specs for the devops label' do
|
||||||
|
subject = described_class.new({ changed_files: non_qa_files }.merge(labels))
|
||||||
|
allow(subject).to receive(:qa_spec_directories_for_devops_stage)
|
||||||
|
.and_return(['qa/qa/specs/features/browser_ui/1_manage/'])
|
||||||
|
expect(subject.execute).to eql 'qa/specs/features/browser_ui/1_manage/'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,22 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# This script assumes the first argument is a path to a file containing a list of changed files and the second argument
|
|
||||||
# is the path of a file where a list of end-to-end spec files with the leading 'qa/' trimmed will be written to if
|
|
||||||
# all the files are end-to-end test spec files.
|
|
||||||
|
|
||||||
abort("ERROR: Please specify the file containing the list of changed files and a file where the qa only spec files will be written") if ARGV.size != 2
|
|
||||||
|
|
||||||
changed_files_path = ARGV.shift
|
|
||||||
output_file = ARGV.shift
|
|
||||||
|
|
||||||
return unless File.exist?(changed_files_path)
|
|
||||||
|
|
||||||
changed_files = File.read(changed_files_path).split(' ')
|
|
||||||
|
|
||||||
all_files_are_qa_specs = changed_files.all? { |file_path| file_path =~ %r{^qa\/qa\/specs\/features\/} }
|
|
||||||
|
|
||||||
if all_files_are_qa_specs
|
|
||||||
qa_spec_paths_trimmed = changed_files.map { |path| path.sub('qa/', '') }
|
|
||||||
File.write(output_file, qa_spec_paths_trimmed.join(' '))
|
|
||||||
end
|
|
Loading…
Reference in a new issue