+
{{ value }}
{{ __('Not enough data') }}
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index 89b673397a2a258c1c31565eddbc5cdd1c38baf1..b1d79a41ba709f5b7b620e11afa7d9f5a1008918 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -51,11 +51,11 @@
}
.stage-header {
- width: 18.5%;
+ width: 20.5%;
}
.median-header {
- width: 21.5%;
+ width: 19.5%;
}
.event-header {
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
index bb46345d362ff996835a16bc364e74d955df0506..f80e891a55854db2b6ee3a029207ea361f329b7f 100644
--- a/app/controllers/concerns/snippets_actions.rb
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -28,4 +28,11 @@ module SnippetsActions
def convert_line_endings(content)
params[:line_ending] == 'raw' ? content : content.gsub(/\r\n/, "\n")
end
+
+ def check_repository_error
+ repository_error = snippet.errors.delete(:repository)
+
+ flash.now[:alert] = repository_error if repository_error
+ recaptcha_check_with_fallback(repository_error.nil?) { render :edit }
+ end
end
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 8317d9fc6d5813527db927b6eac388849e52b6df..241df8d95d7425647465f22b9c17cba939cdafc9 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -62,7 +62,7 @@ class Projects::SnippetsController < Projects::ApplicationController
service_response = Snippets::UpdateService.new(project, current_user, update_params).execute(@snippet)
@snippet = service_response.payload[:snippet]
- recaptcha_check_with_fallback { render :edit }
+ check_repository_error
end
def show
diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb
index 5ce2ed7741757503f53b9207679ed48a234480b1..35ea77183b83706799b9085fc40ed17e96480c4e 100644
--- a/app/controllers/repositories/git_http_controller.rb
+++ b/app/controllers/repositories/git_http_controller.rb
@@ -12,6 +12,8 @@ module Repositories
rescue_from Gitlab::GitAccess::ProjectCreationError, with: :render_422_with_exception
rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503_with_exception
+ before_action :snippet_request_allowed?
+
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
def info_refs
@@ -116,6 +118,12 @@ module Repositories
def log_user_activity
Users::ActivityService.new(user).execute
end
+
+ def snippet_request_allowed?
+ if repo_type.snippet? && Feature.disabled?(:version_snippets, user)
+ render plain: 'The project you were looking for could not be found.', status: :not_found
+ end
+ end
end
end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index e6840e46226dac255ccb430a939b66a15c521770..3f8b13dbcdd0c49db86133d7b225515c7d212f01 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -64,7 +64,7 @@ class SnippetsController < ApplicationController
service_response = Snippets::UpdateService.new(nil, current_user, update_params).execute(@snippet)
@snippet = service_response.payload[:snippet]
- recaptcha_check_with_fallback { render :edit }
+ check_repository_error
end
def show
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 78d815e5858d7430116c216c5251d118bbcfa698..7300283f08649ab9d5a0fc6697eb2e86a3fd8b51 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -260,12 +260,14 @@ module Issuable
highest_priority = highest_label_priority(params).to_sql
- select_columns = [
- "#{table_name}.*",
- "(#{highest_priority}) AS highest_priority"
- ] + extra_select_columns
+ # When using CTE make sure to select the same columns that are on the group_by clause.
+ # This prevents errors when ignored columns are present in the database.
+ issuable_columns = with_cte ? issue_grouping_columns(use_cte: with_cte) : "#{table_name}.*"
- select(select_columns.join(', '))
+ extra_select_columns = extra_select_columns.unshift("(#{highest_priority}) AS highest_priority")
+
+ select(issuable_columns)
+ .select(extra_select_columns)
.group(issue_grouping_columns(use_cte: with_cte))
.reorder(Gitlab::Database.nulls_last_order('highest_priority', direction))
end
@@ -301,7 +303,7 @@ module Issuable
# Returns an array of arel columns
def issue_grouping_columns(use_cte: false)
if use_cte
- [arel_table[:state]] + attribute_names.map { |attr| arel_table[attr.to_sym] }
+ attribute_names.map { |attr| arel_table[attr.to_sym] }
else
arel_table[:id]
end
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index 3ad9f2bc0bf1e77c24b93811a8b44c25af3810d8..b2c0ceb640b5256c1038e56cd94a78e33b3361ed 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -56,8 +56,13 @@ class PipelineSerializer < BaseSerializer
:manual_actions,
:scheduled_actions,
:artifacts,
- :merge_request,
:user,
+ {
+ merge_request: {
+ source_project: [:route, { namespace: :route }],
+ target_project: [:route, { namespace: :route }]
+ }
+ },
{
pending_builds: :project,
project: [:route, { namespace: :route }],
diff --git a/app/services/snippets/update_service.rb b/app/services/snippets/update_service.rb
index c2949ebadbf6b3d3608a34477af5c56f4a125440..874357f36cca9ad5bfa14006733d815804af9694 100644
--- a/app/services/snippets/update_service.rb
+++ b/app/services/snippets/update_service.rb
@@ -52,7 +52,7 @@ module Snippets
create_commit(snippet) if snippet.repository_exists?
end
rescue
- snippet.errors.add(:base, 'Error updating the snippet')
+ snippet.errors.add(:repository, 'Error updating the snippet')
false
end
diff --git a/changelogs/unreleased/fix-api.yml b/changelogs/unreleased/fix-api.yml
new file mode 100644
index 0000000000000000000000000000000000000000..867c534663b453bf13b42144c48c21d7bf644c8d
--- /dev/null
+++ b/changelogs/unreleased/fix-api.yml
@@ -0,0 +1,5 @@
+---
+title: Support finding namespace by ID or path on fork API
+merge_request: 20603
+author: leoleoasd
+type: fixed
diff --git a/changelogs/unreleased/fj-fix-git-error-message-update-snippet.yml b/changelogs/unreleased/fj-fix-git-error-message-update-snippet.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cfb2575ac212a056f0d31bf63bc4f01b77515b08
--- /dev/null
+++ b/changelogs/unreleased/fj-fix-git-error-message-update-snippet.yml
@@ -0,0 +1,5 @@
+---
+title: Show git error message updating snippet
+merge_request: 26570
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-optimize-pipeline-for-mrs.yml b/changelogs/unreleased/sh-optimize-pipeline-for-mrs.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fd604819982f9ae0013971b4cc20836e17869ae2
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-pipeline-for-mrs.yml
@@ -0,0 +1,5 @@
+---
+title: Fix N+1 queries for PipelinesController#index.json
+merge_request: 26643
+author:
+type: performance
diff --git a/doc/administration/git_protocol.md b/doc/administration/git_protocol.md
index b15c316045949224c089b5e938610178cdc6acb1..a8e785f9344c3ae35a33e159ca780bd7bc74fb6b 100644
--- a/doc/administration/git_protocol.md
+++ b/doc/administration/git_protocol.md
@@ -35,10 +35,14 @@ the SSH configuration of your server by adding the line below to the `/etc/ssh/s
AcceptEnv GIT_PROTOCOL
```
-Once configured, restart the SSH daemon. In Ubuntu, run:
+Once configured, restart the SSH daemon for the change to take effect:
```shell
-sudo service ssh restart
+# CentOS 6 / RHEL 6
+sudo service sshd restart
+
+# All other supported distributions
+sudo systemctl restart ssh
```
## Instructions
diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md
index 130ed9f3b25eab42083027a432a77e4669cb62d7..9b5cabae39a127371de9dcd49e89a2ef431677bb 100644
--- a/doc/administration/high_availability/README.md
+++ b/doc/administration/high_availability/README.md
@@ -202,14 +202,8 @@ On different cloud vendors a best effort like for like can be used.
and another for the Queues and Shared State classes respectively. We also recommend
that you run the Redis Sentinel clusters separately as well for each Redis Cluster.
-[^4]: For data objects such as LFS, Uploads, Artifacts, etc... We recommend a Cloud Object Storage
- where possible over NFS due to better performance and availability. Several types of objects
- are supported for S3 storage - [Job artifacts](../job_artifacts.md#using-object-storage),
- [LFS](../lfs/lfs_administration.md#storing-lfs-objects-in-remote-object-storage),
- [Uploads](../uploads.md#using-object-storage-core-only),
- [Merge Request Diffs](../merge_request_diffs.md#using-object-storage),
- [Packages](../packages/index.md#using-object-storage) (Optional Feature),
- [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (Optional Feature).
+[^4]: For data objects such as LFS, Uploads, Artifacts, etc. We recommend a [Cloud Object Storage service](object_storage.md)
+ over NFS where possible, due to better performance and availability.
[^5]: NFS can be used as an alternative for both repository data (replacing Gitaly) and
object storage but this isn't typically recommended for performance reasons. Note however it is required for
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 26c83ab6034dec95062cd3a3ebf68fd8c1a296e1..7187c97ef1352b00cc47334194077baa35ab2498 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -736,10 +736,14 @@ To enable the read-only mode:
This will set the Container Registry into the read only mode.
-1. Next, trigger the garbage collect command:
+1. Next, trigger one of the garbage collect commands:
```sh
+ # Recycling unused tags
sudo /opt/gitlab/embedded/bin/registry garbage-collect /var/opt/gitlab/registry/config.yml
+
+ # Removing unused layers not referenced by manifests
+ sudo /opt/gitlab/embedded/bin/registry garbage-collect -m /var/opt/gitlab/registry/config.yml
```
This will start the garbage collection, which might take some time to complete.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 5b66f0dd5d3fa4768e34c3e38fa3d9b032a61153..5b769ca4ce919ee5c8e3f67bedb57a59f8c85599 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -1207,7 +1207,9 @@ POST /projects/:id/fork
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
-| `namespace` | integer/string | yes | The ID or path of the namespace that the project will be forked to |
+| `namespace` | integer/string | no | (deprecated) The ID or path of the namespace that the project will be forked to |
+| `namespace_id` | integer | no | The ID of the namespace that the project will be forked to |
+| `namespace_path` | string | no | The path of the namespace that the project will be forked to |
| `path` | string | no | The path that will be assigned to the resultant project after forking |
| `name` | string | no | The name that will be assigned to the resultant project after forking |
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index 2c81c41360a3ae00f49125549b245bedec89055a..ec0e5df6a92d5875b6e0bdd22675901689aa4c96 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -42,7 +42,7 @@ The default image is currently
`registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34`.
It includes Ruby 2.6.5, Go 1.12, Git 2.24, Git LFS 2.9, Chrome 73, Node 12, Yarn 1.21,
-PostgreSQL 9.6, and Graphics Magick 1.3.33.
+PostgreSQL 9.6, and Graphics Magick 1.3.34.
The images used in our pipelines are configured in the
[`gitlab-org/gitlab-build-images`](https://gitlab.com/gitlab-org/gitlab-build-images)
@@ -61,8 +61,8 @@ each pipeline includes default variables defined in
## Common job definitions
Most of the jobs [extend from a few CI definitions](../ci/yaml/README.md#extends)
-that are scoped to a single
-[configuration parameter](../ci/yaml/README.md#configuration-parameters).
+defined in [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/global.gitlab-ci.yml)
+that are scoped to a single [configuration parameter](../ci/yaml/README.md#configuration-parameters).
| Job definitions | Description |
|------------------|-------------|
@@ -72,10 +72,27 @@ that are scoped to a single
| `.default-cache` | Allows a job to use a default `cache` definition suitable for Ruby/Rails and frontend tasks. |
| `.use-pg9` | Allows a job to use the `postgres:9.6.17` and `redis:alpine` services. |
| `.use-pg10` | Allows a job to use the `postgres:10.12` and `redis:alpine` services. |
+| `.use-pg11` | Allows a job to use the `postgres:11.6` and `redis:alpine` services. |
| `.use-pg9-ee` | Same as `.use-pg9` but also use the `docker.elastic.co/elasticsearch/elasticsearch:6.4.2` services. |
| `.use-pg10-ee` | Same as `.use-pg10` but also use the `docker.elastic.co/elasticsearch/elasticsearch:6.4.2` services. |
+| `.use-pg11-ee` | Same as `.use-pg11` but also use the `docker.elastic.co/elasticsearch/elasticsearch:6.4.2` services. |
| `.as-if-foss` | Simulate the FOSS project by setting the `FOSS_ONLY='1'` environment variable. |
+## `workflow:rules`
+
+We're using the [`workflow:rules` keyword](../ci/yaml/README.md#workflowrules) to
+define default rules to determine whether or not a pipeline is created.
+
+These rules are defined in
+and are as follows:
+
+1. If `$FORCE_GITLAB_CI` is set, create a pipeline.
+1. For merge requests, create a pipeline.
+1. For `master` branch, create a pipeline (this includes on schedules, pushes, merges, etc.).
+1. For tags, create a pipeline.
+1. If `$GITLAB_INTERNAL` isn't set, don't create a pipeline.
+ 1. For stable, auto-deploy, and security branches, create a pipeline.
+
## `rules`, `if:` conditions and `changes:` patterns
We're using the [`rules` keyword](../ci/yaml/README.md#rules) extensively.
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 8631c8a649d68bf4834442c470ef170d8cd60161..9c98ef1f2f828ec25f9824dc22fe18701681c05c 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -331,7 +331,7 @@ metrics:
unit: "count"
```
-This will render into:
+This works by lowercasing the value of `label` and, if there are more words separated by spaces, replacing those spaces with an underscore (`_`). The transformed value is then checked against the labels of the time series returned by the Prometheus query. If a time series label is found that is equal to the transformed value, then the label value will be used and rendered in the legend like this:

diff --git a/lib/api/entities/remote_mirror.rb b/lib/api/entities/remote_mirror.rb
index dde3e9dea9929c5d84890c762433054e574a7841..18d51726baba8c61647280f6734b4f63777bd875 100644
--- a/lib/api/entities/remote_mirror.rb
+++ b/lib/api/entities/remote_mirror.rb
@@ -12,6 +12,9 @@ module API
expose :last_successful_update_at
expose :last_error
expose :only_protected_branches
+ expose :keep_divergent_refs, if: -> (mirror, _options) do
+ ::Feature.enabled?(:keep_divergent_refs, mirror.project)
+ end
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 6c3b8cb8dd8ffa8aaa963fa290649ab3f48c33fa..c3b5654e217320cf5b8ca077e0cc9bdd026bf7a7 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -142,6 +142,12 @@ module API
end
end
+ def check_namespace_access(namespace)
+ return namespace if can?(current_user, :read_namespace, namespace)
+
+ not_found!('Namespace')
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def find_namespace(id)
if id.to_s =~ /^\d+$/
@@ -153,13 +159,15 @@ module API
# rubocop: enable CodeReuse/ActiveRecord
def find_namespace!(id)
- namespace = find_namespace(id)
+ check_namespace_access(find_namespace(id))
+ end
- if can?(current_user, :read_namespace, namespace)
- namespace
- else
- not_found!('Namespace')
- end
+ def find_namespace_by_path(path)
+ Namespace.find_by_full_path(path)
+ end
+
+ def find_namespace_by_path!(path)
+ check_namespace_access(find_namespace_by_path(path))
end
def find_branch!(branch_name)
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index 2e1891ac656b50f8b88941c46c42b6c1eb077181..cca037b07050f1c88bc176abf75e9c70fcd30b85 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -108,6 +108,10 @@ module API
# check_ip - optional, only in EE version, may limit access to
# group resources based on its IP restrictions
post "/allowed" do
+ if repo_type.snippet? && Feature.disabled?(:version_snippets, actor.user)
+ break response_with_status(code: 404, success: false, message: 'The project you were looking for could not be found.')
+ end
+
# It was moved to a separate method so that EE can alter its behaviour more
# easily.
check_allowed(params)
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index ca55e6ac010f55546acba6122cc7385450269790..3717e25d997b9ca8ac2d55d6fbe819df097c36b3 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -263,7 +263,9 @@ module API
success Entities::Project
end
params do
- optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
+ optional :namespace, type: String, desc: '(deprecated) The ID or name of the namespace that the project will be forked into'
+ optional :namespace_id, type: Integer, desc: 'The ID of the namespace that the project will be forked into'
+ optional :namespace_path, type: String, desc: 'The path of the namespace that the project will be forked into'
optional :path, type: String, desc: 'The path that will be assigned to the fork'
optional :name, type: String, desc: 'The name that will be assigned to the fork'
end
@@ -273,7 +275,15 @@ module API
not_found! unless can?(current_user, :fork_project, user_project)
fork_params = declared_params(include_missing: false)
- fork_params[:namespace] = find_namespace!(fork_params[:namespace]) if fork_params[:namespace].present?
+
+ fork_params[:namespace] =
+ if fork_params[:namespace_id].present?
+ find_namespace!(fork_params[:namespace_id])
+ elsif fork_params[:namespace_path].present?
+ find_namespace_by_path!(fork_params[:namespace_path])
+ elsif fork_params[:namespace].present?
+ find_namespace!(fork_params[:namespace])
+ end
service = ::Projects::ForkService.new(user_project, current_user, fork_params)
@@ -285,8 +295,8 @@ module API
conflict!(forked_project.errors.messages)
else
present forked_project, with: Entities::Project,
- user_can_admin_project: can?(current_user, :admin_project, forked_project),
- current_user: current_user
+ user_can_admin_project: can?(current_user, :admin_project, forked_project),
+ current_user: current_user
end
end
diff --git a/lib/api/remote_mirrors.rb b/lib/api/remote_mirrors.rb
index bdaec67108d0bec1830b6d8210679d9d24e0042f..cb8383ff08a05a4d0c17ed64f5c1700cbdb3d543 100644
--- a/lib/api/remote_mirrors.rb
+++ b/lib/api/remote_mirrors.rb
@@ -33,9 +33,11 @@ module API
requires :url, type: String, desc: 'The URL for a remote mirror'
optional :enabled, type: Boolean, desc: 'Determines if the mirror is enabled'
optional :only_protected_branches, type: Boolean, desc: 'Determines if only protected branches are mirrored'
+ optional :keep_divergent_refs, type: Boolean, desc: 'Determines if divergent refs are kept on the target'
end
post ':id/remote_mirrors' do
create_params = declared_params(include_missing: false)
+ create_params.delete(:keep_divergent_refs) unless ::Feature.enabled?(:keep_divergent_refs, user_project)
new_mirror = user_project.remote_mirrors.create(create_params)
@@ -53,12 +55,15 @@ module API
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
optional :enabled, type: Boolean, desc: 'Determines if the mirror is enabled'
optional :only_protected_branches, type: Boolean, desc: 'Determines if only protected branches are mirrored'
+ optional :keep_divergent_refs, type: Boolean, desc: 'Determines if divergent refs are kept on the target'
end
put ':id/remote_mirrors/:mirror_id' do
mirror = user_project.remote_mirrors.find(params[:mirror_id])
mirror_params = declared_params(include_missing: false)
mirror_params[:id] = mirror_params.delete(:mirror_id)
+ mirror_params.delete(:keep_divergent_refs) unless ::Feature.enabled?(:keep_divergent_refs, user_project)
+
update_params = { remote_mirrors_attributes: mirror_params }
result = ::Projects::UpdateService
diff --git a/lib/gitlab/git_access_snippet.rb b/lib/gitlab/git_access_snippet.rb
index 3956fc8a483a05d5d42a3c3b3664c48e9bb94d98..5817b17164e9c349b501ac5f611d78c4322fbf25 100644
--- a/lib/gitlab/git_access_snippet.rb
+++ b/lib/gitlab/git_access_snippet.rb
@@ -8,7 +8,6 @@ module Gitlab
authentication_mechanism: 'The authentication mechanism is not supported.',
read_snippet: 'You are not allowed to read this snippet.',
update_snippet: 'You are not allowed to update this snippet.',
- project_not_found: 'The project you were looking for could not be found.',
snippet_not_found: 'The snippet you were looking for could not be found.',
repository_not_found: 'The snippet repository you were looking for could not be found.'
}.freeze
@@ -31,10 +30,6 @@ module Gitlab
raise ForbiddenError, ERROR_MESSAGES[:authentication_mechanism]
end
- unless Feature.enabled?(:version_snippets, user)
- raise NotFoundError, ERROR_MESSAGES[:project_not_found]
- end
-
check_snippet_accessibility!
super
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index fd33f32e8770ae92c7607c61585506945c174f47..71bb256cee9d9416b8465e9282b7c8b53b90c313 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -38,9 +38,9 @@ describe Projects::PipelinesController do
expect(response).to match_response_schema('pipeline')
expect(json_response).to include('pipelines')
- expect(json_response['pipelines'].count).to eq 5
- expect(json_response['count']['all']).to eq '5'
- expect(json_response['count']['running']).to eq '1'
+ expect(json_response['pipelines'].count).to eq 6
+ expect(json_response['count']['all']).to eq '6'
+ expect(json_response['count']['running']).to eq '2'
expect(json_response['count']['pending']).to eq '1'
expect(json_response['count']['finished']).to eq '3'
@@ -61,7 +61,7 @@ describe Projects::PipelinesController do
# There appears to be one extra query for Pipelines#has_warnings? for some reason
expect { get_pipelines_index_json }.not_to exceed_query_limit(control_count + 1)
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['pipelines'].count).to eq 10
+ expect(json_response['pipelines'].count).to eq 12
end
end
@@ -77,9 +77,9 @@ describe Projects::PipelinesController do
expect(response).to match_response_schema('pipeline')
expect(json_response).to include('pipelines')
- expect(json_response['pipelines'].count).to eq 5
- expect(json_response['count']['all']).to eq '5'
- expect(json_response['count']['running']).to eq '1'
+ expect(json_response['pipelines'].count).to eq 6
+ expect(json_response['count']['all']).to eq '6'
+ expect(json_response['count']['running']).to eq '2'
expect(json_response['count']['pending']).to eq '1'
expect(json_response['count']['finished']).to eq '3'
@@ -99,8 +99,9 @@ describe Projects::PipelinesController do
# There appears to be one extra query for Pipelines#has_warnings? for some reason
expect { get_pipelines_index_json }.not_to exceed_query_limit(control_count + 1)
+
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['pipelines'].count).to eq 10
+ expect(json_response['pipelines'].count).to eq 12
end
end
@@ -139,7 +140,7 @@ describe Projects::PipelinesController do
it 'returns the pipelines when the user has access' do
get_pipelines_index_json
- expect(json_response['pipelines'].size).to eq(5)
+ expect(json_response['pipelines'].size).to eq(6)
end
end
@@ -155,18 +156,32 @@ describe Projects::PipelinesController do
%w(pending running success failed canceled).each_with_index do |status, index|
create_pipeline(status, project.commit("HEAD~#{index}"))
end
+
+ create_pipeline_with_merge_request
end
- def create_pipeline(status, sha)
+ def create_pipeline_with_merge_request
+ # New merge requests must be created with different branches, so
+ # let's just create new ones with random names.
+ branch_name = "test-#{SecureRandom.hex}"
+ project.repository.create_branch(branch_name, project.repository.root_ref)
+ mr = create(:merge_request, source_project: project, target_project: project, source_branch: branch_name)
+ create_pipeline(:running, project.commit('HEAD'), merge_request: mr)
+ end
+
+ def create_pipeline(status, sha, merge_request: nil)
user = create(:user)
pipeline = create(:ci_empty_pipeline, status: status,
project: project,
sha: sha,
- user: user)
+ user: user,
+ merge_request: merge_request)
create_build(pipeline, 'build', 1, 'build', user)
create_build(pipeline, 'test', 2, 'test', user)
create_build(pipeline, 'deploy', 3, 'deploy', user)
+
+ pipeline
end
def create_build(pipeline, stage, stage_idx, name, user = nil)
diff --git a/spec/controllers/repositories/git_http_controller_spec.rb b/spec/controllers/repositories/git_http_controller_spec.rb
index 2573fdf16ff248baccdec97ab1f55b25a602682b..005db748e91fad961997de4342b5876f04557938 100644
--- a/spec/controllers/repositories/git_http_controller_spec.rb
+++ b/spec/controllers/repositories/git_http_controller_spec.rb
@@ -135,6 +135,38 @@ describe Repositories::GitHttpController do
end
end
+ shared_examples 'snippet feature flag disabled behavior' do
+ before do
+ stub_feature_flags(version_snippets: false)
+
+ request.headers.merge! auth_env(user.username, user.password, nil)
+ end
+
+ describe 'GET #info_refs' do
+ let(:params) { container_params.merge(service: 'git-upload-pack') }
+
+ it 'returns 404' do
+ get :info_refs, params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe 'POST #git_upload_pack' do
+ before do
+ allow(controller).to receive(:authenticate_user).and_return(true)
+ allow(controller).to receive(:verify_workhorse_api!).and_return(true)
+ allow(controller).to receive(:access_check).and_return(nil)
+ end
+
+ it 'returns 404' do
+ post :git_upload_pack, params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
context 'when repository container is a project' do
it_behaves_like 'info_refs behavior' do
let(:user) { project.owner }
@@ -158,6 +190,9 @@ describe Repositories::GitHttpController do
let(:expected_class) { Gitlab::GitAccessSnippet }
let(:expected_object) { personal_snippet }
end
+ it_behaves_like 'snippet feature flag disabled behavior' do
+ let(:user) { personal_snippet.author }
+ end
end
context 'when repository container is a project snippet' do
@@ -172,5 +207,8 @@ describe Repositories::GitHttpController do
let(:expected_class) { Gitlab::GitAccessSnippet }
let(:expected_object) { project_snippet }
end
+ it_behaves_like 'snippet feature flag disabled behavior' do
+ let(:user) { project_snippet.author }
+ end
end
end
diff --git a/spec/features/projects/snippets/user_updates_snippet_spec.rb b/spec/features/projects/snippets/user_updates_snippet_spec.rb
index 93a5b4a726281f0f679d23c6eea3cc6e4c29990e..0c3438575ba4c7ede3a33b450680aa5a4ab7e233 100644
--- a/spec/features/projects/snippets/user_updates_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_updates_snippet_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
describe 'Projects > Snippets > User updates a snippet' do
- let(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, namespace: user.namespace) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
- let(:user) { create(:user) }
before do
stub_feature_flags(snippets_vue: false)
@@ -13,16 +13,33 @@ describe 'Projects > Snippets > User updates a snippet' do
sign_in(user)
visit(project_snippet_path(project, snippet))
- end
- it 'updates a snippet' do
page.within('.detail-page-header') do
first(:link, 'Edit').click
end
+ end
+ it 'updates a snippet' do
fill_in('project_snippet_title', with: 'Snippet new title')
click_button('Save')
expect(page).to have_content('Snippet new title')
end
+
+ context 'when the git operation fails' do
+ before do
+ allow_next_instance_of(Snippets::UpdateService) do |instance|
+ allow(instance).to receive(:create_commit).and_raise(StandardError)
+ end
+
+ fill_in('project_snippet_title', with: 'Snippet new title')
+
+ click_button('Save')
+ end
+
+ it 'renders edit page and displays the error' do
+ expect(page).to have_content('Error updating the snippet')
+ expect(page).to have_content('Edit Snippet')
+ end
+ end
end
diff --git a/spec/features/snippets/user_edits_snippet_spec.rb b/spec/features/snippets/user_edits_snippet_spec.rb
index 1d26660a4f627f850400a470df5e5993b9cb70e0..706758164b28225c81b3c21efb62a7ed81fd66f2 100644
--- a/spec/features/snippets/user_edits_snippet_spec.rb
+++ b/spec/features/snippets/user_edits_snippet_spec.rb
@@ -8,7 +8,7 @@ describe 'User edits snippet', :js do
let(:file_name) { 'test.rb' }
let(:content) { 'puts "test"' }
- let(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, author: user) }
before do
@@ -58,4 +58,21 @@ describe 'User edits snippet', :js do
expect(page).to have_no_xpath("//i[@class='fa fa-lock']")
expect(page).to have_xpath("//i[@class='fa fa-globe']")
end
+
+ context 'when the git operation fails' do
+ before do
+ allow_next_instance_of(Snippets::UpdateService) do |instance|
+ allow(instance).to receive(:create_commit).and_raise(StandardError)
+ end
+
+ fill_in 'personal_snippet_title', with: 'New Snippet Title'
+
+ click_button('Save changes')
+ end
+
+ it 'renders edit page and displays the error' do
+ expect(page).to have_content('Error updating the snippet')
+ expect(page).to have_content('Edit Snippet')
+ end
+ end
end
diff --git a/spec/fixtures/api/schemas/remote_mirror.json b/spec/fixtures/api/schemas/remote_mirror.json
index 416b0f080d9124622c2b15ae44953c62c70de2f2..87bde189db52f59846f665a97a308eecc1fbc15c 100644
--- a/spec/fixtures/api/schemas/remote_mirror.json
+++ b/spec/fixtures/api/schemas/remote_mirror.json
@@ -10,7 +10,7 @@
"last_successful_update_at",
"last_error",
"only_protected_branches"
- ],
+ ],
"properties": {
"id": { "type": "integer" },
"enabled": { "type": "boolean" },
@@ -20,7 +20,8 @@
"last_update_started_at": { "type": ["string", "null"] },
"last_successful_update_at": { "type": ["string", "null"] },
"last_error": { "type": ["string", "null"] },
- "only_protected_branches": { "type": "boolean" }
+ "only_protected_branches": { "type": "boolean" },
+ "keep_divergent_refs": { "type": ["boolean", "null"] }
},
"additionalProperties": false
}
diff --git a/spec/lib/gitlab/git_access_snippet_spec.rb b/spec/lib/gitlab/git_access_snippet_spec.rb
index a68ac8ee8fea2c5ca5badfb2f195816d147aed82..ba7b7da7e7d6f6698d319ed78300ba8c3ad9ab98 100644
--- a/spec/lib/gitlab/git_access_snippet_spec.rb
+++ b/spec/lib/gitlab/git_access_snippet_spec.rb
@@ -31,12 +31,15 @@ describe Gitlab::GitAccessSnippet do
end
describe 'when feature flag :version_snippets is disabled' do
+ let(:user) { snippet.author }
+
before do
stub_feature_flags(version_snippets: false)
end
- it 'does not allow push and pull access' do
- expect { pull_access_check }.to raise_project_not_found
+ it 'allows push and pull access' do
+ expect { pull_access_check }.not_to raise_error
+ expect { push_access_check }.not_to raise_error
end
end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 1f20a088e4f3b4d03ccbdcd33556c258bf0dbef5..95513776f398bdc8b38654426925462f271867d5 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -315,6 +315,18 @@ describe API::Internal::Base do
end
end
+ shared_examples 'snippets with disabled feature flag' do
+ context 'when feature flag :version_snippets is disabled' do
+ it 'returns 404' do
+ stub_feature_flags(version_snippets: false)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
context 'git push with personal snippet' do
it 'responds with success' do
push(key, personal_snippet)
@@ -325,6 +337,10 @@ describe API::Internal::Base do
expect(json_response["gl_repository"]).to eq("snippet-#{personal_snippet.id}")
expect(user.reload.last_activity_on).to be_nil
end
+
+ it_behaves_like 'snippets with disabled feature flag' do
+ subject { push(key, personal_snippet) }
+ end
end
context 'git pull with personal snippet' do
@@ -337,6 +353,10 @@ describe API::Internal::Base do
expect(json_response["gl_repository"]).to eq("snippet-#{personal_snippet.id}")
expect(user.reload.last_activity_on).to eql(Date.today)
end
+
+ it_behaves_like 'snippets with disabled feature flag' do
+ subject { pull(key, personal_snippet) }
+ end
end
context 'git push with project snippet' do
@@ -349,6 +369,10 @@ describe API::Internal::Base do
expect(json_response["gl_repository"]).to eq("snippet-#{project_snippet.id}")
expect(user.reload.last_activity_on).to be_nil
end
+
+ it_behaves_like 'snippets with disabled feature flag' do
+ subject { push(key, project_snippet) }
+ end
end
context 'git pull with project snippet' do
@@ -361,6 +385,10 @@ describe API::Internal::Base do
expect(json_response["gl_repository"]).to eq("snippet-#{project_snippet.id}")
expect(user.reload.last_activity_on).to eql(Date.today)
end
+
+ it_behaves_like 'snippets with disabled feature flag' do
+ subject { pull(key, project_snippet) }
+ end
end
context "git pull" do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 858fdc783ee033d3a2029dbffdd0f4b48664a2cf..1e8ab983b504471eb98579979a8b9753953d2324 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -2871,6 +2871,66 @@ describe API::Projects do
expect(json_response['namespace']['name']).to eq(group2.name)
end
+ context 'when namespace_id is specified' do
+ shared_examples_for 'forking to specified namespace_id' do
+ it 'forks to specified namespace_id' do
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['owner']['id']).to eq(user2.id)
+ expect(json_response['namespace']['id']).to eq(user2.namespace.id)
+ end
+ end
+
+ context 'and namespace_id is specified alone' do
+ before do
+ post api("/projects/#{project.id}/fork", user2), params: { namespace_id: user2.namespace.id }
+ end
+
+ it_behaves_like 'forking to specified namespace_id'
+ end
+
+ context 'and namespace_id and namespace are both specified' do
+ before do
+ post api("/projects/#{project.id}/fork", user2), params: { namespace_id: user2.namespace.id, namespace: admin.namespace.id }
+ end
+
+ it_behaves_like 'forking to specified namespace_id'
+ end
+
+ context 'and namespace_id and namespace_path are both specified' do
+ before do
+ post api("/projects/#{project.id}/fork", user2), params: { namespace_id: user2.namespace.id, namespace_path: admin.namespace.path }
+ end
+
+ it_behaves_like 'forking to specified namespace_id'
+ end
+ end
+
+ context 'when namespace_path is specified' do
+ shared_examples_for 'forking to specified namespace_path' do
+ it 'forks to specified namespace_path' do
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['owner']['id']).to eq(user2.id)
+ expect(json_response['namespace']['path']).to eq(user2.namespace.path)
+ end
+ end
+
+ context 'and namespace_path is specified alone' do
+ before do
+ post api("/projects/#{project.id}/fork", user2), params: { namespace_path: user2.namespace.path }
+ end
+
+ it_behaves_like 'forking to specified namespace_path'
+ end
+
+ context 'and namespace_path and namespace are both specified' do
+ before do
+ post api("/projects/#{project.id}/fork", user2), params: { namespace_path: user2.namespace.path, namespace: admin.namespace.path }
+ end
+
+ it_behaves_like 'forking to specified namespace_path'
+ end
+ end
+
it 'forks to owned subgroup' do
full_path = "#{group2.path}/#{group3.path}"
post api("/projects/#{project.id}/fork", user2), params: { namespace: full_path }
diff --git a/spec/requests/api/remote_mirrors_spec.rb b/spec/requests/api/remote_mirrors_spec.rb
index 2186fe375ac94c8490b821214d03824a496f9a87..5b5188e024cd75e9a1a74f70b0cc0ba0a1e7c2d3 100644
--- a/spec/requests/api/remote_mirrors_spec.rb
+++ b/spec/requests/api/remote_mirrors_spec.rb
@@ -91,6 +91,10 @@ describe API::RemoteMirrors do
let(:route) { ->(id) { "/projects/#{project.id}/remote_mirrors/#{id}" } }
let(:mirror) { project.remote_mirrors.first }
+ before do
+ stub_feature_flags(keep_divergent_refs: false)
+ end
+
it 'requires `admin_remote_mirror` permission' do
put api(route[mirror.id], developer)
@@ -102,12 +106,31 @@ describe API::RemoteMirrors do
put api(route[mirror.id], user), params: {
enabled: '0',
- only_protected_branches: 'true'
+ only_protected_branches: 'true',
+ keep_divergent_refs: 'true'
}
expect(response).to have_gitlab_http_status(:success)
expect(json_response['enabled']).to eq(false)
expect(json_response['only_protected_branches']).to eq(true)
+
+ # Deleted due to lack of feature availability
+ expect(json_response['keep_divergent_refs']).to be_nil
+ end
+
+ context 'with the `keep_divergent_refs` feature enabled' do
+ before do
+ stub_feature_flags(keep_divergent_refs: { enabled: true, project: project })
+ end
+
+ it 'updates the `keep_divergent_refs` attribute' do
+ project.add_maintainer(user)
+
+ put api(route[mirror.id], user), params: { keep_divergent_refs: 'true' }
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response['keep_divergent_refs']).to eq(true)
+ end
end
# TODO: Remove flag: https://gitlab.com/gitlab-org/gitlab/issues/38121
diff --git a/spec/services/snippets/update_service_spec.rb b/spec/services/snippets/update_service_spec.rb
index 2c70cce767da8814317f1d9aefbd9170480fd83b..3605d3f76da1abf5ccc80f6cac7fcd6de63b4534 100644
--- a/spec/services/snippets/update_service_spec.rb
+++ b/spec/services/snippets/update_service_spec.rb
@@ -148,7 +148,7 @@ describe Snippets::UpdateService do
response = subject
expect(response).to be_error
- expect(response.payload[:snippet].errors.full_messages).to eq ['Error updating the snippet']
+ expect(response.payload[:snippet].errors.full_messages).to eq ['Repository Error updating the snippet']
end
end
@@ -173,7 +173,7 @@ describe Snippets::UpdateService do
response = subject
expect(response).to be_error
- expect(response.payload[:snippet].errors.full_messages).to eq ['Error updating the snippet']
+ expect(response.payload[:snippet].errors.full_messages).to eq ['Repository Error updating the snippet']
end
it 'returns error if snippet does not have a snippet_repository' do