diff options
-rw-r--r-- | app/helpers/search_helper.rb | 19 | ||||
-rw-r--r-- | app/views/ci/runner/_how_to_setup_runner_automatically.html.haml | 22 | ||||
-rw-r--r-- | app/views/groups/runners/_group_runners.html.haml | 4 | ||||
-rw-r--r-- | app/views/projects/runners/_specific_runners.html.haml | 25 | ||||
-rw-r--r-- | app/views/search/results/_empty.html.haml | 3 | ||||
-rw-r--r-- | changelogs/unreleased/63768-group-settings-kubernetes-runner-section.yml | 5 | ||||
-rw-r--r-- | changelogs/unreleased/improve-search-empty-message.yml | 5 | ||||
-rw-r--r-- | doc/api/tags.md | 6 | ||||
-rw-r--r-- | doc/api/users.md | 29 | ||||
-rw-r--r-- | doc/development/documentation/improvement-workflow.md | 4 | ||||
-rw-r--r-- | locale/gitlab.pot | 28 | ||||
-rw-r--r-- | qa/qa.rb | 3 | ||||
-rw-r--r-- | qa/qa/git/repository.rb | 11 | ||||
-rw-r--r-- | qa/qa/page/project/settings/repository.rb | 2 | ||||
-rw-r--r-- | qa/qa/resource/repository/push.rb | 44 | ||||
-rw-r--r-- | qa/qa/resource/tag.rb | 30 | ||||
-rw-r--r-- | qa/qa/resource/user_gpg.rb | 46 | ||||
-rw-r--r-- | qa/qa/runtime/gpg.rb | 37 | ||||
-rw-r--r-- | qa/qa/runtime/user.rb | 4 | ||||
-rw-r--r-- | spec/features/runners_spec.rb | 6 | ||||
-rw-r--r-- | spec/helpers/search_helper_spec.rb | 19 |
21 files changed, 278 insertions, 74 deletions
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 0f4e5adca6c..c65611b7efc 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -34,15 +34,15 @@ module SearchHelper from: from, to: to, count: count, - scope: search_entries_info_label(scope, count), + scope: search_entries_scope_label(scope, count), term: term } end - def search_entries_info_label(scope, count) + def search_entries_scope_label(scope, count) case scope - when 'blobs', 'snippet_blobs', 'wiki_blobs' - ns_('SearchResults|result', 'SearchResults|results', count) + when 'blobs' + ns_('SearchResults|code result', 'SearchResults|code results', count) when 'commits' ns_('SearchResults|commit', 'SearchResults|commits', count) when 'issues' @@ -55,10 +55,14 @@ module SearchHelper ns_('SearchResults|comment', 'SearchResults|comments', count) when 'projects' ns_('SearchResults|project', 'SearchResults|projects', count) + when 'snippet_blobs' + ns_('SearchResults|snippet result', 'SearchResults|snippet results', count) when 'snippet_titles' ns_('SearchResults|snippet', 'SearchResults|snippets', count) when 'users' ns_('SearchResults|user', 'SearchResults|users', count) + when 'wiki_blobs' + ns_('SearchResults|wiki result', 'SearchResults|wiki results', count) else raise "Unrecognized search scope '#{scope}'" end @@ -72,6 +76,13 @@ module SearchHelper end end + def search_entries_empty_message(scope, term) + (s_("SearchResults|We couldn't find any %{scope} matching %{term}") % { + scope: search_entries_scope_label(scope, 0), + term: "<code>#{term}</code>" + }).html_safe + end + def find_project_for_result_blob(projects, result) @project end diff --git a/app/views/ci/runner/_how_to_setup_runner_automatically.html.haml b/app/views/ci/runner/_how_to_setup_runner_automatically.html.haml new file mode 100644 index 00000000000..58d2ef5d5e6 --- /dev/null +++ b/app/views/ci/runner/_how_to_setup_runner_automatically.html.haml @@ -0,0 +1,22 @@ +.append-bottom-10 + %h4= _('Set up a %{type} Runner automatically') % { type: type } + +%p + - link_to_help_page = link_to(_('Learn more about Kubernetes'), + help_page_path('user/project/clusters/index'), + target: '_blank', + rel: 'noopener noreferrer') + + = _('You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page } + +%ol + %li + = _('Click the button below to begin the install process by navigating to the Kubernetes page') + %li + = _('Select an existing Kubernetes cluster or create a new one') + %li + = _('From the Kubernetes cluster details view, install Runner from the applications list') + += link_to _('Install Runner on Kubernetes'), + clusters_path, + class: 'btn btn-info' diff --git a/app/views/groups/runners/_group_runners.html.haml b/app/views/groups/runners/_group_runners.html.haml index fd40ec5a984..f752bc0a702 100644 --- a/app/views/groups/runners/_group_runners.html.haml +++ b/app/views/groups/runners/_group_runners.html.haml @@ -10,6 +10,10 @@ -# Proper policies should be implemented per -# https://gitlab.com/gitlab-org/gitlab-foss/issues/45894 - if can?(current_user, :admin_pipeline, @group) + = render partial: 'ci/runner/how_to_setup_runner_automatically', + locals: { type: 'group', + clusters_path: group_clusters_path(@group) } + %hr = render partial: 'ci/runner/how_to_setup_runner', locals: { registration_token: @group.runners_token, type: 'group', diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index dc56a515d4c..4cc67a8f5d8 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -2,28 +2,9 @@ = _('Specific Runners') .bs-callout.help-callout - .append-bottom-10 - %h4= _('Set up a specific Runner automatically') - - %p - - link_to_help_page = link_to(_('Learn more about Kubernetes'), - help_page_path('user/project/clusters/index'), - target: '_blank', - rel: 'noopener noreferrer') - - = _('You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page } - - %ol - %li - = _('Click the button below to begin the install process by navigating to the Kubernetes page') - %li - = _('Select an existing Kubernetes cluster or create a new one') - %li - = _('From the Kubernetes cluster details view, install Runner from the applications list') - - = link_to _('Install Runner on Kubernetes'), - project_clusters_path(@project), - class: 'btn btn-info' + = render partial: 'ci/runner/how_to_setup_runner_automatically', + locals: { type: 'specific', + clusters_path: project_clusters_path(@project) } %hr = render partial: 'ci/runner/how_to_setup_runner', locals: { registration_token: @project.runners_token, diff --git a/app/views/search/results/_empty.html.haml b/app/views/search/results/_empty.html.haml index 9d15995bb51..6c7c6de1178 100644 --- a/app/views/search/results/_empty.html.haml +++ b/app/views/search/results/_empty.html.haml @@ -2,5 +2,4 @@ .search_glyph %h4 = icon('search') - = _("We couldn't find any results matching") - %code= @search_term + = search_entries_empty_message(@scope, @search_term) diff --git a/changelogs/unreleased/63768-group-settings-kubernetes-runner-section.yml b/changelogs/unreleased/63768-group-settings-kubernetes-runner-section.yml new file mode 100644 index 00000000000..ed374922632 --- /dev/null +++ b/changelogs/unreleased/63768-group-settings-kubernetes-runner-section.yml @@ -0,0 +1,5 @@ +--- +title: Add kubernetes section to group runner settings +merge_request: 16338 +author: +type: added diff --git a/changelogs/unreleased/improve-search-empty-message.yml b/changelogs/unreleased/improve-search-empty-message.yml new file mode 100644 index 00000000000..cc9692c4fd6 --- /dev/null +++ b/changelogs/unreleased/improve-search-empty-message.yml @@ -0,0 +1,5 @@ +--- +title: Use search scope label in empty results message +merge_request: 16324 +author: +type: changed diff --git a/doc/api/tags.md b/doc/api/tags.md index 3807688ffe3..56143969e3c 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -116,6 +116,12 @@ Parameters: - `message` (optional) - Creates annotated tag. - `release_description` (optional) - Add release notes to the Git tag and store it in the GitLab database. +```bash +curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/repository/tags?tag_name=test&ref=master" +``` + +Example response: + ```json { "commit": { diff --git a/doc/api/users.md b/doc/api/users.md index 380396c7377..251c11198e1 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -393,20 +393,21 @@ PUT /users/:id Parameters: -- `email` - Email -- `username` - Username -- `name` - Name -- `password` - Password -- `skype` - Skype ID -- `linkedin` - LinkedIn -- `twitter` - Twitter account -- `website_url` - Website URL -- `organization` - Organization name -- `projects_limit` - Limit projects each user can create -- `extern_uid` - External UID -- `provider` - External provider name +- `id` (required) - The ID of the user +- `email` (optional) - Email +- `username` (optional) - Username +- `name` (optional) - Name +- `password` (optional) - Password +- `skype` (optional) - Skype ID +- `linkedin` (optional) - LinkedIn +- `twitter` (optional) - Twitter account +- `website_url` (optional) - Website URL +- `organization` (optional) - Organization name +- `projects_limit` (optional) - Limit projects each user can create +- `extern_uid` (optional) - External UID +- `provider` (optional) - External provider name - `group_id_for_saml` (optional) - ID of group where SAML has been configured -- `bio` - User's biography +- `bio` (optional) - User's biography - `location` (optional) - User's location - `public_email` (optional) - The public email of the user - `admin` (optional) - User is admin - true or false (default) @@ -419,7 +420,7 @@ Parameters: - `private_profile` (optional) - User's profile is private - true or false (default) - `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user **(STARTER)** - `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **(STARTER)** -- `note` (optional) - Admin notes for this user **(STARTER)** +- `note` (optional) - Admin notes for this user **(STARTER)** On password update, user will be forced to change it upon next login. Note, at the moment this method does only return a `404` error, diff --git a/doc/development/documentation/improvement-workflow.md b/doc/development/documentation/improvement-workflow.md index 9f3b789712f..c8e9368fb74 100644 --- a/doc/development/documentation/improvement-workflow.md +++ b/doc/development/documentation/improvement-workflow.md @@ -28,7 +28,7 @@ to accomplish their work with GitLab. 1. Follow GitLab's [Merge Request Guidelines](../contributing/merge_request_workflow.md#merge-request-guidelines). If you need any help to choose the correct place for a doc, discuss a documentation -idea or outline, or request any other help, ping the Technical Writer for the relevant +idea or outline, or request any other help, ping the technical writer for the relevant [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages) in your issue or MR, or write within `#docs` if you are a member of GitLab's Slack workspace. @@ -39,7 +39,7 @@ This person must make a good-faith effort to ensure that the content is clear (sufficiently easy for the intended audience to navigate and understand) and that it meets the [Documentation Guidelines](index.md) and [Style Guide](styleguide.md). -If the author or reviewer has any questions, or would like a techncial writer's review +If the author or reviewer has any questions, or would like a technical writer's review before merging, mention the writer who is assigned to the relevant [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages). The process can involve the following parties/phases, and is replicated in the `Documentation` MR template for GitLab CE and EE, to help with following the process. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b72a5f45658..8aec921f9af 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -13557,6 +13557,14 @@ msgstr "" msgid "SearchResults|Showing %{from} - %{to} of %{count} %{scope} for \"%{term}\"" msgstr "" +msgid "SearchResults|We couldn't find any %{scope} matching %{term}" +msgstr "" + +msgid "SearchResults|code result" +msgid_plural "SearchResults|code results" +msgstr[0] "" +msgstr[1] "" + msgid "SearchResults|comment" msgid_plural "SearchResults|comments" msgstr[0] "" @@ -13587,13 +13595,13 @@ msgid_plural "SearchResults|projects" msgstr[0] "" msgstr[1] "" -msgid "SearchResults|result" -msgid_plural "SearchResults|results" +msgid "SearchResults|snippet" +msgid_plural "SearchResults|snippets" msgstr[0] "" msgstr[1] "" -msgid "SearchResults|snippet" -msgid_plural "SearchResults|snippets" +msgid "SearchResults|snippet result" +msgid_plural "SearchResults|snippet results" msgstr[0] "" msgstr[1] "" @@ -13602,6 +13610,11 @@ msgid_plural "SearchResults|users" msgstr[0] "" msgstr[1] "" +msgid "SearchResults|wiki result" +msgid_plural "SearchResults|wiki results" +msgstr[0] "" +msgstr[1] "" + msgid "Seats currently in use" msgstr "" @@ -14031,10 +14044,10 @@ msgstr "" msgid "Set up CI/CD" msgstr "" -msgid "Set up a %{type} Runner manually" +msgid "Set up a %{type} Runner automatically" msgstr "" -msgid "Set up a specific Runner automatically" +msgid "Set up a %{type} Runner manually" msgstr "" msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}" @@ -17519,9 +17532,6 @@ msgstr "" msgid "We could not determine the path to remove the issue" msgstr "" -msgid "We couldn't find any results matching" -msgstr "" - msgid "We created a short guided tour that will help you learn the basics of GitLab and how it will help you be better at your job. It should only take a couple of minutes. You will be guided by two types of helpers, best recognized by their color." msgstr "" @@ -23,6 +23,7 @@ module QA autoload :Feature, 'qa/runtime/feature' autoload :Fixtures, 'qa/runtime/fixtures' autoload :Logger, 'qa/runtime/logger' + autoload :GPG, 'qa/runtime/gpg' module API autoload :Client, 'qa/runtime/api/client' @@ -67,7 +68,9 @@ module QA autoload :Fork, 'qa/resource/fork' autoload :SSHKey, 'qa/resource/ssh_key' autoload :Snippet, 'qa/resource/snippet' + autoload :Tag, 'qa/resource/tag' autoload :ProjectMember, 'qa/resource/project_member' + autoload :UserGPG, 'qa/resource/user_gpg' module Events autoload :Base, 'qa/resource/events/base' diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb index 24b9fc67dd9..09052a5e33f 100644 --- a/qa/qa/git/repository.rb +++ b/qa/qa/git/repository.rb @@ -14,7 +14,7 @@ module QA include Scenario::Actable RepositoryCommandError = Class.new(StandardError) - attr_writer :use_lfs + attr_writer :use_lfs, :gpg_key_id attr_accessor :env_vars InvalidCredentialsError = Class.new(RuntimeError) @@ -25,6 +25,7 @@ module QA # .netrc can be utilised self.env_vars = [%Q{HOME="#{tmp_home_dir}"}] @use_lfs = false + @gpg_key_id = nil end def self.perform(*args) @@ -99,10 +100,18 @@ module QA git_lfs_track_result.to_s + git_add_result.to_s end + def delete_tag(tag_name) + run(%Q{git push origin --delete #{tag_name}}).to_s + end + def commit(message) run(%Q{git commit -m "#{message}"}).to_s end + def commit_with_gpg(message) + run(%Q{git config user.signingkey #{@gpg_key_id} && git config gpg.program $(which gpg) && git commit -S -m "#{message}"}).to_s + end + def push_changes(branch = 'master') run("git push #{uri} #{branch}").to_s end diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb index 437a945aceb..506e70a180c 100644 --- a/qa/qa/page/project/settings/repository.rb +++ b/qa/qa/page/project/settings/repository.rb @@ -47,3 +47,5 @@ module QA end end end + +QA::Page::Project::Settings::Repository.prepend_if_ee('QA::EE::Page::Project::Settings::Repository') diff --git a/qa/qa/resource/repository/push.rb b/qa/qa/resource/repository/push.rb index a5827fb6e73..68674248be2 100644 --- a/qa/qa/resource/repository/push.rb +++ b/qa/qa/resource/repository/push.rb @@ -8,9 +8,9 @@ module QA class Push < Base attr_accessor :file_name, :file_content, :commit_message, :branch_name, :new_branch, :output, :repository_http_uri, - :repository_ssh_uri, :ssh_key, :user, :use_lfs + :repository_ssh_uri, :ssh_key, :user, :use_lfs, :tag_name - attr_writer :remote_branch + attr_writer :remote_branch, :gpg_key_id def initialize @file_name = 'file.txt' @@ -21,6 +21,8 @@ module QA @repository_http_uri = "" @ssh_key = nil @use_lfs = false + @tag_name = nil + @gpg_key_id = nil end def remote_branch @@ -67,29 +69,43 @@ module QA email = user.email end + unless @gpg_key_id.nil? + repository.gpg_key_id = @gpg_key_id + end + @output += repository.clone repository.configure_identity(username, email) @output += repository.checkout(branch_name, new_branch: new_branch) - if @directory - @directory.each_child do |f| - @output += repository.add_file(f.basename, f.read) if f.file? - end - elsif @files - @files.each do |f| - repository.add_file(f[:name], f[:content]) - end + if @tag_name + @output += repository.delete_tag(@tag_name) else - @output += repository.add_file(file_name, file_content) - end + if @directory + @directory.each_child do |f| + @output += repository.add_file(f.basename, f.read) if f.file? + end + elsif @files + @files.each do |f| + repository.add_file(f[:name], f[:content]) + end + else + @output += repository.add_file(file_name, file_content) + end - @output += repository.commit(commit_message) - @output += repository.push_changes("#{branch_name}:#{remote_branch}") + @output += commit_to repository + @output += repository.push_changes("#{branch_name}:#{remote_branch}") + end repository.delete_ssh_key end end + + private + + def commit_to(repository) + @gpg_key_id.nil? ? repository.commit(@commit_message) : repository.commit_with_gpg(@commit_message) + end end end end diff --git a/qa/qa/resource/tag.rb b/qa/qa/resource/tag.rb new file mode 100644 index 00000000000..ac4fccec525 --- /dev/null +++ b/qa/qa/resource/tag.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module QA + module Resource + class Tag < Base + attr_accessor :project, :name, :ref + + def resource_web_url(resource) + super + rescue ResourceURLMissingError + # this particular resource does not expose a web_url property + end + + def api_get_path + "/projects/#{project.id}/repository/tags/#{name}" + end + + def api_post_path + "/projects/#{project.id}/repository/tags" + end + + def api_post_body + { + tag_name: name, + ref: ref + } + end + end + end +end diff --git a/qa/qa/resource/user_gpg.rb b/qa/qa/resource/user_gpg.rb new file mode 100644 index 00000000000..25d74ad8ce5 --- /dev/null +++ b/qa/qa/resource/user_gpg.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module QA + module Resource + class UserGPG < Base + attr_accessor :id, :gpg + attr_reader :key_id + + def initialize + @gpg = Runtime::GPG.new + @key_id = @gpg.key_id + end + + def fabricate_via_api! + super + @id = self.api_response[:id] + rescue ResourceFabricationFailedError => error + if error.message.include? 'has already been taken' + self + else + raise ResourceFabricationFailedError error + end + end + + def resource_web_url(resource) + super + rescue ResourceURLMissingError + # this particular resource does not expose a web_url property + end + + def api_get_path + "/user/gpg_keys/#{@id}" + end + + def api_post_path + '/user/gpg_keys' + end + + def api_post_body + { + key: @gpg.key + } + end + end + end +end diff --git a/qa/qa/runtime/gpg.rb b/qa/qa/runtime/gpg.rb new file mode 100644 index 00000000000..9f8baf7e580 --- /dev/null +++ b/qa/qa/runtime/gpg.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module QA + module Runtime + class GPG + attr_reader :key, :key_id + + def initialize + @key_id = 'B8358D73048DACC4' + import_key(File.expand_path('qa/ee/fixtures/gpg/admin.asc')) + @key = collect_key.first + end + + private + + def import_key(path) + import_key = "gpg --import #{path}" + execute(import_key) + end + + def collect_key + get_ascii_format = "gpg --armor --export #{@key_id}" + execute(get_ascii_format) + end + + def execute(command) + Open3.capture2e(*command) do |stdin, out, wait| + out.each_char { |char| print char } + + if wait.value.exited? && wait.value.exitstatus.nonzero? + raise CommandError, "Command `#{command}` failed!" + end + end + end + end + end +end diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb index 011e4a548a5..3c26a3ad691 100644 --- a/qa/qa/runtime/user.rb +++ b/qa/qa/runtime/user.rb @@ -25,6 +25,10 @@ module QA Runtime::Env.user_password || default_password end + def email + default_email + end + def ldap_user? Runtime::Env.ldap_username && Runtime::Env.ldap_password end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 63d21d94b5f..0049d3ca7c9 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -272,6 +272,12 @@ describe 'Runners' do expect(page).to have_content 'This group does not provide any group Runners yet' end + + it 'user can see a link to install runners on kubernetes clusters' do + visit group_settings_ci_cd_path(group) + + expect(page).to have_link('Install Runner on Kubernetes', href: group_clusters_path(group)) + end end context 'group with a runner' do diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index e1dc589236b..cc058d5b983 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -103,19 +103,17 @@ describe SearchHelper do using RSpec::Parameterized::TableSyntax where(:scope, :label) do + 'blobs' | 'code result' 'commits' | 'commit' 'issues' | 'issue' 'merge_requests' | 'merge request' 'milestones' | 'milestone' + 'notes' | 'comment' 'projects' | 'project' + 'snippet_blobs' | 'snippet result' 'snippet_titles' | 'snippet' 'users' | 'user' - - 'blobs' | 'result' - 'snippet_blobs' | 'result' - 'wiki_blobs' | 'result' - - 'notes' | 'comment' + 'wiki_blobs' | 'wiki result' end with_them do @@ -140,6 +138,15 @@ describe SearchHelper do end end + describe 'search_entries_empty_message' do + it 'returns the formatted entry message' do + message = search_entries_empty_message('projects', 'foo') + + expect(message).to eq("We couldn't find any projects matching <code>foo</code>") + expect(message).to be_html_safe + end + end + describe 'search_filter_input_options' do context 'project' do before do |