Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
37a0f5e2cf
commit
282e71d660
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
3e29afa3670370b8f5801523711d0689f1228a880b1941c44798f4bc76bedbb0
|
|
@ -0,0 +1 @@
|
|||
b29e850775a327dcf6e37e25a43066a0638a55a4e0bd6b818cf496f0b97c6f82
|
|
@ -0,0 +1 @@
|
|||
5b1c25848e3e890fe27c3a43effce093af5f0fe42118c7976919acef84387a0a
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 system’s 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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -30403,6 +30403,9 @@ msgstr ""
|
|||
msgid "Profiles|Connected Accounts"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Created%{time_ago}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Current path: %{path}"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -211,6 +211,7 @@ merge_requests:
|
|||
- user_note_authors
|
||||
- cleanup_schedule
|
||||
- compliance_violations
|
||||
- created_environments
|
||||
external_pull_requests:
|
||||
- project
|
||||
merge_request_diff:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue