From 07e42c5c425b5a781398fea2aaf7eeb8a825724f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 23 Nov 2017 13:17:10 +0100 Subject: [PATCH 01/74] Clarify the docs review process and mention the supported repos --- doc/development/writing_documentation.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/development/writing_documentation.md b/doc/development/writing_documentation.md index 68ba3dd2da3..b6def7ef541 100644 --- a/doc/development/writing_documentation.md +++ b/doc/development/writing_documentation.md @@ -152,12 +152,23 @@ CE and EE. ## Previewing the changes live If you want to preview the doc changes of your merge request live, you can use -the manual `review-docs-deploy` job in your merge request. +the manual `review-docs-deploy` job in your merge request. You will need at +least Master permissions to be able to run it and is currently enabled for the +following projects: + +- https://gitlab.com/gitlab-org/gitlab-ce +- https://gitlab.com/gitlab-org/gitlab-ee + +NOTE: **Note:** +You will need to push a branch to those repositories, it doesn't work for forks. TIP: **Tip:** If your branch contains only documentation changes, you can use [special branch names](#testing) to avoid long running pipelines. +In the mini pipeline graph, you should see an `>>` icon. Clicking on it will +reveal the `review-docs-deploy` job. Hit the play button for the job to start. + ![Manual trigger a docs build](img/manual_build_docs.png) This job will: From 020a8482a47de625e2bfaa6f13a12e7cd8fe3600 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Fri, 1 Dec 2017 15:32:57 +0100 Subject: [PATCH 02/74] Use commit finder instead of rev parse This has the side effect of making this method rugged call free, which is the reason I actually changed this. --- app/models/repository.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 165dafd83fd..82af299ec5e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -256,7 +256,7 @@ class Repository end def diverging_commit_counts(branch) - root_ref_hash = raw_repository.rev_parse_target(root_ref).oid + root_ref_hash = raw_repository.commit(root_ref).id cache.fetch(:"diverging_commit_counts_#{branch.name}") do # Rugged seems to throw a `ReferenceError` when given branch_names rather # than SHA-1 hashes From b72d243872fae75f6751b9a16b9668acf595894b Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 27 Nov 2017 10:44:36 +0100 Subject: [PATCH 03/74] Keep track of all storage keys in a set That way we don't need the to scan the entire keyspace to get all known keys --- .../bvl-circuitbreaker-keys-set.yml | 5 ++++ lib/gitlab/git/storage.rb | 1 + lib/gitlab/git/storage/circuit_breaker.rb | 14 +++++++---- lib/gitlab/git/storage/health.rb | 25 ++++++------------- .../git/storage/circuit_breaker_spec.rb | 19 ++++++++++++++ spec/lib/gitlab/git/storage/health_spec.rb | 1 + 6 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 changelogs/unreleased/bvl-circuitbreaker-keys-set.yml diff --git a/changelogs/unreleased/bvl-circuitbreaker-keys-set.yml b/changelogs/unreleased/bvl-circuitbreaker-keys-set.yml new file mode 100644 index 00000000000..a56456240df --- /dev/null +++ b/changelogs/unreleased/bvl-circuitbreaker-keys-set.yml @@ -0,0 +1,5 @@ +--- +title: Keep track of all circuitbreaker keys in a set +merge_request: 15613 +author: +type: performance diff --git a/lib/gitlab/git/storage.rb b/lib/gitlab/git/storage.rb index 99518c9b1e4..5933312b0b5 100644 --- a/lib/gitlab/git/storage.rb +++ b/lib/gitlab/git/storage.rb @@ -15,6 +15,7 @@ module Gitlab Failing = Class.new(Inaccessible) REDIS_KEY_PREFIX = 'storage_accessible:'.freeze + REDIS_KNOWN_KEYS = "#{REDIS_KEY_PREFIX}known_keys_set".freeze def self.redis Gitlab::Redis::SharedState diff --git a/lib/gitlab/git/storage/circuit_breaker.rb b/lib/gitlab/git/storage/circuit_breaker.rb index be7598ef011..4328c0ea29b 100644 --- a/lib/gitlab/git/storage/circuit_breaker.rb +++ b/lib/gitlab/git/storage/circuit_breaker.rb @@ -13,10 +13,8 @@ module Gitlab delegate :last_failure, :failure_count, to: :failure_info def self.reset_all! - pattern = "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}*" - Gitlab::Git::Storage.redis.with do |redis| - all_storage_keys = redis.scan_each(match: pattern).to_a + all_storage_keys = redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1) redis.del(*all_storage_keys) unless all_storage_keys.empty? end @@ -135,23 +133,29 @@ module Gitlab redis.hset(cache_key, :last_failure, last_failure.to_i) redis.hincrby(cache_key, :failure_count, 1) redis.expire(cache_key, failure_reset_time) + maintain_known_keys(redis) end end end def track_storage_accessible - return if no_failures? - @failure_info = FailureInfo.new(nil, 0) Gitlab::Git::Storage.redis.with do |redis| redis.pipelined do redis.hset(cache_key, :last_failure, nil) redis.hset(cache_key, :failure_count, 0) + maintain_known_keys(redis) end end end + def maintain_known_keys(redis) + expire_time = Time.now.to_i + failure_reset_time + redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, expire_time, cache_key) + redis.zremrangebyscore(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, '-inf', Time.now.to_i) + end + def get_failure_info last_failure, failure_count = Gitlab::Git::Storage.redis.with do |redis| redis.hmget(cache_key, :last_failure, :failure_count) diff --git a/lib/gitlab/git/storage/health.rb b/lib/gitlab/git/storage/health.rb index 7049772fe3b..90bbe85fd37 100644 --- a/lib/gitlab/git/storage/health.rb +++ b/lib/gitlab/git/storage/health.rb @@ -4,8 +4,8 @@ module Gitlab class Health attr_reader :storage_name, :info - def self.pattern_for_storage(storage_name) - "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage_name}:*" + def self.prefix_for_storage(storage_name) + "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage_name}:" end def self.for_all_storages @@ -25,26 +25,15 @@ module Gitlab private_class_method def self.all_keys_for_storages(storage_names, redis) keys_per_storage = {} + all_keys = redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1) - redis.pipelined do - storage_names.each do |storage_name| - pattern = pattern_for_storage(storage_name) - matched_keys = redis.scan_each(match: pattern) + storage_names.each do |storage_name| + prefix = prefix_for_storage(storage_name) - keys_per_storage[storage_name] = matched_keys - end + keys_per_storage[storage_name] = all_keys.select { |key| key.starts_with?(prefix) } end - # We need to make sure each lazy-loaded `Enumerator` for matched keys - # is loaded into an array. - # - # Otherwise it would be loaded in the second `Redis#pipelined` block - # within `.load_for_keys`. In this pipelined call, the active - # Redis-client changes again, so the values would not be available - # until the end of that pipelined-block. - keys_per_storage.each do |storage_name, key_future| - keys_per_storage[storage_name] = key_future.to_a - end + keys_per_storage end private_class_method def self.load_for_keys(keys_per_storage, redis) diff --git a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb b/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb index 72dabca793a..f34c9f09057 100644 --- a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb +++ b/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb @@ -27,6 +27,7 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: def set_in_redis(name, value) Gitlab::Git::Storage.redis.with do |redis| + redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key) redis.hmset(cache_key, name, value) end.first end @@ -181,6 +182,24 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: expect(circuit_breaker.last_failure).to be_nil end + it 'maintains known storage keys' do + Timecop.freeze do + # Insert an old key to expire + old_entry = Time.now.to_i - 3.days.to_i + Gitlab::Git::Storage.redis.with do |redis| + redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, old_entry, 'to_be_removed') + end + + circuit_breaker.perform { '' } + + known_keys = Gitlab::Git::Storage.redis.with do |redis| + redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1) + end + + expect(known_keys).to contain_exactly(cache_key) + end + end + it 'only performs the accessibility check once' do expect(Gitlab::Git::Storage::ForkedStorageCheck) .to receive(:storage_available?).once.and_call_original diff --git a/spec/lib/gitlab/git/storage/health_spec.rb b/spec/lib/gitlab/git/storage/health_spec.rb index 4a14a5201d1..d7a52a04fbb 100644 --- a/spec/lib/gitlab/git/storage/health_spec.rb +++ b/spec/lib/gitlab/git/storage/health_spec.rb @@ -6,6 +6,7 @@ describe Gitlab::Git::Storage::Health, clean_gitlab_redis_shared_state: true, br def set_in_redis(cache_key, value) Gitlab::Git::Storage.redis.with do |redis| + redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key) redis.hmset(cache_key, :failure_count, value) end.first end From 82da87f5fbc6524eca731d8de467eb93c6d86cce Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 1 Dec 2017 18:20:44 +0100 Subject: [PATCH 04/74] Add version mermaid support was introduced --- doc/user/markdown.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/doc/user/markdown.md b/doc/user/markdown.md index fb61e360996..a671c92640a 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -41,7 +41,7 @@ Line-breaks, or softreturns, are rendered if you end a line with two or more spa Sugar is sweet -Roses are red +Roses are red Violets are blue Sugar is sweet @@ -370,14 +370,17 @@ This also works for the asciidoctor `:stem: latexmath`. For details see the [asc ### Mermaid +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107) in +GitLab 10.3. + > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#mermaid -It is possible to generate diagrams and flowcharts from text using [Mermaid][mermaid]. +It is possible to generate diagrams and flowcharts from text using [Mermaid][mermaid]. -In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block. +In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block. -Example: +Example: ```mermaid graph TD; @@ -385,7 +388,7 @@ Example: A-->C; B-->D; C-->D; - ``` + ``` Becomes: @@ -395,7 +398,7 @@ graph TD; A-->C; B-->D; C-->D; -``` +``` For details see the [Mermaid official page][mermaid]. @@ -697,7 +700,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa This line is also a separate paragraph, but... This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*. -This line is also a separate paragraph, and... +This line is also a separate paragraph, and... This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*) spaces. @@ -710,7 +713,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa This line is also a separate paragraph, but... This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*. -This line is also a separate paragraph, and... +This line is also a separate paragraph, and... This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*) spaces. From 88e3ce30ae97ac8eb4b25381cfbe7772819cce0c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 19 Nov 2017 06:35:25 -0800 Subject: [PATCH 05/74] Optimize API /groups/:id/projects by preloading associations Closes #40308 --- changelogs/unreleased/sh-optimize-groups-api.yml | 5 +++++ lib/api/groups.rb | 1 + spec/requests/api/groups_spec.rb | 14 ++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 changelogs/unreleased/sh-optimize-groups-api.yml diff --git a/changelogs/unreleased/sh-optimize-groups-api.yml b/changelogs/unreleased/sh-optimize-groups-api.yml new file mode 100644 index 00000000000..283c2df5c9f --- /dev/null +++ b/changelogs/unreleased/sh-optimize-groups-api.yml @@ -0,0 +1,5 @@ +--- +title: Optimize API /groups/:id/projects by preloading fork_networks table +merge_request: +author: +type: performance diff --git a/lib/api/groups.rb b/lib/api/groups.rb index bcf2e6dae1d..7e9a5502949 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -172,6 +172,7 @@ module API get ":id/projects" do group = find_group!(params[:id]) projects = GroupProjectsFinder.new(group: group, current_user: current_user, params: project_finder_params).execute + projects = projects.preload(:fork_network, :forked_project_link, :project_feature, :project_group_links, :tags, :taggings, :group, :namespace) projects = reorder_projects(projects) entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project present paginate(projects), with: entity, current_user: current_user diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 04a658cd6c3..554723d6b1e 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -401,6 +401,20 @@ describe API::Groups do expect(response).to have_gitlab_http_status(404) end + + it 'avoids N+1 queries' do + get api("/groups/#{group1.id}/projects", admin) + + control_count = ActiveRecord::QueryRecorder.new do + get api("/groups/#{group1.id}/projects", admin) + end.count + + create(:project, namespace: group1) + + expect do + get api("/groups/#{group1.id}/projects", admin) + end.not_to exceed_query_limit(control_count) + end end context 'when using group path in URL' do From 39d293abd2ec135c53448552d482d17f9726701c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 19 Nov 2017 15:25:48 -0800 Subject: [PATCH 06/74] Omit the `all` call after Project#project_group_links to avoid unnecessary loads --- lib/api/entities.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index ce332fe85d2..ca7708e366e 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -156,7 +156,7 @@ module API expose :public_builds, as: :public_jobs expose :ci_config_path expose :shared_with_groups do |project, options| - SharedGroup.represent(project.project_group_links.all, options) + SharedGroup.represent(project.project_group_links, options) end expose :only_allow_merge_if_pipeline_succeeds expose :request_access_enabled From 02cd1702b27ddb15f15f7efeb5ce60412492e41d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 19 Nov 2017 16:02:46 -0800 Subject: [PATCH 07/74] Only serialize namespace essentials in group's projects API response --- lib/api/entities.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index ca7708e366e..5b9b0afed0f 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -146,7 +146,7 @@ module API expose :shared_runners_enabled expose :lfs_enabled?, as: :lfs_enabled expose :creator_id - expose :namespace, using: 'API::Entities::Namespace' + expose :namespace, using: 'API::Entities::NamespaceBasic' expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? } expose :import_status expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] } @@ -618,9 +618,11 @@ module API expose :created_at end - class Namespace < Grape::Entity + class NamespaceBasic < Grape::Entity expose :id, :name, :path, :kind, :full_path, :parent_id + end + class Namespace < NamespaceBasic expose :members_count_with_descendants, if: -> (namespace, opts) { expose_members_count_with_descendants?(namespace, opts) } do |namespace, _| namespace.users_with_descendants.count end From 0dfb8801012de19be006026a01d7db0f04a2d3b3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 19 Nov 2017 16:46:40 -0800 Subject: [PATCH 08/74] Preload project route to avoid N+1 query --- lib/api/groups.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 7e9a5502949..df2d97bceda 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -172,7 +172,7 @@ module API get ":id/projects" do group = find_group!(params[:id]) projects = GroupProjectsFinder.new(group: group, current_user: current_user, params: project_finder_params).execute - projects = projects.preload(:fork_network, :forked_project_link, :project_feature, :project_group_links, :tags, :taggings, :group, :namespace) + projects = projects.preload(:fork_network, :forked_project_link, :project_feature, :project_group_links, :tags, :taggings, :group, :namespace, :route) projects = reorder_projects(projects) entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project present paginate(projects), with: entity, current_user: current_user From dd562ae749db805c7c2c764f4e8b6082009df17f Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 21 Nov 2017 21:59:00 -0800 Subject: [PATCH 09/74] Fix CHANGELOG entry to mention all associations, not just fork networks --- changelogs/unreleased/sh-optimize-groups-api.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/unreleased/sh-optimize-groups-api.yml b/changelogs/unreleased/sh-optimize-groups-api.yml index 283c2df5c9f..37b74715a81 100644 --- a/changelogs/unreleased/sh-optimize-groups-api.yml +++ b/changelogs/unreleased/sh-optimize-groups-api.yml @@ -1,5 +1,5 @@ --- -title: Optimize API /groups/:id/projects by preloading fork_networks table +title: Optimize API /groups/:id/projects by preloading associations merge_request: author: type: performance From a2babf32fe2b57850bd678919947d53e5127f6fe Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Fri, 24 Nov 2017 13:20:52 +0100 Subject: [PATCH 10/74] More preloading improvement and added batch count services --- app/services/base_count_service.rb | 4 +-- app/services/projects/batch_count_service.rb | 35 +++++++++++++++++++ .../projects/batch_forks_count_service.rb | 14 ++++++++ .../batch_open_issues_count_service.rb | 15 ++++++++ app/services/projects/count_service.rb | 2 ++ lib/api/groups.rb | 25 ++++++++++--- 6 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 app/services/projects/batch_count_service.rb create mode 100644 app/services/projects/batch_forks_count_service.rb create mode 100644 app/services/projects/batch_open_issues_count_service.rb diff --git a/app/services/base_count_service.rb b/app/services/base_count_service.rb index 19873fe09a5..2e4fd1db03e 100644 --- a/app/services/base_count_service.rb +++ b/app/services/base_count_service.rb @@ -12,8 +12,8 @@ class BaseCountService Rails.cache.fetch(cache_key, cache_options) { uncached_count }.to_i end - def refresh_cache - Rails.cache.write(cache_key, uncached_count, raw: raw?) + def refresh_cache(&block) + Rails.cache.write(cache_key, block_given? ? yield : uncached_count, raw: raw?) end def uncached_count diff --git a/app/services/projects/batch_count_service.rb b/app/services/projects/batch_count_service.rb new file mode 100644 index 00000000000..5b69e24b893 --- /dev/null +++ b/app/services/projects/batch_count_service.rb @@ -0,0 +1,35 @@ +module Projects + class BatchCountService + def initialize(projects) + @projects = projects + end + + def count + @projects.map do |project| + [project.id, current_count_service(project).count] + end.to_h + end + + def refresh_cache + @projects.each do |project| + current_count_service(project).refresh_cache { global_count[project.id].to_i } + end + end + + def current_count_service(project) + if defined? @service + @service.project = project + else + count_service.new(project) + end + end + + def global_count(project) + raise NotImplementedError, 'global_count must be implemented and return an hash indexed by the project id' + end + + def count_service + raise NotImplementedError, 'count_service must be implemented and return a Projects::CountService object' + end + end +end diff --git a/app/services/projects/batch_forks_count_service.rb b/app/services/projects/batch_forks_count_service.rb new file mode 100644 index 00000000000..51fad1c12a4 --- /dev/null +++ b/app/services/projects/batch_forks_count_service.rb @@ -0,0 +1,14 @@ +module Projects + # Service class for getting and caching the number of forks of several projects + class BatchForksCountService < Projects::BatchCountService + def global_count + @global_count ||= ForkedProjectLink.where(forked_from_project: @projects.map(&:id)) + .group(:forked_from_project_id) + .count + end + + def count_service + ::Projects::ForksCountService + end + end +end diff --git a/app/services/projects/batch_open_issues_count_service.rb b/app/services/projects/batch_open_issues_count_service.rb new file mode 100644 index 00000000000..a0d9120487c --- /dev/null +++ b/app/services/projects/batch_open_issues_count_service.rb @@ -0,0 +1,15 @@ +module Projects + # Service class for getting and caching the number of forks of several projects + class BatchOpenIssuesCountService < Projects::BatchCountService + def global_count + @global_count ||= Issue.opened.public_only + .where(project: @projects.map(&:id)) + .group(:project_id) + .count + end + + def count_service + ::Projects::OpenIssuesCountService + end + end +end diff --git a/app/services/projects/count_service.rb b/app/services/projects/count_service.rb index 7e575b2d6f3..a3ec52232dd 100644 --- a/app/services/projects/count_service.rb +++ b/app/services/projects/count_service.rb @@ -7,6 +7,8 @@ module Projects # all caches. VERSION = 1 + attr_accessor :project + def initialize(project) @project = project end diff --git a/lib/api/groups.rb b/lib/api/groups.rb index df2d97bceda..746dcc9fc91 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -52,6 +52,24 @@ module API groups end + def find_group_projects(params) + group = find_group!(params[:id]) + projects = GroupProjectsFinder.new(group: group, current_user: current_user, params: project_finder_params).execute + projects = projects.preload(:project_feature, :route, :group) + .preload(namespace: [:route, :owner], + tags: :taggings, + project_group_links: :group, + fork_network: :root_project, + forked_project_link: :forked_from_project, + forked_from_project: [:route, :forks, namespace: :route, tags: :taggings]) + projects = reorder_projects(projects) + paginated_projects = paginate(projects) + projects_with_fork = paginated_projects + paginated_projects.map(&:forked_from_project).compact + ::Projects::BatchForksCountService.new(projects_with_fork).refresh_cache + ::Projects::BatchOpenIssuesCountService.new(paginated_projects).refresh_cache + paginated_projects + end + def present_groups(params, groups) options = { with: Entities::Group, @@ -170,12 +188,9 @@ module API use :pagination end get ":id/projects" do - group = find_group!(params[:id]) - projects = GroupProjectsFinder.new(group: group, current_user: current_user, params: project_finder_params).execute - projects = projects.preload(:fork_network, :forked_project_link, :project_feature, :project_group_links, :tags, :taggings, :group, :namespace, :route) - projects = reorder_projects(projects) + projects = find_group_projects(params) entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project - present paginate(projects), with: entity, current_user: current_user + present projects, with: entity, current_user: current_user end desc 'Get a list of subgroups in this group.' do From 7c7877b54dfb0a07bf128102226e338463654431 Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Fri, 24 Nov 2017 13:32:01 +0100 Subject: [PATCH 11/74] Small renaming --- lib/api/groups.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 746dcc9fc91..05443329a32 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -63,11 +63,11 @@ module API forked_project_link: :forked_from_project, forked_from_project: [:route, :forks, namespace: :route, tags: :taggings]) projects = reorder_projects(projects) - paginated_projects = paginate(projects) - projects_with_fork = paginated_projects + paginated_projects.map(&:forked_from_project).compact + projects = paginate(projects) + projects_with_fork = projects + projects.map(&:forked_from_project).compact ::Projects::BatchForksCountService.new(projects_with_fork).refresh_cache - ::Projects::BatchOpenIssuesCountService.new(paginated_projects).refresh_cache - paginated_projects + ::Projects::BatchOpenIssuesCountService.new(projects).refresh_cache + projects end def present_groups(params, groups) From c85f9c5b1d320f7a6f75e3d08bbafd2fb20d3f58 Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Fri, 24 Nov 2017 17:37:02 +0100 Subject: [PATCH 12/74] Code review comments applied --- app/services/base_count_service.rb | 4 ++++ app/services/projects/batch_count_service.rb | 8 ++++++-- lib/api/groups.rb | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/services/base_count_service.rb b/app/services/base_count_service.rb index 2e4fd1db03e..f2844854112 100644 --- a/app/services/base_count_service.rb +++ b/app/services/base_count_service.rb @@ -12,6 +12,10 @@ class BaseCountService Rails.cache.fetch(cache_key, cache_options) { uncached_count }.to_i end + def count_stored? + Rails.cache.read(cache_key).present? + end + def refresh_cache(&block) Rails.cache.write(cache_key, block_given? ? yield : uncached_count, raw: raw?) end diff --git a/app/services/projects/batch_count_service.rb b/app/services/projects/batch_count_service.rb index 5b69e24b893..b8a030e7bc5 100644 --- a/app/services/projects/batch_count_service.rb +++ b/app/services/projects/batch_count_service.rb @@ -12,7 +12,9 @@ module Projects def refresh_cache @projects.each do |project| - current_count_service(project).refresh_cache { global_count[project.id].to_i } + unless current_count_service(project).count_stored? + current_count_service(project).refresh_cache { global_count[project.id].to_i } + end end end @@ -20,8 +22,10 @@ module Projects if defined? @service @service.project = project else - count_service.new(project) + @service = count_service.new(project) end + + @service end def global_count(project) diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 05443329a32..83c7898150a 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -64,8 +64,8 @@ module API forked_from_project: [:route, :forks, namespace: :route, tags: :taggings]) projects = reorder_projects(projects) projects = paginate(projects) - projects_with_fork = projects + projects.map(&:forked_from_project).compact - ::Projects::BatchForksCountService.new(projects_with_fork).refresh_cache + projects_with_forks = projects + projects.map(&:forked_from_project).compact + ::Projects::BatchForksCountService.new(projects_with_forks).refresh_cache ::Projects::BatchOpenIssuesCountService.new(projects).refresh_cache projects end From 58c5b463ff75618a557d067c16f49ef581cda85c Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Mon, 27 Nov 2017 12:24:33 +0100 Subject: [PATCH 13/74] Refactored /projects and /user/:user_id/projects endpoints --- lib/api/entities.rb | 48 ++++++++++++++++++++++------ lib/api/groups.rb | 16 ++-------- lib/api/projects.rb | 4 +-- lib/api/projects_relation_builder.rb | 34 ++++++++++++++++++++ 4 files changed, 77 insertions(+), 25 deletions(-) create mode 100644 lib/api/projects_relation_builder.rb diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 5b9b0afed0f..8f41b930c0a 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -94,7 +94,13 @@ module API project.avatar_url(only_path: false) end expose :star_count, :forks_count - expose :last_activity_at + expose :created_at, :last_activity_at + + def self.preload_relation(projects_relation) + projects_relation.preload(:project_feature, :route) + .preload(namespace: [:route, :owner], + tags: :taggings) + end end class Project < BasicProjectDetails @@ -128,6 +134,18 @@ module API expose :members do |project| expose_url(api_v4_projects_members_path(id: project.id)) end + + def self.preload_relation(projects_relation) + super(projects_relation).preload(:group) + .preload(project_group_links: :group, + fork_network: :root_project, + forked_project_link: :forked_from_project, + forked_from_project: [:route, :forks, namespace: :route, tags: :taggings]) + end + + def self.forks_counting_projects(projects_relation) + projects_relation + projects_relation.map(&:forked_from_project).compact + end end expose :archived?, as: :archived @@ -635,9 +653,16 @@ module API class MemberAccess < Grape::Entity expose :access_level expose :notification_level do |member, options| - if member.notification_setting - ::NotificationSetting.levels[member.notification_setting.level] - end + notification = member_notification_setting(member) + ::NotificationSetting.levels[notification.level] if notification + end + + private + + def member_notification_setting(member) + member.user.notification_settings.select do |notification| + notification.source_id == member.source_id && notification.source_type == member.source_type + end.last end end @@ -680,18 +705,21 @@ module API expose :permissions do expose :project_access, using: Entities::ProjectAccess do |project, options| if options.key?(:project_members) - (options[:project_members] || []).find { |member| member.source_id == project.id } - else - project.project_members.find_by(user_id: options[:current_user].id) + (options[:project_members] || []).select { |member| member.source_id == project.id }.last + elsif project.project_members.any? + # This is not the bet option to search in a CollectionProxy, but if + # we use find_by we will perform another query, even if the association + # is loaded + project.project_members.select { |project_member| project_member.user_id == options[:current_user].id }.last end end expose :group_access, using: Entities::GroupAccess do |project, options| if project.group if options.key?(:group_members) - (options[:group_members] || []).find { |member| member.source_id == project.namespace_id } - else - project.group.group_members.find_by(user_id: options[:current_user].id) + (options[:group_members] || []).select { |member| member.source_id == project.namespace_id }.last + elsif project.group.group_members.any? + project.group.group_members.select { |group_member| group_member.user_id == options[:current_user].id }.last end end end diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 83c7898150a..b81f07a1770 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -55,19 +55,8 @@ module API def find_group_projects(params) group = find_group!(params[:id]) projects = GroupProjectsFinder.new(group: group, current_user: current_user, params: project_finder_params).execute - projects = projects.preload(:project_feature, :route, :group) - .preload(namespace: [:route, :owner], - tags: :taggings, - project_group_links: :group, - fork_network: :root_project, - forked_project_link: :forked_from_project, - forked_from_project: [:route, :forks, namespace: :route, tags: :taggings]) projects = reorder_projects(projects) - projects = paginate(projects) - projects_with_forks = projects + projects.map(&:forked_from_project).compact - ::Projects::BatchForksCountService.new(projects_with_forks).refresh_cache - ::Projects::BatchOpenIssuesCountService.new(projects).refresh_cache - projects + paginate(projects) end def present_groups(params, groups) @@ -190,7 +179,8 @@ module API get ":id/projects" do projects = find_group_projects(params) entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project - present projects, with: entity, current_user: current_user + + present entity.prepare_relation(projects), with: entity, current_user: current_user end desc 'Get a list of subgroups in this group.' do diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 4cd7e714aa2..12506203429 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -79,9 +79,9 @@ module API projects = projects.with_statistics if params[:statistics] projects = projects.with_issues_enabled if params[:with_issues_enabled] projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled] + projects = paginate(projects) if current_user - projects = projects.includes(:route, :taggings, namespace: :route) project_members = current_user.project_members group_members = current_user.group_members end @@ -95,7 +95,7 @@ module API ) options[:with] = Entities::BasicProjectDetails if params[:simple] - present paginate(projects), options + present options[:with].prepare_relation(projects), options end end diff --git a/lib/api/projects_relation_builder.rb b/lib/api/projects_relation_builder.rb new file mode 100644 index 00000000000..57df6157225 --- /dev/null +++ b/lib/api/projects_relation_builder.rb @@ -0,0 +1,34 @@ +module API + module ProjectsRelationBuilder + extend ActiveSupport::Concern + + module ClassMethods + def prepare_relation(relation) + relation = preload_relation(relation) + execute_batch_counting(relation) + relation + end + + def preload_relation(relation) + raise NotImplementedError, 'self.preload_relation method must be defined and return a relation' + end + + def forks_counting_projects(projects_relation) + projects_relation + end + + def batch_forks_counting(projects_relation) + ::Projects::BatchForksCountService.new(forks_counting_projects(projects_relation)).refresh_cache + end + + def batch_open_issues_counting(projects_relation) + ::Projects::BatchOpenIssuesCountService.new(projects_relation).refresh_cache + end + + def execute_batch_counting(projects_relation) + batch_forks_counting(projects_relation) + batch_open_issues_counting(projects_relation) + end + end + end +end From 194f7bca9a286942bcd764c836180964e33a1e92 Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Thu, 30 Nov 2017 12:52:38 +0100 Subject: [PATCH 14/74] Comments from code review applied. Also switched forked_from_project and ForkedProjectLinks to ForkNetworkMember --- app/models/group.rb | 8 ++ app/models/project.rb | 6 +- app/models/user.rb | 6 +- app/services/projects/batch_count_service.rb | 21 ++---- .../projects/batch_forks_count_service.rb | 8 +- .../batch_open_issues_count_service.rb | 7 +- app/services/projects/count_service.rb | 2 - app/services/projects/forks_count_service.rb | 6 +- .../projects/open_issues_count_service.rb | 10 ++- lib/api/entities.rb | 75 +++++++++++-------- lib/api/projects.rb | 10 +-- lib/api/projects_relation_builder.rb | 10 +-- 12 files changed, 96 insertions(+), 73 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 76262acf50c..27287f079a4 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -289,6 +289,14 @@ class Group < Namespace "#{parent.full_path}/#{path_was}" end + def group_member(user) + if group_members.loaded? + group_members.find { |gm| gm.user_id == user.id } + else + group_members.find_by(user_id: user) + end + end + private def update_two_factor_requirement diff --git a/app/models/project.rb b/app/models/project.rb index eaf4f555d3b..6f57763b05b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1114,7 +1114,11 @@ class Project < ActiveRecord::Base end def project_member(user) - project_members.find_by(user_id: user) + if project_members.loaded? + project_members.find { |member| member.user_id == user.id } + else + project_members.find_by(user_id: user) + end end def default_branch diff --git a/app/models/user.rb b/app/models/user.rb index 14941fd7f98..ee12f541b0f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -998,7 +998,11 @@ class User < ActiveRecord::Base end def notification_settings_for(source) - notification_settings.find_or_initialize_by(source: source) + if notification_settings.loaded? + notification_settings.find { |notification| notification.source == source } + else + notification_settings.find_or_initialize_by(source: source) + end end # Lazy load global notification setting diff --git a/app/services/projects/batch_count_service.rb b/app/services/projects/batch_count_service.rb index b8a030e7bc5..a79fdb62d69 100644 --- a/app/services/projects/batch_count_service.rb +++ b/app/services/projects/batch_count_service.rb @@ -4,28 +4,19 @@ module Projects @projects = projects end - def count - @projects.map do |project| - [project.id, current_count_service(project).count] - end.to_h - end - def refresh_cache @projects.each do |project| - unless current_count_service(project).count_stored? - current_count_service(project).refresh_cache { global_count[project.id].to_i } + service = count_service.new(project) + unless service.count_stored? + service.refresh_cache { global_count[project.id].to_i } end end end - def current_count_service(project) - if defined? @service - @service.project = project - else - @service = count_service.new(project) - end + def project_ids + return @projects if @projects.is_a?(ActiveRecord::Relation) - @service + @projects.map(&:id) end def global_count(project) diff --git a/app/services/projects/batch_forks_count_service.rb b/app/services/projects/batch_forks_count_service.rb index 51fad1c12a4..b9ed0b3cf4f 100644 --- a/app/services/projects/batch_forks_count_service.rb +++ b/app/services/projects/batch_forks_count_service.rb @@ -2,9 +2,11 @@ module Projects # Service class for getting and caching the number of forks of several projects class BatchForksCountService < Projects::BatchCountService def global_count - @global_count ||= ForkedProjectLink.where(forked_from_project: @projects.map(&:id)) - .group(:forked_from_project_id) - .count + @global_count ||= begin + count_service.query(project_ids) + .group(:forked_from_project_id) + .count + end end def count_service diff --git a/app/services/projects/batch_open_issues_count_service.rb b/app/services/projects/batch_open_issues_count_service.rb index a0d9120487c..5087684863b 100644 --- a/app/services/projects/batch_open_issues_count_service.rb +++ b/app/services/projects/batch_open_issues_count_service.rb @@ -2,10 +2,9 @@ module Projects # Service class for getting and caching the number of forks of several projects class BatchOpenIssuesCountService < Projects::BatchCountService def global_count - @global_count ||= Issue.opened.public_only - .where(project: @projects.map(&:id)) - .group(:project_id) - .count + @global_count ||= begin + count_service.query(project_ids).group(:project_id).count + end end def count_service diff --git a/app/services/projects/count_service.rb b/app/services/projects/count_service.rb index a3ec52232dd..7e575b2d6f3 100644 --- a/app/services/projects/count_service.rb +++ b/app/services/projects/count_service.rb @@ -7,8 +7,6 @@ module Projects # all caches. VERSION = 1 - attr_accessor :project - def initialize(project) @project = project end diff --git a/app/services/projects/forks_count_service.rb b/app/services/projects/forks_count_service.rb index d9bdf3a8ad7..d67ae78b268 100644 --- a/app/services/projects/forks_count_service.rb +++ b/app/services/projects/forks_count_service.rb @@ -2,11 +2,15 @@ module Projects # Service class for getting and caching the number of forks of a project. class ForksCountService < Projects::CountService def relation_for_count - @project.forks + self.class.query(@project.id) end def cache_key_name 'forks_count' end + + def self.query(project_ids) + ForkNetworkMember.where(forked_from_project: project_ids) + end end end diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb index 25de97325e2..ab1c477936a 100644 --- a/app/services/projects/open_issues_count_service.rb +++ b/app/services/projects/open_issues_count_service.rb @@ -3,13 +3,17 @@ module Projects # project. class OpenIssuesCountService < Projects::CountService def relation_for_count - # We don't include confidential issues in this number since this would - # expose the number of confidential issues to non project members. - @project.issues.opened.public_only + self.class.query(@project.id) end def cache_key_name 'open_issues_count' end + + def self.query(project_ids) + # We don't include confidential issues in this number since this would + # expose the number of confidential issues to non project members. + Issue.opened.public_only.where(project: project_ids) + end end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 8f41b930c0a..dada353a314 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -96,7 +96,7 @@ module API expose :star_count, :forks_count expose :created_at, :last_activity_at - def self.preload_relation(projects_relation) + def self.preload_relation(projects_relation, options = {}) projects_relation.preload(:project_feature, :route) .preload(namespace: [:route, :owner], tags: :taggings) @@ -134,18 +134,6 @@ module API expose :members do |project| expose_url(api_v4_projects_members_path(id: project.id)) end - - def self.preload_relation(projects_relation) - super(projects_relation).preload(:group) - .preload(project_group_links: :group, - fork_network: :root_project, - forked_project_link: :forked_from_project, - forked_from_project: [:route, :forks, namespace: :route, tags: :taggings]) - end - - def self.forks_counting_projects(projects_relation) - projects_relation + projects_relation.map(&:forked_from_project).compact - end end expose :archived?, as: :archived @@ -165,7 +153,9 @@ module API expose :lfs_enabled?, as: :lfs_enabled expose :creator_id expose :namespace, using: 'API::Entities::NamespaceBasic' - expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? } + expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? } do |project, options| + project.fork_network_member.forked_from_project + end expose :import_status expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] } @@ -182,6 +172,20 @@ module API expose :printing_merge_request_link_enabled expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics + + def self.preload_relation(projects_relation, options = {}) + relation = super(projects_relation).preload(:group) + .preload(project_group_links: :group, + fork_network: :root_project, + fork_network_member: [forked_from_project: [:route, namespace: :route, tags: :taggings]]) + + # Remove this preload once forked_project_links and forked_from_project models have been removed + relation.preload(forked_project_link: :forked_from_project) + end + + def self.forks_counting_projects(projects_relation) + projects_relation + projects_relation.map(&:fork_network_member).compact.map(&:forked_from_project).compact + end end class ProjectStatistics < Grape::Entity @@ -653,16 +657,10 @@ module API class MemberAccess < Grape::Entity expose :access_level expose :notification_level do |member, options| - notification = member_notification_setting(member) - ::NotificationSetting.levels[notification.level] if notification - end - - private - - def member_notification_setting(member) - member.user.notification_settings.select do |notification| - notification.source_id == member.source_id && notification.source_type == member.source_type - end.last + # binding.pry if member.id == 5 + if member.notification_setting + ::NotificationSetting.levels[member.notification_setting.level] + end end end @@ -705,25 +703,36 @@ module API expose :permissions do expose :project_access, using: Entities::ProjectAccess do |project, options| if options.key?(:project_members) - (options[:project_members] || []).select { |member| member.source_id == project.id }.last - elsif project.project_members.any? - # This is not the bet option to search in a CollectionProxy, but if - # we use find_by we will perform another query, even if the association - # is loaded - project.project_members.select { |project_member| project_member.user_id == options[:current_user].id }.last + (options[:project_members] || []).find { |member| member.source_id == project.id } + else + project.project_member(options[:current_user]) end end expose :group_access, using: Entities::GroupAccess do |project, options| if project.group if options.key?(:group_members) - (options[:group_members] || []).select { |member| member.source_id == project.namespace_id }.last - elsif project.group.group_members.any? - project.group.group_members.select { |group_member| group_member.user_id == options[:current_user].id }.last + (options[:group_members] || []).find { |member| member.source_id == project.namespace_id } + else + project.group.group_member(options[:current_user]) end end end end + + def self.preload_relation(projects_relation, options = {}) + relation = super(projects_relation, options) + + unless options.key?(:group_members) + relation = relation.preload(group: [group_members: [:source, user: [notification_settings: :source]]]) + end + + unless options.key?(:project_members) + relation = relation.preload(project_members: [:source, user: [notification_settings: :source]]) + end + + relation + end end class LabelBasic < Grape::Entity diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 12506203429..9e92ff1417e 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -82,20 +82,20 @@ module API projects = paginate(projects) if current_user - project_members = current_user.project_members - group_members = current_user.group_members + project_members = current_user.project_members.preload(:source, user: [notification_settings: :source]) + group_members = current_user.group_members.preload(:source, user: [notification_settings: :source]) end options = options.reverse_merge( with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails, statistics: params[:statistics], - project_members: project_members, - group_members: group_members, + project_members: nil, + group_members: nil, current_user: current_user ) options[:with] = Entities::BasicProjectDetails if params[:simple] - present options[:with].prepare_relation(projects), options + present options[:with].prepare_relation(projects, options), options end end diff --git a/lib/api/projects_relation_builder.rb b/lib/api/projects_relation_builder.rb index 57df6157225..2e042d51c05 100644 --- a/lib/api/projects_relation_builder.rb +++ b/lib/api/projects_relation_builder.rb @@ -3,13 +3,13 @@ module API extend ActiveSupport::Concern module ClassMethods - def prepare_relation(relation) - relation = preload_relation(relation) - execute_batch_counting(relation) - relation + def prepare_relation(projects_relation, options = {}) + projects_relation = preload_relation(projects_relation, options) + execute_batch_counting(projects_relation) + projects_relation end - def preload_relation(relation) + def preload_relation(projects_relation, options = {}) raise NotImplementedError, 'self.preload_relation method must be defined and return a relation' end From 966f83f9653aa774ee82be65dbdae0c6e4f297da Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Thu, 30 Nov 2017 13:13:00 +0100 Subject: [PATCH 15/74] Undoing debugging change --- lib/api/projects.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 9e92ff1417e..14a4fc6f025 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -89,8 +89,8 @@ module API options = options.reverse_merge( with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails, statistics: params[:statistics], - project_members: nil, - group_members: nil, + project_members: project_members, + group_members: group_members, current_user: current_user ) options[:with] = Entities::BasicProjectDetails if params[:simple] From fa6b0a36bd315a4777bb8b3229b89808b95a9489 Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Thu, 30 Nov 2017 14:01:56 +0100 Subject: [PATCH 16/74] Changes after rebase --- lib/api/entities.rb | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index dada353a314..e0eb2ecfb73 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -87,14 +87,25 @@ module API expose :created_at end - class BasicProjectDetails < ProjectIdentity - expose :default_branch, :tag_list + class BasicProjectDetails < Grape::Entity + include ::API::ProjectsRelationBuilder + + expose :default_branch + + # Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770 + expose :tag_list do |project| + # project.tags.order(:name).pluck(:name) is the most suitable option + # to avoid loading all the ActiveRecord objects but, if we use it here + # it override the preloaded associations and makes a query + # (fixed in https://github.com/rails/rails/pull/25976). + project.tags.map(&:name).sort + end expose :ssh_url_to_repo, :http_url_to_repo, :web_url expose :avatar_url do |project, options| project.avatar_url(only_path: false) end expose :star_count, :forks_count - expose :created_at, :last_activity_at + expose :last_activity_at def self.preload_relation(projects_relation, options = {}) projects_relation.preload(:project_feature, :route) From c7e7f4444c791eb0ad409310394954578aa232c6 Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Thu, 30 Nov 2017 16:20:13 +0100 Subject: [PATCH 17/74] Removing blank line --- lib/api/entities.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index e0eb2ecfb73..910ac4f1814 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -91,7 +91,6 @@ module API include ::API::ProjectsRelationBuilder expose :default_branch - # Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770 expose :tag_list do |project| # project.tags.order(:name).pluck(:name) is the most suitable option From c0c0926accdc3c49fc2a75a0eec2f96a8a5ad15c Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Thu, 30 Nov 2017 18:24:31 +0100 Subject: [PATCH 18/74] Removed binding.pry --- lib/api/entities.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 910ac4f1814..248e234580f 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -667,7 +667,6 @@ module API class MemberAccess < Grape::Entity expose :access_level expose :notification_level do |member, options| - # binding.pry if member.id == 5 if member.notification_setting ::NotificationSetting.levels[member.notification_setting.level] end From fe95de88551bd3c8d22591764d948205f9fbc10e Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Thu, 30 Nov 2017 19:09:25 +0100 Subject: [PATCH 19/74] Fixed BasicProjectDetail parent --- lib/api/entities.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 248e234580f..d224f468c18 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -87,7 +87,7 @@ module API expose :created_at end - class BasicProjectDetails < Grape::Entity + class BasicProjectDetails < ProjectIdentity include ::API::ProjectsRelationBuilder expose :default_branch From 3527d1ff2bc06ba38e820b300e49f817d2833379 Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Thu, 30 Nov 2017 22:17:17 +0100 Subject: [PATCH 20/74] Undoing the change to ForkNetworkMember --- app/services/projects/forks_count_service.rb | 5 ++++- lib/api/entities.rb | 18 +++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/services/projects/forks_count_service.rb b/app/services/projects/forks_count_service.rb index d67ae78b268..95ce655b157 100644 --- a/app/services/projects/forks_count_service.rb +++ b/app/services/projects/forks_count_service.rb @@ -10,7 +10,10 @@ module Projects end def self.query(project_ids) - ForkNetworkMember.where(forked_from_project: project_ids) + # We can't directly change ForkedProjectLink to ForkNetworkMember here + # Nowadays, when a call using v3 to projects/:id/fork is made, + # the relationship to ForkNetworkMember is not updated + ForkedProjectLink.where(forked_from_project: project_ids) end end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d224f468c18..7cec8da013d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -163,9 +163,7 @@ module API expose :lfs_enabled?, as: :lfs_enabled expose :creator_id expose :namespace, using: 'API::Entities::NamespaceBasic' - expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? } do |project, options| - project.fork_network_member.forked_from_project - end + expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? } expose :import_status expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] } @@ -184,17 +182,15 @@ module API expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics def self.preload_relation(projects_relation, options = {}) - relation = super(projects_relation).preload(:group) - .preload(project_group_links: :group, - fork_network: :root_project, - fork_network_member: [forked_from_project: [:route, namespace: :route, tags: :taggings]]) - - # Remove this preload once forked_project_links and forked_from_project models have been removed - relation.preload(forked_project_link: :forked_from_project) + super(projects_relation).preload(:group) + .preload(project_group_links: :group, + fork_network: :root_project, + forked_project_link: :forked_from_project, + forked_from_project: [:route, :forks, namespace: :route, tags: :taggings]) end def self.forks_counting_projects(projects_relation) - projects_relation + projects_relation.map(&:fork_network_member).compact.map(&:forked_from_project).compact + projects_relation + projects_relation.map(&:forked_from_project).compact end end From 3dc331c6131040b8c2a0e682c34e5a19c803e000 Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Fri, 1 Dec 2017 09:14:55 +0100 Subject: [PATCH 21/74] Fixed test --- spec/models/project_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 521b7bd70ba..a069ae784af 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -315,7 +315,6 @@ describe Project do it { is_expected.to delegate_method(:empty_repo?).to(:repository) } it { is_expected.to delegate_method(:members).to(:team).with_prefix(true) } - it { is_expected.to delegate_method(:count).to(:forks).with_prefix(true) } it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) } end @@ -2473,7 +2472,7 @@ describe Project do it 'returns the number of forks' do project = build(:project) - allow(project.forks).to receive(:count).and_return(1) + expect_any_instance_of(Projects::ForksCountService).to receive(:count).and_return(1) expect(project.forks_count).to eq(1) end From c5a89b35f0d43b54df8767522428a006770673ac Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Fri, 1 Dec 2017 11:34:24 +0100 Subject: [PATCH 22/74] Using map to retrieve the element ids because of some issues with mysql --- app/services/projects/batch_count_service.rb | 5 +++-- app/services/projects/batch_forks_count_service.rb | 4 +++- app/services/projects/batch_open_issues_count_service.rb | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/services/projects/batch_count_service.rb b/app/services/projects/batch_count_service.rb index a79fdb62d69..178ebc5a143 100644 --- a/app/services/projects/batch_count_service.rb +++ b/app/services/projects/batch_count_service.rb @@ -1,3 +1,6 @@ +# Service class for getting and caching the number of elements of several projects +# Warning: do not user this service with a really large set of projects +# because the service use maps to retrieve the project ids. module Projects class BatchCountService def initialize(projects) @@ -14,8 +17,6 @@ module Projects end def project_ids - return @projects if @projects.is_a?(ActiveRecord::Relation) - @projects.map(&:id) end diff --git a/app/services/projects/batch_forks_count_service.rb b/app/services/projects/batch_forks_count_service.rb index b9ed0b3cf4f..e61fe6c86b2 100644 --- a/app/services/projects/batch_forks_count_service.rb +++ b/app/services/projects/batch_forks_count_service.rb @@ -1,5 +1,7 @@ +# Service class for getting and caching the number of forks of several projects +# Warning: do not user this service with a really large set of projects +# because the service use maps to retrieve the project ids module Projects - # Service class for getting and caching the number of forks of several projects class BatchForksCountService < Projects::BatchCountService def global_count @global_count ||= begin diff --git a/app/services/projects/batch_open_issues_count_service.rb b/app/services/projects/batch_open_issues_count_service.rb index 5087684863b..3b0ade2419b 100644 --- a/app/services/projects/batch_open_issues_count_service.rb +++ b/app/services/projects/batch_open_issues_count_service.rb @@ -1,5 +1,7 @@ +# Service class for getting and caching the number of issues of several projects +# Warning: do not user this service with a really large set of projects +# because the service use maps to retrieve the project ids module Projects - # Service class for getting and caching the number of forks of several projects class BatchOpenIssuesCountService < Projects::BatchCountService def global_count @global_count ||= begin From e2809a1649a1c024b31d090c67c97cd4f9922ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 30 Nov 2017 13:42:18 +0100 Subject: [PATCH 23/74] Update Rails to 4.2.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes since 4.2.8: - [No change in ActionMailer](https://github.com/rails/rails/blob/v4.2.10/actionmailer/CHANGELOG.md) - [1 change and 1 regression fix in ActionPack](https://github.com/rails/rails/blob/v4.2.10/actionpack/CHANGELOG.md) - [No change in ActiveModel](https://github.com/rails/rails/blob/v4.2.10/activemodel/CHANGELOG.md) - [7 fixes in ActiveRecord](https://github.com/rails/rails/blob/v4.2.10/activerecord/CHANGELOG.md) - [5 fixes in ActiveSupport](https://github.com/rails/rails/blob/v4.2.10/activesupport/CHANGELOG.md) - [No change in Railties](https://github.com/rails/rails/blob/v4.2.10/railties/CHANGELOG.md) Signed-off-by: Rémy Coutable --- Gemfile | 2 +- Gemfile.lock | 77 ++++++++++++++++++++++++++-------------------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/Gemfile b/Gemfile index ad4b1e73fff..1ea500d23b3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'rails', '4.2.8' +gem 'rails', '4.2.10' gem 'rails-deprecated_sanitizer', '~> 1.0.3' # Responders respond_to and respond_with diff --git a/Gemfile.lock b/Gemfile.lock index 3f4c930c71d..4ba5ec9775b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,38 +4,38 @@ GEM RedCloth (4.3.2) abstract_type (0.0.7) ace-rails-ap (4.1.2) - actionmailer (4.2.8) - actionpack (= 4.2.8) - actionview (= 4.2.8) - activejob (= 4.2.8) + actionmailer (4.2.10) + actionpack (= 4.2.10) + actionview (= 4.2.10) + activejob (= 4.2.10) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.8) - actionview (= 4.2.8) - activesupport (= 4.2.8) + actionpack (4.2.10) + actionview (= 4.2.10) + activesupport (= 4.2.10) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.8) - activesupport (= 4.2.8) + actionview (4.2.10) + activesupport (= 4.2.10) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (4.2.8) - activesupport (= 4.2.8) + activejob (4.2.10) + activesupport (= 4.2.10) globalid (>= 0.3.0) - activemodel (4.2.8) - activesupport (= 4.2.8) + activemodel (4.2.10) + activesupport (= 4.2.10) builder (~> 3.1) - activerecord (4.2.8) - activemodel (= 4.2.8) - activesupport (= 4.2.8) + activerecord (4.2.10) + activemodel (= 4.2.10) + activesupport (= 4.2.10) arel (~> 6.0) activerecord_sane_schema_dumper (0.2) rails (>= 4, < 5) - activesupport (4.2.8) + activesupport (4.2.10) i18n (~> 0.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) @@ -300,8 +300,8 @@ GEM omniauth (~> 1.3) pyu-ruby-sasl (>= 0.0.3.3, < 0.1) rubyntlm (~> 0.5) - globalid (0.3.7) - activesupport (>= 4.1.0) + globalid (0.4.1) + activesupport (>= 4.2.0) gollum-grit_adapter (1.0.1) gitlab-grit (~> 2.7, >= 2.7.1) gollum-lib (4.2.7) @@ -400,7 +400,8 @@ GEM json (~> 1.8) multi_xml (>= 0.5.2) httpclient (2.8.2) - i18n (0.8.6) + i18n (0.9.1) + concurrent-ruby (~> 1.0) ice_nine (0.11.2) influxdb (0.2.3) cause @@ -474,8 +475,8 @@ GEM railties (>= 4, < 5.2) loofah (2.0.3) nokogiri (>= 1.5.9) - mail (2.6.6) - mime-types (>= 1.16, < 4) + mail (2.7.0) + mini_mime (>= 0.1.1) mail_room (0.9.1) memoist (0.16.0) memoizable (0.4.2) @@ -656,16 +657,16 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (4.2.8) - actionmailer (= 4.2.8) - actionpack (= 4.2.8) - actionview (= 4.2.8) - activejob (= 4.2.8) - activemodel (= 4.2.8) - activerecord (= 4.2.8) - activesupport (= 4.2.8) + rails (4.2.10) + actionmailer (= 4.2.10) + actionpack (= 4.2.10) + actionview (= 4.2.10) + activejob (= 4.2.10) + activemodel (= 4.2.10) + activerecord (= 4.2.10) + activesupport (= 4.2.10) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.8) + railties (= 4.2.10) sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) @@ -678,15 +679,15 @@ GEM rails-i18n (4.0.9) i18n (~> 0.7) railties (~> 4.0) - railties (4.2.8) - actionpack (= 4.2.8) - activesupport (= 4.2.8) + railties (4.2.10) + actionpack (= 4.2.10) + activesupport (= 4.2.10) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.2.2) rake raindrops (0.18.0) - rake (12.1.0) + rake (12.3.0) rblineprof (0.3.6) debugger-ruby_core_source (~> 1.3) rbnacl (4.0.2) @@ -873,7 +874,7 @@ GEM sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.0) + sprockets-rails (3.2.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) @@ -911,7 +912,7 @@ GEM truncato (0.7.10) htmlentities (~> 4.3.1) nokogiri (~> 1.8.0, >= 1.7.0) - tzinfo (1.2.3) + tzinfo (1.2.4) thread_safe (~> 0.1) u2f (0.2.1) uber (0.1.0) @@ -1118,7 +1119,7 @@ DEPENDENCIES rack-cors (~> 0.4.0) rack-oauth2 (~> 1.2.1) rack-proxy (~> 0.6.0) - rails (= 4.2.8) + rails (= 4.2.10) rails-deprecated_sanitizer (~> 1.0.3) rails-i18n (~> 4.0.9) rainbow (~> 2.2) From 86af197cc2cbe28f011421b5c7575ad17273bda9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 30 Nov 2017 17:57:12 +0100 Subject: [PATCH 24/74] Stop calling #strip in a commit title in Notify specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/mailers/notify_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index f942a22b6d1..e1d71a9573b 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -602,7 +602,7 @@ describe Notify do it 'has the correct subject and body' do aggregate_failures do - is_expected.to have_subject("Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})") + is_expected.to have_subject("Re: #{project.name} | #{commit.title} (#{commit.short_id})") is_expected.to have_body_text(commit.short_id) end end @@ -712,7 +712,7 @@ describe Notify do it_behaves_like 'a user cannot unsubscribe through footer link' it 'has the correct subject' do - is_expected.to have_subject "Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})" + is_expected.to have_subject "Re: #{project.name} | #{commit.title} (#{commit.short_id})" end it 'contains a link to the commit' do From 979056e964827a9d6efc979843ac567a3dd5cdfd Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Fri, 1 Dec 2017 19:21:04 +0100 Subject: [PATCH 25/74] Not forcing to redefine preload_relation --- lib/api/projects_relation_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/projects_relation_builder.rb b/lib/api/projects_relation_builder.rb index 2e042d51c05..6482fd94ab8 100644 --- a/lib/api/projects_relation_builder.rb +++ b/lib/api/projects_relation_builder.rb @@ -10,7 +10,7 @@ module API end def preload_relation(projects_relation, options = {}) - raise NotImplementedError, 'self.preload_relation method must be defined and return a relation' + projects_relation end def forks_counting_projects(projects_relation) From 3f45662e5af2f1cb477d2e7e08965f9ffd0304f7 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Dec 2017 14:01:15 -0600 Subject: [PATCH 26/74] Don't disable the Rails mailer when seeding the test environment --- lib/gitlab/seeder.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb index f9ab9bd466f..30df7e4a831 100644 --- a/lib/gitlab/seeder.rb +++ b/lib/gitlab/seeder.rb @@ -8,7 +8,8 @@ end module Gitlab class Seeder def self.quiet - mute_mailer + mute_mailer unless Rails.env.test? + SeedFu.quiet = true yield From 0f99e9a50b3faf1da47d37a0642d22e636498a80 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sun, 3 Dec 2017 13:03:45 +0900 Subject: [PATCH 27/74] Fix typo in docs about Elasticsearch --- changelogs/unreleased/40770-doc-elasticsearch.yml | 5 +++++ doc/development/changelog.md | 2 +- doc/university/glossary/README.md | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/40770-doc-elasticsearch.yml diff --git a/changelogs/unreleased/40770-doc-elasticsearch.yml b/changelogs/unreleased/40770-doc-elasticsearch.yml new file mode 100644 index 00000000000..b770fb7548e --- /dev/null +++ b/changelogs/unreleased/40770-doc-elasticsearch.yml @@ -0,0 +1,5 @@ +--- +title: Fix typo in docs about Elasticsearch +merge_request: 15699 +author: Takuya Noguchi +type: other diff --git a/doc/development/changelog.md b/doc/development/changelog.md index f869938fe11..48cffc0dd18 100644 --- a/doc/development/changelog.md +++ b/doc/development/changelog.md @@ -80,7 +80,7 @@ changes. The first example focuses on _how_ we fixed something, not on _what_ it fixes. The rewritten version clearly describes the _end benefit_ to the user (fewer 500 -errors), and _when_ (searching commits with ElasticSearch). +errors), and _when_ (searching commits with Elasticsearch). Use your best judgement and try to put yourself in the mindset of someone reading the compiled changelog. Does this entry add value? Does it offer context diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md index 076fbf6f710..fbe7353c6ca 100644 --- a/doc/university/glossary/README.md +++ b/doc/university/glossary/README.md @@ -175,7 +175,7 @@ A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of ### EC2 Instance -### ElasticSearch +### Elasticsearch Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data. From dfef98e8a2a8fb5324e186e3252ae77205457c47 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 3 Dec 2017 23:01:12 +0100 Subject: [PATCH 28/74] Fix applications spec --- spec/features/projects/clusters/applications_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb index ef05c10fec1..6104bad0125 100644 --- a/spec/features/projects/clusters/applications_spec.rb +++ b/spec/features/projects/clusters/applications_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' feature 'Clusters Applications', :js do include GoogleApi::CloudPlatformHelpers - set(:project) { create(:project) } - set(:user) { create(:user) } + let(:project) { create(:project) } + let(:user) { create(:user) } before do project.add_master(user) From dc350a1cffeb7127b2bb8fd304f1a4669e3fbb45 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 3 Dec 2017 23:09:03 +0100 Subject: [PATCH 29/74] Remove details file --- app/views/projects/clusters/_details.html.haml | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/views/projects/clusters/_details.html.haml diff --git a/app/views/projects/clusters/_details.html.haml b/app/views/projects/clusters/_details.html.haml deleted file mode 100644 index e69de29bb2d..00000000000 From 50c8bd6350ebfd8d35deb2a1b41eb36e193d1a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Mon, 4 Dec 2017 00:53:04 +0100 Subject: [PATCH 30/74] Generate user agent header for GCP Client --- lib/google_api/cloud_platform/client.rb | 4 ++++ spec/lib/google_api/cloud_platform/client_spec.rb | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb index 9242cbe840c..615cd7dc60a 100644 --- a/lib/google_api/cloud_platform/client.rb +++ b/lib/google_api/cloud_platform/client.rb @@ -82,6 +82,10 @@ module GoogleApi def token_life_time(expires_at) DateTime.strptime(expires_at, '%s').to_time.utc - Time.now.utc end + + def user_agent_header + { 'User-Agent': "GitLab/#{Gitlab::VERSION.match('(\d+\.\d+)').captures.first} (GPN:GitLab;)" } + end end end end diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb index fac23dce44d..41964034062 100644 --- a/spec/lib/google_api/cloud_platform/client_spec.rb +++ b/spec/lib/google_api/cloud_platform/client_spec.rb @@ -125,4 +125,13 @@ describe GoogleApi::CloudPlatform::Client do it { is_expected.to be_nil } end end + + describe '#user_agent_header' do + subject { client.instance_eval { user_agent_header } } + + it 'returns the correct major and minor GitLab version ' do + stub_const('Gitlab::VERSION', '10.3.0-pre') + expect(subject).to eq({ 'User-Agent': 'GitLab/10.3 (GPN:GitLab;)' }) + end + end end From 04c6d102616b48c95b09656efc720c7dfdc99d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Mon, 4 Dec 2017 01:59:29 +0100 Subject: [PATCH 31/74] Use RequestOptions in GCP Client user_agent_header --- lib/google_api/cloud_platform/client.rb | 4 +++- spec/lib/google_api/cloud_platform/client_spec.rb | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb index 615cd7dc60a..15401057903 100644 --- a/lib/google_api/cloud_platform/client.rb +++ b/lib/google_api/cloud_platform/client.rb @@ -84,7 +84,9 @@ module GoogleApi end def user_agent_header - { 'User-Agent': "GitLab/#{Gitlab::VERSION.match('(\d+\.\d+)').captures.first} (GPN:GitLab;)" } + options = Google::Apis::RequestOptions.new + options.header = { 'User-Agent': "GitLab/#{Gitlab::VERSION.match('(\d+\.\d+)').captures.first} (GPN:GitLab;)" } + options end end end diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb index 41964034062..ee8f7afc285 100644 --- a/spec/lib/google_api/cloud_platform/client_spec.rb +++ b/spec/lib/google_api/cloud_platform/client_spec.rb @@ -129,9 +129,14 @@ describe GoogleApi::CloudPlatform::Client do describe '#user_agent_header' do subject { client.instance_eval { user_agent_header } } - it 'returns the correct major and minor GitLab version ' do + it 'returns a RequestOptions object' do + expect(subject).to be_instance_of(Google::Apis::RequestOptions) + end + + it 'has the correct GitLab version in User-Agent header' do stub_const('Gitlab::VERSION', '10.3.0-pre') - expect(subject).to eq({ 'User-Agent': 'GitLab/10.3 (GPN:GitLab;)' }) + + expect(subject.header).to eq({ 'User-Agent': 'GitLab/10.3 (GPN:GitLab;)' }) end end end From 68b43f4d9c5dbc2d8264e5cadb7417e21ac0ed27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Mon, 4 Dec 2017 02:19:02 +0100 Subject: [PATCH 32/74] Test usage of custom user agent in GCP Client --- lib/google_api/cloud_platform/client.rb | 6 +++--- spec/lib/google_api/cloud_platform/client_spec.rb | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb index 15401057903..36be6b7e97a 100644 --- a/lib/google_api/cloud_platform/client.rb +++ b/lib/google_api/cloud_platform/client.rb @@ -44,7 +44,7 @@ module GoogleApi service = Google::Apis::ContainerV1::ContainerService.new service.authorization = access_token - service.get_zone_cluster(project_id, zone, cluster_id) + service.get_zone_cluster(project_id, zone, cluster_id, options: user_agent_header) end def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:) @@ -62,14 +62,14 @@ module GoogleApi } } ) - service.create_cluster(project_id, zone, request_body) + service.create_cluster(project_id, zone, request_body, options: user_agent_header) end def projects_zones_operations(project_id, zone, operation_id) service = Google::Apis::ContainerV1::ContainerService.new service.authorization = access_token - service.get_zone_operation(project_id, zone, operation_id) + service.get_zone_operation(project_id, zone, operation_id, options: user_agent_header) end def parse_operation_id(self_link) diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb index ee8f7afc285..ecb4034ec8b 100644 --- a/spec/lib/google_api/cloud_platform/client_spec.rb +++ b/spec/lib/google_api/cloud_platform/client_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe GoogleApi::CloudPlatform::Client do let(:token) { 'token' } let(:client) { described_class.new(token, nil) } + let(:user_agent_options) { client.instance_eval { user_agent_header } } describe '.session_key_for_redirect_uri' do let(:state) { 'random_string' } @@ -55,7 +56,8 @@ describe GoogleApi::CloudPlatform::Client do before do allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) - .to receive(:get_zone_cluster).and_return(gke_cluster) + .to receive(:get_zone_cluster).with(any_args, options: user_agent_options) + .and_return(gke_cluster) end it { is_expected.to eq(gke_cluster) } @@ -74,7 +76,8 @@ describe GoogleApi::CloudPlatform::Client do before do allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) - .to receive(:create_cluster).and_return(operation) + .to receive(:create_cluster).with(any_args, options: user_agent_options) + .and_return(operation) end it { is_expected.to eq(operation) } @@ -102,7 +105,8 @@ describe GoogleApi::CloudPlatform::Client do before do allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) - .to receive(:get_zone_operation).and_return(operation) + .to receive(:get_zone_operation).with(any_args, options: user_agent_options) + .and_return(operation) end it { is_expected.to eq(operation) } From f8b29b06df86d048a64e5afe37e175691da10e9b Mon Sep 17 00:00:00 2001 From: Alessio Caiazza Date: Mon, 4 Dec 2017 09:12:04 +0100 Subject: [PATCH 33/74] Fix static analysis :green_heart: --- .../projects/clusters/user_controller_spec.rb | 2 +- .../projects/clusters_controller_spec.rb | 18 +++++++++--------- .../projects/clusters/applications_spec.rb | 2 +- spec/features/projects/clusters/gcp_spec.rb | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/spec/controllers/projects/clusters/user_controller_spec.rb b/spec/controllers/projects/clusters/user_controller_spec.rb index 06a9c50c5cc..22005e0dc66 100644 --- a/spec/controllers/projects/clusters/user_controller_spec.rb +++ b/spec/controllers/projects/clusters/user_controller_spec.rb @@ -58,7 +58,7 @@ describe Projects::Clusters::UserController do project.add_master(user) sign_in(user) end - + context 'when creates a cluster' do it 'creates a new cluster' do expect(ClusterProvisionWorker).to receive(:perform_async) diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 0612e0b022f..b79f0c3151f 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -18,11 +18,11 @@ describe Projects::ClustersController do context 'when project has a cluster' do let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } - it { expect(go).to redirect_to(namespace_project_cluster_path(project.namespace, project, project.cluster)) } + it { expect(go).to redirect_to(project_cluster_path(project, project.cluster)) } end context 'when project does not have a cluster' do - it { expect(go).to redirect_to(new_namespace_project_cluster_path(project.namespace, project)) } + it { expect(go).to redirect_to(new_project_cluster_path(project)) } end end @@ -146,7 +146,7 @@ describe Projects::ClustersController do go cluster.reload - expect(response).to redirect_to(namespace_project_cluster_path(project.namespace, project, project.cluster)) + expect(response).to redirect_to(project_cluster_path(project, project.cluster)) expect(flash[:notice]).to eq('Cluster was successfully updated.') expect(cluster.enabled).to be_falsey end @@ -197,7 +197,7 @@ describe Projects::ClustersController do go cluster.reload - expect(response).to redirect_to(namespace_project_cluster_path(project.namespace, project, project.cluster)) + expect(response).to redirect_to(project_cluster_path(project, project.cluster)) expect(flash[:notice]).to eq('Cluster was successfully updated.') expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') @@ -237,8 +237,8 @@ describe Projects::ClustersController do def go put :update, params.merge(namespace_id: project.namespace, - project_id: project, - id: cluster) + project_id: project, + id: cluster) end end @@ -261,7 +261,7 @@ describe Projects::ClustersController do .and change { Clusters::Platforms::Kubernetes.count }.by(-1) .and change { Clusters::Providers::Gcp.count }.by(-1) - expect(response).to redirect_to(namespace_project_clusters_path(project.namespace, project)) + expect(response).to redirect_to(project_clusters_path(project)) expect(flash[:notice]).to eq('Cluster integration was successfully removed.') end end @@ -274,7 +274,7 @@ describe Projects::ClustersController do .to change { Clusters::Cluster.count }.by(-1) .and change { Clusters::Providers::Gcp.count }.by(-1) - expect(response).to redirect_to(namespace_project_clusters_path(project.namespace, project)) + expect(response).to redirect_to(project_clusters_path(project)) expect(flash[:notice]).to eq('Cluster integration was successfully removed.') end end @@ -290,7 +290,7 @@ describe Projects::ClustersController do .and change { Clusters::Platforms::Kubernetes.count }.by(-1) .and change { Clusters::Providers::Gcp.count }.by(0) - expect(response).to redirect_to(namespace_project_clusters_path(project.namespace, project)) + expect(response).to redirect_to(project_clusters_path(project)) expect(flash[:notice]).to eq('Cluster integration was successfully removed.') end end diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb index 6104bad0125..b34cd061ec6 100644 --- a/spec/features/projects/clusters/applications_spec.rb +++ b/spec/features/projects/clusters/applications_spec.rb @@ -18,7 +18,7 @@ feature 'Clusters Applications', :js do context 'when cluster is being created' do let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project])} - + scenario 'user is unable to install applications' do page.within('.js-cluster-application-row-helm') do expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true') diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index df568ebaed2..8a0da669147 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -89,18 +89,18 @@ feature 'Gcp Cluster', :js do page.find(:css, '.js-toggle-cluster').click click_button 'Save' end - + it 'user sees the successful message' do expect(page).to have_content('Cluster was successfully updated.') end end - + context 'when user changes cluster parameters' do before do fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace' click_button 'Save changes' end - + it 'user sees the successful message' do expect(page).to have_content('Cluster was successfully updated.') expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace') @@ -113,7 +113,7 @@ feature 'Gcp Cluster', :js do click_link 'Remove integration' end end - + it 'user sees creation form with the successful message' do expect(page).to have_content('Cluster integration was successfully removed.') expect(page).to have_link('Create on GKE') From 095b5eb4c16a4f9217f2431a24f11121a86725a3 Mon Sep 17 00:00:00 2001 From: Alessio Caiazza Date: Mon, 4 Dec 2017 10:30:24 +0100 Subject: [PATCH 34/74] Fix haml_lint warnings :green_heart: --- app/views/projects/clusters/_enabled.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/clusters/_enabled.html.haml b/app/views/projects/clusters/_enabled.html.haml index 6f7ae71de06..f4d261df8f5 100644 --- a/app/views/projects/clusters/_enabled.html.haml +++ b/app/views/projects/clusters/_enabled.html.haml @@ -13,4 +13,4 @@ - if can?(current_user, :update_cluster, @cluster) .form-group = field.submit _('Save'), class: 'btn btn-success' - \ No newline at end of file + From 327a9898a226a098b18e80e4950702064ecd38f1 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Thu, 30 Nov 2017 21:34:31 +0000 Subject: [PATCH 35/74] Fix the fork project functionality for projects with hashed storage Note the dependency on gitlab-shell v5.10.0 --- GITLAB_SHELL_VERSION | 2 +- app/models/project.rb | 3 +- app/workers/repository_fork_worker.rb | 12 +-- .../40711-fix-forking-hashed-projects.yml | 5 ++ lib/gitlab/shell.rb | 23 ++++-- spec/lib/gitlab/shell_spec.rb | 8 +- spec/models/project_spec.rb | 3 +- spec/workers/repository_fork_worker_spec.rb | 75 +++++++------------ 8 files changed, 60 insertions(+), 71 deletions(-) create mode 100644 changelogs/unreleased/40711-fix-forking-hashed-projects.yml diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index c5b7013b9c5..509b0b618ad 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -5.9.4 +5.10.0 diff --git a/app/models/project.rb b/app/models/project.rb index eaf4f555d3b..f9c82029912 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -562,8 +562,7 @@ class Project < ActiveRecord::Base if forked? RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path, - forked_from_project.full_path, - self.namespace.full_path) + forked_from_project.disk_path) else RepositoryImportWorker.perform_async(self.id) end diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index 264706e3e23..001c11b73e1 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -8,18 +8,18 @@ class RepositoryForkWorker sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION - def perform(project_id, forked_from_repository_storage_path, source_path, target_path) + def perform(project_id, forked_from_repository_storage_path, source_disk_path) project = Project.find(project_id) return unless start_fork(project) Gitlab::Metrics.add_event(:fork_repository, - source_path: source_path, - target_path: target_path) + source_path: source_disk_path, + target_path: project.disk_path) - result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_path, - project.repository_storage_path, target_path) - raise ForkError, "Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}" unless result + result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_disk_path, + project.repository_storage_path, project.disk_path) + raise ForkError, "Unable to fork project #{project_id} for repository #{source_disk_path} -> #{project.disk_path}" unless result project.repository.after_import raise ForkError, "Project #{project_id} had an invalid repository after fork" unless project.valid_repo? diff --git a/changelogs/unreleased/40711-fix-forking-hashed-projects.yml b/changelogs/unreleased/40711-fix-forking-hashed-projects.yml new file mode 100644 index 00000000000..116d7d4e9cf --- /dev/null +++ b/changelogs/unreleased/40711-fix-forking-hashed-projects.yml @@ -0,0 +1,5 @@ +--- +title: Fix the fork project functionality for projects with hashed storage +merge_request: 15671 +author: +type: fixed diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 996d213fdb4..a22a63665be 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -143,20 +143,27 @@ module Gitlab storage, "#{path}.git", "#{new_path}.git"]) end - # Fork repository to new namespace + # Fork repository to new path # forked_from_storage - forked-from project's storage path - # path - project path with namespace + # forked_from_disk_path - project disk path # forked_to_storage - forked-to project's storage path - # fork_namespace - namespace for forked project + # forked_to_disk_path - forked project disk path # # Ex. - # fork_repository("/path/to/forked_from/storage", "gitlab/gitlab-ci", "/path/to/forked_to/storage", "randx") + # fork_repository("/path/to/forked_from/storage", "gitlab/gitlab-ci", "/path/to/forked_to/storage", "new-namespace/gitlab-ci") # # Gitaly note: JV: not easy to migrate because this involves two Gitaly servers, not one. - def fork_repository(forked_from_storage, path, forked_to_storage, fork_namespace) - gitlab_shell_fast_execute([gitlab_shell_projects_path, 'fork-project', - forked_from_storage, "#{path}.git", forked_to_storage, - fork_namespace]) + def fork_repository(forked_from_storage, forked_from_disk_path, forked_to_storage, forked_to_disk_path) + gitlab_shell_fast_execute( + [ + gitlab_shell_projects_path, + 'fork-repository', + forked_from_storage, + "#{forked_from_disk_path}.git", + forked_to_storage, + "#{forked_to_disk_path}.git" + ] + ) end # Remove repository from file system diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index 2158b2837e2..eec6858a5de 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -200,18 +200,18 @@ describe Gitlab::Shell do describe '#fork_repository' do it 'returns true when the command succeeds' do expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'fork-project', 'current/storage', 'project/path.git', 'new/storage', 'new-namespace'], + .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'], nil, popen_vars).and_return([nil, 0]) - expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'new-namespace')).to be true + expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be true end it 'return false when the command fails' do expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'fork-project', 'current/storage', 'project/path.git', 'new/storage', 'new-namespace'], + .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'], nil, popen_vars).and_return(["error", 1]) - expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'new-namespace')).to be false + expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be false end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 521b7bd70ba..34e160aa599 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1717,8 +1717,7 @@ describe Project do expect(RepositoryForkWorker).to receive(:perform_async).with( project.id, forked_from_project.repository_storage_path, - forked_from_project.disk_path, - project.namespace.full_path).and_return(import_jid) + forked_from_project.disk_path).and_return(import_jid) expect(project.add_import_job).to eq(import_jid) end diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index e881ec37ae5..74c85848b7e 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe RepositoryForkWorker do - let(:project) { create(:project, :repository, :import_scheduled) } - let(:fork_project) { create(:project, :repository, forked_from_project: project) } + let(:project) { create(:project, :repository) } + let(:fork_project) { create(:project, :repository, :import_scheduled, forked_from_project: project) } let(:shell) { Gitlab::Shell.new } subject { described_class.new } @@ -12,50 +12,39 @@ describe RepositoryForkWorker do end describe "#perform" do + def perform! + subject.perform(fork_project.id, '/test/path', project.disk_path) + end + + def expect_fork_repository + expect(shell).to receive(:fork_repository).with( + '/test/path', + project.disk_path, + fork_project.repository_storage_path, + fork_project.disk_path + ) + end + describe 'when a worker was reset without cleanup' do let(:jid) { '12345678' } - let(:started_project) { create(:project, :repository, :import_started) } it 'creates a new repository from a fork' do allow(subject).to receive(:jid).and_return(jid) - expect(shell).to receive(:fork_repository).with( - '/test/path', - project.full_path, - project.repository_storage_path, - fork_project.namespace.full_path - ).and_return(true) + expect_fork_repository.and_return(true) - subject.perform( - project.id, - '/test/path', - project.full_path, - fork_project.namespace.full_path) + perform! end end it "creates a new repository from a fork" do - expect(shell).to receive(:fork_repository).with( - '/test/path', - project.full_path, - project.repository_storage_path, - fork_project.namespace.full_path - ).and_return(true) + expect_fork_repository.and_return(true) - subject.perform( - project.id, - '/test/path', - project.full_path, - fork_project.namespace.full_path) + perform! end it 'flushes various caches' do - expect(shell).to receive(:fork_repository).with( - '/test/path', - project.full_path, - project.repository_storage_path, - fork_project.namespace.full_path - ).and_return(true) + expect_fork_repository.and_return(true) expect_any_instance_of(Repository).to receive(:expire_emptiness_caches) .and_call_original @@ -63,32 +52,22 @@ describe RepositoryForkWorker do expect_any_instance_of(Repository).to receive(:expire_exists_cache) .and_call_original - subject.perform(project.id, '/test/path', project.full_path, - fork_project.namespace.full_path) + perform! end it "handles bad fork" do - source_path = project.full_path - target_path = fork_project.namespace.full_path - error_message = "Unable to fork project #{project.id} for repository #{source_path} -> #{target_path}" + error_message = "Unable to fork project #{fork_project.id} for repository #{project.full_path} -> #{fork_project.full_path}" - expect(shell).to receive(:fork_repository).and_return(false) + expect_fork_repository.and_return(false) - expect do - subject.perform(project.id, '/test/path', source_path, target_path) - end.to raise_error(RepositoryForkWorker::ForkError, error_message) + expect { perform! }.to raise_error(RepositoryForkWorker::ForkError, error_message) end it 'handles unexpected error' do - source_path = project.full_path - target_path = fork_project.namespace.full_path + expect_fork_repository.and_raise(RuntimeError) - allow_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_raise(RuntimeError) - - expect do - subject.perform(project.id, '/test/path', source_path, target_path) - end.to raise_error(RepositoryForkWorker::ForkError) - expect(project.reload.import_status).to eq('failed') + expect { perform! }.to raise_error(RepositoryForkWorker::ForkError) + expect(fork_project.reload.import_status).to eq('failed') end end end From 7c2b7296d4e623ba4eaa19cf03405ee7f2ae1ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Mon, 4 Dec 2017 09:49:53 +0000 Subject: [PATCH 36/74] Added default order to UserFinder --- app/finders/users_finder.rb | 2 +- app/models/user.rb | 6 +++++- .../fj-40407-missing-order-paginate.yml | 5 +++++ lib/api/helpers/pagination.rb | 10 +++++++++ lib/api/users.rb | 2 ++ spec/lib/api/helpers/pagination_spec.rb | 21 +++++++++++++++++++ 6 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/fj-40407-missing-order-paginate.yml diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb index 1a7e97004fb..edde8022ec9 100644 --- a/app/finders/users_finder.rb +++ b/app/finders/users_finder.rb @@ -25,7 +25,7 @@ class UsersFinder end def execute - users = User.all + users = User.all.order_id_desc users = by_username(users) users = by_search(users) users = by_blocked(users) diff --git a/app/models/user.rb b/app/models/user.rb index 14941fd7f98..5e7fe01c825 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -487,7 +487,11 @@ class User < ActiveRecord::Base end def two_factor_u2f_enabled? - u2f_registrations.exists? + if u2f_registrations.loaded? + u2f_registrations.any? + else + u2f_registrations.exists? + end end def namespace_uniq diff --git a/changelogs/unreleased/fj-40407-missing-order-paginate.yml b/changelogs/unreleased/fj-40407-missing-order-paginate.yml new file mode 100644 index 00000000000..27471dc2c52 --- /dev/null +++ b/changelogs/unreleased/fj-40407-missing-order-paginate.yml @@ -0,0 +1,5 @@ +--- +title: Added default order to UsersFinder +merge_request: 15679 +author: +type: fixed diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb index 95108292aac..bb70370ba77 100644 --- a/lib/api/helpers/pagination.rb +++ b/lib/api/helpers/pagination.rb @@ -2,6 +2,8 @@ module API module Helpers module Pagination def paginate(relation) + relation = add_default_order(relation) + relation.page(params[:page]).per(params[:per_page]).tap do |data| add_pagination_headers(data) end @@ -45,6 +47,14 @@ module API # Ensure there is in total at least 1 page [paginated_data.total_pages, 1].max end + + def add_default_order(relation) + if relation.is_a?(ActiveRecord::Relation) && relation.order_values.empty? + relation = relation.order(:id) + end + + relation + end end end end diff --git a/lib/api/users.rb b/lib/api/users.rb index 0cd89b1bcf8..e5de31ad51b 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -76,6 +76,8 @@ module API forbidden!("Not authorized to access /api/v4/users") unless authorized entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic + users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin + present paginate(users), with: entity end diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb index 59deca7757b..a547988d631 100644 --- a/spec/lib/api/helpers/pagination_spec.rb +++ b/spec/lib/api/helpers/pagination_spec.rb @@ -92,6 +92,27 @@ describe API::Helpers::Pagination do subject.paginate(resource) end end + + context 'if order' do + it 'is not present it adds default order(:id) if no order is present' do + resource.order_values = [] + + paginated_relation = subject.paginate(resource) + + expect(resource.order_values).to be_empty + expect(paginated_relation.order_values).to be_present + expect(paginated_relation.order_values.first).to be_ascending + expect(paginated_relation.order_values.first.expr.name).to eq :id + end + + it 'is present it does not add anything' do + paginated_relation = subject.paginate(resource.order(created_at: :desc)) + + expect(paginated_relation.order_values).to be_present + expect(paginated_relation.order_values.first).to be_descending + expect(paginated_relation.order_values.first.expr.name).to eq :created_at + end + end end context 'when resource empty' do From 7f2b6b11bd7aaeab73f6f1b5431e9d8f3f034cb6 Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Mon, 4 Dec 2017 11:20:20 +0100 Subject: [PATCH 37/74] Moving query to base count service --- app/services/projects/count_service.rb | 4 ++++ app/services/projects/forks_count_service.rb | 4 ---- app/services/projects/open_issues_count_service.rb | 4 ---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/services/projects/count_service.rb b/app/services/projects/count_service.rb index 7e575b2d6f3..42ebb38f676 100644 --- a/app/services/projects/count_service.rb +++ b/app/services/projects/count_service.rb @@ -11,6 +11,10 @@ module Projects @project = project end + def relation_for_count + self.class.query(@project.id) + end + def cache_key_name raise( NotImplementedError, diff --git a/app/services/projects/forks_count_service.rb b/app/services/projects/forks_count_service.rb index 95ce655b157..dc6eb19affd 100644 --- a/app/services/projects/forks_count_service.rb +++ b/app/services/projects/forks_count_service.rb @@ -1,10 +1,6 @@ module Projects # Service class for getting and caching the number of forks of a project. class ForksCountService < Projects::CountService - def relation_for_count - self.class.query(@project.id) - end - def cache_key_name 'forks_count' end diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb index ab1c477936a..a975a06a05c 100644 --- a/app/services/projects/open_issues_count_service.rb +++ b/app/services/projects/open_issues_count_service.rb @@ -2,10 +2,6 @@ module Projects # Service class for counting and caching the number of open issues of a # project. class OpenIssuesCountService < Projects::CountService - def relation_for_count - self.class.query(@project.id) - end - def cache_key_name 'open_issues_count' end From 6cf76d652d0ae0b77b1250a81aa24e3ce164ccbc Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 4 Dec 2017 11:00:52 +0000 Subject: [PATCH 38/74] Add underline hover state to all links --- app/assets/stylesheets/framework/common.scss | 1 - app/assets/stylesheets/framework/gfm.scss | 1 - app/assets/stylesheets/framework/wells.scss | 5 +++++ app/assets/stylesheets/pages/issuable.scss | 13 ++++++++++++- app/assets/stylesheets/pages/notes.scss | 9 ++++++++- app/assets/stylesheets/pages/profile.scss | 2 +- app/assets/stylesheets/pages/projects.scss | 1 + app/assets/stylesheets/pages/wiki.scss | 6 +++++- app/helpers/commits_helper.rb | 4 ++-- app/views/projects/_readme.html.haml | 2 +- .../projects/artifacts/_tree_directory.html.haml | 4 ++-- app/views/projects/artifacts/_tree_file.html.haml | 8 ++++---- app/views/projects/branches/_branch.html.haml | 3 +-- app/views/projects/commits/_commit.html.haml | 2 +- app/views/projects/empty.html.haml | 6 +++--- .../protected_branches/shared/_index.html.haml | 2 +- .../projects/protected_tags/shared/_index.html.haml | 2 +- app/views/projects/tree/_blob_item.html.haml | 4 ++-- app/views/projects/tree/_tree_item.html.haml | 4 ++-- app/views/projects/wikis/_sidebar.html.haml | 5 ++--- app/views/shared/_label.html.haml | 4 ++-- app/views/shared/notes/_note.html.haml | 3 +-- app/views/users/show.html.haml | 4 ++-- doc/development/doc_styleguide.md | 6 ------ 24 files changed, 59 insertions(+), 42 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index cb1aad90a9c..a42fab50db5 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -39,7 +39,6 @@ color: $brand-info; } -.underlined-link { text-decoration: underline; } .hint { font-style: italic; color: $hint-color; } .light { color: $common-gray; } diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss index 34a35734acc..5621505996d 100644 --- a/app/assets/stylesheets/framework/gfm.scss +++ b/app/assets/stylesheets/framework/gfm.scss @@ -14,6 +14,5 @@ &:hover { background-color: $user-mention-bg-hover; - text-decoration: none; } } diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 68824ff8418..735fc4babd7 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -20,6 +20,11 @@ .ref-name { font-size: 12px; + + &:hover { + text-decoration: underline; + color: $gl-text-color; + } } } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 63c51747f92..b33825a506e 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -110,6 +110,10 @@ padding: 6px 10px; border-radius: $label-border-radius; } + + &:hover .color-label { + text-decoration: underline; + } } &.has-labels { @@ -174,6 +178,14 @@ color: $gray-darkest; } } + + &.assignee { + .author_link:hover { + .author { + text-decoration: underline; + } + } + } } .block-first { @@ -468,7 +480,6 @@ a:not(.btn-retry) { &:hover { color: $md-link-color; - text-decoration: none; .avatar { border-color: rgba($avatar-border, .2); diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 4fe182c9fce..a6009ab328e 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -208,7 +208,6 @@ ul.notes { a { color: $gl-link-color; - text-decoration: none; } p { @@ -395,6 +394,10 @@ ul.notes { &:focus, &:hover { text-decoration: none; + + .note-header-author-name { + text-decoration: underline; + } } } @@ -461,6 +464,10 @@ ul.notes { .system-note-message { white-space: normal; } + + a:hover { + text-decoration: underline; + } } /** diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 28dc71dc641..ac745019319 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -73,7 +73,7 @@ .profile-link-holder { display: inline; - a { + a:not(.text-link) { text-decoration: none; } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 2dc0c288a6d..4c1e6d46242 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -811,6 +811,7 @@ a.deploy-project-label { &:hover, &:focus { color: $gl-text-color; + text-decoration: underline; } } } diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index e150f96f3fa..d8fec583121 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -124,7 +124,11 @@ &:hover, &.active { - color: $black; + text-decoration: none; + + span { + text-decoration: underline; + } } } diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index f9a666fa1e6..f68e2cd3afa 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -63,7 +63,7 @@ module CommitsHelper # Returns a link formatted as a commit branch link def commit_branch_link(url, text) link_to(url, class: 'label label-gray ref-name branch-link') do - icon('code-fork') + " #{text}" + icon('code-fork', class: 'append-right-5') + "#{text}" end end @@ -77,7 +77,7 @@ module CommitsHelper # Returns a link formatted as a commit tag link def commit_tag_link(url, text) link_to(url, class: 'label label-gray ref-name') do - icon('tag') + " #{text}" + icon('tag', class: 'append-right-5') + "#{text}" end end diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml index 44aa9eb3826..32901d30b96 100644 --- a/app/views/projects/_readme.html.haml +++ b/app/views/projects/_readme.html.haml @@ -19,5 +19,5 @@ distributed with computer software, forming part of its documentation. %p We recommend you to - = link_to "add a README", add_special_file_path(@project, file_name: 'README.md'), class: 'underlined-link' + = link_to "add a README", add_special_file_path(@project, file_name: 'README.md') file to the repository and GitLab will render it here instead of this message. diff --git a/app/views/projects/artifacts/_tree_directory.html.haml b/app/views/projects/artifacts/_tree_directory.html.haml index 03be6f15313..1a9ce8d0508 100644 --- a/app/views/projects/artifacts/_tree_directory.html.haml +++ b/app/views/projects/artifacts/_tree_directory.html.haml @@ -3,6 +3,6 @@ %tr.tree-item{ 'data-link' => path_to_directory } %td.tree-item-file-name = tree_icon('folder', '755', directory.name) - = link_to path_to_directory do - %span.str-truncated= directory.name + = link_to path_to_directory, class: 'str-truncated' do + %span= directory.name %td diff --git a/app/views/projects/artifacts/_tree_file.html.haml b/app/views/projects/artifacts/_tree_file.html.haml index a97ddb3c377..cfb91568061 100644 --- a/app/views/projects/artifacts/_tree_file.html.haml +++ b/app/views/projects/artifacts/_tree_file.html.haml @@ -6,12 +6,12 @@ %td.tree-item-file-name = tree_icon('file', blob.mode, blob.name) - if external_link - = link_to path_to_file, class: 'tree-item-file-external-link js-artifact-tree-tooltip', + = link_to path_to_file, class: 'tree-item-file-external-link js-artifact-tree-tooltip str-truncated', target: '_blank', rel: 'noopener noreferrer', title: _('Opens in a new window') do - %span.str-truncated>= blob.name + %span>= blob.name = icon('external-link', class: 'js-artifact-tree-external-icon') - else - = link_to path_to_file do - %span.str-truncated= blob.name + = link_to path_to_file, class: 'str-truncated' do + %span= blob.name %td = number_to_human_size(blob.size, precision: 2) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 6e02ae6c9cc..573050e597d 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -8,8 +8,7 @@ %li{ class: "js-branch-#{branch.name}" } %div = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do - = icon('code-fork') - = branch.name + = icon('code-fork', class: 'append-right-5') + "#{branch.name}"   - if branch.name == @repository.root_ref %span.label.label-primary default diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index a66177f20e9..1b91a94a9f8 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -41,6 +41,6 @@ - if commit.status(ref) = render_commit_status(commit, ref: ref) - = link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent" + = link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent btn-link" = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard")) = link_to_browse_code(project, commit) diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index af564b93dc3..58e89a481a9 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -14,12 +14,12 @@ %p Otherwise you can start with adding a = succeed ',' do - = link_to "README", add_special_file_path(@project, file_name: 'README.md'), class: 'underlined-link' + = link_to "README", add_special_file_path(@project, file_name: 'README.md') a = succeed ',' do - = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link' + = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE') or a - = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link' + = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore') to this project. %p You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected. diff --git a/app/views/projects/protected_branches/shared/_index.html.haml b/app/views/projects/protected_branches/shared/_index.html.haml index ba7d98228c3..e662b877fbb 100644 --- a/app/views/projects/protected_branches/shared/_index.html.haml +++ b/app/views/projects/protected_branches/shared/_index.html.haml @@ -16,7 +16,7 @@ %li prevent pushes from everybody except Masters %li prevent anyone from force pushing to the branch %li prevent anyone from deleting the branch - %p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}. + %p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches")} and #{link_to "project permissions", help_page_path("user/permissions")}. - if can? current_user, :admin_project, @project = content_for :create_protected_branch diff --git a/app/views/projects/protected_tags/shared/_index.html.haml b/app/views/projects/protected_tags/shared/_index.html.haml index e764a37bbd7..24baf1cfc89 100644 --- a/app/views/projects/protected_tags/shared/_index.html.haml +++ b/app/views/projects/protected_tags/shared/_index.html.haml @@ -16,7 +16,7 @@ %li Prevent anyone from updating the tag %li Prevent anyone from deleting the tag - %p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags"), class: "underlined-link"}. + %p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags")}. - if can? current_user, :admin_project, @project = yield :create_protected_tag diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml index fd8175e1e01..c51af901699 100644 --- a/app/views/projects/tree/_blob_item.html.haml +++ b/app/views/projects/tree/_blob_item.html.haml @@ -2,8 +2,8 @@ %td.tree-item-file-name = tree_icon(type, blob_item.mode, blob_item.name) - file_name = blob_item.name - = link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), title: file_name do - %span.str-truncated= file_name + = link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do + %span= file_name %td.hidden-xs.tree-commit %td.tree-time-ago.cgray.text-right = render 'projects/tree/spinner' diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml index 56197382a70..af3816fc9f4 100644 --- a/app/views/projects/tree/_tree_item.html.haml +++ b/app/views/projects/tree/_tree_item.html.haml @@ -2,8 +2,8 @@ %td.tree-item-file-name = tree_icon(type, tree_item.mode, tree_item.name) - path = flatten_tree(@path, tree_item) - = link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path do - %span.str-truncated= path + = link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), class: 'str-truncated', title: path do + %span= path %td.hidden-xs.tree-commit %td.tree-time-ago.text-right = render 'projects/tree/spinner' diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml index 5b781294d68..2c7551c6f8c 100644 --- a/app/views/projects/wikis/_sidebar.html.haml +++ b/app/views/projects/wikis/_sidebar.html.haml @@ -6,9 +6,8 @@ - git_access_url = project_wikis_git_access_path(@project) = link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '' do - = succeed ' ' do - = icon('cloud-download') - = _("Clone repository") + = icon('cloud-download', class: 'append-right-5') + %span= _("Clone repository") .blocks-container .block.block-first diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 23a418ad640..3fcc33044e9 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -38,9 +38,9 @@ = link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: {confirm: 'Remove this label? Are you sure?'} .pull-right.hidden-xs.hidden-sm.hidden-md - = link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do + = link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action btn-link') do view merge requests - = link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do + = link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action btn-link') do view open issues - if current_user diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index b6085fd3af0..c978d9e4821 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -31,8 +31,7 @@ .note-header .note-header-info %a{ href: user_path(note.author) } - %span.note-header-author-name - = sanitize(note.author.name) + %span.note-header-author-name= sanitize(note.author.name) %span.note-headline-light = note.author.to_reference %span.note-headline-light diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index cc59f8660fd..4f4e81c705f 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -51,7 +51,7 @@ .cover-desc - unless @user.public_email.blank? .profile-link-holder.middle-dot-divider - = link_to @user.public_email, "mailto:#{@user.public_email}" + = link_to @user.public_email, "mailto:#{@user.public_email}", class: 'text-link' - unless @user.skype.blank? .profile-link-holder.middle-dot-divider = link_to "skype:#{@user.skype}", title: "Skype" do @@ -66,7 +66,7 @@ = icon('twitter-square') - unless @user.website_url.blank? .profile-link-holder.middle-dot-divider - = link_to @user.short_website_url, @user.full_website_url + = link_to @user.short_website_url, @user.full_website_url, class: 'text-link' - unless @user.location.blank? .profile-link-holder.middle-dot-divider = icon('map-marker') diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md index aaa7032cadb..db13e0e6249 100644 --- a/doc/development/doc_styleguide.md +++ b/doc/development/doc_styleguide.md @@ -170,12 +170,6 @@ You can combine one or more of the following: = link_to 'Help page', help_page_path('user/permissions'), class: 'btn btn-info' ``` -1. **Underlining a link.** - - ```haml - = link_to 'Help page', help_page_path('user/permissions'), class: 'underlined-link' - ``` - 1. **Using links inline of some text.** ```haml From 77c62927a567aa91421439697324587dfbb7007d Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 4 Dec 2017 12:13:06 +0100 Subject: [PATCH 39/74] Add notice to gollum initializer --- config/initializers/gollum.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/initializers/gollum.rb b/config/initializers/gollum.rb index 2fd47a3f4d3..f1066f83dd9 100644 --- a/config/initializers/gollum.rb +++ b/config/initializers/gollum.rb @@ -1,3 +1,7 @@ +# WARNING changes in this file must be manually propagated to gitaly-ruby. +# +# https://gitlab.com/gitlab-org/gitaly/blob/master/ruby/lib/gitlab/gollum.rb + module Gollum GIT_ADAPTER = "rugged".freeze end From e7c64c9da34909c7f67b7da48771d317ef20d159 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Fri, 1 Dec 2017 15:47:28 +0000 Subject: [PATCH 40/74] Fix watch level for mentions in description For a user with the mention notification level set, the type of their corresponding NotificationRecipient must be :mention for them to receive an email. We set this correctly on notes, but we weren't adding it on new issues or MRs - perhaps because these users are also participants. But the type of the NotificationRecipient in that case would be :participant, not mention, so we have to add the mentioned users manually when creating an issue or MR. When editing an issue or MR, and there are newly-mentioned users to email, we still use the :new_issue and :new_merge_request actions, so this works for that case as well. --- .../notification_recipient_service.rb | 13 ++++++++- ...ail-notifications-not-sent-as-expected.yml | 6 ++++ spec/services/notification_service_spec.rb | 28 +++++++++++++++---- 3 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/38862-email-notifications-not-sent-as-expected.yml diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb index c9f07c140f7..3eb8cfcca9b 100644 --- a/app/services/notification_recipient_service.rb +++ b/app/services/notification_recipient_service.rb @@ -98,6 +98,12 @@ module NotificationRecipientService self << [target.participants(user), :participating] end + def add_mentions(user, target:) + return unless target.respond_to?(:mentioned_users) + + self << [target.mentioned_users(user), :mention] + end + # Get project/group users with CUSTOM notification level def add_custom_notifications user_ids = [] @@ -227,6 +233,11 @@ module NotificationRecipientService add_subscribed_users if [:new_issue, :new_merge_request].include?(custom_action) + # These will all be participants as well, but adding with the :mention + # type ensures that users with the mention notification level will + # receive them, too. + add_mentions(current_user, target: target) + add_labels_subscribers end end @@ -263,7 +274,7 @@ module NotificationRecipientService def build! # Add all users participating in the thread (author, assignee, comment authors) add_participants(note.author) - self << [note.mentioned_users, :mention] + add_mentions(note.author, target: note) unless note.for_personal_snippet? # Merge project watchers diff --git a/changelogs/unreleased/38862-email-notifications-not-sent-as-expected.yml b/changelogs/unreleased/38862-email-notifications-not-sent-as-expected.yml new file mode 100644 index 00000000000..6b1b309ab14 --- /dev/null +++ b/changelogs/unreleased/38862-email-notifications-not-sent-as-expected.yml @@ -0,0 +1,6 @@ +--- +title: Fix sending notification emails to users with the mention level set who were + mentioned in an issue or merge request description +merge_request: +author: +type: fixed diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index db5de572b6d..43e2643f709 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -12,6 +12,8 @@ describe NotificationService, :mailer do shared_examples 'notifications for new mentions' do def send_notifications(*new_mentions) + mentionable.description = new_mentions.map(&:to_reference).join(' ') + notification.send(notification_method, mentionable, new_mentions, @u_disabled) end @@ -20,13 +22,13 @@ describe NotificationService, :mailer do should_not_email_anyone end - it 'emails new mentions with a watch level higher than participant' do - send_notifications(@u_watcher, @u_participant_mentioned, @u_custom_global) - should_only_email(@u_watcher, @u_participant_mentioned, @u_custom_global) + it 'emails new mentions with a watch level higher than mention' do + send_notifications(@u_watcher, @u_participant_mentioned, @u_custom_global, @u_mentioned) + should_only_email(@u_watcher, @u_participant_mentioned, @u_custom_global, @u_mentioned) end - it 'does not email new mentions with a watch level equal to or less than participant' do - send_notifications(@u_participating, @u_mentioned) + it 'does not email new mentions with a watch level equal to or less than mention' do + send_notifications(@u_disabled) should_not_email_anyone end end @@ -509,6 +511,14 @@ describe NotificationService, :mailer do should_not_email(issue.assignees.first) end + it "emails any mentioned users with the mention level" do + issue.description = @u_mentioned.to_reference + + notification.new_issue(issue, @u_disabled) + + should_email(@u_mentioned) + end + it "emails the author if they've opted into notifications about their activity" do issue.author.notified_of_own_activity = true @@ -900,6 +910,14 @@ describe NotificationService, :mailer do should_not_email(@u_lazy_participant) end + it "emails any mentioned users with the mention level" do + merge_request.description = @u_mentioned.to_reference + + notification.new_merge_request(merge_request, @u_disabled) + + should_email(@u_mentioned) + end + it "emails the author if they've opted into notifications about their activity" do merge_request.author.notified_of_own_activity = true From 264171f72d4ef3e5dfafaf32d3d267ab279469e1 Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Mon, 4 Dec 2017 12:33:19 +0100 Subject: [PATCH 41/74] Fixed bug --- app/services/projects/count_service.rb | 7 +++++++ spec/services/projects/count_service_spec.rb | 12 ++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/services/projects/count_service.rb b/app/services/projects/count_service.rb index 42ebb38f676..933829b557b 100644 --- a/app/services/projects/count_service.rb +++ b/app/services/projects/count_service.rb @@ -25,5 +25,12 @@ module Projects def cache_key ['projects', 'count_service', VERSION, @project.id, cache_key_name] end + + def self.query(project_ids) + raise( + NotImplementedError, + '"query" must be implemented and return an ActiveRecord::Relation' + ) + end end end diff --git a/spec/services/projects/count_service_spec.rb b/spec/services/projects/count_service_spec.rb index cc496501bad..183f6128c7b 100644 --- a/spec/services/projects/count_service_spec.rb +++ b/spec/services/projects/count_service_spec.rb @@ -4,9 +4,17 @@ describe Projects::CountService do let(:project) { build(:project, id: 1) } let(:service) { described_class.new(project) } - describe '#relation_for_count' do + describe '.query' do it 'raises NotImplementedError' do - expect { service.relation_for_count }.to raise_error(NotImplementedError) + expect { described_class.query(project.id) }.to raise_error(NotImplementedError) + end + end + + describe '#relation_for_count' do + it 'calls the class method query with the project id' do + expect(described_class).to receive(:query).with(project.id) + + service.relation_for_count end end From fce74238f4e07591e5ffb0f798fc5a7284a1e4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 4 Dec 2017 10:57:11 +0100 Subject: [PATCH 42/74] Remove seed file from the development env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This backport a change made in the CE upstream MR, see https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3593 Signed-off-by: Rémy Coutable --- db/fixtures/test/01_admin.rb | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 db/fixtures/test/01_admin.rb diff --git a/db/fixtures/test/01_admin.rb b/db/fixtures/test/01_admin.rb deleted file mode 100644 index 6f241f6fa4a..00000000000 --- a/db/fixtures/test/01_admin.rb +++ /dev/null @@ -1,15 +0,0 @@ -require './spec/support/sidekiq' - -Gitlab::Seeder.quiet do - User.seed do |s| - s.id = 1 - s.name = 'Administrator' - s.email = 'admin@example.com' - s.notification_email = 'admin@example.com' - s.username = 'root' - s.password = '5iveL!fe' - s.admin = true - s.projects_limit = 100 - s.confirmed_at = DateTime.now - end -end From bb80d439f4bbb37032a2ee2d35ee5eab7594de7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Mon, 4 Dec 2017 13:43:45 +0100 Subject: [PATCH 43/74] Refactor GCP Client#user_agent_header to use #tap --- lib/google_api/cloud_platform/client.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb index 36be6b7e97a..b0563fb2d69 100644 --- a/lib/google_api/cloud_platform/client.rb +++ b/lib/google_api/cloud_platform/client.rb @@ -84,9 +84,9 @@ module GoogleApi end def user_agent_header - options = Google::Apis::RequestOptions.new - options.header = { 'User-Agent': "GitLab/#{Gitlab::VERSION.match('(\d+\.\d+)').captures.first} (GPN:GitLab;)" } - options + Google::Apis::RequestOptions.new.tap do |options| + options.header = { 'User-Agent': "GitLab/#{Gitlab::VERSION.match('(\d+\.\d+)').captures.first} (GPN:GitLab;)" } + end end end end From 9710015651351725b9cb84bfe658f293e413bf06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Mon, 4 Dec 2017 13:50:34 +0100 Subject: [PATCH 44/74] Add CHANGELOG entry --- changelogs/unreleased/user-agent-gke-api.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/user-agent-gke-api.yml diff --git a/changelogs/unreleased/user-agent-gke-api.yml b/changelogs/unreleased/user-agent-gke-api.yml new file mode 100644 index 00000000000..1abdbadd53b --- /dev/null +++ b/changelogs/unreleased/user-agent-gke-api.yml @@ -0,0 +1,5 @@ +--- +title: Use custom user agent header in all GCP API requests. +merge_request: 15705 +author: +type: changed From 9737f582a10ff43cac0ee47f20f200ffd744b93a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 4 Dec 2017 13:56:32 +0100 Subject: [PATCH 45/74] Reduce pipeline chain life span to minimize side effects --- lib/gitlab/ci/pipeline/chain/sequence.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/gitlab/ci/pipeline/chain/sequence.rb b/lib/gitlab/ci/pipeline/chain/sequence.rb index 015f2988327..e24630656d3 100644 --- a/lib/gitlab/ci/pipeline/chain/sequence.rb +++ b/lib/gitlab/ci/pipeline/chain/sequence.rb @@ -5,20 +5,19 @@ module Gitlab class Sequence def initialize(pipeline, command, sequence) @pipeline = pipeline + @command = command + @sequence = sequence @completed = [] - - @sequence = sequence.map do |chain| - chain.new(pipeline, command) - end end def build! - @sequence.each do |step| - step.perform! + @sequence.each do |chain| + step = chain.new(@pipeline, @command) + step.perform! break if step.break? - @completed << step + @completed.push(step) end @pipeline.tap do From afa36b71547c4971c7e44bace10f1a5525650be3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 4 Dec 2017 10:28:49 +0000 Subject: [PATCH 46/74] Merge branch 'master-i18n' into 'master' New Crowdin translations See merge request gitlab-org/gitlab-ee!3378 --- locale/de/gitlab.po | 4 +- locale/fr/gitlab.po | 54 +- locale/it/gitlab.po | 4 +- locale/pl_PL/gitlab.po | 2523 ++++++++++++++++++++++++++++++++++++++++ locale/pt_BR/gitlab.po | 10 +- locale/ru/gitlab.po | 4 +- locale/uk/gitlab.po | 110 +- locale/zh_CN/gitlab.po | 32 +- locale/zh_HK/gitlab.po | 6 +- locale/zh_TW/gitlab.po | 452 +++---- 10 files changed, 2861 insertions(+), 338 deletions(-) create mode 100644 locale/pl_PL/gitlab.po diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po index 431e11818c1..ef730d91c75 100644 --- a/locale/de/gitlab.po +++ b/locale/de/gitlab.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-02 14:42+0100\n" -"PO-Revision-Date: 2017-11-03 12:30-0400\n" +"PO-Revision-Date: 2017-11-28 11:32-0500\n" "Last-Translator: gitlab \n" "Language-Team: German\n" "Language: de_DE\n" @@ -2070,7 +2070,7 @@ msgid "Time until first merge request" msgstr "Zeit bis zum ersten Merge Request" msgid "Timeago|%s days ago" -msgstr "" +msgstr "vor %s Tagen" msgid "Timeago|%s days remaining" msgstr "%s Tage verbleibend" diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po index a4f9a34b13b..41fa86451f5 100644 --- a/locale/fr/gitlab.po +++ b/locale/fr/gitlab.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-02 14:42+0100\n" -"PO-Revision-Date: 2017-11-03 12:30-0400\n" +"PO-Revision-Date: 2017-11-21 16:43-0500\n" "Last-Translator: gitlab \n" "Language-Team: French\n" "Language: fr_FR\n" @@ -36,8 +36,8 @@ msgstr "%{commit_author_link} a validé %{commit_timeago}" msgid "%{count} participant" msgid_plural "%{count} participants" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%{count} participant•e" +msgstr[1] "%{count} participant•e•s" msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgstr "%{number_commits_behind} validations de retard sur %{default_branch}, %{number_commits_ahead} validations d'avance" @@ -60,10 +60,10 @@ msgid "(checkout the %{link} for information on how to install it)." msgstr "(Lisez %{link} pour savoir comment l'installer)." msgid "+ %{moreCount} more" -msgstr "" +msgstr "+ %{moreCount} de plus" msgid "- show less" -msgstr "" +msgstr "- en montrer moins" msgid "1 pipeline" msgid_plural "%d pipelines" @@ -125,7 +125,7 @@ msgid "AdminHealthPageLink|health page" msgstr "État des services" msgid "Advanced settings" -msgstr "" +msgstr "Paramètres avancés" msgid "All" msgstr "Tous" @@ -625,8 +625,8 @@ msgstr[1] "Validations" msgid "Commit %d file" msgid_plural "Commit %d files" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Valider %d fichier" +msgstr[1] "Valider %d fichiers" msgid "Commit Message" msgstr "Message de validation" @@ -692,10 +692,10 @@ msgid "ContainerRegistry|Size" msgstr "Taille" msgid "ContainerRegistry|Tag" -msgstr "Étiquette" +msgstr "Tag" msgid "ContainerRegistry|Tag ID" -msgstr "ID d‘étiquette" +msgstr "ID du tag" msgid "ContainerRegistry|Use different image names" msgstr "Utilisez des noms d’images différents" @@ -704,7 +704,7 @@ msgid "ContainerRegistry|With the Docker Container Registry integrated into GitL msgstr "Avec le registre de conteneur Docker intégré à GitLab, chaque projet peut avoir son propre espace pour stocker ses images Docker." msgid "Contribution guide" -msgstr "Guilde de contribution" +msgstr "Guide de contribution" msgid "Contributors" msgstr "Contributeurs" @@ -737,7 +737,7 @@ msgid "Create empty bare repository" msgstr "Créer un dépôt vide" msgid "Create file" -msgstr "" +msgstr "Créer un fichier" msgid "Create merge request" msgstr "Créer une demande de fusion" @@ -746,10 +746,10 @@ msgid "Create new branch" msgstr "Créer une nouvelle branche" msgid "Create new directory" -msgstr "" +msgstr "Créer un nouveau dossier" msgid "Create new file" -msgstr "" +msgstr "Créer un nouveau fichier" msgid "Create new..." msgstr "Créer nouveau..." @@ -922,7 +922,7 @@ msgid "Failed to remove the pipeline schedule" msgstr "Échec de la suppression du pipeline programmé" msgid "File name" -msgstr "" +msgstr "Nom du fichier" msgid "Files" msgstr "Fichiers" @@ -1364,10 +1364,10 @@ msgid "Notifications" msgstr "Notifications" msgid "Number of access attempts" -msgstr "" +msgstr "Nombre de tentatives d'accès" msgid "Number of failures before backing off" -msgstr "" +msgstr "Nombre d'échecs avant annulation" msgid "OfSearchInADropdown|Filter" msgstr "Filtre" @@ -1625,7 +1625,7 @@ msgid "ProjectSettings|This setting will be applied to all projects unless overr msgstr "Ce paramètre s’appliquera à tous les projets à moins qu’un administrateur ne le modifie." msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails." -msgstr "" +msgstr "Les utilisateurs peuvent uniquement pousser sur ce dépôt des validations qui ont été validées avec une de leurs adresses courriels vérifiées." msgid "Projects" msgstr "Projets" @@ -1664,7 +1664,7 @@ msgid "Push events" msgstr "Évènements de poussée" msgid "PushRule|Committer restriction" -msgstr "" +msgstr "Restriction du validateur" msgid "Read more" msgstr "Lire plus" @@ -1682,7 +1682,7 @@ msgid "Registry" msgstr "Registre" msgid "Related Commits" -msgstr "Validations liés" +msgstr "Validations liées" msgid "Related Deployed Jobs" msgstr "Tâches de déploiement liées" @@ -1730,7 +1730,7 @@ msgid "SSH Keys" msgstr "Clés SSH" msgid "Save" -msgstr "" +msgstr "Enregistrer" msgid "Save changes" msgstr "Enregistrer les modifications" @@ -1939,10 +1939,10 @@ msgid "Subgroups" msgstr "Sous-groupes" msgid "Subscribe" -msgstr "" +msgstr "S’abonner" msgid "Switch branch/tag" -msgstr "Changer de branche / d'étiquette" +msgstr "Changer de branche / tag" msgid "System Hooks" msgstr "Crochets système" @@ -1968,7 +1968,7 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa msgstr "La Recherche Globale Avancée de GitLab est un outils qui vous fait gagner du temps. Au lieu de créer du code similaire et perdre du temps, vous pouvez maintenant chercher dans le code d'autres équipes pour vous aider sur votre projet." msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold" -msgstr "" +msgstr "Le seuil d’interruption du disjoncteur devrait être inférieur au seuil de nombre de défaillance" msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." msgstr "L’étape de développement montre le temps entre la première validation et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion." @@ -1983,10 +1983,10 @@ msgid "The issue stage shows the time it takes from creating an issue to assigni msgstr "L'étape des tickets montre le temps nécessaire entre la création d'un ticket et son assignation à un jalon, ou son ajout à une liste d'un tableau de tickets. Commencez par créer des tickets pour voir des données pour cette étape." msgid "The number of attempts GitLab will make to access a storage." -msgstr "" +msgstr "Le nombre de tentatives que GitLab va effectuer pour accéder au stockage." msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host" -msgstr "" +msgstr "Le nombre d'échecs avant que GitLab ne commence à désactiver l'accès à la partition de stockage sur l'hôte" msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}." msgstr "Nombre d’échecs avant que GitLab n’empêche tout accès au stockage. Ce nombre d’échecs peut être réinitialisé dans l’interface d’administration : %{link_to_health_page} ou en suivant le %{api_documentation_link}." @@ -2224,7 +2224,7 @@ msgid "Unstar" msgstr "Supprimer des favoris" msgid "Unsubscribe" -msgstr "" +msgstr "Se désabonner" msgid "Upgrade your plan to activate Advanced Global Search." msgstr "Mettez à jour votre abonnement pour activer la Recherche Globale Avancée." diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po index 4ac7676ca18..7a132aeb238 100644 --- a/locale/it/gitlab.po +++ b/locale/it/gitlab.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-02 14:42+0100\n" -"PO-Revision-Date: 2017-11-03 12:31-0400\n" +"PO-Revision-Date: 2017-11-20 03:59-0500\n" "Last-Translator: gitlab \n" "Language-Team: Italian\n" "Language: it_IT\n" @@ -2085,7 +2085,7 @@ msgid "Timeago|%s minutes remaining" msgstr "%s minuti rimanenti" msgid "Timeago|%s months ago" -msgstr "%s minuti fa" +msgstr "%s mesi fa" msgid "Timeago|%s months remaining" msgstr "%s mesi rimanenti" diff --git a/locale/pl_PL/gitlab.po b/locale/pl_PL/gitlab.po new file mode 100644 index 00000000000..fb4a8af7217 --- /dev/null +++ b/locale/pl_PL/gitlab.po @@ -0,0 +1,2523 @@ +msgid "" +msgstr "" +"Project-Id-Version: gitlab-ee\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-11-02 14:42+0100\n" +"PO-Revision-Date: 2017-11-20 11:16-0500\n" +"Last-Translator: gitlab \n" +"Language-Team: Polish\n" +"Language: pl_PL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: crowdin.com\n" +"X-Crowdin-Project: gitlab-ee\n" +"X-Crowdin-Language: pl\n" +"X-Crowdin-File: /master/locale/gitlab.pot\n" + +msgid "%d commit" +msgid_plural "%d commits" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "%d layer" +msgid_plural "%d layers" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "%s additional commit has been omitted to prevent performance issues." +msgid_plural "%s additional commits have been omitted to prevent performance issues." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "%{commit_author_link} committed %{commit_timeago}" +msgstr "" + +msgid "%{count} participant" +msgid_plural "%{count} participants" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + +msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgstr "" + +msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds." +msgstr "" + +msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved." +msgstr "" + +msgid "%{storage_name}: failed storage access attempt on host:" +msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "(checkout the %{link} for information on how to install it)." +msgstr "" + +msgid "+ %{moreCount} more" +msgstr "" + +msgid "- show less" +msgstr "" + +msgid "1 pipeline" +msgid_plural "%d pipelines" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "1st contribution!" +msgstr "" + +msgid "2FA enabled" +msgstr "" + +msgid "A collection of graphs regarding Continuous Integration" +msgstr "" + +msgid "About auto deploy" +msgstr "" + +msgid "Abuse Reports" +msgstr "" + +msgid "Access Tokens" +msgstr "" + +msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again." +msgstr "" + +msgid "Account" +msgstr "" + +msgid "Active" +msgstr "" + +msgid "Activity" +msgstr "" + +msgid "Add" +msgstr "" + +msgid "Add Changelog" +msgstr "" + +msgid "Add Contribution guide" +msgstr "" + +msgid "Add Group Webhooks and GitLab Enterprise Edition." +msgstr "" + +msgid "Add License" +msgstr "" + +msgid "Add an SSH key to your profile to pull or push via SSH." +msgstr "" + +msgid "Add new directory" +msgstr "" + +msgid "AdminHealthPageLink|health page" +msgstr "" + +msgid "Advanced settings" +msgstr "" + +msgid "All" +msgstr "" + +msgid "An error occurred. Please try again." +msgstr "" + +msgid "Appearance" +msgstr "" + +msgid "Applications" +msgstr "" + +msgid "Archived project! Repository is read-only" +msgstr "" + +msgid "Are you sure you want to delete this pipeline schedule?" +msgstr "" + +msgid "Are you sure you want to discard your changes?" +msgstr "" + +msgid "Are you sure you want to leave this group?" +msgstr "" + +msgid "Are you sure you want to reset registration token?" +msgstr "" + +msgid "Are you sure you want to reset the health check token?" +msgstr "" + +msgid "Are you sure?" +msgstr "" + +msgid "Artifacts" +msgstr "" + +msgid "Attach a file by drag & drop or %{upload_link}" +msgstr "" + +msgid "Authentication Log" +msgstr "" + +msgid "Author" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Auto DevOps (Beta)" +msgstr "" + +msgid "AutoDevOps|Auto DevOps documentation" +msgstr "" + +msgid "AutoDevOps|Enable in settings" +msgstr "" + +msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "AutoDevOps|Learn more in the %{link_to_documentation}" +msgstr "" + +msgid "AutoDevOps|You can activate %{link_to_settings} for this project." +msgstr "" + +msgid "Billing" +msgstr "" + +msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan." +msgstr "" + +msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available." +msgstr "" + +msgid "BillingPlans|Current plan" +msgstr "" + +msgid "BillingPlans|Customer Support" +msgstr "" + +msgid "BillingPlans|Downgrade" +msgstr "" + +msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}." +msgstr "" + +msgid "BillingPlans|Manage plan" +msgstr "" + +msgid "BillingPlans|Please contact %{customer_support_link} in that case." +msgstr "" + +msgid "BillingPlans|See all %{plan_name} features" +msgstr "" + +msgid "BillingPlans|This group uses the plan associated with its parent group." +msgstr "" + +msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}." +msgstr "" + +msgid "BillingPlans|Upgrade" +msgstr "" + +msgid "BillingPlans|You are currently on the %{plan_link} plan." +msgstr "" + +msgid "BillingPlans|frequently asked questions" +msgstr "" + +msgid "BillingPlans|monthly" +msgstr "" + +msgid "BillingPlans|paid annually at %{price_per_year}" +msgstr "" + +msgid "BillingPlans|per user" +msgstr "" + +msgid "Branch" +msgid_plural "Branches" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Branch %{branch_name} was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}" +msgstr "" + +msgid "Branch has changed" +msgstr "" + +msgid "BranchSwitcherPlaceholder|Search branches" +msgstr "" + +msgid "BranchSwitcherTitle|Switch branch" +msgstr "" + +msgid "Branches" +msgstr "" + +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + +msgid "Browse Directory" +msgstr "" + +msgid "Browse File" +msgstr "" + +msgid "Browse Files" +msgstr "" + +msgid "Browse files" +msgstr "" + +msgid "ByAuthor|by" +msgstr "" + +msgid "CI / CD" +msgstr "" + +msgid "CI configuration" +msgstr "" + +msgid "CICD|Jobs" +msgstr "" + +msgid "Cancel" +msgstr "" + +msgid "Cancel edit" +msgstr "" + +msgid "Change Weight" +msgstr "" + +msgid "ChangeTypeActionLabel|Pick into branch" +msgstr "" + +msgid "ChangeTypeActionLabel|Revert in branch" +msgstr "" + +msgid "ChangeTypeAction|Cherry-pick" +msgstr "" + +msgid "ChangeTypeAction|Revert" +msgstr "" + +msgid "Changelog" +msgstr "" + +msgid "Charts" +msgstr "" + +msgid "Chat" +msgstr "" + +msgid "Cherry-pick this commit" +msgstr "" + +msgid "Cherry-pick this merge request" +msgstr "" + +msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all." +msgstr "" + +msgid "CiStatusLabel|canceled" +msgstr "" + +msgid "CiStatusLabel|created" +msgstr "" + +msgid "CiStatusLabel|failed" +msgstr "" + +msgid "CiStatusLabel|manual action" +msgstr "" + +msgid "CiStatusLabel|passed" +msgstr "" + +msgid "CiStatusLabel|passed with warnings" +msgstr "" + +msgid "CiStatusLabel|pending" +msgstr "" + +msgid "CiStatusLabel|skipped" +msgstr "" + +msgid "CiStatusLabel|waiting for manual action" +msgstr "" + +msgid "CiStatusText|blocked" +msgstr "" + +msgid "CiStatusText|canceled" +msgstr "" + +msgid "CiStatusText|created" +msgstr "" + +msgid "CiStatusText|failed" +msgstr "" + +msgid "CiStatusText|manual" +msgstr "" + +msgid "CiStatusText|passed" +msgstr "" + +msgid "CiStatusText|pending" +msgstr "" + +msgid "CiStatusText|skipped" +msgstr "" + +msgid "CiStatus|running" +msgstr "" + +msgid "CircuitBreakerApiLink|circuitbreaker api" +msgstr "" + +msgid "Clone repository" +msgstr "" + +msgid "Close" +msgstr "" + +msgid "Cluster" +msgstr "" + +msgid "ClusterIntegration|A %{link_to_container_project} must have been created under this account" +msgstr "" + +msgid "ClusterIntegration|Cluster details" +msgstr "" + +msgid "ClusterIntegration|Cluster integration" +msgstr "" + +msgid "ClusterIntegration|Cluster integration is disabled for this project." +msgstr "" + +msgid "ClusterIntegration|Cluster integration is enabled for this project." +msgstr "" + +msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it." +msgstr "" + +msgid "ClusterIntegration|Cluster is being created on Google Container Engine..." +msgstr "" + +msgid "ClusterIntegration|Cluster name" +msgstr "" + +msgid "ClusterIntegration|Cluster was successfully created on Google Container Engine" +msgstr "" + +msgid "ClusterIntegration|Copy cluster name" +msgstr "" + +msgid "ClusterIntegration|Create cluster" +msgstr "" + +msgid "ClusterIntegration|Create new cluster on Google Container Engine" +msgstr "" + +msgid "ClusterIntegration|Enable cluster integration" +msgstr "" + +msgid "ClusterIntegration|Google Cloud Platform project ID" +msgstr "" + +msgid "ClusterIntegration|Google Container Engine" +msgstr "" + +msgid "ClusterIntegration|Google Container Engine project" +msgstr "" + +msgid "ClusterIntegration|Learn more about %{link_to_documentation}" +msgstr "" + +msgid "ClusterIntegration|Machine type" +msgstr "" + +msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters" +msgstr "" + +msgid "ClusterIntegration|Manage Cluster integration on your GitLab project" +msgstr "" + +msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}" +msgstr "" + +msgid "ClusterIntegration|Number of nodes" +msgstr "" + +msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:" +msgstr "" + +msgid "ClusterIntegration|Project namespace (optional, unique)" +msgstr "" + +msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration." +msgstr "" + +msgid "ClusterIntegration|Remove cluster integration" +msgstr "" + +msgid "ClusterIntegration|Remove integration" +msgstr "" + +msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your project." +msgstr "" + +msgid "ClusterIntegration|See and edit the details for your cluster" +msgstr "" + +msgid "ClusterIntegration|See machine types" +msgstr "" + +msgid "ClusterIntegration|See your projects" +msgstr "" + +msgid "ClusterIntegration|See zones" +msgstr "" + +msgid "ClusterIntegration|Something went wrong on our end." +msgstr "" + +msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Container Engine" +msgstr "" + +msgid "ClusterIntegration|Toggle Cluster" +msgstr "" + +msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way." +msgstr "" + +msgid "ClusterIntegration|Your account must have %{link_to_container_engine}" +msgstr "" + +msgid "ClusterIntegration|Zone" +msgstr "" + +msgid "ClusterIntegration|access to Google Container Engine" +msgstr "" + +msgid "ClusterIntegration|cluster" +msgstr "" + +msgid "ClusterIntegration|help page" +msgstr "" + +msgid "ClusterIntegration|meets the requirements" +msgstr "" + +msgid "ClusterIntegration|properly configured" +msgstr "" + +msgid "Comments" +msgstr "" + +msgid "Commit" +msgid_plural "Commits" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Commit %d file" +msgid_plural "Commit %d files" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Commit Message" +msgstr "" + +msgid "Commit duration in minutes for last 30 commits" +msgstr "" + +msgid "Commit message" +msgstr "" + +msgid "CommitBoxTitle|Commit" +msgstr "" + +msgid "CommitMessage|Add %{file_name}" +msgstr "" + +msgid "Commits" +msgstr "" + +msgid "Commits feed" +msgstr "" + +msgid "Commits|History" +msgstr "" + +msgid "Committed by" +msgstr "" + +msgid "Compare" +msgstr "" + +msgid "Container Registry" +msgstr "" + +msgid "ContainerRegistry|Created" +msgstr "" + +msgid "ContainerRegistry|First log in to GitLab’s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:" +msgstr "" + +msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:" +msgstr "" + +msgid "ContainerRegistry|How to use the Container Registry" +msgstr "" + +msgid "ContainerRegistry|Learn more about" +msgstr "" + +msgid "ContainerRegistry|No tags in Container Registry for this container image." +msgstr "" + +msgid "ContainerRegistry|Once you log in, you’re free to create and upload a container image using the common %{build} and %{push} commands" +msgstr "" + +msgid "ContainerRegistry|Remove repository" +msgstr "" + +msgid "ContainerRegistry|Remove tag" +msgstr "" + +msgid "ContainerRegistry|Size" +msgstr "" + +msgid "ContainerRegistry|Tag" +msgstr "" + +msgid "ContainerRegistry|Tag ID" +msgstr "" + +msgid "ContainerRegistry|Use different image names" +msgstr "" + +msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images." +msgstr "" + +msgid "Contribution guide" +msgstr "" + +msgid "Contributors" +msgstr "" + +msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node" +msgstr "" + +msgid "Control the maximum concurrency of repository backfill for this secondary node" +msgstr "" + +msgid "Copy SSH public key to clipboard" +msgstr "" + +msgid "Copy URL to clipboard" +msgstr "" + +msgid "Copy commit SHA to clipboard" +msgstr "" + +msgid "Create New Directory" +msgstr "" + +msgid "Create a personal access token on your account to pull or push via %{protocol}." +msgstr "" + +msgid "Create directory" +msgstr "" + +msgid "Create empty bare repository" +msgstr "" + +msgid "Create file" +msgstr "" + +msgid "Create merge request" +msgstr "" + +msgid "Create new branch" +msgstr "" + +msgid "Create new directory" +msgstr "" + +msgid "Create new file" +msgstr "" + +msgid "Create new..." +msgstr "" + +msgid "CreateNewFork|Fork" +msgstr "" + +msgid "CreateTag|Tag" +msgstr "" + +msgid "CreateTokenToCloneLink|create a personal access token" +msgstr "" + +msgid "Cron Timezone" +msgstr "" + +msgid "Cron syntax" +msgstr "" + +msgid "Custom notification events" +msgstr "" + +msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}." +msgstr "" + +msgid "Cycle Analytics" +msgstr "" + +msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project." +msgstr "" + +msgid "CycleAnalyticsStage|Code" +msgstr "" + +msgid "CycleAnalyticsStage|Issue" +msgstr "" + +msgid "CycleAnalyticsStage|Plan" +msgstr "" + +msgid "CycleAnalyticsStage|Production" +msgstr "" + +msgid "CycleAnalyticsStage|Review" +msgstr "" + +msgid "CycleAnalyticsStage|Staging" +msgstr "" + +msgid "CycleAnalyticsStage|Test" +msgstr "" + +msgid "DashboardProjects|All" +msgstr "" + +msgid "DashboardProjects|Personal" +msgstr "" + +msgid "Define a custom pattern with cron syntax" +msgstr "" + +msgid "Delete" +msgstr "" + +msgid "Deploy" +msgid_plural "Deploys" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Deploy Keys" +msgstr "" + +msgid "Description" +msgstr "" + +msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project." +msgstr "" + +msgid "Details" +msgstr "" + +msgid "Directory name" +msgstr "" + +msgid "Discard changes" +msgstr "" + +msgid "Dismiss Cycle Analytics introduction box" +msgstr "" + +msgid "Dismiss Merge Request promotion" +msgstr "" + +msgid "Don't show again" +msgstr "" + +msgid "Download" +msgstr "" + +msgid "Download tar" +msgstr "" + +msgid "Download tar.bz2" +msgstr "" + +msgid "Download tar.gz" +msgstr "" + +msgid "Download zip" +msgstr "" + +msgid "DownloadArtifacts|Download" +msgstr "" + +msgid "DownloadCommit|Email Patches" +msgstr "" + +msgid "DownloadCommit|Plain Diff" +msgstr "" + +msgid "DownloadSource|Download" +msgstr "" + +msgid "Edit" +msgstr "" + +msgid "Edit Pipeline Schedule %{id}" +msgstr "" + +msgid "Emails" +msgstr "" + +msgid "EventFilterBy|Filter by all" +msgstr "" + +msgid "EventFilterBy|Filter by comments" +msgstr "" + +msgid "EventFilterBy|Filter by issue events" +msgstr "" + +msgid "EventFilterBy|Filter by merge events" +msgstr "" + +msgid "EventFilterBy|Filter by push events" +msgstr "" + +msgid "EventFilterBy|Filter by team" +msgstr "" + +msgid "Every day (at 4:00am)" +msgstr "" + +msgid "Every month (on the 1st at 4:00am)" +msgstr "" + +msgid "Every week (Sundays at 4:00am)" +msgstr "" + +msgid "Explore projects" +msgstr "" + +msgid "Explore public groups" +msgstr "" + +msgid "Failed to change the owner" +msgstr "" + +msgid "Failed to remove the pipeline schedule" +msgstr "" + +msgid "File name" +msgstr "" + +msgid "Files" +msgstr "" + +msgid "Filter by commit message" +msgstr "" + +msgid "Find by path" +msgstr "" + +msgid "Find file" +msgstr "" + +msgid "FirstPushedBy|First" +msgstr "" + +msgid "FirstPushedBy|pushed by" +msgstr "" + +msgid "Fork" +msgid_plural "Forks" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "ForkedFromProjectPath|Forked from" +msgstr "" + +msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)" +msgstr "" + +msgid "Format" +msgstr "" + +msgid "From issue creation until deploy to production" +msgstr "" + +msgid "From merge request merge until deploy to production" +msgstr "" + +msgid "GPG Keys" +msgstr "" + +msgid "Geo Nodes" +msgstr "" + +msgid "Geo|File sync capacity" +msgstr "" + +msgid "Geo|Groups to replicate" +msgstr "" + +msgid "Geo|Repository sync capacity" +msgstr "" + +msgid "Geo|Select groups to replicate." +msgstr "" + +msgid "Git storage health information has been reset" +msgstr "" + +msgid "GitLab Runner section" +msgstr "" + +msgid "Go to your fork" +msgstr "" + +msgid "GoToYourFork|Fork" +msgstr "" + +msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service." +msgstr "" + +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" +msgstr "" + +msgid "GroupsEmptyState|A group is a collection of several projects." +msgstr "" + +msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder." +msgstr "" + +msgid "GroupsEmptyState|No groups found" +msgstr "" + +msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group." +msgstr "" + +msgid "GroupsTreeRole|as" +msgstr "" + +msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?" +msgstr "" + +msgid "GroupsTree|Create a project in this group." +msgstr "" + +msgid "GroupsTree|Create a subgroup in this group." +msgstr "" + +msgid "GroupsTree|Edit group" +msgstr "" + +msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner." +msgstr "" + +msgid "GroupsTree|Filter by name..." +msgstr "" + +msgid "GroupsTree|Leave this group" +msgstr "" + +msgid "GroupsTree|Loading groups" +msgstr "" + +msgid "GroupsTree|Sorry, no groups matched your search" +msgstr "" + +msgid "GroupsTree|Sorry, no groups or projects matched your search" +msgstr "" + +msgid "Health Check" +msgstr "" + +msgid "Health information can be retrieved from the following endpoints. More information is available" +msgstr "" + +msgid "HealthCheck|Access token is" +msgstr "" + +msgid "HealthCheck|Healthy" +msgstr "" + +msgid "HealthCheck|No Health Problems Detected" +msgstr "" + +msgid "HealthCheck|Unhealthy" +msgstr "" + +msgid "History" +msgstr "" + +msgid "Housekeeping successfully started" +msgstr "" + +msgid "Import repository" +msgstr "" + +msgid "Improve Issue boards with GitLab Enterprise Edition." +msgstr "" + +msgid "Improve issues management with Issue weight and GitLab Enterprise Edition." +msgstr "" + +msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition." +msgstr "" + +msgid "Install a Runner compatible with GitLab CI" +msgstr "" + +msgid "Instance" +msgid_plural "Instances" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Internal - The group and any internal projects can be viewed by any logged in user." +msgstr "" + +msgid "Internal - The project can be accessed by any logged in user." +msgstr "" + +msgid "Interval Pattern" +msgstr "" + +msgid "Introducing Cycle Analytics" +msgstr "" + +msgid "Issue board focus mode" +msgstr "" + +msgid "Issue boards with milestones" +msgstr "" + +msgid "Issue events" +msgstr "" + +msgid "IssueBoards|Board" +msgstr "" + +msgid "IssueBoards|Boards" +msgstr "" + +msgid "Issues" +msgstr "" + +msgid "LFSStatus|Disabled" +msgstr "" + +msgid "LFSStatus|Enabled" +msgstr "" + +msgid "Labels" +msgstr "" + +msgid "Last %d day" +msgid_plural "Last %d days" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Last Pipeline" +msgstr "" + +msgid "Last commit" +msgstr "" + +msgid "Last edited %{date}" +msgstr "" + +msgid "Last edited by %{name}" +msgstr "" + +msgid "Last update" +msgstr "" + +msgid "Last updated" +msgstr "" + +msgid "LastPushEvent|You pushed to" +msgstr "" + +msgid "LastPushEvent|at" +msgstr "" + +msgid "Learn more in the" +msgstr "" + +msgid "Learn more in the|pipeline schedules documentation" +msgstr "" + +msgid "Leave" +msgstr "" + +msgid "Leave group" +msgstr "" + +msgid "Leave project" +msgstr "" + +msgid "License" +msgstr "" + +msgid "Limited to showing %d event at most" +msgid_plural "Limited to showing %d events at most" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Lock" +msgstr "" + +msgid "Locked" +msgstr "" + +msgid "Locked Files" +msgstr "" + +msgid "Login" +msgstr "" + +msgid "Maximum git storage failures" +msgstr "" + +msgid "Median" +msgstr "" + +msgid "Members" +msgstr "" + +msgid "Merge Requests" +msgstr "" + +msgid "Merge events" +msgstr "" + +msgid "Merge request" +msgstr "" + +msgid "Messages" +msgstr "" + +msgid "MissingSSHKeyWarningLink|add an SSH key" +msgstr "" + +msgid "Monitoring" +msgstr "" + +msgid "More information is available|here" +msgstr "" + +msgid "Multiple issue boards" +msgstr "" + +msgid "New Cluster" +msgstr "" + +msgid "New Issue" +msgid_plural "New Issues" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "New Pipeline Schedule" +msgstr "" + +msgid "New branch" +msgstr "" + +msgid "New directory" +msgstr "" + +msgid "New file" +msgstr "" + +msgid "New group" +msgstr "" + +msgid "New issue" +msgstr "" + +msgid "New merge request" +msgstr "" + +msgid "New project" +msgstr "" + +msgid "New schedule" +msgstr "" + +msgid "New snippet" +msgstr "" + +msgid "New subgroup" +msgstr "" + +msgid "New tag" +msgstr "" + +msgid "No container images stored for this project. Add one by following the instructions above." +msgstr "" + +msgid "No repository" +msgstr "" + +msgid "No schedules" +msgstr "" + +msgid "None" +msgstr "" + +msgid "Not available" +msgstr "" + +msgid "Not enough data" +msgstr "" + +msgid "Notification events" +msgstr "" + +msgid "NotificationEvent|Close issue" +msgstr "" + +msgid "NotificationEvent|Close merge request" +msgstr "" + +msgid "NotificationEvent|Failed pipeline" +msgstr "" + +msgid "NotificationEvent|Merge merge request" +msgstr "" + +msgid "NotificationEvent|New issue" +msgstr "" + +msgid "NotificationEvent|New merge request" +msgstr "" + +msgid "NotificationEvent|New note" +msgstr "" + +msgid "NotificationEvent|Reassign issue" +msgstr "" + +msgid "NotificationEvent|Reassign merge request" +msgstr "" + +msgid "NotificationEvent|Reopen issue" +msgstr "" + +msgid "NotificationEvent|Successful pipeline" +msgstr "" + +msgid "NotificationLevel|Custom" +msgstr "" + +msgid "NotificationLevel|Disabled" +msgstr "" + +msgid "NotificationLevel|Global" +msgstr "" + +msgid "NotificationLevel|On mention" +msgstr "" + +msgid "NotificationLevel|Participate" +msgstr "" + +msgid "NotificationLevel|Watch" +msgstr "" + +msgid "Notifications" +msgstr "" + +msgid "Number of access attempts" +msgstr "" + +msgid "Number of failures before backing off" +msgstr "" + +msgid "OfSearchInADropdown|Filter" +msgstr "" + +msgid "Only project members can comment." +msgstr "" + +msgid "OpenedNDaysAgo|Opened" +msgstr "" + +msgid "Opens in a new window" +msgstr "" + +msgid "Options" +msgstr "" + +msgid "Overview" +msgstr "" + +msgid "Owner" +msgstr "" + +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "People without permission will never get a notification and won\\'t be able to comment." +msgstr "" + +msgid "Pipeline" +msgstr "" + +msgid "Pipeline Health" +msgstr "" + +msgid "Pipeline Schedule" +msgstr "" + +msgid "Pipeline Schedules" +msgstr "" + +msgid "Pipeline quota" +msgstr "" + +msgid "PipelineCharts|Failed:" +msgstr "" + +msgid "PipelineCharts|Overall statistics" +msgstr "" + +msgid "PipelineCharts|Success ratio:" +msgstr "" + +msgid "PipelineCharts|Successful:" +msgstr "" + +msgid "PipelineCharts|Total:" +msgstr "" + +msgid "PipelineSchedules|Activated" +msgstr "" + +msgid "PipelineSchedules|Active" +msgstr "" + +msgid "PipelineSchedules|All" +msgstr "" + +msgid "PipelineSchedules|Inactive" +msgstr "" + +msgid "PipelineSchedules|Input variable key" +msgstr "" + +msgid "PipelineSchedules|Input variable value" +msgstr "" + +msgid "PipelineSchedules|Next Run" +msgstr "" + +msgid "PipelineSchedules|None" +msgstr "" + +msgid "PipelineSchedules|Provide a short description for this pipeline" +msgstr "" + +msgid "PipelineSchedules|Remove variable row" +msgstr "" + +msgid "PipelineSchedules|Take ownership" +msgstr "" + +msgid "PipelineSchedules|Target" +msgstr "" + +msgid "PipelineSchedules|Variables" +msgstr "" + +msgid "PipelineSheduleIntervalPattern|Custom" +msgstr "" + +msgid "Pipelines" +msgstr "" + +msgid "Pipelines charts" +msgstr "" + +msgid "Pipelines for last month" +msgstr "" + +msgid "Pipelines for last week" +msgstr "" + +msgid "Pipelines for last year" +msgstr "" + +msgid "Pipeline|all" +msgstr "" + +msgid "Pipeline|success" +msgstr "" + +msgid "Pipeline|with stage" +msgstr "" + +msgid "Pipeline|with stages" +msgstr "" + +msgid "Preferences" +msgstr "" + +msgid "Private - Project access must be granted explicitly to each user." +msgstr "" + +msgid "Private - The group and its projects can only be viewed by members." +msgstr "" + +msgid "Profile" +msgstr "" + +msgid "Profiles|Account scheduled for removal." +msgstr "" + +msgid "Profiles|Delete Account" +msgstr "" + +msgid "Profiles|Delete account" +msgstr "" + +msgid "Profiles|Delete your account?" +msgstr "" + +msgid "Profiles|Deleting an account has the following effects:" +msgstr "" + +msgid "Profiles|Invalid password" +msgstr "" + +msgid "Profiles|Invalid username" +msgstr "" + +msgid "Profiles|Type your %{confirmationValue} to confirm:" +msgstr "" + +msgid "Profiles|You don't have access to delete this user." +msgstr "" + +msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account." +msgstr "" + +msgid "Profiles|Your account is currently an owner in these groups:" +msgstr "" + +msgid "Profiles|your account" +msgstr "" + +msgid "Project '%{project_name}' is in the process of being deleted." +msgstr "" + +msgid "Project '%{project_name}' queued for deletion." +msgstr "" + +msgid "Project '%{project_name}' was successfully created." +msgstr "" + +msgid "Project '%{project_name}' was successfully updated." +msgstr "" + +msgid "Project access must be granted explicitly to each user." +msgstr "" + +msgid "Project details" +msgstr "" + +msgid "Project export could not be deleted." +msgstr "" + +msgid "Project export has been deleted." +msgstr "" + +msgid "Project export link has expired. Please generate a new export from your project settings." +msgstr "" + +msgid "Project export started. A download link will be sent by email." +msgstr "" + +msgid "ProjectActivityRSS|Subscribe" +msgstr "" + +msgid "ProjectFeature|Disabled" +msgstr "" + +msgid "ProjectFeature|Everyone with access" +msgstr "" + +msgid "ProjectFeature|Only team members" +msgstr "" + +msgid "ProjectFileTree|Name" +msgstr "" + +msgid "ProjectLastActivity|Never" +msgstr "" + +msgid "ProjectLifecycle|Stage" +msgstr "" + +msgid "ProjectNetworkGraph|Graph" +msgstr "" + +msgid "ProjectSettings|Contact an admin to change this setting." +msgstr "" + +msgid "ProjectSettings|Only signed commits can be pushed to this repository." +msgstr "" + +msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin." +msgstr "" + +msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project." +msgstr "" + +msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin." +msgstr "" + +msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails." +msgstr "" + +msgid "Projects" +msgstr "" + +msgid "ProjectsDropdown|Frequently visited" +msgstr "" + +msgid "ProjectsDropdown|Loading projects" +msgstr "" + +msgid "ProjectsDropdown|Projects you visit often will appear here" +msgstr "" + +msgid "ProjectsDropdown|Search your projects" +msgstr "" + +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "" + +msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "" + +msgid "Public - The group and any public projects can be viewed without any authentication." +msgstr "" + +msgid "Public - The project can be accessed without any authentication." +msgstr "" + +msgid "Push Rules" +msgstr "" + +msgid "Push events" +msgstr "" + +msgid "PushRule|Committer restriction" +msgstr "" + +msgid "Read more" +msgstr "" + +msgid "Readme" +msgstr "" + +msgid "RefSwitcher|Branches" +msgstr "" + +msgid "RefSwitcher|Tags" +msgstr "" + +msgid "Registry" +msgstr "" + +msgid "Related Commits" +msgstr "" + +msgid "Related Deployed Jobs" +msgstr "" + +msgid "Related Issues" +msgstr "" + +msgid "Related Jobs" +msgstr "" + +msgid "Related Merge Requests" +msgstr "" + +msgid "Related Merged Requests" +msgstr "" + +msgid "Remind later" +msgstr "" + +msgid "Remove project" +msgstr "" + +msgid "Repository" +msgstr "" + +msgid "Request Access" +msgstr "" + +msgid "Reset git storage health information" +msgstr "" + +msgid "Reset health check access token" +msgstr "" + +msgid "Reset runners registration token" +msgstr "" + +msgid "Revert this commit" +msgstr "" + +msgid "Revert this merge request" +msgstr "" + +msgid "SSH Keys" +msgstr "" + +msgid "Save" +msgstr "" + +msgid "Save changes" +msgstr "" + +msgid "Save pipeline schedule" +msgstr "" + +msgid "Schedule a new pipeline" +msgstr "" + +msgid "Schedules" +msgstr "" + +msgid "Scheduling Pipelines" +msgstr "" + +msgid "Search branches and tags" +msgstr "" + +msgid "Seconds before reseting failure information" +msgstr "" + +msgid "Seconds to wait after a storage failure" +msgstr "" + +msgid "Seconds to wait for a storage access attempt" +msgstr "" + +msgid "Select Archive Format" +msgstr "" + +msgid "Select a timezone" +msgstr "" + +msgid "Select target branch" +msgstr "" + +msgid "Service Templates" +msgstr "" + +msgid "Set a password on your account to pull or push via %{protocol}." +msgstr "" + +msgid "Set up CI" +msgstr "" + +msgid "Set up Koding" +msgstr "" + +msgid "Set up auto deploy" +msgstr "" + +msgid "SetPasswordToCloneLink|set a password" +msgstr "" + +msgid "Settings" +msgstr "" + +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + +msgid "Showing %d event" +msgid_plural "Showing %d events" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Snippets" +msgstr "" + +msgid "Something went wrong on our end." +msgstr "" + +msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName(this.issuableType)}" +msgstr "" + +msgid "Something went wrong while fetching the projects." +msgstr "" + +msgid "Something went wrong while fetching the registry list." +msgstr "" + +msgid "Sort by" +msgstr "" + +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + +msgid "Source code" +msgstr "" + +msgid "Spam Logs" +msgstr "" + +msgid "Specify the following URL during the Runner setup:" +msgstr "" + +msgid "StarProject|Star" +msgstr "" + +msgid "Starred projects" +msgstr "" + +msgid "Start a %{new_merge_request} with these changes" +msgstr "" + +msgid "Start the Runner!" +msgstr "" + +msgid "Subgroups" +msgstr "" + +msgid "Subscribe" +msgstr "" + +msgid "Switch branch/tag" +msgstr "" + +msgid "System Hooks" +msgstr "" + +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Tags" +msgstr "" + +msgid "Target Branch" +msgstr "" + +msgid "Team" +msgstr "" + +msgid "Thanks! Don't show me this again" +msgstr "" + +msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project." +msgstr "" + +msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold" +msgstr "" + +msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." +msgstr "" + +msgid "The collection of events added to the data gathered for that stage." +msgstr "" + +msgid "The fork relationship has been removed." +msgstr "" + +msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage." +msgstr "" + +msgid "The number of attempts GitLab will make to access a storage." +msgstr "" + +msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host" +msgstr "" + +msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}." +msgstr "" + +msgid "The phase of the development lifecycle." +msgstr "" + +msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user." +msgstr "" + +msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit." +msgstr "" + +msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle." +msgstr "" + +msgid "The project can be accessed by any logged in user." +msgstr "" + +msgid "The project can be accessed without any authentication." +msgstr "" + +msgid "The repository for this project does not exist." +msgstr "" + +msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request." +msgstr "" + +msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time." +msgstr "" + +msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running." +msgstr "" + +msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset." +msgstr "" + +msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised." +msgstr "" + +msgid "The time taken by each data entry gathered by that stage." +msgstr "" + +msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6." +msgstr "" + +msgid "There are problems accessing Git storage: " +msgstr "" + +msgid "This branch has changed since you started editing. Would you like to create a new branch?" +msgstr "" + +msgid "This is a confidential issue." +msgstr "" + +msgid "This is the author's first Merge Request to this project." +msgstr "" + +msgid "This issue is confidential and locked." +msgstr "" + +msgid "This issue is locked." +msgstr "" + +msgid "This means you can not push code until you create an empty repository or import existing one." +msgstr "" + +msgid "This merge request is locked." +msgstr "" + +msgid "Time before an issue gets scheduled" +msgstr "" + +msgid "Time before an issue starts implementation" +msgstr "" + +msgid "Time between merge request creation and merge/close" +msgstr "" + +msgid "Time until first merge request" +msgstr "" + +msgid "Timeago|%s days ago" +msgstr "" + +msgid "Timeago|%s days remaining" +msgstr "" + +msgid "Timeago|%s hours remaining" +msgstr "" + +msgid "Timeago|%s minutes ago" +msgstr "" + +msgid "Timeago|%s minutes remaining" +msgstr "" + +msgid "Timeago|%s months ago" +msgstr "" + +msgid "Timeago|%s months remaining" +msgstr "" + +msgid "Timeago|%s seconds remaining" +msgstr "" + +msgid "Timeago|%s weeks ago" +msgstr "" + +msgid "Timeago|%s weeks remaining" +msgstr "" + +msgid "Timeago|%s years ago" +msgstr "" + +msgid "Timeago|%s years remaining" +msgstr "" + +msgid "Timeago|1 day remaining" +msgstr "" + +msgid "Timeago|1 hour remaining" +msgstr "" + +msgid "Timeago|1 minute remaining" +msgstr "" + +msgid "Timeago|1 month remaining" +msgstr "" + +msgid "Timeago|1 week remaining" +msgstr "" + +msgid "Timeago|1 year remaining" +msgstr "" + +msgid "Timeago|Past due" +msgstr "" + +msgid "Timeago|a day ago" +msgstr "" + +msgid "Timeago|a month ago" +msgstr "" + +msgid "Timeago|a week ago" +msgstr "" + +msgid "Timeago|a year ago" +msgstr "" + +msgid "Timeago|about %s hours ago" +msgstr "" + +msgid "Timeago|about a minute ago" +msgstr "" + +msgid "Timeago|about an hour ago" +msgstr "" + +msgid "Timeago|in %s days" +msgstr "" + +msgid "Timeago|in %s hours" +msgstr "" + +msgid "Timeago|in %s minutes" +msgstr "" + +msgid "Timeago|in %s months" +msgstr "" + +msgid "Timeago|in %s seconds" +msgstr "" + +msgid "Timeago|in %s weeks" +msgstr "" + +msgid "Timeago|in %s years" +msgstr "" + +msgid "Timeago|in 1 day" +msgstr "" + +msgid "Timeago|in 1 hour" +msgstr "" + +msgid "Timeago|in 1 minute" +msgstr "" + +msgid "Timeago|in 1 month" +msgstr "" + +msgid "Timeago|in 1 week" +msgstr "" + +msgid "Timeago|in 1 year" +msgstr "" + +msgid "Timeago|in a while" +msgstr "" + +msgid "Timeago|less than a minute ago" +msgstr "" + +msgid "Time|hr" +msgid_plural "Time|hrs" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Time|min" +msgid_plural "Time|mins" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "Time|s" +msgstr "" + +msgid "Total Time" +msgstr "" + +msgid "Total test time for all commits/merges" +msgstr "" + +msgid "Track activity with Contribution Analytics." +msgstr "" + +msgid "Unlock" +msgstr "" + +msgid "Unlocked" +msgstr "" + +msgid "Unstar" +msgstr "" + +msgid "Unsubscribe" +msgstr "" + +msgid "Upgrade your plan to activate Advanced Global Search." +msgstr "" + +msgid "Upgrade your plan to activate Contribution Analytics." +msgstr "" + +msgid "Upgrade your plan to activate Group Webhooks." +msgstr "" + +msgid "Upgrade your plan to activate Issue weight." +msgstr "" + +msgid "Upgrade your plan to improve Issue boards." +msgstr "" + +msgid "Upload New File" +msgstr "" + +msgid "Upload file" +msgstr "" + +msgid "UploadLink|click to upload" +msgstr "" + +msgid "Use the following registration token during setup:" +msgstr "" + +msgid "Use your global notification setting" +msgstr "" + +msgid "View file @ " +msgstr "" + +msgid "View open merge request" +msgstr "" + +msgid "View replaced file @ " +msgstr "" + +msgid "VisibilityLevel|Internal" +msgstr "" + +msgid "VisibilityLevel|Private" +msgstr "" + +msgid "VisibilityLevel|Public" +msgstr "" + +msgid "VisibilityLevel|Unknown" +msgstr "" + +msgid "Want to see the data? Please ask an administrator for access." +msgstr "" + +msgid "We don't have enough data to show this stage." +msgstr "" + +msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group." +msgstr "" + +msgid "Weight" +msgstr "" + +msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable" +msgstr "" + +msgid "Wiki" +msgstr "" + +msgid "WikiClone|Clone your wiki" +msgstr "" + +msgid "WikiClone|Git Access" +msgstr "" + +msgid "WikiClone|Install Gollum" +msgstr "" + +msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:" +msgstr "" + +msgid "WikiClone|Start Gollum and edit locally" +msgstr "" + +msgid "WikiEmptyPageError|You are not allowed to create wiki pages" +msgstr "" + +msgid "WikiHistoricalPage|This is an old version of this page." +msgstr "" + +msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}." +msgstr "" + +msgid "WikiHistoricalPage|history" +msgstr "" + +msgid "WikiHistoricalPage|most recent version" +msgstr "" + +msgid "WikiMarkdownDocs|More examples are in the %{docs_link}" +msgstr "" + +msgid "WikiMarkdownDocs|documentation" +msgstr "" + +msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}" +msgstr "" + +msgid "WikiNewPagePlaceholder|how-to-setup" +msgstr "" + +msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories." +msgstr "" + +msgid "WikiNewPageTitle|New Wiki Page" +msgstr "" + +msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?" +msgstr "" + +msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs." +msgstr "" + +msgid "WikiPageConflictMessage|the page" +msgstr "" + +msgid "WikiPageCreate|Create %{page_title}" +msgstr "" + +msgid "WikiPageEdit|Update %{page_title}" +msgstr "" + +msgid "WikiPage|Page slug" +msgstr "" + +msgid "WikiPage|Write your content or drag files here..." +msgstr "" + +msgid "Wiki|Create Page" +msgstr "" + +msgid "Wiki|Create page" +msgstr "" + +msgid "Wiki|Edit Page" +msgstr "" + +msgid "Wiki|Empty page" +msgstr "" + +msgid "Wiki|More Pages" +msgstr "" + +msgid "Wiki|New page" +msgstr "" + +msgid "Wiki|Page history" +msgstr "" + +msgid "Wiki|Page version" +msgstr "" + +msgid "Wiki|Pages" +msgstr "" + +msgid "Wiki|Wiki Pages" +msgstr "" + +msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members." +msgstr "" + +msgid "Withdraw Access Request" +msgstr "" + +msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?" +msgstr "" + +msgid "You are going to remove %{project_name_with_namespace}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?" +msgstr "" + +msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?" +msgstr "" + +msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?" +msgstr "" + +msgid "You are on a read-only GitLab instance." +msgstr "" + +msgid "You are on a read-only GitLab instance. If you want to make any changes, you must visit the %{link_to_primary_node}." +msgstr "" + +msgid "You can only add files when you are on a branch" +msgstr "" + +msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead." +msgstr "" + +msgid "You cannot write to this read-only GitLab instance." +msgstr "" + +msgid "You have reached your project limit" +msgstr "" + +msgid "You must sign in to star a project" +msgstr "" + +msgid "You need permission." +msgstr "" + +msgid "You will not get any notifications via email" +msgstr "" + +msgid "You will only receive notifications for the events you choose" +msgstr "" + +msgid "You will only receive notifications for threads you have participated in" +msgstr "" + +msgid "You will receive notifications for any activity" +msgstr "" + +msgid "You will receive notifications only for comments in which you were @mentioned" +msgstr "" + +msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account" +msgstr "" + +msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile" +msgstr "" + +msgid "Your comment will not be visible to the public." +msgstr "" + +msgid "Your groups" +msgstr "" + +msgid "Your name" +msgstr "" + +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + +msgid "day" +msgid_plural "days" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "new merge request" +msgstr "" + +msgid "notification emails" +msgstr "" + +msgid "parent" +msgid_plural "parents" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +msgid "password" +msgstr "" + +msgid "personal access token" +msgstr "" + +msgid "to help your contributors communicate effectively!" +msgstr "" + +msgid "username" +msgstr "" + diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po index 473086886ac..fa335bc819d 100644 --- a/locale/pt_BR/gitlab.po +++ b/locale/pt_BR/gitlab.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-02 14:42+0100\n" -"PO-Revision-Date: 2017-11-04 13:27-0400\n" +"PO-Revision-Date: 2017-11-18 12:51-0500\n" "Last-Translator: gitlab \n" "Language-Team: Portuguese, Brazilian\n" "Language: pt_BR\n" @@ -361,10 +361,10 @@ msgid "Browse Directory" msgstr "Navegar no Diretório" msgid "Browse File" -msgstr "Pesquisar Arquivo" +msgstr "Acessar arquivo" msgid "Browse Files" -msgstr "Pesquisar Arquivos" +msgstr "Acessar arquivos" msgid "Browse files" msgstr "Navegar pelos arquivos" @@ -1367,7 +1367,7 @@ msgid "Number of access attempts" msgstr "Número de tentativas de acesso" msgid "Number of failures before backing off" -msgstr "" +msgstr "Número de falhas antes de reverter" msgid "OfSearchInADropdown|Filter" msgstr "Filtrar" @@ -1968,7 +1968,7 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa msgstr "A pesquisa global avançado no GitLab é um serviço poderoso de pesquisa que poupa seu tempo. Ao invés de criar código duplicado e perder seu tempo, você pode agora pesquisar por código de outros times que podem ajudar no seu próprio projeto." msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold" -msgstr "" +msgstr "O limite do recuso do circuitbreaker deve ser inferior ao limite de contagem de falhas" msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." msgstr "A etapa de codificação mostra o tempo desde a entrega do primeiro commit até a criação do merge request. Os dados serão automaticamente adicionados aqui desde o momento de criação do merge request." diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po index 2f612f46799..9111f1eb3d2 100644 --- a/locale/ru/gitlab.po +++ b/locale/ru/gitlab.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-02 14:42+0100\n" -"PO-Revision-Date: 2017-11-05 14:39-0500\n" +"PO-Revision-Date: 2017-11-17 07:55-0500\n" "Last-Translator: gitlab \n" "Language-Team: Russian\n" "Language: ru_RU\n" @@ -188,7 +188,7 @@ msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly msgstr "Приложения для автоматического ревью и автоматического развёртывания требуют указания %{kubernetes} для корректной работы." msgid "AutoDevOps|Auto DevOps (Beta)" -msgstr "" +msgstr "Auto DevOps (бета)" msgid "AutoDevOps|Auto DevOps documentation" msgstr "" diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po index cf51f8ec689..73dfe949ded 100644 --- a/locale/uk/gitlab.po +++ b/locale/uk/gitlab.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-02 14:42+0100\n" -"PO-Revision-Date: 2017-11-05 08:38-0500\n" +"PO-Revision-Date: 2017-11-21 16:43-0500\n" "Last-Translator: gitlab \n" "Language-Team: Ukrainian\n" "Language: uk_UA\n" @@ -18,9 +18,9 @@ msgstr "" msgid "%d commit" msgid_plural "%d commits" -msgstr[0] "%d комміт" -msgstr[1] "%d комміта" -msgstr[2] "%d коммітів" +msgstr[0] "%d коміт" +msgstr[1] "%d коміта" +msgstr[2] "%d комітів" msgid "%d layer" msgid_plural "%d layers" @@ -30,21 +30,21 @@ msgstr[2] "%d шарів" msgid "%s additional commit has been omitted to prevent performance issues." msgid_plural "%s additional commits have been omitted to prevent performance issues." -msgstr[0] "%s доданий Комміт був виключений для запобігання проблем з продуктивністю." -msgstr[1] "%s доданих коммітів були виключені для запобігання проблем з продуктивністю." -msgstr[2] "%s доданих коммітів були виключені для запобігання проблем з продуктивністю." +msgstr[0] "%s доданий коміт був виключений для запобігання проблем із швидкодією." +msgstr[1] "%s доданих коміта були виключені для запобігання проблем із швидкодією." +msgstr[2] "%s доданих комітів були виключені для запобігання проблем із швидкодією." msgid "%{commit_author_link} committed %{commit_timeago}" -msgstr "%{commit_author_link} комміт %{commit_timeago}" +msgstr "%{commit_author_link} закомітив %{commit_timeago}" msgid "%{count} participant" msgid_plural "%{count} participants" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%{count} участник" +msgstr[1] "%{count} участника" +msgstr[2] "%{count} участників" msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" -msgstr "на %{number_commits_behind} коммітів позаду %{default_branch}, на %{number_commits_ahead} коммітів попереду" +msgstr "на %{number_commits_behind} комітів позаду %{default_branch}, на %{number_commits_ahead} комітів попереду" msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "%{number_of_failures} від %{maximum_failures} невдач. GitLab надасть доступ на наступну спробу." @@ -68,7 +68,7 @@ msgid "+ %{moreCount} more" msgstr "+ ще %{moreCount}" msgid "- show less" -msgstr "" +msgstr "- показати менше" msgid "1 pipeline" msgid_plural "%d pipelines" @@ -122,7 +122,7 @@ msgid "Add License" msgstr "Додати ліцензію" msgid "Add an SSH key to your profile to pull or push via SSH." -msgstr "Додати SSH ключа в свій профіль, щоб мати можливість завантажити чи надіслати зміни через SSH." +msgstr "Додайте SSH ключ в свій профіль, щоб мати можливість завантажити чи надіслати зміни через SSH." msgid "Add new directory" msgstr "Додати новий каталог" @@ -266,7 +266,7 @@ msgstr[1] "Гілки" msgstr[2] "Гілок" msgid "Branch %{branch_name} was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}" -msgstr "Гілка %{branch_name} створена. Для настройки автоматичного розгортання виберіть GitLab CI Yaml-шаблон і закоммітьте зміни. %{link_to_autodeploy_doc}" +msgstr "Гілка %{branch_name} створена. Для настройки автоматичного розгортання виберіть GitLab CI Yaml-шаблон і закомітьте зміни. %{link_to_autodeploy_doc}" msgid "Branch has changed" msgstr "Гілка змінилась" @@ -281,7 +281,7 @@ msgid "Branches" msgstr "Гілки" msgid "Branches|Cant find HEAD commit for this branch" -msgstr "Не можу знайти HEAD-комміт для цієї гілки" +msgstr "Не можу знайти HEAD-коміт для цієї гілки" msgid "Branches|Compare" msgstr "Порівняти" @@ -326,7 +326,7 @@ msgid "Branches|Only a project master or owner can delete a protected branch" msgstr "Тільки керівник або власник проекту може видалити захищену гілку" msgid "Branches|Protected branches can be managed in %{project_settings_link}" -msgstr "Управляти захищеними гілками можливо в %{project_settings_link}" +msgstr "Керувати захищеними гілками можливо в %{project_settings_link}" msgid "Branches|Sort by" msgstr "Сортувати за" @@ -359,7 +359,7 @@ msgid "Branches|merged" msgstr "злита" msgid "Branches|project settings" -msgstr "Настройки проекту" +msgstr "Налаштуваннях проекту" msgid "Branches|protected" msgstr "захищені" @@ -419,7 +419,7 @@ msgid "Chat" msgstr "Чат" msgid "Cherry-pick this commit" -msgstr "Cherry-pick в цьому комміті" +msgstr "Cherry-pick в цьому коміті" msgid "Cherry-pick this merge request" msgstr "Cherry-pick в цьому запиті на злиття" @@ -627,42 +627,42 @@ msgstr "Коментарі" msgid "Commit" msgid_plural "Commits" -msgstr[0] "Комміт" -msgstr[1] "Комміта" -msgstr[2] "Коммітів" +msgstr[0] "Коміт" +msgstr[1] "Коміта" +msgstr[2] "Комітів" msgid "Commit %d file" msgid_plural "Commit %d files" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Закомітити %d файл" +msgstr[1] "Закомітити %d файли" +msgstr[2] "Закомітити %d файлів" msgid "Commit Message" -msgstr "Повідомлення для комміту" +msgstr "Коміт-повідомелння" msgid "Commit duration in minutes for last 30 commits" -msgstr "Тривалість останніх 30 коммітів у хвилинах" +msgstr "Тривалість останніх 30 комітів у хвилинах" msgid "Commit message" -msgstr "Комміт повідомлення" +msgstr "Коміт-повідомлення" msgid "CommitBoxTitle|Commit" -msgstr "Комміт" +msgstr "Коміт" msgid "CommitMessage|Add %{file_name}" -msgstr "Додати %{file_name}" +msgstr "Додавання %{file_name}" msgid "Commits" -msgstr "Комміти" +msgstr "Коміти" msgid "Commits feed" -msgstr "Канал коммітів" +msgstr "Канал комітів" msgid "Commits|History" msgstr "Історія" msgid "Committed by" -msgstr "Комміт від" +msgstr "Коміт від" msgid "Compare" msgstr "Порівняти" @@ -938,7 +938,7 @@ msgid "Files" msgstr "Файли" msgid "Filter by commit message" -msgstr "Фільтрувати повідомлення коммітів" +msgstr "Фільтрувати за коміт-повідомленням" msgid "Find by path" msgstr "Пошук по шляху" @@ -1169,7 +1169,7 @@ msgid "Last Pipeline" msgstr "Останній Конвеєр" msgid "Last commit" -msgstr "Останній комміт" +msgstr "Останній коміт" msgid "Last edited %{date}" msgstr "Останні зміни %{date}" @@ -1379,10 +1379,10 @@ msgid "Notifications" msgstr "Сповіщення" msgid "Number of access attempts" -msgstr "" +msgstr "Кількість спроб доступу" msgid "Number of failures before backing off" -msgstr "" +msgstr "Кількість помилок до призупинення" msgid "OfSearchInADropdown|Filter" msgstr "Фільтр" @@ -1628,7 +1628,7 @@ msgid "ProjectSettings|Contact an admin to change this setting." msgstr "Зверніться до адміністратора, щоб змінити це налаштування." msgid "ProjectSettings|Only signed commits can be pushed to this repository." -msgstr "Тільки підписані комміти можуть бути надіслані в цей репозиторій." +msgstr "Тільки підписані коміти можуть бути надіслані в цей репозиторій." msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin." msgstr "Цей параметр застосовується на рівні сервера та може бути перевизначений адміністратором." @@ -1640,7 +1640,7 @@ msgid "ProjectSettings|This setting will be applied to all projects unless overr msgstr "Цей параметр буде застосовано до всіх проектів, якщо адміністратор не змінить його." msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails." -msgstr "" +msgstr "Користувачі можуть виконувати push в цей репозиторій лише тих комітів, які містять одну із підтверджених адрес електронної пошти." msgid "Projects" msgstr "Проекти" @@ -1679,7 +1679,7 @@ msgid "Push events" msgstr "Push-події" msgid "PushRule|Committer restriction" -msgstr "" +msgstr "Обмеження для коміттера" msgid "Read more" msgstr "Докладніше" @@ -1697,7 +1697,7 @@ msgid "Registry" msgstr "Реєстр" msgid "Related Commits" -msgstr "Пов'язані Комміти" +msgstr "Пов'язані Коміти" msgid "Related Deployed Jobs" msgstr "Пов’язані розгорнуті задачі (Jobs)" @@ -1730,13 +1730,13 @@ msgid "Reset git storage health information" msgstr "Скиньте інформацію про працездатність сховища git" msgid "Reset health check access token" -msgstr "Скиньте токен доступу для перевірки перевірки працездатності" +msgstr "Оновити токен доступу для перевірки працездатності" msgid "Reset runners registration token" msgstr "Скинути реєстраційний токен runner-ів" msgid "Revert this commit" -msgstr "Скасувати цей комміт" +msgstr "Скасувати цей коміт" msgid "Revert this merge request" msgstr "Скасувати цей запит на злиття" @@ -1946,7 +1946,7 @@ msgid "Starred projects" msgstr "Відмічені проекти" msgid "Start a %{new_merge_request} with these changes" -msgstr "Почати %{new_merge_request} з цих змін" +msgstr "Почати %{new_merge_request} з цими змінами" msgid "Start the Runner!" msgstr "Запустіть Runner!" @@ -1985,10 +1985,10 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa msgstr "Розширений глобальний пошук в GitLab - це потужний інструмент який заощаджує ваш час. Замість дублювання коду і витрати часу, ви можете шукати код всередині інших команд, який може допомогти у вашому проекті." msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold" -msgstr "" +msgstr "Поріг призупинення circuitbreaker має бути нижчий за поріг повного відключення" msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." -msgstr "На стадії написання коду, показує час першого комміту до створення запиту на об'єднання. Дані будуть автоматично додані після створення вашого першого запиту на об'єднання." +msgstr "На стадії написання коду, показує час першого коміту до створення запиту на об'єднання. Дані будуть автоматично додані після створення вашого першого запиту на об'єднання." msgid "The collection of events added to the data gathered for that stage." msgstr "Колекція подій додана до даних, зібраних для цього етапу." @@ -2000,10 +2000,10 @@ msgid "The issue stage shows the time it takes from creating an issue to assigni msgstr "Етап випуску показує, скільки часу потрібно від створення проблеми до присвоєння випуску, або додавання проблеми в вашу дошку проблем. Почніть створювати проблеми, щоб переглядати дані для цього етапу." msgid "The number of attempts GitLab will make to access a storage." -msgstr "" +msgstr "Кількість спроб, які зробить GitLab для отримання доступу до сховища даних." msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host" -msgstr "" +msgstr "Кількість невдач, після чого GitLab почне тимчасово блокувати доступ до сховища на хості" msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}." msgstr "Кількість збоїв після якої Gitlab повністю заблокує доступ до сховища данних. Лічильник кількості збоїв може бути скинутий в інтерфейсі адміністратора (%{link_to_health_page}), або через %{api_documentation_link}." @@ -2015,7 +2015,7 @@ msgid "The pipelines schedule runs pipelines in the future, repeatedly, for spec msgstr "Розклад конвеєрів запускає в майбутньому конвеєри, для певних гілок або тегів. Заплановані конвеєри успадковують обмеження на доступ до проекту на основі пов'язаного з ними користувача." msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit." -msgstr "На етапі планування відображається час від попереднього кроку до першого комміту. Додається автоматично, як тільки відправится перший комміт." +msgstr "На етапі планування відображається час від попереднього кроку до першого коміту. Додається автоматично, як тільки відправится перший коміт." msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle." msgstr "Стадія ПРОДакшин показує загальний час між створенням проблеми та розгортанням коду у ПРОДакшині. Дані будуть автоматично додані після завершення повної ідеї до ПРОДакшин циклу." @@ -2090,10 +2090,10 @@ msgid "Timeago|%s days ago" msgstr "%s днів тому" msgid "Timeago|%s days remaining" -msgstr "%s днів, що залишилися" +msgstr "залишилося %s днів" msgid "Timeago|%s hours remaining" -msgstr "%s годин, що залишилися" +msgstr "залишилося %s годин" msgid "Timeago|%s minutes ago" msgstr "%s хвилин тому" @@ -2105,7 +2105,7 @@ msgid "Timeago|%s months ago" msgstr "%s місяці(в) тому" msgid "Timeago|%s months remaining" -msgstr "%s місяці(в), що залишилися" +msgstr "залишилося %s місяці(в)" msgid "Timeago|%s seconds remaining" msgstr "%s секунд, що залишаються" @@ -2120,7 +2120,7 @@ msgid "Timeago|%s years ago" msgstr "%s років тому" msgid "Timeago|%s years remaining" -msgstr "%s роки, що залишилися" +msgstr "залишилося %s роки" msgid "Timeago|1 day remaining" msgstr "Залишився 1 день" @@ -2228,7 +2228,7 @@ msgid "Total Time" msgstr "Загальний час" msgid "Total test time for all commits/merges" -msgstr "Загальний час, щоб перевірити всі фіксації/злиття" +msgstr "Загальний час, щоб перевірити всі коміти/злиття" msgid "Track activity with Contribution Analytics." msgstr "Відстежувати активність за допомогою Аналітики контриб’юторів." @@ -2489,7 +2489,7 @@ msgid "Your projects" msgstr "Ваші проекти" msgid "commit" -msgstr "комміт" +msgstr "коміт" msgid "day" msgid_plural "days" diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po index 0d6f0d201c2..3a08b6c20a2 100644 --- a/locale/zh_CN/gitlab.po +++ b/locale/zh_CN/gitlab.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-02 14:42+0100\n" -"PO-Revision-Date: 2017-11-06 01:25-0500\n" +"PO-Revision-Date: 2017-11-23 02:44-0500\n" "Last-Translator: gitlab \n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" @@ -656,10 +656,10 @@ msgid "ContainerRegistry|Created" msgstr "已创建" msgid "ContainerRegistry|First log in to GitLab’s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:" -msgstr "首先使用您的 GitLab 用户名和密码登录 GitLab 的容器注册表。如果您有%{link_2fa},您需要使用%{link_token}:" +msgstr "首先使用您的 GitLab 用户名和密码登录 GitLab 的容器注册表。如果您已经%{link_2fa},则需要使用%{link_token}:" msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:" -msgstr "GitLab 最多支持3个级别的镜像名称。以下镜像示例对您的项目有效:" +msgstr "GitLab 最多支持3个级别的镜像命名。以下镜像名称示例对当前项目有效:" msgid "ContainerRegistry|How to use the Container Registry" msgstr "如何使用容器注册表" @@ -671,7 +671,7 @@ msgid "ContainerRegistry|No tags in Container Registry for this container image. msgstr "容器注册表中没有此容器镜像的标签。" msgid "ContainerRegistry|Once you log in, you’re free to create and upload a container image using the common %{build} and %{push} commands" -msgstr "登录后您可以使用通用的%{build}和%{push}命令自由创建和上传容器映像" +msgstr "登录后您可以使用通用的%{build}和%{push}命令创建和上传容器镜像" msgid "ContainerRegistry|Remove repository" msgstr "删除存储库" @@ -692,7 +692,7 @@ msgid "ContainerRegistry|Use different image names" msgstr "使用不同的镜像名称" msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images." -msgstr "将 Docker 容器注册表集成到 GitLab 中,每个项目都可以有自己的空间来存储 Docker 的图像。" +msgstr "将 Docker 容器注册表集成到 GitLab 中,每个项目都可以有各自的空间来存储 Docker 的镜像。" msgid "Contribution guide" msgstr "贡献指南" @@ -1155,7 +1155,7 @@ msgid "Last update" msgstr "最后更新" msgid "Last updated" -msgstr "最后更新" +msgstr "最近更新" msgid "LastPushEvent|You pushed to" msgstr "您推送了" @@ -1274,7 +1274,7 @@ msgid "New tag" msgstr "新建标签" msgid "No container images stored for this project. Add one by following the instructions above." -msgstr "没有为此项目存储容器镜像。请按照上述说明添加一个。" +msgstr "此项目当前未存储容器镜像。如需使用,请参照上述说明新建容器镜像。" msgid "No repository" msgstr "没有存储库" @@ -1361,7 +1361,7 @@ msgid "Only project members can comment." msgstr "只有项目成员可以发表评论。" msgid "OpenedNDaysAgo|Opened" -msgstr "开始于" +msgstr "创建于" msgid "Opens in a new window" msgstr "打开一个新窗口" @@ -1775,10 +1775,10 @@ msgid "Settings" msgstr "设置" msgid "Show parent pages" -msgstr "查看父页面" +msgstr "查看上级页面" msgid "Show parent subgroups" -msgstr "查看群组中的子群组" +msgstr "查看上级子群组" msgid "Showing %d event" msgid_plural "Showing %d events" @@ -1830,13 +1830,13 @@ msgid "SortOptions|Largest repository" msgstr "最大存储库" msgid "SortOptions|Last created" -msgstr "最新创建" +msgstr "最近创建" msgid "SortOptions|Last joined" msgstr "最新加入" msgid "SortOptions|Last updated" -msgstr "最新更新" +msgstr "最近更新" msgid "SortOptions|Least popular" msgstr "最不受欢迎" @@ -1869,7 +1869,7 @@ msgid "SortOptions|Name, descending" msgstr "名称,降序排列" msgid "SortOptions|Oldest created" -msgstr "最早的创建" +msgstr "最早创建" msgid "SortOptions|Oldest joined" msgstr "最早的加入" @@ -1878,7 +1878,7 @@ msgid "SortOptions|Oldest sign in" msgstr "最早的登录" msgid "SortOptions|Oldest updated" -msgstr "最早的提交" +msgstr "最早更新" msgid "SortOptions|Popularity" msgstr "人气" @@ -1945,7 +1945,7 @@ msgid "Team" msgstr "团队" msgid "Thanks! Don't show me this again" -msgstr "谢谢 ! 请不要再显示" +msgstr "不再显示该提示" msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project." msgstr "GitLab 中的高级全局搜索功能是非常强大的搜索服务。您可以搜索其他团队的代码以帮助您完善自己项目中的代码。从而避免创建重复的代码或浪费时间。" @@ -2465,7 +2465,7 @@ msgstr "通知邮件" msgid "parent" msgid_plural "parents" -msgstr[0] "父级" +msgstr[0] "上级" msgid "password" msgstr "密码" diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po index afb40e8b75f..30d5b0c1416 100644 --- a/locale/zh_HK/gitlab.po +++ b/locale/zh_HK/gitlab.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-02 14:42+0100\n" -"PO-Revision-Date: 2017-11-03 12:30-0400\n" +"PO-Revision-Date: 2017-11-15 02:54-0500\n" "Last-Translator: gitlab \n" "Language-Team: Chinese Traditional, Hong Kong\n" "Language: zh_HK\n" @@ -609,7 +609,7 @@ msgid "ClusterIntegration|properly configured" msgstr "" msgid "Comments" -msgstr "評論 (Comment)" +msgstr "評論" msgid "Commit" msgid_plural "Commits" @@ -876,7 +876,7 @@ msgid "EventFilterBy|Filter by all" msgstr "全部" msgid "EventFilterBy|Filter by comments" -msgstr "按評論 (comment) 過濾" +msgstr "按評論過濾" msgid "EventFilterBy|Filter by issue events" msgstr "按議題事件 (issue event) 過濾" diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po index efef86671f0..6531330074a 100644 --- a/locale/zh_TW/gitlab.po +++ b/locale/zh_TW/gitlab.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-11-02 14:42+0100\n" -"PO-Revision-Date: 2017-11-03 12:30-0400\n" +"PO-Revision-Date: 2017-11-20 03:59-0500\n" "Last-Translator: gitlab \n" "Language-Team: Chinese Traditional\n" "Language: zh_TW\n" @@ -26,20 +26,20 @@ msgstr[0] "%d 個圖層" msgid "%s additional commit has been omitted to prevent performance issues." msgid_plural "%s additional commits have been omitted to prevent performance issues." -msgstr[0] "因效能考量,不顯示 %s 個更動 (commit)。" +msgstr[0] "因效能考量,已隱藏 %s 個更動 (commit)。" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} 在 %{commit_timeago} 送交" msgid "%{count} participant" msgid_plural "%{count} participants" -msgstr[0] "" +msgstr[0] "%{count} 參與者" msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" -msgstr "%{number_commits_behind} 個落後 %{default_branch} 分支的修訂版提交,%{number_commits_ahead} 個超前的修訂版提交" +msgstr "" msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." -msgstr "已失敗 %{number_of_failures} 次,在失敗 %{maximum_failures} 次前 GitLab 會重試。" +msgstr "目前已失敗 %{number_of_failures} 次。GitLab 允許在 %{maximum_failures} 次之內可再嘗試讀取 。" msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds." msgstr "已失敗 %{number_of_failures} 次,在失敗 %{maximum_failures} 次前 GitLab 會在 %{number_of_seconds} 秒後重試。" @@ -55,10 +55,10 @@ msgid "(checkout the %{link} for information on how to install it)." msgstr "(如何安裝請參閱 %{link})" msgid "+ %{moreCount} more" -msgstr "" +msgstr "+ %{moreCount} 更多" msgid "- show less" -msgstr "" +msgstr "顯示較少" msgid "1 pipeline" msgid_plural "%d pipelines" @@ -104,28 +104,28 @@ msgid "Add Contribution guide" msgstr "新增協作指南" msgid "Add Group Webhooks and GitLab Enterprise Edition." -msgstr "加入來自 Webhooks 或者是 GitHub 企業版的團隊" +msgstr "加入來自 Webhooks 或者是 GitLab 企業版的群組" msgid "Add License" msgstr "新增授權條款" msgid "Add an SSH key to your profile to pull or push via SSH." -msgstr "請先新增 SSH 金鑰到您的個人帳號,才能使用 SSH 來上傳 (push) 或下載 (pull) 。" +msgstr "將 SSH 金鑰新增至您的個人帳號後, 即可透過 SSH 來上傳 (push) 或下載 (pull) 。" msgid "Add new directory" msgstr "新增目錄" msgid "AdminHealthPageLink|health page" -msgstr "" +msgstr "系統狀態" msgid "Advanced settings" -msgstr "" +msgstr "進階設定" msgid "All" msgstr "全部" msgid "An error occurred. Please try again." -msgstr "" +msgstr "發生錯誤,請再試一次。" msgid "Appearance" msgstr "外觀" @@ -143,7 +143,7 @@ msgid "Are you sure you want to discard your changes?" msgstr "確定要放棄修改嗎?" msgid "Are you sure you want to leave this group?" -msgstr "" +msgstr "確定要離開這個群組嗎?" msgid "Are you sure you want to reset registration token?" msgstr "確定要重置註冊憑證 (registration token) 嗎?" @@ -155,28 +155,28 @@ msgid "Are you sure?" msgstr "確定嗎?" msgid "Artifacts" -msgstr "產物" +msgstr "" msgid "Attach a file by drag & drop or %{upload_link}" msgstr "拖放檔案到此處或者 %{upload_link}" msgid "Authentication Log" -msgstr "驗證 Log" +msgstr "登入紀錄" msgid "Author" msgstr "作者" msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." -msgstr "自動審查程序 & 自動部屬程序需要一個網域和 %{kubernetes} 才能運作。" +msgstr "自動複閱應用 (review apps) 與自動部署需要網域和 %{kubernetes} 才能運作。" msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." -msgstr "自動審查程序 & 自動部屬程序需要一個網域才能正常運作。" +msgstr "自動複閱應用 (review apps) 與自動部署需要網域才能運作。" msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." -msgstr "自動審查程序 & 自動部屬程序需要 %{kubernetes} 才能正常運作。" +msgstr "自動複閱應用 (review apps) 與自動部署需要 %{kubernetes} 才能運作。" msgid "AutoDevOps|Auto DevOps (Beta)" -msgstr "DevOps 自動化 (測試版)" +msgstr "DevOps 自動化(beta)" msgid "AutoDevOps|Auto DevOps documentation" msgstr "「DevOps 自動化」 文件" @@ -185,13 +185,13 @@ msgid "AutoDevOps|Enable in settings" msgstr "在設定中啟用" msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." -msgstr "" +msgstr "將根據設定的的 CI / CD 流程自動建構、測試和部署應用程式。" msgid "AutoDevOps|Learn more in the %{link_to_documentation}" msgstr "了解更多於 %{link_to_documentation}" msgid "AutoDevOps|You can activate %{link_to_settings} for this project." -msgstr "" +msgstr "你可以為此專案啟動 %{link_to_settings}" msgid "Billing" msgstr "方案" @@ -224,16 +224,16 @@ msgid "BillingPlans|See all %{plan_name} features" msgstr "查看更多 %{plan_name} 功能" msgid "BillingPlans|This group uses the plan associated with its parent group." -msgstr "此群組使用和上層群組相同的方案。" +msgstr "此群組與上層群組使用相同的方案。" msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}." -msgstr "要管理這個群組的方案,請瀏覽 %{parent_billing_page_link} 的計費部分。" +msgstr "請至 %{parent_billing_page_link} 來管理此群組的方案。" msgid "BillingPlans|Upgrade" msgstr "升級" msgid "BillingPlans|You are currently on the %{plan_link} plan." -msgstr "你目前正在使用方案 %{plan_link}" +msgstr "目前使用 %{plan_link} 方案。" msgid "BillingPlans|frequently asked questions" msgstr "常見問題" @@ -242,20 +242,20 @@ msgid "BillingPlans|monthly" msgstr "每個月" msgid "BillingPlans|paid annually at %{price_per_year}" -msgstr "每年支付 %{price_per_year}" +msgstr "每年收取 %{price_per_year}" msgid "BillingPlans|per user" -msgstr "每個使用者" +msgstr "每位使用者" msgid "Branch" msgid_plural "Branches" msgstr[0] "分支 (branch) " msgid "Branch %{branch_name} was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}" -msgstr "已建立分支 (branch) %{branch_name} 。如要設定自動部署, 請選擇合適的 GitLab CI Yaml 模板,然後記得要送交 (commit) 您的編輯內容。%{link_to_autodeploy_doc}\n" +msgstr "已建立分支 (branch) %{branch_name} 。如需設定自動部署, 在選擇合適的 GitLab CI Yaml 模板後,請送交 (commit) 您的編輯內容。%{link_to_autodeploy_doc}" msgid "Branch has changed" -msgstr "" +msgstr "分支(branch)已變更" msgid "BranchSwitcherPlaceholder|Search branches" msgstr "搜尋分支 (branches)" @@ -267,7 +267,7 @@ msgid "Branches" msgstr "分支 (branch) " msgid "Branches|Cant find HEAD commit for this branch" -msgstr "不能找到這個分支的 HEAD 更動。" +msgstr "找不到此分支的 HEAD 更動。" msgid "Branches|Compare" msgstr "比較" @@ -285,19 +285,19 @@ msgid "Branches|Delete protected branch" msgstr "移除受保護的分支" msgid "Branches|Delete protected branch '%{branch_name}'?" -msgstr "確定要移除「%{branch_name}」這個受保護的分支嗎 ?" +msgstr "確定移除受保護的分支 %{branch_name} ?" msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" -msgstr "移除 %{branch_name} 分支將無法還原,你確定?" +msgstr "移除 %{branch_name} 分支將無法還原,確定嗎?" msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" -msgstr "移除已經合併的分支後將無法還原,你確定?" +msgstr "移除已合併的分支後將無法還原。您確定要執行?" msgid "Branches|Filter by branch name" -msgstr "按分支名稱篩選" +msgstr "以分支名稱篩選" msgid "Branches|Merged into %{default_branch}" -msgstr "合併到 %{default_branch}" +msgstr "已合併至 %{default_branch}" msgid "Branches|New branch" msgstr "新增分支" @@ -306,7 +306,7 @@ msgid "Branches|No branches to show" msgstr "找不到分支" msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." -msgstr "當你確認並按下 %{delete_protected_branch},他將無法撤銷或還原。" +msgstr "一旦你確認並按下 %{delete_protected_branch} 之後,此動作將無法撤銷或還原。" msgid "Branches|Only a project master or owner can delete a protected branch" msgstr "只有專案管理者或擁有者才能刪除被保護的分支。" @@ -327,19 +327,19 @@ msgid "Branches|This branch hasn’t been merged into %{default_branch}." msgstr "這個分支尚未合併到 %{default_branch}" msgid "Branches|To avoid data loss, consider merging this branch before deleting it." -msgstr "為避免資料丟失,請在刪除之前合併該分支。" +msgstr "為避免資料遺失,請合併該分支後再將它刪除。" msgid "Branches|To confirm, type %{branch_name_confirmation}:" -msgstr "為了確認,請輸入 %{branch_name_confirmation} :" +msgstr "請輸入 %{branch_name_confirmation} 以進行確認 :" msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." -msgstr "為了捨棄目前變更並使用上游版本覆蓋本分支,請先刪除並按下 \"立刻更新\"。" +msgstr "" msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." -msgstr "你將要永久刪除受保護的 %{branch_name} 分支。" +msgstr "你將永久刪除受保護的 %{branch_name} 分支。" msgid "Branches|diverged from upstream" -msgstr "與上游存在差異" +msgstr "與上游分歧" msgid "Branches|merged" msgstr "已合併" @@ -411,7 +411,7 @@ msgid "Cherry-pick this merge request" msgstr "挑選此合併請求 (merge request) " msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all." -msgstr "選擇哪個群組是你要複製到第二節點的。留白則複製全部。" +msgstr "選擇你想要複製到第二節點的群組。若留白則會複製全部的群組。" msgid "CiStatusLabel|canceled" msgstr "已取消" @@ -435,7 +435,7 @@ msgid "CiStatusLabel|pending" msgstr "等待中" msgid "CiStatusLabel|skipped" -msgstr "已跳過" +msgstr "已略過" msgid "CiStatusLabel|waiting for manual action" msgstr "等待手動操作" @@ -462,151 +462,151 @@ msgid "CiStatusText|pending" msgstr "等待中" msgid "CiStatusText|skipped" -msgstr "已跳過" +msgstr "已略過" msgid "CiStatus|running" msgstr "執行中" msgid "CircuitBreakerApiLink|circuitbreaker api" -msgstr "" +msgstr "斷路器 (circuitbreaker) API" msgid "Clone repository" -msgstr "克隆倉庫" +msgstr "複製(clone)檔案庫(repository)" msgid "Close" msgstr "關閉" msgid "Cluster" -msgstr "" +msgstr "叢集" msgid "ClusterIntegration|A %{link_to_container_project} must have been created under this account" -msgstr "" +msgstr "必須在此帳號下建立 %{link_to_container_project}" msgid "ClusterIntegration|Cluster details" -msgstr "" +msgstr "叢集詳情" msgid "ClusterIntegration|Cluster integration" -msgstr "" +msgstr "叢集整合" msgid "ClusterIntegration|Cluster integration is disabled for this project." -msgstr "" +msgstr "此專案已經禁用叢集整合" msgid "ClusterIntegration|Cluster integration is enabled for this project." -msgstr "" +msgstr "此專案已經啟用叢集整合" msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it." -msgstr "" +msgstr "此專案已啟用叢集整合。禁止叢集整合不會影響您的叢集,它只是暫時關閉 GitLab 的連接。" msgid "ClusterIntegration|Cluster is being created on Google Container Engine..." -msgstr "" +msgstr "在 Google 容器引擎中建立新的叢集" msgid "ClusterIntegration|Cluster name" -msgstr "" +msgstr "叢集名稱" msgid "ClusterIntegration|Cluster was successfully created on Google Container Engine" -msgstr "" +msgstr "在 Google 容器引擎上成功建立叢集" msgid "ClusterIntegration|Copy cluster name" -msgstr "" +msgstr "複製叢集名稱" msgid "ClusterIntegration|Create cluster" -msgstr "" +msgstr "建立叢集" msgid "ClusterIntegration|Create new cluster on Google Container Engine" -msgstr "" +msgstr "在 Google 容器引擎中建立新的叢集" msgid "ClusterIntegration|Enable cluster integration" -msgstr "" +msgstr "啟動叢集整合" msgid "ClusterIntegration|Google Cloud Platform project ID" -msgstr "" +msgstr "Google 雲端專案 ID" msgid "ClusterIntegration|Google Container Engine" -msgstr "" +msgstr "Google 容器引擎" msgid "ClusterIntegration|Google Container Engine project" -msgstr "" +msgstr "Google 容器引擎專案" msgid "ClusterIntegration|Learn more about %{link_to_documentation}" -msgstr "" +msgstr "學習更多有關於%{link_to_documentation}" msgid "ClusterIntegration|Machine type" -msgstr "" +msgstr "機器型別" msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters" -msgstr "" +msgstr "請確認您的帳戶中%{link_to_requirements} 是否建立叢集" msgid "ClusterIntegration|Manage Cluster integration on your GitLab project" -msgstr "" +msgstr "在你的 GitLab 專案上管理叢集整合" msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}" -msgstr "" +msgstr "請至 %{link_gke} 管理你的叢集" msgid "ClusterIntegration|Number of nodes" -msgstr "" +msgstr "所有的端點數量" msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:" -msgstr "" +msgstr "請確認你的 Google 帳號是否符合這些條件" msgid "ClusterIntegration|Project namespace (optional, unique)" -msgstr "" +msgstr "專案命名空間(選填,不可重複)" msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration." msgstr "" msgid "ClusterIntegration|Remove cluster integration" -msgstr "" +msgstr "刪除叢集整合" msgid "ClusterIntegration|Remove integration" -msgstr "" +msgstr "刪除整合" msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your project." -msgstr "" +msgstr "當刪除叢集需要加入專案的定義組態檔,會刪除叢集整合。這並不會刪除你的專案。 刪除叢集的同時,將一起刪除已加入此專案的定義組態檔,但你的專案不會因此被刪除。" msgid "ClusterIntegration|See and edit the details for your cluster" -msgstr "" +msgstr "查看與編輯你的叢集內容" msgid "ClusterIntegration|See machine types" -msgstr "" +msgstr "查看機器型別" msgid "ClusterIntegration|See your projects" -msgstr "" +msgstr "查看您的專案" msgid "ClusterIntegration|See zones" -msgstr "" +msgstr "查看區域" msgid "ClusterIntegration|Something went wrong on our end." -msgstr "" +msgstr "內部發生了錯誤" msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Container Engine" -msgstr "" +msgstr "在 Google Container Engine 上建立叢集時發生了錯誤" msgid "ClusterIntegration|Toggle Cluster" -msgstr "" +msgstr "叢集開關" msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way." -msgstr "" +msgstr "當叢集連結到此專案,你可以使用複閱應用 (review apps),部署你的應用程式,執行你的流水線 (pipelines),還有更多容易上手的方式可以使用。" msgid "ClusterIntegration|Your account must have %{link_to_container_engine}" -msgstr "" +msgstr "您的帳號必須有 %{link_to_container_engine}" msgid "ClusterIntegration|Zone" -msgstr "" +msgstr "區域" msgid "ClusterIntegration|access to Google Container Engine" -msgstr "" +msgstr "存取 Google Container Engine" msgid "ClusterIntegration|cluster" -msgstr "" +msgstr "叢集" msgid "ClusterIntegration|help page" -msgstr "" +msgstr "說明頁面" msgid "ClusterIntegration|meets the requirements" -msgstr "" +msgstr "符合需求" msgid "ClusterIntegration|properly configured" -msgstr "" +msgstr "設定正確" msgid "Comments" msgstr "留言" @@ -617,13 +617,13 @@ msgstr[0] "更動記錄 (commit) " msgid "Commit %d file" msgid_plural "Commit %d files" -msgstr[0] "" +msgstr[0] "提交 %d 個檔案" msgid "Commit Message" msgstr "更動訊息" msgid "Commit duration in minutes for last 30 commits" -msgstr "最近 30 次更動花費的時間(分鐘)" +msgstr "最近 30 次更動所花費的時間(分鐘)" msgid "Commit message" msgstr "更動說明 (commit) " @@ -641,7 +641,7 @@ msgid "Commits feed" msgstr "更動摘要 (commit feed)" msgid "Commits|History" -msgstr "過去更動 (commit) " +msgstr "更動紀錄 (commit)" msgid "Committed by" msgstr "送交者為 " @@ -650,49 +650,49 @@ msgid "Compare" msgstr "比較" msgid "Container Registry" -msgstr "" +msgstr "Container Registry" msgid "ContainerRegistry|Created" -msgstr "" +msgstr "已建立" msgid "ContainerRegistry|First log in to GitLab’s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:" -msgstr "" +msgstr "請先使用你的 Gitlab 帳號來登入 Gitlab 的 Container Registry。如果你有 %{link_2fa} ,你必須使用 %{link_token}:" msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:" -msgstr "" +msgstr "GitLab 支援多達 3 級的映像檔名稱。以下映像檔範例對您的專案有幫助:" msgid "ContainerRegistry|How to use the Container Registry" -msgstr "" +msgstr "如何使用 Container Registry" msgid "ContainerRegistry|Learn more about" -msgstr "" +msgstr "了解更多" msgid "ContainerRegistry|No tags in Container Registry for this container image." -msgstr "" +msgstr "在這個 Container Registry 中不包含任何有標籤的容器映像檔" msgid "ContainerRegistry|Once you log in, you’re free to create and upload a container image using the common %{build} and %{push} commands" -msgstr "" +msgstr "當您登入後,您可以透過 %{build} 和 %{push} 指令來自由建立和上傳容器映像檔 (container image)" msgid "ContainerRegistry|Remove repository" -msgstr "" +msgstr "刪除檔案庫(repository)" msgid "ContainerRegistry|Remove tag" -msgstr "" +msgstr "刪除標籤" msgid "ContainerRegistry|Size" msgstr "" msgid "ContainerRegistry|Tag" -msgstr "" +msgstr "標籤" msgid "ContainerRegistry|Tag ID" -msgstr "" +msgstr "標籤 ID" msgid "ContainerRegistry|Use different image names" -msgstr "" +msgstr "使用不同的映像檔名稱" msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images." -msgstr "" +msgstr "將 Docker Container Registry 整合到 GitLab 中後,每個專案都可以有自己的空間來儲存 Docker 的映像檔" msgid "Contribution guide" msgstr "協作指南" @@ -701,13 +701,13 @@ msgid "Contributors" msgstr "協作者" msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node" -msgstr "" +msgstr "控制次節點 (secondary node) 同步 LFS 和附檔的最大並行率 (concurrency)" msgid "Control the maximum concurrency of repository backfill for this secondary node" -msgstr "" +msgstr "控制次節點 (secondary node) 同步檔案庫 (repository) 的最大並行率 (concurrency)" msgid "Copy SSH public key to clipboard" -msgstr "複製 SSH 公鑰到剪貼簿" +msgstr "複製 SSH 金鑰到剪貼簿" msgid "Copy URL to clipboard" msgstr "複製網址到剪貼簿" @@ -728,19 +728,19 @@ msgid "Create empty bare repository" msgstr "建立一個新的 bare repository" msgid "Create file" -msgstr "" +msgstr "新增檔案" msgid "Create merge request" msgstr "發出合併請求 (merge request) " msgid "Create new branch" -msgstr "" +msgstr "新增分支(branch)" msgid "Create new directory" -msgstr "" +msgstr "新增資料夾" msgid "Create new file" -msgstr "" +msgstr "新增檔案" msgid "Create new..." msgstr "建立..." @@ -764,13 +764,13 @@ msgid "Custom notification events" msgstr "自訂事件通知" msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}." -msgstr "自訂通知層級相當於參與度設定。使用自訂通知層級,您可以只收到特定的事件通知。請參照 %{notification_link} 以獲得更多訊息。" +msgstr "自訂通知的等級與參與度設定相同。使用自訂通知讓你只收到特定的事件通知。想了解更多資訊,請查閱 %{notification_link} 。" msgid "Cycle Analytics" msgstr "週期分析" msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project." -msgstr "週期分析讓您可以有效的釐清專案從發想到產品推出所花的時間長短。" +msgstr "週期分析讓你可以有效地釐清專案從發想到產品推出所花費的時間。" msgid "CycleAnalyticsStage|Code" msgstr "程式開發" @@ -816,7 +816,7 @@ msgid "Description" msgstr "描述" msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project." -msgstr "描述範本允許你為項目的議題和合併請求在建立時選擇特定的範本。" +msgstr "描述範本 (Description templates) 讓你在建立專案的議題 (Issue) 和合併請求時可以選擇特定的範本。" msgid "Details" msgstr "細節" @@ -828,10 +828,10 @@ msgid "Discard changes" msgstr "放棄修改" msgid "Dismiss Cycle Analytics introduction box" -msgstr "" +msgstr "關閉循環分析介紹視窗" msgid "Dismiss Merge Request promotion" -msgstr "關閉合併請求中的促銷廣告" +msgstr "" msgid "Don't show again" msgstr "不再顯示" @@ -900,10 +900,10 @@ msgid "Every week (Sundays at 4:00am)" msgstr "每週執行(週日淩晨 四點)" msgid "Explore projects" -msgstr "瀏覽項目" +msgstr "瀏覽專案" msgid "Explore public groups" -msgstr "" +msgstr "搜尋公開的群組" msgid "Failed to change the owner" msgstr "無法變更所有權" @@ -912,7 +912,7 @@ msgid "Failed to remove the pipeline schedule" msgstr "無法刪除流水線 (pipeline) 排程" msgid "File name" -msgstr "" +msgstr "檔案名稱" msgid "Files" msgstr "檔案" @@ -958,13 +958,13 @@ msgid "Geo Nodes" msgstr "Geo 節點" msgid "Geo|File sync capacity" -msgstr "" +msgstr "檔案同步容量" msgid "Geo|Groups to replicate" msgstr "要複製的群組" msgid "Geo|Repository sync capacity" -msgstr "" +msgstr "檔案庫(repository)同步量" msgid "Geo|Select groups to replicate." msgstr "選擇欲複製之群組。" @@ -982,76 +982,76 @@ msgid "GoToYourFork|Fork" msgstr "前往您的分支 (fork) " msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service." -msgstr "" +msgstr "Google 身份驗證不是 %{link_to_documentation}。如果您想使用此服務,請諮詢管理員。" msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" -msgstr "禁止與其他群組共享 %{group} 中的項目" +msgstr "禁止與其他群組共享 %{group} 中的專案" msgid "GroupSettings|Share with group lock" -msgstr "分享群組鎖" +msgstr "" msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." -msgstr "這個設定已經套用至 %{ancestor_group},並已經覆蓋此子組的設定。" +msgstr "這個設定已經套用至 %{ancestor_group},並覆蓋了它的子群組設定。" msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." -msgstr "此設定已經套用在 %{ancestor_group}。若要與其他群組共享此群組中的項目,請聯繫擁有者覆蓋這個設定或者 %{remove_ancestor_share_with_group_lock}" +msgstr "此設定已經套用在 %{ancestor_group}。若要與其他群組共享此群組中的專案,請聯繫擁有者覆蓋這個設定或者 %{remove_ancestor_share_with_group_lock}" msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "此設定已經套用至 %{ancestor_group}。你可以覆蓋此設定或是 %{remove_ancestor_share_with_group_lock}" msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." -msgstr "此設定將套用在所有子組,除非群組擁有者覆蓋。已經有權限瀏覽本項目的群組將仍可瀏覽,除非手動移除。" +msgstr "除非群組擁有者覆蓋此設定,否則此設定將套用在所有子群組。此外,如果沒有手動移除原本已經擁有瀏覽專案權限的群組,這些群組仍可繼續瀏覽。" msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" -msgstr "無法調用父組的 \"共享群組鎖\",僅父群組的所有者才可操作。" +msgstr "無法啟用上級群組的「共享群組鎖」。只有上級群組的所有者才可啟用。" msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "從 %{ancestor_group_name} 中移除共享群組鎖" msgid "GroupsEmptyState|A group is a collection of several projects." -msgstr "" +msgstr "群組是數個專案的集合。" msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder." -msgstr "" +msgstr "當你在群組下建立一個專案,這個專案的運作方式就如同一個資料夾。" msgid "GroupsEmptyState|No groups found" -msgstr "" +msgstr "找不到群組" msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group." -msgstr "" +msgstr "你可以管理群組內所有成員的每個專案的存取權限" msgid "GroupsTreeRole|as" -msgstr "" +msgstr "的" msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?" -msgstr "" +msgstr "你確定要離開群組 \"${this.group.fullName}\" 嗎?" msgid "GroupsTree|Create a project in this group." -msgstr "" +msgstr "在此群組建立新的專案" msgid "GroupsTree|Create a subgroup in this group." -msgstr "" +msgstr "在此群組中建立子群組" msgid "GroupsTree|Edit group" -msgstr "" +msgstr "編輯群組" msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner." -msgstr "" +msgstr "無法離開群組,請確保您不是唯一的擁有者。" msgid "GroupsTree|Filter by name..." -msgstr "" +msgstr "以名稱篩選⋯" msgid "GroupsTree|Leave this group" -msgstr "" +msgstr "離開此群組" msgid "GroupsTree|Loading groups" -msgstr "" +msgstr "群組讀取中" msgid "GroupsTree|Sorry, no groups matched your search" -msgstr "" +msgstr "不好意思,沒有搜尋到任何符合條件的群組" msgid "GroupsTree|Sorry, no groups or projects matched your search" -msgstr "" +msgstr "不好意思,沒有搜尋到任何符合條件的群組或專案" msgid "Health Check" msgstr "健康檢查" @@ -1081,10 +1081,10 @@ msgid "Import repository" msgstr "匯入檔案庫 (repository)" msgid "Improve Issue boards with GitLab Enterprise Edition." -msgstr "協助改進 GitLab 企業版的問題看板。" +msgstr "協助改進 GitLab 企業版的議題看板(issue boards)" msgid "Improve issues management with Issue weight and GitLab Enterprise Edition." -msgstr "協助改善 GitLab 企業版的問題管理與權重。" +msgstr "協助改進 GitLab 企業版的議題管理與權重。" msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition." msgstr "協助改進 GitLab 企業版的搜尋 & 進階全局搜尋。" @@ -1094,13 +1094,13 @@ msgstr "安裝與 GitLab CI 相容的 Runner" msgid "Instance" msgid_plural "Instances" -msgstr[0] "實例" +msgstr[0] "主機" msgid "Internal - The group and any internal projects can be viewed by any logged in user." -msgstr "" +msgstr "內部 - 任何登入的使用者都可以查看該群組及其專案" msgid "Internal - The project can be accessed by any logged in user." -msgstr "" +msgstr "內部 - 任何登入的使用者都可以存取此專案" msgid "Interval Pattern" msgstr "循環週期" @@ -1109,10 +1109,10 @@ msgid "Introducing Cycle Analytics" msgstr "週期分析簡介" msgid "Issue board focus mode" -msgstr "問題看板模式" +msgstr "議題看板(issue boards)模式" msgid "Issue boards with milestones" -msgstr "問題看板 & 里程碑" +msgstr "議題看板(issue boards)與里程碑" msgid "Issue events" msgstr "議題 (issue) 事件" @@ -1146,10 +1146,10 @@ msgid "Last commit" msgstr "最後更動記錄 (commit) " msgid "Last edited %{date}" -msgstr "上次編輯於 %{date}" +msgstr "最後編輯於 %{date}" msgid "Last edited by %{name}" -msgstr "上次編輯由 %{name}" +msgstr "最後由 %{name} 編輯" msgid "Last update" msgstr "上次更新" @@ -1170,7 +1170,7 @@ msgid "Learn more in the|pipeline schedules documentation" msgstr "流水線 (pipeline) 排程說明文件" msgid "Leave" -msgstr "" +msgstr "離開" msgid "Leave group" msgstr "退出群組" @@ -1192,13 +1192,13 @@ msgid "Locked" msgstr "鎖定" msgid "Locked Files" -msgstr "被鎖定檔案" +msgstr "被鎖定的檔案" msgid "Login" -msgstr "" +msgstr "登入" msgid "Maximum git storage failures" -msgstr "" +msgstr "最大 git 儲存失敗" msgid "Median" msgstr "中位數" @@ -1228,10 +1228,10 @@ msgid "More information is available|here" msgstr "健康檢查" msgid "Multiple issue boards" -msgstr "多個問題看板" +msgstr "多個議題看板 (issue boards)" msgid "New Cluster" -msgstr "" +msgstr "新叢集" msgid "New Issue" msgid_plural "New Issues" @@ -1250,7 +1250,7 @@ msgid "New file" msgstr "新增檔案" msgid "New group" -msgstr "" +msgstr "新群組" msgid "New issue" msgstr "新增議題 (issue) " @@ -1259,7 +1259,7 @@ msgid "New merge request" msgstr "新增合併請求 (merge request) " msgid "New project" -msgstr "" +msgstr "新專案" msgid "New schedule" msgstr "新增排程" @@ -1268,7 +1268,7 @@ msgid "New snippet" msgstr "新文字片段" msgid "New subgroup" -msgstr "" +msgstr "新子群組" msgid "New tag" msgstr "新增標籤" @@ -1349,7 +1349,7 @@ msgid "Notifications" msgstr "通知" msgid "Number of access attempts" -msgstr "" +msgstr "嘗試存取的次數" msgid "Number of failures before backing off" msgstr "" @@ -1376,7 +1376,7 @@ msgid "Owner" msgstr "所有權" msgid "Pagination|Last »" -msgstr "尾頁 »" +msgstr "最末頁 »" msgid "Pagination|Next" msgstr "下一頁" @@ -1385,13 +1385,13 @@ msgid "Pagination|Prev" msgstr "上一頁" msgid "Pagination|« First" -msgstr "« 首頁" +msgstr "« 第一頁" msgid "Password" msgstr "密碼" msgid "People without permission will never get a notification and won\\'t be able to comment." -msgstr "當使用者沒有權限,將不會收到任何通知以及無法留言" +msgstr "沒有權限的使用者將不會收到通知,也無法留言。" msgid "Pipeline" msgstr "流水線 (pipeline) " @@ -1406,7 +1406,7 @@ msgid "Pipeline Schedules" msgstr "流水線 (pipeline) 排程" msgid "Pipeline quota" -msgstr "流水線配額" +msgstr "流水線額度" msgid "PipelineCharts|Failed:" msgstr "失敗:" @@ -1496,52 +1496,52 @@ msgid "Preferences" msgstr "偏好設定" msgid "Private - Project access must be granted explicitly to each user." -msgstr "" +msgstr "私有 - 專案權限必須一一指派給每個使用者" msgid "Private - The group and its projects can only be viewed by members." -msgstr "" +msgstr "私有 - 群組及旗下專案只能被該群組成員查看" msgid "Profile" msgstr "個人資料" msgid "Profiles|Account scheduled for removal." -msgstr "" +msgstr "帳號將會被刪除" msgid "Profiles|Delete Account" -msgstr "" +msgstr "刪除帳號" msgid "Profiles|Delete account" -msgstr "" +msgstr "刪除帳號" msgid "Profiles|Delete your account?" -msgstr "" +msgstr "刪除您的帳號?" msgid "Profiles|Deleting an account has the following effects:" -msgstr "" +msgstr "刪除帳號將會造成以下影響:" msgid "Profiles|Invalid password" -msgstr "" +msgstr "無效的密碼" msgid "Profiles|Invalid username" -msgstr "" +msgstr "無效的使用者名稱" msgid "Profiles|Type your %{confirmationValue} to confirm:" -msgstr "" +msgstr "輸入您的 %{confirmationValue} 以確認:" msgid "Profiles|You don't have access to delete this user." -msgstr "" +msgstr "您沒有權限刪除此帳號" msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account." -msgstr "" +msgstr "你必須轉換你的所有權或在你刪除你帳號前刪除這些群組" msgid "Profiles|Your account is currently an owner in these groups:" -msgstr "" +msgstr "你的帳號目前擁有這些群組:" msgid "Profiles|your account" -msgstr "" +msgstr "你的帳號" msgid "Project '%{project_name}' is in the process of being deleted." -msgstr "" +msgstr "專案 \"%{project_name}\" 正在被刪除。" msgid "Project '%{project_name}' queued for deletion." msgstr "專案 '%{project_name}' 已加入刪除佇列。" @@ -1598,22 +1598,22 @@ msgid "ProjectSettings|Contact an admin to change this setting." msgstr "聯絡管理員以變更設定。" msgid "ProjectSettings|Only signed commits can be pushed to this repository." -msgstr "只有已簽署的變更才能被推送到倉庫。" +msgstr "只有已簽章的變更才能被推送到檔案庫(repository)。" msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin." msgstr "此設定已經套用於伺服器層級,並且可被管理員覆寫。" msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project." -msgstr "此設定已經套用於伺服器級別,但已經在這個專案被覆寫。" +msgstr "此設定已經套用至伺服器層級,但此專案使用另一個設定。" msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin." -msgstr "此設定將套用於所有專案,除非被管理員覆寫。" +msgstr "此設定將套用至所有專案,除非被管理員覆寫。" msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails." -msgstr "" +msgstr "使用者推送的修改 (commits) 只能使用他們自己的電子郵件。" msgid "Projects" -msgstr "" +msgstr "專案" msgid "ProjectsDropdown|Frequently visited" msgstr "經常使用" @@ -1637,10 +1637,10 @@ msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "此功能需要瀏覽器支援 localStorage" msgid "Public - The group and any public projects can be viewed without any authentication." -msgstr "" +msgstr "公開 - 未登入的情況下依然可以查看任何公開專案" msgid "Public - The project can be accessed without any authentication." -msgstr "" +msgstr "公開 - 無須任何身份驗證即可存取該專案" msgid "Push Rules" msgstr "推送 [Push] 規則" @@ -1649,7 +1649,7 @@ msgid "Push events" msgstr "推送 (push) 事件" msgid "PushRule|Committer restriction" -msgstr "" +msgstr "提交限制" msgid "Read more" msgstr "瞭解更多" @@ -1715,7 +1715,7 @@ msgid "SSH Keys" msgstr "SSH 金鑰" msgid "Save" -msgstr "" +msgstr "儲存" msgid "Save changes" msgstr "儲存變更" @@ -1736,13 +1736,13 @@ msgid "Search branches and tags" msgstr "搜尋分支 (branch) 和標籤" msgid "Seconds before reseting failure information" -msgstr "" +msgstr "重置失敗訊息等待時間(秒)" msgid "Seconds to wait after a storage failure" -msgstr "" +msgstr "儲存失敗後等待時間(秒)" msgid "Seconds to wait for a storage access attempt" -msgstr "" +msgstr "等待存取儲存空間的嘗試時間(秒)" msgid "Select Archive Format" msgstr "選擇下載格式" @@ -1775,7 +1775,7 @@ msgid "Settings" msgstr "設定" msgid "Show parent pages" -msgstr "顯示父頁面" +msgstr "顯示上層頁面" msgid "Show parent subgroups" msgstr "顯示群組中的子群組" @@ -1788,25 +1788,25 @@ msgid "Snippets" msgstr "文字片段" msgid "Something went wrong on our end." -msgstr "" +msgstr "發生了錯誤。" msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName(this.issuableType)}" msgstr "有個地方出錯了,因為他嘗試去變更 ${this.issuableDisplayName(this.issuableType)} 的鎖定狀態。" msgid "Something went wrong while fetching the projects." -msgstr "" +msgstr "讀取專案時發生錯誤。" msgid "Something went wrong while fetching the registry list." -msgstr "" +msgstr "讀取註冊列表時發生錯誤。" msgid "Sort by" -msgstr "" +msgstr "排序" msgid "SortOptions|Access level, ascending" -msgstr "訪問級別、升冪排列" +msgstr "存取層級,以升冪排列" msgid "SortOptions|Access level, descending" -msgstr "訪問級別、降冪排列" +msgstr "" msgid "SortOptions|Created date" msgstr "建立日期" @@ -1827,7 +1827,7 @@ msgid "SortOptions|Largest group" msgstr "最大群組" msgid "SortOptions|Largest repository" -msgstr "最大儲存庫" +msgstr "最大檔案庫(repository)" msgid "SortOptions|Last created" msgstr "最近建立" @@ -1920,10 +1920,10 @@ msgid "Start the Runner!" msgstr "啟動 Runner!" msgid "Subgroups" -msgstr "" +msgstr "子群組" msgid "Subscribe" -msgstr "" +msgstr "訂閱" msgid "Switch branch/tag" msgstr "切換分支 (branch) 或標籤" @@ -1951,7 +1951,7 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa msgstr "GitLab 的進階全局搜尋功能是非常強大的搜尋服務。您可以搜尋其他團隊的代碼以幫助您完善自己項目中的代碼。從而避免建立重複的代碼浪費時間。" msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold" -msgstr "" +msgstr "限流阻斷元件的觸發門檻應低於計數錯誤門檻" msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." msgstr "程式開發階段顯示從第一次更動記錄 (commit) 到建立合併請求 (merge request) 的時間。建立第一個合併請求後,資料將自動填入。" @@ -1966,13 +1966,13 @@ msgid "The issue stage shows the time it takes from creating an issue to assigni msgstr "議題 (issue) 階段顯示從議題建立到設定里程碑所花的時間,或是議題被分類到議題看板 (issue board) 中所花的時間。建立第一個議題後,資料將自動填入。" msgid "The number of attempts GitLab will make to access a storage." -msgstr "" +msgstr "GitLab 存取儲存空間的嘗試次數。" msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host" msgstr "" msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}." -msgstr "" +msgstr "GitLab 將阻擋存取失敗的次數。在管理者介面中可以重置失敗次數: %{link_to_health_page} 或使用 %{api_documentation_link}。" msgid "The phase of the development lifecycle." msgstr "專案開發週期的各個階段。" @@ -2005,10 +2005,10 @@ msgid "The testing stage shows the time GitLab CI takes to run every pipeline fo msgstr "測試階段顯示相關合併請求 (merge request) 的流水線 (pipeline) 所花的時間。當第一個流水線 (pipeline) 執行完畢後,資料將自動填入。" msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset." -msgstr "" +msgstr "GitLab 保存失敗訊息的時間(秒)。在此時間內若沒有發生錯誤,失敗訊息將會被重置" msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised." -msgstr "" +msgstr "GitLab 嘗試存取檔案庫 (repository) 的時間 (秒)。超過此時間將會引發逾時錯誤。" msgid "The time taken by each data entry gathered by that stage." msgstr "該階段中每一個資料項目所花的時間。" @@ -2020,7 +2020,7 @@ msgid "There are problems accessing Git storage: " msgstr "存取 Git 儲存空間時出現問題:" msgid "This branch has changed since you started editing. Would you like to create a new branch?" -msgstr "" +msgstr "在您編輯後,此分支已被更改,您想要建立一個新的分支嗎?" msgid "This is a confidential issue." msgstr "這是個隱密問題。" @@ -2205,7 +2205,7 @@ msgid "Unstar" msgstr "取消收藏" msgid "Unsubscribe" -msgstr "" +msgstr "取消訂閱" msgid "Upgrade your plan to activate Advanced Global Search." msgstr "升級您的方案以啟用進階全局搜尋。" @@ -2220,7 +2220,7 @@ msgid "Upgrade your plan to activate Issue weight." msgstr "升級您的方案以啟用問題權重。" msgid "Upgrade your plan to improve Issue boards." -msgstr "升級您的方案以使用問題看版" +msgstr "升級您的方案以使用議題看板(issue boards)" msgid "Upload New File" msgstr "上傳新檔案" @@ -2265,19 +2265,19 @@ msgid "We don't have enough data to show this stage." msgstr "因該階段的資料不足而無法顯示相關資訊" msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group." -msgstr "如果有新的推送或新的议题,Webhook 将自动触发您设置 URL。 您可以配置 Webhook 来监听特定事件,如推送、议题或合并请求。 群组 Webhook 将适用于团队中的所有项目,并允许您设置整个团队中的 Webhook 。" +msgstr "" msgid "Weight" msgstr "權重" msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable" -msgstr "" +msgstr "當存取檔案庫 (repository) 失敗時, GitLab 將在此處指定的時間內防止檔案庫的存取,以此等待檔案系統恢復。失敗的檔案庫分流 (shard) 會暫時無法使用。" msgid "Wiki" msgstr "Wiki" msgid "WikiClone|Clone your wiki" -msgstr "克隆你的維基" +msgstr "複製(clone)您的 Wiki" msgid "WikiClone|Git Access" msgstr "Git 讀取" @@ -2292,10 +2292,10 @@ msgid "WikiClone|Start Gollum and edit locally" msgstr "開始你的 Gollum 並在本機編輯。" msgid "WikiEmptyPageError|You are not allowed to create wiki pages" -msgstr "你沒有權限去建立維基頁面" +msgstr "你沒有權限建立 Wiki 頁面" msgid "WikiHistoricalPage|This is an old version of this page." -msgstr "這是這個頁面較舊的版本。" +msgstr "這個頁面較舊的版本。" msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}." msgstr "你可以查看 %{most_recent_link} 或是瀏覽 %{history_link} 。" @@ -2343,7 +2343,7 @@ msgid "WikiPage|Page slug" msgstr "頁面 slug" msgid "WikiPage|Write your content or drag files here..." -msgstr "寫上你的內容或拖曳檔案到這..." +msgstr "填寫內容或拖曳檔案至此..." msgid "Wiki|Create Page" msgstr "建立頁面" @@ -2394,19 +2394,19 @@ msgid "You are going to transfer %{project_name_with_namespace} to another owner msgstr "將要把 %{project_name_with_namespace} 的所有權轉移給另一個人。真的「確定」要這麼做嗎?" msgid "You are on a read-only GitLab instance." -msgstr "" +msgstr "您在唯讀的 GitLab 主機上。" msgid "You are on a read-only GitLab instance. If you want to make any changes, you must visit the %{link_to_primary_node}." -msgstr "" +msgstr "您在唯讀的 GitLab 主機上,如果您想要進行修改,必須到 %{link_to_primary_node}" msgid "You can only add files when you are on a branch" msgstr "只能在分支 (branch) 上建立檔案" msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead." -msgstr "" +msgstr "您不能寫入唯讀的次要 GitLab Geo 主機。請改用 %{link_to_primary_node}。" msgid "You cannot write to this read-only GitLab instance." -msgstr "" +msgstr "您不能修改這個唯讀的 GitLab 主機。" msgid "You have reached your project limit" msgstr "您已達到專案數量限制" @@ -2442,7 +2442,7 @@ msgid "Your comment will not be visible to the public." msgstr "你的留言將不會被公開。" msgid "Your groups" -msgstr "" +msgstr "您的群組" msgid "Your name" msgstr "您的名字" @@ -2468,14 +2468,14 @@ msgid_plural "parents" msgstr[0] "上層" msgid "password" -msgstr "" +msgstr "密碼" msgid "personal access token" -msgstr "" +msgstr "私人存取憑證 (access token)" msgid "to help your contributors communicate effectively!" msgstr "幫助你的貢獻者進行有效的溝通!" msgid "username" -msgstr "" +msgstr "使用者名稱" From 6b1ad363dc3902fa5052aee8a2f98b128fd617e6 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 4 Dec 2017 22:17:22 +0900 Subject: [PATCH 47/74] Fix features/projects/clusters/user_spec.rb --- spec/features/projects/clusters/user_spec.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb index fe2f6a293c2..e141d19ea5d 100644 --- a/spec/features/projects/clusters/user_spec.rb +++ b/spec/features/projects/clusters/user_spec.rb @@ -21,14 +21,17 @@ feature 'User Cluster', :js do context 'when user filled form with valid parameters' do before do + fill_in 'cluster_name', with: 'dev-cluster' fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'http://example.com' fill_in 'cluster_platform_kubernetes_attributes_token', with: 'my-token' - fill_in 'cluster_name', with: 'dev-cluster' click_button 'Add cluster' end - it 'user sees a cluster details page and creation status' do - expect(page).to have_content('Cluster was successfully created on Google Container Engine') + it 'user sees a cluster details page' do + expect(page).to have_content('Enable cluster integration') + expect(page).to have_content('dev-cluster') + expect(page).to have_content('http://example.com') + expect(page).to have_content('my-token') end end From f17375b9d03ed420c83ed41d58e363a022606c68 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 4 Dec 2017 22:18:58 +0900 Subject: [PATCH 48/74] Optimize Clusters::CreateService#execute --- app/controllers/projects/clusters/user_controller.rb | 2 +- app/services/clusters/create_service.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/clusters/user_controller.rb b/app/controllers/projects/clusters/user_controller.rb index 7f108c54b19..d7678512073 100644 --- a/app/controllers/projects/clusters/user_controller.rb +++ b/app/controllers/projects/clusters/user_controller.rb @@ -11,7 +11,7 @@ class Projects::Clusters::UserController < Projects::ApplicationController def create @cluster = ::Clusters::CreateService .new(project, current_user, create_params) - .execute(nil) + .execute if @cluster.persisted? redirect_to project_cluster_path(project, @cluster) diff --git a/app/services/clusters/create_service.rb b/app/services/clusters/create_service.rb index 1d407739b21..7b697f6d807 100644 --- a/app/services/clusters/create_service.rb +++ b/app/services/clusters/create_service.rb @@ -2,7 +2,7 @@ module Clusters class CreateService < BaseService attr_reader :access_token - def execute(access_token) + def execute(access_token = nil) @access_token = access_token create_cluster.tap do |cluster| From b716bba7e36390b455b3a36ecc5d3b1f9c0d0649 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 4 Dec 2017 22:21:23 +0900 Subject: [PATCH 49/74] Use status polling definitino --- app/controllers/projects/clusters_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb index 639874e6231..0800c1ea0b2 100644 --- a/app/controllers/projects/clusters_controller.rb +++ b/app/controllers/projects/clusters_controller.rb @@ -21,7 +21,7 @@ class Projects::ClustersController < Projects::ApplicationController def status respond_to do |format| format.json do - Gitlab::PollingInterval.set_header(response, interval: 10_000) + Gitlab::PollingInterval.set_header(response, interval: STATUS_POLLING_INTERVAL) render json: ClusterSerializer .new(project: @project, current_user: @current_user) From c4f91fe43c24a347d9ebd832aea0946c2b0ca5c0 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 4 Dec 2017 22:27:31 +0900 Subject: [PATCH 50/74] Optimize Projects::ClustersController#cluster --- app/controllers/projects/clusters_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb index 0800c1ea0b2..ad5178e288c 100644 --- a/app/controllers/projects/clusters_controller.rb +++ b/app/controllers/projects/clusters_controller.rb @@ -59,7 +59,8 @@ class Projects::ClustersController < Projects::ApplicationController private def cluster - @cluster ||= project.clusters.find(params[:id]).present(current_user: current_user) || render_404 + @cluster ||= project.clusters.find_by!(id: params[:id]) + .present(current_user: current_user) end def update_params From 2871d302f0a3fbd79f2427730146399728d02568 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 4 Dec 2017 22:32:01 +0900 Subject: [PATCH 51/74] Remove duplicated validations on cluster_name --- app/models/clusters/cluster.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 441cbcb701d..45beced1427 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -93,11 +93,6 @@ module Clusters return false end - if managed? && name_changed? - errors.add(:base, "cannot modify cluster name") - return false - end - true end end From 61283d51aea1fd548571a68b9dad7d649fbb58de Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 4 Dec 2017 22:34:56 +0900 Subject: [PATCH 52/74] Improve name validation for cluster --- app/validators/cluster_name_validator.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/validators/cluster_name_validator.rb b/app/validators/cluster_name_validator.rb index 13ec342f399..e7d32550176 100644 --- a/app/validators/cluster_name_validator.rb +++ b/app/validators/cluster_name_validator.rb @@ -3,11 +3,7 @@ # Custom validator for ClusterName. class ClusterNameValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - if record.user? - unless value.present? - record.errors.add(attribute, " has to be present") - end - elsif record.gcp? + if record.managed? if record.persisted? && record.name_changed? record.errors.add(attribute, " can not be changed because it's synchronized with provider") end @@ -19,6 +15,10 @@ class ClusterNameValidator < ActiveModel::EachValidator unless value =~ Gitlab::Regex.kubernetes_namespace_regex record.errors.add(attribute, Gitlab::Regex.kubernetes_namespace_regex_message) end + else + unless value.present? + record.errors.add(attribute, " has to be present") + end end end end From 2cdd8e60ae3ddee6c5616888a579e51a2a4936f1 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Mon, 4 Dec 2017 15:02:08 +0100 Subject: [PATCH 53/74] Update parser gem to remove warning about wrong ruby version --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3f4c930c71d..b7087c10a7e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -573,8 +573,8 @@ GEM parallel (1.12.0) paranoia (2.3.1) activerecord (>= 4.0, < 5.2) - parser (2.4.0.0) - ast (~> 2.2) + parser (2.4.0.2) + ast (~> 2.3) parslet (1.5.0) blankslate (~> 2.0) path_expander (1.0.1) From 28e93c7dbb8a592c2bbf8014297d5923de36874f Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 4 Dec 2017 13:26:38 +0000 Subject: [PATCH 54/74] Import axios utils in commons --- app/assets/javascripts/commons/index.js | 1 + changelogs/unreleased/39727-add-axios-to-common.yml | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 changelogs/unreleased/39727-add-axios-to-common.yml diff --git a/app/assets/javascripts/commons/index.js b/app/assets/javascripts/commons/index.js index 768453b28f1..0d2fe2925d8 100644 --- a/app/assets/javascripts/commons/index.js +++ b/app/assets/javascripts/commons/index.js @@ -3,3 +3,4 @@ import './polyfills'; import './jquery'; import './bootstrap'; import './vue'; +import '../lib/utils/axios_utils'; diff --git a/changelogs/unreleased/39727-add-axios-to-common.yml b/changelogs/unreleased/39727-add-axios-to-common.yml new file mode 100644 index 00000000000..688757d2486 --- /dev/null +++ b/changelogs/unreleased/39727-add-axios-to-common.yml @@ -0,0 +1,5 @@ +--- +title: Add axios to common file +merge_request: +author: +type: performance From 12f61e0d2cabb07172ef93143b084104141dd771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20=22BKC=22=20Carlb=C3=A4cker?= Date: Mon, 4 Dec 2017 10:31:13 +0100 Subject: [PATCH 55/74] Move SingleRepositoryWorker#fsck into Gitlab::Git::Repository --- .../repository_check/single_repository_worker.rb | 16 +++++++--------- lib/gitlab/git/repository.rb | 12 ++++++++++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb index 164586cf0b7..3d4bee15f1c 100644 --- a/app/workers/repository_check/single_repository_worker.rb +++ b/app/workers/repository_check/single_repository_worker.rb @@ -32,16 +32,14 @@ module RepositoryCheck end def git_fsck(repository) - path = repository.path_to_repo - cmd = %W(nice git --git-dir=#{path} fsck) - output, status = Gitlab::Popen.popen(cmd) + return false unless repository.exists? - if status.zero? - true - else - Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") - false - end + repository.raw_repository.fsck + + true + rescue Gitlab::Git::Repository::GitError => e + Gitlab::RepositoryCheckLogger.error(e.message) + false end def has_pushes?(project) diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index dbc08747228..867fc2a42f6 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1118,9 +1118,11 @@ module Gitlab end # Refactoring aid; allows us to copy code from app/models/repository.rb - def run_git(args, env: {}) + def run_git(args, env: {}, nice: false) + cmd = [Gitlab.config.git.bin_path, *args] + cmd.unshift("nice") if nice circuit_breaker.perform do - popen([Gitlab.config.git.bin_path, *args], path, env) + popen(cmd, path, env) end end @@ -1187,6 +1189,12 @@ module Gitlab end end + def fsck + output, status = run_git(%W[--git-dir=#{path} fsck], nice: true) + + raise GitError.new("Could not fsck repository:\n#{output}") unless status.zero? + end + def gitaly_repository Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository) end From 4716b0880d787c04d85b13dabfbd3f02c0d362f1 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 5 Dec 2017 00:25:12 +0900 Subject: [PATCH 56/74] Add changelog --- changelogs/unreleased/35616-move-k8-to-cluster-page.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/35616-move-k8-to-cluster-page.yml diff --git a/changelogs/unreleased/35616-move-k8-to-cluster-page.yml b/changelogs/unreleased/35616-move-k8-to-cluster-page.yml new file mode 100644 index 00000000000..39909ffde34 --- /dev/null +++ b/changelogs/unreleased/35616-move-k8-to-cluster-page.yml @@ -0,0 +1,5 @@ +--- +title: Create a new form to add Existing Kubernetes Cluster +merge_request: +author: +type: added From 387f16267ca2dae75f46d0871a34a50792d16089 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 4 Dec 2017 15:18:23 +0000 Subject: [PATCH 57/74] Prevent job link form rendering when user does not have permissions Adds e2e tests --- .../components/graph/job_component.vue | 9 ++- .../projects/pipelines/pipeline_spec.rb | 30 +++++++++ .../pipelines/graph/job_component_spec.js | 65 +++++++++---------- 3 files changed, 66 insertions(+), 38 deletions(-) diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue index 5dea4555515..08199b4234a 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue @@ -78,11 +78,13 @@
+ data-container="body" + class="js-pipeline-graph-job-link" + > + data-container="body" + > { let JobComponent; + let component; const mockJob = { id: 4256, @@ -13,6 +15,7 @@ describe('pipeline graph job component', () => { label: 'passed', group: 'success', details_path: '/root/ci-mock/builds/4256', + has_details: true, action: { icon: 'retry', title: 'Retry', @@ -26,13 +29,13 @@ describe('pipeline graph job component', () => { JobComponent = Vue.extend(jobComponent); }); + afterEach(() => { + component.$destroy(); + }); + describe('name with link', () => { it('should render the job name and status with a link', (done) => { - const component = new JobComponent({ - propsData: { - job: mockJob, - }, - }).$mount(); + component = mountComponent(JobComponent, { job: mockJob }); Vue.nextTick(() => { const link = component.$el.querySelector('a'); @@ -56,23 +59,23 @@ describe('pipeline graph job component', () => { describe('name without link', () => { it('it should render status and name', () => { - const component = new JobComponent({ - propsData: { - job: { - id: 4256, - name: 'test', - status: { - icon: 'icon_status_success', - text: 'passed', - label: 'passed', - group: 'success', - details_path: '/root/ci-mock/builds/4256', - }, + component = mountComponent(JobComponent, { + job: { + id: 4256, + name: 'test', + status: { + icon: 'icon_status_success', + text: 'passed', + label: 'passed', + group: 'success', + details_path: '/root/ci-mock/builds/4256', + has_details: false, }, }, - }).$mount(); + }); expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined(); + expect(component.$el.querySelector('a')).toBeNull(); expect( component.$el.querySelector('.ci-status-text').textContent.trim(), @@ -82,11 +85,7 @@ describe('pipeline graph job component', () => { describe('action icon', () => { it('it should render the action icon', () => { - const component = new JobComponent({ - propsData: { - job: mockJob, - }, - }).$mount(); + component = mountComponent(JobComponent, { job: mockJob }); expect(component.$el.querySelector('a.ci-action-icon-container')).toBeDefined(); expect(component.$el.querySelector('i.ci-action-icon-wrapper')).toBeDefined(); @@ -95,24 +94,20 @@ describe('pipeline graph job component', () => { describe('dropdown', () => { it('should render the dropdown action icon', () => { - const component = new JobComponent({ - propsData: { - job: mockJob, - isDropdown: true, - }, - }).$mount(); + component = mountComponent(JobComponent, { + job: mockJob, + isDropdown: true, + }); expect(component.$el.querySelector('a.ci-action-icon-wrapper')).toBeDefined(); }); }); it('should render provided class name', () => { - const component = new JobComponent({ - propsData: { - job: mockJob, - cssClassJobName: 'css-class-job-name', - }, - }).$mount(); + component = mountComponent(JobComponent, { + job: mockJob, + cssClassJobName: 'css-class-job-name', + }); expect( component.$el.querySelector('a').classList.contains('css-class-job-name'), From 346dcb0626a68ddb1432588e01f78da913cfd827 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 5 Dec 2017 00:46:57 +0900 Subject: [PATCH 58/74] Fix spec/features/projects/clusters/user_spec.rb --- spec/features/projects/clusters/user_spec.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb index e141d19ea5d..e97ba88f2f4 100644 --- a/spec/features/projects/clusters/user_spec.rb +++ b/spec/features/projects/clusters/user_spec.rb @@ -29,9 +29,11 @@ feature 'User Cluster', :js do it 'user sees a cluster details page' do expect(page).to have_content('Enable cluster integration') - expect(page).to have_content('dev-cluster') - expect(page).to have_content('http://example.com') - expect(page).to have_content('my-token') + expect(page.find_field('cluster[name]').value).to eq('dev-cluster') + expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value) + .to have_content('http://example.com') + expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value) + .to have_content('my-token') end end From bf85a90037714c3ba290c9966f435adbfa90b074 Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Mon, 4 Dec 2017 16:19:07 +0000 Subject: [PATCH 59/74] issue note component rename --- ...idget.vue => discussion_locked_widget.vue} | 10 ++++++--- .../notes/components/issue_comment_form.vue | 15 ++++++++----- .../notes/components/issue_discussion.vue | 22 +++++++++---------- .../notes/components/issue_note.vue | 12 +++++----- .../notes/components/issue_note_body.vue | 18 +++++++-------- ...ssue_note_actions.vue => note_actions.vue} | 6 ++--- ...ote_attachment.vue => note_attachment.vue} | 2 +- ...e_awards_list.vue => note_awards_list.vue} | 0 ...e_edited_text.vue => note_edited_text.vue} | 0 ...{issue_note_header.vue => note_header.vue} | 0 ..._widget.vue => note_signed_out_widget.vue} | 1 - .../sidebar/components/lock/edit_form.vue | 9 ++------ .../components/lock/lock_issue_sidebar.vue | 9 ++------ .../components/notes/system_note.vue | 8 +++---- .../javascripts/vue_shared/mixins/issuable.js | 13 +++++++---- ...e_actions_spec.js => note_actions_spec.js} | 4 ++-- ...chment_spec.js => note_attachment_spec.js} | 4 ++-- ..._list_spec.js => note_awards_list_spec.js} | 4 ++-- ..._text_spec.js => note_edited_text_spec.js} | 6 ++--- ...ote_header_spec.js => note_header_spec.js} | 6 ++--- ...spec.js => note_signed_out_widget_spec.js} | 6 ++--- 21 files changed, 77 insertions(+), 78 deletions(-) rename app/assets/javascripts/notes/components/{issue_discussion_locked_widget.vue => discussion_locked_widget.vue} (51%) rename app/assets/javascripts/notes/components/{issue_note_actions.vue => note_actions.vue} (96%) rename app/assets/javascripts/notes/components/{issue_note_attachment.vue => note_attachment.vue} (95%) rename app/assets/javascripts/notes/components/{issue_note_awards_list.vue => note_awards_list.vue} (100%) rename app/assets/javascripts/notes/components/{issue_note_edited_text.vue => note_edited_text.vue} (100%) rename app/assets/javascripts/notes/components/{issue_note_header.vue => note_header.vue} (100%) rename app/assets/javascripts/notes/components/{issue_note_signed_out_widget.vue => note_signed_out_widget.vue} (94%) rename spec/javascripts/notes/components/{issue_note_actions_spec.js => note_actions_spec.js} (95%) rename spec/javascripts/notes/components/{issue_note_attachment_spec.js => note_attachment_spec.js} (80%) rename spec/javascripts/notes/components/{issue_note_awards_list_spec.js => note_awards_list_spec.js} (92%) rename spec/javascripts/notes/components/{issue_note_edited_text_spec.js => note_edited_text_spec.js} (84%) rename spec/javascripts/notes/components/{issue_note_header_spec.js => note_header_spec.js} (93%) rename spec/javascripts/notes/components/{issue_note_signed_out_widget_spec.js => note_signed_out_widget_spec.js} (81%) diff --git a/app/assets/javascripts/notes/components/issue_discussion_locked_widget.vue b/app/assets/javascripts/notes/components/discussion_locked_widget.vue similarity index 51% rename from app/assets/javascripts/notes/components/issue_discussion_locked_widget.vue rename to app/assets/javascripts/notes/components/discussion_locked_widget.vue index 64466b04b40..e6f7ee56ff3 100644 --- a/app/assets/javascripts/notes/components/issue_discussion_locked_widget.vue +++ b/app/assets/javascripts/notes/components/discussion_locked_widget.vue @@ -1,8 +1,12 @@