Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-16 15:14:12 +00:00
parent 37a0f5e2cf
commit 282e71d660
39 changed files with 1262 additions and 432 deletions

View File

@ -13,6 +13,7 @@ class Environment < ApplicationRecord
self.reactive_cache_work_type = :external_dependency
belongs_to :project, optional: false
belongs_to :merge_request, optional: true
use_fast_destroy :all_deployments
nullify_if_blank :external_url
@ -68,6 +69,7 @@ class Environment < ApplicationRecord
allow_nil: true
validate :safe_external_url
validate :merge_request_not_changed
delegate :manual_actions, :other_manual_actions, to: :last_deployment, allow_nil: true
delegate :auto_rollback_enabled?, to: :project
@ -524,6 +526,12 @@ class Environment < ApplicationRecord
self.tier ||= guess_tier
end
def merge_request_not_changed
if merge_request_id_changed? && persisted?
errors.add(:merge_request, 'merge_request cannot be changed')
end
end
# Guessing the tier of the environment if it's not explicitly specified by users.
# See https://en.wikipedia.org/wiki/Deployment_environment for industry standard deployment environments
def guess_tier

View File

@ -116,6 +116,7 @@ class MergeRequest < ApplicationRecord
has_many :draft_notes
has_many :reviews, inverse_of: :merge_request
has_many :created_environments, class_name: 'Environment', foreign_key: :merge_request_id, inverse_of: :merge_request
KNOWN_MERGE_PARAMS = [
:auto_merge_strategy,

View File

@ -25,8 +25,19 @@ module Environments
def execute_for_merge_request_pipeline(merge_request)
return unless merge_request.actual_head_pipeline&.merge_request?
merge_request.environments_in_head_pipeline(deployment_status: :success).each do |environment|
execute(environment)
created_environments = merge_request.created_environments
if created_environments.any?
created_environments.each { |env| execute(env) }
else
environments_in_head_pipeline = merge_request.environments_in_head_pipeline(deployment_status: :success)
environments_in_head_pipeline.each { |env| execute(env) }
if environments_in_head_pipeline.any?
# If we don't see a message often, we'd be able to remove this path. (or likely in GitLab 16.0)
# See https://gitlab.com/gitlab-org/gitlab/-/issues/372965
Gitlab::AppJsonLogger.info(message: 'Running legacy dynamic environment stop logic', project_id: project.id)
end
end
end

View File

@ -16,7 +16,7 @@
%span.gl-text-truncate.gl-sm-ml-3
= key.fingerprint
.gl-mt-3= s_('Profiles|Created%{time_ago}'.html_safe) % { time_ago: time_ago_with_tooltip(key.created_at, html_class: 'gl-ml-2')}
.gl-mt-3= html_escape(s_('Profiles|Created%{time_ago}')) % { time_ago: time_ago_with_tooltip(key.created_at, html_class: 'gl-ml-2').html_safe}
.key-list-item-dates
%span.last-used-at.gl-mr-3

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddMergeRequestIdToEnvironments < Gitlab::Database::Migration[2.0]
def change
add_column :environments, :merge_request_id, :bigint
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class IndexEvironmentsOnMergeRequestId < Gitlab::Database::Migration[2.0]
INDEX_NAME = 'index_environments_on_merge_request_id'
disable_ddl_transaction!
def up
add_concurrent_index :environments, :merge_request_id, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :environments, INDEX_NAME
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddFkConstraintToEnvironmentsMergeRequestId < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
def up
add_concurrent_foreign_key :environments, :merge_requests, column: :merge_request_id, on_delete: :nullify
end
def down
remove_foreign_key_if_exists :environments, column: :merge_request_id
end
end

View File

@ -0,0 +1 @@
3e29afa3670370b8f5801523711d0689f1228a880b1941c44798f4bc76bedbb0

View File

@ -0,0 +1 @@
b29e850775a327dcf6e37e25a43066a0638a55a4e0bd6b818cf496f0b97c6f82

View File

@ -0,0 +1 @@
5b1c25848e3e890fe27c3a43effce093af5f0fe42118c7976919acef84387a0a

View File

@ -15036,7 +15036,8 @@ CREATE TABLE environments (
slug character varying NOT NULL,
auto_stop_at timestamp with time zone,
auto_delete_at timestamp with time zone,
tier smallint
tier smallint,
merge_request_id bigint
);
CREATE SEQUENCE environments_id_seq
@ -28625,6 +28626,8 @@ CREATE INDEX index_emails_on_user_id ON emails USING btree (user_id);
CREATE INDEX index_enabled_clusters_on_id ON clusters USING btree (id) WHERE (enabled = true);
CREATE INDEX index_environments_on_merge_request_id ON environments USING btree (merge_request_id);
CREATE INDEX index_environments_on_name_varchar_pattern_ops ON environments USING btree (name varchar_pattern_ops);
CREATE UNIQUE INDEX index_environments_on_project_id_and_name ON environments USING btree (project_id, name);
@ -32273,6 +32276,9 @@ ALTER TABLE ONLY deployments
ALTER TABLE ONLY epics
ADD CONSTRAINT fk_013c9f36ca FOREIGN KEY (due_date_sourcing_epic_id) REFERENCES epics(id) ON DELETE SET NULL;
ALTER TABLE ONLY environments
ADD CONSTRAINT fk_01a033a308 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE SET NULL;
ALTER TABLE ONLY incident_management_escalation_rules
ADD CONSTRAINT fk_0314ee86eb FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

View File

@ -124,6 +124,7 @@ The following are required to run Geo:
- Git 2.9 or later
- Git-lfs 2.4.2 or later on the user side when using LFS
- All sites must run [the same GitLab and PostgreSQL versions](setup/database.md#postgresql-replication).
- If using different operating system versions between Geo sites, [check OS locale data compatibility](replication/troubleshooting.md#check-os-locale-data-compatibility) across Geo sites.
Additionally, check the GitLab [minimum requirements](../../install/requirements.md),
and we recommend you use the latest version of GitLab for a better experience.

View File

@ -1234,3 +1234,33 @@ If the above steps are **not successful**, proceed through the next steps:
## Additional tools
There are useful snippets for manipulating Geo internals in the [GitLab Rails Cheat Sheet](../../troubleshooting/gitlab_rails_cheat_sheet.md#geo). For example, you can find how to manually sync or verify a replicable in Rails console.
## Check OS locale data compatibility
If different operating systems or different operating system versions are deployed across Geo sites, we recommend that you perform a locale data compatibility check setting up Geo.
Geo uses PostgreSQL and Streaming Replication to replicate data across Geo sites. PostgreSQL uses locale data provided by the operating systems C library for sorting text. If the locale data in the C library is incompatible across Geo sites, erroneous query results that lead to [incorrect behavior on secondary sites](https://gitlab.com/gitlab-org/gitlab/-/issues/360723). See [here](https://wiki.postgresql.org/wiki/Locale_data_changes) for more details.
On all hosts running PostgreSQL, across all Geo sites, run the following shell command:
```shell
( echo "1-1"; echo "11" ) | LC_COLLATE=en_US.UTF-8 sort
```
The output will either look like:
```plaintext
1-1
11
```
or the reverse order:
```plaintext
11
1-1
```
If the output is identical on all hosts, then they running compatible versions of locale data.
If the output differs on some hosts, then PostgreSQL replication will not work properly. We advise that you select operating system versions that are compatible.

View File

@ -648,7 +648,7 @@ installation, run the following in the [GitLab Rails console](operations/rails_c
Plan.default.actual_limits.update!(ci_max_artifact_size_junit: 10)
```
### Number of files per GitLab Pages web-site
### Number of files per GitLab Pages website
The total number of file entries (including directories and symlinks) is limited to `200,000` per
GitLab Pages website.
@ -663,6 +663,14 @@ For example, to change the limit to `100`:
Plan.default.actual_limits.update!(pages_file_entries: 100)
```
### Number of custom domains per GitLab Pages website
The total number of custom domains per GitLab Pages website is limited to `150` for [GitLab SaaS](../subscriptions/gitlab_com/index.md).
The default limit for [GitLab self-managed](../subscriptions/self_managed/index.md) is `0` (unlimited).
To set a limit on your self-managed instance, use the
[Admin Area](pages/index.md#set-maximum-number-of-gitlab-pages-custom-domains-for-a-project).
### Number of registered runners per scope
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321368) in GitLab 13.12. Disabled by default.

View File

@ -1113,6 +1113,19 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
--form "avatar=@/tmp/example.png"
```
### Remove a group avatar
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96421) in GitLab 15.4.
To remove a group avatar, use a blank value for the `avatar` attribute.
Example request:
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22" \
--data "avatar="
```
## Remove group
> - Immediately deleting subgroups was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/360008) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `immediate_delete_subgroup_api`. Disabled by default.

View File

@ -8,6 +8,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## List project repository tags
> `version` value for the `order_by` attribute [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95150) in GitLab 15.4.
Get a list of repository tags from a project, sorted by update date and time in descending order. This endpoint can be accessed without authentication if the
repository is publicly accessible.
@ -19,9 +21,9 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string| yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user|
| `order_by` | string | no | Return tags ordered by `name`, `updated`, or `version` (since 15.4) fields. Default is `updated` |
| `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc` |
| `id` | integer or string| yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `order_by` | string | no | Return tags ordered by `name`, `updated`, or `version`. Default is `updated`. |
| `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc`. |
| `search` | string | no | Return list of tags matching the search criteria. You can use `^term` and `term$` to find tags that begin and end with `term` respectively. No other regular expressions are supported. |
```json
@ -115,7 +117,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------------------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `tag_name` | string | yes | The name of a tag |
| `ref` | string | yes | Create tag using commit SHA, another tag name, or branch name |
| `message` | string | no | Creates annotated tag |
@ -172,5 +174,5 @@ Parameters:
| Attribute | Type | Required | Description |
| ---------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `tag_name` | string | yes | The name of a tag |

View File

@ -168,7 +168,7 @@ Required options:
```yaml
time_frame: all
data_source: redis
instrumentation_class: 'RedisMetric'
instrumentation_class: RedisMetric
options:
event: pushes
prefix: source_code
@ -199,7 +199,7 @@ You must also use the class's name in the YAML setup.
```yaml
time_frame: all
data_source: redis
instrumentation_class: 'MergeUsageCountRedisMetric'
instrumentation_class: MergeUsageCountRedisMetric
options:
event: pushes
prefix: source_code
@ -217,7 +217,7 @@ Count unique values for `i_quickactions_approve` event.
```yaml
time_frame: 28d
data_source: redis_hll
instrumentation_class: 'RedisHLLMetric'
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_approve
@ -248,7 +248,7 @@ You must also use the class's name in the YAML setup.
```yaml
time_frame: 28d
data_source: redis_hll
instrumentation_class: 'MergeUsageCountRedisHLLMetric'
instrumentation_class: MergeUsageCountRedisHLLMetric
options:
events:
- i_quickactions_approve
@ -288,7 +288,7 @@ You must also include the instrumentation class name in the YAML setup.
```yaml
time_frame: 28d
instrumentation_class: 'IssuesBoardsCountMetric'
instrumentation_class: IssuesBoardsCountMetric
```
## Generic metrics
@ -345,7 +345,7 @@ The generator takes the class name as an argument and the following options:
- `--ee` Indicates if the metric is for EE.
```shell
rails generate gitlab:usage_metric CountIssues --type database
rails generate gitlab:usage_metric CountIssues --type database --operation distinct_count
create lib/gitlab/usage/metrics/instrumentations/count_issues_metric.rb
create spec/lib/gitlab/usage/metrics/instrumentations/count_issues_metric_spec.rb
```

View File

@ -16,7 +16,6 @@ GitLab has two types of namespaces:
read about [repository redirects](../project/repository/index.md#what-happens-when-a-repository-path-changes).
- You cannot create subgroups in a personal namespace.
- Groups in your namespace do not inherit your namespace permissions and group features.
- Creating subgroups under this namespace is not allowed.
- All the *Personal Projects* created will fall under the scope of this namespace.
- A *group* or *subgroup* namespace:

View File

@ -102,6 +102,10 @@ To delete a task:
To show who is responsible for a task, you can assign users to it.
Users on GitLab Free can assign one user per task.
Users on GitLab Premium and higher can assign multiple users to a single task.
See also [multiple assignees for issues](project/issues/multiple_assignees_for_issues.md).
Prerequisites:
- You must have at least the Reporter role for the project.

View File

@ -248,6 +248,8 @@ module API
authorize! :admin_group, group
group.remove_avatar! if params.key?(:avatar) && params[:avatar].nil?
if update_group(group)
present_group_details(params, group, with_projects: true)
else

View File

@ -100,6 +100,62 @@ module API
present release, with: Entities::Release, current_user: current_user, include_html_description: params[:include_html_description]
end
desc 'Download a project release asset file' do
detail 'This feature was introduced in GitLab 15.4.'
named 'download_release_asset_file'
end
params do
requires :tag_name, type: String,
desc: 'The name of the tag.', as: :tag
requires :file_path, type: String,
file_path: true,
desc: 'The path to the file to download, as specified when creating the release asset.'
end
route_setting :authentication, job_token_allowed: true
get ':id/releases/:tag_name/downloads/*file_path', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do
authorize_download_code!
not_found! unless release
link = release.links.find_by_filepath!("/#{params[:file_path]}")
not_found! unless link
redirect link.url
end
desc 'Get the latest project release' do
detail 'This feature was introduced in GitLab 15.4.'
named 'get_latest_release'
end
params do
requires :suffix_path, type: String, file_path: true, desc: 'The path to be suffixed to the latest release'
end
route_setting :authentication, job_token_allowed: true
get ':id/releases/permalink/latest(/)(*suffix_path)', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do
authorize_download_code!
# Try to find the latest release
latest_release = find_latest_release
not_found! unless latest_release
# Build the full API URL with the tag of the latest release
redirect_url = api_v4_projects_releases_path(id: user_project.id, tag_name: latest_release.tag)
# Include the additional suffix_path if present
redirect_url += "/#{params[:suffix_path]}" if params[:suffix_path].present?
# Include any query parameter except `order_by` since we have plans to extend it in the future.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/352945 for reference.
query_parameters_except_order_by = get_query_params.except('order_by')
if query_parameters_except_order_by.present?
redirect_url += "?#{query_parameters_except_order_by.compact.to_param}"
end
redirect redirect_url
end
desc 'Create a new release' do
detail 'This feature was introduced in GitLab 11.7.'
named 'create_release'
@ -232,6 +288,16 @@ module API
@release ||= user_project.releases.find_by_tag(params[:tag])
end
def find_latest_release
ReleasesFinder.new(user_project, current_user, { order_by: 'released_at', sort: 'desc' }).execute.first
end
def get_query_params
return {} unless @request.query_string.present?
Rack::Utils.parse_nested_query(@request.query_string)
end
def log_release_created_audit_event(release)
# extended in EE
end

View File

@ -3,5 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::<%= class_name %>Metric do
it_behaves_like 'a correct instrumented metric value', {}, 1
let(:expected_value) { 1 }
it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', data_source: 'database' }
end

View File

@ -18,7 +18,9 @@ module Gitlab
def ensure_environment(build)
return unless build.instance_of?(::Ci::Build) && build.has_environment?
environment = ::Gitlab::Ci::Pipeline::Seed::Environment.new(build).to_resource
environment = ::Gitlab::Ci::Pipeline::Seed::Environment
.new(build, merge_request: @command.merge_request)
.to_resource
if environment.persisted?
build.persisted_environment = environment

View File

@ -5,12 +5,13 @@ module Gitlab
module Pipeline
module Seed
class Environment < Seed::Base
attr_reader :job
attr_reader :job, :merge_request
delegate :simple_variables, to: :job
def initialize(job)
def initialize(job, merge_request: nil)
@job = job
@merge_request = merge_request
end
def to_resource
@ -18,6 +19,7 @@ module Gitlab
# Initialize the attributes at creation
environment.auto_stop_in = expanded_auto_stop_in
environment.tier = deployment_tier
environment.merge_request = merge_request
end
end

View File

@ -30403,6 +30403,9 @@ msgstr ""
msgid "Profiles|Connected Accounts"
msgstr ""
msgid "Profiles|Created%{time_ago}"
msgstr ""
msgid "Profiles|Current path: %{path}"
msgstr ""

View File

@ -1,58 +1,129 @@
# frozen_string_literal: true
require "spec_helper"
require 'spec_helper'
RSpec.describe "User creates branch", :js do
RSpec.describe 'User creates branch', :js do
include Spec::Support::Helpers::Features::BranchesHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:user) { create(:user) }
before do
project.add_developer(user)
sign_in(user)
shared_examples 'creates new branch' do
specify do
branch_name = "deploy_keys_#{SecureRandom.hex(4)}"
visit(new_project_branch_path(project))
create_branch(branch_name)
expect(page).to have_content(branch_name)
end
end
context 'on new branch page' do
it 'renders I18n supported text' do
page.within('#new-branch-form') do
expect(page).to have_content(_('Branch name'))
expect(page).to have_content(_('Create from'))
expect(page).to have_content(_('Existing branch name, tag, or commit SHA'))
shared_examples 'renders not found page' do
specify do
expect(page).to have_title('Not Found')
expect(page).to have_content('Page Not Found')
end
end
context 'when project is public with private repository' do
let_it_be(:project) { create(:project, :public, :repository, :repository_private, group: group) }
context 'when user is an inherited member from the group' do
context 'and user is a guest' do
before do
group.add_guest(user)
sign_in(user)
visit(new_project_branch_path(project))
end
it_behaves_like 'renders not found page'
end
context 'and user is a developer' do
before do
group.add_developer(user)
sign_in(user)
visit(new_project_branch_path(project))
end
it_behaves_like 'creates new branch'
end
end
end
it "creates new branch" do
branch_name = "deploy_keys"
context 'when project is private' do
let_it_be(:project) { create(:project, :private, :repository, group: group) }
create_branch(branch_name)
context 'when user is a direct project member' do
context 'and user is a developer' do
before do
project.add_developer(user)
sign_in(user)
expect(page).to have_content(branch_name)
end
visit(new_project_branch_path(project))
end
context "when branch name is invalid" do
it "does not create new branch" do
invalid_branch_name = "1.0 stable"
context 'when on new branch page' do
it 'renders I18n supported text' do
page.within('#new-branch-form') do
expect(page).to have_content(_('Branch name'))
expect(page).to have_content(_('Create from'))
expect(page).to have_content(_('Existing branch name, tag, or commit SHA'))
end
end
end
fill_in("branch_name", with: invalid_branch_name)
page.find("body").click # defocus the branch_name input
it_behaves_like 'creates new branch'
select_branch("master")
click_button("Create branch")
context 'when branch name is invalid' do
it 'does not create new branch' do
invalid_branch_name = '1.0 stable'
expect(page).to have_content("Branch name is invalid")
expect(page).to have_content("can't contain spaces")
fill_in('branch_name', with: invalid_branch_name)
page.find('body').click # defocus the branch_name input
select_branch('master')
click_button('Create branch')
expect(page).to have_content('Branch name is invalid')
expect(page).to have_content("can't contain spaces")
end
end
context 'when branch name already exists' do
it 'does not create new branch' do
create_branch('master')
expect(page).to have_content('Branch already exists')
end
end
end
end
end
context "when branch name already exists" do
it "does not create new branch" do
create_branch("master")
context 'when user is an inherited member from the group' do
context 'and user is a guest' do
before do
group.add_guest(user)
sign_in(user)
expect(page).to have_content("Branch already exists")
visit(new_project_branch_path(project))
end
it_behaves_like 'renders not found page'
end
context 'and user is a developer' do
before do
group.add_developer(user)
sign_in(user)
visit(new_project_branch_path(project))
end
it_behaves_like 'creates new branch'
end
end
end
end

View File

@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Runners' do
let(:user) { create(:user) }
let_it_be(:user) { create(:user) }
before do
sign_in(user)
@ -24,25 +24,25 @@ RSpec.describe 'Runners' do
end
context 'when a project has enabled shared_runners' do
let(:project) { create(:project) }
let_it_be(:project) { create(:project) }
before do
project.add_maintainer(user)
end
context 'when a project_type runner is activated on the project' do
let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project]) }
it 'user sees the specific runner' do
visit project_runners_path(project)
within '.activated-specific-runners' do
expect(page).to have_content(specific_runner.display_name)
expect(page).to have_content(project_runner.display_name)
end
click_on specific_runner.short_sha
click_on project_runner.short_sha
expect(page).to have_content(specific_runner.platform)
expect(page).to have_content(project_runner.platform)
end
it 'user can pause and resume the specific runner' do
@ -72,7 +72,7 @@ RSpec.describe 'Runners' do
click_on 'Remove runner'
end
expect(page).not_to have_content(specific_runner.display_name)
expect(page).not_to have_content(project_runner.display_name)
end
it 'user edits the runner to be protected' do
@ -92,7 +92,7 @@ RSpec.describe 'Runners' do
context 'when a runner has a tag' do
before do
specific_runner.update!(tag_list: ['tag'])
project_runner.update!(tag_list: ['tag'])
end
it 'user edits runner not to run untagged jobs' do
@ -120,24 +120,23 @@ RSpec.describe 'Runners' do
expect(page.find('.available-shared-runners')).to have_content(shared_runner.display_name)
end
end
end
context 'when multiple runners are configured' do
let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
let!(:specific_runner_2) { create(:ci_runner, :project, projects: [project]) }
context 'when multiple runners are configured' do
let!(:project_runner_2) { create(:ci_runner, :project, projects: [project]) }
it 'adds pagination to the runner list' do
stub_const('Projects::Settings::CiCdController::NUMBER_OF_RUNNERS_PER_PAGE', 1)
it 'adds pagination to the runner list' do
stub_const('Projects::Settings::CiCdController::NUMBER_OF_RUNNERS_PER_PAGE', 1)
visit project_runners_path(project)
visit project_runners_path(project)
expect(find('.pagination')).not_to be_nil
expect(find('.pagination')).not_to be_nil
end
end
end
context 'when a specific runner exists in another project' do
let(:another_project) { create(:project) }
let!(:specific_runner) { create(:ci_runner, :project, projects: [another_project]) }
let!(:project_runner) { create(:ci_runner, :project, projects: [another_project]) }
before do
another_project.add_maintainer(user)
@ -150,13 +149,13 @@ RSpec.describe 'Runners' do
click_on 'Enable for this project'
end
expect(page.find('.activated-specific-runners')).to have_content(specific_runner.display_name)
expect(page.find('.activated-specific-runners')).to have_content(project_runner.display_name)
within '.activated-specific-runners' do
click_on 'Disable for this project'
end
expect(page.find('.available-specific-runners')).to have_content(specific_runner.display_name)
expect(page.find('.available-specific-runners')).to have_content(project_runner.display_name)
end
end
@ -255,7 +254,8 @@ RSpec.describe 'Runners' do
project.add_maintainer(user)
end
let(:group) { create :group }
let_it_be(:group) { create :group }
let_it_be(:project) { create :project, group: group }
context 'as project and group maintainer' do
before do
@ -263,8 +263,6 @@ RSpec.describe 'Runners' do
end
context 'project with a group but no group runner' do
let(:project) { create :project, group: group }
it 'group runners are not available' do
visit project_runners_path(project)
@ -280,8 +278,6 @@ RSpec.describe 'Runners' do
end
context 'project with a group but no group runner' do
let(:project) { create :project, group: group }
it 'group runners are available' do
visit project_runners_path(project)
@ -304,44 +300,46 @@ RSpec.describe 'Runners' do
end
end
context 'project with a group but no group runner' do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
context 'with group project' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
it 'group runners are not available' do
visit project_runners_path(project)
context 'project with a group but no group runner' do
it 'group runners are not available' do
visit project_runners_path(project)
expect(page).to have_content 'This group does not have any group runners yet.'
expect(page).to have_content 'This group does not have any group runners yet.'
expect(page).not_to have_content 'To register them, go to the group\'s Runners page.'
expect(page).to have_content 'Ask your group owner to set up a group runner.'
end
end
context 'project with a group and a group runner' do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let!(:ci_runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
it 'group runners are available' do
visit project_runners_path(project)
expect(page).to have_content 'Available group runners: 1'
expect(page).to have_content 'group-runner'
expect(page).not_to have_content 'To register them, go to the group\'s Runners page.'
expect(page).to have_content 'Ask your group owner to set up a group runner.'
end
end
it 'group runners may be disabled for a project' do
visit project_runners_path(project)
context 'project with a group and a group runner' do
let_it_be(:ci_runner) do
create(:ci_runner, :group, groups: [group], description: 'group-runner')
end
click_on 'Disable group runners'
it 'group runners are available' do
visit project_runners_path(project)
expect(page).to have_content 'Enable group runners'
expect(project.reload.group_runners_enabled).to be false
expect(page).to have_content 'Available group runners: 1'
expect(page).to have_content 'group-runner'
end
click_on 'Enable group runners'
it 'group runners may be disabled for a project' do
visit project_runners_path(project)
expect(page).to have_content 'Disable group runners'
expect(project.reload.group_runners_enabled).to be true
click_on 'Disable group runners'
expect(page).to have_content 'Enable group runners'
expect(project.reload.group_runners_enabled).to be false
click_on 'Enable group runners'
expect(page).to have_content 'Disable group runners'
expect(project.reload.group_runners_enabled).to be true
end
end
end
end

View File

@ -3,5 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountFooMetric do
it_behaves_like 'a correct instrumented metric value', {}, 1
let(:expected_value) { 1 }
it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', data_source: 'database' }
end

View File

@ -2,11 +2,13 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments, :aggregate_failures do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:stage) { build(:ci_stage, project: project, statuses: [job]) }
let(:pipeline) { build(:ci_pipeline, project: project, stages: [stage]) }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:environment) { project.environments.find_by_name('review/master') }
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
@ -24,12 +26,26 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
context 'when a pipeline contains a deployment job' do
let!(:job) { build(:ci_build, :start_review_app, project: project) }
it 'ensures environment existence for the job' do
expect { subject }.to change { Environment.count }.by(1)
context 'and the environment does not exist' do
it 'creates the environment specified by the job' do
expect { subject }.to change { Environment.count }.by(1)
expect(project.environments.find_by_name('review/master')).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
end
context 'and the pipeline is for a merge request' do
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user, merge_request: merge_request)
end
it 'associates the environment with the merge request' do
expect { subject }.to change { Environment.count }.by(1)
expect(environment.merge_request).to eq(merge_request)
end
end
end
context 'when an environment has already been existed' do
@ -40,10 +56,22 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
it 'ensures environment existence for the job' do
expect { subject }.not_to change { Environment.count }
expect(project.environments.find_by_name('review/master')).to be_present
expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
end
context 'and the pipeline is for a merge request' do
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user, merge_request: merge_request)
end
it 'does not associate the environment with the merge request' do
expect { subject }.not_to change { Environment.count }
expect(environment.merge_request).to be_nil
end
end
end
context 'when an environment name contains an invalid character' do
@ -65,7 +93,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
it 'ensures environment existence for the job' do
expect { subject }.to change { Environment.count }.by(1)
expect(project.environments.find_by_name('review/master')).to be_present
expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
end

View File

@ -191,5 +191,34 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
it_behaves_like 'returning a correct environment'
end
context 'when merge_request is provided' do
let(:environment_name) { 'development' }
let(:attributes) { { environment: environment_name, options: { environment: { name: environment_name } } } }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:seed) { described_class.new(job, merge_request: merge_request) }
context 'and environment does not exist' do
let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
it 'creates an environment associated with the merge request' do
expect { subject }.to change { Environment.count }.by(1)
expect(subject.merge_request).to eq(merge_request)
end
end
context 'and environment already exists' do
before do
create(:environment, project: project, name: environment_name)
end
it 'does not change the merge request associated with the environment' do
expect { subject }.not_to change { Environment.count }
expect(subject.merge_request).to be_nil
end
end
end
end
end

View File

@ -211,6 +211,7 @@ merge_requests:
- user_note_authors
- cleanup_schedule
- compliance_violations
- created_environments
external_pull_requests:
- project
merge_request_diff:

View File

@ -17,6 +17,8 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
it { is_expected.to nullify_if_blank(:external_url) }
it { is_expected.to belong_to(:project).required }
it { is_expected.to belong_to(:merge_request).optional }
it { is_expected.to have_many(:deployments) }
it { is_expected.to have_many(:metrics_dashboard_annotations) }
it { is_expected.to have_many(:alert_management_alerts) }
@ -40,6 +42,26 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
expect(environment).to be_valid
end
context 'does not allow changes to merge_request' do
let(:merge_request) { create(:merge_request, source_project: project) }
it 'for an environment that has no merge request associated' do
environment = create(:environment)
environment.merge_request = merge_request
expect(environment).not_to be_valid
end
it 'for an environment that has a merge request associated' do
environment = create(:environment, merge_request: merge_request)
environment.merge_request = nil
expect(environment).not_to be_valid
end
end
end
describe 'validate and sanitize external url' do

View File

@ -31,6 +31,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
it { is_expected.to have_many(:draft_notes) }
it { is_expected.to have_many(:reviews).inverse_of(:merge_request) }
it { is_expected.to have_one(:cleanup_schedule).inverse_of(:merge_request) }
it { is_expected.to have_many(:created_environments).class_name('Environment').inverse_of(:merge_request) }
context 'for forks' do
let!(:project) { create(:project) }

File diff suppressed because it is too large Load Diff

View File

@ -5,26 +5,18 @@ require 'spec_helper'
RSpec.describe 'Creation of a new branch' do
include GraphqlHelpers
let_it_be(:group) { create(:group, :public) }
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project, :public, :empty_repo) }
let(:input) { { project_path: project.full_path, name: new_branch, ref: ref } }
let(:new_branch) { 'new_branch' }
let(:new_branch) { "new_branch_#{SecureRandom.hex(4)}" }
let(:ref) { 'master' }
let(:mutation) { graphql_mutation(:create_branch, input) }
let(:mutation_response) { graphql_mutation_response(:create_branch) }
context 'the user is not allowed to create a branch' do
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'when user has permissions to create a branch' do
before do
project.add_developer(current_user)
end
it 'creates a new branch' do
shared_examples 'creates a new branch' do
specify do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
@ -33,14 +25,75 @@ RSpec.describe 'Creation of a new branch' do
'commit' => a_hash_including('id')
)
end
end
context 'when ref is not correct' do
err_msg = 'Failed to create branch \'another_branch\': invalid reference name \'unknown\''
let(:new_branch) { 'another_branch' }
let(:ref) { 'unknown' }
context 'when project is public' do
let_it_be(:project) { create(:project, :public, :empty_repo) }
it_behaves_like 'a mutation that returns errors in the response',
errors: [err_msg]
context 'when user is not allowed to create a branch' do
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'when user is a direct project member' do
context 'and user is a developer' do
before do
project.add_developer(current_user)
end
it_behaves_like 'creates a new branch'
context 'when ref is not correct' do
err_msg = 'Failed to create branch \'another_branch\': invalid reference name \'unknown\''
let(:new_branch) { 'another_branch' }
let(:ref) { 'unknown' }
it_behaves_like 'a mutation that returns errors in the response', errors: [err_msg]
end
end
end
context 'when user is an inherited member from the group' do
context 'when project has a private repository' do
let_it_be(:project) { create(:project, :public, :empty_repo, :repository_private, group: group) }
context 'and user is a guest' do
before do
group.add_guest(current_user)
end
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'and user is a developer' do
before do
group.add_developer(current_user)
end
it_behaves_like 'creates a new branch'
end
end
end
end
context 'when project is private' do
let_it_be(:project) { create(:project, :private, :empty_repo, group: group) }
context 'when user is an inherited member from the group' do
context 'and user is a guest' do
before do
group.add_guest(current_user)
end
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'and user is a developer' do
before do
group.add_developer(current_user)
end
it_behaves_like 'creates a new branch'
end
end
end
end

View File

@ -923,6 +923,16 @@ RSpec.describe API::Groups do
expect(json_response['prevent_sharing_groups_outside_hierarchy']).to eq(true)
end
it 'removes the group avatar' do
put api("/groups/#{group1.id}", user1), params: { avatar: '' }
aggregate_failures "testing response" do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['avatar_url']).to be_nil
expect(group1.reload.avatar_url).to be_nil
end
end
it 'does not update visibility_level if it is restricted' do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])

View File

@ -573,6 +573,224 @@ RSpec.describe API::Releases do
end
end
describe 'GET /projects/:id/releases/:tag_name/downloads/*file_path' do
let!(:release) { create(:release, project: project, tag: 'v0.1', author: maintainer) }
let!(:link) { create(:release_link, release: release, url: "#{url}#{filepath}", filepath: filepath) }
let(:filepath) { '/bin/bigfile.exe' }
let(:url) { 'https://google.com/-/jobs/140463678/artifacts/download' }
context 'with an invalid release tag' do
it 'returns 404 for maintater' do
get api("/projects/#{project.id}/releases/v0.2/downloads#{filepath}", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Not Found')
end
it 'returns project not found for no user' do
get api("/projects/#{project.id}/releases/v0.2/downloads#{filepath}", nil)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Project Not Found')
end
it 'returns forbidden for guest' do
get api("/projects/#{project.id}/releases/v0.2/downloads#{filepath}", guest)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'with a valid release tag' do
context 'when filepath is provided' do
context 'when filepath exists' do
it 'redirects to the file download URL' do
get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}", maintainer)
expect(response).to redirect_to("#{url}#{filepath}")
end
it 'redirects to the file download URL when using JOB-TOKEN auth' do
job = create(:ci_build, :running, project: project, user: maintainer)
get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}"), params: { job_token: job.token }
expect(response).to redirect_to("#{url}#{filepath}")
end
context 'when user is a guest' do
it 'responds 403 Forbidden' do
get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}", guest)
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'when project is public' do
let(:project) { create(:project, :repository, :public) }
it 'responds 200 OK' do
get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}", guest)
expect(response).to redirect_to("#{url}#{filepath}")
end
end
end
end
context 'when filepath does not exists' do
it 'returns 404 for maintater' do
get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Not found')
end
it 'returns project not found for no user' do
get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", nil)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Project Not Found')
end
it 'returns forbidden for guest' do
get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", guest)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
context 'when filepath is not provided' do
it 'returns 404 for maintater' do
get api("/projects/#{project.id}/releases/v0.1/downloads", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns project not found for no user' do
get api("/projects/#{project.id}/releases/v0.1/downloads", nil)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns forbidden for guest' do
get api("/projects/#{project.id}/releases/v0.1/downloads", guest)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
describe 'GET /projects/:id/releases/permalink/latest' do
context 'when there is no release' do
it 'returns not found' do
get api("/projects/#{project.id}/releases/permalink/latest", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns not found when using JOB-TOKEN auth' do
job = create(:ci_build, :running, project: project, user: maintainer)
get api("/projects/#{project.id}/releases/permalink/latest"), params: { job_token: job.token }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when there are more than one release' do
let!(:release_a) do
create(:release,
project: project,
tag: 'v0.1',
author: maintainer,
description: 'This is v0.1',
released_at: 3.days.ago)
end
let!(:release_b) do
create(:release,
project: project,
tag: 'v0.2',
author: maintainer,
description: 'This is v0.2',
released_at: 2.days.ago)
end
it 'redirects to the latest release tag' do
get api("/projects/#{project.id}/releases/permalink/latest", maintainer)
uri = URI(response.header["Location"])
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
end
it 'redirects to the latest release tag when using JOB-TOKEN auth' do
job = create(:ci_build, :running, project: project, user: maintainer)
get api("/projects/#{project.id}/releases/permalink/latest"), params: { job_token: job.token }
uri = URI(response.header["Location"])
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
end
context 'when there are query parameters present' do
it 'includes the query params on the redirection' do
get api("/projects/#{project.id}/releases/permalink/latest", maintainer), params: { include_html_description: true, other_param: "aaa" }
uri = URI(response.header["Location"])
query_params = Rack::Utils.parse_nested_query(uri.query)
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
expect(query_params).to include({
"include_html_description" => "true",
"other_param" => "aaa"
})
end
it 'discards the `order_by` query param' do
get api("/projects/#{project.id}/releases/permalink/latest", maintainer), params: { order_by: 'something', other_param: "aaa" }
uri = URI(response.header["Location"])
query_params = Rack::Utils.parse_nested_query(uri.query)
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
expect(query_params).to include({
"other_param" => "aaa"
})
expect(query_params).not_to include({
"order_by" => "something"
})
end
end
context 'when downloading a release asset' do
it 'redirects to the right endpoint keeping the suffix_path' do
get api("/projects/#{project.id}/releases/permalink/latest/downloads/bin/example.exe", maintainer)
uri = URI(response.header["Location"])
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}/downloads/bin/example.exe")
end
it 'returns error when there is path traversal in suffix path' do
get api("/projects/#{project.id}/releases/permalink/latest/downloads/bin/../../../../../../../password.txt", maintainer)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('suffix_path should be a valid file path')
end
end
end
end
describe 'POST /projects/:id/releases' do
let(:params) do
{

View File

@ -201,6 +201,25 @@ RSpec.describe Environments::StopService do
project.add_developer(user)
end
context 'and merge request has associated created_environments' do
let!(:environment1) { create(:environment, project: project, merge_request: merge_request) }
let!(:environment2) { create(:environment, project: project, merge_request: merge_request) }
before do
subject
end
it 'stops the associated created_environments' do
expect(environment1.reload).to be_stopped
expect(environment2.reload).to be_stopped
end
it 'does not affect environments that are not associated to the merge request' do
expect(pipeline.environments_in_self_and_project_descendants.first.merge_request).to be_nil
expect(pipeline.environments_in_self_and_project_descendants.first).to be_available
end
end
it 'stops the active environment' do
subject
expect(pipeline.environments_in_self_and_project_descendants.first).to be_stopping

View File

@ -218,7 +218,6 @@
- './ee/spec/elastic/migrate/20220713103500_delete_commits_from_original_index_spec.rb'
- './ee/spec/factories/lfs_object_spec.rb'
- './ee/spec/features/account_recovery_regular_check_spec.rb'
- './ee/spec/features/admin/admin_audit_logs_spec.rb'
- './ee/spec/features/admin/admin_credentials_inventory_spec.rb'
- './ee/spec/features/admin/admin_dashboard_spec.rb'
- './ee/spec/features/admin/admin_dev_ops_reports_spec.rb'