diff options
author | Fabio Papa <fabtheman@gmail.com> | 2019-07-12 10:37:24 -0700 |
---|---|---|
committer | Fabio Papa <fabtheman@gmail.com> | 2019-07-12 10:37:24 -0700 |
commit | 6c51aadf35c4e7899da99c85c3fc4c01484819f2 (patch) | |
tree | c683e3d7ebcf3e188f1dd85b8701ff972e7f8a3d | |
parent | 9b176c65159e4186f79eae2107af80e69132ba09 (diff) | |
parent | 6457d5edb7d66df5dd3d5ba1f1ea0c56a59287a8 (diff) | |
download | gitlab-ce-6c51aadf35c4e7899da99c85c3fc4c01484819f2.tar.gz |
Merge branch 'maintainers-can-create-subgroup' of https://gitlab.com/fapapa/gitlab-ce into maintainers-can-create-subgroup
1176 files changed, 17452 insertions, 8136 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml index 2b881d5f201..2612fd3371d 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -5,6 +5,7 @@ globals: gl: false gon: false localStorage: false + IS_EE: false plugins: - import - html diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml index 6acbce6cf0c..5bc109f2b7f 100644 --- a/.gitlab/ci/docs.gitlab-ci.yml +++ b/.gitlab/ci/docs.gitlab-ci.yml @@ -66,9 +66,7 @@ docs lint: - mv doc/ /tmp/gitlab-docs/content/$DOCS_GITLAB_REPO_SUFFIX - cd /tmp/gitlab-docs # Lint Markdown - # https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md - - bundle exec mdl content/$DOCS_GITLAB_REPO_SUFFIX/**/*.md --ignore-front-matter --rules \ - MD004,MD032,MD034 + - bundle exec mdl content/$DOCS_GITLAB_REPO_SUFFIX -c $CI_PROJECT_DIR/.mdlrc # Build HTML from Markdown - bundle exec nanoc # Check the internal links diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 01c96e06547..c7a51beeee5 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -66,6 +66,8 @@ - scripts/gitaly-test-spawn - date - 'export KNAPSACK_TEST_FILE_PATTERN=$(ruby -r./lib/quality/test_level.rb -e "puts Quality::TestLevel.new.pattern(:${TEST_LEVEL})")' + - mkdir -p tmp/memory_test + - export MEMORY_TEST_PATH="tmp/memory_test/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_memory.csv" - knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml --tag level:${TEST_LEVEL} --tag ~geo" - date artifacts: @@ -77,6 +79,7 @@ - rspec_flaky/ - rspec_profiling/ - tmp/capybara/ + - tmp/memory_test/ # reports: # junit: junit_rspec.xml @@ -139,7 +142,7 @@ setup-test-env: rspec unit pg: <<: *rspec-metadata-pg - parallel: 25 + parallel: 20 rspec integration pg: <<: *rspec-metadata-pg @@ -152,7 +155,7 @@ rspec system pg: rspec unit pg-10: <<: *rspec-metadata-pg-10 <<: *only-schedules-master - parallel: 25 + parallel: 20 rspec integration pg-10: <<: *rspec-metadata-pg-10 @@ -273,6 +276,7 @@ coverage: stage: post-test script: - bundle exec scripts/merge-simplecov + - bundle exec scripts/gather-test-memory-data coverage: '/LOC \((\d+\.\d+%)\) covered.$/' artifacts: name: coverage @@ -280,3 +284,4 @@ coverage: paths: - coverage/index.html - coverage/assets/ + - tmp/memory_test/ diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml index 89b5ae38072..ca55bbd32a7 100644 --- a/.gitlab/ci/reports.gitlab-ci.yml +++ b/.gitlab/ci/reports.gitlab-ci.yml @@ -10,17 +10,17 @@ code_quality: tags: [] before_script: [] cache: {} - dependencies: [] sast: extends: .dedicated-no-docs - before_script: [] tags: [] + before_script: [] + cache: {} variables: - SAST_CONFIDENCE_LEVEL: 2 - DOCKER_DRIVER: overlay2 + SAST_BRAKEMAN_LEVEL: 2 dependency_scanning: - before_script: [] - tags: [] extends: .dedicated-no-docs + tags: [] + before_script: [] + cache: {} diff --git a/.gitlab/issue_templates/Documentation.md b/.gitlab/issue_templates/Documentation.md index 67602b7b2df..f4070a44755 100644 --- a/.gitlab/issue_templates/Documentation.md +++ b/.gitlab/issue_templates/Documentation.md @@ -4,7 +4,7 @@ Note: Doc work as part of feature development is covered in the Feature Request template. * For issues related to features of the docs.gitlab.com site, see - https://gitlab.com/gitlab-com/gitlab-docs/issues/ + https://gitlab.com/gitlab-org/gitlab-docs/issues/ * For information about documentation content and process, see https://docs.gitlab.com/ee/development/documentation/ --> diff --git a/.mdlrc b/.mdlrc new file mode 100644 index 00000000000..b2127dadc22 --- /dev/null +++ b/.mdlrc @@ -0,0 +1,4 @@ +# See https://github.com/markdownlint/markdownlint/blob/master/docs/configuration.md + +ignore_front_matter true +style File.expand_path('.mdlrc.style', __dir__) diff --git a/.mdlrc.style b/.mdlrc.style new file mode 100644 index 00000000000..30abf03f462 --- /dev/null +++ b/.mdlrc.style @@ -0,0 +1,7 @@ +# See https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md + +rule 'MD001' +# False positives, see https://github.com/markdownlint/markdownlint/issues/261 +# rule 'MD004', style: :dash +rule 'MD032' +rule 'MD034' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index ba0a719118c..3f4830156cb 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -1.51.0 +1.53.0 diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 9c6d6293b1a..bd8bf882d06 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -1.6.1 +1.7.0 @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'rails', '5.1.7' +gem 'rails', '5.2.3' # Improves copy-on-write performance for MRI gem 'nakayoshi_fork', '~> 0.0.4' @@ -84,6 +84,7 @@ gem 'rack-cors', '~> 1.0.0', require: 'rack/cors' gem 'graphql', '~> 1.8.0' gem 'graphiql-rails', '~> 1.4.10' gem 'apollo_upload_server', '~> 2.0.0.beta3' +gem 'graphql-docs', '~> 1.6.0', group: [:development, :test] # Disable strong_params so that Mash does not respond to :permitted? gem 'hashie-forbidden_attributes' @@ -132,7 +133,7 @@ gem 'wikicloth', '0.8.1' gem 'asciidoctor', '~> 2.0.10' gem 'asciidoctor-include-ext', '~> 0.3.1', require: false gem 'asciidoctor-plantuml', '0.0.9' -gem 'rouge', '~> 3.1' +gem 'rouge', '~> 3.5' gem 'truncato', '~> 0.7.11' gem 'bootstrap_form', '~> 4.2.0' gem 'nokogiri', '~> 1.10.3' @@ -309,7 +310,7 @@ group :metrics do gem 'influxdb', '~> 0.2', require: false # Prometheus - gem 'prometheus-client-mmap', '~> 0.9.6' + gem 'prometheus-client-mmap', '~> 0.9.8' gem 'raindrops', '~> 0.18' end @@ -339,7 +340,7 @@ group :development, :test do gem 'database_cleaner', '~> 1.7.0' gem 'factory_bot_rails', '~> 4.8.2' - gem 'rspec-rails', '~> 3.7.0' + gem 'rspec-rails', '~> 3.8.0' gem 'rspec-retry', '~> 0.6.1' gem 'rspec_profiling', '~> 0.0.5' gem 'rspec-set', '~> 0.1.3' @@ -368,6 +369,7 @@ group :development, :test do gem 'haml_lint', '~> 0.31.0', require: false gem 'simplecov', '~> 0.16.1', require: false gem 'bundler-audit', '~> 0.5.0', require: false + gem 'mdl', '~> 0.5.0', require: false gem 'benchmark-ips', '~> 2.3.0', require: false @@ -419,7 +421,7 @@ gem 'vmstat', '~> 2.3.0' gem 'sys-filesystem', '~> 1.1.6' # SSH host key support -gem 'net-ssh', '~> 5.0' +gem 'net-ssh', '~> 5.2' gem 'sshkey', '~> 2.0' # Required for ED25519 SSH host key support diff --git a/Gemfile.lock b/Gemfile.lock index 5099167e03f..bf469de3835 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,44 +6,48 @@ GEM ace-rails-ap (4.1.2) acme-client (2.0.2) faraday (~> 0.9, >= 0.9.1) - actioncable (5.1.7) - actionpack (= 5.1.7) + actioncable (5.2.3) + actionpack (= 5.2.3) nio4r (~> 2.0) - websocket-driver (~> 0.6.1) - actionmailer (5.1.7) - actionpack (= 5.1.7) - actionview (= 5.1.7) - activejob (= 5.1.7) + websocket-driver (>= 0.6.1) + actionmailer (5.2.3) + actionpack (= 5.2.3) + actionview (= 5.2.3) + activejob (= 5.2.3) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.1.7) - actionview (= 5.1.7) - activesupport (= 5.1.7) + actionpack (5.2.3) + actionview (= 5.2.3) + activesupport (= 5.2.3) rack (~> 2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.1.7) - activesupport (= 5.1.7) + actionview (5.2.3) + activesupport (= 5.2.3) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.1.7) - activesupport (= 5.1.7) + activejob (5.2.3) + activesupport (= 5.2.3) globalid (>= 0.3.6) - activemodel (5.1.7) - activesupport (= 5.1.7) - activerecord (5.1.7) - activemodel (= 5.1.7) - activesupport (= 5.1.7) - arel (~> 8.0) + activemodel (5.2.3) + activesupport (= 5.2.3) + activerecord (5.2.3) + activemodel (= 5.2.3) + activesupport (= 5.2.3) + arel (>= 9.0) activerecord-explain-analyze (0.1.0) activerecord (>= 4) pg activerecord_sane_schema_dumper (1.0) rails (>= 5, < 6) - activesupport (5.1.7) + activestorage (5.2.3) + actionpack (= 5.2.3) + activerecord (= 5.2.3) + marcel (~> 0.3.1) + activesupport (5.2.3) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -60,7 +64,7 @@ GEM apollo_upload_server (2.0.0.beta.3) graphql (>= 1.8) rails (>= 4.2) - arel (8.0.0) + arel (9.0.0) asana (0.8.1) faraday (~> 0.9) faraday_middleware (~> 0.9) @@ -216,6 +220,8 @@ GEM excon (0.62.0) execjs (2.6.0) expression_parser (0.9.0) + extended-markdown-filter (0.6.0) + html-pipeline (~> 2.0) factory_bot (4.8.2) activesupport (>= 3.0.0) factory_bot_rails (4.8.2) @@ -290,6 +296,7 @@ GEM fuubar (2.2.0) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) + gemoji (3.0.1) gemojione (3.3.0) json get_process_mem (0.2.3) @@ -370,6 +377,14 @@ GEM railties sprockets-rails graphql (1.8.1) + graphql-docs (1.6.0) + commonmarker (~> 0.16) + escape_utils (~> 1.2) + extended-markdown-filter (~> 0.4) + gemoji (~> 3.0) + graphql (~> 1.6) + html-pipeline (~> 2.8) + sass (~> 3.4) grpc (1.19.0) google-protobuf (~> 3.1) googleapis-common-protos-types (~> 1.0.0) @@ -459,6 +474,7 @@ GEM kgio (2.11.2) knapsack (1.17.0) rake + kramdown (1.17.0) kubeclient (4.2.2) http (~> 3.0) recursive-open-struct (~> 1.0, >= 1.0.4) @@ -492,6 +508,12 @@ GEM mail (2.7.1) mini_mime (>= 0.1.1) mail_room (0.9.1) + marcel (0.3.3) + mimemagic (~> 0.3.2) + mdl (0.5.0) + kramdown (~> 1.12, >= 1.12.0) + mixlib-cli (~> 1.7, >= 1.7.0) + mixlib-config (~> 2.2, >= 2.2.1) memoist (0.16.0) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) @@ -505,6 +527,9 @@ GEM mini_mime (1.0.1) mini_portile2 (2.4.0) minitest (5.11.3) + mixlib-cli (1.7.0) + mixlib-config (2.2.18) + tomlrb msgpack (1.2.10) multi_json (1.13.1) multi_xml (0.6.0) @@ -515,7 +540,7 @@ GEM mysql2 (0.4.10) nakayoshi_fork (0.0.4) net-ldap (0.16.0) - net-ssh (5.0.1) + net-ssh (5.2.0) netrc (0.11.0) nio4r (2.3.1) nokogiri (1.10.3) @@ -652,7 +677,7 @@ GEM parser unparser procto (0.0.3) - prometheus-client-mmap (0.9.6) + prometheus-client-mmap (0.9.8) pry (0.11.3) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -687,17 +712,18 @@ GEM rack-test (1.1.0) rack (>= 1.0, < 3) rack-timeout (0.5.1) - rails (5.1.7) - actioncable (= 5.1.7) - actionmailer (= 5.1.7) - actionpack (= 5.1.7) - actionview (= 5.1.7) - activejob (= 5.1.7) - activemodel (= 5.1.7) - activerecord (= 5.1.7) - activesupport (= 5.1.7) + rails (5.2.3) + actioncable (= 5.2.3) + actionmailer (= 5.2.3) + actionpack (= 5.2.3) + actionview (= 5.2.3) + activejob (= 5.2.3) + activemodel (= 5.2.3) + activerecord (= 5.2.3) + activestorage (= 5.2.3) + activesupport (= 5.2.3) bundler (>= 1.3.0) - railties (= 5.1.7) + railties (= 5.2.3) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -711,12 +737,12 @@ GEM rails-i18n (5.1.1) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) - railties (5.1.7) - actionpack (= 5.1.7) - activesupport (= 5.1.7) + railties (5.2.3) + actionpack (= 5.2.3) + activesupport (= 5.2.3) method_source rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) + thor (>= 0.19.0, < 2.0) rainbow (3.0.0) raindrops (0.19.0) rake (12.3.2) @@ -770,41 +796,41 @@ GEM retriable (3.1.2) rinku (2.0.0) rotp (2.1.2) - rouge (3.4.1) + rouge (3.5.1) rqrcode (0.7.0) chunky_png rqrcode-rails3 (0.1.7) rqrcode (>= 0.4.2) - rspec (3.7.0) - rspec-core (~> 3.7.0) - rspec-expectations (~> 3.7.0) - rspec-mocks (~> 3.7.0) - rspec-core (3.7.1) - rspec-support (~> 3.7.0) - rspec-expectations (3.7.0) + rspec (3.8.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-core (3.8.2) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.4) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) - rspec-mocks (3.7.0) + rspec-support (~> 3.8.0) + rspec-mocks (3.8.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) + rspec-support (~> 3.8.0) rspec-parameterized (0.4.2) binding_ninja (>= 0.2.3) parser proc_to_ast rspec (>= 2.13, < 4) unparser - rspec-rails (3.7.2) + rspec-rails (3.8.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.7.0) - rspec-expectations (~> 3.7.0) - rspec-mocks (~> 3.7.0) - rspec-support (~> 3.7.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-support (~> 3.8.0) rspec-retry (0.6.1) rspec-core (> 3.3) rspec-set (0.1.3) - rspec-support (3.7.1) + rspec-support (3.8.2) rspec_junit_formatter (0.4.1) rspec-core (>= 2, < 4, != 2.12.0) rspec_profiling (0.0.5) @@ -943,6 +969,7 @@ GEM parslet (~> 1.8.0) toml-rb (1.0.0) citrus (~> 3.0, > 3.0) + tomlrb (1.2.8) truncato (0.7.11) htmlentities (~> 4.3.1) nokogiri (>= 1.7.0, <= 2.0) @@ -999,7 +1026,7 @@ GEM hashdiff webpack-rails (0.9.11) railties (>= 3.2.0) - websocket-driver (0.6.5) + websocket-driver (0.7.0) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) wikicloth (0.8.1) @@ -1109,6 +1136,7 @@ DEPENDENCIES grape_logging (~> 1.7) graphiql-rails (~> 1.4.10) graphql (~> 1.8.0) + graphql-docs (~> 1.6.0) grpc (~> 1.19.0) haml_lint (~> 0.31.0) hamlit (~> 2.8.8) @@ -1134,6 +1162,7 @@ DEPENDENCIES lograge (~> 0.5) loofah (~> 2.2) mail_room (~> 0.9.1) + mdl (~> 0.5.0) memory_profiler (~> 0.9) method_source (~> 0.8) mimemagic (~> 0.3.2) @@ -1142,7 +1171,7 @@ DEPENDENCIES mysql2 (~> 0.4.10) nakayoshi_fork (~> 0.0.4) net-ldap - net-ssh (~> 5.0) + net-ssh (~> 5.2) nokogiri (~> 1.10.3) oauth2 (~> 1.4) octokit (~> 4.9) @@ -1173,7 +1202,7 @@ DEPENDENCIES peek-redis (~> 1.2.0) pg (~> 1.1) premailer-rails (~> 1.9.7) - prometheus-client-mmap (~> 0.9.6) + prometheus-client-mmap (~> 0.9.8) pry-byebug (~> 3.5.1) pry-rails (~> 0.3.4) puma (~> 3.12) @@ -1184,7 +1213,7 @@ DEPENDENCIES rack-oauth2 (~> 1.9.3) rack-proxy (~> 0.6.0) rack-timeout - rails (= 5.1.7) + rails (= 5.2.3) rails-controller-testing rails-i18n (~> 5.1) rainbow (~> 3.0) @@ -1199,10 +1228,10 @@ DEPENDENCIES redis-rails (~> 5.0.2) request_store (~> 1.3) responders (~> 2.0) - rouge (~> 3.1) + rouge (~> 3.5) rqrcode-rails3 (~> 0.1.7) rspec-parameterized - rspec-rails (~> 3.7.0) + rspec-rails (~> 3.8.0) rspec-retry (~> 0.6.1) rspec-set (~> 0.1.3) rspec_junit_formatter diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 4f66a5d080c..a649c521405 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -24,6 +24,7 @@ const Api = { issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key', projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key', projectTemplatesPath: '/api/:version/projects/:id/templates/:type', + userCountsPath: '/api/:version/user_counts', usersPath: '/api/:version/users.json', userPath: '/api/:version/users/:id', userStatusPath: '/api/:version/users/:id/status', @@ -312,6 +313,11 @@ const Api = { }); }, + userCounts() { + const url = Api.buildUrl(this.userCountsPath); + return axios.get(url); + }, + userStatus(id, options) { const url = Api.buildUrl(this.userStatusPath).replace(':id', encodeURIComponent(id)); return axios.get(url, { diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue index f58149c9f7b..d8b0b60c183 100644 --- a/app/assets/javascripts/boards/components/board_blank_state.vue +++ b/app/assets/javascripts/boards/components/board_blank_state.vue @@ -61,7 +61,7 @@ export default { <div class="board-blank-state p-3"> <p> {{ - __('BoardBlankState|Add the following default lists to your Issue Board with one click:') + s__('BoardBlankState|Add the following default lists to your Issue Board with one click:') }} </p> <ul class="list-unstyled board-blank-state-list"> @@ -76,7 +76,7 @@ export default { </ul> <p> {{ - __( + s__( 'BoardBlankState|Starting out with the default set of lists will get you right on the way to making the most of your board.', ) }} @@ -86,10 +86,10 @@ export default { type="button" @click.stop="addDefaultLists" > - {{ __('BoardBlankState|Add default lists') }} + {{ s__('BoardBlankState|Add default lists') }} </button> <button class="btn btn-default btn-block" type="button" @click.stop="clearBlankState"> - {{ __("BoardBlankState|Nevermind, I'll use my own") }} + {{ s__("BoardBlankState|Nevermind, I'll use my own") }} </button> </div> </template> diff --git a/app/assets/javascripts/boards/components/modal/footer.vue b/app/assets/javascripts/boards/components/modal/footer.vue index a1d634c8f19..eea0fb71c1a 100644 --- a/app/assets/javascripts/boards/components/modal/footer.vue +++ b/app/assets/javascripts/boards/components/modal/footer.vue @@ -41,7 +41,7 @@ export default { const req = this.buildUpdateRequest(list); // Post the data to the backend - gl.boardService.bulkUpdate(issueIds, req).catch(() => { + boardsStore.bulkUpdate(issueIds, req).catch(() => { Flash(__('Failed to update issues, please try again.')); selectedIssues.forEach(issue => { diff --git a/app/assets/javascripts/boards/config_toggle.js b/app/assets/javascripts/boards/config_toggle.js new file mode 100644 index 00000000000..2d1ec238274 --- /dev/null +++ b/app/assets/javascripts/boards/config_toggle.js @@ -0,0 +1 @@ +export default () => {}; diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index 6b54e8baefb..b1b4b1c5508 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -2,7 +2,6 @@ import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable import FilteredSearchContainer from '../filtered_search/container'; import FilteredSearchManager from '../filtered_search/filtered_search_manager'; import boardsStore from './stores/boards_store'; -import { isEE } from '~/lib/utils/common_utils'; export default class FilteredSearchBoards extends FilteredSearchManager { constructor(store, updateUrl = false, cantEdit = []) { @@ -10,7 +9,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager { page: 'boards', isGroupDecendent: true, stateFiltersSelector: '.issues-state-filters', - isGroup: isEE(), + isGroup: IS_EE, filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys, }); diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 23b107abefa..d6a5372b22d 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -7,28 +7,30 @@ import { __ } from '~/locale'; import './models/label'; import './models/assignee'; -import FilteredSearchBoards from './filtered_search_boards'; -import eventHub from './eventhub'; +import FilteredSearchBoards from '~/boards/filtered_search_boards'; +import eventHub from '~/boards/eventhub'; import sidebarEventHub from '~/sidebar/event_hub'; -import './models/issue'; -import './models/list'; -import './models/milestone'; -import './models/project'; -import boardsStore from './stores/boards_store'; -import ModalStore from './stores/modal_store'; -import BoardService from './services/board_service'; -import modalMixin from './mixins/modal_mixins'; -import './filters/due_date_filters'; -import Board from './components/board'; -import BoardSidebar from './components/board_sidebar'; +import 'ee_else_ce/boards/models/issue'; +import 'ee_else_ce/boards/models/list'; +import '~/boards/models/milestone'; +import '~/boards/models/project'; +import boardsStore from '~/boards/stores/boards_store'; +import ModalStore from '~/boards/stores/modal_store'; +import BoardService from 'ee_else_ce/boards/services/board_service'; +import modalMixin from '~/boards/mixins/modal_mixins'; +import '~/boards/filters/due_date_filters'; +import Board from 'ee_else_ce/boards/components/board'; +import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar'; import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown'; -import BoardAddIssuesModal from './components/modal/index.vue'; +import BoardAddIssuesModal from '~/boards/components/modal/index.vue'; import '~/vue_shared/vue_resource_interceptor'; import { NavigationType, convertObjectPropsToCamelCase, parseBoolean, } from '~/lib/utils/common_utils'; +import boardConfigToggle from 'ee_else_ce/boards/config_toggle'; +import toggleFocusMode from 'ee_else_ce/boards/toggle_focus'; let issueBoardsApp; @@ -207,6 +209,8 @@ export default () => { }, }); + boardConfigToggle(boardsStore); + const issueBoardsModal = document.getElementById('js-add-issues-btn'); if (issueBoardsModal) { @@ -281,5 +285,6 @@ export default () => { }); } + toggleFocusMode(ModalStore, boardsStore); mountMultipleBoardsSwitcher(); }; diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js index f858b162c6b..9069b35db9a 100644 --- a/app/assets/javascripts/boards/models/issue.js +++ b/app/assets/javascripts/boards/models/issue.js @@ -5,7 +5,7 @@ import Vue from 'vue'; import './label'; -import { isEE, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import IssueProject from './project'; import boardsStore from '../stores/boards_store'; @@ -91,13 +91,13 @@ class ListIssue { addMilestone(milestone) { const miletoneId = this.milestone ? this.milestone.id : null; - if (isEE && milestone.id !== miletoneId) { + if (IS_EE && milestone.id !== miletoneId) { this.milestone = new ListMilestone(milestone); } } removeMilestone(removeMilestone) { - if (isEE && removeMilestone && removeMilestone.id === this.milestone.id) { + if (IS_EE && removeMilestone && removeMilestone.id === this.milestone.id) { this.milestone = {}; } } diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index cd553d0c4af..7e0ccb9bd2a 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -4,7 +4,7 @@ import { __ } from '~/locale'; import ListLabel from './label'; import ListAssignee from './assignee'; -import { isEE, urlParamsToObject } from '~/lib/utils/common_utils'; +import { urlParamsToObject } from '~/lib/utils/common_utils'; import boardsStore from '../stores/boards_store'; import ListMilestone from './milestone'; @@ -58,7 +58,7 @@ class List { } else if (obj.user) { this.assignee = new ListAssignee(obj.user); this.title = this.assignee.name; - } else if (isEE && obj.milestone) { + } else if (IS_EE && obj.milestone) { this.milestone = new ListMilestone(obj.milestone); this.title = this.milestone.title; } @@ -85,7 +85,7 @@ class List { entityType = 'label_id'; } else if (this.assignee) { entityType = 'assignee_id'; - } else if (isEE && this.milestone) { + } else if (IS_EE && this.milestone) { entityType = 'milestone_id'; } @@ -205,7 +205,7 @@ class List { issue.addAssignee(this.assignee); } - if (isEE && this.milestone) { + if (IS_EE && this.milestone) { if (listFrom && listFrom.type === 'milestone') { issue.removeMilestone(listFrom.milestone); } diff --git a/app/assets/javascripts/boards/models/milestone.js b/app/assets/javascripts/boards/models/milestone.js index 6f81d6bc6f8..7201b6e91f5 100644 --- a/app/assets/javascripts/boards/models/milestone.js +++ b/app/assets/javascripts/boards/models/milestone.js @@ -1,11 +1,9 @@ -import { isEE } from '~/lib/utils/common_utils'; - export default class ListMilestone { constructor(obj) { this.id = obj.id; this.title = obj.title; - if (isEE) { + if (IS_EE) { this.path = obj.path; this.state = obj.state; this.webUrl = obj.web_url || obj.webUrl; diff --git a/app/assets/javascripts/boards/toggle_focus.js b/app/assets/javascripts/boards/toggle_focus.js new file mode 100644 index 00000000000..2d1ec238274 --- /dev/null +++ b/app/assets/javascripts/boards/toggle_focus.js @@ -0,0 +1 @@ +export default () => {}; diff --git a/app/assets/javascripts/commons/index.js b/app/assets/javascripts/commons/index.js index 0d2fe2925d8..ad0f6cc1496 100644 --- a/app/assets/javascripts/commons/index.js +++ b/app/assets/javascripts/commons/index.js @@ -4,3 +4,6 @@ import './jquery'; import './bootstrap'; import './vue'; import '../lib/utils/axios_utils'; +import { openUserCountsBroadcast } from './nav/user_merge_requests'; + +openUserCountsBroadcast(); diff --git a/app/assets/javascripts/commons/nav/user_merge_requests.js b/app/assets/javascripts/commons/nav/user_merge_requests.js new file mode 100644 index 00000000000..8e694cca6a1 --- /dev/null +++ b/app/assets/javascripts/commons/nav/user_merge_requests.js @@ -0,0 +1,67 @@ +import Api from '~/api'; + +let channel; + +function broadcastCount(newCount) { + if (!channel) { + return; + } + + channel.postMessage(newCount); +} + +function updateUserMergeRequestCounts(newCount) { + const mergeRequestsCountEl = document.querySelector('.merge-requests-count'); + mergeRequestsCountEl.textContent = newCount.toLocaleString(); + mergeRequestsCountEl.classList.toggle('hidden', Number(newCount) === 0); +} + +/** + * Refresh user counts (and broadcast if open) + */ +export function refreshUserMergeRequestCounts() { + return Api.userCounts() + .then(({ data }) => { + const count = data.merge_requests; + + updateUserMergeRequestCounts(count); + broadcastCount(count); + }) + .catch(ex => { + console.error(ex); // eslint-disable-line no-console + }); +} + +/** + * Close the broadcast channel for user counts + */ +export function closeUserCountsBroadcast() { + if (!channel) { + return; + } + + channel.close(); + channel = null; +} + +/** + * Open the broadcast channel for user counts, adds user id so we only update + * + * **Please note:** + * Not supported in all browsers, but not polyfilling for now + * to keep bundle size small and + * no special functionality lost except cross tab notifications + */ +export function openUserCountsBroadcast() { + closeUserCountsBroadcast(); + + if (window.BroadcastChannel) { + const currentUserId = typeof gon !== 'undefined' && gon && gon.current_user_id; + if (currentUserId) { + channel = new BroadcastChannel(`mr_count_channel_${currentUserId}`); + channel.onmessage = ev => { + updateUserMergeRequestCounts(ev.data); + }; + } + } +} diff --git a/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue index b89729375be..197a0706062 100644 --- a/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue +++ b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue @@ -41,7 +41,7 @@ export default { noForkText() { return sprintf( __( - 'To protect this issues confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private.', + "To protect this issue's confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private.", ), { link_start: `<a href="${this.newForkPath}" class="help-link">`, link_end: '</a>' }, false, @@ -105,7 +105,7 @@ export default { </script> <template> - <div class="form-group"> + <div class="confidential-merge-request-fork-group form-group"> <label>{{ __('Project') }}</label> <div> <dropdown @@ -118,7 +118,7 @@ export default { <template v-if="projects.length"> {{ __( - 'To protect this issues confidentiality, a private fork of this project was selected.', + "To protect this issue's confidentiality, a private fork of this project was selected.", ) }} </template> @@ -126,7 +126,11 @@ export default { {{ __('No forks available to you.') }}<br /> <span v-html="noForkText"></span> </template> - <gl-link :href="helpPagePath" class="help-link" target="_blank"> + <gl-link + :href="helpPagePath" + class="w-auto p-0 d-inline-block text-primary bg-transparent" + target="_blank" + > <span class="sr-only">{{ __('Read more') }}</span> <i class="fa fa-question-circle" aria-hidden="true"></i> </gl-link> diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js index b56e08175cc..d4b994d4922 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js @@ -17,6 +17,7 @@ Vue.use(Translate); export default () => { const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed'; + const cycleAnalyticsEl = document.querySelector('#cycle-analytics'); // eslint-disable-next-line no-new new Vue({ @@ -33,7 +34,6 @@ export default () => { 'stage-production-component': stageComponent, }, data() { - const cycleAnalyticsEl = document.querySelector('#cycle-analytics'); const cycleAnalyticsService = new CycleAnalyticsService({ requestPath: cycleAnalyticsEl.dataset.requestPath, }); @@ -56,7 +56,13 @@ export default () => { }, }, created() { - this.fetchCycleAnalyticsData(); + // Conditional check placed here to prevent this method from being called on the + // new Cycle Analytics page (i.e. the new page will be initialized blank and only + // after a group is selected the cycle analyitcs data will be fetched). Once the + // old (current) page has been removed this entire created method as well as the + // variable itself can be completely removed. + // Follow up issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/64490 + if (cycleAnalyticsEl.dataset.requestPath) this.fetchCycleAnalyticsData(); }, methods: { handleError() { diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue index ca3285e9afd..a06dbd70ac5 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue @@ -24,6 +24,11 @@ export default { required: false, default: '', }, + hasDraft: { + type: Boolean, + required: false, + default: false, + }, }, computed: { className() { @@ -55,6 +60,7 @@ export default { :help-page-path="helpPagePath" /> <diff-discussion-reply + v-if="!hasDraft" :has-form="line.hasForm" :render-reply-placeholder="Boolean(line.discussions.length)" @showNewDiscussionForm=" diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue index 8c76a555b62..b2bc3d9914f 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_view.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue @@ -57,6 +57,7 @@ export default { :diff-file-hash="diffFile.file_hash" :line="line" :help-page-path="helpPagePath" + :has-draft="shouldRenderDraftRow(diffFile.file_hash, line) || false" /> <inline-draft-comment-row v-if="shouldRenderDraftRow(diffFile.file_hash, line)" diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue index c00b0e010ff..65b41b0e456 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue @@ -28,6 +28,16 @@ export default { required: false, default: '', }, + hasDraftLeft: { + type: Boolean, + required: false, + default: false, + }, + hasDraftRight: { + type: Boolean, + required: false, + default: false, + }, }, computed: { hasExpandedDiscussionOnLeft() { @@ -121,6 +131,7 @@ export default { /> </div> <diff-discussion-reply + v-if="!hasDraftLeft" :has-form="showLeftSideCommentForm" :render-reply-placeholder="shouldRenderReplyPlaceholderOnLeft" @showNewDiscussionForm="showNewDiscussionForm" @@ -145,6 +156,7 @@ export default { /> </div> <diff-discussion-reply + v-if="!hasDraftRight" :has-form="showRightSideCommentForm" :render-reply-placeholder="shouldRenderReplyPlaceholderOnRight" @showNewDiscussionForm="showNewDiscussionForm" diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue index 41a80d99850..c477e68c33c 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue @@ -58,6 +58,8 @@ export default { :diff-file-hash="diffFile.file_hash" :line-index="index" :help-page-path="helpPagePath" + :has-draft-left="hasParallelDraftLeft(diffFile.file_hash, line) || false" + :has-draft-right="hasParallelDraftRight(diffFile.file_hash, line) || false" /> <parallel-draft-comment-row v-if="shouldRenderParallelDraftRow(diffFile.file_hash, line)" diff --git a/app/assets/javascripts/diffs/mixins/draft_comments.js b/app/assets/javascripts/diffs/mixins/draft_comments.js index dfb71bf38ce..b6c9b132aeb 100644 --- a/app/assets/javascripts/diffs/mixins/draft_comments.js +++ b/app/assets/javascripts/diffs/mixins/draft_comments.js @@ -6,5 +6,7 @@ export default { imageDiscussions() { return this.diffFile.discussions; }, + hasParallelDraftLeft: () => () => false, + hasParallelDraftRight: () => () => false, }, }; diff --git a/app/assets/javascripts/event_tracking/notes.js b/app/assets/javascripts/event_tracking/notes.js index 2d1ec238274..1f70290c397 100644 --- a/app/assets/javascripts/event_tracking/notes.js +++ b/app/assets/javascripts/event_tracking/notes.js @@ -1 +1,2 @@ +// Noop function which has a EE counter-part export default () => {}; diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js index 64b09c8b62c..77080691dcb 100644 --- a/app/assets/javascripts/filterable_list.js +++ b/app/assets/javascripts/filterable_list.js @@ -17,11 +17,13 @@ export default class FilterableList { } getFilterEndpoint() { - return `${this.filterForm.getAttribute('action')}?${$(this.filterForm).serialize()}`; + return this.getPagePath(); } getPagePath() { - return this.getFilterEndpoint(); + const action = this.filterForm.getAttribute('action'); + const params = $(this.filterForm).serialize(); + return `${action}${action.indexOf('?') > 0 ? '&' : '?'}${params}`; } initSearch() { diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index 5fcb11a232e..03756a634d5 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -144,7 +144,9 @@ export default { 'triggerFilesChange', ]), initEditor() { - if (this.shouldHideEditor) return; + if (this.shouldHideEditor && (this.file.content || this.file.raw)) { + return; + } this.editor.clearEditor(); diff --git a/app/assets/javascripts/ide/lib/files.js b/app/assets/javascripts/ide/lib/files.js index b8abaa41f23..51278640b5b 100644 --- a/app/assets/javascripts/ide/lib/files.js +++ b/app/assets/javascripts/ide/lib/files.js @@ -77,6 +77,7 @@ export const decorateFiles = ({ const fileFolder = parent && insertParent(parent); if (name) { + const previewMode = viewerInformationForPath(name); parentPath = fileFolder && fileFolder.path; file = decorateData({ @@ -92,9 +93,9 @@ export const decorateFiles = ({ changed: tempFile, content, base64, - binary, + binary: (previewMode && previewMode.binary) || binary, rawPath, - previewMode: viewerInformationForPath(name), + previewMode, parentPath, }); diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js index c88244492e0..a52f1e235ed 100644 --- a/app/assets/javascripts/ide/stores/mutations/file.js +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -1,6 +1,7 @@ import * as types from '../mutation_types'; import { sortTree } from '../utils'; import { diffModes } from '../../constants'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; export default { [types.SET_FILE_ACTIVE](state, { path, active }) { @@ -35,19 +36,18 @@ export default { } }, [types.SET_FILE_DATA](state, { data, file }) { - Object.assign(state.entries[file.path], { - id: data.id, - blamePath: data.blame_path, - commitsPath: data.commits_path, - permalink: data.permalink, - rawPath: data.raw_path, - binary: data.binary, - renderError: data.render_error, - raw: (state.entries[file.path] && state.entries[file.path].raw) || null, - baseRaw: null, - html: data.html, - size: data.size, - lastCommitSha: data.last_commit_sha, + const stateEntry = state.entries[file.path]; + const stagedFile = state.stagedFiles.find(f => f.path === file.path); + const openFile = state.openFiles.find(f => f.path === file.path); + const changedFile = state.changedFiles.find(f => f.path === file.path); + + [stateEntry, stagedFile, openFile, changedFile].forEach(f => { + if (f) { + Object.assign(f, convertObjectPropsToCamelCase(data, { dropKeys: ['raw', 'baseRaw'] }), { + raw: (stateEntry && stateEntry.raw) || null, + baseRaw: null, + }); + } }); }, [types.SET_FILE_RAW_DATA](state, { file, raw }) { diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js index bc9d7fcf30d..c855f3973b0 100644 --- a/app/assets/javascripts/issuable_bulk_update_actions.js +++ b/app/assets/javascripts/issuable_bulk_update_actions.js @@ -1,4 +1,4 @@ -/* eslint-disable consistent-return, func-names, array-callback-return, prefer-arrow-callback, no-unused-vars */ +/* eslint-disable consistent-return, func-names, array-callback-return, prefer-arrow-callback */ import $ from 'jquery'; import _ from 'underscore'; @@ -7,7 +7,7 @@ import Flash from './flash'; import { __ } from './locale'; export default { - init({ container, form, issues, prefixId } = {}) { + init({ form, issues, prefixId } = {}) { this.prefixId = prefixId || 'issue_'; this.form = form || this.getElement('.bulk-update'); this.$labelDropdown = this.form.find('.js-label-select'); diff --git a/app/assets/javascripts/issuable_index.js b/app/assets/javascripts/issuable_index.js index 16f88cddce3..f3f8b6ec715 100644 --- a/app/assets/javascripts/issuable_index.js +++ b/app/assets/javascripts/issuable_index.js @@ -2,26 +2,13 @@ import $ from 'jquery'; import axios from './lib/utils/axios_utils'; import flash from './flash'; import { s__, __ } from './locale'; -import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar'; -import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; +import issuableInitBulkUpdateSidebar from './issuable_init_bulk_update_sidebar'; export default class IssuableIndex { constructor(pagePrefix) { - this.initBulkUpdate(pagePrefix); + issuableInitBulkUpdateSidebar.init(pagePrefix); IssuableIndex.resetIncomingEmailToken(); } - initBulkUpdate(pagePrefix) { - const userCanBulkUpdate = $('.issues-bulk-update').length > 0; - const alreadyInitialized = Boolean(this.bulkUpdateSidebar); - - if (userCanBulkUpdate && !alreadyInitialized) { - IssuableBulkUpdateActions.init({ - prefixId: pagePrefix, - }); - - this.bulkUpdateSidebar = new IssuableBulkUpdateSidebar(); - } - } static resetIncomingEmailToken() { const $resetToken = $('.incoming-email-token-reset'); diff --git a/app/assets/javascripts/issuable_init_bulk_update_sidebar.js b/app/assets/javascripts/issuable_init_bulk_update_sidebar.js new file mode 100644 index 00000000000..da8969c80f3 --- /dev/null +++ b/app/assets/javascripts/issuable_init_bulk_update_sidebar.js @@ -0,0 +1,19 @@ +import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar'; +import issuableBulkUpdateActions from './issuable_bulk_update_actions'; + +export default { + bulkUpdateSidebar: null, + + init(prefixId) { + const bulkUpdateEl = document.querySelector('.issues-bulk-update'); + const alreadyInitialized = Boolean(this.bulkUpdateSidebar); + + if (bulkUpdateEl && !alreadyInitialized) { + issuableBulkUpdateActions.init({ prefixId }); + + this.bulkUpdateSidebar = new IssuableBulkUpdateSidebar(); + } + + return this.bulkUpdateSidebar; + }, +}; diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 3f954b43ee3..bea43430edc 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -11,7 +11,7 @@ import CreateLabelDropdown from './create_label'; import flash from './flash'; import ModalStore from './boards/stores/modal_store'; import boardsStore from './boards/stores/boards_store'; -import { isEE, isScopedLabel } from '~/lib/utils/common_utils'; +import { isScopedLabel } from '~/lib/utils/common_utils'; export default class LabelsSelect { constructor(els, options = {}) { @@ -140,7 +140,7 @@ export default class LabelsSelect { labelCount = data.labels.length; // EE Specific - if (isEE) { + if (IS_EE) { /** * For Scoped labels, the last label selected with the * same key will be applied to the current issueable. diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index cc5e12aa467..5e90893b684 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -727,14 +727,6 @@ export const NavigationType = { }; /** - * Returns the value of `gon.ee` - * Used to check if it's the EE codebase or the CE one. - * - * @returns Boolean - */ -export const isEE = () => window.gon && window.gon.ee; - -/** * Checks if the given Label has a special syntax `::` in * it's title. * diff --git a/app/assets/javascripts/monitoring/components/charts/area.vue b/app/assets/javascripts/monitoring/components/charts/area.vue index 81773bd140e..454ff4f284e 100644 --- a/app/assets/javascripts/monitoring/components/charts/area.vue +++ b/app/assets/javascripts/monitoring/components/charts/area.vue @@ -8,6 +8,7 @@ import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; import Icon from '~/vue_shared/components/icon.vue'; import { chartHeight, graphTypes, lineTypes } from '../../constants'; import { makeDataSeries } from '~/helpers/monitor_helper'; +import { graphDataValidatorForValues } from '../../utils'; let debouncedResize; @@ -23,19 +24,7 @@ export default { graphData: { type: Object, required: true, - validator(data) { - return ( - Array.isArray(data.queries) && - data.queries.filter(query => { - if (Array.isArray(query.result)) { - return ( - query.result.filter(res => Array.isArray(res.values)).length === query.result.length - ); - } - return false; - }).length === data.queries.length - ); - }, + validator: graphDataValidatorForValues.bind(null, false), }, containerWidth: { type: Number, diff --git a/app/assets/javascripts/monitoring/components/charts/column.vue b/app/assets/javascripts/monitoring/components/charts/column.vue index 05a2036f4c3..83136d43479 100644 --- a/app/assets/javascripts/monitoring/components/charts/column.vue +++ b/app/assets/javascripts/monitoring/components/charts/column.vue @@ -4,6 +4,7 @@ import { debounceByAnimationFrame } from '~/lib/utils/common_utils'; import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; import { chartHeight } from '../../constants'; import { makeDataSeries } from '~/helpers/monitor_helper'; +import { graphDataValidatorForValues } from '../../utils'; export default { components: { @@ -14,23 +15,11 @@ export default { graphData: { type: Object, required: true, - validator(data) { - return ( - Array.isArray(data.queries) && - data.queries.filter(query => { - if (Array.isArray(query.result)) { - return ( - query.result.filter(res => Array.isArray(res.values)).length === query.result.length - ); - } - return false; - }).length === data.queries.length - ); - }, - containerWidth: { - type: Number, - required: true, - }, + validator: graphDataValidatorForValues.bind(null, false), + }, + containerWidth: { + type: Number, + required: true, }, }, data() { diff --git a/app/assets/javascripts/monitoring/components/charts/single_stat.vue b/app/assets/javascripts/monitoring/components/charts/single_stat.vue index b03a6ca1806..7428b27a9c3 100644 --- a/app/assets/javascripts/monitoring/components/charts/single_stat.vue +++ b/app/assets/javascripts/monitoring/components/charts/single_stat.vue @@ -1,5 +1,7 @@ <script> import { GlSingleStat } from '@gitlab/ui/dist/charts'; +import { roundOffFloat } from '~/lib/utils/common_utils'; +import { graphDataValidatorForValues } from '../../utils'; export default { components: { @@ -7,22 +9,21 @@ export default { }, inheritAttrs: false, props: { - title: { - type: String, - required: true, - }, - value: { - type: Number, - required: true, - }, - unit: { - type: String, + graphData: { + type: Object, required: true, + validator: graphDataValidatorForValues.bind(null, true), }, }, computed: { - valueWithUnit() { - return `${this.value}${this.unit}`; + queryInfo() { + return this.graphData.queries[0]; + }, + engineeringNotation() { + return `${roundOffFloat(this.queryInfo.result[0].value[1], 1)}${this.queryInfo.unit}`; + }, + graphTitle() { + return this.queryInfo.label; }, }, }; @@ -30,8 +31,8 @@ export default { <template> <div class="prometheus-graph col-12 col-lg-6"> <div class="prometheus-graph-header"> - <h5 ref="graphTitle" class="prometheus-graph-title">{{ title }}</h5> + <h5 ref="graphTitle" class="prometheus-graph-title">{{ graphTitle }}</h5> </div> - <gl-single-stat :value="valueWithUnit" :title="title" variant="success" /> + <gl-single-stat :value="engineeringNotation" :title="graphTitle" variant="success" /> </div> </template> diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index ed25a6e3684..6eaced0c108 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -4,10 +4,11 @@ import _ from 'underscore'; import { mapActions, mapState } from 'vuex'; import { s__ } from '~/locale'; import Icon from '~/vue_shared/components/icon.vue'; -import '~/vue_shared/mixins/is_ee'; import { getParameterValues } from '~/lib/utils/url_utility'; import invalidUrl from '~/lib/utils/invalid_url'; import MonitorAreaChart from './charts/area.vue'; +import MonitorSingleStatChart from './charts/single_stat.vue'; +import PanelType from './panel_type.vue'; import GraphGroup from './graph_group.vue'; import EmptyState from './empty_state.vue'; import { timeWindows, timeWindowsKeyNames } from '../constants'; @@ -19,6 +20,8 @@ let sidebarMutationObserver; export default { components: { MonitorAreaChart, + MonitorSingleStatChart, + PanelType, GraphGroup, EmptyState, Icon, @@ -153,6 +156,7 @@ export default { 'useDashboardEndpoint', 'allDashboards', 'multipleDashboardsEnabled', + 'additionalPanelTypesEnabled', ]), groupsWithData() { return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0); @@ -160,6 +164,12 @@ export default { selectedDashboardText() { return this.currentDashboard || (this.allDashboards[0] && this.allDashboards[0].display_name); }, + addingMetricsAvailable() { + return IS_EE && this.canAddMetrics && !this.showEmptyState; + }, + alertWidgetAvailable() { + return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint; + }, }, created() { this.setEndpoints({ @@ -168,6 +178,7 @@ export default { deploymentsEndpoint: this.deploymentsEndpoint, dashboardEndpoint: this.dashboardEndpoint, currentDashboard: this.currentDashboard, + projectPath: this.projectPath, }); this.timeWindows = timeWindows; @@ -215,6 +226,8 @@ export default { chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)), ); }, + // TODO: BEGIN, Duplicated code with panel_type until feature flag is removed + // Issue number: https://gitlab.com/gitlab-org/gitlab-ce/issues/63845 getGraphAlerts(queries) { if (!this.allAlerts) return {}; const metricIdsForChart = queries.map(q => q.metricId); @@ -223,6 +236,7 @@ export default { getGraphAlertValues(queries) { return Object.values(this.getGraphAlerts(queries)); }, + // TODO: END hideAddMetricModal() { this.$refs.addMetricModal.hide(); }, @@ -313,7 +327,7 @@ export default { </div> </div> <div class="d-flex"> - <div v-if="isEE && canAddMetrics && !showEmptyState"> + <div v-if="addingMetricsAvailable"> <gl-button v-gl-modal-directive="$options.addMetric.modalId" class="js-add-metric-button text-success border-success" @@ -361,24 +375,34 @@ export default { :name="groupData.group" :show-panels="showPanels" > - <monitor-area-chart - v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)" - :key="graphIndex" - :project-path="projectPath" - :graph-data="graphData" - :deployment-data="deploymentData" - :thresholds="getGraphAlertValues(graphData.queries)" - :container-width="elWidth" - group-id="monitor-area-chart" - > - <alert-widget - v-if="isEE && prometheusAlertsAvailable && alertsEndpoint && graphData" - :alerts-endpoint="alertsEndpoint" - :relevant-queries="graphData.queries" - :alerts-to-manage="getGraphAlerts(graphData.queries)" - @setAlerts="setAlerts" + <template v-if="additionalPanelTypesEnabled"> + <panel-type + v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)" + :key="`panel-type-${graphIndex}`" + :graph-data="graphData" + :dashboard-width="elWidth" /> - </monitor-area-chart> + </template> + <template v-else> + <monitor-area-chart + v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)" + :key="graphIndex" + :graph-data="graphData" + :deployment-data="deploymentData" + :thresholds="getGraphAlertValues(graphData.queries)" + :container-width="elWidth" + :project-path="projectPath" + group-id="monitor-area-chart" + > + <alert-widget + v-if="alertWidgetAvailable && graphData" + :alerts-endpoint="alertsEndpoint" + :relevant-queries="graphData.queries" + :alerts-to-manage="getGraphAlerts(graphData.queries)" + @setAlerts="setAlerts" + /> + </monitor-area-chart> + </template> </graph-group> </div> <empty-state diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue new file mode 100644 index 00000000000..45ee067de83 --- /dev/null +++ b/app/assets/javascripts/monitoring/components/panel_type.vue @@ -0,0 +1,62 @@ +<script> +import { mapState } from 'vuex'; +import _ from 'underscore'; +import MonitorAreaChart from './charts/area.vue'; +import MonitorSingleStatChart from './charts/single_stat.vue'; + +export default { + components: { + MonitorAreaChart, + MonitorSingleStatChart, + }, + props: { + graphData: { + type: Object, + required: true, + }, + dashboardWidth: { + type: Number, + required: true, + }, + }, + computed: { + ...mapState('monitoringDashboard', ['deploymentData', 'projectPath']), + alertWidgetAvailable() { + return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint && this.graphData; + }, + }, + methods: { + getGraphAlerts(queries) { + if (!this.allAlerts) return {}; + const metricIdsForChart = queries.map(q => q.metricId); + return _.pick(this.allAlerts, alert => metricIdsForChart.includes(alert.metricId)); + }, + getGraphAlertValues(queries) { + return Object.values(this.getGraphAlerts(queries)); + }, + isPanelType(type) { + return this.graphData.type && this.graphData.type === type; + }, + }, +}; +</script> +<template> + <monitor-single-stat-chart v-if="isPanelType('single-stat')" :graph-data="graphData" /> + <monitor-area-chart + v-else + :graph-data="graphData" + :deployment-data="deploymentData" + :project-path="projectPath" + :thresholds="getGraphAlertValues(graphData.queries)" + :container-width="dashboardWidth" + group-id="monitor-area-chart" + > + <alert-widget + v-if="alertWidgetAvailable" + :alerts-endpoint="alertsEndpoint" + :relevant-queries="graphData.queries" + :alerts-to-manage="getGraphAlerts(graphData.queries)" + @setAlerts="setAlerts" + /> + </monitor-area-chart> +</template> diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js index 97d149e9ad5..c0fee1ebb99 100644 --- a/app/assets/javascripts/monitoring/monitoring_bundle.js +++ b/app/assets/javascripts/monitoring/monitoring_bundle.js @@ -12,6 +12,7 @@ export default (props = {}) => { store.dispatch('monitoringDashboard/setFeatureFlags', { prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint, multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards, + additionalPanelTypesEnabled: gon.features.environmentMetricsAdditionalPanelTypes, }); } diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index 0fa2a5d6370..5b3da51e9a6 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -37,10 +37,11 @@ export const setEndpoints = ({ commit }, endpoints) => { export const setFeatureFlags = ( { commit }, - { prometheusEndpointEnabled, multipleDashboardsEnabled }, + { prometheusEndpointEnabled, multipleDashboardsEnabled, additionalPanelTypesEnabled }, ) => { commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled); commit(types.SET_MULTIPLE_DASHBOARDS_ENABLED, multipleDashboardsEnabled); + commit(types.SET_ADDITIONAL_PANEL_TYPES_ENABLED, additionalPanelTypesEnabled); }; export const requestMetricsDashboard = ({ commit }) => { diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js index 2c78a0b9315..c89daba3df7 100644 --- a/app/assets/javascripts/monitoring/stores/mutation_types.js +++ b/app/assets/javascripts/monitoring/stores/mutation_types.js @@ -11,6 +11,7 @@ export const SET_QUERY_RESULT = 'SET_QUERY_RESULT'; export const SET_TIME_WINDOW = 'SET_TIME_WINDOW'; export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED'; export const SET_MULTIPLE_DASHBOARDS_ENABLED = 'SET_MULTIPLE_DASHBOARDS_ENABLED'; +export const SET_ADDITIONAL_PANEL_TYPES_ENABLED = 'SET_ADDITIONAL_PANEL_TYPES_ENABLED'; export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS'; export const SET_ENDPOINTS = 'SET_ENDPOINTS'; export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE'; diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js index a85a7723c1f..0104dcb867d 100644 --- a/app/assets/javascripts/monitoring/stores/mutations.js +++ b/app/assets/javascripts/monitoring/stores/mutations.js @@ -75,6 +75,7 @@ export default { state.deploymentsEndpoint = endpoints.deploymentsEndpoint; state.dashboardEndpoint = endpoints.dashboardEndpoint; state.currentDashboard = endpoints.currentDashboard; + state.projectPath = endpoints.projectPath; }, [types.SET_DASHBOARD_ENABLED](state, enabled) { state.useDashboardEndpoint = enabled; @@ -92,4 +93,7 @@ export default { [types.SET_ALL_DASHBOARDS](state, dashboards) { state.allDashboards = dashboards; }, + [types.SET_ADDITIONAL_PANEL_TYPES_ENABLED](state, enabled) { + state.additionalPanelTypesEnabled = enabled; + }, }; diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js index de711d6ccae..e54bb712695 100644 --- a/app/assets/javascripts/monitoring/stores/state.js +++ b/app/assets/javascripts/monitoring/stores/state.js @@ -9,6 +9,7 @@ export default () => ({ dashboardEndpoint: invalidUrl, useDashboardEndpoint: false, multipleDashboardsEnabled: false, + additionalPanelTypesEnabled: false, emptyState: 'gettingStarted', showEmptyState: true, groups: [], @@ -17,4 +18,5 @@ export default () => ({ metricsWithData: [], allDashboards: [], currentDashboard: null, + projectPath: null, }); diff --git a/app/assets/javascripts/monitoring/stores/utils.js b/app/assets/javascripts/monitoring/stores/utils.js index 721942f9d3b..938ee2f0a9a 100644 --- a/app/assets/javascripts/monitoring/stores/utils.js +++ b/app/assets/javascripts/monitoring/stores/utils.js @@ -69,13 +69,26 @@ export const sortMetrics = metrics => .sortBy('weight') .value(); -export const normalizeQueryResult = timeSeries => ({ - ...timeSeries, - values: timeSeries.values.map(([timestamp, value]) => [ - new Date(timestamp * 1000).toISOString(), - Number(value), - ]), -}); +export const normalizeQueryResult = timeSeries => { + let normalizedResult = {}; + + if (timeSeries.values) { + normalizedResult = { + ...timeSeries, + values: timeSeries.values.map(([timestamp, value]) => [ + new Date(timestamp * 1000).toISOString(), + Number(value), + ]), + }; + } else if (timeSeries.value) { + normalizedResult = { + ...timeSeries, + value: [new Date(timeSeries.value[0] * 1000).toISOString(), Number(timeSeries.value[1])], + }; + } + + return normalizedResult; +}; export const normalizeMetrics = metrics => { const groupedMetrics = groupQueriesByChartInfo(metrics); diff --git a/app/assets/javascripts/monitoring/utils.js b/app/assets/javascripts/monitoring/utils.js index ef309c8a398..478e2b3d06c 100644 --- a/app/assets/javascripts/monitoring/utils.js +++ b/app/assets/javascripts/monitoring/utils.js @@ -30,4 +30,28 @@ export const getTimeDiff = selectedTimeWindow => { return { start, end }; }; +/** + * This method is used to validate if the graph data format for a chart component + * that needs a time series as a response from a prometheus query (query_range) is + * of a valid format or not. + * @param {Object} graphData the graph data response from a prometheus request + * @returns {boolean} whether the graphData format is correct + */ +export const graphDataValidatorForValues = (isValues, graphData) => { + const responseValueKeyName = isValues ? 'value' : 'values'; + + return ( + Array.isArray(graphData.queries) && + graphData.queries.filter(query => { + if (Array.isArray(query.result)) { + return ( + query.result.filter(res => Array.isArray(res[responseValueKeyName])).length === + query.result.length + ); + } + return false; + }).length === graphData.queries.length + ); +}; + export default {}; diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 6c1738f0f1b..fda494fec07 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -13,6 +13,7 @@ import { splitCamelCase, slugifyWithUnderscore, } from '../../lib/utils/text_utility'; +import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; import * as constants from '../constants'; import eventHub from '../event_hub'; import issueWarning from '../../vue_shared/components/issue/issue_warning.vue'; @@ -234,7 +235,10 @@ export default { toggleIssueState() { if (this.isOpen) { this.closeIssue() - .then(() => this.enableButton()) + .then(() => { + this.enableButton(); + refreshUserMergeRequestCounts(); + }) .catch(() => { this.enableButton(); this.toggleStateButtonLoading(false); @@ -247,7 +251,10 @@ export default { }); } else { this.reopenIssue() - .then(() => this.enableButton()) + .then(() => { + this.enableButton(); + refreshUserMergeRequestCounts(); + }) .catch(({ data }) => { this.enableButton(); this.toggleStateButtonLoading(false); diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue index 2ff0fee62f3..0b136549c14 100644 --- a/app/assets/javascripts/notes/components/discussion_notes.vue +++ b/app/assets/javascripts/notes/components/discussion_notes.vue @@ -8,12 +8,14 @@ import SystemNote from '~/vue_shared/components/notes/system_note.vue'; import NoteableNote from './noteable_note.vue'; import ToggleRepliesWidget from './toggle_replies_widget.vue'; import NoteEditedText from './note_edited_text.vue'; +import DiscussionNotesRepliesWrapper from './discussion_notes_replies_wrapper.vue'; export default { name: 'DiscussionNotes', components: { ToggleRepliesWidget, NoteEditedText, + DiscussionNotesRepliesWrapper, }, props: { discussion: { @@ -119,9 +121,7 @@ export default { /> <slot slot="avatar-badge" name="avatar-badge"></slot> </component> - <div - :class="discussion.diff_discussion ? 'discussion-collapsible bordered-box clearfix' : ''" - > + <discussion-notes-replies-wrapper :is-diff-discussion="discussion.diff_discussion"> <toggle-replies-widget v-if="hasReplies" :collapsed="!isExpanded" @@ -141,7 +141,7 @@ export default { /> </template> <slot :show-replies="isExpanded || !hasReplies" name="footer"></slot> - </div> + </discussion-notes-replies-wrapper> </template> <template v-else> <component diff --git a/app/assets/javascripts/notes/components/discussion_notes_replies_wrapper.vue b/app/assets/javascripts/notes/components/discussion_notes_replies_wrapper.vue new file mode 100644 index 00000000000..2ddca56ddd5 --- /dev/null +++ b/app/assets/javascripts/notes/components/discussion_notes_replies_wrapper.vue @@ -0,0 +1,27 @@ +<script> +/** + * Wrapper for discussion notes replies section. + * + * This is a functional component using the render method because in some cases + * the wrapper is not needed and we want to simply render along the children. + */ +export default { + functional: true, + props: { + isDiffDiscussion: { + type: Boolean, + required: false, + default: false, + }, + }, + render(h, { props, children }) { + if (props.isDiffDiscussion) { + return h('li', { class: 'discussion-collapsible bordered-box clearfix' }, [ + h('ul', { class: 'notes' }, children), + ]); + } + + return children; + }, +}; +</script> diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue index 844d0c3e376..6cc873359da 100644 --- a/app/assets/javascripts/notes/components/note_actions.vue +++ b/app/assets/javascripts/notes/components/note_actions.vue @@ -165,7 +165,7 @@ export default { v-gl-tooltip type="button" title="Edit comment" - class="note-action-button js-note-edit btn btn-transparent" + class="note-action-button js-note-edit btn btn-transparent qa-note-edit-button" @click="onEdit" > <icon name="pencil" css-classes="link-highlight" /> diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 3fbd0a9f715..ac743d9f4b8 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -144,15 +144,6 @@ export default { return {}; }, - componentClassName() { - if (this.shouldRenderDiffs) { - if (!this.lastUpdatedAt && !this.discussion.resolved) { - return 'unresolved'; - } - } - - return ''; - }, isExpanded() { return this.discussion.expanded || this.alwaysExpanded; }, @@ -313,11 +304,11 @@ export default { </script> <template> - <timeline-entry-item class="note note-discussion" :class="componentClassName"> + <timeline-entry-item class="note note-discussion"> <div class="timeline-content"> <div :data-discussion-id="discussion.id" class="discussion js-discussion-container"> <div v-if="shouldRenderDiffs" class="discussion-header note-wrapper"> - <div v-once class="timeline-icon"> + <div v-once class="timeline-icon align-self-start flex-shrink-0"> <user-avatar-link v-if="author" :link-href="author.path" @@ -326,7 +317,7 @@ export default { :img-size="40" /> </div> - <div class="timeline-content"> + <div class="timeline-content w-100"> <note-header :author="author" :created-at="firstNote.created_at" diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index 57dd1c5cab2..c70c0e4095c 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -1,5 +1,4 @@ import Vue from 'vue'; -import { isEE } from '~/lib/utils/common_utils'; import initNoteStats from 'ee_else_ce/event_tracking/notes'; import notesApp from './components/notes_app.vue'; import initDiscussionFilters from './discussion_filters'; @@ -41,9 +40,7 @@ document.addEventListener('DOMContentLoaded', () => { }; }, mounted() { - if (isEE) { - initNoteStats(); - } + initNoteStats(); }, render(createElement) { return createElement('notes-app', { diff --git a/app/assets/javascripts/notes/services/notes_service.js b/app/assets/javascripts/notes/services/notes_service.js index 237e70c0a4c..9e0392110b6 100644 --- a/app/assets/javascripts/notes/services/notes_service.js +++ b/app/assets/javascripts/notes/services/notes_service.js @@ -1,5 +1,4 @@ import Vue from 'vue'; -import Api from '~/api'; import VueResource from 'vue-resource'; import * as constants from '../constants'; @@ -10,9 +9,6 @@ export default { const config = filter !== undefined ? { params: { notes_filter: filter } } : null; return Vue.http.get(endpoint, config); }, - deleteNote(endpoint) { - return Vue.http.delete(endpoint); - }, replyToDiscussion(endpoint, data) { return Vue.http.post(endpoint, data, { emulateJSON: true }); }, @@ -39,13 +35,7 @@ export default { return Vue.http.get(endpoint, options); }, - toggleAward(endpoint, data) { - return Vue.http.post(endpoint, data, { emulateJSON: true }); - }, toggleIssueState(endpoint, data) { return Vue.http.put(endpoint, data); }, - applySuggestion(id) { - return Api.applySuggestion(id); - }, }; diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js index 9054b4779aa..30eab272aa9 100644 --- a/app/assets/javascripts/notes/stores/actions.js +++ b/app/assets/javascripts/notes/stores/actions.js @@ -14,6 +14,7 @@ import sidebarTimeTrackingEventHub from '../../sidebar/event_hub'; import { isInViewport, scrollToElement, isInMRPage } from '../../lib/utils/common_utils'; import mrWidgetEventHub from '../../vue_merge_request_widget/event_hub'; import { __ } from '~/locale'; +import Api from '~/api'; let eTagPoll; @@ -61,7 +62,7 @@ export const updateDiscussion = ({ commit, state }, discussion) => { }; export const deleteNote = ({ commit, dispatch, state }, note) => - service.deleteNote(note.path).then(() => { + axios.delete(note.path).then(() => { const discussion = state.discussions.find(({ id }) => id === note.discussion_id); commit(types.DELETE_NOTE, note); @@ -383,12 +384,9 @@ export const toggleAward = ({ commit, getters }, { awardName, noteId }) => { export const toggleAwardRequest = ({ dispatch }, data) => { const { endpoint, awardName } = data; - return service - .toggleAward(endpoint, { name: awardName }) - .then(res => res.json()) - .then(() => { - dispatch('toggleAward', data); - }); + return axios.post(endpoint, { name: awardName }).then(() => { + dispatch('toggleAward', data); + }); }; export const scrollToNoteIfNeeded = (context, el) => { @@ -449,8 +447,7 @@ export const submitSuggestion = ( { commit, dispatch }, { discussionId, noteId, suggestionId, flashContainer }, ) => - service - .applySuggestion(suggestionId) + Api.applySuggestion(suggestionId) .then(() => commit(types.APPLY_SUGGESTION, { discussionId, noteId, suggestionId })) .then(() => dispatch('resolveDiscussion', { discussionId }).catch(() => {})) .catch(err => { diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js index 23fb5656008..dcdee77a8ab 100644 --- a/app/assets/javascripts/pages/groups/issues/index.js +++ b/app/assets/javascripts/pages/groups/issues/index.js @@ -1,11 +1,15 @@ import projectSelect from '~/project_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; +import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar'; import { FILTERED_SEARCH } from '~/pages/constants'; import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys'; import initManualOrdering from '~/manual_ordering'; +const ISSUE_BULK_UPDATE_PREFIX = 'issue_'; + document.addEventListener('DOMContentLoaded', () => { IssuableFilteredSearchTokenKeys.addExtraTokensForIssues(); + issuableInitBulkUpdateSidebar.init(ISSUE_BULK_UPDATE_PREFIX); initFilteredSearch({ page: FILTERED_SEARCH.ISSUES, diff --git a/app/assets/javascripts/pages/projects/issues/form.js b/app/assets/javascripts/pages/projects/issues/form.js index 941c4552579..2205a7bafe3 100644 --- a/app/assets/javascripts/pages/projects/issues/form.js +++ b/app/assets/javascripts/pages/projects/issues/form.js @@ -17,7 +17,5 @@ export default () => { new MilestoneSelect(); new IssuableTemplateSelectors(); - if (gon.features.graphql) { - initSuggestions(); - } + initSuggestions(); }; diff --git a/app/assets/javascripts/projects/projects_filterable_list.js b/app/assets/javascripts/projects/projects_filterable_list.js new file mode 100644 index 00000000000..433c894e668 --- /dev/null +++ b/app/assets/javascripts/projects/projects_filterable_list.js @@ -0,0 +1,7 @@ +import FilterableList from '~/filterable_list'; + +export default class ProjectsFilterableList extends FilterableList { + getFilterEndpoint() { + return this.getPagePath().replace('/projects?', '/projects.json?'); + } +} diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js index c67d59d2be5..913b62ba26d 100644 --- a/app/assets/javascripts/projects_list.js +++ b/app/assets/javascripts/projects_list.js @@ -1,4 +1,4 @@ -import FilterableList from './filterable_list'; +import ProjectsFilterableList from './projects/projects_filterable_list'; /** * Makes search request for projects when user types a value in the search input. @@ -11,7 +11,7 @@ export default class ProjectsList { const holder = document.querySelector('.js-projects-list-holder'); if (form && filter && holder) { - const list = new FilterableList(form, filter, holder); + const list = new ProjectsFilterableList(form, filter, holder); list.initSearch(); } } diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue index 1e266dd4ced..e157036871b 100644 --- a/app/assets/javascripts/registry/components/collapsible_container.vue +++ b/app/assets/javascripts/registry/components/collapsible_container.vue @@ -31,6 +31,7 @@ export default { data() { return { isOpen: false, + modalId: `confirm-repo-deletion-modal-${this.repo.id}`, }; }, computed: { @@ -80,7 +81,7 @@ export default { <gl-button v-if="repo.canDelete" v-gl-tooltip - v-gl-modal="'confirm-repo-deletion-modal'" + v-gl-modal="modalId" :title="s__('ContainerRegistry|Remove repository')" :aria-label="s__('ContainerRegistry|Remove repository')" class="js-remove-repo" @@ -100,12 +101,7 @@ export default { {{ s__('ContainerRegistry|No tags in Container Registry for this container image.') }} </div> </div> - - <gl-modal - modal-id="confirm-repo-deletion-modal" - ok-variant="danger" - @ok="handleDeleteRepository" - > + <gl-modal :modal-id="modalId" ok-variant="danger" @ok="handleDeleteRepository"> <template v-slot:modal-title>{{ s__('ContainerRegistry|Remove repository') }}</template> <p v-html=" diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue index 0ec5e2c7a87..a498a553908 100644 --- a/app/assets/javascripts/registry/components/table_registry.vue +++ b/app/assets/javascripts/registry/components/table_registry.vue @@ -32,6 +32,7 @@ export default { data() { return { itemToBeDeleted: null, + modalId: `confirm-image-deletion-modal-${this.repo.id}`, }; }, computed: { @@ -114,7 +115,7 @@ export default { <gl-button v-if="item.canDelete" v-gl-tooltip - v-gl-modal="'confirm-image-deletion-modal'" + v-gl-modal="modalId" :title="s__('ContainerRegistry|Remove image')" :aria-label="s__('ContainerRegistry|Remove image')" variant="danger" @@ -134,11 +135,7 @@ export default { :page-info="repo.pagination" /> - <gl-modal - modal-id="confirm-image-deletion-modal" - ok-variant="danger" - @ok="handleDeleteRegistry" - > + <gl-modal :modal-id="modalId" ok-variant="danger" @ok="handleDeleteRegistry"> <template v-slot:modal-title>{{ s__('ContainerRegistry|Remove image') }}</template> <template v-slot:modal-ok>{{ s__('ContainerRegistry|Remove image and tags') }}</template> <p diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue index 1e66ccbfa29..0d9e992e596 100644 --- a/app/assets/javascripts/repository/components/table/index.vue +++ b/app/assets/javascripts/repository/components/table/index.vue @@ -76,7 +76,7 @@ export default { variables: { projectPath: this.projectPath, ref: this.ref, - path: this.path, + path: this.path || '/', nextPageCursor: this.nextPageCursor, pageSize: PAGE_SIZE, }, diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue index 70dc3d2cdfa..be1e4811856 100644 --- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue +++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue @@ -2,6 +2,7 @@ import Flash from '~/flash'; import eventHub from '~/sidebar/event_hub'; import Store from '~/sidebar/stores/sidebar_store'; +import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; import AssigneeTitle from './assignee_title.vue'; import Assignees from './assignees.vue'; import { __ } from '~/locale'; @@ -73,6 +74,9 @@ export default { this.mediator .saveAssignees(this.field) .then(setLoadingFalse.bind(this)) + .then(() => { + refreshUserMergeRequestCounts(); + }) .catch(() => { setLoadingFalse(); return new Flash(__('Error occurred when saving assignees')); diff --git a/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css b/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css index 00a55c0027a..6a7b2f52549 100644 --- a/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css +++ b/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css @@ -48,6 +48,7 @@ font-size: .8rem; font-weight: 400; color: #2e2e2e; + z-index: 9999; /* toolbar should always be on top */ } .gitlab-wrapper-open { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue index 34cdb70ce14..5c7859828d8 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -125,7 +125,9 @@ export default { this.isStopping = false; }) .catch(() => { - createFlash('Something went wrong while stopping this environment. Please try again.'); + createFlash( + __('Something went wrong while stopping this environment. Please try again.'), + ); this.isStopping = false; }); } diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue index e20a16900d4..fb826be19f5 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue @@ -139,7 +139,7 @@ export default { type="button" class="btn dropdown-toggle qa-dropdown-toggle" data-toggle="dropdown" - aria-label="Download as" + :aria-label="__('Download as')" aria-haspopup="true" aria-expanded="false" > diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue index 5958c2cf87e..8e8e67228ed 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue @@ -6,6 +6,7 @@ import statusIcon from '../mr_widget_status_icon.vue'; import MrWidgetAuthor from '../../components/mr_widget_author.vue'; import eventHub from '../../event_hub'; import { AUTO_MERGE_STRATEGIES } from '../../constants'; +import { __ } from '~/locale'; export default { name: 'MRWidgetAutoMergeEnabled', @@ -55,7 +56,7 @@ export default { }) .catch(() => { this.isCancellingAutoMerge = false; - Flash('Something went wrong. Please try again.'); + Flash(__('Something went wrong. Please try again.')); }); }, removeSourceBranch() { @@ -76,7 +77,7 @@ export default { }) .catch(() => { this.isRemovingSourceBranch = false; - Flash('Something went wrong. Please try again.'); + Flash(__('Something went wrong. Please try again.')); }); }, }, @@ -107,15 +108,15 @@ export default { <section class="mr-info-list"> <p> {{ s__('mrWidget|The changes will be merged into') }} - <a :href="mr.targetBranchPath" class="label-branch"> {{ mr.targetBranch }} </a> + <a :href="mr.targetBranchPath" class="label-branch">{{ mr.targetBranch }}</a> </p> <p v-if="mr.shouldRemoveSourceBranch"> {{ s__('mrWidget|The source branch will be deleted') }} </p> <p v-else class="d-flex align-items-start"> - <span class="append-right-10"> - {{ s__('mrWidget|The source branch will not be deleted') }} - </span> + <span class="append-right-10">{{ + s__('mrWidget|The source branch will not be deleted') + }}</span> <a v-if="canRemoveSourceBranch" :disabled="isRemovingSourceBranch" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue index 0bcccc50eb2..c7b064b8506 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue @@ -4,6 +4,7 @@ import simplePoll from '../../../lib/utils/simple_poll'; import eventHub from '../../event_hub'; import statusIcon from '../mr_widget_status_icon.vue'; import Flash from '../../../flash'; +import { __, sprintf } from '~/locale'; export default { name: 'MRWidgetRebase', @@ -40,6 +41,17 @@ export default { showDisabledButton() { return ['failed', 'loading'].includes(this.status); }, + fastForwardMergeText() { + return sprintf( + __( + `Fast-forward merge is not possible. Rebase the source branch onto %{startTag}${this.mr.targetBranch}%{endTag} to allow this merge request to be merged.`, + ), + { + startTag: '<span class="label-branch">', + endTag: '</span>', + }, + ); + }, }, methods: { rebase() { @@ -54,7 +66,7 @@ export default { .catch(error => { this.rebasingError = error.merge_error; this.isMakingRequest = false; - Flash('Something went wrong. Please try again.'); + Flash(__('Something went wrong. Please try again.')); }); }, checkRebaseStatus(continuePolling, stopPolling) { @@ -69,7 +81,7 @@ export default { if (res.merge_error && res.merge_error.length) { this.rebasingError = res.merge_error; - Flash('Something went wrong. Please try again.'); + Flash(__('Something went wrong. Please try again.')); } eventHub.$emit('MRWidgetRebaseSuccess'); @@ -78,7 +90,7 @@ export default { }) .catch(() => { this.isMakingRequest = false; - Flash('Something went wrong. Please try again.'); + Flash(__('Something went wrong. Please try again.')); stopPolling(); }); }, @@ -91,19 +103,14 @@ export default { <div class="rebase-state-find-class-convention media media-body space-children"> <template v-if="mr.rebaseInProgress || isMakingRequest"> - <span class="bold"> Rebase in progress </span> + <span class="bold">{{ __('Rebase in progress') }}</span> </template> <template v-if="!mr.rebaseInProgress && !mr.canPushToSourceBranch"> - <span class="bold"> - Fast-forward merge is not possible. Rebase the source branch onto - <span class="label-branch">{{ mr.targetBranch }}</span> to allow this merge request to be - merged. - </span> + <span class="bold" v-html="fastForwardMergeText"></span> </template> <template v-if="!mr.rebaseInProgress && mr.canPushToSourceBranch && !isMakingRequest"> <div - class="accept-merge-holder clearfix -js-toggle-container accept-action media space-children" + class="accept-merge-holder clearfix js-toggle-container accept-action media space-children" > <button :disabled="isMakingRequest" @@ -111,14 +118,14 @@ js-toggle-container accept-action media space-children" class="btn btn-sm btn-reopen btn-success qa-mr-rebase-button" @click="rebase" > - <gl-loading-icon v-if="isMakingRequest" /> - Rebase + <gl-loading-icon v-if="isMakingRequest" />{{ __('Rebase') }} </button> - <span v-if="!rebasingError" class="bold"> - Fast-forward merge is not possible. Rebase the source branch onto the target branch or - merge target branch into source branch to allow this merge request to be merged. - </span> - <span v-else class="bold danger"> {{ rebasingError }} </span> + <span v-if="!rebasingError" class="bold">{{ + __( + 'Fast-forward merge is not possible. Rebase the source branch onto the target branch or merge target branch into source branch to allow this merge request to be merged.', + ) + }}</span> + <span v-else class="bold danger">{{ rebasingError }}</span> </div> </template> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue index a38495bb4cc..7312b31c01c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue @@ -22,19 +22,29 @@ export default { <span v-html="emptyStateSVG"></span> </div> <div class="text col-md-7 order-md-first col-12"> - <span> - Merge requests are a place to propose changes you have made to a project and discuss those - changes with others. - </span> - <p>Interested parties can even contribute by pushing commits if they want to.</p> + <span>{{ + s__( + 'mrWidgetNothingToMerge|Merge requests are a place to propose changes you have made to a project and discuss those changes with others.', + ) + }}</span> <p> - Currently there are no changes in this merge request's source branch. Please push new - commits or use a different branch. + {{ + s__( + 'mrWidgetNothingToMerge|Interested parties can even contribute by pushing commits if they want to.', + ) + }} + </p> + <p> + {{ + s__( + "mrWidgetNothingToMerge|Currently there are no changes in this merge request's source branch. Please push new commits or use a different branch.", + ) + }} </p> <div> - <a v-if="mr.newBlobPath" :href="mr.newBlobPath" class="btn btn-inverted btn-success"> - Create file - </a> + <a v-if="mr.newBlobPath" :href="mr.newBlobPath" class="btn btn-inverted btn-success">{{ + __('Create file') + }}</a> </div> </div> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue index ca1b4a57717..d4514767912 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue @@ -6,6 +6,7 @@ import simplePoll from '~/lib/utils/simple_poll'; import { __ } from '~/locale'; import readyToMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/ready_to_merge'; import MergeRequest from '../../../merge_request'; +import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; import Flash from '../../../flash'; import statusIcon from '../mr_widget_status_icon.vue'; import eventHub from '../../event_hub'; @@ -174,6 +175,8 @@ export default { MergeRequest.decreaseCounter(); stopPolling(); + refreshUserMergeRequestCounts(); + // If user checked remove source branch and we didn't remove the branch yet // we should start another polling for source branch remove process if (this.removeSourceBranch && data.source_branch_exists) { @@ -248,7 +251,7 @@ export default { type="button" class="btn btn-sm btn-info dropdown-toggle js-merge-moment" data-toggle="dropdown" - aria-label="Select merge moment" + :aria-label="__('Select merge moment')" > <i class="fa fa-chevron-down qa-merge-moment-dropdown" aria-hidden="true"></i> </button> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue index 7c322388d30..91c0b40a0b5 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue @@ -46,14 +46,20 @@ export default { <status-icon :show-disabled-button="Boolean(mr.removeWIPPath)" status="warning" /> <div class="media-body space-children"> <span class="bold"> - This is a Work in Progress + {{ __('This is a Work in Progress') }} <i v-tooltip class="fa fa-question-circle" - title="When this merge request is ready, - remove the WIP: prefix from the title to allow it to be merged" - aria-label="When this merge request is ready, - remove the WIP: prefix from the title to allow it to be merged" + :title=" + s__( + 'mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged', + ) + " + :aria-label=" + s__( + 'mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged', + ) + " > </i> </span> @@ -64,8 +70,8 @@ export default { class="btn btn-default btn-sm js-remove-wip" @click="removeWIP" > - <i v-if="isMakingRequest" class="fa fa-spinner fa-spin" aria-hidden="true"> </i> Resolve WIP - status + <i v-if="isMakingRequest" class="fa fa-spinner fa-spin" aria-hidden="true"> </i> + {{ s__('mrWidget|Resolve WIP status') }} </button> </div> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index a79da476890..8d415c1bbea 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -263,8 +263,11 @@ export default { if (!data.pipeline) return; const { label } = data.pipeline.details.status; - const title = `Pipeline ${label}`; - const message = `Pipeline ${label} for "${data.title}"`; + const title = sprintf(__('Pipeline %{label}'), { label }); + const message = sprintf(__('Pipeline %{label} for "%{dataTitle}"'), { + dataTitle: data.title, + label, + }); notify.notifyMe(title, message, this.mr.gitlabLogo); }, diff --git a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue index e9ab6f5ba7a..15cb0bd9792 100644 --- a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue +++ b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue @@ -1,7 +1,6 @@ <script> import { GlTooltipDirective } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; -import { pluralize } from '~/lib/utils/text_utility'; import { __, sprintf } from '~/locale'; import { getCommitIconMap } from '~/ide/utils'; @@ -69,7 +68,7 @@ export default { }); } else if (this.file.changed && this.file.staged) { return sprintf(__('Unstaged and staged %{type}'), { - type: pluralize(type), + type, }); } diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index a1168fa0f1e..ae9b013d980 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -1,6 +1,7 @@ <script> import _ from 'underscore'; import { GlTooltipDirective, GlLink } from '@gitlab/ui'; +import { __, sprintf } from '~/locale'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import UserAvatarLink from './user_avatar/user_avatar_link.vue'; import Icon from '../../vue_shared/components/icon.vue'; @@ -129,7 +130,9 @@ export default { * @returns {String} */ userImageAltDescription() { - return this.author && this.author.username ? `${this.author.username}'s avatar` : null; + return this.author && this.author.username + ? sprintf(__("%{username}'s avatar"), { username: this.author.username }) + : null; }, }, }; @@ -180,7 +183,7 @@ export default { {{ title }} </gl-link> </tooltip-on-truncate> - <span v-else> Can't find HEAD commit for this branch </span> + <span v-else>{{ __("Can't find HEAD commit for this branch") }}</span> </div> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js b/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js index ba63683f5c0..da0b45110e2 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js +++ b/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js @@ -3,6 +3,7 @@ import { __ } from '~/locale'; const viewers = { image: { id: 'image', + binary: true, }, markdown: { id: 'markdown', diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue index 2ca933a37d2..fc6a45b957e 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue @@ -91,7 +91,9 @@ export default { | </template> <template v-if="hasDimensions"> - <strong>W</strong>: {{ width }} | <strong>H</strong>: {{ height }} + <strong>{{ s__('ImageViewerDimensions|W') }}</strong + >: {{ width }} | <strong>{{ s__('ImageViewerDimensions|H') }}</strong + >: {{ height }} </template> </p> </div> diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue index 5fdc915fffb..655f0054887 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue @@ -40,7 +40,7 @@ export default { this.fetchMarkdownPreview(); }, destroyed() { - if (this.isLoading) axiosSource.cancel('Cancelling Preview'); + if (this.isLoading) axiosSource.cancel(__('Cancelling Preview')); }, methods: { fetchMarkdownPreview() { diff --git a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue index 36b3ee05456..d5558d93219 100644 --- a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue +++ b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue @@ -1,5 +1,7 @@ <script> /* eslint-disable vue/require-default-prop */ +import { __ } from '~/locale'; + export default { name: 'DeprecatedModal', // use GlModal instead @@ -39,7 +41,7 @@ export default { closeButtonLabel: { type: String, required: false, - default: 'Cancel', + default: __('Cancel'), }, primaryButtonLabel: { type: String, @@ -94,7 +96,7 @@ export default { type="button" class="close float-right" data-dismiss="modal" - aria-label="Close" + :aria-label="__('Close')" @click="emitCancel($event)" > <span aria-hidden="true">×</span> diff --git a/app/assets/javascripts/vue_shared/components/droplab_dropdown_button.vue b/app/assets/javascripts/vue_shared/components/droplab_dropdown_button.vue index 7d49c87271d..c35fee84771 100644 --- a/app/assets/javascripts/vue_shared/components/droplab_dropdown_button.vue +++ b/app/assets/javascripts/vue_shared/components/droplab_dropdown_button.vue @@ -69,7 +69,7 @@ export default { data-display="static" data-toggle="dropdown" > - <icon name="arrow-down" aria-label="toggle dropdown" /> + <icon name="arrow-down" :aria-label="__('toggle dropdown')" /> </button> <ul :class="dropdownClass" class="dropdown-menu dropdown-open-top"> <template v-for="(action, index) in actions"> diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue index 4e5dfbf3bf8..20bcceeb477 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue @@ -115,7 +115,7 @@ export default { data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" - aria-label="Expand dropdown" + :aria-label="__('Expand dropdown')" > <icon name="angle-down" :size="12" /> </button> @@ -125,7 +125,7 @@ export default { ref="searchInput" v-model="filter" type="search" - placeholder="Filter" + :placeholder="__('Filter')" class="js-filtered-dropdown-input dropdown-input-field" /> <icon class="dropdown-input-search" name="search" /> diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index 3f45dc7853b..c652a684d7c 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -1,5 +1,6 @@ <script> import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui'; +import { __, sprintf } from '~/locale'; import CiIconBadge from './ci_badge_link.vue'; import TimeagoTooltip from './time_ago_tooltip.vue'; import UserAvatarImage from './user_avatar/user_avatar_image.vue'; @@ -65,7 +66,7 @@ export default { computed: { userAvatarAltText() { - return `${this.user.name}'s avatar`; + return sprintf(__(`%{username}'s avatar`), { username: this.user.name }); }, }, @@ -87,16 +88,12 @@ export default { <strong> {{ itemName }} #{{ itemId }} </strong> - <template v-if="shouldRenderTriggeredLabel"> - triggered - </template> - <template v-else> - created - </template> + <template v-if="shouldRenderTriggeredLabel">{{ __('triggered') }}</template> + <template v-else>{{ __('created') }}</template> <timeago-tooltip :time="time" /> - by + {{ __('by') }} <template v-if="user"> <gl-link diff --git a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue index e438ff16a41..47f0851f650 100644 --- a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue +++ b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue @@ -1,7 +1,7 @@ <script> import { GlLink } from '@gitlab/ui'; import _ from 'underscore'; -import { sprintf } from '~/locale'; +import { __, sprintf } from '~/locale'; import icon from '../../../vue_shared/components/icon.vue'; function buildDocsLinkStart(path) { @@ -47,7 +47,9 @@ export default { }, confidentialAndLockedDiscussionText() { return sprintf( - 'This issue is %{confidentialLinkStart}confidential%{linkEnd} and %{lockedLinkStart}locked%{linkEnd}.', + __( + 'This issue is %{confidentialLinkStart}confidential%{linkEnd} and %{lockedLinkStart}locked%{linkEnd}.', + ), { confidentialLinkStart: buildDocsLinkStart(this.confidentialIssueDocsPath), lockedLinkStart: buildDocsLinkStart(this.lockedIssueDocsPath), @@ -66,7 +68,7 @@ export default { <span v-if="isLockedAndConfidential"> <span v-html="confidentialAndLockedDiscussionText"></span> {{ - __(`People without permission will never get a notification and won't be able to comment.`) + __("People without permission will never get a notification and won't be able to comment.") }} </span> diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue index eb0f666422f..b76679960ca 100644 --- a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue +++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue @@ -160,8 +160,8 @@ export default { :disabled="removeDisabled" type="button" class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button mr-xl-0 align-self-xl-center" - title="Remove" - aria-label="Remove" + :title="__('Remove')" + :aria-label="__('Remove')" @click="onRemoveRequest" > <icon :size="16" class="btn-item-remove-icon" name="close" /> diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 3bdc0bb8ebd..b520d302407 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -1,7 +1,7 @@ <script> import $ from 'jquery'; import _ from 'underscore'; -import { __ } from '~/locale'; +import { __, sprintf } from '~/locale'; import { stripHtml } from '~/lib/utils/text_utility'; import Flash from '../../../flash'; import GLForm from '../../../gl_form'; @@ -118,6 +118,18 @@ export default { lineType() { return this.line ? this.line.type : ''; }, + addMultipleToDiscussionWarning() { + return sprintf( + __( + '%{icon}You are about to add %{usersTag} people to the discussion. Proceed with caution.', + ), + { + icon: '<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>', + usersTag: `<strong><span class="js-referenced-users-count">${this.referencedUsers.length}</span></strong>`, + }, + false, + ); + }, }, mounted() { /* @@ -172,7 +184,7 @@ export default { renderMarkdown(data = {}) { this.markdownPreviewLoading = false; - this.markdownPreview = data.body || 'Nothing to preview.'; + this.markdownPreview = data.body || __('Nothing to preview.'); if (data.references) { this.referencedCommands = data.references.commands; @@ -207,7 +219,11 @@ export default { <div v-show="!previewMarkdown" class="md-write-holder"> <div class="zen-backdrop"> <slot name="textarea"></slot> - <a class="zen-control zen-control-leave js-zen-leave" href="#" aria-label="Enter zen mode"> + <a + class="zen-control zen-control-leave js-zen-leave" + href="#" + :aria-label="__('Enter zen mode')" + > <icon :size="32" name="screen-normal" /> </a> <markdown-toolbar @@ -246,13 +262,7 @@ export default { <template v-if="previewMarkdown && !markdownPreviewLoading"> <div v-if="referencedCommands" class="referenced-commands" v-html="referencedCommands"></div> <div v-if="shouldShowReferencedUsers" class="referenced-users"> - <span> - <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> You are about to add - <strong> - <span class="js-referenced-users-count">{{ referencedUsers.length }}</span> - </strong> - people to the discussion. Proceed with caution. - </span> + <span v-html="addMultipleToDiscussionWarning"></span> </div> </template> </div> diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue index 8d3705e1e4a..7f0fcfac071 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue @@ -1,5 +1,6 @@ <script> import Vue from 'vue'; +import { __ } from '~/locale'; import SuggestionDiff from './suggestion_diff.vue'; import Flash from '~/flash'; @@ -56,7 +57,7 @@ export default { const suggestionElements = container.querySelectorAll('.js-render-suggestion'); if (this.lineType === 'old') { - Flash('Unable to apply suggestions to a deleted line.', 'alert', this.$el); + Flash(__('Unable to apply suggestions to a deleted line.'), 'alert', this.$el); } suggestionElements.forEach((suggestionEl, i) => { diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue index d6c398c8946..8ce5b615795 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue @@ -33,13 +33,18 @@ export default { <div class="comment-toolbar clearfix"> <div class="toolbar-text"> <template v-if="!hasQuickActionsDocsPath && markdownDocsPath"> - <gl-link :href="markdownDocsPath" target="_blank" tabindex="-1" - >Markdown is supported</gl-link - > + <gl-link :href="markdownDocsPath" target="_blank" tabindex="-1">{{ + __('Markdown is supported') + }}</gl-link> </template> <template v-if="hasQuickActionsDocsPath && markdownDocsPath"> - <gl-link :href="markdownDocsPath" target="_blank" tabindex="-1">Markdown</gl-link> and - <gl-link :href="quickActionsDocsPath" target="_blank" tabindex="-1">quick actions</gl-link> + <gl-link :href="markdownDocsPath" target="_blank" tabindex="-1">{{ + __('Markdown') + }}</gl-link> + and + <gl-link :href="quickActionsDocsPath" target="_blank" tabindex="-1">{{ + __('quick actions') + }}</gl-link> are supported </template> </div> @@ -57,15 +62,17 @@ export default { <i class="fa fa-file-image-o toolbar-button-icon" aria-hidden="true"></i> </span> <span class="uploading-error-message"></span> - <button class="retry-uploading-link" type="button">Try again</button> or - <button class="attach-new-file markdown-selector" type="button">attach a new file</button> + <button class="retry-uploading-link" type="button">{{ __('Try again') }}</button> or + <button class="attach-new-file markdown-selector" type="button"> + {{ __('attach a new file') }} + </button> </span> <button class="markdown-selector button-attach-file btn-link" tabindex="-1" type="button"> <i class="fa fa-file-image-o toolbar-button-icon" aria-hidden="true"></i - ><span class="text-attach-file">Attach a file</span> + ><span class="text-attach-file">{{ __('Attach a file') }}</span> </button> <button class="btn btn-default btn-sm hide button-cancel-uploading-files" type="button"> - Cancel + {{ __('Cancel') }} </button> </span> </div> diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.vue b/app/assets/javascripts/vue_shared/components/memory_graph.vue index 16f4ff068f6..26d7d8e8866 100644 --- a/app/assets/javascripts/vue_shared/components/memory_graph.vue +++ b/app/assets/javascripts/vue_shared/components/memory_graph.vue @@ -1,4 +1,5 @@ <script> +import { __, sprintf } from '~/locale'; import { getTimeago } from '../../lib/utils/datetime_utility'; export default { @@ -20,7 +21,7 @@ export default { computed: { getFormattedMedian() { const deployedSince = getTimeago().format(this.deploymentTime * 1000); - return `Deployed ${deployedSince}`; + return sprintf(__('Deployed %{deployedSince}'), { deployedSince }); }, }, mounted() { diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue index 3c86b7e4c61..d6dfe9eded8 100644 --- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue @@ -103,7 +103,7 @@ export default { <div v-if="hasMoreCommits" class="flex-list"> <div class="system-note-commit-list-toggler flex-row" @click="expanded = !expanded"> <icon :name="toggleIcon" :size="8" class="append-right-5" /> - <span>Toggle commit list</span> + <span>{{ __('Toggle commit list') }}</span> </div> </div> </div> diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue index b9311d65360..43bbb756805 100644 --- a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue +++ b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue @@ -14,7 +14,7 @@ /> */ - +import { __ } from '~/locale'; import defaultAvatarUrl from 'images/no_avatar.png'; import { placeholderImage } from '../../../lazy_loader'; @@ -39,7 +39,7 @@ export default { imgAlt: { type: String, required: false, - default: 'project avatar', + default: __('project avatar'), }, size: { type: Number, diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue index b5e43da401e..4dcc121496c 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue @@ -85,7 +85,7 @@ export default { @click="toggleSidebar" > <span class="sidebar-collapsed-value"> - <span v-if="showFromText">From</span> <span>{{ dateText('min') }}</span> + <span v-if="showFromText">{{ __('From') }}</span> <span>{{ dateText('min') }}</span> </span> </collapsed-calendar-icon> <div v-if="hasMinAndMaxDates" class="text-center sidebar-collapsed-divider">-</div> @@ -96,7 +96,7 @@ export default { @click="toggleSidebar" > <span class="sidebar-collapsed-value"> - <span v-if="!minDate">Until</span> <span>{{ dateText('max') }}</span> + <span v-if="!minDate">{{ __('Until') }}</span> <span>{{ dateText('max') }}</span> </span> </collapsed-calendar-icon> </div> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue index 45f01a6fced..6caf8bc92c2 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue @@ -74,7 +74,7 @@ export default { return dateInWords(this.selectedDate, true); }, collapsedText() { - return this.selectedDateWords ? this.selectedDateWords : 'None'; + return this.selectedDateWords ? this.selectedDateWords : __('None'); }, }, methods: { @@ -112,7 +112,7 @@ export default { class="btn-blank btn-link btn-primary-hover-link btn-sidebar-action" @click="toggleDatePicker" > - Edit + {{ __('Edit') }} </button> <toggle-sidebar v-if="showToggleSidebar" :collapsed="collapsed" @toggle="toggleSidebar" /> </div> @@ -137,11 +137,11 @@ export default { class="btn-blank btn-link btn-secondary-hover-link" @click="newDateSelected(null)" > - remove + {{ __('remove') }} </button> </span> </template> - <span v-else class="no-value"> None </span> + <span v-else class="no-value">{{ __('None') }}</span> </span> </div> </div> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue index 3b5ce0e9910..913c971a512 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue @@ -48,7 +48,7 @@ export default { 'fa-angle-double-right': !collapsed, 'fa-angle-double-left': collapsed, }" - aria-label="toggle collapse" + :aria-label="__('toggle collapse')" class="fa" > </i> diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue index a6c1737dcab..ea483416c46 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue @@ -17,6 +17,7 @@ import { GlTooltip } from '@gitlab/ui'; import defaultAvatarUrl from 'images/no_avatar.png'; +import { __ } from '~/locale'; import { placeholderImage } from '../../../lazy_loader'; export default { @@ -43,7 +44,7 @@ export default { imgAlt: { type: String, required: false, - default: 'user avatar', + default: __('user avatar'), }, size: { type: Number, diff --git a/app/assets/javascripts/vue_shared/directives/tooltip.js b/app/assets/javascripts/vue_shared/directives/tooltip.js index 2d1f7a1cfd0..73e92728cb9 100644 --- a/app/assets/javascripts/vue_shared/directives/tooltip.js +++ b/app/assets/javascripts/vue_shared/directives/tooltip.js @@ -3,8 +3,12 @@ import '~/commons/bootstrap'; export default { bind(el) { + const glTooltipDelay = localStorage.getItem('gl-tooltip-delay'); + const delay = glTooltipDelay ? JSON.parse(glTooltipDelay) : 0; + $(el).tooltip({ trigger: 'hover', + delay, }); }, diff --git a/app/assets/javascripts/vue_shared/mixins/is_ee.js b/app/assets/javascripts/vue_shared/mixins/is_ee.js deleted file mode 100644 index 8e00d93ef18..00000000000 --- a/app/assets/javascripts/vue_shared/mixins/is_ee.js +++ /dev/null @@ -1,10 +0,0 @@ -import Vue from 'vue'; -import { isEE } from '~/lib/utils/common_utils'; - -Vue.mixin({ - computed: { - isEE() { - return isEE(); - }, - }, -}); diff --git a/app/assets/stylesheets/components/toast.scss b/app/assets/stylesheets/components/toast.scss index acbd909d595..e27bf282247 100644 --- a/app/assets/stylesheets/components/toast.scss +++ b/app/assets/stylesheets/components/toast.scss @@ -15,11 +15,15 @@ .toasted.gl-toast { border-radius: $border-radius-default; font-size: $gl-font-size; - padding: $gl-padding-8 $gl-padding-24; + padding: $gl-padding-8 $gl-padding $gl-padding-8 $gl-padding-24; margin-top: $toast-default-margin; line-height: $gl-line-height; background-color: rgba($gray-900, $toast-background-opacity); + span { + padding-right: $gl-padding-8; + } + @include media-breakpoint-down(xs) { .action:first-of-type { // Ensures actions buttons are right aligned on mobile @@ -29,19 +33,14 @@ .action { color: $blue-300; - margin: 0 0 0 $toast-action-margin-left; + margin: 0 0 0 $toast-default-margin; text-transform: none; font-size: $gl-font-size; - - &:first-of-type { - padding-right: 0; - } } .toast-close { font-size: $default-icon-size; margin-left: $toast-default-margin; - padding-left: $gl-padding; } } } diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 9b1d9d51f9c..82b4ec750ff 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -9,7 +9,6 @@ @import 'framework/animations'; @import 'framework/vue_transitions'; -@import 'framework/asciidoctor'; @import 'framework/banner'; @import 'framework/blocks'; @import 'framework/buttons'; diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index 6f5a2e561af..6bc5632365f 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -104,7 +104,7 @@ } .btn { - @include transition(background-color, border-color, color, box-shadow); + @include transition(border-color); } .dropdown-menu-toggle, diff --git a/app/assets/stylesheets/framework/asciidoctor.scss b/app/assets/stylesheets/framework/asciidoctor.scss deleted file mode 100644 index 1586265d40e..00000000000 --- a/app/assets/stylesheets/framework/asciidoctor.scss +++ /dev/null @@ -1,27 +0,0 @@ -.admonitionblock td.icon { - width: 1%; - - [class^='fa icon-'] { - @extend .fa-2x; - } - - .icon-note { - @extend .fa-thumb-tack; - } - - .icon-tip { - @extend .fa-lightbulb-o; - } - - .icon-warning { - @extend .fa-exclamation-triangle; - } - - .icon-caution { - @extend .fa-fire; - } - - .icon-important { - @extend .fa-exclamation-circle; - } -} diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 767832e242c..e0b6da31261 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -24,12 +24,11 @@ border-radius: $border-radius-default; font-size: $gl-font-size; font-weight: $gl-font-weight-normal; - padding: $gl-vert-padding $gl-btn-padding; + padding: $gl-bordered-btn-vert-padding $gl-bordered-btn-horz-padding; &:focus, &:active { background-color: $btn-active-gray; - box-shadow: $gl-btn-active-background; } } @@ -50,77 +49,89 @@ color: $text; } - &:hover, - &:focus { - background-color: $hover-background; - border-color: $hover-border; - color: $hover-text; + &:not(:disabled):not(.disabled) { + &:hover { + box-shadow: inset 0 0 0 1px $hover-border, 0 2px 2px 0 $gl-btn-hover-shadow-light; + } - > .icon { - color: $hover-text; + &:focus { + box-shadow: inset 0 0 0 1px $hover-border, 0 0 4px 1px $blue-300; } - } - &:focus { - box-shadow: 0 0 4px 1px $blue-300; - } + &:hover, + &:focus { + background-color: $hover-background; + border-color: $hover-border; + color: $hover-text; - &:active { - background-color: $active-background; - border-color: $active-border; - box-shadow: inset 0 2px 4px 0 rgba($black, 0.2); - color: $active-text; + > .icon { + color: $hover-text; + } + } - > .icon { + &:active, + &:active:focus { + background-color: $active-background; + border-color: $active-border; + box-shadow: inset 0 0 0 1px $hover-border, inset 0 2px 4px 0 rgba($black, 0.2); color: $active-text; - } - &:focus { - box-shadow: inset 0 2px 4px 0 rgba($black, 0.2); + > .icon { + color: $active-text; + } } } } -@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) { +@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color, $hover-shadow-color: $gl-btn-hover-shadow-dark) { background-color: $light; border-color: $border-light; color: $color; - &:hover, - &:focus { - background-color: $normal; - border-color: $border-normal; - color: $color; - } + &:not(:disabled):not(.disabled) { + &:hover { + box-shadow: inset 0 0 0 1px $border-normal, 0 2px 2px 0 $hover-shadow-color; + } - &:active, - &.active { - box-shadow: $gl-btn-active-background; + &:focus { + box-shadow: inset 0 0 0 1px $border-normal, 0 0 4px 1px $blue-300; + } - background-color: $dark; - border-color: $border-dark; - color: $color; + &:hover, + &:focus { + background-color: $normal; + border-color: $border-normal; + color: $color; + } + + &:active, + &.active { + box-shadow: inset 0 2px 4px 0 $gl-btn-hover-shadow-dark; + background-color: $dark; + border-color: $border-dark; + color: $color; + } } } @mixin btn-green { - @include btn-color($green-500, $green-600, $green-600, $green-700, $green-700, $green-800, $white-light); + @include btn-color($green-500, $green-600, $green-500, $green-700, $green-600, $green-800, $white-light); } @mixin btn-blue { - @include btn-color($blue-500, $blue-600, $blue-600, $blue-700, $blue-700, $blue-800, $white-light); + @include btn-color($blue-500, $blue-600, $blue-500, $blue-700, $blue-600, $blue-800, $white-light); } @mixin btn-orange { - @include btn-color($orange-500, $orange-600, $orange-600, $orange-700, $orange-700, $orange-800, $white-light); + @include btn-color($orange-500, $orange-600, $orange-500, $orange-700, $orange-600, $orange-800, $white-light); } @mixin btn-red { - @include btn-color($red-500, $red-600, $red-600, $red-700, $red-700, $red-800, $white-light); + @include btn-color($red-500, $red-600, $red-500, $red-700, $red-600, $red-800, $white-light); } @mixin btn-white { - @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-gray-dark, $gl-text-color); + @include btn-color($white-light, $gray-400, $gray-200, $gray-400, $gl-gray-200, $gray-500, $gl-text-color, $gl-btn-hover-shadow-light); } @mixin btn-with-margin { @@ -149,23 +160,22 @@ color: $gl-text-color; white-space: nowrap; + line-height: $gl-btn-line-height; &:focus:active { outline: 0; } - &.btn-sm { - padding: 4px 10px; - font-size: $gl-btn-small-font-size; - line-height: $gl-btn-small-line-height; - } - &.btn-xs { - padding: 2px $gl-btn-padding; font-size: $gl-btn-xs-font-size; line-height: $gl-btn-xs-line-height; } + &.btn-sm, + &.btn-xs { + padding: 3px $gl-bordered-btn-vert-padding; + } + &.btn-success, &.btn-register { @include btn-green; @@ -239,7 +249,7 @@ &.dropdown-toggle { .fa-caret-down { - margin-left: 3px; + margin: 0; } } @@ -272,10 +282,7 @@ } svg { - height: 15px; - width: 15px; - position: relative; - top: 2px; + @include btn-svg; } svg, @@ -330,6 +337,12 @@ &.btn-grouped { @include btn-with-margin; } + + .btn { + border-radius: $border-radius-default; + font-size: $gl-font-size; + line-height: $gl-btn-line-height; + } } .btn-clipboard { @@ -487,18 +500,25 @@ &:active, &:focus { color: $gl-text-color-secondary; + border: 1px solid $border-gray-normal-dashed; background-color: $white-normal; } } -.btn-svg svg { - @include btn-svg; +.btn-svg { + padding: $gl-bordered-btn-vert-padding; + + svg { + @include btn-svg; + display: block; + } } // All disabled buttons, regardless of color, type, etc %disabled { background-color: $gray-light !important; border-color: $gray-200 !important; + box-shadow: none; color: $gl-text-color-disabled !important; opacity: 1 !important; cursor: default !important; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index a12029d2419..05afcecca05 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -8,12 +8,6 @@ } } -@mixin chevron-active { - .fa-chevron-down { - color: $gray-darkest; - } -} - @mixin set-visible { transform: translateY(0); display: block; @@ -49,7 +43,6 @@ .dropdown-toggle, .dropdown-menu-toggle { - @include chevron-active; border-color: $gray-darkest; } @@ -63,13 +56,14 @@ margin-top: 11px; } -.dropdown-toggle { - padding: 6px 8px 6px 10px; +.dropdown-toggle, +.confidential-merge-request-fork-group .dropdown-toggle { + padding: $gl-bordered-btn-vert-padding $gl-bordered-btn-horz-padding; background-color: $white-light; color: $gl-text-color; font-size: 14px; + line-height: $gl-btn-line-height; text-align: left; - border: 1px solid $border-color; border-radius: $border-radius-base; white-space: nowrap; @@ -102,10 +96,6 @@ padding-right: 25px; } - .fa { - color: $gray-darkest; - } - .fa-chevron-down { font-size: $dropdown-chevron-size; position: relative; @@ -114,12 +104,10 @@ } &:hover { - @include chevron-active; border-color: $gray-darkest; } &:focus:active { - @include chevron-active; border-color: $dropdown-toggle-active-border-color; outline: 0; } @@ -287,8 +275,8 @@ list-style: none; padding: 0 1px; - a:not(.help-link), - button:not(.btn), + a, + button, .menu-item { @include dropdown-link; } diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 954551fef97..460d9ea9526 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -265,7 +265,6 @@ ul.controls { } .issuable-pipeline-broken a, - .issuable-pipeline-status a, .author-link { display: flex; } diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index f75e5b55506..fd9a75bc5b6 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -19,23 +19,31 @@ } } - // leave enough space for the close icon .modal-title { + line-height: $gl-line-height-24; + + // leave enough space for the close icon &.mw-100, &.w-100 { - // after upgrading to Bootstrap 4.2 we can use $modal-header-padding-x here - // https://github.com/twbs/bootstrap/pull/26976 - margin-right: -28px; - padding-right: 28px; + margin-right: -$modal-header-padding-x; + padding-right: $modal-header-padding-x; } } + + .close { + font-weight: $gl-font-weight-normal; + line-height: $gl-line-height; + color: $gray-900; + opacity: 1; + } } .modal-body { background-color: $modal-body-bg; line-height: $line-height-base; position: relative; - padding: #{3 * $grid-size} #{2 * $grid-size}; + min-height: $modal-body-height; + padding: #{2 * $grid-size} #{6 * $grid-size} #{2 * $grid-size} #{2 * $grid-size}; text-align: left; white-space: normal; @@ -52,12 +60,20 @@ display: flex; flex-direction: row; + .btn { + margin: 0; + } + .btn + .btn:not(.dropdown-toggle-split), .btn + .btn-group, .btn-group + .btn { margin-left: $grid-size; } + .btn-group .btn + .btn { + margin-left: -1px; + } + @include media-breakpoint-down(xs) { flex-direction: column; @@ -67,6 +83,11 @@ margin-left: 0; margin-top: $grid-size; } + + .btn-group .btn + .btn { + margin-left: -1px; + margin-top: 0; + } } } @@ -85,9 +106,23 @@ body.modal-open { .modal { background-color: $black-transparent; - @include media-breakpoint-up(md) { + .modal-content { + border-radius: $modal-border-radius; + + > :first-child { + border-top-left-radius: $modal-border-radius; + border-top-right-radius: $modal-border-radius; + } + + > :last-child { + border-bottom-left-radius: $modal-border-radius; + border-bottom-right-radius: $modal-border-radius; + } + } + + @include media-breakpoint-up(sm) { .modal-dialog { - margin: 30px auto; + margin: 64px auto; } } } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 7baab478034..c201605e83d 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -1,5 +1,5 @@ /** - * Apply Markdown typography + * Apply Markup (Markdown/AsciiDoc) typography * */ .md:not(.use-csslab) { @@ -245,6 +245,21 @@ } } + ul.checklist, + ul.none, + ol.none, + ul.no-bullet, + ol.no-bullet, + ol.unnumbered, + ul.unstyled, + ol.unstyled { + list-style-type: none; + + li { + margin-left: 0; + } + } + li { line-height: 1.6em; margin-left: 25px; @@ -321,6 +336,54 @@ visibility: visible; } } + + .big { + font-size: larger; + } + + .small { + font-size: smaller; + } + + .underline { + text-decoration: underline; + } + + .overline { + text-decoration: overline; + } + + .line-through { + text-decoration: line-through; + } + + .admonitionblock td.icon { + width: 1%; + + [class^='fa icon-'] { + @extend .fa-2x; + } + + .icon-note { + @extend .fa-thumb-tack; + } + + .icon-tip { + @extend .fa-lightbulb-o; + } + + .icon-warning { + @extend .fa-exclamation-triangle; + } + + .icon-caution { + @extend .fa-fire; + } + + .icon-important { + @extend .fa-exclamation-circle; + } + } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index b6a24247d40..047a9799c3f 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -405,6 +405,8 @@ $tanuki-yellow: #fca326; */ $green-500-focus: rgba($green-500, 0.4); $gl-btn-active-background: rgba(0, 0, 0, 0.16); +$gl-btn-hover-shadow-dark: rgba($black, 0.2); +$gl-btn-hover-shadow-light: rgba($black, 0.1); $gl-btn-active-gradient: inset 0 2px 3px $gl-btn-active-background; /* @@ -481,6 +483,8 @@ $gl-btn-padding: 10px; $gl-btn-line-height: 16px; $gl-btn-vert-padding: 8px; $gl-btn-horz-padding: 12px; +$gl-bordered-btn-vert-padding: $gl-btn-vert-padding - 1px; +$gl-bordered-btn-horz-padding: $gl-btn-horz-padding - 1px; $gl-btn-small-font-size: 13px; $gl-btn-small-line-height: 18px; $gl-btn-xs-font-size: 13px; @@ -507,7 +511,6 @@ $toast-height: 48px; $toast-max-width: 586px; $toast-padding-right: 42px; $toast-default-margin: 8px; -$toast-action-margin-left: 16px; $toast-background-opacity: 0.95; /* @@ -805,8 +808,9 @@ $border-color-settings: #e1e1e1; /* Modals */ -$modal-body-height: 134px; +$modal-body-height: 80px; $modal-border-color: #e9ecef; +$modal-border-radius: 0.25rem; $priority-label-empty-state-width: 114px; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index e12ea6fcb99..ffc6e433988 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -214,10 +214,10 @@ .label, .btn { - padding: $gl-vert-padding $gl-btn-padding; + padding: $gl-bordered-btn-vert-padding $gl-bordered-btn-horz-padding; border: 1px $border-color solid; font-size: $gl-font-size; - line-height: $line-height-base; + line-height: $gl-btn-line-height; border-radius: 0; display: flex; align-items: center; diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 623c44e062f..3ffe8ae304d 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -1095,6 +1095,10 @@ table.code { .discussion-collapsible { margin: 0 $gl-padding $gl-padding 71px; + + .notes { + border-radius: $border-radius-default; + } } .parallel { diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 6a0127eb51c..66ea70e79da 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -929,10 +929,6 @@ margin: 0; } } - - .dropdown-toggle > .icon { - margin: 0 3px; - } } .right-sidebar-collapsed { diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 8359a60ec9f..e51ca44476c 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -267,7 +267,6 @@ ul.related-merge-requests > li { .fa-caret-down { pointer-events: none; color: inherit; - margin-left: 0; } } } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index c6bac33e888..1d57a4a4784 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -417,7 +417,6 @@ table { i { color: $white-light; - padding-right: 2px; margin-top: 2px; } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 7bd1a4138e4..b9b8eabf909 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -139,7 +139,6 @@ $note-form-margin-left: 72px; border-radius: 4px 4px 0 0; &.collapsed { - border: 0; border-radius: 4px; } } @@ -406,7 +405,7 @@ $note-form-margin-left: 72px; border-radius: 0; @media (min-width: map-get($grid-breakpoints, md)) { - top: 91px; + top: $mr-tabs-height + $header-height; .with-performance-bar & { top: 126px; @@ -598,7 +597,8 @@ $note-form-margin-left: 72px; } .discussion-header { - min-height: 74px; + min-height: $line-height-base * 2em; + box-sizing: content-box; .note-header-info { padding-bottom: 0; @@ -608,13 +608,10 @@ $note-form-margin-left: 72px; overflow-x: auto; overflow-y: hidden; } -} -.unresolved { - .discussion-header { - .note-header-info { - margin-top: $gl-padding-8; - } + &.note-wrapper { + display: flex; + align-items: center; } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index c80beceae52..73ba09dbba5 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -429,7 +429,7 @@ padding: 0; background: transparent; border: 0; - line-height: 34px; + line-height: 2; margin: 0; > li + li::before { @@ -792,7 +792,6 @@ .btn { margin-top: $gl-padding; - padding: $gl-btn-vert-padding $gl-btn-padding; line-height: $gl-btn-line-height; .icon { diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 5664f46484e..5c732ab0d1f 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -90,7 +90,7 @@ .add-to-tree { vertical-align: top; - padding: 8px; + padding: $gl-bordered-btn-vert-padding; svg { top: 0; diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 10120a472d3..60400f10ca5 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -168,6 +168,10 @@ } ul.wiki-pages-list.content-list { + a { + color: $blue-600; + } + ul { list-style: none; margin-left: 0; diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index a570da61d54..e9ec8876688 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -103,7 +103,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController [ *::ApplicationSettingsHelper.visible_attributes, *::ApplicationSettingsHelper.external_authorization_service_attributes, - *lets_encrypt_visible_attributes, + :lets_encrypt_notification_email, + :lets_encrypt_terms_of_service_accepted, :domain_blacklist_file, disabled_oauth_sign_in_sources: [], import_sources: [], @@ -143,13 +144,4 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController render action end - - def lets_encrypt_visible_attributes - return [] unless Feature.enabled?(:pages_auto_ssl) - - [ - :lets_encrypt_notification_email, - :lets_encrypt_terms_of_service_accepted - ] - end end diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb index 0dd7500623d..353a9806fd1 100644 --- a/app/controllers/boards/issues_controller.rb +++ b/app/controllers/boards/issues_controller.rb @@ -2,16 +2,24 @@ module Boards class IssuesController < Boards::ApplicationController + # This is the maximum amount of issues which can be moved by one request to + # bulk_move for now. This is temporary and might be removed in future by + # introducing an alternative (async?) approach. + # (related: https://gitlab.com/groups/gitlab-org/-/epics/382) + MAX_MOVE_ISSUES_COUNT = 50 + include BoardsResponses include ControllerWithCrossProjectAccessCheck requires_cross_project_access if: -> { board&.group_board? } - before_action :whitelist_query_limiting, only: [:index, :update] + before_action :whitelist_query_limiting, only: [:index, :update, :bulk_move] before_action :authorize_read_issue, only: [:index] before_action :authorize_create_issue, only: [:create] before_action :authorize_update_issue, only: [:update] skip_before_action :authenticate_user!, only: [:index] + before_action :validate_id_list, only: [:bulk_move] + before_action :can_move_issues?, only: [:bulk_move] # rubocop: disable CodeReuse/ActiveRecord def index @@ -46,6 +54,17 @@ module Boards end end + def bulk_move + service = Boards::Issues::MoveService.new(board_parent, current_user, move_params(true)) + + issues = Issue.find(params[:ids]) + if service.execute_multiple(issues) + head :ok + else + head :unprocessable_entity + end + end + def update service = Boards::Issues::MoveService.new(board_parent, current_user, move_params) @@ -58,6 +77,10 @@ module Boards private + def can_move_issues? + head(:forbidden) unless can?(current_user, :admin_issue, board) + end + def render_issues(issues, metadata) data = { issues: serialize_as_json(issues) } data.merge!(metadata) @@ -90,8 +113,9 @@ module Boards end end - def move_params - params.permit(:board_id, :id, :from_list_id, :to_list_id, :move_before_id, :move_after_id) + def move_params(multiple = false) + id_param = multiple ? :ids : :id + params.permit(id_param, :board_id, :from_list_id, :to_list_id, :move_before_id, :move_after_id) end def issue_params @@ -112,5 +136,10 @@ module Boards # Also see https://gitlab.com/gitlab-org/gitlab-ce/issues/42439 Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42428') end + + def validate_id_list + head(:bad_request) unless params[:ids].is_a?(Array) + head(:unprocessable_entity) if params[:ids].size > MAX_MOVE_ISSUES_COUNT + end end end diff --git a/app/controllers/chaos_controller.rb b/app/controllers/chaos_controller.rb index 8d518c14b90..2985da35d83 100644 --- a/app/controllers/chaos_controller.rb +++ b/app/controllers/chaos_controller.rb @@ -1,35 +1,36 @@ # frozen_string_literal: true class ChaosController < ActionController::Base - before_action :validate_request + before_action :validate_chaos_secret, unless: :development? + before_action :request_start_time def leakmem - memory_mb = (params[:memory_mb]&.to_i || 100) - duration_s = (params[:duration_s]&.to_i || 30).seconds - - start = Time.now retainer = [] # Add `n` 1mb chunks of memory to the retainer array memory_mb.times { retainer << "x" * 1.megabyte } - duration_taken = (Time.now - start).seconds - Kernel.sleep duration_s - duration_taken if duration_s > duration_taken + Kernel.sleep(duration_left) render plain: "OK" end - def cpuspin - duration_s = (params[:duration_s]&.to_i || 30).seconds - end_time = Time.now + duration_s.seconds - - rand while Time.now < end_time + def cpu_spin + rand while Time.now < expected_end_time render plain: "OK" end + def db_spin + while Time.now < expected_end_time + ActiveRecord::Base.connection.execute("SELECT 1") + + end_interval_time = Time.now + [duration_s, interval_s].min + rand while Time.now < end_interval_time + end + end + def sleep - duration_s = (params[:duration_s]&.to_i || 30).seconds - Kernel.sleep duration_s + Kernel.sleep(duration_left) render plain: "OK" end @@ -40,17 +41,57 @@ class ChaosController < ActionController::Base private - def validate_request - secret = ENV['GITLAB_CHAOS_SECRET'] - # GITLAB_CHAOS_SECRET is required unless you're running in Development mode - if !secret && !Rails.env.development? - render plain: "chaos misconfigured: please configure GITLAB_CHAOS_SECRET when using GITLAB_ENABLE_CHAOS_ENDPOINTS outside of a development environment", status: :internal_server_error - end + def request_start_time + @start_time ||= Time.now + end + + def expected_end_time + request_start_time + duration_s + end - return unless secret + def duration_left + # returns 0 if over time + [expected_end_time - Time.now, 0].max + end + + def validate_chaos_secret + unless chaos_secret_configured + render plain: "chaos misconfigured: please configure GITLAB_CHAOS_SECRET", + status: :internal_server_error + return + end - unless request.headers["HTTP_X_CHAOS_SECRET"] == secret - render plain: "To experience chaos, please set X-Chaos-Secret header", status: :unauthorized + unless Devise.secure_compare(chaos_secret_configured, chaos_secret_request) + render plain: "To experience chaos, please set a valid `X-Chaos-Secret` header or `token` param", + status: :unauthorized + return end end + + def chaos_secret_configured + ENV['GITLAB_CHAOS_SECRET'] + end + + def chaos_secret_request + request.headers["HTTP_X_CHAOS_SECRET"] || params[:token] + end + + def interval_s + interval_s = params[:interval_s] || 1 + interval_s.to_f.seconds + end + + def duration_s + duration_s = params[:duration_s] || 30 + duration_s.to_i.seconds + end + + def memory_mb + memory_mb = params[:memory_mb] || 100 + memory_mb.to_i + end + + def development? + Rails.env.development? + end end diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index 065d2d3a4ec..6fa2f75be33 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -92,7 +92,7 @@ module IssuableActions end def bulk_update - result = Issuable::BulkUpdateService.new(project, current_user, bulk_update_params).execute(resource_name) + result = Issuable::BulkUpdateService.new(current_user, bulk_update_params).execute(resource_name) quantity = result[:count] render json: { notice: "#{quantity} #{resource_name.pluralize(quantity)} updated" } @@ -181,7 +181,7 @@ module IssuableActions end def authorize_admin_issuable! - unless can?(current_user, :"admin_#{resource_name}", @project) # rubocop:disable Gitlab/ModuleWithInstanceVariables + unless can?(current_user, :"admin_#{resource_name}", parent) return access_denied! end end diff --git a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb index f47ead2f0da..2e9905997db 100644 --- a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb +++ b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb @@ -28,7 +28,7 @@ module RequiresWhitelistedMonitoringClient def valid_token? token = params[:token].presence || request.headers['TOKEN'] token.present? && - ActiveSupport::SecurityUtils.variable_size_secure_compare( + ActiveSupport::SecurityUtils.secure_compare( token, Gitlab::CurrentSettings.health_check_access_token ) diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index 1ce0afac83b..9fbbe373b0d 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -11,7 +11,6 @@ class GraphqlController < ApplicationController # around in GraphiQL. protect_from_forgery with: :null_session, only: :execute - before_action :check_graphql_feature_flag! before_action :authorize_access_api! before_action(only: [:execute]) { authenticate_sessionless_user!(:api) } @@ -86,8 +85,4 @@ class GraphqlController < ApplicationController render json: error, status: status end - - def check_graphql_feature_flag! - render_404 unless Gitlab::Graphql.enabled? - end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index aff418faae5..0176962cf0a 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -107,7 +107,7 @@ class GroupsController < Groups::ApplicationController if Groups::UpdateService.new(@group, current_user, group_params).execute redirect_to edit_group_path(@group, anchor: params[:update_section]), notice: "Group '#{@group.name}' was successfully updated." else - @group.restore_path! + @group.path = @group.path_before_last_save || @group.path_was render action: "edit" end diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index fb43356ff10..6314d9f2a9f 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -50,7 +50,7 @@ module Projects end def cycle_analytics - @cycle_analytics ||= ::CycleAnalytics.new(project, options(events_params)) + @cycle_analytics ||= ::CycleAnalytics::ProjectLevel.new(project, options: options(events_params)) end def events_params diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 8c071496ba9..2d46a71bf99 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -9,7 +9,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController before_action :authorize_read_cycle_analytics! def show - @cycle_analytics = ::CycleAnalytics.new(@project, options(cycle_analytics_params)) + @cycle_analytics = ::CycleAnalytics::ProjectLevel.new(@project, options: options(cycle_analytics_params)) @cycle_analytics_no_data = @cycle_analytics.no_stats? diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb index 0a009477d61..32111b07a0b 100644 --- a/app/controllers/projects/deployments_controller.rb +++ b/app/controllers/projects/deployments_controller.rb @@ -15,24 +15,22 @@ class Projects::DeploymentsController < Projects::ApplicationController # rubocop: enable CodeReuse/ActiveRecord def metrics - return render_404 unless deployment.has_metrics? + return render_404 unless deployment_metrics.has_metrics? - @metrics = deployment.metrics + @metrics = deployment_metrics.metrics if @metrics&.any? render json: @metrics, status: :ok else head :no_content end - rescue NotImplementedError - render_404 end def additional_metrics - return render_404 unless deployment.has_metrics? + return render_404 unless deployment_metrics.has_metrics? respond_to do |format| format.json do - metrics = deployment.additional_metrics + metrics = deployment_metrics.additional_metrics if metrics.any? render json: metrics @@ -45,6 +43,10 @@ class Projects::DeploymentsController < Projects::ApplicationController private + def deployment_metrics + @deployment_metrics ||= DeploymentMetrics.new(deployment.project, deployment) + end + # rubocop: disable CodeReuse/ActiveRecord def deployment @deployment ||= environment.deployments.find_by(iid: params[:id]) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index ecf05e6ea64..1bca52106fa 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -13,6 +13,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint) push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards) + push_frontend_feature_flag(:environment_metrics_additional_panel_types) push_frontend_feature_flag(:prometheus_computed_alerts) end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index b866f574f67..228de8bc6f3 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -45,8 +45,6 @@ class Projects::IssuesController < Projects::ApplicationController before_action :authorize_import_issues!, only: [:import_csv] before_action :authorize_download_code!, only: [:related_branches] - before_action :set_suggested_issues_feature_flags, only: [:new] - respond_to :html def index @@ -285,8 +283,4 @@ class Projects::IssuesController < Projects::ApplicationController # 3. https://gitlab.com/gitlab-org/gitlab-ce/issues/42426 Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42422') end - - def set_suggested_issues_feature_flags - push_frontend_feature_flag(:graphql, default_enabled: true) - end end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 3592505a977..f4fbeacfaba 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -429,7 +429,7 @@ class IssuableFinder items = klass.with(cte.to_arel).from(klass.table_name) end - items.full_search(search, matched_columns: params[:in]) + items.full_search(search, matched_columns: params[:in], use_minimum_char_limit: !use_cte_for_search?) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb index 152ebb930e2..7edd14e48f7 100644 --- a/app/graphql/gitlab_schema.rb +++ b/app/graphql/gitlab_schema.rb @@ -66,6 +66,8 @@ class GitlabSchema < GraphQL::Schema if gid.model_class < ApplicationRecord Gitlab::Graphql::Loaders::BatchModelLoader.new(gid.model_class, gid.model_id).find + elsif gid.model_class.respond_to?(:lazy_find) + gid.model_class.lazy_find(gid.model_id) else gid.find end diff --git a/app/graphql/mutations/award_emojis/base.rb b/app/graphql/mutations/award_emojis/base.rb index d868db84f9d..583744c3884 100644 --- a/app/graphql/mutations/award_emojis/base.rb +++ b/app/graphql/mutations/award_emojis/base.rb @@ -3,8 +3,6 @@ module Mutations module AwardEmojis class Base < BaseMutation - include Gitlab::Graphql::Authorize::AuthorizeResource - authorize :award_emoji argument :awardable_id, diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb index 08d2a1f18a3..7273a74cb86 100644 --- a/app/graphql/mutations/base_mutation.rb +++ b/app/graphql/mutations/base_mutation.rb @@ -2,6 +2,7 @@ module Mutations class BaseMutation < GraphQL::Schema::RelayClassicMutation + prepend Gitlab::Graphql::Authorize::AuthorizeResource prepend Gitlab::Graphql::CopyFieldDescription field :errors, [GraphQL::STRING_TYPE], diff --git a/app/graphql/mutations/merge_requests/base.rb b/app/graphql/mutations/merge_requests/base.rb index e85d16fc2c5..28e0cdc8cc7 100644 --- a/app/graphql/mutations/merge_requests/base.rb +++ b/app/graphql/mutations/merge_requests/base.rb @@ -3,7 +3,6 @@ module Mutations module MergeRequests class Base < BaseMutation - include Gitlab::Graphql::Authorize::AuthorizeResource include Mutations::ResolvesProject argument :project_path, GraphQL::ID_TYPE, diff --git a/app/graphql/mutations/notes/base.rb b/app/graphql/mutations/notes/base.rb new file mode 100644 index 00000000000..31dabc0a660 --- /dev/null +++ b/app/graphql/mutations/notes/base.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Mutations + module Notes + class Base < BaseMutation + field :note, + Types::Notes::NoteType, + null: true, + description: 'The note after mutation' + + private + + def find_object(id:) + GitlabSchema.object_from_id(id) + end + + def check_object_is_noteable!(object) + unless object.is_a?(Noteable) + raise Gitlab::Graphql::Errors::ResourceNotAvailable, + 'Cannot add notes to this resource' + end + end + + def check_object_is_note!(object) + unless object.is_a?(Note) + raise Gitlab::Graphql::Errors::ResourceNotAvailable, + 'Resource is not a note' + end + end + end + end +end diff --git a/app/graphql/mutations/notes/create/base.rb b/app/graphql/mutations/notes/create/base.rb new file mode 100644 index 00000000000..d3a5dae2188 --- /dev/null +++ b/app/graphql/mutations/notes/create/base.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Mutations + module Notes + module Create + # This is a Base class for the Note creation Mutations and is not + # mounted as a GraphQL mutation itself. + class Base < Mutations::Notes::Base + authorize :create_note + + argument :noteable_id, + GraphQL::ID_TYPE, + required: true, + description: 'The global id of the resource to add a note to' + + argument :body, + GraphQL::STRING_TYPE, + required: true, + description: copy_field_description(Types::Notes::NoteType, :body) + + private + + def resolve(args) + noteable = authorized_find!(id: args[:noteable_id]) + + check_object_is_noteable!(noteable) + + note = ::Notes::CreateService.new( + noteable.project, + current_user, + create_note_params(noteable, args) + ).execute + + { + note: (note if note.persisted?), + errors: errors_on_object(note) + } + end + + def create_note_params(noteable, args) + { + noteable: noteable, + note: args[:body] + } + end + end + end + end +end diff --git a/app/graphql/mutations/notes/create/diff_note.rb b/app/graphql/mutations/notes/create/diff_note.rb new file mode 100644 index 00000000000..9b5f3092006 --- /dev/null +++ b/app/graphql/mutations/notes/create/diff_note.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Mutations + module Notes + module Create + class DiffNote < Base + graphql_name 'CreateDiffNote' + + argument :position, + Types::Notes::DiffPositionInputType, + required: true, + description: copy_field_description(Types::Notes::NoteType, :position) + + private + + def create_note_params(noteable, args) + super(noteable, args).merge({ + type: 'DiffNote', + position: position(noteable, args) + }) + end + + def position(noteable, args) + position = args[:position].to_h + position[:position_type] = 'text' + position.merge!(position[:paths].to_h) + + Gitlab::Diff::Position.new(position) + end + end + end + end +end diff --git a/app/graphql/mutations/notes/create/image_diff_note.rb b/app/graphql/mutations/notes/create/image_diff_note.rb new file mode 100644 index 00000000000..d94fd4d6ff8 --- /dev/null +++ b/app/graphql/mutations/notes/create/image_diff_note.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Mutations + module Notes + module Create + class ImageDiffNote < Base + graphql_name 'CreateImageDiffNote' + + argument :position, + Types::Notes::DiffImagePositionInputType, + required: true, + description: copy_field_description(Types::Notes::NoteType, :position) + + private + + def create_note_params(noteable, args) + super(noteable, args).merge({ + type: 'DiffNote', + position: position(noteable, args) + }) + end + + def position(noteable, args) + position = args[:position].to_h + position[:position_type] = 'image' + position.merge!(position[:paths].to_h) + + Gitlab::Diff::Position.new(position) + end + end + end + end +end diff --git a/app/graphql/mutations/notes/create/note.rb b/app/graphql/mutations/notes/create/note.rb new file mode 100644 index 00000000000..5236e48026e --- /dev/null +++ b/app/graphql/mutations/notes/create/note.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Mutations + module Notes + module Create + class Note < Base + graphql_name 'CreateNote' + + argument :discussion_id, + GraphQL::ID_TYPE, + required: false, + description: 'The global id of the discussion this note is in reply to' + + private + + def create_note_params(noteable, args) + discussion_id = nil + + if args[:discussion_id] + discussion = GitlabSchema.object_from_id(args[:discussion_id]) + authorize_discussion!(discussion) + + discussion_id = discussion.id + end + + super(noteable, args).merge({ + in_reply_to_discussion_id: discussion_id + }) + end + + def authorize_discussion!(discussion) + unless Ability.allowed?(current_user, :read_note, discussion, scope: :user) + raise Gitlab::Graphql::Errors::ResourceNotAvailable, + "The discussion does not exist or you don't have permission to perform this action" + end + end + end + end + end +end diff --git a/app/graphql/mutations/notes/destroy.rb b/app/graphql/mutations/notes/destroy.rb new file mode 100644 index 00000000000..a81322bc9b7 --- /dev/null +++ b/app/graphql/mutations/notes/destroy.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Mutations + module Notes + class Destroy < Base + graphql_name 'DestroyNote' + + authorize :admin_note + + argument :id, + GraphQL::ID_TYPE, + required: true, + description: 'The global id of the note to destroy' + + def resolve(id:) + note = authorized_find!(id: id) + + check_object_is_note!(note) + + ::Notes::DestroyService.new(note.project, current_user).execute(note) + + { + errors: [] + } + end + end + end +end diff --git a/app/graphql/mutations/notes/update.rb b/app/graphql/mutations/notes/update.rb new file mode 100644 index 00000000000..ebf57b800c0 --- /dev/null +++ b/app/graphql/mutations/notes/update.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Mutations + module Notes + class Update < Base + graphql_name 'UpdateNote' + + authorize :admin_note + + argument :id, + GraphQL::ID_TYPE, + required: true, + description: 'The global id of the note to update' + + argument :body, + GraphQL::STRING_TYPE, + required: true, + description: copy_field_description(Types::Notes::NoteType, :body) + + def resolve(args) + note = authorized_find!(id: args[:id]) + + check_object_is_note!(note) + + note = ::Notes::UpdateService.new( + note.project, + current_user, + { note: args[:body] } + ).execute(note) + + { + note: note.reset, + errors: errors_on_object(note) + } + end + end + end +end diff --git a/app/graphql/types/base_input_object.rb b/app/graphql/types/base_input_object.rb index aebed035d3b..90a29b0cfb8 100644 --- a/app/graphql/types/base_input_object.rb +++ b/app/graphql/types/base_input_object.rb @@ -2,5 +2,6 @@ module Types class BaseInputObject < GraphQL::Schema::InputObject + prepend Gitlab::Graphql::CopyFieldDescription end end diff --git a/app/graphql/types/diff_paths_input_type.rb b/app/graphql/types/diff_paths_input_type.rb new file mode 100644 index 00000000000..43feddd9827 --- /dev/null +++ b/app/graphql/types/diff_paths_input_type.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Types + # rubocop: disable Graphql/AuthorizeTypes + class DiffPathsInputType < BaseInputObject + argument :old_path, GraphQL::STRING_TYPE, required: false, + description: 'The path of the file on the start sha' + argument :new_path, GraphQL::STRING_TYPE, required: false, + description: 'The path of the file on the head sha' + end + # rubocop: enable Graphql/AuthorizeTypes +end diff --git a/app/graphql/types/diff_refs_type.rb b/app/graphql/types/diff_refs_type.rb new file mode 100644 index 00000000000..33a5780cd68 --- /dev/null +++ b/app/graphql/types/diff_refs_type.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Types + # rubocop: disable Graphql/AuthorizeTypes + # Types that use DiffRefsType should have their own authorization + class DiffRefsType < BaseObject + graphql_name 'DiffRefs' + + field :head_sha, GraphQL::STRING_TYPE, null: false, description: 'The sha of the head at the time the comment was made' + field :base_sha, GraphQL::STRING_TYPE, null: false, description: 'The merge base of the branch the comment was made on' + field :start_sha, GraphQL::STRING_TYPE, null: false, description: 'The sha of the branch being compared against' + end + # rubocop: enable Graphql/AuthorizeTypes +end diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index 6734d4761c2..b8f63a750c5 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -23,6 +23,7 @@ module Types field :updated_at, Types::TimeType, null: false field :source_project, Types::ProjectType, null: true field :target_project, Types::ProjectType, null: false + field :diff_refs, Types::DiffRefsType, null: true # Alias for target_project field :project, Types::ProjectType, null: false field :project_id, GraphQL::INT_TYPE, null: false, method: :target_project_id diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index bc5fb709522..f843d6ad86f 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -10,5 +10,10 @@ module Types mount_mutation Mutations::AwardEmojis::Remove mount_mutation Mutations::AwardEmojis::Toggle mount_mutation Mutations::MergeRequests::SetWip, calls_gitaly: true + mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true + mount_mutation Mutations::Notes::Create::DiffNote, calls_gitaly: true + mount_mutation Mutations::Notes::Create::ImageDiffNote, calls_gitaly: true + mount_mutation Mutations::Notes::Update + mount_mutation Mutations::Notes::Destroy end end diff --git a/app/graphql/types/notes/diff_image_position_input_type.rb b/app/graphql/types/notes/diff_image_position_input_type.rb new file mode 100644 index 00000000000..23b53b20815 --- /dev/null +++ b/app/graphql/types/notes/diff_image_position_input_type.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Types + module Notes + # rubocop: disable Graphql/AuthorizeTypes + class DiffImagePositionInputType < DiffPositionBaseInputType + graphql_name 'DiffImagePositionInput' + + argument :x, GraphQL::INT_TYPE, required: true, + description: copy_field_description(Types::Notes::DiffPositionType, :x) + argument :y, GraphQL::INT_TYPE, required: true, + description: copy_field_description(Types::Notes::DiffPositionType, :y) + argument :width, GraphQL::INT_TYPE, required: true, + description: copy_field_description(Types::Notes::DiffPositionType, :width) + argument :height, GraphQL::INT_TYPE, required: true, + description: copy_field_description(Types::Notes::DiffPositionType, :height) + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/notes/diff_position_base_input_type.rb b/app/graphql/types/notes/diff_position_base_input_type.rb new file mode 100644 index 00000000000..a9b4e1a8948 --- /dev/null +++ b/app/graphql/types/notes/diff_position_base_input_type.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Types + module Notes + # rubocop: disable Graphql/AuthorizeTypes + class DiffPositionBaseInputType < BaseInputObject + argument :head_sha, GraphQL::STRING_TYPE, required: true, + description: copy_field_description(Types::DiffRefsType, :head_sha) + argument :base_sha, GraphQL::STRING_TYPE, required: false, + description: copy_field_description(Types::DiffRefsType, :base_sha) + argument :start_sha, GraphQL::STRING_TYPE, required: true, + description: copy_field_description(Types::DiffRefsType, :start_sha) + + argument :paths, + Types::DiffPathsInputType, + required: true, + description: 'The paths of the file that was changed. ' \ + 'Both of the properties of this input are optional, but at least one of them is required' + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/notes/diff_position_input_type.rb b/app/graphql/types/notes/diff_position_input_type.rb new file mode 100644 index 00000000000..02c91e173cb --- /dev/null +++ b/app/graphql/types/notes/diff_position_input_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Types + module Notes + # rubocop: disable Graphql/AuthorizeTypes + class DiffPositionInputType < DiffPositionBaseInputType + graphql_name 'DiffPositionInput' + + argument :old_line, GraphQL::INT_TYPE, required: false, + description: copy_field_description(Types::Notes::DiffPositionType, :old_line) + argument :new_line, GraphQL::INT_TYPE, required: true, + description: copy_field_description(Types::Notes::DiffPositionType, :new_line) + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/notes/diff_position_type.rb b/app/graphql/types/notes/diff_position_type.rb index ebc24451715..6a0377fbfdf 100644 --- a/app/graphql/types/notes/diff_position_type.rb +++ b/app/graphql/types/notes/diff_position_type.rb @@ -7,12 +7,7 @@ module Types class DiffPositionType < BaseObject graphql_name 'DiffPosition' - field :head_sha, GraphQL::STRING_TYPE, null: false, - description: "The sha of the head at the time the comment was made" - field :base_sha, GraphQL::STRING_TYPE, null: true, - description: "The merge base of the branch the comment was made on" - field :start_sha, GraphQL::STRING_TYPE, null: false, - description: "The sha of the branch being compared against" + field :diff_refs, Types::DiffRefsType, null: false field :file_path, GraphQL::STRING_TYPE, null: false, description: "The path of the file that was changed" diff --git a/app/graphql/types/notes/discussion_type.rb b/app/graphql/types/notes/discussion_type.rb index c4691942f2d..a3fb28298f6 100644 --- a/app/graphql/types/notes/discussion_type.rb +++ b/app/graphql/types/notes/discussion_type.rb @@ -8,8 +8,16 @@ module Types authorize :read_note field :id, GraphQL::ID_TYPE, null: false + field :reply_id, GraphQL::ID_TYPE, null: false, description: 'The ID used to reply to this discussion' field :created_at, Types::TimeType, null: false field :notes, Types::Notes::NoteType.connection_type, null: false, description: "All notes in the discussion" + + # The gem we use to generate Global IDs is hard-coded to work with + # `id` properties. To generate a GID for the `reply_id` property, + # we must use the ::Gitlab::GlobalId module. + def reply_id + ::Gitlab::GlobalId.build(object, id: object.reply_id) + end end end end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 0d6a6496993..4b0713001a1 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -18,7 +18,16 @@ module BlobHelper end def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) - segments = [ide_path, 'project', project.full_path, 'edit', ref] + project_path = + if !current_user || can?(current_user, :push_code, project) + project.full_path + else + # We currently always fork to the user's namespace + # in edit_fork_button_tag + "#{current_user.namespace.full_path}/#{project.path}" + end + + segments = [ide_path, 'project', project_path, 'edit', ref] segments.concat(['-', encode_ide_path(path)]) if path.present? File.join(segments) end diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index 8ef68018d23..bbe05f40999 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -99,7 +99,11 @@ module BoardsHelper recent_project_boards_path(@project) if current_board_parent.is_a?(Project) end + def serializer + CurrentBoardSerializer.new + end + def current_board_json - board.to_json + serializer.represent(board).as_json end end diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 64c5fae7d96..fd94f07cc2c 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -46,7 +46,7 @@ module DropdownsHelper def dropdown_toggle(toggle_text, data_attr, options = {}) default_label = data_attr[:default_label] - content_tag(:button, disabled: options[:disabled], class: "dropdown-menu-toggle #{options[:toggle_class] if options.key?(:toggle_class)}", id: (options[:id] if options.key?(:id)), type: "button", data: data_attr) do + content_tag(:button, disabled: options[:disabled], class: "dropdown-menu-toggle btn #{options[:toggle_class] if options.key?(:toggle_class)}", id: (options[:id] if options.key?(:id)), type: "button", data: data_attr) do output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}") output << icon('chevron-down') output.html_safe diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index 8ccb39f8444..d76a0f3a3b8 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -3,7 +3,7 @@ require 'nokogiri' module MarkupHelper - include ActionView::Helpers::TagHelper + include ActionView::Helpers::TextHelper include ::Gitlab::ActionViewOutput::Context def plain?(filename) @@ -154,9 +154,7 @@ module MarkupHelper elsif asciidoc?(file_name) asciidoc_unsafe(text, context) elsif plain?(file_name) - content_tag :pre, class: 'plain-readme' do - text - end + plain_unsafe(text) else other_markup_unsafe(file_name, text, context) end @@ -271,6 +269,12 @@ module MarkupHelper Gitlab::Asciidoc.render(text, context) end + def plain_unsafe(text) + content_tag :pre, class: 'plain-readme' do + text + end + end + def other_markup_unsafe(file_name, text, context = {}) Gitlab::OtherMarkup.render(file_name, text, context) end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 8dee842a22d..8d0079a4dd3 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -662,6 +662,6 @@ module ProjectsHelper end def vue_file_list_enabled? - Gitlab::Graphql.enabled? && Feature.enabled?(:vue_file_list, @project) + Feature.enabled?(:vue_file_list, @project) end end diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 26692934456..d5e5b472115 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -31,22 +31,24 @@ module SortingHelper end def projects_sort_options_hash - Feature.enabled?(:project_list_filter_bar) && !current_controller?('admin/projects') ? projects_sort_common_options_hash : old_projects_sort_options_hash - end + use_old_sorting = Feature.disabled?(:project_list_filter_bar) || current_controller?('admin/projects') - # TODO: Simplify these sorting options - # https://gitlab.com/gitlab-org/gitlab-ce/issues/60798 - # https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/11209#note_162234858 - def old_projects_sort_options_hash options = { sort_value_latest_activity => sort_title_latest_activity, + sort_value_recently_created => sort_title_created_date, sort_value_name => sort_title_name, - sort_value_oldest_activity => sort_title_oldest_activity, - sort_value_oldest_created => sort_title_oldest_created, - sort_value_recently_created => sort_title_recently_created, - sort_value_stars_desc => sort_title_most_stars + sort_value_stars_desc => sort_title_stars } + if use_old_sorting + options = options.merge({ + sort_value_oldest_activity => sort_title_oldest_activity, + sort_value_oldest_created => sort_title_oldest_created, + sort_value_recently_created => sort_title_recently_created, + sort_value_stars_desc => sort_title_most_stars + }) + end + if current_controller?('admin/projects') options[sort_value_largest_repo] = sort_title_largest_repo end @@ -54,26 +56,14 @@ module SortingHelper options end - def projects_sort_common_options_hash - { - sort_value_latest_activity => sort_title_latest_activity, - sort_value_recently_created => sort_title_created_date, - sort_value_name => sort_title_name, - sort_value_stars_desc => sort_title_stars - } - end - def projects_sort_option_titles - { - sort_value_latest_activity => sort_title_latest_activity, - sort_value_recently_created => sort_title_created_date, - sort_value_name => sort_title_name, - sort_value_stars_desc => sort_title_stars, + # Only used for the project filter search bar + projects_sort_options_hash.merge({ sort_value_oldest_activity => sort_title_latest_activity, sort_value_oldest_created => sort_title_created_date, sort_value_name_desc => sort_title_name, sort_value_stars_asc => sort_title_stars - } + }) end def projects_reverse_sort_options_hash @@ -210,47 +200,42 @@ module SortingHelper sort_options_hash[sort_value] end - def issuable_sort_icon_suffix(sort_value) + def sort_direction_icon(sort_value) case sort_value when sort_value_milestone, sort_value_due_date, /_asc\z/ - 'lowest' + 'sort-lowest' else - 'highest' + 'sort-highest' end end - # TODO: dedupicate issuable and project sort direction - # https://gitlab.com/gitlab-org/gitlab-ce/issues/60798 - def issuable_sort_direction_button(sort_value) + def sort_direction_button(reverse_url, reverse_sort, sort_value) link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort' - reverse_sort = issuable_reverse_sort_order_hash[sort_value] + icon = sort_direction_icon(sort_value) + url = reverse_url - if reverse_sort - reverse_url = page_filter_path(sort: reverse_sort) - else - reverse_url = '#' + unless reverse_sort + url = '#' link_class += ' disabled' end - link_to(reverse_url, type: 'button', class: link_class, title: s_('SortOptions|Sort direction')) do - sprite_icon("sort-#{issuable_sort_icon_suffix(sort_value)}", size: 16) + link_to(url, type: 'button', class: link_class, title: s_('SortOptions|Sort direction')) do + sprite_icon(icon, size: 16) end end + def issuable_sort_direction_button(sort_value) + reverse_sort = issuable_reverse_sort_order_hash[sort_value] + url = page_filter_path(sort: reverse_sort) + + sort_direction_button(url, reverse_sort, sort_value) + end + def project_sort_direction_button(sort_value) - link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort' reverse_sort = projects_reverse_sort_options_hash[sort_value] + url = filter_projects_path(sort: reverse_sort) - if reverse_sort - reverse_url = filter_projects_path(sort: reverse_sort) - else - reverse_url = '#' - link_class += ' disabled' - end - - link_to(reverse_url, type: 'button', class: link_class, title: s_('SortOptions|Sort direction')) do - sprite_icon("sort-#{issuable_sort_icon_suffix(sort_value)}", size: 16) - end + sort_direction_button(url, reverse_sort, sort_value) end # Titles. diff --git a/app/helpers/storage_helper.rb b/app/helpers/storage_helper.rb index ecf37bae6b3..ce810433a3a 100644 --- a/app/helpers/storage_helper.rb +++ b/app/helpers/storage_helper.rb @@ -17,6 +17,6 @@ module StorageHelper counter_lfs_objects: storage_counter(statistics.lfs_objects_size) } - _("%{counter_repositories} repositories, %{counter_wikis} wikis, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS") % counters + _("Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects}") % counters end end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 4690b6ffbe1..a3575462de0 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -147,4 +147,11 @@ module TreeHelper def relative_url_root Gitlab.config.gitlab.relative_url_root.presence || '/' end + + # project and path are used on the EE version + def tree_content_data(logs_path, project, path) + { + "logs-path" => logs_path + } + end end diff --git a/app/models/chat_team.rb b/app/models/chat_team.rb index 52b5a7b4a91..28aab279545 100644 --- a/app/models/chat_team.rb +++ b/app/models/chat_team.rb @@ -12,6 +12,6 @@ class ChatTeam < ApplicationRecord # Either the group is not found, or the user doesn't have the proper # access on the mattermost instance. In the first case, we're done either way # in the latter case, we can't recover by retrying, so we just log what happened - Rails.logger.error("Mattermost team deletion failed: #{e}") + Rails.logger.error("Mattermost team deletion failed: #{e}") # rubocop:disable Gitlab/RailsLogger end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 89cc082d0bc..635fcc86166 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -266,7 +266,7 @@ module Ci begin Ci::Build.retry(build, build.user) rescue Gitlab::Access::AccessDeniedError => ex - Rails.logger.error "Unable to auto-retry job #{build.id}: #{ex}" + Rails.logger.error "Unable to auto-retry job #{build.id}: #{ex}" # rubocop:disable Gitlab/RailsLogger end end end @@ -578,7 +578,7 @@ module Ci end def valid_token?(token) - self.token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) + self.token && ActiveSupport::SecurityUtils.secure_compare(token, self.token) end def has_tags? diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 20ca4a9ab24..2262282e647 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -196,7 +196,7 @@ module Ci sql = 'CASE ci_pipelines.source WHEN (?) THEN 0 ELSE 1 END, ci_pipelines.id DESC' query = ApplicationRecord.send(:sanitize_sql_array, [sql, sources[:merge_request_event]]) # rubocop:disable GitlabSecurity/PublicSend - order(query) + order(Arel.sql(query)) end scope :for_user, -> (user) { where(user: user) } diff --git a/app/models/clusters/clusters_hierarchy.rb b/app/models/clusters/clusters_hierarchy.rb new file mode 100644 index 00000000000..dab034b7234 --- /dev/null +++ b/app/models/clusters/clusters_hierarchy.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +module Clusters + class ClustersHierarchy + DEPTH_COLUMN = :depth + + def initialize(clusterable) + @clusterable = clusterable + end + + # Returns clusters in order from deepest to highest group + def base_and_ancestors + cte = recursive_cte + cte_alias = cte.table.alias(model.table_name) + + model + .unscoped + .where('clusters.id IS NOT NULL') + .with + .recursive(cte.to_arel) + .from(cte_alias) + .order(DEPTH_COLUMN => :asc) + end + + private + + attr_reader :clusterable + + def recursive_cte + cte = Gitlab::SQL::RecursiveCTE.new(:clusters_cte) + + base_query = case clusterable + when ::Group + group_clusters_base_query + when ::Project + project_clusters_base_query + else + raise ArgumentError, "unknown type for #{clusterable}" + end + + cte << base_query + cte << parent_query(cte) + + cte + end + + def group_clusters_base_query + group_parent_id_alias = alias_as_column(groups[:parent_id], 'group_parent_id') + join_sources = ::Group.left_joins(:clusters).join_sources + + model + .unscoped + .select([clusters_star, group_parent_id_alias, "1 AS #{DEPTH_COLUMN}"]) + .where(groups[:id].eq(clusterable.id)) + .from(groups) + .joins(join_sources) + end + + def project_clusters_base_query + projects = ::Project.arel_table + project_parent_id_alias = alias_as_column(projects[:namespace_id], 'group_parent_id') + join_sources = ::Project.left_joins(:clusters).join_sources + + model + .unscoped + .select([clusters_star, project_parent_id_alias, "1 AS #{DEPTH_COLUMN}"]) + .where(projects[:id].eq(clusterable.id)) + .from(projects) + .joins(join_sources) + end + + def parent_query(cte) + group_parent_id_alias = alias_as_column(groups[:parent_id], 'group_parent_id') + + model + .unscoped + .select([clusters_star, group_parent_id_alias, cte.table[DEPTH_COLUMN] + 1]) + .from([cte.table, groups]) + .joins('LEFT OUTER JOIN cluster_groups ON cluster_groups.group_id = namespaces.id') + .joins('LEFT OUTER JOIN clusters ON cluster_groups.cluster_id = clusters.id') + .where(groups[:id].eq(cte.table[:group_parent_id])) + end + + def model + Clusters::Cluster + end + + def clusters + @clusters ||= model.arel_table + end + + def groups + @groups ||= ::Group.arel_table + end + + def clusters_star + @clusters_star ||= clusters[Arel.star] + end + + def alias_as_column(value, alias_to) + Arel::Nodes::As.new(value, Arel::Nodes::SqlLiteral.new(alias_to)) + end + end +end diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index 42203a5f214..9713e79f525 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -87,6 +87,16 @@ module CacheMarkdownField __send__(cached_markdown_fields.html_field(markdown_field)) # rubocop:disable GitlabSecurity/PublicSend end + # Updates the markdown cache if necessary, then returns the field + # Unlike `cached_html_for` it returns `nil` if the field does not exist + def updated_cached_html_for(markdown_field) + return unless cached_markdown_fields.markdown_fields.include?(markdown_field) + + refresh_markdown_cache if attribute_invalidated?(cached_markdown_fields.html_field(markdown_field)) + + cached_html_for(markdown_field) + end + def latest_cached_markdown_version @latest_cached_markdown_version ||= (Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16) | local_version end @@ -139,8 +149,9 @@ module CacheMarkdownField # The HTML becomes invalid if any dependent fields change. For now, assume # author and project invalidate the cache in all circumstances. define_method(invalidation_method) do - invalidations = changed_markdown_fields & [markdown_field.to_s, *INVALIDATED_BY] - invalidations.delete(markdown_field.to_s) if changed_markdown_fields.include?("#{markdown_field}_html") + changed_fields = changed_attributes.keys + invalidations = changed_fields & [markdown_field.to_s, *INVALIDATED_BY] + invalidations.delete(markdown_field.to_s) if changed_fields.include?("#{markdown_field}_html") !invalidations.empty? || !cached_html_up_to_date?(markdown_field) end end diff --git a/app/models/concerns/cacheable_attributes.rb b/app/models/concerns/cacheable_attributes.rb index 8cbf4bcfaf7..53dff2adfc3 100644 --- a/app/models/concerns/cacheable_attributes.rb +++ b/app/models/concerns/cacheable_attributes.rb @@ -49,7 +49,7 @@ module CacheableAttributes current_without_cache.tap { |current_record| current_record&.cache! } rescue => e if Rails.env.production? - Rails.logger.warn("Cached record for #{name} couldn't be loaded, falling back to uncached record: #{e}") + Rails.logger.warn("Cached record for #{name} couldn't be loaded, falling back to uncached record: #{e}") # rubocop:disable Gitlab/RailsLogger else raise e end diff --git a/app/models/concerns/ci/contextable.rb b/app/models/concerns/ci/contextable.rb index e1d5ce7f7d4..91dda803031 100644 --- a/app/models/concerns/ci/contextable.rb +++ b/app/models/concerns/ci/contextable.rb @@ -59,6 +59,7 @@ module Ci variables.append(key: 'CI', value: 'true') variables.append(key: 'GITLAB_CI', value: 'true') variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(',')) + variables.append(key: 'CI_SERVER_HOST', value: Gitlab.config.gitlab.host) variables.append(key: 'CI_SERVER_NAME', value: 'GitLab') variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION) variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s) diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb index 5a358ae2ef6..8f28c897eb6 100644 --- a/app/models/concerns/deployment_platform.rb +++ b/app/models/concerns/deployment_platform.rb @@ -12,11 +12,26 @@ module DeploymentPlatform private def find_deployment_platform(environment) - find_cluster_platform_kubernetes(environment: environment) || - find_group_cluster_platform_kubernetes(environment: environment) || + find_platform_kubernetes(environment) || find_instance_cluster_platform_kubernetes(environment: environment) end + def find_platform_kubernetes(environment) + if Feature.enabled?(:clusters_cte) + find_platform_kubernetes_with_cte(environment) + else + find_cluster_platform_kubernetes(environment: environment) || + find_group_cluster_platform_kubernetes(environment: environment) + end + end + + # EE would override this and utilize environment argument + def find_platform_kubernetes_with_cte(_environment) + Clusters::ClustersHierarchy.new(self).base_and_ancestors + .enabled.default_environment + .first&.platform_kubernetes + end + # EE would override this and utilize environment argument def find_cluster_platform_kubernetes(environment: nil) clusters.enabled.default_environment diff --git a/app/models/concerns/from_union.rb b/app/models/concerns/from_union.rb index 9b8595b1211..e28dee34815 100644 --- a/app/models/concerns/from_union.rb +++ b/app/models/concerns/from_union.rb @@ -40,11 +40,7 @@ module FromUnion .new(members, remove_duplicates: remove_duplicates) .to_sql - # This pattern is necessary as a bug in Rails 4 can cause the use of - # `from("string here").includes(:foo)` to break ActiveRecord. This is - # fixed in https://github.com/rails/rails/pull/25374, which is released as - # part of Rails 5. - from([Arel.sql("(#{union}) #{alias_as}")]) + from(Arel.sql("(#{union}) #{alias_as}")) end # rubocop: enable Gitlab/Union end diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index 78bcce2f592..27a5c3d5286 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -33,22 +33,24 @@ module HasStatus canceled = scope_relevant.canceled.select('count(*)').to_sql warnings = scope_warnings.select('count(*) > 0').to_sql.presence || 'false' - "(CASE - WHEN (#{builds})=(#{skipped}) AND (#{warnings}) THEN 'success' - WHEN (#{builds})=(#{skipped}) THEN 'skipped' - WHEN (#{builds})=(#{success}) THEN 'success' - WHEN (#{builds})=(#{created}) THEN 'created' - WHEN (#{builds})=(#{preparing}) THEN 'preparing' - WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success' - WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled' - WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending' - WHEN (#{running})+(#{pending})>0 THEN 'running' - WHEN (#{manual})>0 THEN 'manual' - WHEN (#{scheduled})>0 THEN 'scheduled' - WHEN (#{preparing})>0 THEN 'preparing' - WHEN (#{created})>0 THEN 'running' - ELSE 'failed' - END)" + Arel.sql( + "(CASE + WHEN (#{builds})=(#{skipped}) AND (#{warnings}) THEN 'success' + WHEN (#{builds})=(#{skipped}) THEN 'skipped' + WHEN (#{builds})=(#{success}) THEN 'success' + WHEN (#{builds})=(#{created}) THEN 'created' + WHEN (#{builds})=(#{preparing}) THEN 'preparing' + WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success' + WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled' + WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending' + WHEN (#{running})+(#{pending})>0 THEN 'running' + WHEN (#{manual})>0 THEN 'manual' + WHEN (#{scheduled})>0 THEN 'scheduled' + WHEN (#{preparing})>0 THEN 'preparing' + WHEN (#{created})>0 THEN 'running' + ELSE 'failed' + END)" + ) end def status @@ -88,22 +90,22 @@ module HasStatus state :scheduled, value: 'scheduled' end - scope :created, -> { where(status: 'created') } - scope :preparing, -> { where(status: 'preparing') } - scope :relevant, -> { where(status: AVAILABLE_STATUSES - ['created']) } - scope :running, -> { where(status: 'running') } - scope :pending, -> { where(status: 'pending') } - scope :success, -> { where(status: 'success') } - scope :failed, -> { where(status: 'failed') } - scope :canceled, -> { where(status: 'canceled') } - scope :skipped, -> { where(status: 'skipped') } - scope :manual, -> { where(status: 'manual') } - scope :scheduled, -> { where(status: 'scheduled') } - scope :alive, -> { where(status: [:created, :preparing, :pending, :running]) } - scope :created_or_pending, -> { where(status: [:created, :pending]) } - scope :running_or_pending, -> { where(status: [:running, :pending]) } - scope :finished, -> { where(status: [:success, :failed, :canceled]) } - scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) } + scope :created, -> { with_status(:created) } + scope :preparing, -> { with_status(:preparing) } + scope :relevant, -> { without_status(:created) } + scope :running, -> { with_status(:running) } + scope :pending, -> { with_status(:pending) } + scope :success, -> { with_status(:success) } + scope :failed, -> { with_status(:failed) } + scope :canceled, -> { with_status(:canceled) } + scope :skipped, -> { with_status(:skipped) } + scope :manual, -> { with_status(:manual) } + scope :scheduled, -> { with_status(:scheduled) } + scope :alive, -> { with_status(:created, :preparing, :pending, :running) } + scope :created_or_pending, -> { with_status(:created, :pending) } + scope :running_or_pending, -> { with_status(:running, :pending) } + scope :finished, -> { with_status(:success, :failed, :canceled) } + scope :failed_or_canceled, -> { with_status(:failed, :canceled) } scope :cancelable, -> do where(status: [:running, :preparing, :pending, :created, :scheduled]) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 299e413321d..952de92cae1 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -168,7 +168,7 @@ module Issuable # matched_columns - Modify the scope of the query. 'title', 'description' or joining them with a comma. # # Returns an ActiveRecord::Relation. - def full_search(query, matched_columns: 'title,description') + def full_search(query, matched_columns: 'title,description', use_minimum_char_limit: true) allowed_columns = [:title, :description] matched_columns = matched_columns.to_s.split(',').map(&:to_sym) matched_columns &= allowed_columns @@ -176,7 +176,7 @@ module Issuable # Matching title or description if the matched_columns did not contain any allowed columns. matched_columns = [:title, :description] if matched_columns.empty? - fuzzy_search(query, matched_columns) + fuzzy_search(query, matched_columns, use_minimum_char_limit: use_minimum_char_limit) end def simple_sorts diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 0d88b34fb48..2f3f9b399d9 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -63,6 +63,9 @@ module Mentionable skip_project_check: skip_project_check? ).merge(mentionable_params) + cached_html = self.try(:updated_cached_html_for, attr.to_sym) + options[:rendered] = cached_html if cached_html + extractor.analyze(text, options) end diff --git a/app/models/concerns/project_api_compatibility.rb b/app/models/concerns/project_api_compatibility.rb new file mode 100644 index 00000000000..cb00efb06df --- /dev/null +++ b/app/models/concerns/project_api_compatibility.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# Add methods used by the projects API +module ProjectAPICompatibility + extend ActiveSupport::Concern + + def build_git_strategy=(value) + write_attribute(:build_allow_git_fetch, value == 'fetch') + end + + def auto_devops_enabled=(value) + self.build_auto_devops if self.auto_devops&.enabled.nil? + self.auto_devops.update! enabled: value + end + + def auto_devops_deploy_strategy=(value) + self.build_auto_devops if self.auto_devops&.enabled.nil? + self.auto_devops.update! deploy_strategy: value + end +end diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb index f268a842db4..551a2e56ecf 100644 --- a/app/models/concerns/project_features_compatibility.rb +++ b/app/models/concerns/project_features_compatibility.rb @@ -9,32 +9,70 @@ require 'gitlab/utils' module ProjectFeaturesCompatibility extend ActiveSupport::Concern + # TODO: remove in API v5, replaced by *_access_level def wiki_enabled=(value) - write_feature_attribute(:wiki_access_level, value) + write_feature_attribute_boolean(:wiki_access_level, value) end + # TODO: remove in API v5, replaced by *_access_level def builds_enabled=(value) - write_feature_attribute(:builds_access_level, value) + write_feature_attribute_boolean(:builds_access_level, value) end + # TODO: remove in API v5, replaced by *_access_level def merge_requests_enabled=(value) - write_feature_attribute(:merge_requests_access_level, value) + write_feature_attribute_boolean(:merge_requests_access_level, value) end + # TODO: remove in API v5, replaced by *_access_level def issues_enabled=(value) - write_feature_attribute(:issues_access_level, value) + write_feature_attribute_boolean(:issues_access_level, value) end + # TODO: remove in API v5, replaced by *_access_level def snippets_enabled=(value) - write_feature_attribute(:snippets_access_level, value) + write_feature_attribute_boolean(:snippets_access_level, value) + end + + def repository_access_level=(value) + write_feature_attribute_string(:repository_access_level, value) + end + + def wiki_access_level=(value) + write_feature_attribute_string(:wiki_access_level, value) + end + + def builds_access_level=(value) + write_feature_attribute_string(:builds_access_level, value) + end + + def merge_requests_access_level=(value) + write_feature_attribute_string(:merge_requests_access_level, value) + end + + def issues_access_level=(value) + write_feature_attribute_string(:issues_access_level, value) + end + + def snippets_access_level=(value) + write_feature_attribute_string(:snippets_access_level, value) end private - def write_feature_attribute(field, value) + def write_feature_attribute_boolean(field, value) + access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED + write_feature_attribute_raw(field, access_level) + end + + def write_feature_attribute_string(field, value) + access_level = ProjectFeature.access_level_from_str(value) + write_feature_attribute_raw(field, access_level) + end + + def write_feature_attribute_raw(field, value) build_project_feature unless project_feature - access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED - project_feature.__send__(:write_attribute, field, access_level) # rubocop:disable GitlabSecurity/PublicSend + project_feature.__send__(:write_attribute, field, value) # rubocop:disable GitlabSecurity/PublicSend end end diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb index 6c3962b4c4f..d91be73d6f0 100644 --- a/app/models/concerns/reactive_caching.rb +++ b/app/models/concerns/reactive_caching.rb @@ -173,12 +173,16 @@ module ReactiveCaching end def within_reactive_cache_lifetime?(*args) - !!Rails.cache.read(alive_reactive_cache_key(*args)) + if Feature.enabled?(:reactive_caching_check_key_exists, default_enabled: true) + Rails.cache.exist?(alive_reactive_cache_key(*args)) + else + !!Rails.cache.read(alive_reactive_cache_key(*args)) + end end def enqueuing_update(*args) yield - ensure + ReactiveCachingWorker.perform_in(self.class.reactive_cache_refresh_interval, self.class, id, *args) end end diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb index 22b6b1d720c..e4fe46d722a 100644 --- a/app/models/concerns/relative_positioning.rb +++ b/app/models/concerns/relative_positioning.rb @@ -179,7 +179,7 @@ module RelativePositioning relation = yield relation if block_given? relation - .pluck(self.class.parent_column, "#{calculation}(relative_position) AS position") + .pluck(self.class.parent_column, Arel.sql("#{calculation}(relative_position) AS position")) .first&. last end diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb index b9ffc64e4a9..9becab632f3 100644 --- a/app/models/concerns/routable.rb +++ b/app/models/concerns/routable.rb @@ -46,7 +46,7 @@ module Routable # See https://gitlab.com/gitlab-org/gitlab-ce/issues/18603. Also note that # our unique index is case-sensitive in Postgres. binary = Gitlab::Database.mysql? ? 'BINARY' : '' - order_sql = "(CASE WHEN #{binary} routes.path = #{connection.quote(path)} THEN 0 ELSE 1 END)" + order_sql = Arel.sql("(CASE WHEN #{binary} routes.path = #{connection.quote(path)} THEN 0 ELSE 1 END)") found = where_full_path_in([path]).reorder(order_sql).take return found if found diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb index a15dc19e07a..78544405c49 100644 --- a/app/models/concerns/storage/legacy_namespace.rb +++ b/app/models/concerns/storage/legacy_namespace.rb @@ -64,7 +64,7 @@ module Storage unless gitlab_shell.mv_namespace(repository_storage, full_path_before_last_save, full_path) - Rails.logger.error "Exception moving path #{repository_storage} from #{full_path_before_last_save} to #{full_path}" + Rails.logger.error "Exception moving path #{repository_storage} from #{full_path_before_last_save} to #{full_path}" # rubocop:disable Gitlab/RailsLogger # if we cannot move namespace directory we should rollback # db changes in order to prevent out of sync between db and fs diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb index 8c769be0489..1293df571a3 100644 --- a/app/models/concerns/token_authenticatable.rb +++ b/app/models/concerns/token_authenticatable.rb @@ -52,7 +52,7 @@ module TokenAuthenticatable mod.define_method("#{token_field}_matches?") do |other_token| token = read_attribute(token_field) - token.present? && ActiveSupport::SecurityUtils.variable_size_secure_compare(other_token, token) + token.present? && ActiveSupport::SecurityUtils.secure_compare(other_token, token) end end diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb deleted file mode 100644 index d0f5b6970b1..00000000000 --- a/app/models/cycle_analytics.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -class CycleAnalytics - STAGES = %i[issue plan code test review staging production].freeze - - def initialize(project, options) - @project = project - @options = options - end - - def all_medians_per_stage - STAGES.each_with_object({}) do |stage_name, medians_per_stage| - medians_per_stage[stage_name] = self[stage_name].median - end - end - - def summary - @summary ||= ::Gitlab::CycleAnalytics::StageSummary.new(@project, - from: @options[:from], - current_user: @options[:current_user]).data - end - - def stats - @stats ||= stats_per_stage - end - - def no_stats? - stats.all? { |hash| hash[:value].nil? } - end - - def permissions(user:) - Gitlab::CycleAnalytics::Permissions.get(user: user, project: @project) - end - - def [](stage_name) - Gitlab::CycleAnalytics::Stage[stage_name].new(project: @project, options: @options) - end - - private - - def stats_per_stage - STAGES.map do |stage_name| - self[stage_name].as_json - end - end -end diff --git a/app/models/cycle_analytics/base.rb b/app/models/cycle_analytics/base.rb new file mode 100644 index 00000000000..d7b28cd1b67 --- /dev/null +++ b/app/models/cycle_analytics/base.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module CycleAnalytics + class Base + STAGES = %i[issue plan code test review staging production].freeze + + def all_medians_by_stage + STAGES.each_with_object({}) do |stage_name, medians_per_stage| + medians_per_stage[stage_name] = self[stage_name].median + end + end + + def stats + @stats ||= STAGES.map do |stage_name| + self[stage_name].as_json + end + end + + def no_stats? + stats.all? { |hash| hash[:value].nil? } + end + + def [](stage_name) + Gitlab::CycleAnalytics::Stage[stage_name].new(project: @project, options: @options) + end + end +end diff --git a/app/models/cycle_analytics/project_level.rb b/app/models/cycle_analytics/project_level.rb new file mode 100644 index 00000000000..22631cc7d41 --- /dev/null +++ b/app/models/cycle_analytics/project_level.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module CycleAnalytics + class ProjectLevel < Base + attr_reader :project, :options + + def initialize(project, options:) + @project = project + @options = options + end + + def summary + @summary ||= ::Gitlab::CycleAnalytics::StageSummary.new(project, + from: options[:from], + current_user: options[:current_user]).data + end + + def permissions(user:) + Gitlab::CycleAnalytics::Permissions.get(user: user, project: project) + end + end +end diff --git a/app/models/deployment.rb b/app/models/deployment.rb index a8f5642f726..b69cda4f2f9 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -85,11 +85,6 @@ class Deployment < ApplicationRecord Commit.truncate_sha(sha) end - # Deprecated - will be replaced by a persisted cluster_id - def deployment_platform_cluster - environment.deployment_platform&.cluster - end - def execute_hooks deployment_data = Gitlab::DataBuilder::Deployment.build(self) project.execute_services(deployment_data, :deployment_hooks) @@ -176,45 +171,8 @@ class Deployment < ApplicationRecord deployed_at&.to_time&.in_time_zone&.to_s(:medium) end - def has_metrics? - success? && prometheus_adapter&.can_query? - end - - def metrics - return {} unless has_metrics? - - metrics = prometheus_adapter.query(:deployment, self) - metrics&.merge(deployment_time: finished_at.to_i) || {} - end - - def additional_metrics - return {} unless has_metrics? - - metrics = prometheus_adapter.query(:additional_metrics_deployment, self) - metrics&.merge(deployment_time: finished_at.to_i) || {} - end - private - def prometheus_adapter - service = project.find_or_initialize_service('prometheus') - - if service.can_query? - service - else - cluster_prometheus - end - end - - # TODO remove fallback case to deployment_platform_cluster. - # Otherwise we will continue to pay the performance penalty described in - # https://gitlab.com/gitlab-org/gitlab-ce/issues/63475 - def cluster_prometheus - cluster_with_fallback = cluster || deployment_platform_cluster - - cluster_with_fallback.application_prometheus if cluster_with_fallback&.application_prometheus_available? - end - def ref_path File.join(environment.ref_path, 'deployments', iid.to_s) end diff --git a/app/models/deployment_metrics.rb b/app/models/deployment_metrics.rb new file mode 100644 index 00000000000..cfe762ca25e --- /dev/null +++ b/app/models/deployment_metrics.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +class DeploymentMetrics + include Gitlab::Utils::StrongMemoize + + attr_reader :project, :deployment + + delegate :cluster, to: :deployment + + def initialize(project, deployment) + @project = project + @deployment = deployment + end + + def has_metrics? + deployment.success? && prometheus_adapter&.can_query? + end + + def metrics + return {} unless has_metrics? + + metrics = prometheus_adapter.query(:deployment, deployment) + metrics&.merge(deployment_time: deployment.finished_at.to_i) || {} + end + + def additional_metrics + return {} unless has_metrics? + + metrics = prometheus_adapter.query(:additional_metrics_deployment, deployment) + metrics&.merge(deployment_time: deployment.finished_at.to_i) || {} + end + + private + + def prometheus_adapter + strong_memoize(:prometheus_adapter) do + service = project.find_or_initialize_service('prometheus') + + if service.can_query? + service + else + cluster_prometheus + end + end + end + + # TODO remove fallback case to deployment_platform_cluster. + # Otherwise we will continue to pay the performance penalty described in + # https://gitlab.com/gitlab-org/gitlab-ce/issues/63475 + # + # Removal issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/64105 + def cluster_prometheus + cluster_with_fallback = cluster || deployment_platform_cluster + + cluster_with_fallback.application_prometheus if cluster_with_fallback&.application_prometheus_available? + end + + def deployment_platform_cluster + deployment.environment.deployment_platform&.cluster + end +end diff --git a/app/models/discussion.rb b/app/models/discussion.rb index ae13cdfd85f..dd896f77084 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -38,6 +38,17 @@ class Discussion grouped_notes.values.map { |notes| build(notes, context_noteable) } end + def self.lazy_find(discussion_id) + BatchLoader.for(discussion_id).batch do |discussion_ids, loader| + results = Note.where(discussion_id: discussion_ids).fresh.to_a.group_by(&:discussion_id) + results.each do |discussion_id, notes| + next if notes.empty? + + loader.call(discussion_id, Discussion.build(notes)) + end + end + end + # Returns an alphanumeric discussion ID based on `build_discussion_id` def self.discussion_id(note) Digest::SHA1.hexdigest(build_discussion_id(note).join("-")) diff --git a/app/models/email.rb b/app/models/email.rb index 0ddaa049c3b..580633d3232 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -4,9 +4,8 @@ class Email < ApplicationRecord include Sortable include Gitlab::SQL::Pattern - belongs_to :user + belongs_to :user, optional: false - validates :user_id, presence: true validates :email, presence: true, uniqueness: true, devise_email: true validate :unique_email, if: ->(email) { email.email_changed? } diff --git a/app/models/environment_status.rb b/app/models/environment_status.rb index 2fb6cadc8cd..d7dc64190d6 100644 --- a/app/models/environment_status.rb +++ b/app/models/environment_status.rb @@ -3,11 +3,10 @@ class EnvironmentStatus include Gitlab::Utils::StrongMemoize - attr_reader :environment, :merge_request, :sha + attr_reader :project, :environment, :merge_request, :sha delegate :id, to: :environment delegate :name, to: :environment - delegate :project, to: :environment delegate :status, to: :deployment, allow_nil: true delegate :deployed_at, to: :deployment, allow_nil: true @@ -21,7 +20,8 @@ class EnvironmentStatus build_environments_status(mr, user, mr.merge_pipeline) end - def initialize(environment, merge_request, sha) + def initialize(project, environment, merge_request, sha) + @project = project @environment = environment @merge_request = merge_request @sha = sha @@ -33,8 +33,14 @@ class EnvironmentStatus end end + def has_metrics? + strong_memoize(:has_metrics) do + deployment_metrics.has_metrics? + end + end + def changes - return [] if project.route_map_for(sha).nil? + return [] unless has_route_map? changed_files.map { |file| build_change(file) }.compact end @@ -44,10 +50,18 @@ class EnvironmentStatus .merge_request_diff_files.where(deleted_file: false) end + def has_route_map? + project.route_map_for(sha).present? + end + private PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze + def deployment_metrics + @deployment_metrics ||= DeploymentMetrics.new(project, deployment) + end + def build_change(file) public_path = project.public_path_for_source_path(file.new_path, sha) return if public_path.nil? @@ -67,7 +81,7 @@ class EnvironmentStatus pipeline.environments.available.map do |environment| next unless Ability.allowed?(user, :read_environment, environment) - EnvironmentStatus.new(environment, mr, pipeline.sha) + EnvironmentStatus.new(pipeline.project, environment, mr, pipeline.sha) end.compact end private_class_method :build_environments_status diff --git a/app/models/label_note.rb b/app/models/label_note.rb index d6814f4a948..ba5f1f82a81 100644 --- a/app/models/label_note.rb +++ b/app/models/label_note.rb @@ -62,19 +62,27 @@ class LabelNote < Note end def note_text(html: false) - added = labels_str('added', label_refs_by_action('add', html)) - removed = labels_str('removed', label_refs_by_action('remove', html)) + added = labels_str(label_refs_by_action('add', html), prefix: 'added', suffix: added_suffix) + removed = labels_str(label_refs_by_action('remove', html), prefix: removed_prefix) [added, removed].compact.join(' and ') end + def removed_prefix + 'removed' + end + + def added_suffix + '' + end + # returns string containing added/removed labels including # count of deleted labels: # # added ~1 ~2 + 1 deleted label # added 3 deleted labels # added ~1 ~2 labels - def labels_str(prefix, label_refs) + def labels_str(label_refs, prefix: '', suffix: '') existing_refs = label_refs.select { |ref| ref.present? }.sort refs_str = existing_refs.empty? ? nil : existing_refs.join(' ') @@ -84,9 +92,9 @@ class LabelNote < Note return unless refs_str || deleted_str label_list_str = [refs_str, deleted_str].compact.join(' + ') - suffix = 'label'.pluralize(deleted > 0 ? deleted : existing_refs.count) + suffix += ' label'.pluralize(deleted > 0 ? deleted : existing_refs.count) - "#{prefix} #{label_list_str} #{suffix}" + "#{prefix} #{label_list_str} #{suffix.squish}" end def label_refs_by_action(action, html) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index e96e26cc773..53977748c30 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1127,6 +1127,19 @@ class MergeRequest < ApplicationRecord "refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/merge" end + def train_ref_path + "refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/train" + end + + def cleanup_refs(only: :all) + target_refs = [] + target_refs << ref_path if %i[all head].include?(only) + target_refs << merge_ref_path if %i[all merge].include?(only) + target_refs << train_ref_path if %i[all train].include?(only) + + project.repository.delete_refs(*target_refs) + end + def self.merge_request_ref?(ref) ref.start_with?("refs/#{Repository::REF_MERGE_REQUEST}/") end diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb index 22cedf57b86..5c53cfd8c27 100644 --- a/app/models/merge_requests_closing_issues.rb +++ b/app/models/merge_requests_closing_issues.rb @@ -25,7 +25,7 @@ class MergeRequestsClosingIssues < ApplicationRecord class << self def count_for_collection(ids, current_user) - closing_merge_requests(ids, current_user).group(:issue_id).pluck('issue_id', 'COUNT(*) as count') + closing_merge_requests(ids, current_user).group(:issue_id).pluck('issue_id', Arel.sql('COUNT(*) as count')) end def count_for_issue(id, current_user) diff --git a/app/models/namespace.rb b/app/models/namespace.rb index af50293a179..1d95590bac9 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -41,8 +41,7 @@ class Namespace < ApplicationRecord validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :name, presence: true, - length: { maximum: 255 }, - namespace_name: true + length: { maximum: 255 } validates :description, length: { maximum: 255 } validates :path, diff --git a/app/models/note.rb b/app/models/note.rb index 4e9ea146485..5c31cff9816 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -158,6 +158,8 @@ class Note < ApplicationRecord Discussion.build_collection(all.includes(:noteable).fresh, context_noteable) end + # Note: Where possible consider using Discussion#lazy_find to return + # Discussions in order to benefit from having records batch loaded. def find_discussion(discussion_id) notes = where(discussion_id: discussion_id).fresh.to_a diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index d6d879c6d89..e6e491634ab 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -12,9 +12,11 @@ class PagesDomain < ApplicationRecord validates :domain, hostname: { allow_numeric_hostname: true } validates :domain, uniqueness: { case_sensitive: false } - validates :certificate, presence: { message: 'must be present if HTTPS-only is enabled' }, if: ->(domain) { domain.project&.pages_https_only? } + validates :certificate, presence: { message: 'must be present if HTTPS-only is enabled' }, + if: :certificate_should_be_present? validates :certificate, certificate: true, if: ->(domain) { domain.certificate.present? } - validates :key, presence: { message: 'must be present if HTTPS-only is enabled' }, if: ->(domain) { domain.project&.pages_https_only? } + validates :key, presence: { message: 'must be present if HTTPS-only is enabled' }, + if: :certificate_should_be_present? validates :key, certificate_key: true, if: ->(domain) { domain.key.present? } validates :verification_code, presence: true, allow_blank: false @@ -249,4 +251,8 @@ class PagesDomain < ApplicationRecord rescue OpenSSL::PKey::PKeyError, OpenSSL::Cipher::CipherError nil end + + def certificate_should_be_present? + !auto_ssl_enabled? && project&.pages_https_only? + end end diff --git a/app/models/project.rb b/app/models/project.rb index 0f4fba5d0b6..2a29cb1ae09 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -15,6 +15,7 @@ class Project < ApplicationRecord include CaseSensitivity include TokenAuthenticatable include ValidAttribute + include ProjectAPICompatibility include ProjectFeaturesCompatibility include SelectForProjectAuthorization include Presentable @@ -356,7 +357,7 @@ class Project < ApplicationRecord scope :with_unmigrated_storage, -> { where('storage_version < :version OR storage_version IS NULL', version: LATEST_STORAGE_VERSION) } # last_activity_at is throttled every minute, but last_repository_updated_at is updated with every push - scope :sorted_by_activity, -> { reorder("GREATEST(COALESCE(last_activity_at, '1970-01-01'), COALESCE(last_repository_updated_at, '1970-01-01')) DESC") } + scope :sorted_by_activity, -> { reorder(Arel.sql("GREATEST(COALESCE(last_activity_at, '1970-01-01'), COALESCE(last_repository_updated_at, '1970-01-01')) DESC")) } scope :sorted_by_stars_desc, -> { reorder(star_count: :desc) } scope :sorted_by_stars_asc, -> { reorder(star_count: :asc) } @@ -611,7 +612,7 @@ class Project < ApplicationRecord end end - def initialize(attributes = {}) + def initialize(attributes = nil) # We can't use default_value_for because the database has a default # value of 0 for visibility_level. If someone attempts to create a # private project, default_value_for will assume that the @@ -621,6 +622,8 @@ class Project < ApplicationRecord # # To fix the problem, we assign the actual default in the application if # no explicit visibility has been initialized. + attributes ||= {} + unless visibility_attribute_present?(attributes) attributes[:visibility_level] = Gitlab::CurrentSettings.default_project_visibility end @@ -687,10 +690,6 @@ class Project < ApplicationRecord { scope: :project, status: auto_devops&.enabled || Feature.enabled?(:force_autodevops_on_by_default, self) } end - def multiple_mr_assignees_enabled? - Feature.enabled?(:multiple_merge_request_assignees, self) - end - def daily_statistics_enabled? Feature.enabled?(:project_daily_statistics, self, default_enabled: true) end @@ -783,6 +782,7 @@ class Project < ApplicationRecord job_id end + # rubocop:disable Gitlab/RailsLogger def log_import_activity(job_id, type: :import) job_type = type.to_s.capitalize @@ -792,6 +792,7 @@ class Project < ApplicationRecord Rails.logger.error("#{job_type} job failed to create for #{full_path}.") end end + # rubocop:enable Gitlab/RailsLogger def reset_cache_and_import_attrs run_after_commit do @@ -1554,7 +1555,7 @@ class Project < ApplicationRecord end def valid_runners_token?(token) - self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token) + self.runners_token && ActiveSupport::SecurityUtils.secure_compare(token, self.runners_token) end # rubocop: disable CodeReuse/ServiceClass @@ -1664,6 +1665,7 @@ class Project < ApplicationRecord end # rubocop: enable CodeReuse/ServiceClass + # rubocop:disable Gitlab/RailsLogger def write_repository_config(gl_full_path: full_path) # We'd need to keep track of project full path otherwise directory tree # created with hashed storage enabled cannot be usefully imported using @@ -1673,6 +1675,7 @@ class Project < ApplicationRecord Rails.logger.error("Error writing to .git/config for project #{full_path} (#{id}): #{e.message}.") nil end + # rubocop:enable Gitlab/RailsLogger def after_import repository.after_import @@ -1714,6 +1717,7 @@ class Project < ApplicationRecord @pipeline_status ||= Gitlab::Cache::Ci::ProjectPipelineStatus.load_for_project(self) end + # rubocop:disable Gitlab/RailsLogger def add_export_job(current_user:, after_export_strategy: nil, params: {}) job_id = ProjectExportWorker.perform_async(current_user.id, self.id, after_export_strategy, params) @@ -1723,6 +1727,7 @@ class Project < ApplicationRecord Rails.logger.error "Export job failed to start for project ID #{self.id}" end end + # rubocop:enable Gitlab/RailsLogger def import_export_shared @import_export_shared ||= Gitlab::ImportExport::Shared.new(self) @@ -1913,9 +1918,8 @@ class Project < ApplicationRecord @route_maps_by_commit ||= Hash.new do |h, sha| h[sha] = begin data = repository.route_map_for(sha) - next unless data - Gitlab::RouteMap.new(data) + Gitlab::RouteMap.new(data) if data rescue Gitlab::RouteMap::FormatError nil end @@ -2144,7 +2148,7 @@ class Project < ApplicationRecord public? && repository_exists? && Gitlab::CurrentSettings.hashed_storage_enabled && - Feature.enabled?(:object_pools, self) + Feature.enabled?(:object_pools, self, default_enabled: true) end def leave_pool_repository diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index 0542581c6e0..7ff06655de0 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -24,6 +24,12 @@ class ProjectFeature < ApplicationRecord FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze PRIVATE_FEATURES_MIN_ACCESS_LEVEL = { merge_requests: Gitlab::Access::REPORTER }.freeze + STRING_OPTIONS = HashWithIndifferentAccess.new({ + 'disabled' => DISABLED, + 'private' => PRIVATE, + 'enabled' => ENABLED, + 'public' => PUBLIC + }).freeze class << self def access_level_attribute(feature) @@ -45,6 +51,14 @@ class ProjectFeature < ApplicationRecord PRIVATE_FEATURES_MIN_ACCESS_LEVEL.fetch(feature, Gitlab::Access::GUEST) end + def access_level_from_str(level) + STRING_OPTIONS.fetch(level) + end + + def str_from_access_level(level) + STRING_OPTIONS.key(level) + end + private def ensure_feature!(feature) @@ -83,6 +97,10 @@ class ProjectFeature < ApplicationRecord public_send(ProjectFeature.access_level_attribute(feature)) # rubocop:disable GitlabSecurity/PublicSend end + def string_access_level(feature) + ProjectFeature.str_from_access_level(access_level(feature)) + end + def builds_enabled? builds_access_level > DISABLED end diff --git a/app/models/project_import_state.rb b/app/models/project_import_state.rb index 1605345efd5..23adffb33d8 100644 --- a/app/models/project_import_state.rb +++ b/app/models/project_import_state.rb @@ -65,7 +65,7 @@ class ProjectImportState < ApplicationRecord update_column(:last_error, sanitized_message) rescue ActiveRecord::ActiveRecordError => e - Rails.logger.error("Error setting import status to failed: #{e.message}. Original error: #{sanitized_message}") + Rails.logger.error("Error setting import status to failed: #{e.message}. Original error: #{sanitized_message}") # rubocop:disable Gitlab/RailsLogger ensure @errors = original_errors end diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb index f0ef2d925ab..47106d7bdbb 100644 --- a/app/models/project_services/ci_service.rb +++ b/app/models/project_services/ci_service.rb @@ -7,7 +7,7 @@ class CiService < Service default_value_for :category, 'ci' def valid_token?(token) - self.respond_to?(:token) && self.token.present? && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) + self.respond_to?(:token) && self.token.present? && ActiveSupport::SecurityUtils.secure_compare(token, self.token) end def self.supported_events diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index dbdc8345c93..d2660051343 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -46,7 +46,7 @@ class DroneCiService < CiService end def commit_status(sha, ref) - with_reactive_cache(sha, ref) {|cached| cached[:commit_status] } + with_reactive_cache(sha, ref) { |cached| cached[:commit_status] } end def calculate_reactive_cache(sha, ref) @@ -68,7 +68,7 @@ class DroneCiService < CiService end { commit_status: status } - rescue Errno::ECONNREFUSED + rescue *Gitlab::HTTP::HTTP_ERRORS { commit_status: :error } end diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb index bfabc6d262c..5f5cff97808 100644 --- a/app/models/project_services/slash_commands_service.rb +++ b/app/models/project_services/slash_commands_service.rb @@ -12,7 +12,7 @@ class SlashCommandsService < Service def valid_token?(token) self.respond_to?(:token) && self.token.present? && - ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) + ActiveSupport::SecurityUtils.secure_compare(token, self.token) end def self.supported_events diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb index 8a179b4d56d..3802d258664 100644 --- a/app/models/project_statistics.rb +++ b/app/models/project_statistics.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class ProjectStatistics < ApplicationRecord + include AfterCommitQueue + belongs_to :project belongs_to :namespace @@ -15,6 +17,7 @@ class ProjectStatistics < ApplicationRecord COLUMNS_TO_REFRESH = [:repository_size, :wiki_size, :lfs_objects_size, :commit_count].freeze INCREMENTABLE_COLUMNS = { build_artifacts_size: %i[storage_size], packages_size: %i[storage_size] }.freeze + NAMESPACE_RELATABLE_COLUMNS = [:repository_size, :wiki_size, :lfs_objects_size].freeze scope :for_project_ids, ->(project_ids) { where(project_id: project_ids) } @@ -22,13 +25,17 @@ class ProjectStatistics < ApplicationRecord repository_size + lfs_objects_size end - def refresh!(only: nil) + def refresh!(only: []) COLUMNS_TO_REFRESH.each do |column, generator| - if only.blank? || only.include?(column) + if only.empty? || only.include?(column) public_send("update_#{column}") # rubocop:disable GitlabSecurity/PublicSend end end + if only.empty? || only.any? { |column| NAMESPACE_RELATABLE_COLUMNS.include?(column) } + schedule_namespace_aggregation_worker + end + save! end @@ -81,4 +88,18 @@ class ProjectStatistics < ApplicationRecord update_all(updates.join(', ')) end + + private + + def schedule_namespace_aggregation_worker + run_after_commit do + next unless schedule_aggregation_worker? + + Namespaces::ScheduleAggregationWorker.perform_async(project.namespace_id) + end + end + + def schedule_aggregation_worker? + Feature.enabled?(:update_statistics_namespace, project&.root_ancestor) + end end diff --git a/app/models/prometheus_metric.rb b/app/models/prometheus_metric.rb index 62090444f79..b8e7673dcf5 100644 --- a/app/models/prometheus_metric.rb +++ b/app/models/prometheus_metric.rb @@ -3,68 +3,7 @@ class PrometheusMetric < ApplicationRecord belongs_to :project, validate: true, inverse_of: :prometheus_metrics - enum group: { - # built-in groups - nginx_ingress_vts: -1, - ha_proxy: -2, - aws_elb: -3, - nginx: -4, - kubernetes: -5, - nginx_ingress: -6, - - # custom/user groups - business: 0, - response: 1, - system: 2 - } - - GROUP_DETAILS = { - # built-in groups - nginx_ingress_vts: { - group_title: _('Response metrics (NGINX Ingress VTS)'), - required_metrics: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg), - priority: 10 - }.freeze, - nginx_ingress: { - group_title: _('Response metrics (NGINX Ingress)'), - required_metrics: %w(nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum), - priority: 10 - }.freeze, - ha_proxy: { - group_title: _('Response metrics (HA Proxy)'), - required_metrics: %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total), - priority: 10 - }.freeze, - aws_elb: { - group_title: _('Response metrics (AWS ELB)'), - required_metrics: %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum), - priority: 10 - }.freeze, - nginx: { - group_title: _('Response metrics (NGINX)'), - required_metrics: %w(nginx_server_requests nginx_server_requestMsec), - priority: 10 - }.freeze, - kubernetes: { - group_title: _('System metrics (Kubernetes)'), - required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total), - priority: 5 - }.freeze, - - # custom/user groups - business: { - group_title: _('Business metrics (Custom)'), - priority: 0 - }.freeze, - response: { - group_title: _('Response metrics (Custom)'), - priority: -5 - }.freeze, - system: { - group_title: _('System metrics (Custom)'), - priority: -10 - }.freeze - }.freeze + enum group: PrometheusMetricEnums.groups validates :title, presence: true validates :query, presence: true @@ -121,6 +60,6 @@ class PrometheusMetric < ApplicationRecord private def group_details(group) - GROUP_DETAILS.fetch(group.to_sym) + PrometheusMetricEnums.group_details.fetch(group.to_sym) end end diff --git a/app/models/prometheus_metric_enums.rb b/app/models/prometheus_metric_enums.rb new file mode 100644 index 00000000000..6cb22cc69cd --- /dev/null +++ b/app/models/prometheus_metric_enums.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module PrometheusMetricEnums + def self.groups + { + # built-in groups + nginx_ingress_vts: -1, + ha_proxy: -2, + aws_elb: -3, + nginx: -4, + kubernetes: -5, + nginx_ingress: -6, + + # custom/user groups + business: 0, + response: 1, + system: 2 + } + end + + def self.group_details + { + # built-in groups + nginx_ingress_vts: { + group_title: _('Response metrics (NGINX Ingress VTS)'), + required_metrics: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg), + priority: 10 + }.freeze, + nginx_ingress: { + group_title: _('Response metrics (NGINX Ingress)'), + required_metrics: %w(nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum), + priority: 10 + }.freeze, + ha_proxy: { + group_title: _('Response metrics (HA Proxy)'), + required_metrics: %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total), + priority: 10 + }.freeze, + aws_elb: { + group_title: _('Response metrics (AWS ELB)'), + required_metrics: %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum), + priority: 10 + }.freeze, + nginx: { + group_title: _('Response metrics (NGINX)'), + required_metrics: %w(nginx_server_requests nginx_server_requestMsec), + priority: 10 + }.freeze, + kubernetes: { + group_title: _('System metrics (Kubernetes)'), + required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total), + priority: 5 + }.freeze, + + # custom/user groups + business: { + group_title: _('Business metrics (Custom)'), + priority: 0 + }.freeze, + response: { + group_title: _('Response metrics (Custom)'), + priority: -5 + }.freeze, + system: { + group_title: _('System metrics (Custom)'), + priority: -10 + }.freeze + }.freeze + end +end diff --git a/app/models/repository.rb b/app/models/repository.rb index a408db7ebbe..187382ad182 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -273,7 +273,7 @@ class Repository # This will still fail if the file is corrupted (e.g. 0 bytes) raw_repository.write_ref(keep_around_ref_name(sha), sha) rescue Gitlab::Git::CommandError => ex - Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}" + Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}" # rubocop:disable Gitlab/RailsLogger end end @@ -845,6 +845,10 @@ class Repository raw.merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref) end + def delete_refs(*ref_names) + raw.delete_refs(*ref_names) + end + def ff_merge(user, source, target_branch, merge_request: nil) their_commit_id = commit(source)&.id raise 'Invalid merge source' if their_commit_id.nil? @@ -930,6 +934,7 @@ class Repository async_remove_remote(remote_name) if tmp_remote_name end + # rubocop:disable Gitlab/RailsLogger def async_remove_remote(remote_name) return unless remote_name @@ -943,6 +948,7 @@ class Repository job_id end + # rubocop:enable Gitlab/RailsLogger def fetch_source_branch!(source_repository, source_branch, local_ref) raw_repository.fetch_source_branch!(source_repository.raw_repository, source_branch, local_ref) diff --git a/app/models/resource_label_event.rb b/app/models/resource_label_event.rb index f2c7cb6a65d..ad08f4763ae 100644 --- a/app/models/resource_label_event.rb +++ b/app/models/resource_label_event.rb @@ -36,10 +36,9 @@ class ResourceLabelEvent < ApplicationRecord issue || merge_request end - # create same discussion id for all actions with the same user and time def discussion_id(resource = nil) strong_memoize(:discussion_id) do - Digest::SHA1.hexdigest([self.class.name, created_at, user_id].join("-")) + Digest::SHA1.hexdigest(discussion_id_key.join("-")) end end @@ -121,4 +120,8 @@ class ResourceLabelEvent < ApplicationRecord def resource_parent issuable.project || issuable.group end + + def discussion_id_key + [self.class.name, created_at, user_id] + end end diff --git a/app/models/ssh_host_key.rb b/app/models/ssh_host_key.rb index b6fb39ee81f..9bd35d30845 100644 --- a/app/models/ssh_host_key.rb +++ b/app/models/ssh_host_key.rb @@ -106,7 +106,7 @@ class SshHostKey if status.success? && !errors.present? { known_hosts: known_hosts } else - Rails.logger.debug("Failed to detect SSH host keys for #{id}: #{errors}") + Rails.logger.debug("Failed to detect SSH host keys for #{id}: #{errors}") # rubocop:disable Gitlab/RailsLogger { error: 'Failed to detect SSH host keys' } end diff --git a/app/models/storage/legacy_project.rb b/app/models/storage/legacy_project.rb index b483c677be9..928c773c307 100644 --- a/app/models/storage/legacy_project.rb +++ b/app/models/storage/legacy_project.rb @@ -41,7 +41,7 @@ module Storage gitlab_shell.mv_repository(repository_storage, "#{old_full_path}.wiki", "#{new_full_path}.wiki") return true rescue => e - Rails.logger.error "Exception renaming #{old_full_path} -> #{new_full_path}: #{e}" + Rails.logger.error "Exception renaming #{old_full_path} -> #{new_full_path}: #{e}" # rubocop:disable Gitlab/RailsLogger # Returning false does not rollback after_* transaction but gives # us information about failing some of tasks return false diff --git a/app/models/uploads/base.rb b/app/models/uploads/base.rb index f9814159958..29f376670da 100644 --- a/app/models/uploads/base.rb +++ b/app/models/uploads/base.rb @@ -7,7 +7,7 @@ module Uploads attr_reader :logger def initialize(logger: nil) - @logger ||= Rails.logger + @logger ||= Rails.logger # rubocop:disable Gitlab/RailsLogger end def delete_keys_async(keys_to_delete) diff --git a/app/models/user.rb b/app/models/user.rb index 26be197209a..02637b70f03 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1117,9 +1117,10 @@ class User < ApplicationRecord def ensure_namespace_correct if namespace - namespace.path = namespace.name = username if username_changed? + namespace.path = username if username_changed? + namespace.name = name if name_changed? else - build_namespace(path: username, name: username) + build_namespace(path: username, name: name) end end diff --git a/app/policies/clusters/instance_policy.rb b/app/policies/clusters/instance_policy.rb index f72096e8fc6..bd7ff413afe 100644 --- a/app/policies/clusters/instance_policy.rb +++ b/app/policies/clusters/instance_policy.rb @@ -2,11 +2,6 @@ module Clusters class InstancePolicy < BasePolicy - include ClusterableActions - - condition(:has_clusters, scope: :subject) { clusterable_has_clusters? } - condition(:can_have_multiple_clusters) { multiple_clusters_available? } - rule { admin }.policy do enable :read_cluster enable :add_cluster @@ -14,7 +9,5 @@ module Clusters enable :update_cluster enable :admin_cluster end - - rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster end end diff --git a/app/policies/concerns/clusterable_actions.rb b/app/policies/concerns/clusterable_actions.rb deleted file mode 100644 index 08ddd742ea9..00000000000 --- a/app/policies/concerns/clusterable_actions.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -module ClusterableActions - private - - # Overridden on EE module - def multiple_clusters_available? - false - end - - def clusterable_has_clusters? - !subject.clusters.empty? - end -end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index bd1eb02ca1f..0add8bfad31 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class GroupPolicy < BasePolicy - include ClusterableActions - desc "Group is public" with_options scope: :subject, score: 0 condition(:public_group) { @subject.public? } @@ -29,9 +27,6 @@ class GroupPolicy < BasePolicy GroupProjectsFinder.new(group: @subject, current_user: @user, options: { include_subgroups: true, only_owned: true }).execute.any? end - condition(:has_clusters, scope: :subject) { clusterable_has_clusters? } - condition(:can_have_multiple_clusters) { multiple_clusters_available? } - with_options scope: :subject, score: 0 condition(:request_access_enabled) { @subject.request_access_enabled } @@ -126,8 +121,6 @@ class GroupPolicy < BasePolicy rule { owner & (~share_with_group_locked | ~has_parent | ~parent_share_with_group_locked | can_change_parent_share_with_group_lock) }.enable :change_share_with_group_lock - rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster - rule { developer & developer_maintainer_access }.enable :create_projects rule { create_projects_disabled }.prevent :create_projects diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 3c9ffbb2065..e79bac6bee3 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -2,7 +2,6 @@ class ProjectPolicy < BasePolicy extend ClassMethods - include ClusterableActions READONLY_FEATURES_WHEN_ARCHIVED = %i[ issue @@ -114,9 +113,6 @@ class ProjectPolicy < BasePolicy @subject.feature_available?(:merge_requests, @user) end - condition(:has_clusters, scope: :subject) { clusterable_has_clusters? } - condition(:can_have_multiple_clusters) { multiple_clusters_available? } - condition(:internal_builds_disabled) do !@subject.builds_enabled? end @@ -430,8 +426,6 @@ class ProjectPolicy < BasePolicy (~guest & can?(:read_project_for_iids) & merge_requests_visible_to_user) | can?(:read_merge_request) end.enable :read_merge_request_iid - rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster - rule { ~can?(:read_cross_project) & ~classification_label_authorized }.policy do # Preventing access here still allows the projects to be listed. Listing # projects doesn't check the `:read_project` ability. But instead counts diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb index 34bdf156623..fff6d23efdf 100644 --- a/app/presenters/clusterable_presenter.rb +++ b/app/presenters/clusterable_presenter.rb @@ -13,7 +13,8 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated end def can_add_cluster? - can?(current_user, :add_cluster, clusterable) + can?(current_user, :add_cluster, clusterable) && + (has_no_clusters? || multiple_clusters_available?) end def can_create_cluster? @@ -63,4 +64,15 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated def learn_more_link raise NotImplementedError end + + private + + # Overridden on EE module + def multiple_clusters_available? + false + end + + def has_no_clusters? + clusterable.clusters.empty? + end end diff --git a/app/serializers/current_board_entity.rb b/app/serializers/current_board_entity.rb new file mode 100644 index 00000000000..371151532f8 --- /dev/null +++ b/app/serializers/current_board_entity.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class CurrentBoardEntity < Grape::Entity + expose :id + expose :name +end diff --git a/app/serializers/current_board_serializer.rb b/app/serializers/current_board_serializer.rb new file mode 100644 index 00000000000..c58c77194f2 --- /dev/null +++ b/app/serializers/current_board_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class CurrentBoardSerializer < BaseSerializer + entity CurrentBoardEntity +end diff --git a/app/serializers/environment_status_entity.rb b/app/serializers/environment_status_entity.rb index f6321b9e520..811cc2ad5af 100644 --- a/app/serializers/environment_status_entity.rb +++ b/app/serializers/environment_status_entity.rb @@ -11,7 +11,7 @@ class EnvironmentStatusEntity < Grape::Entity project_environment_path(es.project, es.environment) end - expose :metrics_url, if: ->(*) { can_read_environment? && deployment.has_metrics? } do |es| + expose :metrics_url, if: ->(*) { can_read_environment? && has_metrics? } do |es| metrics_project_environment_deployment_path(es.project, es.environment, es.deployment) end @@ -45,8 +45,8 @@ class EnvironmentStatusEntity < Grape::Entity object.environment end - def deployment - object.deployment + def has_metrics? + object.has_metrics? end def project diff --git a/app/services/akismet_service.rb b/app/services/akismet_service.rb index 82ae66ab0f5..63be3c371ec 100644 --- a/app/services/akismet_service.rb +++ b/app/services/akismet_service.rb @@ -25,7 +25,7 @@ class AkismetService is_spam, is_blatant = akismet_client.check(options[:ip_address], options[:user_agent], params) is_spam || is_blatant rescue => e - Rails.logger.error("Unable to connect to Akismet: #{e}, skipping check") + Rails.logger.error("Unable to connect to Akismet: #{e}, skipping check") # rubocop:disable Gitlab/RailsLogger false end end @@ -63,7 +63,7 @@ class AkismetService akismet_client.public_send(type, options[:ip_address], options[:user_agent], params) # rubocop:disable GitlabSecurity/PublicSend true rescue => e - Rails.logger.error("Unable to connect to Akismet: #{e}, skipping!") + Rails.logger.error("Unable to connect to Akismet: #{e}, skipping!") # rubocop:disable Gitlab/RailsLogger false end end diff --git a/app/services/audit_event_service.rb b/app/services/audit_event_service.rb index 201048aaba5..73f3408a240 100644 --- a/app/services/audit_event_service.rb +++ b/app/services/audit_event_service.rb @@ -35,8 +35,12 @@ class AuditEventService @file_logger ||= Gitlab::AuditJsonLogger.build end + def formatted_details + @details.merge(@details.slice(:from, :to).transform_values(&:to_s)) + end + def log_security_event_to_file - file_logger.info(base_payload.merge(@details)) + file_logger.info(base_payload.merge(formatted_details)) end def log_security_event_to_database diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb index e27d34dbcab..755d723b9a0 100644 --- a/app/services/boards/issues/move_service.rb +++ b/app/services/boards/issues/move_service.rb @@ -4,14 +4,37 @@ module Boards module Issues class MoveService < Boards::BaseService def execute(issue) - return false unless can?(current_user, :update_issue, issue) - return false if issue_params(issue).empty? + issue_modification_params = issue_params(issue) + return false if issue_modification_params.empty? - update(issue) + move_single_issue(issue, issue_modification_params) + end + + def execute_multiple(issues) + return false if issues.empty? + + last_inserted_issue_id = nil + issues.map do |issue| + issue_modification_params = issue_params(issue) + next if issue_modification_params.empty? + + if last_inserted_issue_id + issue_modification_params[:move_between_ids] = move_between_ids({ move_after_id: nil, move_before_id: last_inserted_issue_id }) + end + + last_inserted_issue_id = issue.id + move_single_issue(issue, issue_modification_params) + end.all? end private + def move_single_issue(issue, issue_modification_params) + return false unless can?(current_user, :update_issue, issue) + + update(issue, issue_modification_params) + end + def board @board ||= parent.boards.find(params[:board_id]) end @@ -33,8 +56,8 @@ module Boards end # rubocop: enable CodeReuse/ActiveRecord - def update(issue) - ::Issues::UpdateService.new(issue.project, current_user, issue_params(issue)).execute(issue) + def update(issue, issue_modification_params) + ::Issues::UpdateService.new(issue.project, current_user, issue_modification_params).execute(issue) end def issue_params(issue) @@ -48,6 +71,7 @@ module Boards ) end + move_between_ids = move_between_ids(params) if move_between_ids attrs[:move_between_ids] = move_between_ids attrs[:board_group_id] = board.group&.id @@ -78,8 +102,8 @@ module Boards end # rubocop: enable CodeReuse/ActiveRecord - def move_between_ids - ids = [params[:move_after_id], params[:move_before_id]] + def move_between_ids(move_params) + ids = [move_params[:move_after_id], move_params[:move_before_id]] .map(&:to_i) .map { |m| m.positive? ? m : nil } diff --git a/app/services/ci/archive_trace_service.rb b/app/services/ci/archive_trace_service.rb index a1dd00721b5..b5cfa1d019c 100644 --- a/app/services/ci/archive_trace_service.rb +++ b/app/services/ci/archive_trace_service.rb @@ -24,11 +24,11 @@ module Ci def archive_error(error, job) failed_archive_counter.increment - Rails.logger.error "Failed to archive trace. id: #{job.id} message: #{error.message}" + Rails.logger.error "Failed to archive trace. id: #{job.id} message: #{error.message}" # rubocop:disable Gitlab/RailsLogger Gitlab::Sentry .track_exception(error, - issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/51502', + issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/51502', extra: { job_id: job.id }) end end diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb index 4a7ce00b8e2..aaf56048b5c 100644 --- a/app/services/ci/process_pipeline_service.rb +++ b/app/services/ci/process_pipeline_service.rb @@ -44,7 +44,7 @@ module Ci # rubocop: disable CodeReuse/ActiveRecord def stage_indexes_of_created_processables - created_processables.order(:stage_idx).pluck('distinct stage_idx') + created_processables.order(:stage_idx).pluck(Arel.sql('DISTINCT stage_idx')) end # rubocop: enable CodeReuse/ActiveRecord @@ -68,7 +68,7 @@ module Ci latest_statuses = pipeline.statuses.latest .group(:name) .having('count(*) > 1') - .pluck('max(id)', 'name') + .pluck(Arel.sql('MAX(id)'), 'name') # mark builds that are retried pipeline.statuses.latest diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb index dedab98b56d..9d4cf5df713 100644 --- a/app/services/ci/register_job_service.rb +++ b/app/services/ci/register_job_service.rb @@ -6,7 +6,7 @@ module Ci class RegisterJobService attr_reader :runner - JOB_QUEUE_DURATION_SECONDS_BUCKETS = [1, 3, 10, 30, 60, 300].freeze + JOB_QUEUE_DURATION_SECONDS_BUCKETS = [1, 3, 10, 30, 60, 300, 900].freeze JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET = 5.freeze Result = Struct.new(:build, :valid?) diff --git a/app/services/clusters/create_service.rb b/app/services/clusters/create_service.rb index 886e484caaf..5fb5e15c32d 100644 --- a/app/services/clusters/create_service.rb +++ b/app/services/clusters/create_service.rb @@ -10,24 +10,27 @@ module Clusters def execute(access_token: nil) raise ArgumentError, 'Unknown clusterable provided' unless clusterable - raise ArgumentError, _('Instance does not support multiple Kubernetes clusters') unless can_create_cluster? cluster_params = params.merge(user: current_user).merge(clusterable_params) cluster_params[:provider_gcp_attributes].try do |provider| provider[:access_token] = access_token end - create_cluster(cluster_params).tap do |cluster| - ClusterProvisionWorker.perform_async(cluster.id) if cluster.persisted? + cluster = Clusters::Cluster.new(cluster_params) + + unless can_create_cluster? + cluster.errors.add(:base, _('Instance does not support multiple Kubernetes clusters')) end - end - private + return cluster if cluster.errors.present? - def create_cluster(cluster_params) - Clusters::Cluster.create(cluster_params) + cluster.tap do |cluster| + cluster.save && ClusterProvisionWorker.perform_async(cluster.id) + end end + private + def clusterable @clusterable ||= params.delete(:clusterable) end diff --git a/app/services/clusters/gcp/kubernetes.rb b/app/services/clusters/gcp/kubernetes.rb index 90ed529670c..85711764785 100644 --- a/app/services/clusters/gcp/kubernetes.rb +++ b/app/services/clusters/gcp/kubernetes.rb @@ -9,6 +9,8 @@ module Clusters GITLAB_CLUSTER_ROLE_BINDING_NAME = 'gitlab-admin' GITLAB_CLUSTER_ROLE_NAME = 'cluster-admin' PROJECT_CLUSTER_ROLE_NAME = 'edit' + GITLAB_KNATIVE_SERVING_ROLE_NAME = 'gitlab-knative-serving-role' + GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME = 'gitlab-knative-serving-rolebinding' end end end diff --git a/app/services/clusters/gcp/kubernetes/create_or_update_service_account_service.rb b/app/services/clusters/gcp/kubernetes/create_or_update_service_account_service.rb index 49e766cbf13..7c5450dbcd6 100644 --- a/app/services/clusters/gcp/kubernetes/create_or_update_service_account_service.rb +++ b/app/services/clusters/gcp/kubernetes/create_or_update_service_account_service.rb @@ -41,7 +41,15 @@ module Clusters kubeclient.create_or_update_service_account(service_account_resource) kubeclient.create_or_update_secret(service_account_token_resource) - create_role_or_cluster_role_binding if rbac + + return unless rbac + + create_role_or_cluster_role_binding + + return unless namespace_creator + + create_or_update_knative_serving_role + create_or_update_knative_serving_role_binding end private @@ -63,6 +71,14 @@ module Clusters end end + def create_or_update_knative_serving_role + kubeclient.update_role(knative_serving_role_resource) + end + + def create_or_update_knative_serving_role_binding + kubeclient.update_role_binding(knative_serving_role_binding_resource) + end + def service_account_resource Gitlab::Kubernetes::ServiceAccount.new( service_account_name, @@ -92,6 +108,29 @@ module Clusters Gitlab::Kubernetes::RoleBinding.new( name: role_binding_name, role_name: Clusters::Gcp::Kubernetes::PROJECT_CLUSTER_ROLE_NAME, + role_kind: :ClusterRole, + namespace: service_account_namespace, + service_account_name: service_account_name + ).generate + end + + def knative_serving_role_resource + Gitlab::Kubernetes::Role.new( + name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME, + namespace: service_account_namespace, + rules: [{ + apiGroups: %w(serving.knative.dev), + resources: %w(configurations configurationgenerations routes revisions revisionuids autoscalers services), + verbs: %w(get list create update delete patch watch) + }] + ).generate + end + + def knative_serving_role_binding_resource + Gitlab::Kubernetes::RoleBinding.new( + name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, + role_name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME, + role_kind: :Role, namespace: service_account_namespace, service_account_name: service_account_name ).generate diff --git a/app/services/concerns/exclusive_lease_guard.rb b/app/services/concerns/exclusive_lease_guard.rb index 2cb73555d85..0c5ecca3a50 100644 --- a/app/services/concerns/exclusive_lease_guard.rb +++ b/app/services/concerns/exclusive_lease_guard.rb @@ -58,6 +58,6 @@ module ExclusiveLeaseGuard end def log_error(message, extra_args = {}) - Rails.logger.error(message) + Rails.logger.error(message) # rubocop:disable Gitlab/RailsLogger end end diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index e9659f5489a..e78e5d5fc2c 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -27,8 +27,7 @@ module Groups @group.build_chat_team(name: response['name'], team_id: response['id']) end - @group.save - @group.add_owner(current_user) + @group.add_owner(current_user) if @group.save @group end diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb index 654fe84e3dc..9e00cbbbc55 100644 --- a/app/services/groups/destroy_service.rb +++ b/app/services/groups/destroy_service.rb @@ -6,7 +6,7 @@ module Groups def async_execute job_id = GroupDestroyWorker.perform_async(group.id, current_user.id) - Rails.logger.info("User #{current_user.id} scheduled a deletion of group ID #{group.id} with job ID #{job_id}") + Rails.logger.info("User #{current_user.id} scheduled a deletion of group ID #{group.id} with job ID #{job_id}") # rubocop:disable Gitlab/RailsLogger end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/services/issuable/bulk_update_service.rb b/app/services/issuable/bulk_update_service.rb index c4beddf2294..6d215d7a3b9 100644 --- a/app/services/issuable/bulk_update_service.rb +++ b/app/services/issuable/bulk_update_service.rb @@ -1,7 +1,15 @@ # frozen_string_literal: true module Issuable - class BulkUpdateService < IssuableBaseService + class BulkUpdateService + include Gitlab::Allowable + + attr_accessor :current_user, :params + + def initialize(user = nil, params = {}) + @current_user, @params = user, params.dup + end + # rubocop: disable CodeReuse/ActiveRecord def execute(type) model_class = type.classify.constantize diff --git a/app/services/issuable/clone/content_rewriter.rb b/app/services/issuable/clone/content_rewriter.rb index 00d7078859d..f75b51c4be3 100644 --- a/app/services/issuable/clone/content_rewriter.rb +++ b/app/services/issuable/clone/content_rewriter.rb @@ -23,10 +23,14 @@ module Issuable end def rewrite_notes + new_discussion_ids = {} original_entity.notes_with_associations.find_each do |note| new_note = note.dup + new_discussion_ids[note.discussion_id] ||= Discussion.discussion_id(new_note) new_params = { - project: new_entity.project, noteable: new_entity, + project: new_entity.project, + noteable: new_entity, + discussion_id: new_discussion_ids[note.discussion_id], note: rewrite_content(new_note.note), note_html: nil, created_at: note.created_at, diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 02de080e0ba..db673cace81 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -182,7 +182,7 @@ class IssuableBaseService < BaseService # To be overridden by subclasses end - def before_update(issuable) + def before_update(issuable, skip_spam_check: false) # To be overridden by subclasses end @@ -257,7 +257,7 @@ class IssuableBaseService < BaseService last_edited_at: Time.now, last_edited_by: current_user)) - before_update(issuable) + before_update(issuable, skip_spam_check: true) if issuable.with_transaction_returning_status { issuable.save } # We do not touch as it will affect a update on updated_at field diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index 6b9f23f24cd..7cd825aa967 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -17,8 +17,8 @@ module Issues super end - def before_update(issue) - spam_check(issue, current_user) + def before_update(issue, skip_spam_check: false) + spam_check(issue, current_user) unless skip_spam_check end def handle_changes(issue, options) diff --git a/app/services/labels/create_service.rb b/app/services/labels/create_service.rb index db710bac900..c032985be42 100644 --- a/app/services/labels/create_service.rb +++ b/app/services/labels/create_service.rb @@ -20,7 +20,7 @@ module Labels label.save label else - Rails.logger.warn("target_params should contain :project or :group or :template, actual value: #{target_params}") + Rails.logger.warn("target_params should contain :project or :group or :template, actual value: #{target_params}") # rubocop:disable Gitlab/RailsLogger end end end diff --git a/app/services/merge_requests/merge_base_service.rb b/app/services/merge_requests/merge_base_service.rb index 095bdca5472..1ed396cee1e 100644 --- a/app/services/merge_requests/merge_base_service.rb +++ b/app/services/merge_requests/merge_base_service.rb @@ -28,6 +28,17 @@ module MergeRequests private + def check_source + unless source + raise_error('No source for merge') + end + end + + # Overridden in EE. + def check_size_limit + # No-op + end + # Overridden in EE. def error_check! # No-op diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index d8a78001b79..6309052244d 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -48,13 +48,13 @@ module MergeRequests def error_check! super + check_source + error = if @merge_request.should_be_rebased? 'Only fast-forward merge is allowed for your project. Please update your source branch' elsif !@merge_request.mergeable? 'Merge request is not mergeable' - elsif !source - 'No source for merge' end raise_error(error) if error @@ -113,12 +113,12 @@ module MergeRequests end def handle_merge_error(log_message:, save_message_on_model: false) - Rails.logger.error("MergeService ERROR: #{merge_request_info} - #{log_message}") + Rails.logger.error("MergeService ERROR: #{merge_request_info} - #{log_message}") # rubocop:disable Gitlab/RailsLogger @merge_request.update(merge_error: log_message) if save_message_on_model end def log_info(message) - @logger ||= Rails.logger + @logger ||= Rails.logger # rubocop:disable Gitlab/RailsLogger @logger.info("#{merge_request_info} - #{message}") end diff --git a/app/services/merge_requests/merge_to_ref_service.rb b/app/services/merge_requests/merge_to_ref_service.rb index 0ea50a5dbf5..37b5805ae7e 100644 --- a/app/services/merge_requests/merge_to_ref_service.rb +++ b/app/services/merge_requests/merge_to_ref_service.rb @@ -16,7 +16,7 @@ module MergeRequests def execute(merge_request) @merge_request = merge_request - validate! + error_check! commit_id = commit @@ -39,21 +39,9 @@ module MergeRequests merge_request.diff_head_sha end - def validate! - error_check! - end - + override :error_check! def error_check! - super - - error = - if !hooks_validation_pass?(merge_request) - hooks_validation_error(merge_request) - elsif source.blank? - 'No source for merge' - end - - raise_error(error) if error + check_source end ## diff --git a/app/services/projects/after_import_service.rb b/app/services/projects/after_import_service.rb index bbdde4408d2..e30da0f26df 100644 --- a/app/services/projects/after_import_service.rb +++ b/app/services/projects/after_import_service.rb @@ -13,7 +13,7 @@ module Projects repository.delete_all_refs_except(RESERVED_REF_PREFIXES) end rescue Projects::HousekeepingService::LeaseTaken => e - Rails.logger.info( + Rails.logger.info( # rubocop:disable Gitlab/RailsLogger "Could not perform housekeeping for project #{@project.full_path} (#{@project.id}): #{e}") end diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 9f335cceb67..89dc4375c63 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -151,7 +151,7 @@ module Projects log_message = message.dup log_message << " Project ID: #{@project.id}" if @project&.id - Rails.logger.error(log_message) + Rails.logger.error(log_message) # rubocop:disable Gitlab/RailsLogger if @project && @project.persisted? && @project.import_state @project.import_state.mark_as_failed(message) diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index d8e670e40ce..b805a7f1211 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -18,7 +18,7 @@ module Projects schedule_stale_repos_removal job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params) - Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}") + Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}") # rubocop:disable Gitlab/RailsLogger end def execute diff --git a/app/services/projects/hashed_storage/migrate_attachments_service.rb b/app/services/projects/hashed_storage/migrate_attachments_service.rb index 3d0b8f58612..affe6e5668d 100644 --- a/app/services/projects/hashed_storage/migrate_attachments_service.rb +++ b/app/services/projects/hashed_storage/migrate_attachments_service.rb @@ -5,7 +5,7 @@ module Projects class MigrateAttachmentsService < BaseAttachmentService def initialize(project, old_disk_path, logger: nil) @project = project - @logger = logger || Rails.logger + @logger = logger || Rails.logger # rubocop:disable Gitlab/RailsLogger @old_disk_path = old_disk_path @skipped = false end diff --git a/app/services/projects/hashed_storage/rollback_attachments_service.rb b/app/services/projects/hashed_storage/rollback_attachments_service.rb index 5c6b92f965c..fb09eaa4586 100644 --- a/app/services/projects/hashed_storage/rollback_attachments_service.rb +++ b/app/services/projects/hashed_storage/rollback_attachments_service.rb @@ -5,7 +5,7 @@ module Projects class RollbackAttachmentsService < BaseAttachmentService def initialize(project, logger: nil) @project = project - @logger = logger || Rails.logger + @logger = logger || Rails.logger # rubocop:disable Gitlab/RailsLogger @old_disk_path = project.disk_path end diff --git a/app/services/projects/hashed_storage/rollback_service.rb b/app/services/projects/hashed_storage/rollback_service.rb index 25767f5de5e..ee41aae64a5 100644 --- a/app/services/projects/hashed_storage/rollback_service.rb +++ b/app/services/projects/hashed_storage/rollback_service.rb @@ -8,7 +8,7 @@ module Projects def initialize(project, old_disk_path, logger: nil) @project = project @old_disk_path = old_disk_path - @logger = logger || Rails.logger + @logger = logger || Rails.logger # rubocop:disable Gitlab/RailsLogger end def execute diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb index e3491282a8a..9c6d7ef41f6 100644 --- a/app/services/projects/import_export/export_service.rb +++ b/app/services/projects/import_export/export_service.rb @@ -62,7 +62,7 @@ module Projects end def cleanup_and_notify_error - Rails.logger.error("Import/Export - Project #{project.name} with ID: #{project.id} export error - #{@shared.errors.join(', ')}") + Rails.logger.error("Import/Export - Project #{project.name} with ID: #{project.id} export error - #{@shared.errors.join(', ')}") # rubocop:disable Gitlab/RailsLogger FileUtils.rm_rf(@shared.export_path) @@ -76,7 +76,7 @@ module Projects end def notify_success - Rails.logger.info("Import/Export - Project #{project.name} with ID: #{project.id} successfully exported") + Rails.logger.info("Import/Export - Project #{project.name} with ID: #{project.id} successfully exported") # rubocop:disable Gitlab/RailsLogger end def notify_error diff --git a/app/services/projects/propagate_service_template.rb b/app/services/projects/propagate_service_template.rb index a25c985585b..64f9b611c40 100644 --- a/app/services/projects/propagate_service_template.rb +++ b/app/services/projects/propagate_service_template.rb @@ -15,7 +15,7 @@ module Projects def propagate return unless @template.active? - Rails.logger.info("Propagating services for template #{@template.id}") + Rails.logger.info("Propagating services for template #{@template.id}") # rubocop:disable Gitlab/RailsLogger propagate_projects_with_template end diff --git a/app/services/projects/update_statistics_service.rb b/app/services/projects/update_statistics_service.rb index 28677a398f3..cc6ffa9eafc 100644 --- a/app/services/projects/update_statistics_service.rb +++ b/app/services/projects/update_statistics_service.rb @@ -5,7 +5,7 @@ module Projects def execute return unless project - Rails.logger.info("Updating statistics for project #{project.id}") + Rails.logger.info("Updating statistics for project #{project.id}") # rubocop:disable Gitlab/RailsLogger project.statistics.refresh!(only: statistics.map(&:to_sym)) end diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb index 62222d3fd2a..4f10f220298 100644 --- a/app/services/submit_usage_ping_service.rb +++ b/app/services/submit_usage_ping_service.rb @@ -28,7 +28,7 @@ class SubmitUsagePingService true rescue Gitlab::HTTP::Error => e - Rails.logger.info "Unable to contact GitLab, Inc.: #{e}" + Rails.logger.info "Unable to contact GitLab, Inc.: #{e}" # rubocop:disable Gitlab/RailsLogger false end diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb index 1fee8bfcd31..6d675c026bb 100644 --- a/app/services/web_hook_service.rb +++ b/app/services/web_hook_service.rb @@ -53,7 +53,7 @@ class WebHookService error_message: e.to_s ) - Rails.logger.error("WebHook Error => #{e}") + Rails.logger.error("WebHook Error => #{e}") # rubocop:disable Gitlab/RailsLogger { status: :error, diff --git a/app/uploaders/file_mover.rb b/app/uploaders/file_mover.rb index 12be1e2bb22..7c7953c8a0e 100644 --- a/app/uploaders/file_mover.rb +++ b/app/uploaders/file_mover.rb @@ -98,7 +98,7 @@ class FileMover end def revert - Rails.logger.warn("Markdown not updated, file move reverted for #{to_model}") + Rails.logger.warn("Markdown not updated, file move reverted for #{to_model}") # rubocop:disable Gitlab/RailsLogger if temp_file_uploader.file_storage? FileUtils.move(file_path, temp_file_path) diff --git a/app/validators/namespace_name_validator.rb b/app/validators/namespace_name_validator.rb deleted file mode 100644 index fb1c241037c..00000000000 --- a/app/validators/namespace_name_validator.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -# NamespaceNameValidator -# -# Custom validator for GitLab namespace name strings. -class NamespaceNameValidator < ActiveModel::EachValidator - def validate_each(record, attribute, value) - unless value =~ Gitlab::Regex.namespace_name_regex - record.errors.add(attribute, Gitlab::Regex.namespace_name_regex_message) - end - end -end diff --git a/app/views/admin/application_settings/_pages.html.haml b/app/views/admin/application_settings/_pages.html.haml index d7d709ffd62..5b8c1e4d54f 100644 --- a/app/views/admin/application_settings/_pages.html.haml +++ b/app/views/admin/application_settings/_pages.html.haml @@ -15,22 +15,21 @@ .form-text.text-muted = _("Domain verification is an essential security measure for public GitLab sites. Users are required to demonstrate they control a domain before it is enabled") = link_to icon('question-circle'), help_page_path('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record') - - if Feature.enabled?(:pages_auto_ssl) - %h5 - = _("Configure Let's Encrypt") - %p - - lets_encrypt_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: "https://letsencrypt.org/" } - = _("%{lets_encrypt_link_start}Let's Encrypt%{lets_encrypt_link_end} is a free, automated, and open certificate authority (CA), that give digital certificates in order to enable HTTPS (SSL/TLS) for websites.").html_safe % { lets_encrypt_link_start: lets_encrypt_link_start, lets_encrypt_link_end: '</a>'.html_safe } - .form-group - = f.label :lets_encrypt_notification_email, _("Email"), class: 'label-bold' - = f.text_field :lets_encrypt_notification_email, class: 'form-control' - .form-text.text-muted - = _("A Let's Encrypt account will be configured for this GitLab installation using your email address. You will receive emails to warn of expiring certificates.") - .form-group - .form-check - = f.check_box :lets_encrypt_terms_of_service_accepted, class: 'form-check-input' - = f.label :lets_encrypt_terms_of_service_accepted, class: 'form-check-label' do - - terms_of_service_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: lets_encrypt_terms_of_service_admin_application_settings_path } - = _("I have read and agree to the Let's Encrypt %{link_start}Terms of Service%{link_end}").html_safe % { link_start: terms_of_service_link_start, link_end: '</a>'.html_safe } + %h5 + = _("Configure Let's Encrypt") + %p + - lets_encrypt_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: "https://letsencrypt.org/" } + = _("%{lets_encrypt_link_start}Let's Encrypt%{lets_encrypt_link_end} is a free, automated, and open certificate authority (CA), that give digital certificates in order to enable HTTPS (SSL/TLS) for websites.").html_safe % { lets_encrypt_link_start: lets_encrypt_link_start, lets_encrypt_link_end: '</a>'.html_safe } + .form-group + = f.label :lets_encrypt_notification_email, _("Email"), class: 'label-bold' + = f.text_field :lets_encrypt_notification_email, class: 'form-control' + .form-text.text-muted + = _("A Let's Encrypt account will be configured for this GitLab installation using your email address. You will receive emails to warn of expiring certificates.") + .form-group + .form-check + = f.check_box :lets_encrypt_terms_of_service_accepted, class: 'form-check-input' + = f.label :lets_encrypt_terms_of_service_accepted, class: 'form-check-label' do + - terms_of_service_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: lets_encrypt_terms_of_service_admin_application_settings_path } + = _("I have read and agree to the Let's Encrypt %{link_start}Terms of Service%{link_end}").html_safe % { link_start: terms_of_service_link_start, link_end: '</a>'.html_safe } = f.submit _('Save changes'), class: "btn btn-success" diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index f524d35d79e..98230684d56 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -43,11 +43,7 @@ = render_if_exists 'admin/namespace_plan_info', namespace: @group %li - %span.light= _('Storage:') - %strong= storage_counter(@group.storage_size) - ( - = storage_counters_details(@group) - ) + = render 'shared/storage_counter_statistics', storage_size: @group.storage_size, storage_details: @group %li %span.light= _('Group Git LFS status:') diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index e23accc1ea9..0fae8060b32 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -73,11 +73,7 @@ = @project.repository.relative_path %li - %span.light= _('Storage:') - %strong= storage_counter(@project.statistics&.storage_size) - - if @project.statistics - = surround '(', ')' do - = storage_counters_details(@project.statistics) + = render 'shared/storage_counter_statistics', storage_size: @project.statistics&.storage_size, storage_details: @project.statistics %li %span.light last commit: diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 5c6131db37d..a988f746ced 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -139,7 +139,7 @@ %strong = link_to @user.created_by.name, [:admin, @user.created_by] - = render_if_exists partial: "namespaces/shared_runner_status", locals: { namespace: @user.namespace } + = render_if_exists 'namespaces/shared_runner_status', namespace: @user.namespace .col-md-6 - unless @user == current_user diff --git a/app/views/ci/status/_icon.html.haml b/app/views/ci/status/_icon.html.haml index f38bdb2e5ed..1249b98221f 100644 --- a/app/views/ci/status/_icon.html.haml +++ b/app/views/ci/status/_icon.html.haml @@ -1,9 +1,10 @@ -- status = local_assigns.fetch(:status) -- size = local_assigns.fetch(:size, 16) -- type = local_assigns.fetch(:type, 'pipeline') -- tooltip_placement = local_assigns.fetch(:tooltip_placement, "left") -- path = local_assigns.fetch(:path, status.has_details? ? status.details_path : nil) -- css_classes = "ci-status-link ci-status-icon ci-status-icon-#{status.group} has-tooltip" +- status = local_assigns.fetch(:status) +- size = local_assigns.fetch(:size, 16) +- type = local_assigns.fetch(:type, 'pipeline') +- tooltip_placement = local_assigns.fetch(:tooltip_placement, "left") +- path = local_assigns.fetch(:path, status.has_details? ? status.details_path : nil) +- option_css_classes = local_assigns.fetch(:option_css_classes, '') +- css_classes = "ci-status-link ci-status-icon ci-status-icon-#{status.group} has-tooltip #{option_css_classes}" - title = s_("PipelineStatusTooltip|Pipeline: %{ci_status}") % {ci_status: status.label} - if type == 'commit' - title = s_("PipelineStatusTooltip|Commit: %{ci_status}") % {ci_status: status.label} diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml index 37ba2143eba..b9be6028b72 100644 --- a/app/views/dashboard/milestones/index.html.haml +++ b/app/views/dashboard/milestones/index.html.haml @@ -8,7 +8,7 @@ - if current_user .page-title-controls = render 'shared/new_project_item_select', - path: 'milestones/new', label: 'New milestone', + path: '-/milestones/new', label: 'New milestone', include_groups: true, type: :milestones .top-area diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index 7dacd0b1d72..2f10f08c839 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -1,10 +1,10 @@ = form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive'}) do |f| .form-group = f.label "Username or email", for: "user_login", class: 'label-bold' - = f.text_field :login, class: "form-control top qa-login-field", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required." + = f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required.", data: { qa_selector: 'login_field' } .form-group = f.label :password, class: 'label-bold' - = f.password_field :password, class: "form-control bottom qa-password-field", required: true, title: "This field is required." + = f.password_field :password, class: "form-control bottom", required: true, title: "This field is required.", data: { qa_selector: 'password_field' } - if devise_mapping.rememberable? .remember-me %label{ for: "user_remember_me" } @@ -17,4 +17,4 @@ = recaptcha_tags .submit-container.move-submit-down - = f.submit "Sign in", class: "btn btn-success qa-sign-in-button" + = f.submit "Sign in", class: "btn btn-success", data: { qa_selector: 'sign_in_button' } diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 91d17cfd745..f05e269553a 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,3 +1,5 @@ +- @can_bulk_update = can?(current_user, :admin_issue, @group) + - page_title "Issues" = content_for :meta_tags do = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@group.name} issues") @@ -9,8 +11,15 @@ = render 'shared/issuable/nav', type: :issues .nav-controls = render 'shared/issuable/feed_buttons' + + - if @can_bulk_update + = render_if_exists 'shared/issuable/bulk_update_button' + = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", type: :issues, with_feature_enabled: 'issues', with_shared: false, include_projects_in_subgroups: true = render 'shared/issuable/search_bar', type: :issues + - if @can_bulk_update + = render_if_exists 'shared/issuable/group_bulk_update_sidebar', group: @group, type: :issues + = render 'shared/issues' diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 496ec3c78b0..a5f57f5893c 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -1,5 +1,5 @@ - if @group && @group.persisted? && @group.path - - group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) } + - group_data_attrs = { group_path: j(@group.path), name: j(@group.name), issues_path: issues_group_path(@group), mr_path: merge_requests_group_path(@group) } - if @project && @project.persisted? - project_data_attrs = { project_path: j(@project.path), name: j(@project.name), issues_path: project_issues_path(@project), mr_path: project_merge_requests_path(@project), issues_disabled: !@project.issues_enabled? } .search.search-form{ data: { track_label: "navbar_search", track_event: "activate_form_input" } } diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index ff3410f6268..e9a4a068599 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -1,7 +1,7 @@ !!! 5 %html.devise-layout-html{ class: system_message_class } = render "layouts/head" - %body.ui-indigo.login-page.application.navless.qa-login-page{ data: { page: body_data_page } } + %body.ui-indigo.login-page.application.navless{ data: { page: body_data_page, qa_selector: 'login_page' } } = header_message .page-wrap = render "layouts/header/empty" diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml index 4f3e4031fe3..45fc39dbbdb 100644 --- a/app/views/layouts/header/_current_user_dropdown.html.haml +++ b/app/views/layouts/header/_current_user_dropdown.html.haml @@ -24,4 +24,4 @@ - if current_user_menu?(:sign_out) %li.divider %li - = link_to _("Sign out"), destroy_user_session_path, class: "sign-out-link" + = link_to _("Sign out"), destroy_user_session_path, class: "sign-out-link qa-sign-out-link" diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index f9ee6f42e23..fc2dea25c77 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -5,7 +5,7 @@ - else - search_path_url = search_path -%header.navbar.navbar-gitlab.qa-navbar.navbar-expand-sm.js-navbar +%header.navbar.navbar-gitlab.navbar-expand-sm.js-navbar{ data: { qa_selector: 'navbar' } } %a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content .container-fluid .header-content @@ -64,7 +64,7 @@ .dropdown-menu.dropdown-menu-right = render 'layouts/header/help_dropdown' - if header_link?(:user_dropdown) - %li.nav-item.header-user.dropdown{ data: { track_label: "profile_dropdown", track_event: "click_dropdown" } } + %li.nav-item.header-user.dropdown{ data: { track_label: "profile_dropdown", track_event: "click_dropdown", qa_selector: 'user_menu' } } = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do = image_tag avatar_icon_for_user(current_user, 23), width: 23, height: 23, class: "header-user-avatar qa-user-avatar" = sprite_icon('angle-down', css_class: 'caret-down') diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml index e423631ec99..7541737f79c 100644 --- a/app/views/projects/_new_project_fields.html.haml +++ b/app/views/projects/_new_project_fields.html.haml @@ -36,17 +36,17 @@ = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", required: true - if current_user.can_create_group? .form-text.text-muted - Want to house several dependent projects under the same namespace? - = link_to "Create a group.", new_group_path + - link_start_group_path = '<a href="%{path}">' % { path: new_group_path } + - project_tip = s_('ProjectsNew|Want to house several dependent projects under the same namespace? %{link_start}Create a group.%{link_end}') % { link_start: link_start_group_path, link_end: '</a>' } + = project_tip.html_safe .form-group = f.label :description, class: 'label-bold' do - Project description - %span (optional) - = f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250, data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_description", track_value: "" } + = s_('ProjectsNew|Project description %{tag_start}(optional)%{tag_end}').html_safe % { tag_start: '<span>'.html_safe, tag_end: '</span>'.html_safe } + = f.text_area :description, placeholder: s_('ProjectsNew|Description format'), class: "form-control", rows: 3, maxlength: 250, data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_description", track_value: "" } = f.label :visibility_level, class: 'label-bold' do - Visibility Level + = s_('ProjectsNew|Visibility Level') = link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer' = render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false @@ -57,9 +57,9 @@ = check_box_tag 'project[initialize_with_readme]', '1', false, class: 'form-check-input qa-initialize-with-readme-checkbox', data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "init_with_readme" } = label_tag 'project[initialize_with_readme]', class: 'form-check-label' do .option-title - %strong Initialize repository with a README + %strong= s_('ProjectsNew|Initialize repository with a README') .option-description - Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository. + = s_('ProjectsNew|Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository.') -= f.submit 'Create project', class: "btn btn-success project-submit", data: { track_label: "#{track_label}", track_event: "click_button", track_property: "create_project", track_value: "" } -= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "cancel" } += f.submit _('Create project'), class: "btn btn-success project-submit", data: { track_label: "#{track_label}", track_event: "click_button", track_property: "create_project", track_value: "" } += link_to _('Cancel'), dashboard_projects_path, class: 'btn btn-cancel', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "cancel" } diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml index c13a47b0b09..6100fd3ad37 100644 --- a/app/views/projects/environments/show.html.haml +++ b/app/views/projects/environments/show.html.haml @@ -3,6 +3,9 @@ - breadcrumb_title @environment.name - page_title _("Environments") +- content_for :page_specific_javascripts do + = stylesheet_link_tag 'page_bundles/xterm' + %div{ class: container_class } - if can?(current_user, :stop_environment, @environment) #stop-environment-modal.modal.fade{ tabindex: -1 } diff --git a/app/views/projects/issues/import_csv/_button.html.haml b/app/views/projects/issues/import_csv/_button.html.haml index acc2c50294f..8442a53ed61 100644 --- a/app/views/projects/issues/import_csv/_button.html.haml +++ b/app/views/projects/issues/import_csv/_button.html.haml @@ -1,6 +1,6 @@ - type = local_assigns.fetch(:type, :icon) -%button.csv-import-button.btn{ title: _('Import CSV'), class: ('has-tooltip' if type == :icon), +%button.csv-import-button.btn.btn-svg{ title: _('Import CSV'), class: ('has-tooltip' if type == :icon), data: { toggle: 'modal', target: '.issues-import-modal' } } - if type == :icon = sprite_icon('upload') diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index eb516684e52..dee3931ff04 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -38,7 +38,7 @@ = link_to_label(label, type: :merge_request, css_class: 'label-link') .issuable-meta - %ul.controls + %ul.controls.d-flex.align-items-end - if merge_request.merged? %li.issuable-status.d-none.d-sm-inline-block MERGED @@ -47,14 +47,14 @@ = icon('ban') CLOSED - if can?(current_user, :read_pipeline, merge_request.head_pipeline) - %li.issuable-pipeline-status.d-none.d-sm-inline-block - = render 'ci/status/icon', status: merge_request.head_pipeline.detailed_status(current_user) + %li.issuable-pipeline-status.d-none.d-sm-flex + = render 'ci/status/icon', status: merge_request.head_pipeline.detailed_status(current_user), option_css_classes: 'd-flex' - if merge_request.open? && merge_request.broken? - %li.issuable-pipeline-broken.d-none.d-sm-inline-block + %li.issuable-pipeline-broken.d-none.d-sm-flex = link_to merge_request_path(merge_request), class: "has-tooltip", title: _('Cannot be merged automatically') do = icon('exclamation-triangle') - if merge_request.assignees.any? - %li + %li.d-flex = render 'shared/issuable/assignees', project: merge_request.project, issuable: merge_request = render_if_exists 'projects/merge_requests/approvals_count', merge_request: merge_request diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 1cfe302fdc7..33de0aa153b 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,7 +1,7 @@ - @hide_breadcrumbs = true - @hide_top_links = true -- page_title 'New Project' -- header_title "Projects", dashboard_projects_path +- page_title _('New Project') +- header_title _("Projects"), dashboard_projects_path - active_tab = local_assigns.fetch(:active_tab, 'blank') .project-edit-container.prepend-top-default @@ -33,16 +33,16 @@ %ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' } %li.nav-item{ role: 'presentation' } %a.nav-link.active{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab', track_label: 'blank_project', track_event: "click_tab" }, role: 'tab' } - %span.d-none.d-sm-block Blank project - %span.d-block.d-sm-none Blank + %span.d-none.d-sm-block= s_('ProjectsNew|Blank project') + %span.d-block.d-sm-none= s_('ProjectsNew|Blank') %li.nav-item{ role: 'presentation' } %a.nav-link{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab', track_label: 'create_from_template', track_event: "click_tab" }, role: 'tab' } - %span.d-none.d-sm-block.qa-project-create-from-template-tab Create from template - %span.d-block.d-sm-none Template + %span.d-none.d-sm-block.qa-project-create-from-template-tab= s_('ProjectsNew|Create from template') + %span.d-block.d-sm-none= s_('ProjectsNew|Template') %li.nav-item{ role: 'presentation' } %a.nav-link{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab', track_label: 'import_project', track_event: "click_tab" }, role: 'tab' } - %span.d-none.d-sm-block Import project - %span.d-block.d-sm-none Import + %span.d-none.d-sm-block= s_('ProjectsNew|Import project') + %span.d-block.d-sm-none= s_('ProjectsNew|Import') = render_if_exists 'projects/new_ci_cd_only_project_tab', active_tab: active_tab .tab-content.gitlab-tab-content @@ -67,8 +67,8 @@ = render 'import_project_pane', active_tab: active_tab - else .nothing-here-block - %h4 No import options available - %p Contact an administrator to enable options for importing your project. + %h4= s_('ProjectsNew|No import options available') + %p= s_('ProjectsNew|Contact an administrator to enable options for importing your project.') = render_if_exists 'projects/new_ci_cd_only_project_pane', active_tab: active_tab @@ -76,5 +76,6 @@ .center %h2 %i.fa.fa-spinner.fa-spin - Creating project & repository. - %p Please wait a moment, this page will automatically refresh when ready. + = s_('ProjectsNew|Creating project & repository.') + %p + = s_('ProjectsNew|Please wait a moment, this page will automatically refresh when ready.') diff --git a/app/views/projects/pages_domains/_form.html.haml b/app/views/projects/pages_domains/_form.html.haml index 5b657966909..0e5c65a2f72 100644 --- a/app/views/projects/pages_domains/_form.html.haml +++ b/app/views/projects/pages_domains/_form.html.haml @@ -11,7 +11,7 @@ - if Gitlab.config.pages.external_https - - auto_ssl_available = ::Gitlab::LetsEncrypt.enabled?(@domain) + - auto_ssl_available = ::Gitlab::LetsEncrypt.enabled? - auto_ssl_enabled = @domain.auto_ssl_enabled? - auto_ssl_available_and_enabled = auto_ssl_available && auto_ssl_enabled diff --git a/app/views/projects/pages_domains/_helper_text.html.haml b/app/views/projects/pages_domains/_helper_text.html.haml index 5a79fefabfc..4e3daa597e0 100644 --- a/app/views/projects/pages_domains/_helper_text.html.haml +++ b/app/views/projects/pages_domains/_helper_text.html.haml @@ -2,8 +2,4 @@ - docs_link_start = "<a href=\"%{docs_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { docs_link_url: docs_link_url } - docs_link_end = "</a>".html_safe --# Hiding behind a feature flag to avoid any changes to this feature's implemention --# when the :pages_auto_ssl feature flag is disabled. This check should be removed --# once the :pages_auto_ssl feature flag is removed. -- if Feature.enabled?(:pages_auto_ssl) - %p= _("Learn more about adding certificates to your project by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}.").html_safe % { docs_link_url: docs_link_url, docs_link_start: docs_link_start, docs_link_end: docs_link_end } +%p= _("Learn more about adding certificates to your project by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}.").html_safe % { docs_link_url: docs_link_url, docs_link_start: docs_link_start, docs_link_end: docs_link_end } diff --git a/app/views/projects/settings/operations/_external_dashboard.html.haml b/app/views/projects/settings/operations/_external_dashboard.html.haml index a124283921d..08d50a336fd 100644 --- a/app/views/projects/settings/operations/_external_dashboard.html.haml +++ b/app/views/projects/settings/operations/_external_dashboard.html.haml @@ -1,3 +1,3 @@ .js-operation-settings{ data: { operations_settings_endpoint: project_settings_operations_path(@project), external_dashboard: { url: metrics_external_dashboard_url, - help_page_path: help_page_path('user/project/operations/link_to_external_dashboard') } } } + help_page_path: help_page_path('user/project/operations/linking_to_an_external_dashboard') } } } diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index 889a13339fd..cb459b031fc 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -1,4 +1,4 @@ -.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path } +.tree-content-holder.js-tree-content{ data: tree_content_data(@logs_path, @project, @path) } .table-holder.bordered-box %table.table#tree-slider{ class: "table_#{@hex_path} tree-table qa-file-tree" } %thead diff --git a/app/views/shared/_storage_counter_statistics.html.haml b/app/views/shared/_storage_counter_statistics.html.haml new file mode 100644 index 00000000000..99b2323ca82 --- /dev/null +++ b/app/views/shared/_storage_counter_statistics.html.haml @@ -0,0 +1,4 @@ +%span.light= _('Storage:') +%strong= storage_counter(storage_size) +- if storage_details + (#{storage_counters_details(storage_details)}) diff --git a/app/views/shared/issuable/_feed_buttons.html.haml b/app/views/shared/issuable/_feed_buttons.html.haml index 83f60fa6fe2..c9506a3295c 100644 --- a/app/views/shared/issuable/_feed_buttons.html.haml +++ b/app/views/shared/issuable/_feed_buttons.html.haml @@ -1,4 +1,4 @@ -= link_to safe_params.merge(rss_url_options), class: 'btn has-tooltip', data: { container: 'body' }, title: _('Subscribe to RSS feed') do - = icon('rss') -= link_to safe_params.merge(calendar_url_options), class: 'btn has-tooltip', data: { container: 'body' }, title: _('Subscribe to calendar') do - = custom_icon('icon_calendar') += link_to safe_params.merge(rss_url_options), class: 'btn btn-svg has-tooltip js-rss-button', data: { container: 'body' }, title: _('Subscribe to RSS feed') do + = sprite_icon('rss') += link_to safe_params.merge(calendar_url_options), class: 'btn btn-svg has-tooltip', data: { container: 'body' }, title: _('Subscribe to calendar') do + = sprite_icon('calendar') diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 1bd56e064d5..214e87052da 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -17,8 +17,7 @@ = render 'shared/issuable/form/template_selector', issuable: issuable = render 'shared/issuable/form/title', issuable: issuable, form: form, has_wip_commits: commits && commits.detect(&:work_in_progress?) -- if Gitlab::Graphql.enabled? - #js-suggestions{ data: { project_path: @project.full_path } } +#js-suggestions{ data: { project_path: @project.full_path } } = render 'shared/form_elements/description', model: issuable, form: form, project: project diff --git a/app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml b/app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml index 5336159e762..60dc893d9f9 100644 --- a/app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml +++ b/app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml @@ -1,4 +1,4 @@ -= form.label :assignee_id, "Assignee", class: "col-form-label #{has_due_date ? "col-lg-4" : "col-sm-2"}" += form.label :assignee_id, "Assignee", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}" .col-sm-10{ class: ("col-md-8" if has_due_date) } .issuable-form-select-holder.selectbox - issuable.assignees.each do |assignee| diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 90fb067e75d..71bd9320593 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -58,7 +58,7 @@ .description.d-none.d-sm-block.append-right-default = markdown_field(project, :description) - .controls.d-flex.flex-sm-column.align-items-center.align-items-sm-end.flex-wrap.flex-shrink-0{ class: css_controls_class } + .controls.d-flex.flex-sm-column.align-items-center.align-items-sm-end.flex-wrap.flex-shrink-0.text-secondary{ class: css_controls_class } .icon-container.d-flex.align-items-center - if project.archived %span.d-flex.icon-wrapper.badge.badge-warning archived diff --git a/app/workers/concerns/application_worker.rb b/app/workers/concerns/application_worker.rb index 25c3a945077..2b36ccb8304 100644 --- a/app/workers/concerns/application_worker.rb +++ b/app/workers/concerns/application_worker.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'sidekiq/api' + Sidekiq::Worker.extend ActiveSupport::Concern module ApplicationWorker @@ -44,6 +46,10 @@ module ApplicationWorker get_sidekiq_options['queue'].to_s end + def queue_size + Sidekiq::Queue.new(queue).size + end + def bulk_perform_async(args_list) Sidekiq::Client.push_bulk('class' => self, 'args' => args_list) end diff --git a/app/workers/concerns/new_issuable.rb b/app/workers/concerns/new_issuable.rb index a89451a4475..22ba7c97309 100644 --- a/app/workers/concerns/new_issuable.rb +++ b/app/workers/concerns/new_issuable.rb @@ -27,6 +27,6 @@ module NewIssuable # rubocop: enable CodeReuse/ActiveRecord def log_error(record_class, record_id) - Rails.logger.error("#{self.class}: couldn't find #{record_class} with ID=#{record_id}, skipping job") + Rails.logger.error("#{self.class}: couldn't find #{record_class} with ID=#{record_id}, skipping job") # rubocop:disable Gitlab/RailsLogger end end diff --git a/app/workers/create_gpg_signature_worker.rb b/app/workers/create_gpg_signature_worker.rb index 7fac7822cf7..e3fb5d479ae 100644 --- a/app/workers/create_gpg_signature_worker.rb +++ b/app/workers/create_gpg_signature_worker.rb @@ -22,7 +22,7 @@ class CreateGpgSignatureWorker commits.each do |commit| Gitlab::Gpg::Commit.new(commit).signature rescue => e - Rails.logger.error("Failed to create signature for commit #{commit.id}. Error: #{e.message}") + Rails.logger.error("Failed to create signature for commit #{commit.id}. Error: #{e.message}") # rubocop:disable Gitlab/RailsLogger end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/workers/delete_user_worker.rb b/app/workers/delete_user_worker.rb index 4d0295f8d2e..efa8794b214 100644 --- a/app/workers/delete_user_worker.rb +++ b/app/workers/delete_user_worker.rb @@ -9,6 +9,6 @@ class DeleteUserWorker Users::DestroyService.new(current_user).execute(delete_user, options.symbolize_keys) rescue Gitlab::Access::AccessDeniedError => e - Rails.logger.warn("User could not be destroyed: #{e}") + Rails.logger.warn("User could not be destroyed: #{e}") # rubocop:disable Gitlab/RailsLogger end end diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb index c4bcda2da16..e70bf17d5a9 100644 --- a/app/workers/email_receiver_worker.rb +++ b/app/workers/email_receiver_worker.rb @@ -16,7 +16,7 @@ class EmailReceiverWorker private def handle_failure(raw, error) - Rails.logger.warn("Email can not be processed: #{error}\n\n#{raw}") + Rails.logger.warn("Email can not be processed: #{error}\n\n#{raw}") # rubocop:disable Gitlab/RailsLogger return unless raw.present? diff --git a/app/workers/expire_build_artifacts_worker.rb b/app/workers/expire_build_artifacts_worker.rb index 251e95c68d5..6f0e0fd33f7 100644 --- a/app/workers/expire_build_artifacts_worker.rb +++ b/app/workers/expire_build_artifacts_worker.rb @@ -18,7 +18,7 @@ class ExpireBuildArtifactsWorker # rubocop: disable CodeReuse/ActiveRecord def perform_legacy_artifacts_removal - Rails.logger.info 'Scheduling removal of build artifacts' + Rails.logger.info 'Scheduling removal of build artifacts' # rubocop:disable Gitlab/RailsLogger build_ids = Ci::Build.with_expired_artifacts.pluck(:id) build_ids = build_ids.map { |build_id| [build_id] } diff --git a/app/workers/expire_build_instance_artifacts_worker.rb b/app/workers/expire_build_instance_artifacts_worker.rb index 94426dcf921..71e61dcb878 100644 --- a/app/workers/expire_build_instance_artifacts_worker.rb +++ b/app/workers/expire_build_instance_artifacts_worker.rb @@ -12,7 +12,7 @@ class ExpireBuildInstanceArtifactsWorker return unless build&.project && !build.project.pending_delete - Rails.logger.info "Removing artifacts for build #{build.id}..." + Rails.logger.info "Removing artifacts for build #{build.id}..." # rubocop:disable Gitlab/RailsLogger build.erase_erasable_artifacts! end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/workers/new_note_worker.rb b/app/workers/new_note_worker.rb index 98f9f45e608..1d1ea926c21 100644 --- a/app/workers/new_note_worker.rb +++ b/app/workers/new_note_worker.rb @@ -11,7 +11,7 @@ class NewNoteWorker NotificationService.new.new_note(note) unless skip_notification?(note) Notes::PostProcessService.new(note).execute else - Rails.logger.error("NewNoteWorker: couldn't find note with ID=#{note_id}, skipping job") + Rails.logger.error("NewNoteWorker: couldn't find note with ID=#{note_id}, skipping job") # rubocop:disable Gitlab/RailsLogger end end diff --git a/app/workers/object_storage/migrate_uploads_worker.rb b/app/workers/object_storage/migrate_uploads_worker.rb index 12400d4e025..55ac7cd9b3c 100644 --- a/app/workers/object_storage/migrate_uploads_worker.rb +++ b/app/workers/object_storage/migrate_uploads_worker.rb @@ -37,6 +37,7 @@ module ObjectStorage end end + # rubocop:disable Gitlab/RailsLogger def report!(results) success, failures = results.partition(&:success?) @@ -45,6 +46,7 @@ module ObjectStorage raise MigrationFailures.new(failures.map(&:error)) if failures.any? end + # rubocop:enable Gitlab/RailsLogger def header(success, failures) _("Migrated %{success_count}/%{total_count} files.") % { success_count: success.count, total_count: success.count + failures.count } @@ -98,7 +100,7 @@ module ObjectStorage report!(results) rescue SanityCheckError => e # do not retry: the job is insane - Rails.logger.warn "#{self.class}: Sanity check error (#{e.message})" + Rails.logger.warn "#{self.class}: Sanity check error (#{e.message})" # rubocop:disable Gitlab/RailsLogger end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/workers/pages_domain_ssl_renewal_cron_worker.rb b/app/workers/pages_domain_ssl_renewal_cron_worker.rb index 40c34d29970..e5dde07a648 100644 --- a/app/workers/pages_domain_ssl_renewal_cron_worker.rb +++ b/app/workers/pages_domain_ssl_renewal_cron_worker.rb @@ -5,9 +5,9 @@ class PagesDomainSslRenewalCronWorker include CronjobQueue def perform - PagesDomain.need_auto_ssl_renewal.find_each do |domain| - next unless ::Gitlab::LetsEncrypt.enabled?(domain) + return unless ::Gitlab::LetsEncrypt.enabled? + PagesDomain.need_auto_ssl_renewal.find_each do |domain| PagesDomainSslRenewalWorker.perform_async(domain.id) end end diff --git a/app/workers/pages_domain_ssl_renewal_worker.rb b/app/workers/pages_domain_ssl_renewal_worker.rb index b32458ca777..87fd8059946 100644 --- a/app/workers/pages_domain_ssl_renewal_worker.rb +++ b/app/workers/pages_domain_ssl_renewal_worker.rb @@ -6,7 +6,7 @@ class PagesDomainSslRenewalWorker def perform(domain_id) domain = PagesDomain.find_by_id(domain_id) return unless domain&.enabled? - return unless ::Gitlab::LetsEncrypt.enabled?(domain) + return unless ::Gitlab::LetsEncrypt.enabled? ::PagesDomains::ObtainLetsEncryptCertificateService.new(domain).execute end diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index a9b88a133be..35e9c58eb13 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -35,7 +35,7 @@ class RepositoryForkWorker def start_fork(project) return true if start(project.import_state) - Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while forking.") + Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while forking.") # rubocop:disable Gitlab/RailsLogger false end end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 59691f48a39..dff9c8f50bf 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -36,7 +36,7 @@ class RepositoryImportWorker def start_import return true if start(project.import_state) - Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while importing.") + Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while importing.") # rubocop:disable Gitlab/RailsLogger false end diff --git a/app/workers/repository_update_remote_mirror_worker.rb b/app/workers/repository_update_remote_mirror_worker.rb index c0bae08ba85..03a7ff2cd7a 100644 --- a/app/workers/repository_update_remote_mirror_worker.rb +++ b/app/workers/repository_update_remote_mirror_worker.rb @@ -45,6 +45,6 @@ class RepositoryUpdateRemoteMirrorWorker def fail_remote_mirror(remote_mirror, message) remote_mirror.mark_as_failed(message) - Rails.logger.error(message) + Rails.logger.error(message) # rubocop:disable Gitlab/RailsLogger end end diff --git a/app/workers/run_pipeline_schedule_worker.rb b/app/workers/run_pipeline_schedule_worker.rb index 43e0b9db22f..351850e53cb 100644 --- a/app/workers/run_pipeline_schedule_worker.rb +++ b/app/workers/run_pipeline_schedule_worker.rb @@ -30,6 +30,7 @@ class RunPipelineScheduleWorker private + # rubocop:disable Gitlab/RailsLogger def error(schedule, error) failed_creation_counter.increment @@ -41,6 +42,7 @@ class RunPipelineScheduleWorker issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/41231', extra: { schedule_id: schedule.id }) end + # rubocop:enable Gitlab/RailsLogger def failed_creation_counter @failed_creation_counter ||= diff --git a/app/workers/stuck_ci_jobs_worker.rb b/app/workers/stuck_ci_jobs_worker.rb index 25809f68080..30fba038937 100644 --- a/app/workers/stuck_ci_jobs_worker.rb +++ b/app/workers/stuck_ci_jobs_worker.rb @@ -14,7 +14,7 @@ class StuckCiJobsWorker def perform return unless try_obtain_lease - Rails.logger.info "#{self.class}: Cleaning stuck builds" + Rails.logger.info "#{self.class}: Cleaning stuck builds" # rubocop:disable Gitlab/RailsLogger drop :running, BUILD_RUNNING_OUTDATED_TIMEOUT, 'ci_builds.updated_at < ?', :stuck_or_timeout_failure drop :pending, BUILD_PENDING_OUTDATED_TIMEOUT, 'ci_builds.updated_at < ?', :stuck_or_timeout_failure @@ -66,7 +66,7 @@ class StuckCiJobsWorker # rubocop: enable CodeReuse/ActiveRecord def drop_build(type, build, status, timeout, reason) - Rails.logger.info "#{self.class}: Dropping #{type} build #{build.id} for runner #{build.runner_id} (status: #{status}, timeout: #{timeout}, reason: #{reason})" + Rails.logger.info "#{self.class}: Dropping #{type} build #{build.id} for runner #{build.runner_id} (status: #{status}, timeout: #{timeout}, reason: #{reason})" # rubocop:disable Gitlab/RailsLogger Gitlab::OptimisticLocking.retry_lock(build, 3) do |b| b.drop(reason) end diff --git a/app/workers/stuck_import_jobs_worker.rb b/app/workers/stuck_import_jobs_worker.rb index c8a186ba4ce..a9ff5b22b25 100644 --- a/app/workers/stuck_import_jobs_worker.rb +++ b/app/workers/stuck_import_jobs_worker.rb @@ -38,7 +38,7 @@ class StuckImportJobsWorker completed_import_states = enqueued_import_states_with_jid.where(id: completed_import_state_ids) completed_import_state_jids = completed_import_states.map { |import_state| import_state.jid }.join(', ') - Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_import_state_jids}") + Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_import_state_jids}") # rubocop:disable Gitlab/RailsLogger completed_import_states.each do |import_state| import_state.mark_as_failed(error_message) diff --git a/app/workers/stuck_merge_jobs_worker.rb b/app/workers/stuck_merge_jobs_worker.rb index f34ed6c4844..e840ae47421 100644 --- a/app/workers/stuck_merge_jobs_worker.rb +++ b/app/workers/stuck_merge_jobs_worker.rb @@ -5,7 +5,7 @@ class StuckMergeJobsWorker include CronjobQueue def self.logger - Rails.logger + Rails.logger # rubocop:disable Gitlab/RailsLogger end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/workers/trending_projects_worker.rb b/app/workers/trending_projects_worker.rb index 3297a1fe3d0..55b599ba38f 100644 --- a/app/workers/trending_projects_worker.rb +++ b/app/workers/trending_projects_worker.rb @@ -5,7 +5,7 @@ class TrendingProjectsWorker include CronjobQueue def perform - Rails.logger.info('Refreshing trending projects') + Rails.logger.info('Refreshing trending projects') # rubocop:disable Gitlab/RailsLogger TrendingProject.refresh! end diff --git a/app/workers/update_merge_requests_worker.rb b/app/workers/update_merge_requests_worker.rb index c7213df652a..6c0e472e05a 100644 --- a/app/workers/update_merge_requests_worker.rb +++ b/app/workers/update_merge_requests_worker.rb @@ -27,7 +27,7 @@ class UpdateMergeRequestsWorker "ref=#{ref}" ].join(',') - Rails.logger.info("UpdateMergeRequestsWorker#perform #{args_log}") if time.real > LOG_TIME_THRESHOLD + Rails.logger.info("UpdateMergeRequestsWorker#perform #{args_log}") if time.real > LOG_TIME_THRESHOLD # rubocop:disable Gitlab/RailsLogger end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/workers/upload_checksum_worker.rb b/app/workers/upload_checksum_worker.rb index 2a0536106d7..834dcaa435d 100644 --- a/app/workers/upload_checksum_worker.rb +++ b/app/workers/upload_checksum_worker.rb @@ -8,6 +8,6 @@ class UploadChecksumWorker upload.calculate_checksum! upload.save! rescue ActiveRecord::RecordNotFound - Rails.logger.error("UploadChecksumWorker: couldn't find upload #{upload_id}, skipping") + Rails.logger.error("UploadChecksumWorker: couldn't find upload #{upload_id}, skipping") # rubocop:disable Gitlab/RailsLogger end end diff --git a/bin/bundle b/bin/bundle index 66e9889e8b4..f19acf5b5cc 100755 --- a/bin/bundle +++ b/bin/bundle @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) load Gem.bin_path('bundler', 'bundle') diff --git a/bin/setup b/bin/setup index 883825bc0a2..94fd4d79775 100755 --- a/bin/setup +++ b/bin/setup @@ -1,33 +1,36 @@ #!/usr/bin/env ruby - -require "pathname" +require 'fileutils' +include FileUtils # path to your application root. -APP_ROOT = Pathname.new File.expand_path("../../", __FILE__) +APP_ROOT = File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end -Dir.chdir APP_ROOT do - # This script is a starting point to set up your application. - # Add necessary setup steps to this file: +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') - puts "== Installing dependencies ==" - system! "gem install bundler --conservative" - system("bundle check") || system!("bundle install") + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') # puts "\n== Copying sample files ==" - # unless File.exist?("config/database.yml") - # cp "config/database.yml.sample", "config/database.yml" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' # end puts "\n== Preparing database ==" - system! "bin/rails db:setup" + system! 'bin/rails db:setup' puts "\n== Removing old logs and tempfiles ==" - system! "bin/rails log:clear tmp:clear" + system! 'bin/rails log:clear tmp:clear' puts "\n== Restarting application server ==" - system! "bin/rails restart" + system! 'bin/rails restart' end diff --git a/bin/update b/bin/update index a8e4462f203..58bfaed518c 100755 --- a/bin/update +++ b/bin/update @@ -1,10 +1,9 @@ #!/usr/bin/env ruby -require 'pathname' require 'fileutils' include FileUtils # path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) +APP_ROOT = File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") @@ -18,6 +17,9 @@ chdir APP_ROOT do system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') + puts "\n== Updating database ==" system! 'bin/rails db:migrate' @@ -1,63 +1,21 @@ #!/bin/sh +set -e + cd $(dirname $0)/.. app_root=$(pwd) -# Switch to experimental PUMA configuration -if [ -n "${EXPERIMENTAL_PUMA}" ]; then - exec bin/web_puma "$@" -fi - -unicorn_pidfile="$app_root/tmp/pids/unicorn.pid" -unicorn_config="$app_root/config/unicorn.rb" -unicorn_cmd="bundle exec unicorn_rails -c $unicorn_config -E $RAILS_ENV" - -get_unicorn_pid() -{ - local pid=$(cat $unicorn_pidfile) - if [ -z "$pid" ] ; then - echo "Could not find a PID in $unicorn_pidfile" - exit 1 - fi - unicorn_pid=$pid -} - -start() -{ - exec $unicorn_cmd -D -} - -start_foreground() -{ - exec $unicorn_cmd -} - -stop() -{ - get_unicorn_pid - kill -QUIT $unicorn_pid -} +case "$USE_WEB_SERVER" in + puma|"") # and the "" defines default + exec bin/web_puma "$@" + ;; -reload() -{ - get_unicorn_pid - kill -USR2 $unicorn_pid -} + unicorn) + exec bin/web_unicorn "$@" + ;; -case "$1" in - start) - start - ;; - start_foreground) - start_foreground - ;; - stop) - stop - ;; - reload) - reload - ;; - *) - echo "Usage: RAILS_ENV=your_env $0 {start|stop|reload}" - ;; + *) + echo "Unkown web server used by USE_WEB_SERVER: $USE_WEB_SERVER." + exit 1 + ;; esac diff --git a/bin/web_unicorn b/bin/web_unicorn new file mode 100755 index 00000000000..ecd0bbd10b0 --- /dev/null +++ b/bin/web_unicorn @@ -0,0 +1,58 @@ +#!/bin/sh + +cd $(dirname $0)/.. +app_root=$(pwd) + +unicorn_pidfile="$app_root/tmp/pids/unicorn.pid" +unicorn_config="$app_root/config/unicorn.rb" +unicorn_cmd="bundle exec unicorn_rails -c $unicorn_config -E $RAILS_ENV" + +get_unicorn_pid() +{ + local pid=$(cat $unicorn_pidfile) + if [ -z "$pid" ] ; then + echo "Could not find a PID in $unicorn_pidfile" + exit 1 + fi + unicorn_pid=$pid +} + +start() +{ + exec $unicorn_cmd -D +} + +start_foreground() +{ + exec $unicorn_cmd +} + +stop() +{ + get_unicorn_pid + kill -QUIT $unicorn_pid +} + +reload() +{ + get_unicorn_pid + kill -USR2 $unicorn_pid +} + +case "$1" in + start) + start + ;; + start_foreground) + start_foreground + ;; + stop) + stop + ;; + reload) + reload + ;; + *) + echo "Usage: RAILS_ENV=your_env $0 {start|stop|reload}" + ;; +esac diff --git a/bin/yarn b/bin/yarn new file mode 100755 index 00000000000..460dd565b4a --- /dev/null +++ b/bin/yarn @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path('..', __dir__) +Dir.chdir(APP_ROOT) do + begin + exec "yarnpkg", *ARGV + rescue Errno::ENOENT + $stderr.puts "Yarn executable was not detected in the system." + $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + exit 1 + end +end diff --git a/changelogs/unreleased/12533-shared-runners-warning.yml b/changelogs/unreleased/12533-shared-runners-warning.yml new file mode 100644 index 00000000000..b2b526cc2e4 --- /dev/null +++ b/changelogs/unreleased/12533-shared-runners-warning.yml @@ -0,0 +1,5 @@ +--- +title: Removes EE differences for app/views/admin/users/show.html.haml +merge_request: +author: +type: other diff --git a/changelogs/unreleased/35757-move-issues-in-boards-pderichs.yml b/changelogs/unreleased/35757-move-issues-in-boards-pderichs.yml new file mode 100644 index 00000000000..ae50350f70d --- /dev/null +++ b/changelogs/unreleased/35757-move-issues-in-boards-pderichs.yml @@ -0,0 +1,5 @@ +--- +title: Add endpoint to move multiple issues in boards +merge_request: 30216 +author: +type: added diff --git a/changelogs/unreleased/40379-CJK-search-min-chars.yml b/changelogs/unreleased/40379-CJK-search-min-chars.yml new file mode 100644 index 00000000000..6f6c4df464f --- /dev/null +++ b/changelogs/unreleased/40379-CJK-search-min-chars.yml @@ -0,0 +1,5 @@ +--- +title: Remove minimum character limits for fuzzy searches when using a CTE +merge_request: 29810 +author: +type: fixed diff --git a/changelogs/unreleased/51952-redirect-to-webide-in-fork.yml b/changelogs/unreleased/51952-redirect-to-webide-in-fork.yml new file mode 100644 index 00000000000..ada0efca968 --- /dev/null +++ b/changelogs/unreleased/51952-redirect-to-webide-in-fork.yml @@ -0,0 +1,5 @@ +--- +title: Open WebIDE in fork when user doesn't have access +merge_request: 30642 +author: +type: changed diff --git a/changelogs/unreleased/55623-group-cluster-apis.yml b/changelogs/unreleased/55623-group-cluster-apis.yml new file mode 100644 index 00000000000..fe987ef4a82 --- /dev/null +++ b/changelogs/unreleased/55623-group-cluster-apis.yml @@ -0,0 +1,5 @@ +--- +title: Add API for CRUD group clusters +merge_request: 30213 +author: +type: added diff --git a/changelogs/unreleased/60856-deleting-binary-file.yml b/changelogs/unreleased/60856-deleting-binary-file.yml new file mode 100644 index 00000000000..9b587ed591b --- /dev/null +++ b/changelogs/unreleased/60856-deleting-binary-file.yml @@ -0,0 +1,5 @@ +--- +title: Removing an image should not output binary data +merge_request: 30314 +author: +type: fixed diff --git a/changelogs/unreleased/61145-fix-button-dimensions.yml b/changelogs/unreleased/61145-fix-button-dimensions.yml new file mode 100644 index 00000000000..8f209ceaa8e --- /dev/null +++ b/changelogs/unreleased/61145-fix-button-dimensions.yml @@ -0,0 +1,5 @@ +--- +title: Updating button dimensions according to design spec +merge_request: 28545 +author: +type: fixed diff --git a/changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml b/changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml new file mode 100644 index 00000000000..3445057f7fb --- /dev/null +++ b/changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml @@ -0,0 +1,5 @@ +--- +title: Improved readability of storage statistics in group / project admin area +merge_request: 30406 +author: +type: other diff --git a/changelogs/unreleased/62088-search-back.yml b/changelogs/unreleased/62088-search-back.yml new file mode 100644 index 00000000000..4758e927880 --- /dev/null +++ b/changelogs/unreleased/62088-search-back.yml @@ -0,0 +1,5 @@ +--- +title: Fixed back navigation for projects filter +merge_request: 30373 +author: +type: fixed diff --git a/changelogs/unreleased/62826-graphql-note-mutations.yml b/changelogs/unreleased/62826-graphql-note-mutations.yml new file mode 100644 index 00000000000..85273186bb6 --- /dev/null +++ b/changelogs/unreleased/62826-graphql-note-mutations.yml @@ -0,0 +1,5 @@ +--- +title: GraphQL mutations for managing Notes +merge_request: 30210 +author: +type: added diff --git a/changelogs/unreleased/63298-prevent-excessive-sanitization-asciidoc.yml b/changelogs/unreleased/63298-prevent-excessive-sanitization-asciidoc.yml new file mode 100644 index 00000000000..cd8206cdb99 --- /dev/null +++ b/changelogs/unreleased/63298-prevent-excessive-sanitization-asciidoc.yml @@ -0,0 +1,5 @@ +--- +title: "Prevent excessive sanitization of AsciiDoc ouptut" +merge_request: 30290 +author: Guillaume Grossetie +type: added
\ No newline at end of file diff --git a/changelogs/unreleased/63475-fix-n-1.yml b/changelogs/unreleased/63475-fix-n-1.yml new file mode 100644 index 00000000000..3ed825290fd --- /dev/null +++ b/changelogs/unreleased/63475-fix-n-1.yml @@ -0,0 +1,5 @@ +--- +title: Improve performance of MergeRequestsController#ci_environment_status endpoint +merge_request: 30224 +author: +type: performance diff --git a/changelogs/unreleased/63667-hashed-storage-migration-count-correctly.yml b/changelogs/unreleased/63667-hashed-storage-migration-count-correctly.yml new file mode 100644 index 00000000000..ead79be2505 --- /dev/null +++ b/changelogs/unreleased/63667-hashed-storage-migration-count-correctly.yml @@ -0,0 +1,5 @@ +--- +title: Display the correct amount of projects being migrated/rolled-back to Hashed Storage when specifying ranges +merge_request: 29996 +author: +type: fixed diff --git a/changelogs/unreleased/63691-fix-doc-link.yml b/changelogs/unreleased/63691-fix-doc-link.yml new file mode 100644 index 00000000000..e63756e8227 --- /dev/null +++ b/changelogs/unreleased/63691-fix-doc-link.yml @@ -0,0 +1,5 @@ +--- +title: Correct link to docs for External Dashboard +merge_request: 30019 +author: +type: fixed diff --git a/changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml b/changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml new file mode 100644 index 00000000000..a0ef34f3700 --- /dev/null +++ b/changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml @@ -0,0 +1,5 @@ +--- +title: Update mixin-deep to 1.3.2 +merge_request: 30223 +author: Takuya Noguchi +type: other diff --git a/changelogs/unreleased/64066-fix-uneven-click-areas.yml b/changelogs/unreleased/64066-fix-uneven-click-areas.yml new file mode 100644 index 00000000000..ce0572cad34 --- /dev/null +++ b/changelogs/unreleased/64066-fix-uneven-click-areas.yml @@ -0,0 +1,5 @@ +--- +title: Fix spacing issues for toasts +merge_request: 30345 +author: +type: fixed diff --git a/changelogs/unreleased/64091-fix-broken-terminal.yml b/changelogs/unreleased/64091-fix-broken-terminal.yml new file mode 100644 index 00000000000..156f6de5008 --- /dev/null +++ b/changelogs/unreleased/64091-fix-broken-terminal.yml @@ -0,0 +1,5 @@ +--- +title: Fix environments broken terminal +merge_request: 30401 +author: +type: fixed diff --git a/changelogs/unreleased/64161-gitlab-fqdn.yml b/changelogs/unreleased/64161-gitlab-fqdn.yml new file mode 100644 index 00000000000..2946be37caa --- /dev/null +++ b/changelogs/unreleased/64161-gitlab-fqdn.yml @@ -0,0 +1,5 @@ +--- +title: Add CI variable to provide GitLab HOST +merge_request: 30417 +author: +type: added diff --git a/changelogs/unreleased/64176-fix-error-handling.yml b/changelogs/unreleased/64176-fix-error-handling.yml new file mode 100644 index 00000000000..e7a9a5897ae --- /dev/null +++ b/changelogs/unreleased/64176-fix-error-handling.yml @@ -0,0 +1,5 @@ +--- +title: Fix invalid SSL certificate errors on Drone CI service +merge_request: 30422 +author: +type: fixed diff --git a/changelogs/unreleased/64314-ci-icon.yml b/changelogs/unreleased/64314-ci-icon.yml new file mode 100644 index 00000000000..8a550b6fa5b --- /dev/null +++ b/changelogs/unreleased/64314-ci-icon.yml @@ -0,0 +1,5 @@ +--- +title: Aligns CI icon in Merge Request dashboard +merge_request: 30558 +author: +type: fixed diff --git a/changelogs/unreleased/64321-wrong-url-when-creating-milestones-from-instance-milestones-dashboard.yml b/changelogs/unreleased/64321-wrong-url-when-creating-milestones-from-instance-milestones-dashboard.yml new file mode 100644 index 00000000000..825247db3e7 --- /dev/null +++ b/changelogs/unreleased/64321-wrong-url-when-creating-milestones-from-instance-milestones-dashboard.yml @@ -0,0 +1,5 @@ +--- +title: Fix wrong URL when creating milestones from instance milestones dashboard +merge_request: 30512 +author: +type: fixed diff --git a/changelogs/unreleased/64331-Assignee-field-in-a-new-issue-has-an-incorrect-line-wrap.yml b/changelogs/unreleased/64331-Assignee-field-in-a-new-issue-has-an-incorrect-line-wrap.yml new file mode 100644 index 00000000000..7d67b94869d --- /dev/null +++ b/changelogs/unreleased/64331-Assignee-field-in-a-new-issue-has-an-incorrect-line-wrap.yml @@ -0,0 +1,5 @@ +--- +title: Fixed incorrect line wrap for assignee label in issues +merge_request: 30523 +author: Marc Schwede +type: fixed diff --git a/changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml b/changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml new file mode 100644 index 00000000000..3695f3063f3 --- /dev/null +++ b/changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml @@ -0,0 +1,5 @@ +--- +title: Remove unresolved class and fixed height in discussion header +merge_request: 28440 +author: David Palubin +type: other diff --git a/changelogs/unreleased/allow-reactive-caching-of-nil.yml b/changelogs/unreleased/allow-reactive-caching-of-nil.yml new file mode 100644 index 00000000000..abd88c09d74 --- /dev/null +++ b/changelogs/unreleased/allow-reactive-caching-of-nil.yml @@ -0,0 +1,5 @@ +--- +title: Allow ReactiveCaching to support nil value +merge_request: 30456 +author: +type: performance diff --git a/changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml b/changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml new file mode 100644 index 00000000000..0500978a2e1 --- /dev/null +++ b/changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml @@ -0,0 +1,5 @@ +--- +title: "Document the negative commit message push rule for the API." +merge_request: 14004 +author: Maikel Vlasman +type: added diff --git a/changelogs/unreleased/backstage-gb-improve-performance-environment-statuses-endpoint.yml b/changelogs/unreleased/backstage-gb-improve-performance-environment-statuses-endpoint.yml new file mode 100644 index 00000000000..f614e076268 --- /dev/null +++ b/changelogs/unreleased/backstage-gb-improve-performance-environment-statuses-endpoint.yml @@ -0,0 +1,5 @@ +--- +title: Improve performance of fetching environments statuses +merge_request: 30560 +author: +type: performance diff --git a/changelogs/unreleased/bvl-rename-routes-after-user-rename.yml b/changelogs/unreleased/bvl-rename-routes-after-user-rename.yml new file mode 100644 index 00000000000..8b0a01dea53 --- /dev/null +++ b/changelogs/unreleased/bvl-rename-routes-after-user-rename.yml @@ -0,0 +1,5 @@ +--- +title: Update a user's routes after updating their name +merge_request: 23272 +author: +type: fixed diff --git a/changelogs/unreleased/caneldem-master-patch-77839.yml b/changelogs/unreleased/caneldem-master-patch-77839.yml new file mode 100644 index 00000000000..6239bcf67c4 --- /dev/null +++ b/changelogs/unreleased/caneldem-master-patch-77839.yml @@ -0,0 +1,5 @@ +--- +title: Propagate python version variable +merge_request: +author: Can Eldem +type: changed diff --git a/changelogs/unreleased/centralize-markdownlint-config.yml b/changelogs/unreleased/centralize-markdownlint-config.yml new file mode 100644 index 00000000000..9ca36078cf1 --- /dev/null +++ b/changelogs/unreleased/centralize-markdownlint-config.yml @@ -0,0 +1,5 @@ +--- +title: Centralize markdownlint configuration +merge_request: 30263 +author: +type: other diff --git a/changelogs/unreleased/clusters-group-cte.yml b/changelogs/unreleased/clusters-group-cte.yml new file mode 100644 index 00000000000..4b51249062d --- /dev/null +++ b/changelogs/unreleased/clusters-group-cte.yml @@ -0,0 +1,5 @@ +--- +title: Use CTE to fetch clusters hierarchy in single query +merge_request: 30063 +author: +type: performance diff --git a/changelogs/unreleased/embedded-metrics-be-2.yml b/changelogs/unreleased/embedded-metrics-be-2.yml new file mode 100644 index 00000000000..2623b4a2e0c --- /dev/null +++ b/changelogs/unreleased/embedded-metrics-be-2.yml @@ -0,0 +1,5 @@ +--- +title: Expose placeholder element for metrics charts in GFM +merge_request: 29861 +author: +type: added diff --git a/changelogs/unreleased/fe-delete-old-boardservice-on-component-modal-footer.yml b/changelogs/unreleased/fe-delete-old-boardservice-on-component-modal-footer.yml new file mode 100644 index 00000000000..4392b72443b --- /dev/null +++ b/changelogs/unreleased/fe-delete-old-boardservice-on-component-modal-footer.yml @@ -0,0 +1,6 @@ +--- +title: change the use of boardService in favor of boardsStore on footer for the board + component +merge_request: 30616 +author: eduarmreyes +type: other diff --git a/changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml b/changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml new file mode 100644 index 00000000000..6ae6db08ba1 --- /dev/null +++ b/changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml @@ -0,0 +1,5 @@ +--- +title: Fix median counting for cycle analytics +merge_request: 30229 +author: +type: fixed diff --git a/changelogs/unreleased/fix-unicorn-sampler-workers-count.yml b/changelogs/unreleased/fix-unicorn-sampler-workers-count.yml new file mode 100644 index 00000000000..9a263b7f460 --- /dev/null +++ b/changelogs/unreleased/fix-unicorn-sampler-workers-count.yml @@ -0,0 +1,5 @@ +--- +title: Make unicorn_workers to return meaningful results +merge_request: 30506 +author: +type: fixed diff --git a/changelogs/unreleased/fj-fix-subgroup-search-url.yml b/changelogs/unreleased/fj-fix-subgroup-search-url.yml new file mode 100644 index 00000000000..bee6e97fb6f --- /dev/null +++ b/changelogs/unreleased/fj-fix-subgroup-search-url.yml @@ -0,0 +1,5 @@ +--- +title: Fix subgroup url in search drop down +merge_request: 30457 +author: +type: fixed diff --git a/changelogs/unreleased/gitaly-version-v1-53-0.yml b/changelogs/unreleased/gitaly-version-v1-53-0.yml new file mode 100644 index 00000000000..7d3e5ce3cb3 --- /dev/null +++ b/changelogs/unreleased/gitaly-version-v1-53-0.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade to Gitaly v1.53.0 +merge_request: 30614 +author: +type: changed diff --git a/changelogs/unreleased/gitaly-version-v1.52.0.yml b/changelogs/unreleased/gitaly-version-v1.52.0.yml new file mode 100644 index 00000000000..d56a392c4ff --- /dev/null +++ b/changelogs/unreleased/gitaly-version-v1.52.0.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade to Gitaly v1.52.0 +merge_request: 30568 +author: +type: changed diff --git a/changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml b/changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml new file mode 100644 index 00000000000..958334cc28e --- /dev/null +++ b/changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml @@ -0,0 +1,5 @@ +--- +title: Create Knative role and binding with service account +merge_request: 30235 +author: +type: changed diff --git a/changelogs/unreleased/id-clean-up-mr-assignees.yml b/changelogs/unreleased/id-clean-up-mr-assignees.yml new file mode 100644 index 00000000000..7ff03c9f00b --- /dev/null +++ b/changelogs/unreleased/id-clean-up-mr-assignees.yml @@ -0,0 +1,5 @@ +--- +title: Add cleanup migration for MR's multiple assignees +merge_request: 30261 +author: +type: changed diff --git a/changelogs/unreleased/issue_64021.yml b/changelogs/unreleased/issue_64021.yml new file mode 100644 index 00000000000..088d9348619 --- /dev/null +++ b/changelogs/unreleased/issue_64021.yml @@ -0,0 +1,5 @@ +--- +title: Skip spam check for task list updates +merge_request: 30279 +author: +type: fixed diff --git a/changelogs/unreleased/jc-detect-nfs-for-rugged.yml b/changelogs/unreleased/jc-detect-nfs-for-rugged.yml new file mode 100644 index 00000000000..ca738181a55 --- /dev/null +++ b/changelogs/unreleased/jc-detect-nfs-for-rugged.yml @@ -0,0 +1,5 @@ +--- +title: Use Rugged if we detect storage is NFS and we can access the disk +merge_request: 29725 +author: +type: performance diff --git a/changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml b/changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml new file mode 100644 index 00000000000..b953d7c0fc8 --- /dev/null +++ b/changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml @@ -0,0 +1,5 @@ +--- +title: Enable Git object pools +merge_request: 29595 +author: jramsay +type: changed diff --git a/changelogs/unreleased/mw-project-list-color-fix.yml b/changelogs/unreleased/mw-project-list-color-fix.yml new file mode 100644 index 00000000000..6f8b2742ec6 --- /dev/null +++ b/changelogs/unreleased/mw-project-list-color-fix.yml @@ -0,0 +1,5 @@ +--- +title: Add text-secondary to controls in project list +merge_request: 30567 +author: +type: fixed diff --git a/changelogs/unreleased/patch-29.yml b/changelogs/unreleased/patch-29.yml new file mode 100644 index 00000000000..674c06e1259 --- /dev/null +++ b/changelogs/unreleased/patch-29.yml @@ -0,0 +1,5 @@ +--- +title: Updates PHP template to php:latest to ensure always targeting latest stable +merge_request: 30319 +author: Paul Giberson +type: changed diff --git a/changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml b/changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml new file mode 100644 index 00000000000..d7bfc67b208 --- /dev/null +++ b/changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml @@ -0,0 +1,5 @@ +--- +title: Modify cycle analytics on project level +merge_request: 30356 +author: +type: changed diff --git a/changelogs/unreleased/project_api.yml b/changelogs/unreleased/project_api.yml new file mode 100644 index 00000000000..a04f9bb5608 --- /dev/null +++ b/changelogs/unreleased/project_api.yml @@ -0,0 +1,5 @@ +--- +title: Improve Project API +merge_request: 28327 +author: Mathieu Parent +type: added diff --git a/changelogs/unreleased/registry-fix-multi-delete-modal.yml b/changelogs/unreleased/registry-fix-multi-delete-modal.yml new file mode 100644 index 00000000000..94a2df7a7e7 --- /dev/null +++ b/changelogs/unreleased/registry-fix-multi-delete-modal.yml @@ -0,0 +1,5 @@ +--- +title: Prevent multiple confirmation modals from opening when deleting a repository +merge_request: 30532 +author: +type: fixed diff --git a/changelogs/unreleased/remove-auto-ssl-ff.yml b/changelogs/unreleased/remove-auto-ssl-ff.yml new file mode 100644 index 00000000000..1b4d0dcde08 --- /dev/null +++ b/changelogs/unreleased/remove-auto-ssl-ff.yml @@ -0,0 +1,6 @@ +--- +title: Add support for generating SSL certificates for custon pages domains through + Let's Encrypt +merge_request: +author: +type: added diff --git a/changelogs/unreleased/sh-audit-event-json-log-format-from-and-to.yml b/changelogs/unreleased/sh-audit-event-json-log-format-from-and-to.yml new file mode 100644 index 00000000000..5bd21d7ef2b --- /dev/null +++ b/changelogs/unreleased/sh-audit-event-json-log-format-from-and-to.yml @@ -0,0 +1,5 @@ +--- +title: Format `from` and `to` fields in JSON audit log +merge_request: 30333 +author: +type: changed diff --git a/changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml b/changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml new file mode 100644 index 00000000000..a0db68adb78 --- /dev/null +++ b/changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml @@ -0,0 +1,5 @@ +--- +title: Prevent amplification of ReactiveCachingWorker jobs upon failures +merge_request: 30432 +author: +type: performance diff --git a/changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml b/changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml new file mode 100644 index 00000000000..b408019c736 --- /dev/null +++ b/changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade Rouge to 3.5.1 +merge_request: 30431 +author: +type: changed diff --git a/changelogs/unreleased/tz-update-mr-count-over-tabs.yml b/changelogs/unreleased/tz-update-mr-count-over-tabs.yml new file mode 100644 index 00000000000..61a49dd8ecc --- /dev/null +++ b/changelogs/unreleased/tz-update-mr-count-over-tabs.yml @@ -0,0 +1,6 @@ +--- +title: New API for User Counts, updates on success of an MR the count on top and in + other tabs +merge_request: 29441 +author: +type: added diff --git a/changelogs/unreleased/update-clair-version.yml b/changelogs/unreleased/update-clair-version.yml new file mode 100644 index 00000000000..59b6e113fd5 --- /dev/null +++ b/changelogs/unreleased/update-clair-version.yml @@ -0,0 +1,6 @@ +--- +title: Extract clair version as CLAIR_EXECUTABLE_VERSION variable and update clair + executable from v8 to v11 +merge_request: 30396 +author: +type: changed diff --git a/changelogs/unreleased/update-pages-to-1-7-0.yml b/changelogs/unreleased/update-pages-to-1-7-0.yml new file mode 100644 index 00000000000..d41346f021c --- /dev/null +++ b/changelogs/unreleased/update-pages-to-1-7-0.yml @@ -0,0 +1,5 @@ +--- +title: Update GitLab Pages to v1.7.0 +merge_request: +author: +type: other diff --git a/changelogs/unreleased/winh-notes-service-applySuggestion.yml b/changelogs/unreleased/winh-notes-service-applySuggestion.yml new file mode 100644 index 00000000000..30e540237b6 --- /dev/null +++ b/changelogs/unreleased/winh-notes-service-applySuggestion.yml @@ -0,0 +1,5 @@ +--- +title: Remove applySuggestion from notes service +merge_request: 30399 +author: Frank van Rest +type: other diff --git a/changelogs/unreleased/winh-notes-service-deleteNote.yml b/changelogs/unreleased/winh-notes-service-deleteNote.yml new file mode 100644 index 00000000000..cf2b37755a2 --- /dev/null +++ b/changelogs/unreleased/winh-notes-service-deleteNote.yml @@ -0,0 +1,5 @@ +--- +title: Remove deleteNote from notes service +merge_request: 30537 +author: Frank van Rest +type: other diff --git a/changelogs/unreleased/winh-notes-service-toggleAward.yml b/changelogs/unreleased/winh-notes-service-toggleAward.yml new file mode 100644 index 00000000000..0471888c285 --- /dev/null +++ b/changelogs/unreleased/winh-notes-service-toggleAward.yml @@ -0,0 +1,5 @@ +--- +title: Remove toggleAward from notes service +merge_request: 30536 +author: Frank van Rest +type: other diff --git a/changelogs/unreleased/z-index-tools.yml b/changelogs/unreleased/z-index-tools.yml new file mode 100644 index 00000000000..1102612670b --- /dev/null +++ b/changelogs/unreleased/z-index-tools.yml @@ -0,0 +1,5 @@ +--- +title: 'Review Tools: Add large z-index to toolbar' +merge_request: 30583 +author: +type: fixed diff --git a/config/application.rb b/config/application.rb index c5ef6a2c60d..de386506233 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,11 +1,18 @@ -require File.expand_path('boot', __dir__) +require_relative 'boot' -require 'rails/all' +# Based on https://github.com/rails/rails/blob/v5.2.3/railties/lib/rails/all.rb +# Only load the railties we need instead of loading everything +require 'active_record/railtie' +require 'action_controller/railtie' +require 'action_view/railtie' +require 'action_mailer/railtie' +require 'rails/test_unit/railtie' -Bundler.require(:default, Rails.env) +Bundler.require(*Rails.groups) module Gitlab class Application < Rails::Application + require_dependency Rails.root.join('lib/gitlab') require_dependency Rails.root.join('lib/gitlab/redis/wrapper') require_dependency Rails.root.join('lib/gitlab/redis/cache') require_dependency Rails.root.join('lib/gitlab/redis/queues') @@ -24,6 +31,8 @@ module Gitlab # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. + config.active_record.sqlite3.represent_boolean_as_integer = true + # Sidekiq uses eager loading, but directories not in the standard Rails # directories must be added to the eager load paths: # https://github.com/mperham/sidekiq/wiki/FAQ#why-doesnt-sidekiq-autoload-my-rails-application-code @@ -85,13 +94,6 @@ module Gitlab # Configure the default encoding used in templates for Ruby 1.9. config.encoding = "utf-8" - # ActionCable mount point. - # The default Rails' mount point is `/cable` which may conflict with existing - # namespaces/users. - # https://github.com/rails/rails/blob/5-0-stable/actioncable/lib/action_cable.rb#L38 - # Please change this value when configuring ActionCable for real usage. - config.action_cable.mount_path = "/-/cable" - # Configure sensitive parameters which will be filtered from the log file. # # Parameters filtered: @@ -271,5 +273,10 @@ module Gitlab Gitlab::Routing.add_helpers(project_url_helpers) Gitlab::Routing.add_helpers(MilestonesRoutingHelper) end + + # This makes generated cookies to be compatible with Rails 5.1 and older + # We can remove this when we're confident that there are no issues with the Rails 5.2 upgrade + # and we won't need to rollback to older versions + config.action_dispatch.use_authenticated_cookie_encryption = false end end diff --git a/config/brakeman.ignore b/config/brakeman.ignore new file mode 100644 index 00000000000..0e4fef65781 --- /dev/null +++ b/config/brakeman.ignore @@ -0,0 +1,24 @@ +{ + "ignored_warnings": [ + { + "warning_type": "Cross-Site Request Forgery", + "warning_code": 7, + "fingerprint": "dc562678129557cdb8b187217da304044547a3605f05fe678093dcb4b4d8bbe4", + "message": "'protect_from_forgery' should be called in Oauth::GeoAuthController", + "file": "app/controllers/oauth/geo_auth_controller.rb", + "line": 1, + "link": "http://brakemanscanner.org/docs/warning_types/cross-site_request_forgery/", + "code": null, + "render_path": null, + "location": { + "type": "controller", + "controller": "Oauth::GeoAuthController" + }, + "user_input": null, + "confidence": "High", + "note": "" + } + ], + "updated": "2017-01-20 02:06:54 +0000", + "brakeman_version": "3.4.1" +} diff --git a/config/database_geo.yml.postgresql b/config/database_geo.yml.postgresql new file mode 100644 index 00000000000..2918879f7ed --- /dev/null +++ b/config/database_geo.yml.postgresql @@ -0,0 +1,51 @@ +# +# PRODUCTION +# +production: + adapter: postgresql + encoding: unicode + database: gitlabhq_geo_production + pool: 10 + username: git + password: "secure password" + host: localhost + fdw: true + +# +# Development specific +# +development: + adapter: postgresql + encoding: unicode + database: gitlabhq_geo_development + pool: 5 + username: postgres + password: "secure password" + host: localhost + fdw: true + +# +# Staging specific +# +staging: + adapter: postgresql + encoding: unicode + database: gitlabhq_geo_staging + pool: 10 + username: git + password: "secure password" + host: localhost + fdw: true + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: &test + adapter: postgresql + encoding: unicode + database: gitlabhq_geo_test + pool: 5 + username: postgres + password: + host: localhost + fdw: true diff --git a/config/environments/development.rb b/config/environments/development.rb index ac9b02b08d5..3881f1be152 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -7,6 +7,7 @@ Rails.application.configure do config.cache_classes = false # Show full error reports and disable caching + config.active_record.verbose_query_logs = true config.consider_all_requests_local = true config.action_controller.perform_caching = false diff --git a/config/environments/test.rb b/config/environments/test.rb index e7166882eea..153d16e4e55 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -23,6 +23,7 @@ Rails.application.configure do config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' } # Show full error reports and disable caching + config.active_record.verbose_query_logs = true config.consider_all_requests_local = true config.action_controller.perform_caching = false @@ -40,7 +41,7 @@ Rails.application.configure do # Print deprecation notices to the stderr config.active_support.deprecation = :stderr - config.eager_load = false + config.eager_load = true config.cache_store = :null_store diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index c82d9b5ceef..334c241bcaa 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -664,6 +664,9 @@ production: &base # Port where the client side certificate is requested by the webserver (NGINX/Apache) # client_certificate_required_port: 3444 + # Browser session with smartcard sign-in is required for Git access + # required_for_git_access: false + ## Kerberos settings kerberos: # Allow the HTTP Negotiate authentication method for Git clients diff --git a/config/initializers/0_license.rb b/config/initializers/0_license.rb new file mode 100644 index 00000000000..f750022dfdf --- /dev/null +++ b/config/initializers/0_license.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +Gitlab.ee do + begin + public_key_file = File.read(Rails.root.join(".license_encryption_key.pub")) + public_key = OpenSSL::PKey::RSA.new(public_key_file) + Gitlab::License.encryption_key = public_key + rescue + warn "WARNING: No valid license encryption key provided." + end + + # Needed to run migration + if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.data_source_exists?('licenses') + message = LicenseHelper.license_message(signed_in: true, is_admin: true, in_html: false) + if ::License.block_changes? && message.present? + warn "WARNING: #{message}" + end + end +end diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index bf187e9a282..3a121addc98 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -55,7 +55,7 @@ if Settings.ldap['enabled'] || Rails.env.test? server['tls_options'] ||= {} if server['ssl_version'] || server['ca_file'] - Rails.logger.warn 'DEPRECATED: LDAP options `ssl_version` and `ca_file` should be nested within `tls_options`' + Rails.logger.warn 'DEPRECATED: LDAP options `ssl_version` and `ca_file` should be nested within `tls_options`' # rubocop:disable Gitlab/RailsLogger end if server['ssl_version'] @@ -76,6 +76,7 @@ Gitlab.ee do Settings['smartcard'] ||= Settingslogic.new({}) Settings.smartcard['enabled'] = false if Settings.smartcard['enabled'].nil? Settings.smartcard['client_certificate_required_port'] = 3444 if Settings.smartcard['client_certificate_required_port'].nil? + Settings.smartcard['required_for_git_access'] = false if Settings.smartcard['required_for_git_access'].nil? end Settings['omniauth'] ||= Settingslogic.new({}) diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb index 741c8ef1ca0..53c3eac3c74 100644 --- a/config/initializers/7_prometheus_metrics.rb +++ b/config/initializers/7_prometheus_metrics.rb @@ -17,7 +17,7 @@ def prometheus_default_multiproc_dir end Prometheus::Client.configure do |config| - config.logger = Rails.logger + config.logger = Rails.logger # rubocop:disable Gitlab/RailsLogger config.initial_mmap_file_size = 4 * 1024 diff --git a/config/initializers/active_record_data_types.rb b/config/initializers/active_record_data_types.rb index e95157bfde5..151bce4d130 100644 --- a/config/initializers/active_record_data_types.rb +++ b/config/initializers/active_record_data_types.rb @@ -22,7 +22,7 @@ if Gitlab::Database.postgresql? # # When schema dumping, `timestamptz` columns will be output as # `t.datetime_with_timezone`. - def initialize_type_map(mapping) + def initialize_type_map(mapping = type_map) super mapping mapping.register_type 'timestamptz' do |_, _, sql_type| @@ -51,7 +51,7 @@ elsif Gitlab::Database.mysql? # # When schema dumping, `timestamp` columns will be output as # `t.datetime_with_timezone`. - def initialize_type_map(mapping) + def initialize_type_map(mapping = type_map) super mapping mapping.register_type(/timestamp/i) do |sql_type| diff --git a/config/initializers/active_record_lifecycle.rb b/config/initializers/active_record_lifecycle.rb index 7fa37121efc..61f1d299960 100644 --- a/config/initializers/active_record_lifecycle.rb +++ b/config/initializers/active_record_lifecycle.rb @@ -7,7 +7,7 @@ if defined?(ActiveRecord::Base) && !Sidekiq.server? ActiveSupport.on_load(:active_record) do ActiveRecord::Base.establish_connection - Rails.logger.debug("ActiveRecord connection established") + Rails.logger.debug("ActiveRecord connection established") # rubocop:disable Gitlab/RailsLogger end end end @@ -18,6 +18,6 @@ if defined?(ActiveRecord::Base) # as there's no need for the master process to hold a connection ActiveRecord::Base.connection.disconnect! - Rails.logger.debug("ActiveRecord connection disconnected") + Rails.logger.debug("ActiveRecord connection disconnected") # rubocop:disable Gitlab/RailsLogger end end diff --git a/config/initializers/active_record_preloader.rb b/config/initializers/active_record_preloader.rb index 3b16014f302..a293909149e 100644 --- a/config/initializers/active_record_preloader.rb +++ b/config/initializers/active_record_preloader.rb @@ -1,9 +1,22 @@ module ActiveRecord module Associations class Preloader + class NullPreloader + def self.new(klass, owners, reflection, preload_scope) + self + end + + def self.run(preloader) + end + + def self.preloaded_records + [] + end + end + module NoCommitPreloader - def preloader_for(reflection, owners, rhs_klass) - return NullPreloader if rhs_klass == ::Commit + def preloader_for(reflection, owners) + return NullPreloader if owners.first.association(reflection.name).klass == ::Commit super end diff --git a/config/initializers/active_record_query_cache.rb b/config/initializers/active_record_query_cache.rb new file mode 100644 index 00000000000..61505a1edd3 --- /dev/null +++ b/config/initializers/active_record_query_cache.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +ActiveRecord::ConnectionAdapters::ConnectionPool.prepend Gitlab::Patch::ActiveRecordQueryCache diff --git a/config/initializers/active_record_verbose_query_logs.rb b/config/initializers/active_record_verbose_query_logs.rb deleted file mode 100644 index 1c5fbc8e830..00000000000 --- a/config/initializers/active_record_verbose_query_logs.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -# This is backport of https://github.com/rails/rails/pull/26815/files -# Enabled by default for every non-production environment - -module ActiveRecord - class LogSubscriber - module VerboseQueryLogs - def debug(progname = nil, &block) - return unless super - - log_query_source - end - - def log_query_source - source_line, line_number = extract_callstack(caller_locations) - - if source_line - if defined?(::Rails.root) - app_root = "#{::Rails.root}/".freeze - source_line = source_line.sub(app_root, "") - end - - logger.debug(" ↳ #{source_line}:#{line_number}") - end - end - - def extract_callstack(callstack) - line = callstack.find do |frame| - frame.absolute_path && !ignored_callstack(frame.absolute_path) - end - - offending_line = line || callstack.first - [ - offending_line.path, - offending_line.lineno, - offending_line.label - ] - end - - LOG_SUBSCRIBER_FILE = ActiveRecord::LogSubscriber.method(:logger).source_location.first - RAILS_GEM_ROOT = File.expand_path("../../../..", LOG_SUBSCRIBER_FILE) + "/" - APP_CONFIG_ROOT = File.expand_path("..", __dir__) + "/" - - def ignored_callstack(path) - path.start_with?(APP_CONFIG_ROOT, RAILS_GEM_ROOT, RbConfig::CONFIG["rubylibdir"]) - end - end - - if Rails.version.start_with?("5.2") - raise "Remove this monkey patch: #{__FILE__}" - else - prepend(VerboseQueryLogs) unless Rails.env.production? - end - end -end diff --git a/config/initializers/ar_speed_up_migration_checking.rb b/config/initializers/ar_speed_up_migration_checking.rb index aae774daa35..c4ffcc54cb2 100644 --- a/config/initializers/ar_speed_up_migration_checking.rb +++ b/config/initializers/ar_speed_up_migration_checking.rb @@ -2,16 +2,14 @@ if Rails.env.test? require 'active_record/migration' module ActiveRecord - class Migrator - class << self - alias_method :migrations_unmemoized, :migrations + class MigrationContext + alias_method :migrations_unmemoized, :migrations - # This method is called a large number of times per rspec example, and - # it reads + parses `db/migrate/*` each time. Memoizing it can save 0.5 - # seconds per spec. - def migrations(paths) - (@migrations ||= migrations_unmemoized(paths)).dup - end + # This method is called a large number of times per rspec example, and + # it reads + parses `db/migrate/*` each time. Memoizing it can save 0.5 + # seconds per spec. + def migrations + @migrations ||= migrations_unmemoized end end end diff --git a/config/initializers/config_initializers_active_record_locking.rb b/config/initializers/config_initializers_active_record_locking.rb index 608d63223a3..915247826e9 100644 --- a/config/initializers/config_initializers_active_record_locking.rb +++ b/config/initializers/config_initializers_active_record_locking.rb @@ -22,10 +22,11 @@ module ActiveRecord # Patched because when `lock_version` is read as `0`, it may actually be `NULL` in the DB. possible_previous_lock_value = previous_lock_value.to_i == 0 ? [nil, 0] : previous_lock_value - affected_rows = self.class.unscoped._update_record( - arel_attributes_with_values(attribute_names), - self.class.primary_key => id_in_database, - locking_column => possible_previous_lock_value + affected_rows = self.class.unscoped.where( + locking_column => possible_previous_lock_value, + self.class.primary_key => id_in_database + ).update_all( + attributes_with_values_for_update(attribute_names) ) if affected_rows != 1 diff --git a/config/initializers/console_message.rb b/config/initializers/console_message.rb index 05eb395028d..04c109aa844 100644 --- a/config/initializers/console_message.rb +++ b/config/initializers/console_message.rb @@ -2,9 +2,18 @@ if defined?(Rails::Console) # note that this will not print out when using `spring` justify = 15 - puts "-------------------------------------------------------------------------------------" + + puts '-' * 80 puts " GitLab:".ljust(justify) + "#{Gitlab::VERSION} (#{Gitlab.revision})" puts " GitLab Shell:".ljust(justify) + "#{Gitlab::VersionInfo.parse(Gitlab::Shell.new.version)}" puts " #{Gitlab::Database.human_adapter_name}:".ljust(justify) + Gitlab::Database.version - puts "-------------------------------------------------------------------------------------" + + Gitlab.ee do + if Gitlab::Geo.enabled? + puts " Geo enabled:".ljust(justify) + 'yes' + puts " Geo server:".ljust(justify) + EE::GeoHelper.current_node_human_status + end + end + + puts '-' * 80 end diff --git a/config/initializers/deprecations.rb b/config/initializers/deprecations.rb index 14616e726d9..0d096e34eb7 100644 --- a/config/initializers/deprecations.rb +++ b/config/initializers/deprecations.rb @@ -2,7 +2,7 @@ if Rails.env.development? || ENV['GITLAB_LEGACY_PATH_LOG_MESSAGE'] deprecator = ActiveSupport::Deprecation.new('11.0', 'GitLab') deprecator.behavior = -> (message, callstack) { - Rails.logger.warn("#{message}: #{callstack[1..20].join}") + Rails.logger.warn("#{message}: #{callstack[1..20].join}") # rubocop:disable Gitlab/RailsLogger } ActiveSupport::Deprecation.deprecate_methods(Gitlab::GitalyClient::StorageSettings, :legacy_disk_path, deprecator: deprecator) diff --git a/config/initializers/elastic_client_setup.rb b/config/initializers/elastic_client_setup.rb new file mode 100644 index 00000000000..2ecb7956007 --- /dev/null +++ b/config/initializers/elastic_client_setup.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +# Be sure to restart your server when you modify this file. + +require 'gitlab/current_settings' + +Gitlab.ee do + Elasticsearch::Model::Response::Records.prepend GemExtensions::Elasticsearch::Model::Response::Records + Elasticsearch::Model::Adapter::Multiple::Records.prepend GemExtensions::Elasticsearch::Model::Adapter::Multiple::Records + Elasticsearch::Model::Indexing::InstanceMethods.prepend GemExtensions::Elasticsearch::Model::Indexing::InstanceMethods + + module Elasticsearch + module Model + module Client + # This mutex is only used to synchronize *creation* of a new client, so + # all including classes can share the same client instance + CLIENT_MUTEX = Mutex.new + + cattr_accessor :cached_client + cattr_accessor :cached_config + + module ClassMethods + # Override the default ::Elasticsearch::Model::Client implementation to + # return a client configured from application settings. All including + # classes will use the same instance, which is refreshed automatically + # if the settings change. + # + # _client is present to match the arity of the overridden method, where + # it is also not used. + # + # @return [Elasticsearch::Transport::Client] + def client(_client = nil) + store = ::Elasticsearch::Model::Client + + store::CLIENT_MUTEX.synchronize do + config = Gitlab::CurrentSettings.elasticsearch_config + + if store.cached_client.nil? || config != store.cached_config + store.cached_client = ::Gitlab::Elastic::Client.build(config) + store.cached_config = config + end + end + + store.cached_client + end + end + end + end + end +end diff --git a/config/initializers/geo.rb b/config/initializers/geo.rb new file mode 100644 index 00000000000..4cc9fbf49b2 --- /dev/null +++ b/config/initializers/geo.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +Gitlab.ee do + if File.exist?(Rails.root.join('config/database_geo.yml')) + Rails.application.configure do + config.geo_database = config_for(:database_geo) + end + end + + begin + if Gitlab::Geo.connected? && Gitlab::Geo.primary? + Gitlab::Geo.current_node&.update_clone_url! + end + rescue => e + warn "WARNING: Unable to check/update clone_url_prefix for Geo: #{e}" + end +end diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb index 959daa93f78..9f466dc39de 100644 --- a/config/initializers/health_check.rb +++ b/config/initializers/health_check.rb @@ -1,4 +1,10 @@ HealthCheck.setup do |config| config.standard_checks = %w(database migrations cache) config.full_checks = %w(database migrations cache) + + Gitlab.ee do + config.add_custom_check('geo') do + Gitlab::Geo::HealthCheck.new.perform_checks + end + end end diff --git a/config/initializers/load_balancing.rb b/config/initializers/load_balancing.rb new file mode 100644 index 00000000000..029c0ff4277 --- /dev/null +++ b/config/initializers/load_balancing.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# We need to run this initializer after migrations are done so it doesn't fail on CI + +Gitlab.ee do + if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.data_source_exists?('licenses') + if Gitlab::Database::LoadBalancing.enable? + Gitlab::Database.disable_prepared_statements + + Gitlab::Application.configure do |config| + config.middleware.use(Gitlab::Database::LoadBalancing::RackMiddleware) + end + + Gitlab::Database::LoadBalancing.configure_proxy + + # This needs to be executed after fork of clustered processes + Gitlab::Cluster::LifecycleEvents.on_worker_start do + # Service discovery must be started after configuring the proxy, as service + # discovery depends on this. + Gitlab::Database::LoadBalancing.start_service_discovery + end + + end + end +end diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb index 1ad93e14f7e..fbec28186eb 100644 --- a/config/initializers/lograge.rb +++ b/config/initializers/lograge.rb @@ -18,7 +18,7 @@ unless Sidekiq.server? .map { |k, v| { key: k, value: v } } payload = { - time: event.time.utc.iso8601(3), + time: Time.now.utc.iso8601(3), params: params, remote_ip: event.payload[:remote_ip], user_id: event.payload[:user_id], diff --git a/config/initializers/mysql_ignore_postgresql_options.rb b/config/initializers/mysql_ignore_postgresql_options.rb index 9a569be7674..e6a7d9bef52 100644 --- a/config/initializers/mysql_ignore_postgresql_options.rb +++ b/config/initializers/mysql_ignore_postgresql_options.rb @@ -15,7 +15,6 @@ if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) module ConnectionAdapters class Mysql2Adapter < AbstractMysqlAdapter alias_method :__gitlab_add_index, :add_index - alias_method :__gitlab_add_index_sql, :add_index_sql alias_method :__gitlab_add_index_options, :add_index_options def add_index(table_name, column_name, options = {}) @@ -24,12 +23,6 @@ if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) end end - def add_index_sql(table_name, column_name, options = {}) - unless options[:opclasses] - __gitlab_add_index_sql(table_name, column_name, options) - end - end - def add_index_options(table_name, column_name, options = {}) if options[:using] && options[:using] == :gin options = options.dup diff --git a/config/initializers/postgresql_cte.rb b/config/initializers/postgresql_cte.rb index 56689bc8e74..68d53c4edbf 100644 --- a/config/initializers/postgresql_cte.rb +++ b/config/initializers/postgresql_cte.rb @@ -94,8 +94,8 @@ module ActiveRecord end end - def build_arel - arel = super() + def build_arel(aliases) + arel = super build_with(arel) if @values[:with] diff --git a/config/initializers/rack_attack_logging.rb b/config/initializers/rack_attack_logging.rb index 338e968cc6c..7eb34bd69e5 100644 --- a/config/initializers/rack_attack_logging.rb +++ b/config/initializers/rack_attack_logging.rb @@ -12,7 +12,7 @@ ActiveSupport::Notifications.subscribe('rack.attack') do |name, start, finish, r fullpath: req.fullpath } - if req.env['rack.attack.matched'] != 'throttle_unauthenticated' + if %w(throttle_authenticated_api throttle_authenticated_web).include? req.env['rack.attack.matched'] user_id = req.env['rack.attack.match_discriminator'] user = User.find_by(id: user_id) diff --git a/config/initializers/rack_timeout.rb b/config/initializers/rack_timeout.rb index 58f46b55725..246cf3482a4 100644 --- a/config/initializers/rack_timeout.rb +++ b/config/initializers/rack_timeout.rb @@ -14,8 +14,8 @@ if defined?(::Puma) && !Rails.env.test? Gitlab::Application.configure do |config| config.middleware.insert_before(Rack::Runtime, Rack::Timeout, - service_timeout: 60, - wait_timeout: 90) + service_timeout: ENV.fetch('GITLAB_RAILS_RACK_TIMEOUT', 60).to_i, + wait_timeout: ENV.fetch('GITLAB_RAILS_WAIT_TIMEOUT', 90).to_i) end observer = Gitlab::Cluster::RackTimeoutObserver.new diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 7b69cf11288..166fb9b6916 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -72,7 +72,7 @@ Sidekiq.configure_server do |config| cron_jobs[k]['class'] = cron_jobs[k].delete('job_class') else cron_jobs.delete(k) - Rails.logger.error("Invalid cron_jobs config key: '#{k}'. Check your gitlab config file.") + Rails.logger.error("Invalid cron_jobs config key: '#{k}'. Check your gitlab config file.") # rubocop:disable Gitlab/RailsLogger end end Sidekiq::Cron::Job.load_from_hash! cron_jobs @@ -83,7 +83,20 @@ Sidekiq.configure_server do |config| Rails.application.config.database_configuration[Rails.env] db_config['pool'] = Sidekiq.options[:concurrency] ActiveRecord::Base.establish_connection(db_config) - Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{ActiveRecord::Base.connection.pool.instance_variable_get('@size')}") + Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{ActiveRecord::Base.connection.pool.instance_variable_get('@size')}") # rubocop:disable Gitlab/RailsLogger + + Gitlab.ee do + Gitlab::Mirror.configure_cron_job! + + Gitlab::Geo.configure_cron_jobs! + + if Gitlab::Geo.geo_database_configured? + Rails.configuration.geo_database['pool'] = Sidekiq.options[:concurrency] + Geo::TrackingBase.establish_connection(Rails.configuration.geo_database) + + Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{Geo::TrackingBase.connection_pool.size} (Geo tracking database)") # rubocop:disable Gitlab/RailsLogger + end + end # Avoid autoload issue such as 'Mail::Parsers::AddressStruct' # https://github.com/mikel/mail/issues/912#issuecomment-214850355 diff --git a/config/initializers/sidekiq_cluster.rb b/config/initializers/sidekiq_cluster.rb new file mode 100644 index 00000000000..baa7495aa29 --- /dev/null +++ b/config/initializers/sidekiq_cluster.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +if ENV['ENABLE_SIDEKIQ_CLUSTER'] && Gitlab.ee? + Thread.new do + Thread.current.abort_on_exception = true + + parent = Process.ppid + + loop do + sleep(5) + + # In cluster mode it's possible that the master process is SIGKILL'd. In + # this case the parent PID changes and we need to terminate ourselves. + if Process.ppid != parent + Process.kill(:TERM, Process.pid) + break + end + end + end +end diff --git a/config/prometheus/cluster_metrics.yml b/config/prometheus/cluster_metrics.yml new file mode 100644 index 00000000000..3df76b0974f --- /dev/null +++ b/config/prometheus/cluster_metrics.yml @@ -0,0 +1,63 @@ +- group: Cluster Health + priority: 1 + metrics: + - title: "CPU Usage" + y_label: "CPU" + required_metrics: ['container_cpu_usage_seconds_total'] + weight: 1 + queries: + - query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{id="/"}[15m])) by (job)) without (job)' + label: Usage + unit: "cores" + appearance: + line: + width: 2 + area: + opacity: 0 + - query_range: 'sum(kube_pod_container_resource_requests_cpu_cores{kubernetes_namespace="gitlab-managed-apps"})' + label: Requested + unit: "cores" + appearance: + line: + width: 2 + area: + opacity: 0 + - query_range: 'sum(kube_node_status_capacity_cpu_cores{kubernetes_namespace="gitlab-managed-apps"})' + label: Capacity + unit: "cores" + appearance: + line: + type: 'dashed' + width: 2 + area: + opacity: 0 + - title: "Memory usage" + y_label: "Memory" + required_metrics: ['container_memory_usage_bytes'] + weight: 1 + queries: + - query_range: 'avg(sum(container_memory_usage_bytes{id="/"}) by (job)) without (job) / 2^30' + label: Usage + unit: "GiB" + appearance: + line: + width: 2 + area: + opacity: 0 + - query_range: 'sum(kube_pod_container_resource_requests_memory_bytes{kubernetes_namespace="gitlab-managed-apps"})/2^30' + label: Requested + unit: "GiB" + appearance: + line: + width: 2 + area: + opacity: 0 + - query_range: 'sum(kube_node_status_capacity_memory_bytes{kubernetes_namespace="gitlab-managed-apps"})/2^30' + label: Capacity + unit: "GiB" + appearance: + line: + type: 'dashed' + width: 2 + area: + opacity: 0 diff --git a/config/pseudonymizer.yml b/config/pseudonymizer.yml new file mode 100644 index 00000000000..1d85ac1db45 --- /dev/null +++ b/config/pseudonymizer.yml @@ -0,0 +1,475 @@ +tables: + approvals: + whitelist: + - id + - merge_request_id + - user_id + - created_at + - updated_at + approver_groups: + whitelist: + - id + - target_type + - group_id + - created_at + - updated_at + board_assignees: + whitelist: + - id + - board_id + - assignee_id + board_labels: + whitelist: + - id + - board_id + - label_id + boards: + whitelist: + - id + - project_id + - created_at + - updated_at + - milestone_id + - group_id + - weight + epic_issues: + whitelist: + - id + - epic_id + - issue_id + - relative_position + epic_metrics: + whitelist: + - id + - epic_id + - created_at + - updated_at + epics: + whitelist: + - id + - milestone_id + - group_id + - author_id + - assignee_id + - iid + - updated_by_id + - last_edited_by_id + - lock_version + - start_date + - end_date + - last_edited_at + - created_at + - updated_at + - title + - description + issue_assignees: + whitelist: + - user_id + - issue_id + issue_links: + whitelist: + - id + - source_id + - target_id + - created_at + - updated_at + issue_metrics: + whitelist: + - id + - issue_id + - first_mentioned_in_commit_at + - first_associated_with_milestone_at + - first_added_to_board_at + - created_at + - updated_at + issues: + whitelist: + - id + - title + - author_id + - project_id + - created_at + - confidential + - updated_at + - description + - milestone_id + - state + - updated_by_id + - weight + - due_date + - moved_to_id + - lock_version + - time_estimate + - last_edited_at + - last_edited_by_id + - discussion_locked + - closed_at + label_links: + whitelist: + - id + - label_id + - target_id + - target_type + - created_at + - updated_at + label_priorities: + whitelist: + - id + - project_id + - label_id + - priority + - created_at + - updated_at + labels: + whitelist: + - id + - title + - color + - project_id + - created_at + - updated_at + - template + - type + - group_id + licenses: + whitelist: + - id + - created_at + - updated_at + merge_request_diffs: + whitelist: + - id + - state + - merge_request_id + - created_at + - updated_at + - base_commit_sha + - real_size + - head_commit_sha + - start_commit_sha + - commits_count + merge_request_metrics: + whitelist: + - id + - merge_request_id + - latest_build_started_at + - latest_build_finished_at + - first_deployed_to_production_at + - merged_at + - created_at + - updated_at + - pipeline_id + - merged_by_id + - latest_closed_by_id + - latest_closed_at + merge_requests: + whitelist: + - id + - target_branch + - source_branch + - source_project_id + - author_id + - assignee_id + - created_at + - updated_at + - milestone_id + - state + - merge_status + - target_project_id + - updated_by_id + - merge_error + - merge_params + - merge_when_pipeline_succeeds + - merge_user_id + - approvals_before_merge + - lock_version + - time_estimate + - squash + - last_edited_at + - last_edited_by_id + - head_pipeline_id + - discussion_locked + - latest_merge_request_diff_id + - allow_maintainer_to_push + merge_requests_closing_issues: + whitelist: + - id + - merge_request_id + - issue_id + - created_at + - updated_at + milestones: + whitelist: + - id + - project_id + - due_date + - created_at + - updated_at + - state + - start_date + - group_id + namespace_statistics: + whitelist: + - id + - namespace_id + - shared_runners_seconds + - shared_runners_seconds_last_reset + namespaces: + whitelist: + - id + - name + - path + - owner_id + - created_at + - updated_at + - type + - avatar + - membership_lock + - share_with_group_lock + - visibility_level + - request_access_enabled + - ldap_sync_status + - ldap_sync_error + - ldap_sync_last_update_at + - ldap_sync_last_successful_update_at + - ldap_sync_last_sync_at + - lfs_enabled + - parent_id + - shared_runners_minutes_limit + - repository_size_limit + - require_two_factor_authentication + - two_factor_grace_period + - plan_id + - project_creation_level + members: + whitelist: + - id + - access_level + - source_id + - source_type + - user_id + - notification_level + - type + - created_by_id + - invite_email + - invite_accepted_at + - requested_at + - expires_at + - ldap + - override + notification_settings: + whitelist: + - id + - user_id + - source_id + - source_type + - level + - created_at + - updated_at + - new_note + - new_issue + - reopen_issue + - close_issue + - reassign_issue + - new_merge_request + - reopen_merge_request + - close_merge_request + - reassign_merge_request + - merge_merge_request + - failed_pipeline + - success_pipeline + project_authorizations: + whitelist: + - user_id + - project_id + - access_level + project_auto_devops: + whitelist: + - id + - project_id + - created_at + - updated_at + - enabled + project_custom_attributes: + whitelist: + - id + - created_at + - updated_at + - project_id + - key + - value + project_features: + whitelist: + - id + - project_id + - merge_requests_access_level + - issues_access_level + - wiki_access_level + - snippets_access_level + - builds_access_level + - created_at + - updated_at + - repository_access_level + project_group_links: + whitelist: + - id + - project_id + - group_id + - created_at + - updated_at + - group_access + - expires_at + project_import_data: + whitelist: + - id + - project_id + project_mirror_data: + whitelist: + - id + - project_id + - retry_count + - last_update_started_at + - last_update_scheduled_at + - next_execution_timestamp + project_repository_states: + whitelist: + - id + - project_id + - repository_verification_checksum + - wiki_verification_checksum + - last_repository_verification_failure + - last_wiki_verification_failure + project_statistics: + whitelist: + - id + - project_id + - namespace_id + - commit_count + - storage_size + - repository_size + - lfs_objects_size + - build_artifacts_size + - shared_runners_seconds + - shared_runners_seconds_last_reset + projects: + whitelist: + - id + - name + - path + - description + - created_at + - updated_at + - creator_id + - namespace_id + - last_activity_at + - import_url + - visibility_level + - archived + - avatar + - merge_requests_template + - star_count + - merge_requests_rebase_enabled + - import_type + - import_source + - approvals_before_merge + - reset_approvals_on_push + - merge_requests_ff_only_enabled + - issues_template + - mirror + - mirror_user_id + - shared_runners_enabled + - build_coverage_regex + - build_allow_git_fetch + - build_timeout + - mirror_trigger_builds + - pending_delete + - public_builds + - last_repository_check_failed + - last_repository_check_at + - container_registry_enabled + - only_allow_merge_if_pipeline_succeeds + - has_external_issue_tracker + - repository_storage + - repository_read_only + - request_access_enabled + - has_external_wiki + - ci_config_path + - lfs_enabled + - only_allow_merge_if_all_discussions_are_resolved + - repository_size_limit + - printing_merge_request_link_enabled + - auto_cancel_pending_pipelines + - service_desk_enabled + - delete_error + - last_repository_updated_at + - disable_overriding_approvers_per_merge_request + - storage_version + - resolve_outdated_diff_discussions + - remote_mirror_available_overridden + - only_mirror_protected_branches + - pull_mirror_available_overridden + - mirror_overwrites_diverged_branches + - external_authorization_classification_label + subscriptions: + whitelist: + - id + - user_id + - subscribable_id + - subscribable_type + - subscribed + - created_at + - updated_at + - project_id + users: + whitelist: + - id + - remember_created_at + - sign_in_count + - current_sign_in_at + - last_sign_in_at + - current_sign_in_ip + - last_sign_in_ip + - created_at + - updated_at + - admin + - projects_limit + - failed_attempts + - locked_at + - can_create_group + - can_create_team + - state + - color_scheme_id + - password_expires_at + - created_by_id + - last_credential_check_at + - avatar + - confirmed_at + - confirmation_sent_at + - unconfirmed_email + - hide_no_ssh_key + - website_url + - admin_email_unsubscribed_at + - notification_email + - hide_no_password + - password_automatically_set + - location + - public_email + - dashboard + - project_view + - consumed_timestep + - layout + - hide_project_limit + - note + - otp_grace_period_started_at + - external + - organization + - auditor + - require_two_factor_authentication_from_group + - two_factor_grace_period + - ghost + - last_activity_on + - notified_of_own_activity + - bot_type + - preferred_language + - theme_id + diff --git a/config/routes.rb b/config/routes.rb index a42fc037227..d208df62b4a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -82,7 +82,11 @@ Rails.application.routes.draw do resources :issues, only: [:index, :create, :update] end - resources :issues, module: :boards, only: [:index, :update] + resources :issues, module: :boards, only: [:index, :update] do + collection do + put :bulk_move, format: :json + end + end Gitlab.ee do resources :users, module: :boards, only: [:index] @@ -106,6 +110,22 @@ Rails.application.routes.draw do draw :jira_connect end + Gitlab.ee do + constraints(::Constraints::FeatureConstrainer.new(:analytics)) do + draw :analytics + end + end + + if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? + resource :chaos, only: [] do + get :leakmem + get :cpu_spin + get :db_spin + get :sleep + get :kill + end + end + if ENV['GITLAB_ENABLE_CHAOS_ENDPOINTS'] get '/chaos/leakmem' => 'chaos#leakmem' get '/chaos/cpuspin' => 'chaos#cpuspin' diff --git a/config/routes/api.rb b/config/routes/api.rb index 3ba9176d943..d55bbdf6776 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -1,7 +1,5 @@ -constraints(::Constraints::FeatureConstrainer.new(:graphql, default_enabled: true)) do - post '/api/graphql', to: 'graphql#execute' - mount GraphiQL::Rails::Engine, at: '/-/graphql-explorer', graphql_path: '/api/graphql' -end +post '/api/graphql', to: 'graphql#execute' +mount GraphiQL::Rails::Engine, at: '/-/graphql-explorer', graphql_path: '/api/graphql' -::API::API.logger Rails.logger +::API::API.logger Rails.logger # rubocop:disable Gitlab/RailsLogger mount ::API::API => '/' diff --git a/config/settings.rb b/config/settings.rb index 6df2132332c..da459afcce2 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -62,6 +62,31 @@ class Settings < Settingslogic (base_url(gitlab) + [gitlab.relative_url_root]).join('') end + def kerberos_protocol + kerberos.https ? "https" : "http" + end + + def kerberos_port + kerberos.use_dedicated_port ? kerberos.port : gitlab.port + end + + # Curl expects username/password for authentication. However when using GSS-Negotiate not credentials should be needed. + # By inserting in the Kerberos dedicated URL ":@", we give to curl an empty username and password and GSS auth goes ahead + # Known bug reported in http://sourceforge.net/p/curl/bugs/440/ and http://curl.haxx.se/docs/knownbugs.html + def build_gitlab_kerberos_url + [ + kerberos_protocol, + "://:@", + gitlab.host, + ":#{kerberos_port}", + gitlab.relative_url_root + ].join('') + end + + def alternative_gitlab_kerberos_url? + kerberos.enabled && (build_gitlab_kerberos_url != build_gitlab_url) + end + # check that values in `current` (string or integer) is a contant in `modul`. def verify_constant_array(modul, current, default) values = default || [] diff --git a/config/webpack.config.js b/config/webpack.config.js index 19b48845305..cd793743eb7 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -322,7 +322,10 @@ module.exports = { }), new webpack.DefinePlugin({ + // This one is used to define window.gon.ee and other things properly in tests: 'process.env.IS_GITLAB_EE': JSON.stringify(IS_EE), + // This one is used to check against "EE" properly in application code + IS_EE: IS_EE ? 'window.gon && window.gon.ee' : JSON.stringify(false), }), ].filter(Boolean), @@ -341,6 +344,8 @@ module.exports = { devtool: NO_SOURCEMAPS ? false : devtool, - // sqljs requires fs - node: { fs: 'empty' }, + node: { + fs: 'empty', // sqljs requires fs + setImmediate: false, + }, }; diff --git a/danger/only_documentation/Dangerfile b/danger/only_documentation/Dangerfile index 8e4564f22b6..ff65f8713d2 100644 --- a/danger/only_documentation/Dangerfile +++ b/danger/only_documentation/Dangerfile @@ -1,7 +1,7 @@ # rubocop:disable Style/SignalException # frozen_string_literal: true -has_only_docs_changes = helper.all_changed_files.all? { |file| file.start_with?('doc/') } +has_only_docs_changes = helper.all_changed_files.all? { |file| file.start_with?('doc/', '.gitlab/ci/docs.gitlab-ci.yml', '.mdlrc') } is_docs_only_branch = gitlab.branch_for_head =~ /(^docs[\/-].*|.*-docs$)/ if is_docs_only_branch && !has_only_docs_changes diff --git a/db/fixtures/development/99_common_metrics.rb b/db/fixtures/development/99_common_metrics.rb index 1f39c0ce5a0..d52f78ea536 100644 --- a/db/fixtures/development/99_common_metrics.rb +++ b/db/fixtures/development/99_common_metrics.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -require Rails.root.join('db/importers/common_metrics_importer.rb') - -::Importers::CommonMetricsImporter.new.execute +::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute diff --git a/db/fixtures/production/999_common_metrics.rb b/db/fixtures/production/999_common_metrics.rb index 1f39c0ce5a0..d52f78ea536 100644 --- a/db/fixtures/production/999_common_metrics.rb +++ b/db/fixtures/production/999_common_metrics.rb @@ -1,5 +1,3 @@ # frozen_string_literal: true -require Rails.root.join('db/importers/common_metrics_importer.rb') - -::Importers::CommonMetricsImporter.new.execute +::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute diff --git a/db/importers/common_metrics_importer.rb b/db/importers/common_metrics_importer.rb deleted file mode 100644 index 195bde8f34a..00000000000 --- a/db/importers/common_metrics_importer.rb +++ /dev/null @@ -1,105 +0,0 @@ -# frozen_string_literal: true - -module Importers - class PrometheusMetric < ActiveRecord::Base - enum group: { - # built-in groups - nginx_ingress_vts: -1, - ha_proxy: -2, - aws_elb: -3, - nginx: -4, - kubernetes: -5, - nginx_ingress: -6, - - # custom groups - business: 0, - response: 1, - system: 2 - } - - scope :common, -> { where(common: true) } - - GROUP_TITLES = { - business: _('Business metrics (Custom)'), - response: _('Response metrics (Custom)'), - system: _('System metrics (Custom)'), - nginx_ingress_vts: _('Response metrics (NGINX Ingress VTS)'), - nginx_ingress: _('Response metrics (NGINX Ingress)'), - ha_proxy: _('Response metrics (HA Proxy)'), - aws_elb: _('Response metrics (AWS ELB)'), - nginx: _('Response metrics (NGINX)'), - kubernetes: _('System metrics (Kubernetes)') - }.freeze - end - - class CommonMetricsImporter - MissingQueryId = Class.new(StandardError) - - attr_reader :content - - def initialize(filename = 'common_metrics.yml') - @content = YAML.load_file(Rails.root.join('config', 'prometheus', filename)) - end - - def execute - PrometheusMetric.reset_column_information - - process_content do |id, attributes| - find_or_build_metric!(id) - .update!(**attributes) - end - end - - private - - def process_content(&blk) - content['panel_groups'].map do |group| - process_group(group, &blk) - end - end - - def process_group(group, &blk) - attributes = { - group: find_group_title_key(group['group']) - } - - group['panels'].map do |panel| - process_panel(panel, attributes, &blk) - end - end - - def process_panel(panel, attributes, &blk) - attributes = attributes.merge( - title: panel['title'], - y_label: panel['y_label']) - - panel['metrics'].map do |metric_details| - process_metric_details(metric_details, attributes, &blk) - end - end - - def process_metric_details(metric_details, attributes, &blk) - attributes = attributes.merge( - legend: metric_details['label'], - query: metric_details['query_range'], - unit: metric_details['unit']) - - yield(metric_details['id'], attributes) - end - - def find_or_build_metric!(id) - raise MissingQueryId unless id - - PrometheusMetric.common.find_by(identifier: id) || - PrometheusMetric.new(common: true, identifier: id) - end - - def find_group_title_key(title) - PrometheusMetric.groups[find_group_title(title)] - end - - def find_group_title(title) - PrometheusMetric::GROUP_TITLES.invert[title] - end - end -end diff --git a/db/migrate/20180831164910_import_common_metrics.rb b/db/migrate/20180831164910_import_common_metrics.rb index f67d5f40aad..4e61a25c1ad 100644 --- a/db/migrate/20180831164910_import_common_metrics.rb +++ b/db/migrate/20180831164910_import_common_metrics.rb @@ -3,12 +3,10 @@ class ImportCommonMetrics < ActiveRecord::Migration[4.2] include Gitlab::Database::MigrationHelpers - require Rails.root.join('db/importers/common_metrics_importer.rb') - DOWNTIME = false def up - Importers::CommonMetricsImporter.new.execute + ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute end def down diff --git a/db/migrate/20181006004100_import_common_metrics_nginx_vts.rb b/db/migrate/20181006004100_import_common_metrics_nginx_vts.rb index 5cd312837df..2b238774dca 100644 --- a/db/migrate/20181006004100_import_common_metrics_nginx_vts.rb +++ b/db/migrate/20181006004100_import_common_metrics_nginx_vts.rb @@ -1,12 +1,10 @@ class ImportCommonMetricsNginxVts < ActiveRecord::Migration[5.0] include Gitlab::Database::MigrationHelpers - require Rails.root.join('db/importers/common_metrics_importer.rb') - DOWNTIME = false def up - Importers::CommonMetricsImporter.new.execute + ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute end def down diff --git a/db/migrate/20190326164045_import_common_metrics_knative.rb b/db/migrate/20190326164045_import_common_metrics_knative.rb index 340ec1e1f75..6b331755774 100644 --- a/db/migrate/20190326164045_import_common_metrics_knative.rb +++ b/db/migrate/20190326164045_import_common_metrics_knative.rb @@ -3,12 +3,10 @@ class ImportCommonMetricsKnative < ActiveRecord::Migration[5.0] include Gitlab::Database::MigrationHelpers - require Rails.root.join('db/importers/common_metrics_importer.rb') - DOWNTIME = false def up - Importers::CommonMetricsImporter.new.execute + ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute end def down diff --git a/db/migrate/20190408163745_prometheus_knative05_fix.rb b/db/migrate/20190408163745_prometheus_knative05_fix.rb index c11f6f0e29b..3d0aa782669 100644 --- a/db/migrate/20190408163745_prometheus_knative05_fix.rb +++ b/db/migrate/20190408163745_prometheus_knative05_fix.rb @@ -6,12 +6,10 @@ class PrometheusKnative05Fix < ActiveRecord::Migration[5.0] include Gitlab::Database::MigrationHelpers - require Rails.root.join('db/importers/common_metrics_importer.rb') - DOWNTIME = false def up - Importers::CommonMetricsImporter.new.execute + ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute end def down diff --git a/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb b/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb index fa332fd5c70..325895a5ddb 100644 --- a/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb +++ b/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb @@ -82,7 +82,7 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration[4.2] iteration = 0 records = 0 begin - Rails.logger.info "Building user_interacted_projects table, batch ##{iteration}" + Rails.logger.info "Building user_interacted_projects table, batch ##{iteration}" # rubocop:disable Gitlab/RailsLogger result = execute <<~SQL INSERT INTO user_interacted_projects (user_id, project_id) SELECT e.user_id, e.project_id @@ -93,7 +93,7 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration[4.2] SQL iteration += 1 records += result.cmd_tuples - Rails.logger.info "Building user_interacted_projects table, batch ##{iteration} complete, created #{records} overall" + Rails.logger.info "Building user_interacted_projects table, batch ##{iteration} complete, created #{records} overall" # rubocop:disable Gitlab/RailsLogger Kernel.sleep(SLEEP_TIME) if result.cmd_tuples > 0 end while result.cmd_tuples > 0 end diff --git a/db/post_migrate/20190628191740_schedule_fixing_names_of_user_namespaces.rb b/db/post_migrate/20190628191740_schedule_fixing_names_of_user_namespaces.rb new file mode 100644 index 00000000000..8fa7068b957 --- /dev/null +++ b/db/post_migrate/20190628191740_schedule_fixing_names_of_user_namespaces.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class ScheduleFixingNamesOfUserNamespaces < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + class Namespace < ActiveRecord::Base + include ::EachBatch + + self.table_name = 'namespaces' + + scope :user_namespaces, -> { where(type: nil) } + end + + class Route < ActiveRecord::Base + include ::EachBatch + + self.table_name = 'routes' + + scope :project_routes, -> { where(source_type: 'Project') } + end + + disable_ddl_transaction! + + def up + queue_background_migration_jobs_by_range_at_intervals( + ScheduleFixingNamesOfUserNamespaces::Namespace.user_namespaces, + 'FixUserNamespaceNames', + 60.seconds, + batch_size: 5000 + ) + + queue_background_migration_jobs_by_range_at_intervals( + ScheduleFixingNamesOfUserNamespaces::Route.project_routes, + 'FixUserProjectRouteNames', + 60.seconds, + batch_size: 5000 + ) + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20190702173936_populate_remaining_merge_request_assignees.rb b/db/post_migrate/20190702173936_populate_remaining_merge_request_assignees.rb new file mode 100644 index 00000000000..c435b94015d --- /dev/null +++ b/db/post_migrate/20190702173936_populate_remaining_merge_request_assignees.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class PopulateRemainingMergeRequestAssignees < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + BATCH_SIZE = 10_000 + MIGRATION = 'PopulateMergeRequestAssigneesTable' + + disable_ddl_transaction! + + def up + Gitlab::BackgroundMigration.steal(MIGRATION) + + Gitlab::BackgroundMigration::PopulateMergeRequestAssigneesTable.new.perform_all_sync(batch_size: BATCH_SIZE) + end +end diff --git a/db/schema.rb b/db/schema.rb index 8c085d64e27..8188c2263ba 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,11 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20190703130053) do +ActiveRecord::Schema.define(version: 2019_07_03_130053) do # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" enable_extension "pg_trgm" + enable_extension "plpgsql" create_table "abuse_reports", id: :serial, force: :cascade do |t| t.integer "reporter_id" diff --git a/doc/README.md b/doc/README.md index 5eaa998a7b8..25db0efb960 100644 --- a/doc/README.md +++ b/doc/README.md @@ -86,7 +86,7 @@ The following documentation relates to the DevOps **Manage** stage: | Manage Topics | Description | |:--------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [Authentication and<br/>Authorization](administration/auth/README.md) **[CORE ONLY]** | Supported authentication and authorization providers. | +| [Authentication and<br/>Authorization](administration/auth/README.md) **(CORE ONLY)** | Supported authentication and authorization providers. | | [GitLab Cycle Analytics](user/project/cycle_analytics.md) | Measure the time it takes to go from an [idea to production](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab) for each project you have. | | [Instance Statistics](user/instance_statistics/index.md) | Discover statistics on how many GitLab features you use and user activity. | @@ -107,18 +107,18 @@ The following documentation relates to the DevOps **Plan** stage: | Plan Topics | Description | |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------| -| [Burndown Charts](user/project/milestones/burndown_charts.md) **[STARTER]** | Watch your project's progress throughout a specific milestone. | +| [Burndown Charts](user/project/milestones/burndown_charts.md) **(STARTER)** | Watch your project's progress throughout a specific milestone. | | [Discussions](user/discussions/index.md) | Threads, comments, and resolvable discussions in issues, commits, and merge requests. | | [Due Dates](user/project/issues/due_dates.md) | Keep track of issue deadlines. | -| [Epics](user/group/epics/index.md) **[ULTIMATE]** | Tracking groups of issues that share a theme. | +| [Epics](user/group/epics/index.md) **(ULTIMATE)** | Tracking groups of issues that share a theme. | | [Issues](user/project/issues/index.md), including [confidential issues](user/project/issues/confidential_issues.md),<br/>[issue and merge request templates](user/project/description_templates.md),<br/>and [moving issues](user/project/issues/managing_issues.md#moving-issues) | Project issues, restricting access to issues, create templates for submitting new issues and merge requests, and moving issues between projects. | | [Labels](user/project/labels.md) | Categorize issues or merge requests with descriptive labels. | | [Milestones](user/project/milestones/index.md) | Set milestones for delivery of issues and merge requests, with optional due date. | | [Project Issue Board](user/project/issue_board.md) | Display issues on a Scrum or Kanban board. | | [Quick Actions](user/project/quick_actions.md) | Shortcuts for common actions on issues or merge requests, replacing the need to click buttons or use dropdowns in GitLab's UI. | -| [Related Issues](user/project/issues/related_issues.md) **[STARTER]** | Create a relationship between issues. | -| [Roadmap](user/group/roadmap/index.md) **[ULTIMATE]** | Visualize epic timelines. | -| [Service Desk](user/project/service_desk.md) **[PREMIUM]** | A simple way to allow people to create issues in your GitLab instance without needing their own user account. | +| [Related Issues](user/project/issues/related_issues.md) **(STARTER)** | Create a relationship between issues. | +| [Roadmap](user/group/roadmap/index.md) **(ULTIMATE)** | Visualize epic timelines. | +| [Service Desk](user/project/service_desk.md) **(PREMIUM)** | A simple way to allow people to create issues in your GitLab instance without needing their own user account. | | [Time Tracking](workflow/time_tracking.md) | Track time spent on issues and merge requests. | | [Todos](workflow/todos.md) | Keep track of work requiring attention with a chronological list displayed on a simple dashboard. | @@ -143,14 +143,14 @@ The following documentation relates to the DevOps **Create** stage: | Create Topics - Projects and Groups | Description | |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------| -| [Advanced global search](user/search/advanced_global_search.md) **[STARTER]** | Leverage Elasticsearch for faster, more advanced code search across your entire GitLab instance. | -| [Advanced syntax search](user/search/advanced_search_syntax.md) **[STARTER]** | Use advanced queries for more targeted search results. | -| [Contribution analytics](user/group/contribution_analytics/index.md) **[STARTER]** | See detailed statistics of group contributors. | +| [Advanced global search](user/search/advanced_global_search.md) **(STARTER)** | Leverage Elasticsearch for faster, more advanced code search across your entire GitLab instance. | +| [Advanced syntax search](user/search/advanced_search_syntax.md) **(STARTER)** | Use advanced queries for more targeted search results. | +| [Contribution analytics](user/group/contribution_analytics/index.md) **(STARTER)** | See detailed statistics of group contributors. | | [Create](gitlab-basics/create-project.md) and [fork](gitlab-basics/fork-project.md) projects, and<br/>[import and export projects<br/>between instances](user/project/settings/import_export.md) | Create, duplicate, and move projects. | -| [File locking](user/project/file_lock.md) **[PREMIUM]** | Lock files to avoid merge conflicts. | +| [File locking](user/project/file_lock.md) **(PREMIUM)** | Lock files to avoid merge conflicts. | | [GitLab Pages](user/project/pages/index.md) | Build, test, and deploy your static website with GitLab Pages. | | [Groups](user/group/index.md) and [Subgroups](user/group/subgroups/index.md) | Organize your projects in groups. | -| [Issues Analytics](user/group/issues_analytics/index.md) **[PREMIUM]** | Check how many issues were created per month. | +| [Issues Analytics](user/group/issues_analytics/index.md) **(PREMIUM)** | Check how many issues were created per month. | | [Projects](user/project/index.md), including [project access](public_access/public_access.md)<br/>and [settings](user/project/settings/index.md) | Host source code, and control your project's visibility and set configuration. | | [Search through GitLab](user/search/index.md) | Search for issues, merge requests, projects, groups, and todos. | | [Snippets](user/snippets.md) | Snippets allow you to create little bits of code. | @@ -175,9 +175,9 @@ The following documentation relates to the DevOps **Create** stage: | [Files](user/project/repository/index.md#files) | Files management. | | [Jupyter Notebook files](user/project/repository/index.md#jupyter-notebook-files) | GitLab's support for `.ipynb` files. | | [Protected branches](user/project/protected_branches.md) | Use protected branches. | -| [Push rules](push_rules/push_rules.md) **[STARTER]** | Additional control over pushes to your projects. | +| [Push rules](push_rules/push_rules.md) **(STARTER)** | Additional control over pushes to your projects. | | [Repositories](user/project/repository/index.md) | Manage source code repositories in GitLab's user interface. | -| [Repository mirroring](workflow/repository_mirroring.md) **[STARTER]** | Push to or pull from repositories outside of GitLab | +| [Repository mirroring](workflow/repository_mirroring.md) **(STARTER)** | Push to or pull from repositories outside of GitLab | | [Start a merge request](user/project/repository/web_editor.md#tips) | Start merge request when committing via GitLab's user interface. | <div align="right"> @@ -209,7 +209,7 @@ The following documentation relates to the DevOps **Create** stage: | [GitLab API](api/README.md) | Integrate GitLab via a simple and powerful API. | | [GitLab Integration](integration/README.md) | Integrate with multiple third-party services with GitLab to allow external issue trackers and external authentication. | | [GitLab Webhooks](user/project/integrations/webhooks.md) | Let GitLab notify you when new code has been pushed to your project. | -| [Jira Development Panel](integration/jira_development_panel.md) **[PREMIUM]** | See GitLab information in the Jira Development Panel. | +| [Jira Development Panel](integration/jira_development_panel.md) **(PREMIUM)** | See GitLab information in the Jira Development Panel. | | [Project Services](user/project/integrations/project_services.md) | Integrate a project with external services, such as CI and chat. | | [Trello Power-Up](integration/trello_power_up.md) | Integrate with GitLab's Trello Power-Up. | @@ -233,10 +233,10 @@ The following documentation relates to the DevOps **Verify** stage: | Verify Topics | Description | |:----------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------| -| [Code Quality reports](user/project/merge_requests/code_quality.md) **[STARTER]** | Analyze source code quality. | +| [Code Quality reports](user/project/merge_requests/code_quality.md) **(STARTER)** | Analyze source code quality. | | [GitLab CI/CD](ci/README.md) | Explore the features and capabilities of Continuous Integration with GitLab. | | [JUnit test reports](ci/junit_test_reports.md) | Display JUnit test reports on merge requests. | -| [Multi-project pipelines](ci/multi_project_pipelines.md) **[PREMIUM]** | Visualize entire pipelines that span multiple projects, including all cross-project inter-dependencies. | +| [Multi-project pipelines](ci/multi_project_pipelines.md) **(PREMIUM)** | Visualize entire pipelines that span multiple projects, including all cross-project inter-dependencies. | | [Pipeline Graphs](ci/pipelines.md#visualizing-pipelines) | Visualize builds. | | [Review Apps](ci/review_apps/index.md) | Preview changes to your application right from a merge request. | @@ -257,7 +257,7 @@ The following documentation relates to the DevOps **Package** stage: | Package Topics | Description | |:----------------------------------------------------------------|:-------------------------------------------------------| | [GitLab Container Registry](user/project/container_registry.md) | Learn how to use GitLab's built-in Container Registry. | -| [GitLab Packages](administration/packages.md) **[PREMIUM]** | Use GitLab as an NPM registry or Maven repository. | +| [GitLab Packages](administration/packages.md) **(PREMIUM)** | Use GitLab as an NPM registry or Maven repository. | <div align="right"> <a type="button" class="btn btn-default" href="#overview"> @@ -276,10 +276,10 @@ The following documentation relates to the DevOps **Release** stage: | Release Topics | Description | |:------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------| | [Auto Deploy](topics/autodevops/index.md#auto-deploy) | Configure GitLab for the deployment of your application. | -| [Canary Deployments](user/project/canary_deployments.md) **[PREMIUM]** | Employ a popular CI strategy where a small portion of the fleet is updated to the new version first. | -| [Deploy Boards](user/project/deploy_boards.md) **[PREMIUM]** | View the current health and status of each CI environment running on Kubernetes, displaying the status of the pods in the deployment. | +| [Canary Deployments](user/project/canary_deployments.md) **(PREMIUM)** | Employ a popular CI strategy where a small portion of the fleet is updated to the new version first. | +| [Deploy Boards](user/project/deploy_boards.md) **(PREMIUM)** | View the current health and status of each CI environment running on Kubernetes, displaying the status of the pods in the deployment. | | [Environments and deployments](ci/environments.md) | With environments, you can control the continuous deployment of your software within GitLab. | -| [Environment-specific variables](ci/variables/README.md#limiting-environment-scopes-of-environment-variables-premium) **[PREMIUM]** | Limit scope of variables to specific environments. | +| [Environment-specific variables](ci/variables/README.md#limiting-environment-scopes-of-environment-variables-premium) **(PREMIUM)** | Limit scope of variables to specific environments. | | [GitLab CI/CD](ci/README.md) | Explore the features and capabilities of Continuous Deployment and Delivery with GitLab. | | [GitLab Pages](user/project/pages/index.md) | Build, test, and deploy a static site directly from GitLab. | | [Protected Runners](ci/runners/README.md#protected-runners) | Select Runners to only pick jobs for protected branches and tags. | @@ -307,7 +307,7 @@ The following documentation relates to the DevOps **Configure** stage: | [GitLab ChatOps](ci/chatops/README.md) | Interact with CI/CD jobs through chat services. | | [Installing Applications](user/project/clusters/index.md#installing-applications) | Deploy Helm, Ingress, and Prometheus on Kubernetes. | | [Mattermost slash commands](user/project/integrations/mattermost_slash_commands.md) | Enable and use slash commands from within Mattermost. | -| [Multiple Kubernetes Clusters](user/project/clusters/index.md#multiple-kubernetes-clusters-premium) **[PREMIUM]** | Associate more than one Kubernetes clusters to your project. | +| [Multiple Kubernetes Clusters](user/project/clusters/index.md#multiple-kubernetes-clusters-premium) **(PREMIUM)** | Associate more than one Kubernetes clusters to your project. | | [Protected variables](ci/variables/README.md#protected-environment-variables) | Restrict variables to protected branches and tags. | | [Serverless](user/project/clusters/serverless/index.md) | Run serverless workloads on Kubernetes. | | [Slack slash commands](user/project/integrations/slack_slash_commands.md) | Enable and use slash commands from within Slack. | @@ -329,8 +329,8 @@ The following documentation relates to the DevOps **Monitor** stage: | Monitor Topics | Description | |:------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------| -| [GitLab Performance Monitoring](administration/monitoring/performance/index.md) **[CORE ONLY]** | Use InfluxDB and Grafana to monitor the performance of your GitLab instance (will be eventually replaced by Prometheus). | -| [GitLab Prometheus](administration/monitoring/prometheus/index.md) **[CORE ONLY]** | Configure the bundled Prometheus to collect various metrics from your GitLab instance. | +| [GitLab Performance Monitoring](administration/monitoring/performance/index.md) **(CORE ONLY)** | Use InfluxDB and Grafana to monitor the performance of your GitLab instance (will be eventually replaced by Prometheus). | +| [GitLab Prometheus](administration/monitoring/prometheus/index.md) **(CORE ONLY)** | Configure the bundled Prometheus to collect various metrics from your GitLab instance. | | [Health check](user/admin_area/monitoring/health_check.md) | GitLab provides liveness and readiness probes to indicate service health and reachability to required services. | | [Prometheus project integration](user/project/integrations/prometheus.md) | Configure the Prometheus integration per project and monitor your CI/CD environments. | | [Prometheus metrics](user/project/integrations/prometheus_library/index.md) | Let Prometheus collect metrics from various services, like Kubernetes, NGINX, NGINX ingress controller, HAProxy, and Amazon Cloud Watch. | @@ -353,13 +353,13 @@ The following documentation relates to the DevOps **Secure** stage: | Secure Topics | Description | |:------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------| -| [Container Scanning](user/application_security/container_scanning/index.md) **[ULTIMATE]** | Use Clair to scan docker images for known vulnerabilities. | -| [Dependency Scanning](user/application_security/dependency_scanning/index.md) **[ULTIMATE]** | Analyze your dependencies for known vulnerabilities. | -| [Dynamic Application Security Testing (DAST)](user/application_security/dast/index.md) **[ULTIMATE]** | Analyze running web applications for known vulnerabilities. | -| [Group Security Dashboard](user/application_security/security_dashboard/index.md) **[ULTIMATE]** | View vulnerabilities in all the projects in a group and its subgroups. | -| [License Management](user/application_security/license_management/index.md) **[ULTIMATE]** | Search your project's dependencies for their licenses. | -| [Project Security Dashboard](user/application_security/security_dashboard/index.md) **[ULTIMATE]** | View the latest security reports for your project. | -| [Static Application Security Testing (SAST)](user/application_security/sast/index.md) **[ULTIMATE]** | Analyze source code for known vulnerabilities. | +| [Container Scanning](user/application_security/container_scanning/index.md) **(ULTIMATE)** | Use Clair to scan docker images for known vulnerabilities. | +| [Dependency Scanning](user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. | +| [Dynamic Application Security Testing (DAST)](user/application_security/dast/index.md) **(ULTIMATE)** | Analyze running web applications for known vulnerabilities. | +| [Group Security Dashboard](user/application_security/security_dashboard/index.md) **(ULTIMATE)** | View vulnerabilities in all the projects in a group and its subgroups. | +| [License Management](user/application_security/license_management/index.md) **(ULTIMATE)** | Search your project's dependencies for their licenses. | +| [Project Security Dashboard](user/application_security/security_dashboard/index.md) **(ULTIMATE)** | View the latest security reports for your project. | +| [Static Application Security Testing (SAST)](user/application_security/sast/index.md) **(ULTIMATE)** | Analyze source code for known vulnerabilities. | ## Subscribe to GitLab diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md index d7a2e13b53e..a80ff330e03 100644 --- a/doc/administration/audit_events.md +++ b/doc/administration/audit_events.md @@ -2,7 +2,7 @@ last_updated: 2019-02-04 --- -# Audit Events **[STARTER]** +# Audit Events **(STARTER)** GitLab offers a way to view the changes made within the GitLab server for owners and administrators on a [paid plan][ee]. @@ -32,7 +32,7 @@ There are two kinds of events logged: - Instance events scoped to the whole GitLab instance, used by your Compliance team to perform formal audits. -### Group events **[STARTER]** +### Group events **(STARTER)** NOTE: **Note:** You need Owner [permissions] to view the group Audit Events page. @@ -59,7 +59,7 @@ From there, you can see the following actions: - 2FA enforcement/grace period changed - Roles allowed to create project changed -### Project events **[STARTER]** +### Project events **(STARTER)** NOTE: **Note:** You need Maintainer [permissions] or higher to view the project Audit Events page. @@ -74,7 +74,7 @@ From there, you can see the following actions: - Permission changes of a user assigned to a project - User was removed from project -### Instance events **[PREMIUM ONLY]** +### Instance events **(PREMIUM ONLY)** > [Introduced][ee-2336] in [GitLab Premium][ee] 9.3. @@ -99,7 +99,7 @@ It is possible to filter particular actions by choosing an audit data type from the filter drop-down. You can further filter by specific group, project or user (for authentication events). -![audit log](audit_log.png) +![audit log](img/audit_log.png) ### Missing events diff --git a/doc/administration/auditor_users.md b/doc/administration/auditor_users.md index ef8c8197d6d..65d36612d85 100644 --- a/doc/administration/auditor_users.md +++ b/doc/administration/auditor_users.md @@ -1,4 +1,4 @@ -# Auditor users **[PREMIUM ONLY]** +# Auditor users **(PREMIUM ONLY)** >[Introduced][ee-998] in [GitLab Premium][eep] 8.17. @@ -52,7 +52,7 @@ section. **Admin Area > Users**. You will find the option of the access level under the 'Access' section. - ![Admin Area Form](auditor_access_form.png) + ![Admin Area Form](img/auditor_access_form.png) 1. Click **Save changes** or **Create user** for the changes to take effect. diff --git a/doc/administration/auth/README.md b/doc/administration/auth/README.md index e215a0df6ec..d8094587d14 100644 --- a/doc/administration/auth/README.md +++ b/doc/administration/auth/README.md @@ -9,11 +9,11 @@ providers. - [LDAP](ldap.md) Includes Active Directory, Apple Open Directory, Open LDAP, and 389 Server - - [LDAP for GitLab EE](ldap-ee.md): LDAP additions to GitLab Enterprise Editions **[STARTER ONLY]** + - [LDAP for GitLab EE](ldap-ee.md): LDAP additions to GitLab Enterprise Editions **(STARTER ONLY)** - [OmniAuth](../../integration/omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, Crowd, Azure, Authentiq ID, and JWT - [CAS](../../integration/cas.md) Configure GitLab to sign in using CAS - [SAML](../../integration/saml.md) Configure GitLab as a SAML 2.0 Service Provider - [Okta](okta.md) Configure GitLab to sign in using Okta - [Authentiq](authentiq.md): Enable the Authentiq OmniAuth provider for passwordless authentication -- [Smartcard](smartcard.md) Smartcard authentication **[PREMIUM ONLY]** +- [Smartcard](smartcard.md) Smartcard authentication **(PREMIUM ONLY)** diff --git a/doc/administration/auth/authentiq.md b/doc/administration/auth/authentiq.md index 726622d8599..835c97c0288 100644 --- a/doc/administration/auth/authentiq.md +++ b/doc/administration/auth/authentiq.md @@ -8,47 +8,48 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t 1. On your GitLab server, open the configuration file: - For omnibus installation - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + For omnibus installation - For installations from source: + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - ```sh - sudo -u git -H editor /home/git/gitlab/config/gitlab.yml - ``` + For installations from source: + + ```sh + sudo -u git -H editor /home/git/gitlab/config/gitlab.yml + ``` 1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings to enable single sign-on and add Authentiq as an OAuth provider. 1. Add the provider configuration for Authentiq: - For Omnibus packages: - - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "authentiq", - "app_id" => "YOUR_CLIENT_ID", - "app_secret" => "YOUR_CLIENT_SECRET", - "args" => { - "scope": 'aq:name email~rs address aq:push' - } - } - ] - ``` - - For installations from source: - - ```yaml - - { name: 'authentiq', - app_id: 'YOUR_CLIENT_ID', - app_secret: 'YOUR_CLIENT_SECRET', - args: { - scope: 'aq:name email~rs address aq:push' - } - } - ``` + For Omnibus packages: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "authentiq", + "app_id" => "YOUR_CLIENT_ID", + "app_secret" => "YOUR_CLIENT_SECRET", + "args" => { + "scope": 'aq:name email~rs address aq:push' + } + } + ] + ``` + + For installations from source: + + ```yaml + - { name: 'authentiq', + app_id: 'YOUR_CLIENT_ID', + app_secret: 'YOUR_CLIENT_SECRET', + args: { + scope: 'aq:name email~rs address aq:push' + } + } + ``` 1. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits. See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq/wiki/Scopes,-callback-url-configuration-and-responses) for more information on scopes and modifiers. diff --git a/doc/administration/auth/crowd.md b/doc/administration/auth/crowd.md index 6db74958d5a..86c7bad2ebf 100644 --- a/doc/administration/auth/crowd.md +++ b/doc/administration/auth/crowd.md @@ -6,55 +6,56 @@ 1. Go through the 'Add application' steps, entering the appropriate details. The screenshot below shows an example configuration. - ![Example Crowd application configuration](img/crowd_application.png) + ![Example Crowd application configuration](img/crowd_application.png) ## Configure GitLab 1. On your GitLab server, open the configuration file. - **Omnibus:** + **Omnibus:** - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - **Source:** + **Source:** - ```sh - cd /home/git/gitlab + ```sh + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + sudo -u git -H editor config/gitlab.yml + ``` 1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings. 1. Add the provider configuration: - **Omnibus:** - - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "crowd", - "args" => { - "crowd_server_url" => "CROWD_SERVER_URL", - "application_name" => "YOUR_APP_NAME", - "application_password" => "YOUR_APP_PASSWORD" - } - } - ] - ``` - - **Source:** - - ``` - - { name: 'crowd', - args: { - crowd_server_url: 'CROWD_SERVER_URL', - application_name: 'YOUR_APP_NAME', - application_password: 'YOUR_APP_PASSWORD' } } - ``` + **Omnibus:** + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "crowd", + "args" => { + "crowd_server_url" => "CROWD_SERVER_URL", + "application_name" => "YOUR_APP_NAME", + "application_password" => "YOUR_APP_PASSWORD" + } + } + ] + ``` + + **Source:** + + ``` + - { name: 'crowd', + args: { + crowd_server_url: 'CROWD_SERVER_URL', + application_name: 'YOUR_APP_NAME', + application_password: 'YOUR_APP_PASSWORD' } } + ``` + 1. Change `CROWD_SERVER_URL` to the URL of your Crowd server. 1. Change `YOUR_APP_NAME` to the application name from Crowd applications page. 1. Change `YOUR_APP_PASSWORD` to the application password you've set. @@ -77,4 +78,4 @@ could not authorize you from Crowd because invalid credentials Please make sure the Crowd users who need to login to GitLab are authorized to [the application](#configure-a-new-crowd-application) in the step of **Authorisation**. This could be verified by try "Authentication test" for Crowd as of 2.11. -![Example Crowd application authorisation configuration](img/crowd_application_authorisation.png)
\ No newline at end of file +![Example Crowd application authorisation configuration](img/crowd_application_authorisation.png) diff --git a/doc/administration/auth/google_secure_ldap.md b/doc/administration/auth/google_secure_ldap.md index c668f19ca7d..0e6d7ff1df1 100644 --- a/doc/administration/auth/google_secure_ldap.md +++ b/doc/administration/auth/google_secure_ldap.md @@ -1,4 +1,4 @@ -# Google Secure LDAP **[CORE ONLY]** +# Google Secure LDAP **(CORE ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/46391) in GitLab 11.9. @@ -13,7 +13,7 @@ The steps below cover: ## Configuring Google LDAP client -1. Navigate to <https://admin.google.com> and sign in as a GSuite domain administrator. +1. Navigate to <https://admin.google.com/Dashboard> and sign in as a GSuite domain administrator. 1. Go to **Apps > LDAP > Add Client**. @@ -66,7 +66,7 @@ values obtained during the LDAP client configuration earlier: 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby + ```ruby gitlab_rails['ldap_enabled'] = true gitlab_rails['ldap_servers'] = YAML.load <<-EOS # remember to close this block with 'EOS' below main: # 'main' is the GitLab 'provider ID' of this LDAP server @@ -127,7 +127,7 @@ values obtained during the LDAP client configuration earlier: AcZSFJQjdg5BTyvdEDhaYUKGdRw= -----END PRIVATE KEY----- EOS - ``` + ``` 1. Save the file and [reconfigure] GitLab for the changes to take effect. @@ -137,7 +137,7 @@ values obtained during the LDAP client configuration earlier: 1. Edit `config/gitlab.yml`: - ```yaml + ```yaml ldap: enabled: true servers: @@ -202,6 +202,5 @@ values obtained during the LDAP client configuration earlier: 1. Save the file and [restart] GitLab for the changes to take effect. - [reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure [restart]: ../restart_gitlab.md#installations-from-source diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md index 1f67e8f5744..320a65b665d 100644 --- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md +++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md @@ -111,7 +111,7 @@ The initial configuration of LDAP in GitLab requires changes to the `gitlab.rb` The two Active Directory specific values are `active_directory: true` and `uid: 'sAMAccountName'`. `sAMAccountName` is an attribute returned by Active Directory used for GitLab usernames. See the example output from `ldapsearch` for a full list of attributes a "person" object (user) has in **AD** - [`ldapsearch` example](#using-ldapsearch-unix) -> Both group_base and admin_group configuration options are only available in GitLab Enterprise Edition. See [GitLab EE - LDAP Features](../how_to_configure_ldap_gitlab_ee/index.html#gitlab-enterprise-edition---ldap-features) **[STARTER ONLY]** +> Both group_base and admin_group configuration options are only available in GitLab Enterprise Edition. See [GitLab EE - LDAP Features](../how_to_configure_ldap_gitlab_ee/index.html#gitlab-enterprise-edition---ldap-features) **(STARTER ONLY)** ### Example `gitlab.rb` LDAP @@ -267,4 +267,4 @@ have extended functionalities with LDAP, such as: - Updating user permissions - Multiple LDAP servers -Read through the article on [LDAP for GitLab EE](../how_to_configure_ldap_gitlab_ee/index.md) **[STARTER ONLY]** for an overview. +Read through the article on [LDAP for GitLab EE](../how_to_configure_ldap_gitlab_ee/index.md) **(STARTER ONLY)** for an overview. diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md index 4d82a7370bb..2683950f143 100644 --- a/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md +++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md @@ -6,7 +6,7 @@ article_type: admin guide date: 2017-05-03 --- -# How to configure LDAP with GitLab EE **[STARTER ONLY]** +# How to configure LDAP with GitLab EE **(STARTER ONLY)** ## Introduction diff --git a/doc/administration/auth/jwt.md b/doc/administration/auth/jwt.md index 497298503ad..7db22bdd5df 100644 --- a/doc/administration/auth/jwt.md +++ b/doc/administration/auth/jwt.md @@ -3,65 +3,65 @@ To enable the JWT OmniAuth provider, you must register your application with JWT. JWT will provide you with a secret key for you to use. -1. On your GitLab server, open the configuration file. +1. On your GitLab server, open the configuration file. - For Omnibus GitLab: + For Omnibus GitLab: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + ```sh + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml + ``` -1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings. -1. Add the provider configuration. +1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings. +1. Add the provider configuration. - For Omnibus GitLab: + For Omnibus GitLab: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { name: 'jwt', - args: { - secret: 'YOUR_APP_SECRET', - algorithm: 'HS256', # Supported algorithms: 'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512' - uid_claim: 'email', - required_claims: ['name', 'email'], - info_maps: { name: 'name', email: 'email' }, - auth_url: 'https://example.com/', - valid_within: 3600 # 1 hour - } - } - ] - ``` + ```ruby + gitlab_rails['omniauth_providers'] = [ + { name: 'jwt', + args: { + secret: 'YOUR_APP_SECRET', + algorithm: 'HS256', # Supported algorithms: 'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512' + uid_claim: 'email', + required_claims: ['name', 'email'], + info_maps: { name: 'name', email: 'email' }, + auth_url: 'https://example.com/', + valid_within: 3600 # 1 hour + } + } + ] + ``` - For installation from source: + For installation from source: - ``` - - { name: 'jwt', - args: { - secret: 'YOUR_APP_SECRET', - algorithm: 'HS256', # Supported algorithms: 'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512' - uid_claim: 'email', - required_claims: ['name', 'email'], - info_map: { name: 'name', email: 'email' }, - auth_url: 'https://example.com/', - valid_within: 3600 # 1 hour - } - } - ``` + ``` + - { name: 'jwt', + args: { + secret: 'YOUR_APP_SECRET', + algorithm: 'HS256', # Supported algorithms: 'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512' + uid_claim: 'email', + required_claims: ['name', 'email'], + info_map: { name: 'name', email: 'email' }, + auth_url: 'https://example.com/', + valid_within: 3600 # 1 hour + } + } + ``` - NOTE: **Note:** For more information on each configuration option refer to - the [OmniAuth JWT usage documentation](https://github.com/mbleigh/omniauth-jwt#usage). + NOTE: **Note:** For more information on each configuration option refer to + the [OmniAuth JWT usage documentation](https://github.com/mbleigh/omniauth-jwt#usage). -1. Change `YOUR_APP_SECRET` to the client secret and set `auth_url` to your redirect URL. -1. Save the configuration file. -1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. Change `YOUR_APP_SECRET` to the client secret and set `auth_url` to your redirect URL. +1. Save the configuration file. +1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page there should now be a JWT icon below the regular sign in form. Click the icon to begin the authentication process. JWT will ask the user to diff --git a/doc/administration/auth/ldap-ee.md b/doc/administration/auth/ldap-ee.md index b45966fa920..2afac23c20c 100644 --- a/doc/administration/auth/ldap-ee.md +++ b/doc/administration/auth/ldap-ee.md @@ -1,4 +1,4 @@ -# LDAP Additions in GitLab EE **[STARTER ONLY]** +# LDAP Additions in GitLab EE **(STARTER ONLY)** This is a continuation of the main [LDAP documentation](ldap.md), detailing LDAP features specific to GitLab Enterprise Edition Starter, Premium and Ultimate. @@ -85,19 +85,19 @@ following. 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['ldap_servers'] = YAML.load <<-EOS - main: - ## snip... - ## - ## Base where we can search for groups - ## - ## Ex. ou=groups,dc=gitlab,dc=example - ## - ## - group_base: ou=groups,dc=example,dc=com - EOS - ``` + ```ruby + gitlab_rails['ldap_servers'] = YAML.load <<-EOS + main: + ## snip... + ## + ## Base where we can search for groups + ## + ## Ex. ou=groups,dc=gitlab,dc=example + ## + ## + group_base: ou=groups,dc=example,dc=com + EOS + ``` 1. [Reconfigure GitLab][reconfigure] for the changes to take effect. @@ -105,14 +105,14 @@ following. 1. Edit `/home/git/gitlab/config/gitlab.yml`: - ```yaml - production: - ldap: - servers: - main: - # snip... - group_base: ou=groups,dc=example,dc=com - ``` + ```yaml + production: + ldap: + servers: + main: + # snip... + group_base: ou=groups,dc=example,dc=com + ``` 1. [Restart GitLab][restart] for the changes to take effect. @@ -140,30 +140,30 @@ group, as opposed to the full DN. 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['ldap_servers'] = YAML.load <<-EOS - main: - ## snip... - ## - ## Base where we can search for groups - ## - ## Ex. ou=groups,dc=gitlab,dc=example - ## - ## - group_base: ou=groups,dc=example,dc=com - - ## - ## The CN of a group containing GitLab administrators - ## - ## Ex. administrators - ## - ## Note: Not `cn=administrators` or the full DN - ## - ## - admin_group: my_admin_group - - EOS - ``` + ```ruby + gitlab_rails['ldap_servers'] = YAML.load <<-EOS + main: + ## snip... + ## + ## Base where we can search for groups + ## + ## Ex. ou=groups,dc=gitlab,dc=example + ## + ## + group_base: ou=groups,dc=example,dc=com + + ## + ## The CN of a group containing GitLab administrators + ## + ## Ex. administrators + ## + ## Note: Not `cn=administrators` or the full DN + ## + ## + admin_group: my_admin_group + + EOS + ``` 1. [Reconfigure GitLab][reconfigure] for the changes to take effect. @@ -171,15 +171,15 @@ group, as opposed to the full DN. 1. Edit `/home/git/gitlab/config/gitlab.yml`: - ```yaml - production: - ldap: - servers: - main: - # snip... - group_base: ou=groups,dc=example,dc=com - admin_group: my_admin_group - ``` + ```yaml + production: + ldap: + servers: + main: + # snip... + group_base: ou=groups,dc=example,dc=com + admin_group: my_admin_group + ``` 1. [Restart GitLab][restart] for the changes to take effect. @@ -191,7 +191,6 @@ to lock down user abilities to invite new members to a group. When enabled follo 1. Only administrator can manage memberships of any group including access levels. 2. Users are not allowed to share project with other groups or invite members to a project created in a group. - ## Adjusting LDAP user sync schedule > Introduced in GitLab Enterprise Edition Starter. @@ -211,9 +210,9 @@ sync to run once every 12 hours at the top of the hour. 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['ldap_sync_worker_cron'] = "0 */12 * * *" - ``` + ```ruby + gitlab_rails['ldap_sync_worker_cron'] = "0 */12 * * *" + ``` 1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. @@ -221,11 +220,11 @@ sync to run once every 12 hours at the top of the hour. 1. Edit `config/gitlab.yaml`: - ```yaml - cron_jobs: - ldap_sync_worker_cron: - "0 */12 * * *" - ``` + ```yaml + cron_jobs: + ldap_sync_worker_cron: + "0 */12 * * *" + ``` 1. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect. @@ -252,9 +251,9 @@ sync to run once every 2 hours at the top of the hour. 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['ldap_group_sync_worker_cron'] = "0 */2 * * * *" - ``` + ```ruby + gitlab_rails['ldap_group_sync_worker_cron'] = "0 */2 * * * *" + ``` 1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. @@ -262,11 +261,11 @@ sync to run once every 2 hours at the top of the hour. 1. Edit `config/gitlab.yaml`: - ```yaml - cron_jobs: - ldap_group_sync_worker_cron: - "*/30 * * * *" - ``` + ```yaml + cron_jobs: + ldap_group_sync_worker_cron: + "*/30 * * * *" + ``` 1. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect. @@ -283,20 +282,20 @@ task. 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['ldap_servers'] = YAML.load <<-EOS - main: - ## snip... - ## - ## An array of CNs of groups containing users that should be considered external - ## - ## Ex. ['interns', 'contractors'] - ## - ## Note: Not `cn=interns` or the full DN - ## - external_groups: ['interns', 'contractors'] - EOS - ``` + ```ruby + gitlab_rails['ldap_servers'] = YAML.load <<-EOS + main: + ## snip... + ## + ## An array of CNs of groups containing users that should be considered external + ## + ## Ex. ['interns', 'contractors'] + ## + ## Note: Not `cn=interns` or the full DN + ## + external_groups: ['interns', 'contractors'] + EOS + ``` 1. [Reconfigure GitLab][reconfigure] for the changes to take effect. @@ -304,14 +303,14 @@ task. 1. Edit `config/gitlab.yaml`: - ```yaml - production: - ldap: - servers: - main: - # snip... - external_groups: ['interns', 'contractors'] - ``` + ```yaml + production: + ldap: + servers: + main: + # snip... + external_groups: ['interns', 'contractors'] + ``` 1. [Restart GitLab][restart] for the changes to take effect. @@ -436,66 +435,71 @@ step of the sync. 1. Start a Rails console - ```bash - # For Omnibus installations - sudo gitlab-rails console + ```bash + # For Omnibus installations + sudo gitlab-rails console - # For installations from source - sudo -u git -H bundle exec rails console production - ``` + # For installations from source + sudo -u git -H bundle exec rails console production + ``` 1. Set the log level to debug (only for this session): - ```ruby - Rails.logger.level = Logger::DEBUG - ``` + ```ruby + Rails.logger.level = Logger::DEBUG + ``` + 1. Choose a GitLab group to test with. This group should have an LDAP group link already configured. If the output is `nil`, the group could not be found. If a bunch of group attributes are output, your group was found successfully. - ```ruby - group = Group.find_by(name: 'my_group') + ```ruby + group = Group.find_by(name: 'my_group') + + # Output + => #<Group:0x007fe825196558 id: 1234, name: "my_group"...> + ``` - # Output - => #<Group:0x007fe825196558 id: 1234, name: "my_group"...> - ``` 1. Run a group sync for this particular group. - ```ruby - EE::Gitlab::Auth::LDAP::Sync::Group.execute_all_providers(group) - ``` + ```ruby + EE::Gitlab::Auth::LDAP::Sync::Group.execute_all_providers(group) + ``` + 1. Look through the output of the sync. See [example log output](#example-log-output) below for more information about the output. 1. If you still aren't able to see why the user isn't being added, query the LDAP group directly to see what members are listed. Still in the Rails console, run the following query: - ```ruby - adapter = Gitlab::Auth::LDAP::Adapter.new('ldapmain') # If `main` is the LDAP provider - ldap_group = EE::Gitlab::Auth::LDAP::Group.find_by_cn('group_cn_here', adapter) + ```ruby + adapter = Gitlab::Auth::LDAP::Adapter.new('ldapmain') # If `main` is the LDAP provider + ldap_group = EE::Gitlab::Auth::LDAP::Group.find_by_cn('group_cn_here', adapter) + + # Output + => #<EE::Gitlab::Auth::LDAP::Group:0x007fcbdd0bb6d8 + ``` - # Output - => #<EE::Gitlab::Auth::LDAP::Group:0x007fcbdd0bb6d8 - ``` 1. Query the LDAP group's member DNs and see if the user's DN is in the list. One of the DNs here should match the 'Identifier' from the LDAP identity checked earlier. If it doesn't, the user does not appear to be in the LDAP group. - ```ruby - ldap_group.member_dns + ```ruby + ldap_group.member_dns + + # Output + => ["uid=john,ou=people,dc=example,dc=com", "uid=mary,ou=people,dc=example,dc=com"] + ``` - # Output - => ["uid=john,ou=people,dc=example,dc=com", "uid=mary,ou=people,dc=example,dc=com"] - ``` 1. Some LDAP servers don't store members by DN. Rather, they use UIDs instead. If you didn't see results from the last query, try querying by UIDs instead. - ```ruby - ldap_group.member_uids + ```ruby + ldap_group.member_uids - # Output - => ['john','mary'] - ``` + # Output + => ['john','mary'] + ``` #### Example log output diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md index 79ac7fe0352..86e6be5f4fa 100644 --- a/doc/administration/auth/ldap.md +++ b/doc/administration/auth/ldap.md @@ -12,7 +12,7 @@ including group membership syncing as well as multiple LDAP servers support. The information on this page is relevant for both GitLab CE and EE. For more details about EE-specific LDAP features, see the -[LDAP Enterprise Edition documentation](ldap-ee.md). **[STARTER ONLY]** +[LDAP Enterprise Edition documentation](ldap-ee.md). **(STARTER ONLY)** ## Security @@ -46,7 +46,7 @@ LDAP-enabled users can always authenticate with Git using their GitLab username or email and LDAP password, even if password authentication for Git is disabled in the application settings. -## Google Secure LDAP **[CORE ONLY]** +## Google Secure LDAP **(CORE ONLY)** > Introduced in GitLab 11.9. @@ -62,7 +62,7 @@ to connect to one GitLab server. For a complete guide on configuring LDAP with GitLab Community Edition, please check the admin guide [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md). -For GitLab Enterprise Editions, see also [How to configure LDAP with GitLab EE](how_to_configure_ldap_gitlab_ee/index.md). **[STARTER ONLY]** +For GitLab Enterprise Editions, see also [How to configure LDAP with GitLab EE](how_to_configure_ldap_gitlab_ee/index.md). **(STARTER ONLY)** To enable LDAP integration you need to add your LDAP server settings in `/etc/gitlab/gitlab.rb` or `/home/git/gitlab/config/gitlab.yml` for Omnibus @@ -387,7 +387,7 @@ group, you can use the following syntax: Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at <https://docs.microsoft.com/en-us/windows/desktop/ADSI/search-filter-syntax>. Support for nested members in the user filter should not be confused with -[group sync nested groups support](ldap-ee.md#supported-ldap-group-typesattributes). **[STARTER ONLY]** +[group sync nested groups support](ldap-ee.md#supported-ldap-group-typesattributes). **(STARTER ONLY)** Please note that GitLab does not support the custom filter syntax used by omniauth-ldap. @@ -398,30 +398,30 @@ The `user_filter` DN can contain special characters. For example: - A comma: - ``` - OU=GitLab, Inc,DC=gitlab,DC=com - ``` + ``` + OU=GitLab, Inc,DC=gitlab,DC=com + ``` - Open and close brackets: - ``` - OU=Gitlab (Inc),DC=gitlab,DC=com - ``` + ``` + OU=Gitlab (Inc),DC=gitlab,DC=com + ``` - These characters must be escaped as documented in - [RFC 4515](https://tools.ietf.org/search/rfc4515). + These characters must be escaped as documented in + [RFC 4515](https://tools.ietf.org/search/rfc4515). - Escape commas with `\2C`. For example: - ``` - OU=GitLab\2C Inc,DC=gitlab,DC=com - ``` + ``` + OU=GitLab\2C Inc,DC=gitlab,DC=com + ``` - Escape open and close brackets with `\28` and `\29`, respectively. For example: - ``` - OU=Gitlab \28Inc\29,DC=gitlab,DC=com - ``` + ``` + OU=Gitlab \28Inc\29,DC=gitlab,DC=com + ``` ## Enabling LDAP sign-in for existing GitLab users @@ -445,13 +445,13 @@ the configuration option `lowercase_usernames`. By default, this configuration o 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['ldap_servers'] = YAML.load <<-EOS - main: - # snip... - lowercase_usernames: true - EOS - ``` + ```ruby + gitlab_rails['ldap_servers'] = YAML.load <<-EOS + main: + # snip... + lowercase_usernames: true + EOS + ``` 1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. @@ -459,14 +459,14 @@ the configuration option `lowercase_usernames`. By default, this configuration o 1. Edit `config/gitlab.yaml`: - ```yaml - production: - ldap: - servers: - main: - # snip... - lowercase_usernames: true - ``` + ```yaml + production: + ldap: + servers: + main: + # snip... + lowercase_usernames: true + ``` 1. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect. @@ -519,13 +519,13 @@ ldapsearch -H ldaps://$host:$port -D "$bind_dn" -y bind_dn_password.txt -b "$ba - Run the following check command to make sure that the LDAP settings are correct and GitLab can see your users: - ```bash - # For Omnibus installations - sudo gitlab-rake gitlab:ldap:check + ```bash + # For Omnibus installations + sudo gitlab-rake gitlab:ldap:check - # For installations from source - sudo -u git -H bundle exec rake gitlab:ldap:check RAILS_ENV=production - ``` + # For installations from source + sudo -u git -H bundle exec rake gitlab:ldap:check RAILS_ENV=production + ``` ### Connection Refused diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md index 6e48add6930..454da8c2866 100644 --- a/doc/administration/auth/oidc.md +++ b/doc/administration/auth/oidc.md @@ -5,76 +5,76 @@ GitLab can use [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0 To enable the OpenID Connect OmniAuth provider, you must register your application with an OpenID Connect provider. The OpenID Connect will provide you with a client details and secret for you to use. -1. On your GitLab server, open the configuration file. - - For Omnibus GitLab: - - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` - - For installations from source: - - ```sh - cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` - - See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings. - -1. Add the provider configuration. - - For Omnibus GitLab: - - ```ruby - gitlab_rails['omniauth_providers'] = [ - { 'name' => 'openid_connect', - 'label' => '<your_oidc_label>', - 'args' => { - "name' => 'openid_connect', - 'scope' => ['openid','profile'], - 'response_type' => 'code', - 'issuer' => '<your_oidc_url>', - 'discovery' => true, - 'client_auth_method' => 'query', - 'uid_field' => '<uid_field>', - 'client_options' => { - 'identifier' => '<your_oidc_client_id>', - 'secret' => '<your_oidc_client_secret>', - 'redirect_uri' => '<your_gitlab_url>/users/auth/openid_connect/callback' - } - } - } - ] - ``` - - For installation from source: - - ```yaml - - { name: 'openid_connect', - label: '<your_oidc_label>', - args: { - name: 'openid_connect', - scope: ['openid','profile'], - response_type: 'code', - issuer: '<your_oidc_url>', - discovery: true, - client_auth_method: 'query', - uid_field: '<uid_field>', - client_options: { - identifier: '<your_oidc_client_id>', - secret: '<your_oidc_client_secret>', - redirect_uri: '<your_gitlab_url>/users/auth/openid_connect/callback' - } - } - } - ``` - - > **Note:** - > - > - For more information on each configuration option refer to - the [OmniAuth OpenID Connect usage documentation](https://github.com/m0n9oose/omniauth_openid_connect#usage) and - the [OpenID Connect Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html). +1. On your GitLab server, open the configuration file. + + For Omnibus GitLab: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For installations from source: + + ```sh + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml + ``` + + See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings. + +1. Add the provider configuration. + + For Omnibus GitLab: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { 'name' => 'openid_connect', + 'label' => '<your_oidc_label>', + 'args' => { + "name' => 'openid_connect', + 'scope' => ['openid','profile'], + 'response_type' => 'code', + 'issuer' => '<your_oidc_url>', + 'discovery' => true, + 'client_auth_method' => 'query', + 'uid_field' => '<uid_field>', + 'client_options' => { + 'identifier' => '<your_oidc_client_id>', + 'secret' => '<your_oidc_client_secret>', + 'redirect_uri' => '<your_gitlab_url>/users/auth/openid_connect/callback' + } + } + } + ] + ``` + + For installation from source: + + ```yaml + - { name: 'openid_connect', + label: '<your_oidc_label>', + args: { + name: 'openid_connect', + scope: ['openid','profile'], + response_type: 'code', + issuer: '<your_oidc_url>', + discovery: true, + client_auth_method: 'query', + uid_field: '<uid_field>', + client_options: { + identifier: '<your_oidc_client_id>', + secret: '<your_oidc_client_secret>', + redirect_uri: '<your_gitlab_url>/users/auth/openid_connect/callback' + } + } + } + ``` + + > **Note:** + > + > - For more information on each configuration option refer to + the [OmniAuth OpenID Connect usage documentation](https://github.com/m0n9oose/omniauth_openid_connect#usage) and + the [OpenID Connect Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html). 1. For the configuration above, change the values for the provider to match your OpenID Connect client setup. Use the following as a guide: - `<your_oidc_label>` is the label that will be displayed on the login page. diff --git a/doc/administration/auth/okta.md b/doc/administration/auth/okta.md index aa4e1b0d2e0..566003ba708 100644 --- a/doc/administration/auth/okta.md +++ b/doc/administration/auth/okta.md @@ -16,7 +16,7 @@ The following documentation enables Okta as a SAML provider. 1. Next, you'll need the to fill in the SAML general config. Here's an example image. - ![Okta admin panel view](img/okta_admin_panel.png) + ![Okta admin panel view](img/okta_admin_panel.png) 1. The last part of the configuration is the feedback section where you can just say you're a customer and creating an app for internal use. @@ -24,7 +24,7 @@ The following documentation enables Okta as a SAML provider. profile. Click on the SAML 2.0 config instructions button which should look like the following: - ![Okta SAML settings](img/okta_saml_settings.png) + ![Okta SAML settings](img/okta_saml_settings.png) 1. On the screen that comes up take note of the **Identity Provider Single Sign-On URL** which you'll use for the @@ -38,112 +38,112 @@ Now that the Okta app is configured, it's time to enable it in GitLab. ## Configure GitLab -1. On your GitLab server, open the configuration file: - - **For Omnibus GitLab installations** - - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` - - **For installations from source** - - ```sh - cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` - -1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) - for initial settings. - -1. To allow your users to use Okta to sign up without having to manually create - an account first, don't forget to add the following values to your - configuration: - - **For Omnibus GitLab installations** - - ```ruby - gitlab_rails['omniauth_allow_single_sign_on'] = ['saml'] - gitlab_rails['omniauth_block_auto_created_users'] = false - ``` - - **For installations from source** - - ```yaml - allow_single_sign_on: ["saml"] - block_auto_created_users: false - ``` - -1. You can also automatically link Okta users with existing GitLab users if - their email addresses match by adding the following setting: - - **For Omnibus GitLab installations** - - ```ruby - gitlab_rails['omniauth_auto_link_saml_user'] = true - ``` - - **For installations from source** - - ```yaml - auto_link_saml_user: true - ``` - -1. Add the provider configuration. - - >**Notes:** - > - >- Change the value for `assertion_consumer_service_url` to match the HTTPS endpoint - of GitLab (append `users/auth/saml/callback` to the HTTPS URL of your GitLab - installation to generate the correct value). - > - >- To get the `idp_cert_fingerprint` fingerprint, first download the - certificate from the Okta app you registered and then run: - `openssl x509 -in okta.cert -noout -fingerprint`. Substitute `okta.cert` - with the location of your certificate. - > - >- Change the value of `idp_sso_target_url`, with the value of the - **Identity Provider Single Sign-On URL** from the step when you - configured the Okta app. - > - >- Change the value of `issuer` to the value of the **Audience Restriction** from your Okta app configuration. This will identify GitLab - to the IdP. - > - >- Leave `name_identifier_format` as-is. - - **For Omnibus GitLab installations** - - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - name: 'saml', - args: { - assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', - idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', - idp_sso_target_url: 'https://gitlab.oktapreview.com/app/gitlabdev773716_gitlabsaml_1/exk8odl81tBrjpD4B0h7/sso/saml', - issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' - }, - label: 'Okta' # optional label for SAML login button, defaults to "Saml" - } - ] - ``` - - **For installations from source** - - ```yaml - - { - name: 'saml', - args: { - assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', - idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', - idp_sso_target_url: 'https://gitlab.oktapreview.com/app/gitlabdev773716_gitlabsaml_1/exk8odl81tBrjpD4B0h7/sso/saml', - issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' - }, - label: 'Okta' # optional label for SAML login button, defaults to "Saml" - } - ``` +1. On your GitLab server, open the configuration file: + + **For Omnibus GitLab installations** + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + **For installations from source** + + ```sh + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml + ``` + +1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) + for initial settings. + +1. To allow your users to use Okta to sign up without having to manually create + an account first, don't forget to add the following values to your + configuration: + + **For Omnibus GitLab installations** + + ```ruby + gitlab_rails['omniauth_allow_single_sign_on'] = ['saml'] + gitlab_rails['omniauth_block_auto_created_users'] = false + ``` + + **For installations from source** + + ```yaml + allow_single_sign_on: ["saml"] + block_auto_created_users: false + ``` + +1. You can also automatically link Okta users with existing GitLab users if + their email addresses match by adding the following setting: + + **For Omnibus GitLab installations** + + ```ruby + gitlab_rails['omniauth_auto_link_saml_user'] = true + ``` + + **For installations from source** + + ```yaml + auto_link_saml_user: true + ``` + +1. Add the provider configuration. + + >**Notes:** + > + >- Change the value for `assertion_consumer_service_url` to match the HTTPS endpoint + of GitLab (append `users/auth/saml/callback` to the HTTPS URL of your GitLab + installation to generate the correct value). + > + >- To get the `idp_cert_fingerprint` fingerprint, first download the + certificate from the Okta app you registered and then run: + `openssl x509 -in okta.cert -noout -fingerprint`. Substitute `okta.cert` + with the location of your certificate. + > + >- Change the value of `idp_sso_target_url`, with the value of the + **Identity Provider Single Sign-On URL** from the step when you + configured the Okta app. + > + >- Change the value of `issuer` to the value of the **Audience Restriction** from your Okta app configuration. This will identify GitLab + to the IdP. + > + >- Leave `name_identifier_format` as-is. + + **For Omnibus GitLab installations** + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + name: 'saml', + args: { + assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', + idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', + idp_sso_target_url: 'https://gitlab.oktapreview.com/app/gitlabdev773716_gitlabsaml_1/exk8odl81tBrjpD4B0h7/sso/saml', + issuer: 'https://gitlab.example.com', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + }, + label: 'Okta' # optional label for SAML login button, defaults to "Saml" + } + ] + ``` + + **For installations from source** + + ```yaml + - { + name: 'saml', + args: { + assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', + idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', + idp_sso_target_url: 'https://gitlab.oktapreview.com/app/gitlabdev773716_gitlabsaml_1/exk8odl81tBrjpD4B0h7/sso/saml', + issuer: 'https://gitlab.example.com', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + }, + label: 'Okta' # optional label for SAML login button, defaults to "Saml" + } + ``` 1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart](../restart_gitlab.md#installations-from-source) GitLab for Omnibus and installations from source respectively for the changes to take effect. diff --git a/doc/administration/auth/smartcard.md b/doc/administration/auth/smartcard.md index b33c5359b44..e47751e0cc5 100644 --- a/doc/administration/auth/smartcard.md +++ b/doc/administration/auth/smartcard.md @@ -1,4 +1,4 @@ -# Smartcard authentication **[PREMIUM ONLY]** +# Smartcard authentication **(PREMIUM ONLY)** GitLab supports authentication using smartcards. @@ -56,11 +56,11 @@ attribute. As a prerequisite, you must use an LDAP server that: 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['smartcard_enabled'] = true - gitlab_rails['smartcard_ca_file'] = "/etc/ssl/certs/CA.pem" - gitlab_rails['smartcard_client_certificate_required_port'] = 3444 - ``` + ```ruby + gitlab_rails['smartcard_enabled'] = true + gitlab_rails['smartcard_ca_file'] = "/etc/ssl/certs/CA.pem" + gitlab_rails['smartcard_client_certificate_required_port'] = 3444 + ``` 1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect. @@ -154,15 +154,46 @@ attribute. As a prerequisite, you must use an LDAP server that: 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['ldap_servers'] = YAML.load <<-EOS - main: - # snip... - # Enable smartcard authentication against the LDAP server. Valid values - # are "false", "optional", and "required". - smartcard_auth: optional - EOS - ``` + ```ruby + gitlab_rails['ldap_servers'] = YAML.load <<-EOS + main: + # snip... + # Enable smartcard authentication against the LDAP server. Valid values + # are "false", "optional", and "required". + smartcard_auth: optional + EOS + ``` + +1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) + GitLab for the changes to take effect. + +**For installations from source** + +1. Edit `config/gitlab.yml`: + + ```yaml + production: + ldap: + servers: + main: + # snip... + # Enable smartcard authentication against the LDAP server. Valid values + # are "false", "optional", and "required". + smartcard_auth: optional + ``` + +1. Save the file and [restart](../restart_gitlab.md#installations-from-source) + GitLab for the changes to take effect. + +### Require browser session with smartcard sign-in for Git access + +**For Omnibus installations** + +1. Edit `/etc/gitlab/gitlab.rb`: + + ```ruby + gitlab_rails['smartcard_required_for_git_access'] = true + ``` 1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect. @@ -171,16 +202,13 @@ attribute. As a prerequisite, you must use an LDAP server that: 1. Edit `config/gitlab.yml`: - ```yaml - production: - ldap: - servers: - main: - # snip... - # Enable smartcard authentication against the LDAP server. Valid values - # are "false", "optional", and "required". - smartcard_auth: optional - ``` + ```yaml + ## Smartcard authentication settings + smartcard: + # snip... + # Browser session with smartcard sign-in is required for Git access + required_for_git_access: true + ``` 1. Save the file and [restart](../restart_gitlab.md#installations-from-source) GitLab for the changes to take effect. diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md index 2e4b4efa0ac..3e3054af509 100644 --- a/doc/administration/container_registry.md +++ b/doc/administration/container_registry.md @@ -1,7 +1,5 @@ # GitLab Container Registry administration -> **Notes:** -> > - [Introduced][ce-4040] in GitLab 8.8. > - Container Registry manifest `v1` support was added in GitLab 8.9 to support > Docker versions earlier than 1.10. @@ -125,21 +123,21 @@ otherwise you will run into conflicts. 1. Your `/etc/gitlab/gitlab.rb` should contain the Registry URL as well as the path to the existing TLS certificate and key used by GitLab: - ```ruby - registry_external_url 'https://gitlab.example.com:4567' - ``` + ```ruby + registry_external_url 'https://gitlab.example.com:4567' + ``` - Note how the `registry_external_url` is listening on HTTPS under the - existing GitLab URL, but on a different port. + Note how the `registry_external_url` is listening on HTTPS under the + existing GitLab URL, but on a different port. - If your TLS certificate is not in `/etc/gitlab/ssl/gitlab.example.com.crt` - and key not in `/etc/gitlab/ssl/gitlab.example.com.key` uncomment the lines - below: + If your TLS certificate is not in `/etc/gitlab/ssl/gitlab.example.com.crt` + and key not in `/etc/gitlab/ssl/gitlab.example.com.key` uncomment the lines + below: - ```ruby - registry_nginx['ssl_certificate'] = "/path/to/certificate.pem" - registry_nginx['ssl_certificate_key'] = "/path/to/certificate.key" - ``` + ```ruby + registry_nginx['ssl_certificate'] = "/path/to/certificate.pem" + registry_nginx['ssl_certificate_key'] = "/path/to/certificate.key" + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -150,12 +148,12 @@ otherwise you will run into conflicts. 1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and configure it with the following settings: - ``` - registry: - enabled: true - host: gitlab.example.com - port: 4567 - ``` + ``` + registry: + enabled: true + host: gitlab.example.com + port: 4567 + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. 1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path). @@ -188,17 +186,17 @@ Let's assume that you want the container Registry to be accessible at `/etc/gitlab/ssl/registry.gitlab.example.com.key` and make sure they have correct permissions: - ```bash - chmod 600 /etc/gitlab/ssl/registry.gitlab.example.com.* - ``` + ```bash + chmod 600 /etc/gitlab/ssl/registry.gitlab.example.com.* + ``` 1. Once the TLS certificate is in place, edit `/etc/gitlab/gitlab.rb` with: - ```ruby - registry_external_url 'https://registry.gitlab.example.com' - ``` + ```ruby + registry_external_url 'https://registry.gitlab.example.com' + ``` - Note how the `registry_external_url` is listening on HTTPS. + Note how the `registry_external_url` is listening on HTTPS. 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -219,11 +217,11 @@ look like: 1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and configure it with the following settings: - ``` - registry: - enabled: true - host: registry.gitlab.example.com - ``` + ```yaml + registry: + enabled: true + host: registry.gitlab.example.com + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. 1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path). @@ -248,9 +246,9 @@ Registry application itself. 1. Open `/etc/gitlab/gitlab.rb` and set `registry['enable']` to `false`: - ```ruby - registry['enable'] = false - ``` + ```ruby + registry['enable'] = false + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -261,10 +259,10 @@ Registry application itself. 1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and set `enabled` to `false`: - ``` - registry: - enabled: false - ``` + ```yaml + registry: + enabled: false + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. @@ -280,9 +278,9 @@ the Container Registry by themselves, follow the steps below. 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['gitlab_default_projects_features_container_registry'] = false - ``` + ```ruby + gitlab_rails['gitlab_default_projects_features_container_registry'] = false + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -293,16 +291,16 @@ the Container Registry by themselves, follow the steps below. 1. Open `/home/git/gitlab/config/gitlab.yml`, find the `default_projects_features` entry and configure it so that `container_registry` is set to `false`: - ``` - ## Default project features settings - default_projects_features: - issues: true - merge_requests: true - wiki: true - snippets: false - builds: true - container_registry: false - ``` + ```yaml + ## Default project features settings + default_projects_features: + issues: true + merge_requests: true + wiki: true + snippets: false + builds: true + container_registry: false + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. @@ -332,9 +330,9 @@ The default location where images are stored in Omnibus, is 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['registry_path'] = "/path/to/registry/storage" - ``` + ```ruby + gitlab_rails['registry_path'] = "/path/to/registry/storage" + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -348,10 +346,10 @@ The default location where images are stored in source installations, is 1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and change the `path` setting: - ``` - registry: - path: shared/registry - ``` + ```yaml + registry: + path: shared/registry + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. @@ -393,17 +391,17 @@ To configure the `s3` storage driver in Omnibus: 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - registry['storage'] = { - 's3' => { - 'accesskey' => 's3-access-key', - 'secretkey' => 's3-secret-key-for-access-key', - 'bucket' => 'your-s3-bucket', - 'region' => 'your-s3-region', - 'regionendpoint' => 'your-s3-regionendpoint' - } - } - ``` + ```ruby + registry['storage'] = { + 's3' => { + 'accesskey' => 's3-access-key', + 'secretkey' => 's3-secret-key-for-access-key', + 'bucket' => 'your-s3-bucket', + 'region' => 'your-s3-region', + 'regionendpoint' => 'your-s3-regionendpoint' + } + } + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -442,9 +440,9 @@ In the examples below we set the Registry's port to `5001`. 1. Open `/etc/gitlab/gitlab.rb` and set `registry['registry_http_addr']`: - ```ruby - registry['registry_http_addr'] = "localhost:5001" - ``` + ```ruby + registry['registry_http_addr'] = "localhost:5001" + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -455,10 +453,10 @@ In the examples below we set the Registry's port to `5001`. 1. Open the configuration file of your Registry server and edit the [`http:addr`][registry-http-config] value: - ``` - http - addr: localhost:5001 - ``` + ```yaml + http + addr: localhost:5001 + ``` 1. Save the file and restart the Registry server. @@ -476,14 +474,14 @@ You can use GitLab as an auth endpoint and use a non-bundled Container Registry. 1. Open `/etc/gitlab/gitlab.rb` and set necessary configurations: - ```ruby - gitlab_rails['registry_enabled'] = true - gitlab_rails['registry_host'] = "registry.gitlab.example.com" - gitlab_rails['registry_port'] = "5005" - gitlab_rails['registry_api_url'] = "http://localhost:5000" - gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry" - gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer" - ``` + ```ruby + gitlab_rails['registry_enabled'] = true + gitlab_rails['registry_host'] = "registry.gitlab.example.com" + gitlab_rails['registry_port'] = "5005" + gitlab_rails['registry_api_url'] = "http://localhost:5000" + gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry" + gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer" + ``` 1. A certificate keypair is required for GitLab and the Container Registry to communicate securely. By default omnibus-gitlab will generate one keypair, @@ -492,19 +490,19 @@ You can use GitLab as an auth endpoint and use a non-bundled Container Registry. custom certificate key. To do that, add the following to `/etc/gitlab/gitlab.rb` - ```ruby - gitlab_rails['registry_key_path'] = "/custom/path/to/registry-key.key" - # registry['internal_key'] should contain the contents of the custom key - # file. Line breaks in the key file should be marked using `\n` character - # Example: - registry['internal_key'] = "---BEGIN RSA PRIVATE KEY---\nMIIEpQIBAA\n" - ``` + ```ruby + gitlab_rails['registry_key_path'] = "/custom/path/to/registry-key.key" + # registry['internal_key'] should contain the contents of the custom key + # file. Line breaks in the key file should be marked using `\n` character + # Example: + registry['internal_key'] = "---BEGIN RSA PRIVATE KEY---\nMIIEpQIBAA\n" + ``` - **Note:** The file specified at `registry_key_path` gets populated with the - content specified by `internal_key`, each time reconfigure is executed. If - no file is specified, omnibus-gitlab will default it to - `/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key` and will populate - it. + **Note:** The file specified at `registry_key_path` gets populated with the + content specified by `internal_key`, each time reconfigure is executed. If + no file is specified, omnibus-gitlab will default it to + `/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key` and will populate + it. 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -512,18 +510,18 @@ You can use GitLab as an auth endpoint and use a non-bundled Container Registry. 1. Open `/home/git/gitlab/config/gitlab.yml`, and edit the configuration settings under `registry`: - ``` - ## Container Registry + ```yaml + ## Container Registry - registry: - enabled: true - host: "registry.gitlab.example.com" - port: "5005" - api_url: "http://localhost:5000" - path: /var/opt/gitlab/gitlab-rails/shared/registry - key: /var/opt/gitlab/gitlab-rails/certificate.key - issuer: omnibus-gitlab-issuer - ``` + registry: + enabled: true + host: "registry.gitlab.example.com" + port: "5005" + api_url: "http://localhost:5000" + path: /var/opt/gitlab/gitlab-rails/shared/registry + key: /var/opt/gitlab/gitlab-rails/certificate.key + issuer: omnibus-gitlab-issuer + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. @@ -550,20 +548,20 @@ To configure a notification endpoint in Omnibus: 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - registry['notifications'] = [ - { - 'name' => 'test_endpoint', - 'url' => 'https://gitlab.example.com/notify', - 'timeout' => '500ms', - 'threshold' => 5, - 'backoff' => '1s', - 'headers' => { - "Authorization" => ["AUTHORIZATION_EXAMPLE_TOKEN"] - } - } - ] - ``` + ```ruby + registry['notifications'] = [ + { + 'name' => 'test_endpoint', + 'url' => 'https://gitlab.example.com/notify', + 'timeout' => '500ms', + 'threshold' => 5, + 'backoff' => '1s', + 'headers' => { + "Authorization" => ["AUTHORIZATION_EXAMPLE_TOKEN"] + } + } + ] + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -629,16 +627,16 @@ Start with a value between `25000000` (25MB) and `50000000` (50MB). 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - registry['storage'] = { - 's3' => { - 'accesskey' => 'AKIAKIAKI', - 'secretkey' => 'secret123', - 'bucket' => 'gitlab-registry-bucket-AKIAKIAKI', - 'chunksize' => 25000000 - } - } - ``` + ```ruby + registry['storage'] = { + 's3' => { + 'accesskey' => 'AKIAKIAKI', + 'secretkey' => 'secret123', + 'bucket' => 'gitlab-registry-bucket-AKIAKIAKI', + 'chunksize' => 25000000 + } + } + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -648,14 +646,14 @@ Start with a value between `25000000` (25MB) and `50000000` (50MB). 1. Edit `config/gitlab.yml`: - ```yaml - storage: - s3: - accesskey: 'AKIAKIAKI' - secretkey: 'secret123' - bucket: 'gitlab-registry-bucket-AKIAKIAKI' - chunksize: 25000000 - ``` + ```yaml + storage: + s3: + accesskey: 'AKIAKIAKI' + secretkey: 'secret123' + bucket: 'gitlab-registry-bucket-AKIAKIAKI' + chunksize: 25000000 + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. @@ -669,9 +667,9 @@ You can add a configuration option for backwards compatibility. 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - registry['compatibility_schema1_enabled'] = true - ``` + ```ruby + registry['compatibility_schema1_enabled'] = true + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -681,11 +679,11 @@ You can add a configuration option for backwards compatibility. 1. Edit the YML configuration file you created when you [deployed the registry][registry-deploy]. Add the following snippet: - ```yaml - compatibility: - schema1: - enabled: true - ``` + ```yaml + compatibility: + schema1: + enabled: true + ``` 1. Restart the registry for the changes to take affect. @@ -694,16 +692,15 @@ You can add a configuration option for backwards compatibility. A Docker connection error can occur when there are special characters in either the group, project or branch name. Special characters can include: -* Leading underscore -* Trailing hyphen/dash -* Double hyphen/dash +- Leading underscore +- Trailing hyphen/dash +- Double hyphen/dash -To get around this, you can [change the group path](../user/group/index.md#changing-a-groups-path), -[change the project path](../user/project/settings/index.md#renaming-a-repository) or change the -branch name. Another option is to create a [push rule](../push_rules/push_rules.html) to prevent +To get around this, you can [change the group path](../user/group/index.md#changing-a-groups-path), +[change the project path](../user/project/settings/index.md#renaming-a-repository) or change the +branch name. Another option is to create a [push rule](../push_rules/push_rules.html) to prevent this at the instance level. - [ce-18239]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18239 [docker-insecure-self-signed]: https://docs.docker.com/registry/insecure/#use-self-signed-certificates [reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure diff --git a/doc/administration/database_load_balancing.md b/doc/administration/database_load_balancing.md index 98404ff2a10..dc4cc401fca 100644 --- a/doc/administration/database_load_balancing.md +++ b/doc/administration/database_load_balancing.md @@ -1,4 +1,4 @@ -# Database Load Balancing **[PREMIUM ONLY]** +# Database Load Balancing **(PREMIUM ONLY)** > [Introduced][ee-1283] in [GitLab Premium][eep] 9.0. @@ -74,9 +74,9 @@ the following. This will balance the load between `host1.example.com` and 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com'] } - ``` + ```ruby + gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com'] } + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -86,16 +86,16 @@ the following. This will balance the load between `host1.example.com` and 1. Edit `/home/git/gitlab/config/database.yml` and add or amend the following lines: - ```yaml - production: - username: gitlab - database: gitlab - encoding: unicode - load_balancing: - hosts: - - host1.example.com - - host2.example.com - ``` + ```yaml + production: + username: gitlab + database: gitlab + encoding: unicode + load_balancing: + hosts: + - host1.example.com + - host2.example.com + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. @@ -265,7 +265,7 @@ production: replica_check_interval: 30 ``` -[hot-standby]: https://www.postgresql.org/docs/9.6/static/hot-standby.html +[hot-standby]: https://www.postgresql.org/docs/9.6/hot-standby.html [ee-1283]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1283 [eep]: https://about.gitlab.com/pricing/ [reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab" diff --git a/doc/administration/dependency_proxy.md b/doc/administration/dependency_proxy.md index 4dc1f4dcba4..d2c52b67e67 100644 --- a/doc/administration/dependency_proxy.md +++ b/doc/administration/dependency_proxy.md @@ -1,6 +1,6 @@ -# GitLab Dependency Proxy administration **[PREMIUM ONLY]** +# GitLab Dependency Proxy administration **(PREMIUM ONLY)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing) 11.11. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11. GitLab can be utilized as a dependency proxy for a variety of common package managers. @@ -70,6 +70,7 @@ To change the local storage path: enabled: true storage_path: shared/dependency_proxy ``` + 1. [Restart GitLab] for the changes to take effect. ### Using object storage diff --git a/doc/administration/geo/disaster_recovery/background_verification.md b/doc/administration/geo/disaster_recovery/background_verification.md index e19cd9bbfec..8eee9427b56 100644 --- a/doc/administration/geo/disaster_recovery/background_verification.md +++ b/doc/administration/geo/disaster_recovery/background_verification.md @@ -1,4 +1,4 @@ -# Automatic background verification **[PREMIUM ONLY]** +# Automatic background verification **(PREMIUM ONLY)** NOTE: **Note:** Automatic background verification of repositories and wikis was added in diff --git a/doc/administration/geo/disaster_recovery/bring_primary_back.md b/doc/administration/geo/disaster_recovery/bring_primary_back.md index 9a981b49349..64d7ef2d609 100644 --- a/doc/administration/geo/disaster_recovery/bring_primary_back.md +++ b/doc/administration/geo/disaster_recovery/bring_primary_back.md @@ -1,4 +1,4 @@ -# Bring a demoted primary node back online **[PREMIUM ONLY]** +# Bring a demoted primary node back online **(PREMIUM ONLY)** After a failover, it is possible to fail back to the demoted **primary** node to restore your original configuration. This process consists of two steps: @@ -30,7 +30,7 @@ To bring the former **primary** node up to date: `sudo systemctl enable gitlab-runsvdir`. For CentOS 6, you need to install the GitLab instance from scratch and set it up as a **secondary** node by following [Setup instructions][setup-geo]. In this case, you don't need to follow the next step. - + NOTE: **Note:** If you [changed the DNS records](index.md#step-4-optional-updating-the-primary-domain-dns-record) for this node during disaster recovery procedure you may need to [block all the writes to this node](planned_failover.md#prevent-updates-to-the-primary-node) diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md index 86182b84062..d44e141b66b 100644 --- a/doc/administration/geo/disaster_recovery/index.md +++ b/doc/administration/geo/disaster_recovery/index.md @@ -1,4 +1,4 @@ -# Disaster Recovery **[PREMIUM ONLY]** +# Disaster Recovery **(PREMIUM ONLY)** Geo replicates your database, your Git repositories, and few other assets. We will support and replicate more data in the future, that will enable you to diff --git a/doc/administration/geo/disaster_recovery/planned_failover.md b/doc/administration/geo/disaster_recovery/planned_failover.md index c1a95157f8d..0c2d48854f0 100644 --- a/doc/administration/geo/disaster_recovery/planned_failover.md +++ b/doc/administration/geo/disaster_recovery/planned_failover.md @@ -1,4 +1,4 @@ -# Disaster recovery for planned failover **[PREMIUM ONLY]** +# Disaster recovery for planned failover **(PREMIUM ONLY)** The primary use-case of Disaster Recovery is to ensure business continuity in the event of unplanned outage, but it can be used in conjunction with a planned @@ -187,7 +187,7 @@ access to the **primary** node during the maintenance window. before it is completed will cause the work to be lost. 1. On the **primary** node, navigate to **Admin Area > Geo** and wait for the following conditions to be true of the **secondary** node you are failing over to: - + - All replication meters to each 100% replicated, 0% failures. - All verification meters reach 100% verified, 0% failures. - Database replication lag is 0ms. diff --git a/doc/administration/geo/replication/configuration.md b/doc/administration/geo/replication/configuration.md index dd5e09c0dd7..0e11dffa0d6 100644 --- a/doc/administration/geo/replication/configuration.md +++ b/doc/administration/geo/replication/configuration.md @@ -1,4 +1,4 @@ -# Geo configuration **[PREMIUM ONLY]** +# Geo configuration **(PREMIUM ONLY)** ## Configuring a new **secondary** node diff --git a/doc/administration/geo/replication/database.md b/doc/administration/geo/replication/database.md index 021ed2d9f3c..9272287a4a7 100644 --- a/doc/administration/geo/replication/database.md +++ b/doc/administration/geo/replication/database.md @@ -1,4 +1,4 @@ -# Geo database replication **[PREMIUM ONLY]** +# Geo database replication **(PREMIUM ONLY)** NOTE: **Note:** The following steps are for Omnibus installs only. Using Geo with source-based installs was **deprecated** in GitLab 11.5. @@ -116,7 +116,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o by default. However, Geo requires the **secondary** node to be able to connect to the **primary** node's database. For this reason, we need the address of each node. - + NOTE: **Note:** For external PostgreSQL instances, see [additional instructions](external_database.md). If you are using a cloud provider, you can lookup the addresses for each @@ -424,22 +424,22 @@ data before running `pg_basebackup`. - If PostgreSQL is listening on a non-standard port, add `--port=` as well. - If your database is too large to be transferred in 30 minutes, you will need - to increase the timeout, e.g., `--backup-timeout=3600` if you expect the - initial replication to take under an hour. - - Pass `--sslmode=disable` to skip PostgreSQL TLS authentication altogether - (e.g., you know the network path is secure, or you are using a site-to-site - VPN). This is **not** safe over the public Internet! - - You can read more details about each `sslmode` in the - [PostgreSQL documentation][pg-docs-ssl]; - the instructions above are carefully written to ensure protection against - both passive eavesdroppers and active "man-in-the-middle" attackers. - - Change the `--slot-name` to the name of the replication slot - to be used on the **primary** database. The script will attempt to create the - replication slot automatically if it does not exist. - - If you're repurposing an old server into a Geo **secondary** node, you'll need to - add `--force` to the command line. - - When not in a production machine you can disable backup step if you - really sure this is what you want by adding `--skip-backup` + to increase the timeout, e.g., `--backup-timeout=3600` if you expect the + initial replication to take under an hour. + - Pass `--sslmode=disable` to skip PostgreSQL TLS authentication altogether + (e.g., you know the network path is secure, or you are using a site-to-site + VPN). This is **not** safe over the public Internet! + - You can read more details about each `sslmode` in the + [PostgreSQL documentation][pg-docs-ssl]; + the instructions above are carefully written to ensure protection against + both passive eavesdroppers and active "man-in-the-middle" attackers. + - Change the `--slot-name` to the name of the replication slot + to be used on the **primary** database. The script will attempt to create the + replication slot automatically if it does not exist. + - If you're repurposing an old server into a Geo **secondary** node, you'll need to + add `--force` to the command line. + - When not in a production machine you can disable backup step if you + really sure this is what you want by adding `--skip-backup` The replication process is now complete. diff --git a/doc/administration/geo/replication/docker_registry.md b/doc/administration/geo/replication/docker_registry.md index 5b02b861c61..d5c2d2362b1 100644 --- a/doc/administration/geo/replication/docker_registry.md +++ b/doc/administration/geo/replication/docker_registry.md @@ -1,4 +1,4 @@ -# Docker Registry for a secondary node **[PREMIUM ONLY]** +# Docker Registry for a secondary node **(PREMIUM ONLY)** You can set up a [Docker Registry] on your **secondary** Geo node that mirrors the one on the **primary** Geo node. diff --git a/doc/administration/geo/replication/external_database.md b/doc/administration/geo/replication/external_database.md index 452e4f490a6..85687d4a648 100644 --- a/doc/administration/geo/replication/external_database.md +++ b/doc/administration/geo/replication/external_database.md @@ -1,4 +1,4 @@ -# Geo with external PostgreSQL instances **[PREMIUM ONLY]** +# Geo with external PostgreSQL instances **(PREMIUM ONLY)** This document is relevant if you are using a PostgreSQL instance that is *not managed by Omnibus*. This includes cloud-managed instances like AWS RDS, or diff --git a/doc/administration/geo/replication/faq.md b/doc/administration/geo/replication/faq.md index c527248bc72..b3580a706c3 100644 --- a/doc/administration/geo/replication/faq.md +++ b/doc/administration/geo/replication/faq.md @@ -1,4 +1,4 @@ -# Geo Frequently Asked Questions **[PREMIUM ONLY]** +# Geo Frequently Asked Questions **(PREMIUM ONLY)** ## What are the minimum requirements to run Geo? diff --git a/doc/administration/geo/replication/high_availability.md b/doc/administration/geo/replication/high_availability.md index 61e18df2480..c737fa37077 100644 --- a/doc/administration/geo/replication/high_availability.md +++ b/doc/administration/geo/replication/high_availability.md @@ -1,4 +1,4 @@ -# Geo High Availability **[PREMIUM ONLY]** +# Geo High Availability **(PREMIUM ONLY)** This document describes a minimal reference architecture for running Geo in a high availability configuration. If your HA setup differs from the one diff --git a/doc/administration/geo/replication/index.md b/doc/administration/geo/replication/index.md index 8e1d1cb46ba..f0d329d5296 100644 --- a/doc/administration/geo/replication/index.md +++ b/doc/administration/geo/replication/index.md @@ -1,4 +1,4 @@ -# Geo Replication **[PREMIUM ONLY]** +# Geo Replication **(PREMIUM ONLY)** Geo is the solution for widely distributed development teams. @@ -178,7 +178,7 @@ The steps below should be followed in the order they appear. **Make sure the Git If you installed GitLab using the Omnibus packages (highly recommended): -1. [Install GitLab Enterprise Edition](https://about.gitlab.com/installation/) on the server that will serve as the **secondary** node. Do not create an account or log in to the new **secondary** node. +1. [Install GitLab Enterprise Edition](https://about.gitlab.com/install/) on the server that will serve as the **secondary** node. Do not create an account or log in to the new **secondary** node. 1. [Upload the GitLab License](../../../user/admin_area/license.md) on the **primary** node to unlock Geo. The license must be for [GitLab Premium](https://about.gitlab.com/pricing/) or higher. 1. [Set up the database replication](database.md) (`primary (read-write) <-> secondary (read-only)` topology). 1. [Configure fast lookup of authorized SSH keys in the database](../../operations/fast_ssh_key_lookup.md). This step is required and needs to be done on **both** the **primary** and **secondary** nodes. diff --git a/doc/administration/geo/replication/object_storage.md b/doc/administration/geo/replication/object_storage.md index c3c11dbaf1e..878b67a8f8e 100644 --- a/doc/administration/geo/replication/object_storage.md +++ b/doc/administration/geo/replication/object_storage.md @@ -1,4 +1,4 @@ -# Geo with Object storage **[PREMIUM ONLY]** +# Geo with Object storage **(PREMIUM ONLY)** Geo can be used in combination with Object Storage (AWS S3, or other compatible object storage). @@ -34,10 +34,10 @@ the bucket used by **secondary** nodes. If you are using Google Cloud Storage, consider using [Multi-Regional Storage](https://cloud.google.com/storage/docs/storage-classes#multi-regional). -Or you can use the [Storage Transfer Service](https://cloud.google.com/storage/transfer/), +Or you can use the [Storage Transfer Service](https://cloud.google.com/storage-transfer/docs/), although this only supports daily synchronization. For manual synchronization, or scheduled by `cron`, please have a look at: -- [`s3cmd sync`](http://s3tools.org/s3cmd-sync) +- [`s3cmd sync`](https://s3tools.org/s3cmd-sync) - [`gsutil rsync`](https://cloud.google.com/storage/docs/gsutil/commands/rsync) diff --git a/doc/administration/geo/replication/remove_geo_node.md b/doc/administration/geo/replication/remove_geo_node.md index 6bdaad8f783..e24eb2bd428 100644 --- a/doc/administration/geo/replication/remove_geo_node.md +++ b/doc/administration/geo/replication/remove_geo_node.md @@ -1,4 +1,4 @@ -# Removing secondary Geo nodes **[PREMIUM ONLY]** +# Removing secondary Geo nodes **(PREMIUM ONLY)** **Secondary** nodes can be removed from the Geo cluster using the Geo admin page of the **primary** node. To remove a **secondary** node: @@ -19,10 +19,10 @@ Once removed from the Geo admin page, you must stop and uninstall the **secondar ```bash # Stop gitlab and remove its supervision process sudo gitlab-ctl uninstall - + # Debian/Ubuntu sudo dpkg --remove gitlab-ee - + # Redhat/Centos sudo rpm --erase gitlab-ee ``` @@ -32,9 +32,9 @@ Once GitLab has been uninstalled from the **secondary** node, the replication sl 1. On the **primary** node, start a PostgreSQL console session: ```bash - sudo gitlab-psql + sudo gitlab-psql ``` - + NOTE: **Note:** Using `gitlab-rails dbconsole` will not work, because managing replication slots requires superuser permissions. @@ -43,9 +43,9 @@ Once GitLab has been uninstalled from the **secondary** node, the replication sl ```sql SELECT * FROM pg_replication_slots; ``` - + 1. Remove the replication slot for the **secondary** node: ```sql SELECT pg_drop_replication_slot('<name_of_slot>'); - ``` + ``` diff --git a/doc/administration/geo/replication/security_review.md b/doc/administration/geo/replication/security_review.md index cd54e2dc8c4..ed3f1faa93e 100644 --- a/doc/administration/geo/replication/security_review.md +++ b/doc/administration/geo/replication/security_review.md @@ -1,4 +1,4 @@ -# Geo security review (Q&A) **[PREMIUM ONLY]** +# Geo security review (Q&A) **(PREMIUM ONLY)** The following security review of the Geo feature set focuses on security aspects of the feature as they apply to customers running their own GitLab @@ -115,7 +115,7 @@ questions from [owasp.org](https://www.owasp.org). ### What operating systems support the application? - Geo imposes no additional restrictions on operating system (see the - [GitLab installation](https://about.gitlab.com/installation/) page for more + [GitLab installation](https://about.gitlab.com/install/) page for more details), however we recommend using the operating systems listed in the [Geo documentation](index.md#requirements-for-running-geo). ### What details regarding required OS components and lock‐down needs have been defined? diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md index c7c78407084..28abfff973d 100644 --- a/doc/administration/geo/replication/troubleshooting.md +++ b/doc/administration/geo/replication/troubleshooting.md @@ -1,4 +1,4 @@ -# Geo Troubleshooting **[PREMIUM ONLY]** +# Geo Troubleshooting **(PREMIUM ONLY)** Setting up Geo requires careful attention to details and sometimes it's easy to miss a step. diff --git a/doc/administration/geo/replication/tuning.md b/doc/administration/geo/replication/tuning.md index 1943f2230df..3ee9937774a 100644 --- a/doc/administration/geo/replication/tuning.md +++ b/doc/administration/geo/replication/tuning.md @@ -1,4 +1,4 @@ -# Tuning Geo **[PREMIUM ONLY]** +# Tuning Geo **(PREMIUM ONLY)** ## Changing the sync capacity values diff --git a/doc/administration/geo/replication/updating_the_geo_nodes.md b/doc/administration/geo/replication/updating_the_geo_nodes.md index d56a59f4967..c27f6c78455 100644 --- a/doc/administration/geo/replication/updating_the_geo_nodes.md +++ b/doc/administration/geo/replication/updating_the_geo_nodes.md @@ -1,4 +1,4 @@ -# Updating the Geo nodes **[PREMIUM ONLY]** +# Updating the Geo nodes **(PREMIUM ONLY)** Depending on which version of Geo you are updating to/from, there may be different steps. diff --git a/doc/administration/geo/replication/using_a_geo_server.md b/doc/administration/geo/replication/using_a_geo_server.md index f1f1fe48748..55b5d486676 100644 --- a/doc/administration/geo/replication/using_a_geo_server.md +++ b/doc/administration/geo/replication/using_a_geo_server.md @@ -1,6 +1,6 @@ [//]: # (Please update EE::GitLab::GeoGitAccess::GEO_SERVER_DOCS_URL if this file is moved) -# Using a Geo Server **[PREMIUM ONLY]** +# Using a Geo Server **(PREMIUM ONLY)** After you set up the [database replication and configure the Geo nodes][req], use your closest GitLab node as you would a normal standalone GitLab instance. diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md index 7c7bb9045c7..4407facfca9 100644 --- a/doc/administration/gitaly/index.md +++ b/doc/administration/gitaly/index.md @@ -91,7 +91,7 @@ your GitLab installation has two repository storages, `default` and First install Gitaly using either Omnibus or from source. -Omnibus: [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab +Omnibus: [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab package you want using **steps 1 and 2** from the GitLab downloads page but **_do not_** provide the `EXTERNAL_URL=` value. @@ -220,7 +220,7 @@ network, firewall, or name resolution problem preventing your GitLab server from reaching the Gitaly server then all Gitaly requests will fail. -Additionally, you need to +Additionally, you need to [disable Rugged if previously manually enabled](../high_availability/nfs.md#improving-nfs-performance-with-gitlab). We assume that your Gitaly server can be reached at @@ -436,17 +436,17 @@ particular machine. ## Eliminating NFS altogether -If you are planning to use Gitaly without NFS for your storage needs -and want to eliminate NFS from your environment altogether, there are +If you are planning to use Gitaly without NFS for your storage needs +and want to eliminate NFS from your environment altogether, there are a few things that you need to do: 1. Make sure the [`git` user home directory](https://docs.gitlab.com/omnibus/settings/configuration.html#moving-the-home-directory-for-a-user) is on local disk. - 1. Configure [database lookup of SSH keys](https://docs.gitlab.com/ce/administration/operations/fast_ssh_key_lookup.html) + 1. Configure [database lookup of SSH keys](../operations/fast_ssh_key_lookup.md) to eliminate the need for a shared authorized_keys file. - 1. Configure [object storage for job artifacts](https://docs.gitlab.com/ce/administration/job_artifacts.html#using-object-storage) - including [live tracing](https://docs.gitlab.com/ce/administration/job_traces.html#new-live-trace-architecture). - 1. Configure [object storage for LFS objects](https://docs.gitlab.com/ce/workflow/lfs/lfs_administration.html#storing-lfs-objects-in-remote-object-storage). - 1. Configure [object storage for uploads](https://docs.gitlab.com/ce/administration/uploads.html#using-object-storage-core-only). + 1. Configure [object storage for job artifacts](../job_artifacts.md#using-object-storage) + including [live tracing](../job_traces.md#new-live-trace-architecture). + 1. Configure [object storage for LFS objects](../../workflow/lfs/lfs_administration.md#storing-lfs-objects-in-remote-object-storage). + 1. Configure [object storage for uploads](../uploads.md#using-object-storage-core-only). NOTE: **Note:** One current feature of GitLab still requires a shared directory (NFS): [GitLab Pages](../../user/project/pages/index.md). There is [work in progress](https://gitlab.com/gitlab-org/gitlab-pages/issues/196) diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md index e81d2741082..4317d14ba68 100644 --- a/doc/administration/high_availability/README.md +++ b/doc/administration/high_availability/README.md @@ -162,14 +162,14 @@ contention due to certain workloads. - **Status:** Work-in-progress - **Supported Users (approximate):** 10,000 -- **Related Issues:** [gitlab-com/support/support-team-meta#1513](https://gitlab.com/gitlab-com/support/support-team-meta/issues/1513), +- **Related Issues:** [gitlab-com/support/support-team-meta#1513](https://gitlab.com/gitlab-com/support/support-team-meta/issues/1513), [gitlab-org/quality/team-tasks#110](https://gitlab.com/gitlab-org/quality/team-tasks/issues/110) The Support and Quality teams are in the process of building and performance testing an environment that will support about 10,000 users. The specifications below -are a work-in-progress representation of the work so far. Quality will be -certifying this environment in FY20-Q2. The specifications may be adjusted -prior to certification based on performance testing. +are a work-in-progress representation of the work so far. Quality will be +certifying this environment in FY20-Q2. The specifications may be adjusted +prior to certification based on performance testing. - 3 PostgreSQL - 4 CPU, 8GB RAM per node - 1 PgBouncer - 2 CPU, 4GB RAM @@ -211,4 +211,3 @@ separately: 1. [Configure the GitLab application servers](gitlab.md) 1. [Configure the load balancers](load_balancer.md) 1. [Monitoring node (Prometheus and Grafana)](monitoring_node.md) - diff --git a/doc/administration/high_availability/consul.md b/doc/administration/high_availability/consul.md index 056b7fc15d9..1f93c8130d3 100644 --- a/doc/administration/high_availability/consul.md +++ b/doc/administration/high_availability/consul.md @@ -1,16 +1,83 @@ -# Working with the bundled Consul service **[PREMIUM ONLY]** +# Working with the bundled Consul service **(PREMIUM ONLY)** ## Overview -As part of its High Availability stack, GitLab Premium includes a bundled version of [Consul](http://consul.io) that can be managed through `/etc/gitlab/gitlab.rb`. +As part of its High Availability stack, GitLab Premium includes a bundled version of [Consul](https://www.consul.io/) that can be managed through `/etc/gitlab/gitlab.rb`. A Consul cluster consists of multiple server agents, as well as client agents that run on other nodes which need to talk to the consul cluster. +## Prerequisites + +First, make sure to [download/install](https://about.gitlab.com/install/) +GitLab Omnibus **on each node**. + +Choose an installation method, then make sure you complete steps: + +1. Install and configure the necessary dependencies. +1. Add the GitLab package repository and install the package. + +When installing the GitLab package, do not supply `EXTERNAL_URL` value. + +## Configuring the Consul nodes + +On each Consul node perform the following: + +1. Make sure you collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step, before executing the next step. + +1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: + + ```ruby + # Disable all components except Consul + roles ['consul_role'] + + # START user configuration + # Replace placeholders: + # + # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z + # with the addresses gathered for CONSUL_SERVER_NODES + consul['configuration'] = { + server: true, + retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z) + } + + # Disable auto migrations + gitlab_rails['auto_migrate'] = false + # + # END user configuration + ``` + + > `consul_role` was introduced with GitLab 10.3 + +1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes + to take effect. + +### Consul checkpoint + +Before moving on, make sure Consul is configured correctly. Run the following +command to verify all server nodes are communicating: + +```sh +/opt/gitlab/embedded/bin/consul members +``` + +The output should be similar to: + +``` +Node Address Status Type Build Protocol DC +CONSUL_NODE_ONE XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul +CONSUL_NODE_TWO XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul +CONSUL_NODE_THREE XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul +``` + +If any of the nodes isn't `alive` or if any of the three nodes are missing, +check the [Troubleshooting section](#troubleshooting) before proceeding. + ## Operations ### Checking cluster membership To see which nodes are part of the cluster, run the following on any member in the cluster + ``` # /opt/gitlab/embedded/bin/consul members Node Address Status Type Build Protocol DC @@ -46,18 +113,18 @@ You will see messages like the following in `gitlab-ctl tail consul` output if y 2017-09-25_19:53:41.74356 2017/09/25 19:53:41 [ERR] agent: failed to sync remote state: No cluster leader ``` - To fix this: 1. Pick an address on each node that all of the other nodes can reach this node through. 1. Update your `/etc/gitlab/gitlab.rb` - ```ruby - consul['configuration'] = { - ... - bind_addr: 'IP ADDRESS' - } - ``` + ```ruby + consul['configuration'] = { + ... + bind_addr: 'IP ADDRESS' + } + ``` + 1. Run `gitlab-ctl reconfigure` If you still see the errors, you may have to [erase the consul database and reinitialize](#recreate-from-scratch) on the affected node. @@ -78,12 +145,13 @@ To fix this: 1. Pick an address on the node that all of the other nodes can reach this node through. 1. Update your `/etc/gitlab/gitlab.rb` - ```ruby - consul['configuration'] = { - ... - bind_addr: 'IP ADDRESS' - } - ``` + ```ruby + consul['configuration'] = { + ... + bind_addr: 'IP ADDRESS' + } + ``` + 1. Run `gitlab-ctl reconfigure` ### Outage recovery @@ -91,6 +159,7 @@ To fix this: If you lost enough server agents in the cluster to break quorum, then the cluster is considered failed, and it will not function without manual intervenetion. #### Recreate from scratch + By default, GitLab does not store anything in the consul cluster that cannot be recreated. To erase the consul database and reinitialize ``` @@ -102,4 +171,5 @@ By default, GitLab does not store anything in the consul cluster that cannot be After this, the cluster should start back up, and the server agents rejoin. Shortly after that, the client agents should rejoin as well. #### Recover a failed cluster + If you have taken advantage of consul to store other data, and want to restore the failed cluster, please follow the [Consul guide](https://www.consul.io/docs/guides/outage.html) to recover a failed cluster. diff --git a/doc/administration/high_availability/database.md b/doc/administration/high_availability/database.md index 20bbfdb2603..1702a731647 100644 --- a/doc/administration/high_availability/database.md +++ b/doc/administration/high_availability/database.md @@ -1,6 +1,6 @@ # Configuring PostgreSQL for Scaling and High Availability -## Provide your own PostgreSQL instance **[CORE ONLY]** +## Provide your own PostgreSQL instance **(CORE ONLY)** If you're hosting GitLab on a cloud provider, you can optionally use a managed service for PostgreSQL. For example, AWS offers a managed Relational @@ -21,27 +21,27 @@ This section is relevant for [Scaled Architecture](README.md#scalable-architectu environments including [Basic Scaling](README.md#basic-scaling) and [Full Scaling](README.md#full-scaling). -### Provide your own PostgreSQL instance **[CORE ONLY]** +### Provide your own PostgreSQL instance **(CORE ONLY)** If you want to use your own deployed PostgreSQL instance(s), see [Provide your own PostgreSQL instance](#provide-your-own-postgresql-instance-core-only) for more details. However, you can use the GitLab Omnibus package to easily deploy the bundled PostgreSQL. -### Standalone PostgreSQL using GitLab Omnibus **[CORE ONLY]** +### Standalone PostgreSQL using GitLab Omnibus **(CORE ONLY)** 1. SSH into the PostgreSQL server. -1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab +1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab package you want using **steps 1 and 2** from the GitLab downloads page. - - Do not complete any other steps on the download page. + - Do not complete any other steps on the download page. 1. Generate a password hash for PostgreSQL. This assumes you will use the default username of `gitlab` (recommended). The command will request a password and confirmation. Use the value that is output by this command in the next step as the value of `POSTGRESQL_PASSWORD_HASH`. - ```sh - sudo gitlab-ctl pg-password-md5 gitlab - ``` + ```sh + sudo gitlab-ctl pg-password-md5 gitlab + ``` 1. Edit `/etc/gitlab/gitlab.rb` and add the contents below, updating placeholder values appropriately. @@ -51,32 +51,32 @@ deploy the bundled PostgreSQL. addresses of the GitLab application servers that will connect to the database. Example: `%w(123.123.123.123/32 123.123.123.234/32)` - ```ruby - # Disable all components except PostgreSQL - roles ['postgres_role'] - repmgr['enable'] = false - consul['enable'] = false - prometheus['enable'] = false - alertmanager['enable'] = false - pgbouncer_exporter['enable'] = false - redis_exporter['enable'] = false - gitlab_monitor['enable'] = false - - postgresql['listen_address'] = '0.0.0.0' - postgresql['port'] = 5432 - - # Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value - postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH' - - # Replace XXX.XXX.XXX.XXX/YY with Network Address - # ???? - postgresql['trust_auth_cidr_addresses'] = %w(APPLICATION_SERVER_IP_BLOCKS) - - # Disable automatic database migrations - gitlab_rails['auto_migrate'] = false - ``` + ```ruby + # Disable all components except PostgreSQL + roles ['postgres_role'] + repmgr['enable'] = false + consul['enable'] = false + prometheus['enable'] = false + alertmanager['enable'] = false + pgbouncer_exporter['enable'] = false + redis_exporter['enable'] = false + gitlab_monitor['enable'] = false + + postgresql['listen_address'] = '0.0.0.0' + postgresql['port'] = 5432 + + # Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value + postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH' + + # Replace XXX.XXX.XXX.XXX/YY with Network Address + # ???? + postgresql['trust_auth_cidr_addresses'] = %w(APPLICATION_SERVER_IP_BLOCKS) + + # Disable automatic database migrations + gitlab_rails['auto_migrate'] = false + ``` - NOTE: **Note:** The role `postgres_role` was introduced with GitLab 10.3 + NOTE: **Note:** The role `postgres_role` was introduced with GitLab 10.3 1. [Reconfigure GitLab] for the changes to take effect. 1. Note the PostgreSQL node's IP address or hostname, port, and @@ -97,14 +97,14 @@ environments including [Horizontal](README.md#horizontal), [Hybrid](README.md#hybrid), and [Fully Distributed](README.md#fully-distributed). -### Provide your own PostgreSQL instance **[CORE ONLY]** +### Provide your own PostgreSQL instance **(CORE ONLY)** If you want to use your own deployed PostgreSQL instance(s), see [Provide your own PostgreSQL instance](#provide-your-own-postgresql-instance-core-only) for more details. However, you can use the GitLab Omnibus package to easily deploy the bundled PostgreSQL. -### High Availability with GitLab Omnibus **[PREMIUM ONLY]** +### High Availability with GitLab Omnibus **(PREMIUM ONLY)** > Important notes: > @@ -194,9 +194,9 @@ When using default setup, minimum configuration requires: - `CONSUL_PASSWORD_HASH`. This is a hash generated out of consul username/password pair. Can be generated with: - ```sh - sudo gitlab-ctl pg-password-md5 CONSUL_USERNAME - ``` + ```sh + sudo gitlab-ctl pg-password-md5 CONSUL_USERNAME + ``` - `CONSUL_SERVER_NODES`. The IP addresses or DNS records of the Consul server nodes. @@ -237,9 +237,9 @@ We will need the following password information for the application's database u - `POSTGRESQL_PASSWORD_HASH`. This is a hash generated out of the username/password pair. Can be generated with: - ```sh - sudo gitlab-ctl pg-password-md5 POSTGRESQL_USERNAME - ``` + ```sh + sudo gitlab-ctl pg-password-md5 POSTGRESQL_USERNAME + ``` ##### Pgbouncer information @@ -250,9 +250,9 @@ When using default setup, minimum configuration requires: - `PGBOUNCER_PASSWORD_HASH`. This is a hash generated out of pgbouncer username/password pair. Can be generated with: - ```sh - sudo gitlab-ctl pg-password-md5 PGBOUNCER_USERNAME - ``` + ```sh + sudo gitlab-ctl pg-password-md5 PGBOUNCER_USERNAME + ``` - `PGBOUNCER_NODE`, is the IP address or a FQDN of the node running Pgbouncer. @@ -281,119 +281,68 @@ Few notes on the service itself: #### Installing Omnibus GitLab -First, make sure to [download/install](https://about.gitlab.com/installation) +First, make sure to [download/install](https://about.gitlab.com/install/) GitLab Omnibus **on each node**. Make sure you install the necessary dependencies from step 1, add GitLab package repository from step 2. When installing the GitLab package, do not supply `EXTERNAL_URL` value. -#### Configuring the Consul nodes - -On each Consul node perform the following: - -1. Make sure you collect [`CONSUL_SERVER_NODES`](#consul-information) before executing the next step. - -1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: - - ```ruby - # Disable all components except Consul - roles ['consul_role'] - - # START user configuration - # Replace placeholders: - # - # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z - # with the addresses gathered for CONSUL_SERVER_NODES - consul['configuration'] = { - server: true, - retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z) - } - - # Disable auto migrations - gitlab_rails['auto_migrate'] = false - # - # END user configuration - ``` - - > `consul_role` was introduced with GitLab 10.3 - -1. [Reconfigure GitLab] for the changes to take effect. - -##### Consul Checkpoint - -Before moving on, make sure Consul is configured correctly. Run the following -command to verify all server nodes are communicating: - -```sh -/opt/gitlab/embedded/bin/consul members -``` - -The output should be similar to: - -``` -Node Address Status Type Build Protocol DC -CONSUL_NODE_ONE XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul -CONSUL_NODE_TWO XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul -CONSUL_NODE_THREE XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul -``` - -If any of the nodes isn't `alive` or if any of the three nodes are missing, -check the [Troubleshooting section](#troubleshooting) before proceeding. - #### Configuring the Database nodes +1. Make sure to [configure the Consul nodes](consul.md). 1. Make sure you collect [`CONSUL_SERVER_NODES`](#consul-information), [`PGBOUNCER_PASSWORD_HASH`](#pgbouncer-information), [`POSTGRESQL_PASSWORD_HASH`](#postgresql-information), the [number of db nodes](#postgresql-information), and the [network address](#network-information) before executing the next step. 1. On the master database node, edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: - ```ruby - # Disable all components except PostgreSQL and Repmgr and Consul - roles ['postgres_role'] - - # PostgreSQL configuration - postgresql['listen_address'] = '0.0.0.0' - postgresql['hot_standby'] = 'on' - postgresql['wal_level'] = 'replica' - postgresql['shared_preload_libraries'] = 'repmgr_funcs' - - # Disable automatic database migrations - gitlab_rails['auto_migrate'] = false - - # Configure the consul agent - consul['services'] = %w(postgresql) - - # START user configuration - # Please set the real values as explained in Required Information section - # - # Replace PGBOUNCER_PASSWORD_HASH with a generated md5 value - postgresql['pgbouncer_user_password'] = 'PGBOUNCER_PASSWORD_HASH' - # Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value - postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH' - # Replace X with value of number of db nodes + 1 - postgresql['max_wal_senders'] = X - - # Replace XXX.XXX.XXX.XXX/YY with Network Address - postgresql['trust_auth_cidr_addresses'] = %w(XXX.XXX.XXX.XXX/YY) - repmgr['trust_auth_cidr_addresses'] = %w(127.0.0.1/32 XXX.XXX.XXX.XXX/YY) - - # Replace placeholders: - # - # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z - # with the addresses gathered for CONSUL_SERVER_NODES - consul['configuration'] = { - retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z) - } - # - # END user configuration - ``` - - > `postgres_role` was introduced with GitLab 10.3 + ```ruby + # Disable all components except PostgreSQL and Repmgr and Consul + roles ['postgres_role'] + + # PostgreSQL configuration + postgresql['listen_address'] = '0.0.0.0' + postgresql['hot_standby'] = 'on' + postgresql['wal_level'] = 'replica' + postgresql['shared_preload_libraries'] = 'repmgr_funcs' + + # Disable automatic database migrations + gitlab_rails['auto_migrate'] = false + + # Configure the consul agent + consul['services'] = %w(postgresql) + + # START user configuration + # Please set the real values as explained in Required Information section + # + # Replace PGBOUNCER_PASSWORD_HASH with a generated md5 value + postgresql['pgbouncer_user_password'] = 'PGBOUNCER_PASSWORD_HASH' + # Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value + postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH' + # Replace X with value of number of db nodes + 1 + postgresql['max_wal_senders'] = X + + # Replace XXX.XXX.XXX.XXX/YY with Network Address + postgresql['trust_auth_cidr_addresses'] = %w(XXX.XXX.XXX.XXX/YY) + repmgr['trust_auth_cidr_addresses'] = %w(127.0.0.1/32 XXX.XXX.XXX.XXX/YY) + + # Replace placeholders: + # + # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z + # with the addresses gathered for CONSUL_SERVER_NODES + consul['configuration'] = { + retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z) + } + # + # END user configuration + ``` + + > `postgres_role` was introduced with GitLab 10.3 1. On secondary nodes, add all the configuration specified above for primary node to `/etc/gitlab/gitlab.rb`. In addition, append the following configuration to inform gitlab-ctl that they are standby nodes initially and it need not attempt to register them as primary node + ``` # HA setting to specify if a node should attempt to be master on initialization repmgr['master_on_initialization'] = false @@ -418,31 +367,31 @@ Select one node as a primary node. 1. Open a database prompt: - ```sh - gitlab-psql -d gitlabhq_production - ``` + ```sh + gitlab-psql -d gitlabhq_production + ``` 1. Enable the `pg_trgm` extension: - ```sh - CREATE EXTENSION pg_trgm; - ``` + ```sh + CREATE EXTENSION pg_trgm; + ``` 1. Exit the database prompt by typing `\q` and Enter. 1. Verify the cluster is initialized with one node: - ```sh - gitlab-ctl repmgr cluster show - ``` + ```sh + gitlab-ctl repmgr cluster show + ``` - The output should be similar to the following: + The output should be similar to the following: - ``` - Role | Name | Upstream | Connection String - ----------+----------|----------|---------------------------------------- - * master | HOSTNAME | | host=HOSTNAME user=gitlab_repmgr dbname=gitlab_repmgr - ``` + ``` + Role | Name | Upstream | Connection String + ----------+----------|----------|---------------------------------------- + * master | HOSTNAME | | host=HOSTNAME user=gitlab_repmgr dbname=gitlab_repmgr + ``` 1. Note down the hostname/ip in the connection string: `host=HOSTNAME`. We will refer to the hostname in the next section as `MASTER_NODE_NAME`. If the value @@ -453,43 +402,43 @@ Select one node as a primary node. 1. Set up the repmgr standby: - ```sh - gitlab-ctl repmgr standby setup MASTER_NODE_NAME - ``` - - Do note that this will remove the existing data on the node. The command - has a wait time. - - The output should be similar to the following: - - ```console - # gitlab-ctl repmgr standby setup MASTER_NODE_NAME - Doing this will delete the entire contents of /var/opt/gitlab/postgresql/data - If this is not what you want, hit Ctrl-C now to exit - To skip waiting, rerun with the -w option - Sleeping for 30 seconds - Stopping the database - Removing the data - Cloning the data - Starting the database - Registering the node with the cluster - ok: run: repmgrd: (pid 19068) 0s - ``` + ```sh + gitlab-ctl repmgr standby setup MASTER_NODE_NAME + ``` + + Do note that this will remove the existing data on the node. The command + has a wait time. + + The output should be similar to the following: + + ```console + # gitlab-ctl repmgr standby setup MASTER_NODE_NAME + Doing this will delete the entire contents of /var/opt/gitlab/postgresql/data + If this is not what you want, hit Ctrl-C now to exit + To skip waiting, rerun with the -w option + Sleeping for 30 seconds + Stopping the database + Removing the data + Cloning the data + Starting the database + Registering the node with the cluster + ok: run: repmgrd: (pid 19068) 0s + ``` 1. Verify the node now appears in the cluster: - ```sh - gitlab-ctl repmgr cluster show - ``` + ```sh + gitlab-ctl repmgr cluster show + ``` - The output should be similar to the following: + The output should be similar to the following: - ``` - Role | Name | Upstream | Connection String - ----------+---------|-----------|------------------------------------------------ - * master | MASTER | | host=MASTER_NODE_NAME user=gitlab_repmgr dbname=gitlab_repmgr - standby | STANDBY | MASTER | host=STANDBY_HOSTNAME user=gitlab_repmgr dbname=gitlab_repmgr - ``` + ``` + Role | Name | Upstream | Connection String + ----------+---------|-----------|------------------------------------------------ + * master | MASTER | | host=MASTER_NODE_NAME user=gitlab_repmgr dbname=gitlab_repmgr + standby | STANDBY | MASTER | host=STANDBY_HOSTNAME user=gitlab_repmgr dbname=gitlab_repmgr + ``` Repeat the above steps on all secondary nodes. @@ -529,88 +478,7 @@ Check the [Troubleshooting section](#troubleshooting) before proceeding. #### Configuring the Pgbouncer node -1. Make sure you collect [`CONSUL_SERVER_NODES`](#consul-information), [`CONSUL_PASSWORD_HASH`](#consul-information), and [`PGBOUNCER_PASSWORD_HASH`](#pgbouncer-information) before executing the next step. - -1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: - - ```ruby - # Disable all components except Pgbouncer and Consul agent - roles ['pgbouncer_role'] - - # Configure Pgbouncer - pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul) - - # Configure Consul agent - consul['watchers'] = %w(postgresql) - - # START user configuration - # Please set the real values as explained in Required Information section - # Replace CONSUL_PASSWORD_HASH with with a generated md5 value - # Replace PGBOUNCER_PASSWORD_HASH with with a generated md5 value - pgbouncer['users'] = { - 'gitlab-consul': { - password: 'CONSUL_PASSWORD_HASH' - }, - 'pgbouncer': { - password: 'PGBOUNCER_PASSWORD_HASH' - } - } - # Replace placeholders: - # - # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z - # with the addresses gathered for CONSUL_SERVER_NODES - consul['configuration'] = { - retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z) - } - # - # END user configuration - ``` - - > `pgbouncer_role` was introduced with GitLab 10.3 - -1. [Reconfigure GitLab] for the changes to take effect. - -1. Create a `.pgpass` file so Consule is able to - reload pgbouncer. Enter the `PGBOUNCER_PASSWORD` twice when asked: - - ```sh - gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul - ``` - -##### PGBouncer Checkpoint - -1. Ensure the node is talking to the current master: - - ```sh - gitlab-ctl pgb-console # You will be prompted for PGBOUNCER_PASSWORD - ``` - - If there is an error `psql: ERROR: Auth failed` after typing in the - password, ensure you previously generated the MD5 password hashes with the correct - format. The correct format is to concatenate the password and the username: - `PASSWORDUSERNAME`. For example, `Sup3rS3cr3tpgbouncer` would be the text - needed to generate an MD5 password hash for the `pgbouncer` user. - -1. Once the console prompt is available, run the following queries: - - ```sh - show databases ; show clients ; - ``` - - The output should be similar to the following: - - ``` - name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections - ---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+--------------------- - gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production | | 20 | 0 | | 0 | 0 - pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0 - (2 rows) - - type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | ptr | link | remote_pid | tls - ------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+----- - C | pgbouncer | pgbouncer | active | 127.0.0.1 | 56846 | 127.0.0.1 | 6432 | 2017-08-21 18:09:59 | 2017-08-21 18:10:48 | 0x22b3880 | | 0 | - (2 rows) - ``` +See our [documentation for Pgbouncer](pgbouncer.md) for information on running Pgbouncer as part of an HA setup. #### Configuring the Application nodes @@ -619,15 +487,15 @@ attributes set, but the following need to be set. 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - # Disable PostgreSQL on the application node - postgresql['enable'] = false + ```ruby + # Disable PostgreSQL on the application node + postgresql['enable'] = false - gitlab_rails['db_host'] = 'PGBOUNCER_NODE' - gitlab_rails['db_port'] = 6432 - gitlab_rails['db_password'] = 'POSTGRESQL_USER_PASSWORD' - gitlab_rails['auto_migrate'] = false - ``` + gitlab_rails['db_host'] = 'PGBOUNCER_NODE' + gitlab_rails['db_port'] = 6432 + gitlab_rails['db_password'] = 'POSTGRESQL_USER_PASSWORD' + gitlab_rails['auto_migrate'] = false + ``` 1. [Reconfigure GitLab] for the changes to take effect. @@ -787,45 +655,45 @@ After deploying the configuration follow these steps: 1. On `10.6.0.21`, our primary database - Enable the `pg_trgm` extension + Enable the `pg_trgm` extension - ```sh - gitlab-psql -d gitlabhq_production - ``` + ```sh + gitlab-psql -d gitlabhq_production + ``` - ``` - CREATE EXTENSION pg_trgm; - ``` + ``` + CREATE EXTENSION pg_trgm; + ``` 1. On `10.6.0.22`, our first standby database - Make this node a standby of the primary + Make this node a standby of the primary - ```sh - gitlab-ctl repmgr standby setup 10.6.0.21 - ``` + ```sh + gitlab-ctl repmgr standby setup 10.6.0.21 + ``` 1. On `10.6.0.23`, our second standby database - Make this node a standby of the primary + Make this node a standby of the primary - ```sh - gitlab-ctl repmgr standby setup 10.6.0.21 - ``` + ```sh + gitlab-ctl repmgr standby setup 10.6.0.21 + ``` 1. On `10.6.0.31`, our application server - Set gitlab-consul's pgbouncer password to `toomanysecrets` + Set gitlab-consul's pgbouncer password to `toomanysecrets` - ```sh - gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul - ``` + ```sh + gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul + ``` - Run database migrations + Run database migrations - ```sh - gitlab-rake gitlab:db:configure - ``` + ```sh + gitlab-rake gitlab:db:configure + ``` #### Example minimal setup @@ -962,16 +830,16 @@ standby nodes. 1. Ensure the old master node is not still active. 1. Login to the server that should become the new master and run: - ```sh - gitlab-ctl repmgr standby promote - ``` + ```sh + gitlab-ctl repmgr standby promote + ``` 1. If there are any other standby servers in the cluster, have them follow the new master server: - ```sh - gitlab-ctl repmgr standby follow NEW_MASTER - ``` + ```sh + gitlab-ctl repmgr standby follow NEW_MASTER + ``` #### Restore procedure @@ -981,42 +849,42 @@ after it has been restored to service. - If you want to remove the node from the cluster, on any other node in the cluster, run: - ```sh - gitlab-ctl repmgr standby unregister --node=X - ``` + ```sh + gitlab-ctl repmgr standby unregister --node=X + ``` - where X is the value of node in `repmgr.conf` on the old server. + where X is the value of node in `repmgr.conf` on the old server. - To find this, you can use: + To find this, you can use: - ```sh - awk -F = '$1 == "node" { print $2 }' /var/opt/gitlab/postgresql/repmgr.conf - ``` + ```sh + awk -F = '$1 == "node" { print $2 }' /var/opt/gitlab/postgresql/repmgr.conf + ``` - It will output something like: + It will output something like: - ``` - 959789412 - ``` + ``` + 959789412 + ``` - Then you will use this id to unregister the node: + Then you will use this id to unregister the node: - ```sh - gitlab-ctl repmgr standby unregister --node=959789412 - ``` + ```sh + gitlab-ctl repmgr standby unregister --node=959789412 + ``` - To add the node as a standby server: - ```sh - gitlab-ctl repmgr standby follow NEW_MASTER - gitlab-ctl restart repmgrd - ``` + ```sh + gitlab-ctl repmgr standby follow NEW_MASTER + gitlab-ctl restart repmgrd + ``` - CAUTION: **Warning:** When the server is brought back online, and before - you switch it to a standby node, repmgr will report that there are two masters. - If there are any clients that are still attempting to write to the old master, - this will cause a split, and the old master will need to be resynced from - scratch by performing a `gitlab-ctl repmgr standby setup NEW_MASTER`. + CAUTION: **Warning:** When the server is brought back online, and before + you switch it to a standby node, repmgr will report that there are two masters. + If there are any clients that are still attempting to write to the old master, + this will cause a split, and the old master will need to be resynced from + scratch by performing a `gitlab-ctl repmgr standby setup NEW_MASTER`. #### Alternate configurations @@ -1059,13 +927,13 @@ the previous section: 1. On the current master node, create a password for the `gitlab` and `gitlab_repmgr` user: - ```sh - gitlab-psql -d template1 - template1=# \password gitlab_repmgr - Enter password: **** - Confirm password: **** - template1=# \password gitlab - ``` + ```sh + gitlab-psql -d template1 + template1=# \password gitlab_repmgr + Enter password: **** + Confirm password: **** + template1=# \password gitlab + ``` 1. On each database node: @@ -1079,9 +947,9 @@ the previous section: 1. Create a `.pgpass` file. Enter the `gitlab_repmgr` password twice to when asked: - ```sh - gitlab-ctl write-pgpass --user gitlab_repmgr --hostuser gitlab-psql --database '*' - ``` + ```sh + gitlab-ctl write-pgpass --user gitlab_repmgr --hostuser gitlab-psql --database '*' + ``` 1. On each pgbouncer node, edit `/etc/gitlab/gitlab.rb`: 1. Ensure `gitlab_rails['db_password']` is set to the plaintext password for @@ -1109,7 +977,7 @@ If you enable Monitoring, it must be enabled on **all** database servers. ## Troubleshooting -#### Consul and PostgreSQL changes not taking effect. +### Consul and PostgreSQL changes not taking effect Due to the potential impacts, `gitlab-ctl reconfigure` only reloads Consul and PostgreSQL, it will not restart the services. However, not all changes can be activated by reloading. @@ -1119,7 +987,7 @@ For PostgreSQL, it is usually safe to restart the master node by default. Automa On the consul server nodes, it is important to restart the consul service in a controlled fashion. Read our [consul documentation](consul.md#restarting-the-server-cluster) for instructions on how to restart the service. -#### `gitlab-ctl repmgr-check-master` command produces errors +### `gitlab-ctl repmgr-check-master` command produces errors If this command displays errors about database permissions it is likely that something failed during install, resulting in the `gitlab-consul` database user getting incorrect permissions. Follow these @@ -1134,7 +1002,7 @@ steps to fix the problem: Now there should not be errors. If errors still occur then there is another problem. -#### PGBouncer error `ERROR: pgbouncer cannot connect to server` +### PGBouncer error `ERROR: pgbouncer cannot connect to server` You may get this error when running `gitlab-rake gitlab:db:configure` or you may see the error in the PGBouncer log file. @@ -1162,7 +1030,7 @@ postgresql['trust_auth_cidr_addresses'] = %w(123.123.123.123/32 <other_cidrs>) [Reconfigure GitLab] for the changes to take effect. -#### Issues with other components +### Issues with other components If you're running into an issue with a component not outlined here, be sure to check the troubleshooting section of their specific documentation page. diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md index 3045be616a6..83838928519 100644 --- a/doc/administration/high_availability/gitlab.md +++ b/doc/administration/high_availability/gitlab.md @@ -7,33 +7,33 @@ 1. If necessary, install the NFS client utility packages using the following commands: - ``` - # Ubuntu/Debian - apt-get install nfs-common + ``` + # Ubuntu/Debian + apt-get install nfs-common - # CentOS/Red Hat - yum install nfs-utils nfs-utils-lib - ``` + # CentOS/Red Hat + yum install nfs-utils nfs-utils-lib + ``` 1. Specify the necessary NFS shares. Mounts are specified in `/etc/fstab`. The exact contents of `/etc/fstab` will depend on how you chose to configure your NFS server. See [NFS documentation](nfs.md) for the various options. Here is an example snippet to add to `/etc/fstab`: - ``` - 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - 10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - ``` + ``` + 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + ``` 1. Create the shared directories. These may be different depending on your NFS mount locations. - ``` - mkdir -p /var/opt/gitlab/.ssh /var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/git-data - ``` + ``` + mkdir -p /var/opt/gitlab/.ssh /var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/git-data + ``` 1. Download/install GitLab Omnibus using **steps 1 and 2** from [GitLab downloads](https://about.gitlab.com/downloads). Do not complete other @@ -46,52 +46,52 @@ added NFS mounts in the default data locations. Additionally the UID and GIDs given are just examples and you should configure with your preferred values. - ```ruby - external_url 'https://gitlab.example.com' - - # Prevent GitLab from starting if NFS data mounts are not available - high_availability['mountpoint'] = '/var/opt/gitlab/git-data' - - # Disable components that will not be on the GitLab application server - roles ['application_role'] - nginx['enable'] = true - - # PostgreSQL connection details - gitlab_rails['db_adapter'] = 'postgresql' - gitlab_rails['db_encoding'] = 'unicode' - gitlab_rails['db_host'] = '10.1.0.5' # IP/hostname of database server - gitlab_rails['db_password'] = 'DB password' - - # Redis connection details - gitlab_rails['redis_port'] = '6379' - gitlab_rails['redis_host'] = '10.1.0.6' # IP/hostname of Redis server - gitlab_rails['redis_password'] = 'Redis Password' - - # Ensure UIDs and GIDs match between servers for permissions via NFS - user['uid'] = 9000 - user['gid'] = 9000 - web_server['uid'] = 9001 - web_server['gid'] = 9001 - registry['uid'] = 9002 - registry['gid'] = 9002 - ``` + ```ruby + external_url 'https://gitlab.example.com' + + # Prevent GitLab from starting if NFS data mounts are not available + high_availability['mountpoint'] = '/var/opt/gitlab/git-data' + + # Disable components that will not be on the GitLab application server + roles ['application_role'] + nginx['enable'] = true + + # PostgreSQL connection details + gitlab_rails['db_adapter'] = 'postgresql' + gitlab_rails['db_encoding'] = 'unicode' + gitlab_rails['db_host'] = '10.1.0.5' # IP/hostname of database server + gitlab_rails['db_password'] = 'DB password' + + # Redis connection details + gitlab_rails['redis_port'] = '6379' + gitlab_rails['redis_host'] = '10.1.0.6' # IP/hostname of Redis server + gitlab_rails['redis_password'] = 'Redis Password' + + # Ensure UIDs and GIDs match between servers for permissions via NFS + user['uid'] = 9000 + user['gid'] = 9000 + web_server['uid'] = 9001 + web_server['gid'] = 9001 + registry['uid'] = 9002 + registry['gid'] = 9002 + ``` 1. [Enable monitoring](#enable-monitoring) - > **Note:** To maintain uniformity of links across HA clusters, the `external_url` - on the first application server as well as the additional application - servers should point to the external url that users will use to access GitLab. - In a typical HA setup, this will be the url of the load balancer which will - route traffic to all GitLab application servers in the HA cluster. - > - > **Note:** When you specify `https` in the `external_url`, as in the example - above, GitLab assumes you have SSL certificates in `/etc/gitlab/ssl/`. If - certificates are not present, Nginx will fail to start. See - [Nginx documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https) - for more information. - > - > **Note:** It is best to set the `uid` and `gid`s prior to the initial reconfigure - of GitLab. Omnibus will not recursively `chown` directories if set after the initial reconfigure. + > **Note:** To maintain uniformity of links across HA clusters, the `external_url` + on the first application server as well as the additional application + servers should point to the external url that users will use to access GitLab. + In a typical HA setup, this will be the url of the load balancer which will + route traffic to all GitLab application servers in the HA cluster. + > + > **Note:** When you specify `https` in the `external_url`, as in the example + above, GitLab assumes you have SSL certificates in `/etc/gitlab/ssl/`. If + certificates are not present, Nginx will fail to start. See + [Nginx documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https) + for more information. + > + > **Note:** It is best to set the `uid` and `gid`s prior to the initial reconfigure + of GitLab. Omnibus will not recursively `chown` directories if set after the initial reconfigure. ## First GitLab application server @@ -114,18 +114,18 @@ need some extra configuration. secondary servers **prior to** running the first `reconfigure` in the steps above. - ```ruby - gitlab_shell['secret_token'] = 'fbfb19c355066a9afb030992231c4a363357f77345edd0f2e772359e5be59b02538e1fa6cae8f93f7d23355341cea2b93600dab6d6c3edcdced558fc6d739860' - gitlab_rails['otp_key_base'] = 'b719fe119132c7810908bba18315259ed12888d4f5ee5430c42a776d840a396799b0a5ef0a801348c8a357f07aa72bbd58e25a84b8f247a25c72f539c7a6c5fa' - gitlab_rails['secret_key_base'] = '6e657410d57c71b4fc3ed0d694e7842b1895a8b401d812c17fe61caf95b48a6d703cb53c112bc01ebd197a85da81b18e29682040e99b4f26594772a4a2c98c6d' - gitlab_rails['db_key_base'] = 'bf2e47b68d6cafaef1d767e628b619365becf27571e10f196f98dc85e7771042b9203199d39aff91fcb6837c8ed83f2a912b278da50999bb11a2fbc0fba52964' - ``` + ```ruby + gitlab_shell['secret_token'] = 'fbfb19c355066a9afb030992231c4a363357f77345edd0f2e772359e5be59b02538e1fa6cae8f93f7d23355341cea2b93600dab6d6c3edcdced558fc6d739860' + gitlab_rails['otp_key_base'] = 'b719fe119132c7810908bba18315259ed12888d4f5ee5430c42a776d840a396799b0a5ef0a801348c8a357f07aa72bbd58e25a84b8f247a25c72f539c7a6c5fa' + gitlab_rails['secret_key_base'] = '6e657410d57c71b4fc3ed0d694e7842b1895a8b401d812c17fe61caf95b48a6d703cb53c112bc01ebd197a85da81b18e29682040e99b4f26594772a4a2c98c6d' + gitlab_rails['db_key_base'] = 'bf2e47b68d6cafaef1d767e628b619365becf27571e10f196f98dc85e7771042b9203199d39aff91fcb6837c8ed83f2a912b278da50999bb11a2fbc0fba52964' + ``` 1. Run `touch /etc/gitlab/skip-auto-reconfigure` to prevent database migrations from running on upgrade. Only the primary GitLab application server should handle migrations. -1. **Optional** Configure host keys. Copy all contents(primary and public keys) inside `/etc/ssh/` on +1. **Recommended** Configure host keys. Copy the contents (primary and public keys) of `/etc/ssh/` on the primary application server to `/etc/ssh` on all secondary servers. This prevents false man-in-the-middle-attack alerts when accessing servers in your High Availability cluster behind a load balancer. diff --git a/doc/administration/high_availability/monitoring_node.md b/doc/administration/high_availability/monitoring_node.md index ef415dde10a..cbc1d4bcd52 100644 --- a/doc/administration/high_availability/monitoring_node.md +++ b/doc/administration/high_availability/monitoring_node.md @@ -4,7 +4,7 @@ ## Standalone Monitoring node using GitLab Omnibus -The GitLab Omnibus package can be used to configure a standalone Monitoring node running Prometheus and Grafana. +The GitLab Omnibus package can be used to configure a standalone Monitoring node running [Prometheus](../monitoring/prometheus/index.md) and [Grafana](../monitoring/performance/grafana_configuration.md). The monitoring node is not highly available. See [Scaling and High Availability](README.md) for an overview of GitLab scaling and high availability options. @@ -12,7 +12,7 @@ The steps below are the minimum necessary to configure a Monitoring node running Omnibus: 1. SSH into the Monitoring node. -1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab +1. [Download/install](https://about.gitlab.com/install) the Omnibus GitLab package you want using **steps 1 and 2** from the GitLab downloads page. - Do not complete any other steps on the download page. @@ -20,44 +20,44 @@ Omnibus: 1. Edit `/etc/gitlab/gitlab.rb` and add the contents: - ```ruby - external_url 'http://gitlab.example.com' - - # Enable Prometheus - prometheus['enable'] = true - prometheus['listen_address'] = '0.0.0.0:9090' - prometheus['monitor_kubernetes'] = false - - # Enable Grafana - grafana['enable'] = true - grafana['admin_password'] = 'toomanysecrets' - - # Enable service discovery for Prometheus - consul['enable'] = true - consul['monitoring_service_discovery'] = true - - # Replace placeholders - # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z - # with the addresses of the Consul server nodes - consul['configuration'] = { - retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z), - } - - # Disable all other services - gitlab_rails['auto_migrate'] = false - alertmanager['enable'] = false - gitaly['enable'] = false - gitlab_monitor['enable'] = false - gitlab_workhorse['enable'] = false - nginx['enable'] = true - postgres_exporter['enable'] = false - postgresql['enable'] = false - redis['enable'] = false - redis_exporter['enable'] = false - sidekiq['enable'] = false - unicorn['enable'] = false - node_exporter['enable'] = false - ``` + ```ruby + external_url 'http://gitlab.example.com' + + # Enable Prometheus + prometheus['enable'] = true + prometheus['listen_address'] = '0.0.0.0:9090' + prometheus['monitor_kubernetes'] = false + + # Enable Grafana + grafana['enable'] = true + grafana['admin_password'] = 'toomanysecrets' + + # Enable service discovery for Prometheus + consul['enable'] = true + consul['monitoring_service_discovery'] = true + + # Replace placeholders + # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z + # with the addresses of the Consul server nodes + consul['configuration'] = { + retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z), + } + + # Disable all other services + gitlab_rails['auto_migrate'] = false + alertmanager['enable'] = false + gitaly['enable'] = false + gitlab_monitor['enable'] = false + gitlab_workhorse['enable'] = false + nginx['enable'] = true + postgres_exporter['enable'] = false + postgresql['enable'] = false + redis['enable'] = false + redis_exporter['enable'] = false + sidekiq['enable'] = false + unicorn['enable'] = false + node_exporter['enable'] = false + ``` 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md index 561ba214686..6ab6b8bed30 100644 --- a/doc/administration/high_availability/nfs.md +++ b/doc/administration/high_availability/nfs.md @@ -42,8 +42,8 @@ maintaining ID mapping without LDAP, in most cases you should enable numeric UID and GIDs (which is off by default in some cases) for simplified permission management between systems: - - [NetApp instructions](https://library.netapp.com/ecmdocs/ECMP1401220/html/GUID-24367A9F-E17B-4725-ADC1-02D86F56F78E.html) - - For non-NetApp devices, disable NFSv4 `idmapping` by performing opposite of [enable NFSv4 idmapper](https://wiki.archlinux.org/index.php/NFS#Enabling_NFSv4_idmapping) +- [NetApp instructions](https://library.netapp.com/ecmdocs/ECMP1401220/html/GUID-24367A9F-E17B-4725-ADC1-02D86F56F78E.html) +- For non-NetApp devices, disable NFSv4 `idmapping` by performing opposite of [enable NFSv4 idmapper](https://wiki.archlinux.org/index.php/NFS#Enabling_NFSv4_idmapping) ### Improving NFS performance with GitLab @@ -87,10 +87,10 @@ on an Linux NFS server, do the following: 1. On the NFS server, run: - ```sh - echo 0 > /proc/sys/fs/leases-enable - sysctl -w fs.leases-enable=0 - ``` + ```sh + echo 0 > /proc/sys/fs/leases-enable + sysctl -w fs.leases-enable=0 + ``` 1. Restart the NFS server process. For example, on CentOS run `service nfs restart`. diff --git a/doc/administration/high_availability/pgbouncer.md b/doc/administration/high_availability/pgbouncer.md index 053dae25823..6890b0f7db7 100644 --- a/doc/administration/high_availability/pgbouncer.md +++ b/doc/administration/high_availability/pgbouncer.md @@ -14,7 +14,88 @@ It is recommended to run pgbouncer alongside the `gitlab-rails` service, or on i ### Running Pgbouncer as part of an HA GitLab installation -See our [HA documentation for PostgreSQL](database.md) for information on running pgbouncer as part of a HA setup +1. Make sure you collect [`CONSUL_SERVER_NODES`](database.md#consul-information), [`CONSUL_PASSWORD_HASH`](database.md#consul-information), and [`PGBOUNCER_PASSWORD_HASH`](database.md#pgbouncer-information) before executing the next step. + +1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: + + ```ruby + # Disable all components except Pgbouncer and Consul agent + roles ['pgbouncer_role'] + + # Configure Pgbouncer + pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul) + + # Configure Consul agent + consul['watchers'] = %w(postgresql) + + # START user configuration + # Please set the real values as explained in Required Information section + # Replace CONSUL_PASSWORD_HASH with with a generated md5 value + # Replace PGBOUNCER_PASSWORD_HASH with with a generated md5 value + pgbouncer['users'] = { + 'gitlab-consul': { + password: 'CONSUL_PASSWORD_HASH' + }, + 'pgbouncer': { + password: 'PGBOUNCER_PASSWORD_HASH' + } + } + # Replace placeholders: + # + # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z + # with the addresses gathered for CONSUL_SERVER_NODES + consul['configuration'] = { + retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z) + } + # + # END user configuration + ``` + + > `pgbouncer_role` was introduced with GitLab 10.3 + +1. Run `gitlab-ctl reconfigure` + +1. Create a `.pgpass` file so Consul is able to + reload pgbouncer. Enter the `PGBOUNCER_PASSWORD` twice when asked: + + ```sh + gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul + ``` + +#### PGBouncer Checkpoint + +1. Ensure the node is talking to the current master: + + ```sh + gitlab-ctl pgb-console # You will be prompted for PGBOUNCER_PASSWORD + ``` + + If there is an error `psql: ERROR: Auth failed` after typing in the + password, ensure you previously generated the MD5 password hashes with the correct + format. The correct format is to concatenate the password and the username: + `PASSWORDUSERNAME`. For example, `Sup3rS3cr3tpgbouncer` would be the text + needed to generate an MD5 password hash for the `pgbouncer` user. + +1. Once the console prompt is available, run the following queries: + + ```sh + show databases ; show clients ; + ``` + + The output should be similar to the following: + + ``` + name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections + ---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+--------------------- + gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production | | 20 | 0 | | 0 | 0 + pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0 + (2 rows) + + type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | ptr | link | remote_pid | tls + ------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+----- + C | pgbouncer | pgbouncer | active | 127.0.0.1 | 56846 | 127.0.0.1 | 6432 | 2017-08-21 18:09:59 | 2017-08-21 18:10:48 | 0x22b3880 | | 0 | + (2 rows) + ``` ### Running Pgbouncer as part of a non-HA GitLab installation @@ -24,39 +105,39 @@ See our [HA documentation for PostgreSQL](database.md) for information on runnin 1. On your database node, ensure the following is set in your `/etc/gitlab/gitlab.rb` - ```ruby - postgresql['pgbouncer_user_password'] = 'PGBOUNCER_USER_PASSWORD_HASH' - postgresql['sql_user_password'] = 'SQL_USER_PASSWORD_HASH' - postgresql['listen_address'] = 'XX.XX.XX.Y' # Where XX.XX.XX.Y is the ip address on the node postgresql should listen on - postgresql['md5_auth_cidr_addresses'] = %w(AA.AA.AA.B/32) # Where AA.AA.AA.B is the IP address of the pgbouncer node - ``` + ```ruby + postgresql['pgbouncer_user_password'] = 'PGBOUNCER_USER_PASSWORD_HASH' + postgresql['sql_user_password'] = 'SQL_USER_PASSWORD_HASH' + postgresql['listen_address'] = 'XX.XX.XX.Y' # Where XX.XX.XX.Y is the ip address on the node postgresql should listen on + postgresql['md5_auth_cidr_addresses'] = %w(AA.AA.AA.B/32) # Where AA.AA.AA.B is the IP address of the pgbouncer node + ``` 1. Run `gitlab-ctl reconfigure` - **Note:** If the database was already running, it will need to be restarted after reconfigure by running `gitlab-ctl restart postgresql`. + **Note:** If the database was already running, it will need to be restarted after reconfigure by running `gitlab-ctl restart postgresql`. 1. On the node you are running pgbouncer on, make sure the following is set in `/etc/gitlab/gitlab.rb` - ```ruby - pgbouncer['enable'] = true - pgbouncer['databases'] = { - gitlabhq_production: { - host: 'DATABASE_HOST', - user: 'pgbouncer', - password: 'PGBOUNCER_USER_PASSWORD_HASH' - } - } - ``` + ```ruby + pgbouncer['enable'] = true + pgbouncer['databases'] = { + gitlabhq_production: { + host: 'DATABASE_HOST', + user: 'pgbouncer', + password: 'PGBOUNCER_USER_PASSWORD_HASH' + } + } + ``` 1. Run `gitlab-ctl reconfigure` 1. On the node running unicorn, make sure the following is set in `/etc/gitlab/gitlab.rb` - ```ruby - gitlab_rails['db_host'] = 'PGBOUNCER_HOST' - gitlab_rails['db_port'] = '6432' - gitlab_rails['db_password'] = 'SQL_USER_PASSWORD' - ``` + ```ruby + gitlab_rails['db_host'] = 'PGBOUNCER_HOST' + gitlab_rails['db_port'] = '6432' + gitlab_rails['db_password'] = 'SQL_USER_PASSWORD' + ``` 1. Run `gitlab-ctl reconfigure` @@ -66,28 +147,28 @@ See our [HA documentation for PostgreSQL](database.md) for information on runnin > [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/3786) in GitLab 12.0. - If you enable Monitoring, it must be enabled on **all** pgbouncer servers. +If you enable Monitoring, it must be enabled on **all** pgbouncer servers. - 1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration: +1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration: - ```ruby - # Enable service discovery for Prometheus - consul['enable'] = true - consul['monitoring_service_discovery'] = true + ```ruby + # Enable service discovery for Prometheus + consul['enable'] = true + consul['monitoring_service_discovery'] = true - # Replace placeholders - # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z - # with the addresses of the Consul server nodes - consul['configuration'] = { - retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z), - } + # Replace placeholders + # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z + # with the addresses of the Consul server nodes + consul['configuration'] = { + retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z), + } - # Set the network addresses that the exporters will listen on - node_exporter['listen_address'] = '0.0.0.0:9100' - pgbouncer_exporter['listen_address'] = '0.0.0.0:9188' - ``` + # Set the network addresses that the exporters will listen on + node_exporter['listen_address'] = '0.0.0.0:9100' + pgbouncer_exporter['listen_address'] = '0.0.0.0:9188' + ``` - 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. +1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. ### Interacting with pgbouncer @@ -109,6 +190,7 @@ pgbouncer=# The password you will be prompted for is the PGBOUNCER_USER_PASSWORD To get some basic information about the instance, run + ```shell pgbouncer=# show databases; show clients; show servers; name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 874525dd836..c29514ed9f6 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -1,6 +1,6 @@ # Configuring Redis for Scaling and High Availability -## Provide your own Redis instance **[CORE ONLY]** +## Provide your own Redis instance **(CORE ONLY)** The following are the requirements for providing your own Redis instance: @@ -20,14 +20,14 @@ This section is relevant for [Scaled Architecture](README.md#scalable-architectu environments including [Basic Scaling](README.md#basic-scaling) and [Full Scaling](README.md#full-scaling). -### Provide your own Redis instance **[CORE ONLY]** +### Provide your own Redis instance **(CORE ONLY)** If you want to use your own deployed Redis instance(s), see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only) for more details. However, you can use the GitLab Omnibus package to easily deploy the bundled Redis. -### Standalone Redis using GitLab Omnibus **[CORE ONLY]** +### Standalone Redis using GitLab Omnibus **(CORE ONLY)** The GitLab Omnibus package can be used to configure a standalone Redis server. In this configuration Redis is not highly available, and represents a single @@ -41,34 +41,34 @@ The steps below are the minimum necessary to configure a Redis server with Omnibus: 1. SSH into the Redis server. -1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab +1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab package you want using **steps 1 and 2** from the GitLab downloads page. - Do not complete any other steps on the download page. 1. Edit `/etc/gitlab/gitlab.rb` and add the contents: - ```ruby - ## Enable Redis - redis['enable'] = true - - ## Disable all other services - sidekiq['enable'] = false - gitlab_workhorse['enable'] = false - unicorn['enable'] = false - postgresql['enable'] = false - nginx['enable'] = false - prometheus['enable'] = false - alertmanager['enable'] = false - pgbouncer_exporter['enable'] = false - gitlab_monitor['enable'] = false - gitaly['enable'] = false - - redis['bind'] = '0.0.0.0' - redis['port'] = '6379' - redis['password'] = 'SECRET_PASSWORD_HERE' - - gitlab_rails['auto_migrate'] = false - ``` + ```ruby + ## Enable Redis + redis['enable'] = true + + ## Disable all other services + sidekiq['enable'] = false + gitlab_workhorse['enable'] = false + unicorn['enable'] = false + postgresql['enable'] = false + nginx['enable'] = false + prometheus['enable'] = false + alertmanager['enable'] = false + pgbouncer_exporter['enable'] = false + gitlab_monitor['enable'] = false + gitaly['enable'] = false + + redis['bind'] = '0.0.0.0' + redis['port'] = '6379' + redis['password'] = 'SECRET_PASSWORD_HERE' + + gitlab_rails['auto_migrate'] = false + ``` 1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. 1. Note the Redis node's IP address or hostname, port, and @@ -89,14 +89,14 @@ environments including [Horizontal](README.md#horizontal), [Hybrid](README.md#hybrid), and [Fully Distributed](README.md#fully-distributed). -### Provide your own Redis instance **[CORE ONLY]** +### Provide your own Redis instance **(CORE ONLY)** If you want to use your own deployed Redis instance(s), see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only) for more details. However, you can use the GitLab Omnibus package to easily deploy the bundled Redis. -### High Availability with GitLab Omnibus **[PREMIUM ONLY]** +### High Availability with GitLab Omnibus **(PREMIUM ONLY)** > Experimental Redis Sentinel support was [introduced in GitLab 8.11][ce-1877]. Starting with 8.14, Redis Sentinel is no longer experimental. @@ -357,39 +357,39 @@ The prerequisites for a HA Redis setup are the following: ### Step 1. Configuring the master Redis instance 1. SSH into the **master** Redis server. -1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab +1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab package you want using **steps 1 and 2** from the GitLab downloads page. - - Make sure you select the correct Omnibus package, with the same version - and type (Community, Enterprise editions) of your current install. - - Do not complete any other steps on the download page. + - Make sure you select the correct Omnibus package, with the same version + and type (Community, Enterprise editions) of your current install. + - Do not complete any other steps on the download page. 1. Edit `/etc/gitlab/gitlab.rb` and add the contents: - ```ruby - # Specify server role as 'redis_master_role' - roles ['redis_master_role'] + ```ruby + # Specify server role as 'redis_master_role' + roles ['redis_master_role'] - # IP address pointing to a local IP that the other machines can reach to. - # You can also set bind to '0.0.0.0' which listen in all interfaces. - # If you really need to bind to an external accessible IP, make - # sure you add extra firewall rules to prevent unauthorized access. - redis['bind'] = '10.0.0.1' + # IP address pointing to a local IP that the other machines can reach to. + # You can also set bind to '0.0.0.0' which listen in all interfaces. + # If you really need to bind to an external accessible IP, make + # sure you add extra firewall rules to prevent unauthorized access. + redis['bind'] = '10.0.0.1' - # Define a port so Redis can listen for TCP requests which will allow other - # machines to connect to it. - redis['port'] = 6379 + # Define a port so Redis can listen for TCP requests which will allow other + # machines to connect to it. + redis['port'] = 6379 - # Set up password authentication for Redis (use the same password in all nodes). - redis['password'] = 'redis-password-goes-here' - ``` + # Set up password authentication for Redis (use the same password in all nodes). + redis['password'] = 'redis-password-goes-here' + ``` 1. Only the primary GitLab application server should handle migrations. To prevent database migrations from running on upgrade, add the following configuration to your `/etc/gitlab/gitlab.rb` file: - ``` - gitlab_rails['auto_migrate'] = false - ``` + ``` + gitlab_rails['auto_migrate'] = false + ``` 1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. @@ -400,44 +400,44 @@ The prerequisites for a HA Redis setup are the following: ### Step 2. Configuring the slave Redis instances 1. SSH into the **slave** Redis server. -1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab +1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab package you want using **steps 1 and 2** from the GitLab downloads page. - - Make sure you select the correct Omnibus package, with the same version - and type (Community, Enterprise editions) of your current install. - - Do not complete any other steps on the download page. + - Make sure you select the correct Omnibus package, with the same version + and type (Community, Enterprise editions) of your current install. + - Do not complete any other steps on the download page. 1. Edit `/etc/gitlab/gitlab.rb` and add the contents: - ```ruby - # Specify server role as 'redis_slave_role' - roles ['redis_slave_role'] + ```ruby + # Specify server role as 'redis_slave_role' + roles ['redis_slave_role'] - # IP address pointing to a local IP that the other machines can reach to. - # You can also set bind to '0.0.0.0' which listen in all interfaces. - # If you really need to bind to an external accessible IP, make - # sure you add extra firewall rules to prevent unauthorized access. - redis['bind'] = '10.0.0.2' + # IP address pointing to a local IP that the other machines can reach to. + # You can also set bind to '0.0.0.0' which listen in all interfaces. + # If you really need to bind to an external accessible IP, make + # sure you add extra firewall rules to prevent unauthorized access. + redis['bind'] = '10.0.0.2' - # Define a port so Redis can listen for TCP requests which will allow other - # machines to connect to it. - redis['port'] = 6379 + # Define a port so Redis can listen for TCP requests which will allow other + # machines to connect to it. + redis['port'] = 6379 - # The same password for Redis authentication you set up for the master node. - redis['password'] = 'redis-password-goes-here' + # The same password for Redis authentication you set up for the master node. + redis['password'] = 'redis-password-goes-here' - # The IP of the master Redis node. - redis['master_ip'] = '10.0.0.1' + # The IP of the master Redis node. + redis['master_ip'] = '10.0.0.1' - # Port of master Redis server, uncomment to change to non default. Defaults - # to `6379`. - #redis['master_port'] = 6379 - ``` + # Port of master Redis server, uncomment to change to non default. Defaults + # to `6379`. + #redis['master_port'] = 6379 + ``` 1. To prevent reconfigure from running automatically on upgrade, run: - ``` - sudo touch /etc/gitlab/skip-auto-reconfigure - ``` + ``` + sudo touch /etc/gitlab/skip-auto-reconfigure + ``` 1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. 1. Go through the steps again for all the other slave nodes. @@ -487,89 +487,89 @@ multiple machines with the Sentinel daemon. 1. **You can omit this step if the Sentinels will be hosted in the same node as the other Redis instances.** - [Download/install](https://about.gitlab.com/downloads-ee) the - Omnibus GitLab Enterprise Edition package using **steps 1 and 2** from the - GitLab downloads page. - - Make sure you select the correct Omnibus package, with the same version - the GitLab application is running. - - Do not complete any other steps on the download page. + [Download/install](https://about.gitlab.com/downloads-ee) the + Omnibus GitLab Enterprise Edition package using **steps 1 and 2** from the + GitLab downloads page. + - Make sure you select the correct Omnibus package, with the same version + the GitLab application is running. + - Do not complete any other steps on the download page. 1. Edit `/etc/gitlab/gitlab.rb` and add the contents (if you are installing the Sentinels in the same node as the other Redis instances, some values might be duplicate below): - ```ruby - roles ['redis_sentinel_role'] - - # Must be the same in every sentinel node - redis['master_name'] = 'gitlab-redis' - - # The same password for Redis authentication you set up for the master node. - redis['master_password'] = 'redis-password-goes-here' - - # The IP of the master Redis node. - redis['master_ip'] = '10.0.0.1' - - # Define a port so Redis can listen for TCP requests which will allow other - # machines to connect to it. - redis['port'] = 6379 - - # Port of master Redis server, uncomment to change to non default. Defaults - # to `6379`. - #redis['master_port'] = 6379 - - ## Configure Sentinel - sentinel['bind'] = '10.0.0.1' - - # Port that Sentinel listens on, uncomment to change to non default. Defaults - # to `26379`. - # sentinel['port'] = 26379 - - ## Quorum must reflect the amount of voting sentinels it take to start a failover. - ## Value must NOT be greater then the amount of sentinels. - ## - ## The quorum can be used to tune Sentinel in two ways: - ## 1. If a the quorum is set to a value smaller than the majority of Sentinels - ## we deploy, we are basically making Sentinel more sensible to master failures, - ## triggering a failover as soon as even just a minority of Sentinels is no longer - ## able to talk with the master. - ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are - ## making Sentinel able to failover only when there are a very large number (larger - ## than majority) of well connected Sentinels which agree about the master being down.s - sentinel['quorum'] = 2 - - ## Consider unresponsive server down after x amount of ms. - # sentinel['down_after_milliseconds'] = 10000 - - ## Specifies the failover timeout in milliseconds. It is used in many ways: - ## - ## - The time needed to re-start a failover after a previous failover was - ## already tried against the same master by a given Sentinel, is two - ## times the failover timeout. - ## - ## - The time needed for a slave replicating to a wrong master according - ## to a Sentinel current configuration, to be forced to replicate - ## with the right master, is exactly the failover timeout (counting since - ## the moment a Sentinel detected the misconfiguration). - ## - ## - The time needed to cancel a failover that is already in progress but - ## did not produced any configuration change (SLAVEOF NO ONE yet not - ## acknowledged by the promoted slave). - ## - ## - The maximum time a failover in progress waits for all the slaves to be - ## reconfigured as slaves of the new master. However even after this time - ## the slaves will be reconfigured by the Sentinels anyway, but not with - ## the exact parallel-syncs progression as specified. - # sentinel['failover_timeout'] = 60000 - ``` + ```ruby + roles ['redis_sentinel_role'] + + # Must be the same in every sentinel node + redis['master_name'] = 'gitlab-redis' + + # The same password for Redis authentication you set up for the master node. + redis['master_password'] = 'redis-password-goes-here' + + # The IP of the master Redis node. + redis['master_ip'] = '10.0.0.1' + + # Define a port so Redis can listen for TCP requests which will allow other + # machines to connect to it. + redis['port'] = 6379 + + # Port of master Redis server, uncomment to change to non default. Defaults + # to `6379`. + #redis['master_port'] = 6379 + + ## Configure Sentinel + sentinel['bind'] = '10.0.0.1' + + # Port that Sentinel listens on, uncomment to change to non default. Defaults + # to `26379`. + # sentinel['port'] = 26379 + + ## Quorum must reflect the amount of voting sentinels it take to start a failover. + ## Value must NOT be greater then the amount of sentinels. + ## + ## The quorum can be used to tune Sentinel in two ways: + ## 1. If a the quorum is set to a value smaller than the majority of Sentinels + ## we deploy, we are basically making Sentinel more sensible to master failures, + ## triggering a failover as soon as even just a minority of Sentinels is no longer + ## able to talk with the master. + ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are + ## making Sentinel able to failover only when there are a very large number (larger + ## than majority) of well connected Sentinels which agree about the master being down.s + sentinel['quorum'] = 2 + + ## Consider unresponsive server down after x amount of ms. + # sentinel['down_after_milliseconds'] = 10000 + + ## Specifies the failover timeout in milliseconds. It is used in many ways: + ## + ## - The time needed to re-start a failover after a previous failover was + ## already tried against the same master by a given Sentinel, is two + ## times the failover timeout. + ## + ## - The time needed for a slave replicating to a wrong master according + ## to a Sentinel current configuration, to be forced to replicate + ## with the right master, is exactly the failover timeout (counting since + ## the moment a Sentinel detected the misconfiguration). + ## + ## - The time needed to cancel a failover that is already in progress but + ## did not produced any configuration change (SLAVEOF NO ONE yet not + ## acknowledged by the promoted slave). + ## + ## - The maximum time a failover in progress waits for all the slaves to be + ## reconfigured as slaves of the new master. However even after this time + ## the slaves will be reconfigured by the Sentinels anyway, but not with + ## the exact parallel-syncs progression as specified. + # sentinel['failover_timeout'] = 60000 + ``` 1. To prevent database migrations from running on upgrade, run: - ``` - sudo touch /etc/gitlab/skip-auto-reconfigure - ``` + ``` + sudo touch /etc/gitlab/skip-auto-reconfigure + ``` - Only the primary GitLab application server should handle migrations. + Only the primary GitLab application server should handle migrations. 1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. 1. Go through the steps again for all the other Sentinel nodes. @@ -593,20 +593,20 @@ which ideally should not have Redis or Sentinels on it for a HA setup. 1. SSH into the server where the GitLab application is installed. 1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: - ``` - ## Must be the same in every sentinel node - redis['master_name'] = 'gitlab-redis' - - ## The same password for Redis authentication you set up for the master node. - redis['master_password'] = 'redis-password-goes-here' - - ## A list of sentinels with `host` and `port` - gitlab_rails['redis_sentinels'] = [ - {'host' => '10.0.0.1', 'port' => 26379}, - {'host' => '10.0.0.2', 'port' => 26379}, - {'host' => '10.0.0.3', 'port' => 26379} - ] - ``` + ```ruby + ## Must be the same in every sentinel node + redis['master_name'] = 'gitlab-redis' + + ## The same password for Redis authentication you set up for the master node. + redis['master_password'] = 'redis-password-goes-here' + + ## A list of sentinels with `host` and `port` + gitlab_rails['redis_sentinels'] = [ + {'host' => '10.0.0.1', 'port' => 26379}, + {'host' => '10.0.0.2', 'port' => 26379}, + {'host' => '10.0.0.3', 'port' => 26379} + ] + ``` 1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. @@ -791,31 +791,34 @@ cache, queues, and shared_state. To make this work with Sentinel: 1. Set the appropriate variable in `/etc/gitlab/gitlab.rb` for each instance you are using: - ```ruby - gitlab_rails['redis_cache_instance'] = REDIS_CACHE_URL - gitlab_rails['redis_queues_instance'] = REDIS_QUEUES_URL - gitlab_rails['redis_shared_state_instance'] = REDIS_SHARED_STATE_URL - ``` + ```ruby + gitlab_rails['redis_cache_instance'] = REDIS_CACHE_URL + gitlab_rails['redis_queues_instance'] = REDIS_QUEUES_URL + gitlab_rails['redis_shared_state_instance'] = REDIS_SHARED_STATE_URL + ``` + **Note**: Redis URLs should be in the format: `redis://:PASSWORD@SENTINEL_MASTER_NAME` - 1. PASSWORD is the plaintext password for the Redis instance - 1. SENTINEL_MASTER_NAME is the Sentinel master name (e.g. `gitlab-redis-cache`) + 1. PASSWORD is the plaintext password for the Redis instance + 1. SENTINEL_MASTER_NAME is the Sentinel master name (e.g. `gitlab-redis-cache`) + 1. Include an array of hashes with host/port combinations, such as the following: - ```ruby - gitlab_rails['redis_cache_sentinels'] = [ - { host: REDIS_CACHE_SENTINEL_HOST, port: PORT1 }, - { host: REDIS_CACHE_SENTINEL_HOST2, port: PORT2 } - ] - gitlab_rails['redis_queues_sentinels'] = [ - { host: REDIS_QUEUES_SENTINEL_HOST, port: PORT1 }, - { host: REDIS_QUEUES_SENTINEL_HOST2, port: PORT2 } - ] - gitlab_rails['redis_shared_state_sentinels'] = [ - { host: SHARED_STATE_SENTINEL_HOST, port: PORT1 }, - { host: SHARED_STATE_SENTINEL_HOST2, port: PORT2 } - ] - ``` + ```ruby + gitlab_rails['redis_cache_sentinels'] = [ + { host: REDIS_CACHE_SENTINEL_HOST, port: PORT1 }, + { host: REDIS_CACHE_SENTINEL_HOST2, port: PORT2 } + ] + gitlab_rails['redis_queues_sentinels'] = [ + { host: REDIS_QUEUES_SENTINEL_HOST, port: PORT1 }, + { host: REDIS_QUEUES_SENTINEL_HOST2, port: PORT2 } + ] + gitlab_rails['redis_shared_state_sentinels'] = [ + { host: SHARED_STATE_SENTINEL_HOST, port: PORT1 }, + { host: SHARED_STATE_SENTINEL_HOST2, port: PORT2 } + ] + ``` + 1. Note that for each persistence class, GitLab will default to using the configuration specified in `gitlab_rails['redis_sentinels']` unless overridden by the settings above. @@ -879,12 +882,12 @@ in order for the HA setup to work as expected. Before proceeding with the troubleshooting below, check your firewall rules: - Redis machines - - Accept TCP connection in `6379` - - Connect to the other Redis machines via TCP in `6379` + - Accept TCP connection in `6379` + - Connect to the other Redis machines via TCP in `6379` - Sentinel machines - - Accept TCP connection in `26379` - - Connect to other Sentinel machines via TCP in `26379` - - Connect to the Redis machines via TCP in `6379` + - Accept TCP connection in `26379` + - Connect to other Sentinel machines via TCP in `26379` + - Connect to the Redis machines via TCP in `6379` ### Troubleshooting Redis replication @@ -952,38 +955,38 @@ To make sure your configuration is correct: 1. SSH into your GitLab application server 1. Enter the Rails console: - ``` - # For Omnibus installations - sudo gitlab-rails console + ``` + # For Omnibus installations + sudo gitlab-rails console - # For source installations - sudo -u git rails console production - ``` + # For source installations + sudo -u git rails console production + ``` 1. Run in the console: - ```ruby - redis = Redis.new(Gitlab::Redis::SharedState.params) - redis.info - ``` + ```ruby + redis = Redis.new(Gitlab::Redis::SharedState.params) + redis.info + ``` - Keep this screen open and try to simulate a failover below. + Keep this screen open and try to simulate a failover below. 1. To simulate a failover on master Redis, SSH into the Redis server and run: - ```bash - # port must match your master redis port, and the sleep time must be a few seconds bigger than defined one - redis-cli -h localhost -p 6379 DEBUG sleep 20 - ``` + ```bash + # port must match your master redis port, and the sleep time must be a few seconds bigger than defined one + redis-cli -h localhost -p 6379 DEBUG sleep 20 + ``` 1. Then back in the Rails console from the first step, run: - ``` - redis.info - ``` + ``` + redis.info + ``` - You should see a different port after a few seconds delay - (the failover/reconnect time). + You should see a different port after a few seconds delay + (the failover/reconnect time). ## Changelog diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md index be6b547372a..a5463e5128c 100644 --- a/doc/administration/high_availability/redis_source.md +++ b/doc/administration/high_availability/redis_source.md @@ -49,22 +49,22 @@ Assuming that the Redis master instance IP is `10.0.0.1`: 1. [Install Redis](../../install/installation.md#7-redis). 1. Edit `/etc/redis/redis.conf`: - ```conf - ## Define a `bind` address pointing to a local IP that your other machines - ## can reach you. If you really need to bind to an external accessible IP, make - ## sure you add extra firewall rules to prevent unauthorized access: - bind 10.0.0.1 - - ## Define a `port` to force redis to listen on TCP so other machines can - ## connect to it (default port is `6379`). - port 6379 - - ## Set up password authentication (use the same password in all nodes). - ## The password should be defined equal for both `requirepass` and `masterauth` - ## when setting up Redis to use with Sentinel. - requirepass redis-password-goes-here - masterauth redis-password-goes-here - ``` + ```conf + ## Define a `bind` address pointing to a local IP that your other machines + ## can reach you. If you really need to bind to an external accessible IP, make + ## sure you add extra firewall rules to prevent unauthorized access: + bind 10.0.0.1 + + ## Define a `port` to force redis to listen on TCP so other machines can + ## connect to it (default port is `6379`). + port 6379 + + ## Set up password authentication (use the same password in all nodes). + ## The password should be defined equal for both `requirepass` and `masterauth` + ## when setting up Redis to use with Sentinel. + requirepass redis-password-goes-here + masterauth redis-password-goes-here + ``` 1. Restart the Redis service for the changes to take effect. @@ -75,25 +75,25 @@ Assuming that the Redis slave instance IP is `10.0.0.2`: 1. [Install Redis](../../install/installation.md#7-redis). 1. Edit `/etc/redis/redis.conf`: - ```conf - ## Define a `bind` address pointing to a local IP that your other machines - ## can reach you. If you really need to bind to an external accessible IP, make - ## sure you add extra firewall rules to prevent unauthorized access: - bind 10.0.0.2 + ```conf + ## Define a `bind` address pointing to a local IP that your other machines + ## can reach you. If you really need to bind to an external accessible IP, make + ## sure you add extra firewall rules to prevent unauthorized access: + bind 10.0.0.2 - ## Define a `port` to force redis to listen on TCP so other machines can - ## connect to it (default port is `6379`). - port 6379 + ## Define a `port` to force redis to listen on TCP so other machines can + ## connect to it (default port is `6379`). + port 6379 - ## Set up password authentication (use the same password in all nodes). - ## The password should be defined equal for both `requirepass` and `masterauth` - ## when setting up Redis to use with Sentinel. - requirepass redis-password-goes-here - masterauth redis-password-goes-here + ## Set up password authentication (use the same password in all nodes). + ## The password should be defined equal for both `requirepass` and `masterauth` + ## when setting up Redis to use with Sentinel. + requirepass redis-password-goes-here + masterauth redis-password-goes-here - ## Define `slaveof` pointing to the Redis master instance with IP and port. - slaveof 10.0.0.1 6379 - ``` + ## Define `slaveof` pointing to the Redis master instance with IP and port. + slaveof 10.0.0.1 6379 + ``` 1. Restart the Redis service for the changes to take effect. 1. Go through the steps again for all the other slave nodes. @@ -110,56 +110,57 @@ master with IP `10.0.0.1` (some settings might overlap with the master): 1. [Install Redis Sentinel](https://redis.io/topics/sentinel) 1. Edit `/etc/redis/sentinel.conf`: - ```conf - ## Define a `bind` address pointing to a local IP that your other machines - ## can reach you. If you really need to bind to an external accessible IP, make - ## sure you add extra firewall rules to prevent unauthorized access: - bind 10.0.0.1 - - ## Define a `port` to force Sentinel to listen on TCP so other machines can - ## connect to it (default port is `6379`). - port 26379 - - ## Set up password authentication (use the same password in all nodes). - ## The password should be defined equal for both `requirepass` and `masterauth` - ## when setting up Redis to use with Sentinel. - requirepass redis-password-goes-here - masterauth redis-password-goes-here - - ## Define with `sentinel auth-pass` the same shared password you have - ## defined for both Redis master and slaves instances. - sentinel auth-pass gitlab-redis redis-password-goes-here - - ## Define with `sentinel monitor` the IP and port of the Redis - ## master node, and the quorum required to start a failover. - sentinel monitor gitlab-redis 10.0.0.1 6379 2 - - ## Define with `sentinel down-after-milliseconds` the time in `ms` - ## that an unresponsive server will be considered down. - sentinel down-after-milliseconds gitlab-redis 10000 - - ## Define a value for `sentinel failover_timeout` in `ms`. This has multiple - ## meanings: - ## - ## * The time needed to re-start a failover after a previous failover was - ## already tried against the same master by a given Sentinel, is two - ## times the failover timeout. - ## - ## * The time needed for a slave replicating to a wrong master according - ## to a Sentinel current configuration, to be forced to replicate - ## with the right master, is exactly the failover timeout (counting since - ## the moment a Sentinel detected the misconfiguration). - ## - ## * The time needed to cancel a failover that is already in progress but - ## did not produced any configuration change (SLAVEOF NO ONE yet not - ## acknowledged by the promoted slave). - ## - ## * The maximum time a failover in progress waits for all the slaves to be - ## reconfigured as slaves of the new master. However even after this time - ## the slaves will be reconfigured by the Sentinels anyway, but not with - ## the exact parallel-syncs progression as specified. - sentinel failover_timeout 30000 - ``` + ```conf + ## Define a `bind` address pointing to a local IP that your other machines + ## can reach you. If you really need to bind to an external accessible IP, make + ## sure you add extra firewall rules to prevent unauthorized access: + bind 10.0.0.1 + + ## Define a `port` to force Sentinel to listen on TCP so other machines can + ## connect to it (default port is `6379`). + port 26379 + + ## Set up password authentication (use the same password in all nodes). + ## The password should be defined equal for both `requirepass` and `masterauth` + ## when setting up Redis to use with Sentinel. + requirepass redis-password-goes-here + masterauth redis-password-goes-here + + ## Define with `sentinel auth-pass` the same shared password you have + ## defined for both Redis master and slaves instances. + sentinel auth-pass gitlab-redis redis-password-goes-here + + ## Define with `sentinel monitor` the IP and port of the Redis + ## master node, and the quorum required to start a failover. + sentinel monitor gitlab-redis 10.0.0.1 6379 2 + + ## Define with `sentinel down-after-milliseconds` the time in `ms` + ## that an unresponsive server will be considered down. + sentinel down-after-milliseconds gitlab-redis 10000 + + ## Define a value for `sentinel failover_timeout` in `ms`. This has multiple + ## meanings: + ## + ## * The time needed to re-start a failover after a previous failover was + ## already tried against the same master by a given Sentinel, is two + ## times the failover timeout. + ## + ## * The time needed for a slave replicating to a wrong master according + ## to a Sentinel current configuration, to be forced to replicate + ## with the right master, is exactly the failover timeout (counting since + ## the moment a Sentinel detected the misconfiguration). + ## + ## * The time needed to cancel a failover that is already in progress but + ## did not produced any configuration change (SLAVEOF NO ONE yet not + ## acknowledged by the promoted slave). + ## + ## * The maximum time a failover in progress waits for all the slaves to be + ## reconfigured as slaves of the new master. However even after this time + ## the slaves will be reconfigured by the Sentinels anyway, but not with + ## the exact parallel-syncs progression as specified. + sentinel failover_timeout 30000 + ``` + 1. Restart the Redis service for the changes to take effect. 1. Go through the steps again for all the other Sentinel nodes. @@ -180,21 +181,21 @@ setup: [resque.yml.example][resque], and uncomment the Sentinel lines, pointing to the correct server credentials: - ```yaml - # resque.yaml - production: - url: redis://:redi-password-goes-here@gitlab-redis/ - sentinels: - - - host: 10.0.0.1 - port: 26379 # point to sentinel, not to redis port - - - host: 10.0.0.2 - port: 26379 # point to sentinel, not to redis port - - - host: 10.0.0.3 - port: 26379 # point to sentinel, not to redis port - ``` + ```yaml + # resque.yaml + production: + url: redis://:redi-password-goes-here@gitlab-redis/ + sentinels: + - + host: 10.0.0.1 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.2 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.3 + port: 26379 # point to sentinel, not to redis port + ``` 1. [Restart GitLab][restart] for the changes to take effect. @@ -232,23 +233,23 @@ or a failover promotes a different **Master** node. 1. In `/etc/redis/redis.conf`: - ```conf - bind 10.0.0.1 - port 6379 - requirepass redis-password-goes-here - masterauth redis-password-goes-here - ``` + ```conf + bind 10.0.0.1 + port 6379 + requirepass redis-password-goes-here + masterauth redis-password-goes-here + ``` 1. In `/etc/redis/sentinel.conf`: - ```conf - bind 10.0.0.1 - port 26379 - sentinel auth-pass gitlab-redis redis-password-goes-here - sentinel monitor gitlab-redis 10.0.0.1 6379 2 - sentinel down-after-milliseconds gitlab-redis 10000 - sentinel failover_timeout 30000 - ``` + ```conf + bind 10.0.0.1 + port 26379 + sentinel auth-pass gitlab-redis redis-password-goes-here + sentinel monitor gitlab-redis 10.0.0.1 6379 2 + sentinel down-after-milliseconds gitlab-redis 10000 + sentinel failover_timeout 30000 + ``` 1. Restart the Redis service for the changes to take effect. @@ -256,24 +257,24 @@ or a failover promotes a different **Master** node. 1. In `/etc/redis/redis.conf`: - ```conf - bind 10.0.0.2 - port 6379 - requirepass redis-password-goes-here - masterauth redis-password-goes-here - slaveof 10.0.0.1 6379 - ``` + ```conf + bind 10.0.0.2 + port 6379 + requirepass redis-password-goes-here + masterauth redis-password-goes-here + slaveof 10.0.0.1 6379 + ``` 1. In `/etc/redis/sentinel.conf`: - ```conf - bind 10.0.0.2 - port 26379 - sentinel auth-pass gitlab-redis redis-password-goes-here - sentinel monitor gitlab-redis 10.0.0.1 6379 2 - sentinel down-after-milliseconds gitlab-redis 10000 - sentinel failover_timeout 30000 - ``` + ```conf + bind 10.0.0.2 + port 26379 + sentinel auth-pass gitlab-redis redis-password-goes-here + sentinel monitor gitlab-redis 10.0.0.1 6379 2 + sentinel down-after-milliseconds gitlab-redis 10000 + sentinel failover_timeout 30000 + ``` 1. Restart the Redis service for the changes to take effect. @@ -281,24 +282,24 @@ or a failover promotes a different **Master** node. 1. In `/etc/redis/redis.conf`: - ```conf - bind 10.0.0.3 - port 6379 - requirepass redis-password-goes-here - masterauth redis-password-goes-here - slaveof 10.0.0.1 6379 - ``` + ```conf + bind 10.0.0.3 + port 6379 + requirepass redis-password-goes-here + masterauth redis-password-goes-here + slaveof 10.0.0.1 6379 + ``` 1. In `/etc/redis/sentinel.conf`: - ```conf - bind 10.0.0.3 - port 26379 - sentinel auth-pass gitlab-redis redis-password-goes-here - sentinel monitor gitlab-redis 10.0.0.1 6379 2 - sentinel down-after-milliseconds gitlab-redis 10000 - sentinel failover_timeout 30000 - ``` + ```conf + bind 10.0.0.3 + port 26379 + sentinel auth-pass gitlab-redis redis-password-goes-here + sentinel monitor gitlab-redis 10.0.0.1 6379 2 + sentinel down-after-milliseconds gitlab-redis 10000 + sentinel failover_timeout 30000 + ``` 1. Restart the Redis service for the changes to take effect. @@ -306,20 +307,20 @@ or a failover promotes a different **Master** node. 1. Edit `/home/git/gitlab/config/resque.yml`: - ```yaml - production: - url: redis://:redi-password-goes-here@gitlab-redis/ - sentinels: - - - host: 10.0.0.1 - port: 26379 # point to sentinel, not to redis port - - - host: 10.0.0.2 - port: 26379 # point to sentinel, not to redis port - - - host: 10.0.0.3 - port: 26379 # point to sentinel, not to redis port - ``` + ```yaml + production: + url: redis://:redi-password-goes-here@gitlab-redis/ + sentinels: + - + host: 10.0.0.1 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.2 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.3 + port: 26379 # point to sentinel, not to redis port + ``` 1. [Restart GitLab][restart] for the changes to take effect. diff --git a/doc/administration/audit_log.png b/doc/administration/img/audit_log.png Binary files differindex d4f4c2abf38..d4f4c2abf38 100644 --- a/doc/administration/audit_log.png +++ b/doc/administration/img/audit_log.png diff --git a/doc/administration/auditor_access_form.png b/doc/administration/img/auditor_access_form.png Binary files differindex c179a7d3b0a..c179a7d3b0a 100644 --- a/doc/administration/auditor_access_form.png +++ b/doc/administration/img/auditor_access_form.png diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md index 84a34ae7d6e..73a39a6dd35 100644 --- a/doc/administration/incoming_email.md +++ b/doc/administration/incoming_email.md @@ -11,7 +11,7 @@ GitLab has several features based on receiving incoming emails: allow GitLab users to create a new merge request by sending an email to a user-specific email address. - [Service Desk](../user/project/service_desk.md): provide e-mail support to - your customers through GitLab. **[PREMIUM]** + your customers through GitLab. **(PREMIUM)** ## Requirements @@ -102,16 +102,16 @@ for a real-world example of this exploit. 1. Reconfigure GitLab for the changes to take effect: - ```sh - sudo gitlab-ctl reconfigure - sudo gitlab-ctl restart - ``` + ```sh + sudo gitlab-ctl reconfigure + sudo gitlab-ctl restart + ``` 1. Verify that everything is configured correctly: - ```sh - sudo gitlab-rake gitlab:incoming_email:check - ``` + ```sh + sudo gitlab-rake gitlab:incoming_email:check + ``` Reply by email should now be working. @@ -119,31 +119,31 @@ Reply by email should now be working. 1. Go to the GitLab installation directory: - ```sh - cd /home/git/gitlab - ``` + ```sh + cd /home/git/gitlab + ``` 1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and fill in the details for your specific IMAP server and email account (see [examples](#config-examples) below). 1. Enable `mail_room` in the init script at `/etc/default/gitlab`: - ```sh - sudo mkdir -p /etc/default - echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab - ``` + ```sh + sudo mkdir -p /etc/default + echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab + ``` 1. Restart GitLab: - ```sh - sudo service gitlab restart - ``` + ```sh + sudo service gitlab restart + ``` 1. Verify that everything is configured correctly: - ```sh - sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production - ``` + ```sh + sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production + ``` Reply by email should now be working. diff --git a/doc/administration/index.md b/doc/administration/index.md index f480d18ea00..00c8863f200 100644 --- a/doc/administration/index.md +++ b/doc/administration/index.md @@ -2,7 +2,7 @@ description: 'Learn how to install, configure, update, and maintain your GitLab instance.' --- -# Administrator documentation **[CORE ONLY]** +# Administrator documentation **(CORE ONLY)** Learn how to administer your self-managed GitLab instance. @@ -11,7 +11,7 @@ GitLab has two product distributions available through [different subscriptions] - The open source [GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce). - The open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee). -You can [install either GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/). +You can [install either GitLab CE or GitLab EE](https://about.gitlab.com/install/ce-or-ee/). However, the features you'll have access to depend on the subscription you choose (Core, Starter, Premium, or Ultimate). @@ -32,14 +32,14 @@ Learn how to install, configure, update, and maintain your GitLab instance. ### Installing GitLab - [Install](../install/README.md): Requirements, directory structures, and installation methods. - - [Database load balancing](database_load_balancing.md): Distribute database queries among multiple database servers. **[STARTER ONLY]** - - [Omnibus support for log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only) **[STARTER ONLY]** + - [Database load balancing](database_load_balancing.md): Distribute database queries among multiple database servers. **(STARTER ONLY)** + - [Omnibus support for log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only) **(STARTER ONLY)** - [High Availability](high_availability/README.md): Configure multiple servers for scaling or high availability. - [Installing GitLab HA on Amazon Web Services (AWS)](../install/aws/index.md): Set up GitLab High Availability on Amazon AWS. -- [Geo](geo/replication/index.md): Replicate your GitLab instance to other geographic locations as a read-only fully operational version. **[PREMIUM ONLY]** -- [Disaster Recovery](geo/disaster_recovery/index.md): Quickly fail-over to a different site with minimal effort in a disaster situation. **[PREMIUM ONLY]** -- [Pivotal Tile](../install/pivotal/index.md): Deploy GitLab as a pre-configured appliance using Ops Manager (BOSH) for Pivotal Cloud Foundry. **[PREMIUM ONLY]** -- [Add License](../user/admin_area/license.md): Upload a license at install time to unlock features that are in paid tiers of GitLab. **[STARTER ONLY]** +- [Geo](geo/replication/index.md): Replicate your GitLab instance to other geographic locations as a read-only fully operational version. **(PREMIUM ONLY)** +- [Disaster Recovery](geo/disaster_recovery/index.md): Quickly fail-over to a different site with minimal effort in a disaster situation. **(PREMIUM ONLY)** +- [Pivotal Tile](../install/pivotal/index.md): Deploy GitLab as a pre-configured appliance using Ops Manager (BOSH) for Pivotal Cloud Foundry. **(PREMIUM ONLY)** +- [Add License](../user/admin_area/license.md): Upload a license at install time to unlock features that are in paid tiers of GitLab. **(STARTER ONLY)** ### Configuring GitLab @@ -60,9 +60,9 @@ Learn how to install, configure, update, and maintain your GitLab instance. - [Diff limits](../user/admin_area/diff_limits.md): Configure the diff rendering size limits of branch comparison pages. - [Merge request diffs storage](merge_request_diffs.md): Configure merge requests diffs external storage. - [Broadcast Messages](../user/admin_area/broadcast_messages.md): Send messages to GitLab users through the UI. -- [Elasticsearch](../integration/elasticsearch.md): Enable Elasticsearch to empower GitLab's Advanced Global Search. Useful when you deal with a huge amount of data. **[STARTER ONLY]** -- [External Classification Policy Authorization](../user/admin_area/settings/external_authorization.md) **[PREMIUM ONLY]** -- [Upload a license](../user/admin_area/license.md): Upload a license to unlock features that are in paid tiers of GitLab. **[STARTER ONLY]** +- [Elasticsearch](../integration/elasticsearch.md): Enable Elasticsearch to empower GitLab's Advanced Global Search. Useful when you deal with a huge amount of data. **(STARTER ONLY)** +- [External Classification Policy Authorization](../user/admin_area/settings/external_authorization.md) **(PREMIUM ONLY)** +- [Upload a license](../user/admin_area/license.md): Upload a license to unlock features that are in paid tiers of GitLab. **(STARTER ONLY)** - [Admin Area](../user/admin_area/index.md): for self-managed instance-wide configuration and maintenance. #### Customizing GitLab's appearance @@ -72,7 +72,7 @@ Learn how to install, configure, update, and maintain your GitLab instance. - [Branded login page](../customization/branded_login_page.md): Customize the login page with your own logo, title, and description. - [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page. - ["New Project" page](../customization/new_project_page.md): Customize the text to be displayed on the page that opens whenever your users create a new project. -- [Additional custom email text](../user/admin_area/settings/email.md#custom-additional-text-premium-only): Add additional custom text to emails sent from GitLab. **[PREMIUM ONLY]** +- [Additional custom email text](../user/admin_area/settings/email.md#custom-additional-text-premium-only): Add additional custom text to emails sent from GitLab. **(PREMIUM ONLY)** ### Maintaining GitLab @@ -107,15 +107,15 @@ Learn how to install, configure, update, and maintain your GitLab instance. - [Sign-up restrictions](../user/admin_area/settings/sign_up_restrictions.md): block email addresses of specific domains, or whitelist only specific domains. - [Access restrictions](../user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols): Define which Git access protocols can be used to talk to GitLab (SSH, HTTP, HTTPS). - [Authentication and Authorization](auth/README.md): Configure external authentication with LDAP, SAML, CAS and additional providers. - - [Sync LDAP](auth/ldap-ee.md) **[STARTER ONLY]** - - [Kerberos authentication](../integration/kerberos.md) **[STARTER ONLY]** + - [Sync LDAP](auth/ldap-ee.md) **(STARTER ONLY)** + - [Kerberos authentication](../integration/kerberos.md) **(STARTER ONLY)** - See also other [authentication](../topics/authentication/index.md#gitlab-administrators) topics (for example, enforcing 2FA). -- [Email users](../tools/email.md): Email GitLab users from within GitLab. **[STARTER ONLY]** +- [Email users](../tools/email.md): Email GitLab users from within GitLab. **(STARTER ONLY)** - [User Cohorts](../user/admin_area/user_cohorts.md): Display the monthly cohorts of new users and their activities over time. - [Audit logs and events](audit_events.md): View the changes made within the GitLab server for: - - Groups and projects. **[STARTER]** - - Instances. **[PREMIUM ONLY]** -- [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance. **[PREMIUM ONLY]** + - Groups and projects. **(STARTER)** + - Instances. **(PREMIUM ONLY)** +- [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance. **(PREMIUM ONLY)** - [Incoming email](incoming_email.md): Configure incoming emails to allow users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/managing_issues.md#new-issue-via-email) and [merge requests by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email), and to enable [Service Desk](../user/project/service_desk.md). @@ -131,15 +131,15 @@ Learn how to install, configure, update, and maintain your GitLab instance. - [Gitaly](gitaly/index.md): Configuring Gitaly, GitLab's Git repository storage service. - [Default labels](../user/admin_area/labels.md): Create labels that will be automatically added to every new project. - [Restrict the use of public or internal projects](../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects): Restrict the use of visibility levels for users when they create a project or a snippet. -- [Custom project templates](../user/admin_area/custom_project_templates.md): Configure a set of projects to be used as custom templates when creating a new project. **[PREMIUM ONLY]** -- [Packages](packages.md): Enable GitLab to act as a Maven repository or NPM registry. **[PREMIUM ONLY]** +- [Custom project templates](../user/admin_area/custom_project_templates.md): Configure a set of projects to be used as custom templates when creating a new project. **(PREMIUM ONLY)** +- [Packages](packages.md): Enable GitLab to act as a Maven repository or NPM registry. **(PREMIUM ONLY)** ### Repository settings - [Repository checks](repository_checks.md): Periodic Git repository checks. - [Repository storage paths](repository_storage_paths.md): Manage the paths used to store repositories. - [Repository storage rake tasks](raketasks/storage.md): A collection of rake tasks to list and migrate existing projects and attachments associated with it from Legacy storage to Hashed storage. -- [Limit repository size](../user/admin_area/settings/account_and_limit_settings.md): Set a hard limit for your repositories' size. **[STARTER ONLY]** +- [Limit repository size](../user/admin_area/settings/account_and_limit_settings.md): Set a hard limit for your repositories' size. **(STARTER ONLY)** ## Continuous Integration settings @@ -148,7 +148,7 @@ Learn how to install, configure, update, and maintain your GitLab instance. - [Job artifacts](job_artifacts.md): Enable, disable, and configure job artifacts (a set of files and directories which are outputted by a job when it completes successfully). - [Job traces](job_traces.md): Information about the job traces (logs). - [Register Shared and specific Runners](../ci/runners/README.md#registering-a-shared-runner): Learn how to register and configure Shared and specific Runners to your own instance. -- [Shared Runners pipelines quota](../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota-starter-only): Limit the usage of pipeline minutes for Shared Runners. **[STARTER ONLY]** +- [Shared Runners pipelines quota](../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota-starter-only): Limit the usage of pipeline minutes for Shared Runners. **(STARTER ONLY)** - [Enable/disable Auto DevOps](../topics/autodevops/index.md#enablingdisabling-auto-devops): Enable or disable Auto DevOps for your instance. ## Git configuration options @@ -178,7 +178,7 @@ Learn how to install, configure, update, and maintain your GitLab instance. ## Analytics -- [Pseudonymizer](pseudonymizer.md): Export data from GitLab's database to CSV files in a secure way. **[ULTIMATE]** +- [Pseudonymizer](pseudonymizer.md): Export data from GitLab's database to CSV files in a secure way. **(ULTIMATE)** ## Troubleshooting diff --git a/doc/administration/instance_review.md b/doc/administration/instance_review.md index b1244f44e95..ab6a4646a71 100644 --- a/doc/administration/instance_review.md +++ b/doc/administration/instance_review.md @@ -1,4 +1,4 @@ -# Instance Review **[CORE ONLY]** +# Instance Review **(CORE ONLY)** > [Introduced][6995] in [GitLab Core][ee] 11.3. diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md index 82e0c14ffc2..c2ac063ce37 100644 --- a/doc/administration/integration/plantuml.md +++ b/doc/administration/integration/plantuml.md @@ -1,6 +1,7 @@ # PlantUML & GitLab -> [Introduced][ce-8537] in GitLab 8.16. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8537) in +> GitLab 8.16. When [PlantUML](http://plantuml.com) integration is enabled and configured in GitLab we are able to create simple diagrams in AsciiDoc and Markdown documents @@ -15,7 +16,9 @@ server that will generate the diagrams. With Docker, you can just run a container like this: -`docker run -d --name plantuml -p 8080:8080 plantuml/plantuml-server:tomcat` +```sh +docker run -d --name plantuml -p 8080:8080 plantuml/plantuml-server:tomcat +``` The **PlantUML URL** will be the hostname of the server running the container. @@ -26,7 +29,7 @@ own PlantUML server is easy in Debian/Ubuntu distributions using Tomcat. First you need to create a `plantuml.war` file from the source code: -``` +```sh sudo apt-get install graphviz openjdk-8-jdk git-core maven git clone https://github.com/plantuml/plantuml-server.git cd plantuml-server @@ -36,7 +39,7 @@ mvn package The above sequence of commands will generate a WAR file that can be deployed using Tomcat: -``` +```sh sudo apt-get install tomcat7 sudo cp target/plantuml.war /var/lib/tomcat7/webapps/plantuml.war sudo chown tomcat7:tomcat7 /var/lib/tomcat7/webapps/plantuml.war @@ -46,7 +49,7 @@ sudo service tomcat7 restart Once the Tomcat service restarts the PlantUML service will be ready and listening for requests on port 8080: -``` +```text http://localhost:8080/plantuml ``` @@ -57,9 +60,10 @@ you can change these defaults by editing the `/etc/tomcat7/server.xml` file. You need to enable PlantUML integration from Settings under Admin Area. To do that, login with an Admin account and do following: - - in GitLab go to **Admin Area**->**Settings**->**Integrations**->**PlantUML** - - check **Enable PlantUML** checkbox - - set the PlantUML instance as **PlantUML URL** +- In GitLab, go to **Admin Area > Settings > Integrations**. +- Expand the **PlantUML** section. +- Check **Enable PlantUML** checkbox. +- Set the PlantUML instance as **PlantUML URL**. ## Creating Diagrams @@ -68,33 +72,34 @@ our AsciiDoc snippets, wikis and repos using delimited blocks: - **Markdown** - <pre> - ```plantuml - Bob -> Alice : hello - Alice -> Bob : Go Away - ```</pre> + ````markdown + ```plantuml + Bob -> Alice : hello + Alice -> Bob : Go Away + ``` + ```` - **AsciiDoc** - ``` - [plantuml, format="png", id="myDiagram", width="200px"] - ---- - Bob->Alice : hello - Alice -> Bob : Go Away - ---- - ``` + ```text + [plantuml, format="png", id="myDiagram", width="200px"] + ---- + Bob->Alice : hello + Alice -> Bob : Go Away + ---- + ``` - **reStructuredText** - ``` - .. plantuml:: - :caption: Caption with **bold** and *italic* + ```text + .. plantuml:: + :caption: Caption with **bold** and *italic* - Bob -> Alice: hello - Alice -> Bob: Go Away - ``` + Bob -> Alice: hello + Alice -> Bob: Go Away + ``` - You can also use the `uml::` directive for compatibility with [sphinxcontrib-plantuml](https://pypi.python.org/pypi/sphinxcontrib-plantuml), but please note that we currently only support the `caption` option. + You can also use the `uml::` directive for compatibility with [sphinxcontrib-plantuml](https://pypi.org/project/sphinxcontrib-plantuml/), but please note that we currently only support the `caption` option. The above blocks will be converted to an HTML img tag with source pointing to the PlantUML instance. If the PlantUML server is correctly configured, this should @@ -111,12 +116,10 @@ diagram delimiters `@startuml`/`@enduml` as these are replaced by the AsciiDoc ` Some parameters can be added to the AsciiDoc block definition: - - *format*: Can be either `png` or `svg`. Note that `svg` is not supported by - all browsers so use with care. The default is `png`. - - *id*: A CSS id added to the diagram HTML tag. - - *width*: Width attribute added to the img tag. - - *height*: Height attribute added to the img tag. +- *format*: Can be either `png` or `svg`. Note that `svg` is not supported by + all browsers so use with care. The default is `png`. +- *id*: A CSS id added to the diagram HTML tag. +- *width*: Width attribute added to the img tag. +- *height*: Height attribute added to the img tag. Markdown does not support any parameters and will always use PNG format. - -[ce-8537]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8537 diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md index c34858cd0db..24c9cc0bea9 100644 --- a/doc/administration/integration/terminal.md +++ b/doc/administration/integration/terminal.md @@ -41,9 +41,9 @@ detail below. - Every session URL that is created has an authorization header that needs to be sent, to establish a `wss` connection. - The session URL is not exposed to the users in any way. GitLab holds all the state internally and proxies accordingly. -## Enabling and disabling terminal support +## Enabling and disabling terminal support -NOTE: **Note:** AWS Elastic Load Balancers (ELBs) do not support web sockets. +NOTE: **Note:** AWS Elastic Load Balancers (ELBs) do not support web sockets. AWS Application Load Balancers (ALBs) must be used if you want web terminals to work. See [AWS Elastic Load Balancing Product Comparison](https://aws.amazon.com/elasticloadbalancing/features/#compare) for more information. diff --git a/doc/administration/issue_closing_pattern.md b/doc/administration/issue_closing_pattern.md index 9c352096ecc..e1bbabb2878 100644 --- a/doc/administration/issue_closing_pattern.md +++ b/doc/administration/issue_closing_pattern.md @@ -1,4 +1,4 @@ -# Issue closing pattern **[CORE ONLY]** +# Issue closing pattern **(CORE ONLY)** >**Note:** This is the administration documentation. @@ -27,9 +27,10 @@ Because Rubular doesn't understand `%{issue_ref}`, you can replace this by 1. Change the value of `gitlab_rails['gitlab_issue_closing_pattern']` to a regular expression of your liking: - ```ruby - gitlab_rails['gitlab_issue_closing_pattern'] = "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)" - ``` + ```ruby + gitlab_rails['gitlab_issue_closing_pattern'] = "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)" + ``` + 1. [Reconfigure] GitLab for the changes to take effect. **For installations from source** @@ -37,9 +38,9 @@ Because Rubular doesn't understand `%{issue_ref}`, you can replace this by 1. Open `gitlab.yml` with your editor. 1. Change the value of `issue_closing_pattern`: - ```yaml - issue_closing_pattern: "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)" - ``` + ```yaml + issue_closing_pattern: "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)" + ``` 1. [Restart] GitLab for the changes to take effect. diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md index 05e15fc303b..9df7b2526e2 100644 --- a/doc/administration/job_artifacts.md +++ b/doc/administration/job_artifacts.md @@ -1,7 +1,5 @@ # Jobs artifacts administration -> **Notes:** -> > - Introduced in GitLab 8.2 and GitLab Runner 0.7.0. > - Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format changed to `ZIP`. > - Starting with GitLab 8.17, builds are renamed to jobs. @@ -21,9 +19,9 @@ To disable artifacts site-wide, follow the steps below. 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['artifacts_enabled'] = false - ``` + ```ruby + gitlab_rails['artifacts_enabled'] = false + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -33,10 +31,10 @@ To disable artifacts site-wide, follow the steps below. 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - artifacts: - enabled: false - ``` + ```yaml + artifacts: + enabled: false + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. @@ -61,9 +59,9 @@ _The artifacts are stored by default in 1. To change the storage path for example to `/mnt/storage/artifacts`, edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['artifacts_path'] = "/mnt/storage/artifacts" - ``` + ```ruby + gitlab_rails['artifacts_path'] = "/mnt/storage/artifacts" + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -77,18 +75,16 @@ _The artifacts are stored by default in 1. To change the storage path for example to `/mnt/storage/artifacts`, edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - artifacts: - enabled: true - path: /mnt/storage/artifacts - ``` + ```yaml + artifacts: + enabled: true + path: /mnt/storage/artifacts + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. ### Using object storage -> **Notes:** -> > - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1762) in > [GitLab Premium](https://about.gitlab.com/pricing/) 9.4. > - Since version 9.5, artifacts are [browsable](../user/project/pipelines/job_artifacts.md#browsing-artifacts), @@ -141,35 +137,35 @@ _The artifacts are stored by default in 1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with the values you want: - ```ruby - gitlab_rails['artifacts_enabled'] = true - gitlab_rails['artifacts_object_store_enabled'] = true - gitlab_rails['artifacts_object_store_remote_directory'] = "artifacts" - gitlab_rails['artifacts_object_store_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-central-1', - 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', - 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY' - } - ``` - - NOTE: For GitLab 9.4+, if you are using AWS IAM profiles, be sure to omit the - AWS access key and secret access key/value pairs. For example: - - ```ruby - gitlab_rails['artifacts_object_store_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-central-1', - 'use_iam_profile' => true - } - ``` + ```ruby + gitlab_rails['artifacts_enabled'] = true + gitlab_rails['artifacts_object_store_enabled'] = true + gitlab_rails['artifacts_object_store_remote_directory'] = "artifacts" + gitlab_rails['artifacts_object_store_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-central-1', + 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', + 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY' + } + ``` + + NOTE: For GitLab 9.4+, if you are using AWS IAM profiles, be sure to omit the + AWS access key and secret access key/value pairs. For example: + + ```ruby + gitlab_rails['artifacts_object_store_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-central-1', + 'use_iam_profile' => true + } + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. 1. Migrate any existing local artifacts to the object storage: - ```bash - gitlab-rake gitlab:artifacts:migrate - ``` + ```bash + gitlab-rake gitlab:artifacts:migrate + ``` --- @@ -181,25 +177,25 @@ _The artifacts are stored by default in 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - artifacts: - enabled: true - object_store: - enabled: true - remote_directory: "artifacts" # The bucket name - connection: - provider: AWS # Only AWS supported at the moment - aws_access_key_id: AWS_ACCESS_KEY_ID - aws_secret_access_key: AWS_SECRET_ACCESS_KEY - region: eu-central-1 - ``` + ```yaml + artifacts: + enabled: true + object_store: + enabled: true + remote_directory: "artifacts" # The bucket name + connection: + provider: AWS # Only AWS supported at the moment + aws_access_key_id: AWS_ACCESS_KEY_ID + aws_secret_access_key: AWS_SECRET_ACCESS_KEY + region: eu-central-1 + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. 1. Migrate any existing local artifacts to the object storage: - ```bash - sudo -u git -H bundle exec rake gitlab:artifacts:migrate RAILS_ENV=production - ``` + ```bash + sudo -u git -H bundle exec rake gitlab:artifacts:migrate RAILS_ENV=production + ``` ## Expiring artifacts @@ -217,9 +213,9 @@ steps below. 1. Edit `/etc/gitlab/gitlab.rb` and comment out or add the following line - ```ruby - gitlab_rails['expire_build_artifacts_worker_cron'] = "50 * * * *" - ``` + ```ruby + gitlab_rails['expire_build_artifacts_worker_cron'] = "50 * * * *" + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -230,10 +226,10 @@ steps below. 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - expire_build_artifacts_worker: - cron: "50 * * * *" - ``` + ```yaml + expire_build_artifacts_worker: + cron: "50 * * * *" + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. @@ -250,15 +246,15 @@ you can flip the feature flag from a Rails console. 1. Enter the Rails console: - ```sh - sudo gitlab-rails console - ``` + ```sh + sudo gitlab-rails console + ``` 1. Flip the switch and disable it: - ```ruby - Feature.enable('ci_disable_validates_dependencies') - ``` + ```ruby + Feature.enable('ci_disable_validates_dependencies') + ``` --- @@ -266,16 +262,16 @@ you can flip the feature flag from a Rails console. 1. Enter the Rails console: - ```sh - cd /home/git/gitlab - RAILS_ENV=production sudo -u git -H bundle exec rails console - ``` + ```sh + cd /home/git/gitlab + RAILS_ENV=production sudo -u git -H bundle exec rails console + ``` 1. Flip the switch and disable it: - ```ruby - Feature.enable('ci_disable_validates_dependencies') - ``` + ```ruby + Feature.enable('ci_disable_validates_dependencies') + ``` ## Set the maximum file size of the artifacts diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md index aa9d87562a3..6a06eb240de 100644 --- a/doc/administration/job_traces.md +++ b/doc/administration/job_traces.md @@ -25,11 +25,11 @@ To change the location where the job logs will be stored, follow the steps below **In Omnibus installations:** -1. Edit `/etc/gitlab/gitlab.rb` and add or amend the following line: +1. Edit `/etc/gitlab/gitlab.rb` and add or amend the following line: - ``` - gitlab_ci['builds_directory'] = '/mnt/to/gitlab-ci/builds' - ``` + ```ruby + gitlab_ci['builds_directory'] = '/mnt/to/gitlab-ci/builds' + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -39,12 +39,12 @@ To change the location where the job logs will be stored, follow the steps below 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - gitlab_ci: - # The location where build traces are stored (default: builds/). - # Relative paths are relative to Rails.root. - builds_path: path/to/builds/ - ``` + ```yaml + gitlab_ci: + # The location where build traces are stored (default: builds/). + # Relative paths are relative to Rails.root. + builds_path: path/to/builds/ + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. @@ -67,24 +67,24 @@ To archive those legacy job traces, please follow the instruction below. 1. Execute the following command - ```bash - gitlab-rake gitlab:traces:archive - ``` + ```bash + gitlab-rake gitlab:traces:archive + ``` - After you executed this task, GitLab instance queues up Sidekiq jobs (asynchronous processes) - for migrating job trace files from local storage to object storage. - It could take time to complete the all migration jobs. You can check the progress by the following command + After you executed this task, GitLab instance queues up Sidekiq jobs (asynchronous processes) + for migrating job trace files from local storage to object storage. + It could take time to complete the all migration jobs. You can check the progress by the following command - ```bash - sudo gitlab-rails console - ``` + ```bash + sudo gitlab-rails console + ``` - ```bash - [1] pry(main)> Sidekiq::Stats.new.queues['pipeline_background:archive_trace'] - => 100 - ``` + ```bash + [1] pry(main)> Sidekiq::Stats.new.queues['pipeline_background:archive_trace'] + => 100 + ``` - If the count becomes zero, the archiving processes are done + If the count becomes zero, the archiving processes are done ## How to migrate archived job traces to object storage @@ -95,9 +95,9 @@ If job traces have already been archived into local storage, and you want to mig 1. Ensure [Object storage integration for Job Artifacts](job_artifacts.md#object-storage-settings) is enabled 1. Execute the following command - ```bash - gitlab-rake gitlab:traces:migrate - ``` + ```bash + gitlab-rake gitlab:traces:migrate + ``` ## How to remove job traces diff --git a/doc/administration/logs.md b/doc/administration/logs.md index b49d8c8a28f..5a2f389d298 100644 --- a/doc/administration/logs.md +++ b/doc/administration/logs.md @@ -4,7 +4,7 @@ GitLab has an advanced log system where everything is logged so that you can analyze your instance using various system log files. In addition to system log files, GitLab Enterprise Edition comes with Audit Events. Find more about them [in Audit Events -documentation](https://docs.gitlab.com/ee/administration/audit_events.html) +documentation](audit_events.md) System log files are typically plain text in a standard log file format. This guide talks about how to read and use these system log files. diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md index 5e9ba4f640f..99cd9051778 100644 --- a/doc/administration/merge_request_diffs.md +++ b/doc/administration/merge_request_diffs.md @@ -1,4 +1,4 @@ -# Merge request diffs storage **[CORE ONLY]** +# Merge request diffs storage **(CORE ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/52568) in GitLab 11.8. @@ -10,7 +10,7 @@ By default, merge request diffs are stored in the database, in a table named `merge_request_diff_files`. Larger installations may find this table grows too large, in which case, switching to external storage is recommended. -### Using external storage +## Using external storage Merge request diffs can be stored on disk, or in object storage. In general, it is better to store the diffs in the database than on disk. @@ -21,18 +21,18 @@ To enable external storage of merge request diffs, follow the instructions below 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['external_diffs_enabled'] = true - ``` + ```ruby + gitlab_rails['external_diffs_enabled'] = true + ``` 1. _The external diffs will be stored in in `/var/opt/gitlab/gitlab-rails/shared/external-diffs`._ To change the path, for example, to `/mnt/storage/external-diffs`, edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['external_diffs_storage_path'] = "/mnt/storage/external-diffs" - ``` + ```ruby + gitlab_rails['external_diffs_storage_path'] = "/mnt/storage/external-diffs" + ``` 1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. @@ -41,31 +41,31 @@ To enable external storage of merge request diffs, follow the instructions below 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - external_diffs: - enabled: true - ``` + ```yaml + external_diffs: + enabled: true + ``` -1. _The external diffs will be stored in +1. _The external diffs will be stored in `/home/git/gitlab/shared/external-diffs`._ To change the path, for example, to `/mnt/storage/external-diffs`, edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - external_diffs: - enabled: true - storage_path: /mnt/storage/external-diffs - ``` + ```yaml + external_diffs: + enabled: true + storage_path: /mnt/storage/external-diffs + ``` 1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect. -### Using object storage +## Using object storage Instead of storing the external diffs on disk, we recommended the use of an object store like AWS S3 instead. This configuration relies on valid AWS credentials to be configured already. -### Object Storage Settings +## Object Storage Settings For source installations, these settings are nested under `external_diffs:` and then `object_store:`. On Omnibus installations, they are prefixed by @@ -80,7 +80,7 @@ then `object_store:`. On Omnibus installations, they are prefixed by | `proxy_download` | Set to true to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` | | `connection` | Various connection options described below | | -#### S3 compatible connection settings +### S3 compatible connection settings The connection settings match those provided by [Fog](https://github.com/fog), and are as follows: @@ -101,28 +101,28 @@ The connection settings match those provided by [Fog](https://github.com/fog), a 1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with the values you want: - ```ruby - gitlab_rails['external_diffs_enabled'] = true - gitlab_rails['external_diffs_object_store_enabled'] = true - gitlab_rails['external_diffs_object_store_remote_directory'] = "external-diffs" - gitlab_rails['external_diffs_object_store_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-central-1', - 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', - 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY' - } - ``` - - Note that, if you are using AWS IAM profiles, be sure to omit the - AWS access key and secret access key/value pairs. For example: - - ```ruby - gitlab_rails['external_diffs_object_store_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-central-1', - 'use_iam_profile' => true - } - ``` + ```ruby + gitlab_rails['external_diffs_enabled'] = true + gitlab_rails['external_diffs_object_store_enabled'] = true + gitlab_rails['external_diffs_object_store_remote_directory'] = "external-diffs" + gitlab_rails['external_diffs_object_store_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-central-1', + 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', + 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY' + } + ``` + + Note that, if you are using AWS IAM profiles, be sure to omit the + AWS access key and secret access key/value pairs. For example: + + ```ruby + gitlab_rails['external_diffs_object_store_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-central-1', + 'use_iam_profile' => true + } + ``` 1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. @@ -131,22 +131,22 @@ The connection settings match those provided by [Fog](https://github.com/fog), a 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - external_diffs: - enabled: true - object_store: - enabled: true - remote_directory: "external-diffs" # The bucket name - connection: - provider: AWS # Only AWS supported at the moment - aws_access_key_id: AWS_ACCESS_KEY_ID - aws_secret_access_key: AWS_SECRET_ACCESS_KEY - region: eu-central-1 - ``` + ```yaml + external_diffs: + enabled: true + object_store: + enabled: true + remote_directory: "external-diffs" # The bucket name + connection: + provider: AWS # Only AWS supported at the moment + aws_access_key_id: AWS_ACCESS_KEY_ID + aws_secret_access_key: AWS_SECRET_ACCESS_KEY + region: eu-central-1 + ``` 1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect. -### Alternative in-database storage +## Alternative in-database storage Enabling external diffs may reduce the performance of merge requests, as they must be retrieved in a separate operation to other data. A compromise may be @@ -157,11 +157,11 @@ To enable this feature, perform the following steps: **In Omnibus installations:** -1. Edit `/etc/gitlab/gitlab.rb` and add the following line: +1. Edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['external_diffs_when'] = 'outdated' - ``` + ```ruby + gitlab_rails['external_diffs_when'] = 'outdated' + ``` 1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. @@ -170,11 +170,11 @@ To enable this feature, perform the following steps: 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - external_diffs: - enabled: true - when: outdated - ``` + ```yaml + external_diffs: + enabled: true + when: outdated + ``` 1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect. diff --git a/doc/administration/monitoring/ip_whitelist.md b/doc/administration/monitoring/ip_whitelist.md index ad2773de132..6bb2fe81b2c 100644 --- a/doc/administration/monitoring/ip_whitelist.md +++ b/doc/administration/monitoring/ip_whitelist.md @@ -12,9 +12,9 @@ hosts or use IP ranges: 1. Open `/etc/gitlab/gitlab.rb` and add or uncomment the following: - ```ruby - gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '192.168.0.1'] - ``` + ```ruby + gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '192.168.0.1'] + ``` 1. Save the file and [reconfigure] GitLab for the changes to take effect. @@ -24,13 +24,13 @@ hosts or use IP ranges: 1. Edit `config/gitlab.yml`: - ```yaml - monitoring: - # by default only local IPs are allowed to access monitoring resources - ip_whitelist: - - 127.0.0.0/8 - - 192.168.0.1 - ``` + ```yaml + monitoring: + # by default only local IPs are allowed to access monitoring resources + ip_whitelist: + - 127.0.0.0/8 + - 192.168.0.1 + ``` 1. Save the file and [restart] GitLab for the changes to take effect. diff --git a/doc/administration/monitoring/performance/grafana_configuration.md b/doc/administration/monitoring/performance/grafana_configuration.md index 51b0d78681d..4dd0bbbe937 100644 --- a/doc/administration/monitoring/performance/grafana_configuration.md +++ b/doc/administration/monitoring/performance/grafana_configuration.md @@ -1,6 +1,6 @@ # Grafana Configuration -[Grafana](http://grafana.org/) is a tool that allows you to visualize time +[Grafana](https://grafana.org/) is a tool that allows you to visualize time series metrics through graphs and dashboards. It supports several backend data stores, including InfluxDB. GitLab writes performance data to InfluxDB and Grafana will allow you to query to display useful graphs. @@ -13,7 +13,7 @@ services. [GitLab Omnibus can help you install Grafana (recommended)](https://docs.gitlab.com/omnibus/settings/grafana.html) or Grafana supplies package repositories (Yum/Apt) for easy installation. -See [Grafana installation documentation](http://docs.grafana.org/installation/) +See [Grafana installation documentation](https://grafana.com/docs/installation/) for detailed steps. NOTE: **Note:** diff --git a/doc/administration/monitoring/performance/influxdb_configuration.md b/doc/administration/monitoring/performance/influxdb_configuration.md index fa281f47ed8..cf6728510fe 100644 --- a/doc/administration/monitoring/performance/influxdb_configuration.md +++ b/doc/administration/monitoring/performance/influxdb_configuration.md @@ -187,7 +187,7 @@ Read more on: [influxdb documentation]: https://docs.influxdata.com/influxdb/v0.9/ [influxdb cli]: https://docs.influxdata.com/influxdb/v0.9/tools/shell/ [udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/ -[influxdb]: https://influxdata.com/time-series-platform/influxdb/ +[influxdb]: https://www.influxdata.com/products/influxdb-overview/ [tsm tree]: https://influxdata.com/blog/new-storage-engine-time-structured-merge-tree/ [tsm1-commit]: https://github.com/influxdata/influxdb/commit/15d723dc77651bac83e09e2b1c94be480966cb0d [influx-admin]: https://docs.influxdata.com/influxdb/v0.9/administration/authentication_and_authorization/#create-a-new-admin-user diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index f09548aa024..89501f20d99 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -49,7 +49,7 @@ The following metrics are available: | unicorn_queued_connections | Gauge | 11.0 | The number of queued Unicorn connections | | unicorn_workers | Gauge | 12.0 | The number of Unicorn workers | -## Sidekiq Metrics available for Geo **[PREMIUM]** +## Sidekiq Metrics available for Geo **(PREMIUM)** Sidekiq jobs may also gather metrics, and these metrics can be accessed if the Sidekiq exporter is enabled (e.g. via the `monitoring.sidekiq_exporter` configuration option in `gitlab.yml`. diff --git a/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md b/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md index f68b03d1ade..9aa4dfa5ab7 100644 --- a/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md +++ b/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md @@ -12,9 +12,9 @@ To enable the GitLab monitor exporter: 1. Edit `/etc/gitlab/gitlab.rb` 1. Add or find and uncomment the following line, making sure it's set to `true`: - ```ruby - gitlab_monitor['enable'] = true - ``` + ```ruby + gitlab_monitor['enable'] = true + ``` 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to take effect diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md index ce65d77274b..341ea3330d7 100644 --- a/doc/administration/monitoring/prometheus/index.md +++ b/doc/administration/monitoring/prometheus/index.md @@ -39,9 +39,9 @@ To disable Prometheus and all of its exporters, as well as any added in the futu 1. Edit `/etc/gitlab/gitlab.rb` 1. Add or find and uncomment the following line, making sure it's set to `false`: - ```ruby - prometheus_monitoring['enable'] = false - ``` + ```ruby + prometheus_monitoring['enable'] = false + ``` 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to take effect. @@ -61,19 +61,19 @@ To change the address/port that Prometheus listens on: 1. Edit `/etc/gitlab/gitlab.rb` 1. Add or find and uncomment the following line: - ```ruby - prometheus['listen_address'] = 'localhost:9090' - ``` + ```ruby + prometheus['listen_address'] = 'localhost:9090' + ``` - Replace `localhost:9090` with the address/port you want Prometheus to - listen on. If you would like to allow access to Prometheus to hosts other - than `localhost`, leave out the host, or use `0.0.0.0` to allow public access: + Replace `localhost:9090` with the address/port you want Prometheus to + listen on. If you would like to allow access to Prometheus to hosts other + than `localhost`, leave out the host, or use `0.0.0.0` to allow public access: - ```ruby - prometheus['listen_address'] = ':9090' - # or - prometheus['listen_address'] = '0.0.0.0:9090' - ``` + ```ruby + prometheus['listen_address'] = ':9090' + # or + prometheus['listen_address'] = '0.0.0.0:9090' + ``` 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to take effect @@ -90,22 +90,22 @@ To use an external Prometheus server: 1. Edit `/etc/gitlab/gitlab.rb`. 1. Disable the bundled Prometheus: - ```ruby - prometheus['enable'] = false - ``` + ```ruby + prometheus['enable'] = false + ``` 1. Set each bundled service's [exporter](#bundled-software-metrics) to listen on a network address, for example: - ```ruby - gitlab_monitor['listen_address'] = '0.0.0.0' - sidekiq['listen_address'] = '0.0.0.0' - gitlab_monitor['listen_port'] = '9168' - node_exporter['listen_address'] = '0.0.0.0:9100' - redis_exporter['listen_address'] = '0.0.0.0:9121' - postgres_exporter['listen_address'] = '0.0.0.0:9187' - gitaly['prometheus_listen_addr'] = "0.0.0.0:9236" - gitlab_workhorse['prometheus_listen_addr'] = "0.0.0.0:9229" - ``` + ```ruby + gitlab_monitor['listen_address'] = '0.0.0.0' + sidekiq['listen_address'] = '0.0.0.0' + gitlab_monitor['listen_port'] = '9168' + node_exporter['listen_address'] = '0.0.0.0:9100' + redis_exporter['listen_address'] = '0.0.0.0:9121' + postgres_exporter['listen_address'] = '0.0.0.0:9187' + gitaly['prometheus_listen_addr'] = "0.0.0.0:9236" + gitlab_workhorse['prometheus_listen_addr'] = "0.0.0.0:9229" + ``` 1. Install and set up a dedicated Prometheus instance, if necessary, using the [official installation instructions](https://prometheus.io/docs/prometheus/latest/installation/). 1. Add the Prometheus server IP address to the [monitoring IP whitelist](../ip_whitelist.html). For example: @@ -117,14 +117,14 @@ To use an external Prometheus server: 1. To scrape nginx metrics, you'll also need to configure nginx to allow the Prometheus server IP. For example: - ```ruby - nginx['status']['options'] = { - "server_tokens" => "off", - "access_log" => "off", - "allow" => "192.168.0.1", - "deny" => "all", - } - ``` + ```ruby + nginx['status']['options'] = { + "server_tokens" => "off", + "access_log" => "off", + "allow" => "192.168.0.1", + "deny" => "all", + } + ``` 1. [Reconfigure GitLab][reconfigure] to apply the changes 1. Edit the Prometheus server's configuration file. @@ -132,17 +132,17 @@ To use an external Prometheus server: [scrape target configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#%3Cscrape_config%3E). For example, a sample snippet using `static_configs`: - ```yaml - scrape_configs: - - job_name: 'gitlab_exporters' - static_configs: - - targets: ['1.1.1.1:9168', '1.1.1.1:9236', '1.1.1.1:9236', '1.1.1.1:9100', '1.1.1.1:9121', '1.1.1.1:9187'] + ```yaml + scrape_configs: + - job_name: 'gitlab_exporters' + static_configs: + - targets: ['1.1.1.1:9168', '1.1.1.1:9236', '1.1.1.1:9236', '1.1.1.1:9100', '1.1.1.1:9121', '1.1.1.1:9187'] - - job_name: 'gitlab_metrics' - metrics_path: /-/metrics - static_configs: - - targets: ['1.1.1.1:443'] - ``` + - job_name: 'gitlab_metrics' + metrics_path: /-/metrics + static_configs: + - targets: ['1.1.1.1:443'] + ``` 1. Restart the Prometheus server. @@ -241,9 +241,9 @@ To disable the monitoring of Kubernetes: 1. Edit `/etc/gitlab/gitlab.rb`. 1. Add or find and uncomment the following line and set it to `false`: - ```ruby - prometheus['monitor_kubernetes'] = false - ``` + ```ruby + prometheus['monitor_kubernetes'] = false + ``` 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to take effect. diff --git a/doc/administration/monitoring/prometheus/node_exporter.md b/doc/administration/monitoring/prometheus/node_exporter.md index aef7758a88f..bcacfaa3be5 100644 --- a/doc/administration/monitoring/prometheus/node_exporter.md +++ b/doc/administration/monitoring/prometheus/node_exporter.md @@ -13,9 +13,9 @@ To enable the node exporter: 1. Edit `/etc/gitlab/gitlab.rb` 1. Add or find and uncomment the following line, making sure it's set to `true`: - ```ruby - node_exporter['enable'] = true - ``` + ```ruby + node_exporter['enable'] = true + ``` 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to take effect diff --git a/doc/administration/monitoring/prometheus/pgbouncer_exporter.md b/doc/administration/monitoring/prometheus/pgbouncer_exporter.md index d76834fdbea..6183594c69c 100644 --- a/doc/administration/monitoring/prometheus/pgbouncer_exporter.md +++ b/doc/administration/monitoring/prometheus/pgbouncer_exporter.md @@ -12,9 +12,9 @@ To enable the PgBouncer exporter: 1. Edit `/etc/gitlab/gitlab.rb` 1. Add or find and uncomment the following line, making sure it's set to `true`: - ```ruby - pgbouncer_exporter['enable'] = true - ``` + ```ruby + pgbouncer_exporter['enable'] = true + ``` 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to take effect. diff --git a/doc/administration/monitoring/prometheus/postgres_exporter.md b/doc/administration/monitoring/prometheus/postgres_exporter.md index 8e2d3162f88..3ad15b65497 100644 --- a/doc/administration/monitoring/prometheus/postgres_exporter.md +++ b/doc/administration/monitoring/prometheus/postgres_exporter.md @@ -12,9 +12,9 @@ To enable the postgres exporter: 1. Edit `/etc/gitlab/gitlab.rb` 1. Add or find and uncomment the following line, making sure it's set to `true`: - ```ruby - postgres_exporter['enable'] = true - ``` + ```ruby + postgres_exporter['enable'] = true + ``` 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to take effect diff --git a/doc/administration/monitoring/prometheus/redis_exporter.md b/doc/administration/monitoring/prometheus/redis_exporter.md index d54d409dbb6..1520115a38d 100644 --- a/doc/administration/monitoring/prometheus/redis_exporter.md +++ b/doc/administration/monitoring/prometheus/redis_exporter.md @@ -13,9 +13,9 @@ To enable the Redis exporter: 1. Edit `/etc/gitlab/gitlab.rb` 1. Add or find and uncomment the following line, making sure it's set to `true`: - ```ruby - redis_exporter['enable'] = true - ``` + ```ruby + redis_exporter['enable'] = true + ``` 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to take effect diff --git a/doc/administration/operations/extra_sidekiq_processes.md b/doc/administration/operations/extra_sidekiq_processes.md index 7297507f599..a16cd5166b7 100644 --- a/doc/administration/operations/extra_sidekiq_processes.md +++ b/doc/administration/operations/extra_sidekiq_processes.md @@ -1,4 +1,4 @@ -# Extra Sidekiq processes **[STARTER ONLY]** +# Extra Sidekiq processes **(STARTER ONLY)** NOTE: **Note:** The information in this page applies only to Omnibus GitLab. diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md index 3631ea0822f..ea69378b249 100644 --- a/doc/administration/operations/fast_ssh_key_lookup.md +++ b/doc/administration/operations/fast_ssh_key_lookup.md @@ -6,7 +6,7 @@ using [ssh certificates](ssh_certificates.md), they are even faster, but are not a drop-in replacement. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1631) in -> [GitLab Starter](https://about.gitlab.com/gitlab-ee) 9.3. +> [GitLab Starter](https://about.gitlab.com/pricing/) 9.3. > > [Available in](https://gitlab.com/gitlab-org/gitlab-ee/issues/3953) GitLab > Community Edition 10.4. @@ -30,7 +30,7 @@ instructions will break installations using older versions of OpenSSH, such as those included with CentOS 6 as of September 2017. If you want to use this feature for CentOS 6, follow [the instructions on how to build and install a custom OpenSSH package](#compiling-a-custom-version-of-openssh-for-centos-6) before continuing. -## Fast lookup is required for Geo **[PREMIUM]** +## Fast lookup is required for Geo **(PREMIUM)** By default, GitLab manages an `authorized_keys` file, which contains all the public SSH keys for users allowed to access GitLab. However, to maintain a @@ -117,81 +117,81 @@ the database. The following instructions can be used to build OpenSSH 7.5: 1. First, download the package and install the required packages: - ``` - sudo su - - cd /tmp - curl --remote-name https://mirrors.evowise.com/pub/OpenBSD/OpenSSH/portable/openssh-7.5p1.tar.gz - tar xzvf openssh-7.5p1.tar.gz - yum install rpm-build gcc make wget openssl-devel krb5-devel pam-devel libX11-devel xmkmf libXt-devel - ``` + ``` + sudo su - + cd /tmp + curl --remote-name https://mirrors.evowise.com/pub/OpenBSD/OpenSSH/portable/openssh-7.5p1.tar.gz + tar xzvf openssh-7.5p1.tar.gz + yum install rpm-build gcc make wget openssl-devel krb5-devel pam-devel libX11-devel xmkmf libXt-devel + ``` 1. Prepare the build by copying files to the right place: - ``` - mkdir -p /root/rpmbuild/{SOURCES,SPECS} - cp ./openssh-7.5p1/contrib/redhat/openssh.spec /root/rpmbuild/SPECS/ - cp openssh-7.5p1.tar.gz /root/rpmbuild/SOURCES/ - cd /root/rpmbuild/SPECS - ``` + ``` + mkdir -p /root/rpmbuild/{SOURCES,SPECS} + cp ./openssh-7.5p1/contrib/redhat/openssh.spec /root/rpmbuild/SPECS/ + cp openssh-7.5p1.tar.gz /root/rpmbuild/SOURCES/ + cd /root/rpmbuild/SPECS + ``` 1. Next, set the spec settings properly: - ``` - sed -i -e "s/%define no_gnome_askpass 0/%define no_gnome_askpass 1/g" openssh.spec - sed -i -e "s/%define no_x11_askpass 0/%define no_x11_askpass 1/g" openssh.spec - sed -i -e "s/BuildPreReq/BuildRequires/g" openssh.spec - ``` + ``` + sed -i -e "s/%define no_gnome_askpass 0/%define no_gnome_askpass 1/g" openssh.spec + sed -i -e "s/%define no_x11_askpass 0/%define no_x11_askpass 1/g" openssh.spec + sed -i -e "s/BuildPreReq/BuildRequires/g" openssh.spec + ``` 1. Build the RPMs: - ``` - rpmbuild -bb openssh.spec - ``` + ``` + rpmbuild -bb openssh.spec + ``` 1. Ensure the RPMs were built: - ``` - ls -al /root/rpmbuild/RPMS/x86_64/ - ``` + ``` + ls -al /root/rpmbuild/RPMS/x86_64/ + ``` - You should see something as the following: + You should see something as the following: - ``` - total 1324 - drwxr-xr-x. 2 root root 4096 Jun 20 19:37 . - drwxr-xr-x. 3 root root 19 Jun 20 19:37 .. - -rw-r--r--. 1 root root 470828 Jun 20 19:37 openssh-7.5p1-1.x86_64.rpm - -rw-r--r--. 1 root root 490716 Jun 20 19:37 openssh-clients-7.5p1-1.x86_64.rpm - -rw-r--r--. 1 root root 17020 Jun 20 19:37 openssh-debuginfo-7.5p1-1.x86_64.rpm - -rw-r--r--. 1 root root 367516 Jun 20 19:37 openssh-server-7.5p1-1.x86_64.rpm - ``` + ``` + total 1324 + drwxr-xr-x. 2 root root 4096 Jun 20 19:37 . + drwxr-xr-x. 3 root root 19 Jun 20 19:37 .. + -rw-r--r--. 1 root root 470828 Jun 20 19:37 openssh-7.5p1-1.x86_64.rpm + -rw-r--r--. 1 root root 490716 Jun 20 19:37 openssh-clients-7.5p1-1.x86_64.rpm + -rw-r--r--. 1 root root 17020 Jun 20 19:37 openssh-debuginfo-7.5p1-1.x86_64.rpm + -rw-r--r--. 1 root root 367516 Jun 20 19:37 openssh-server-7.5p1-1.x86_64.rpm + ``` 1. Install the packages. OpenSSH packages will replace `/etc/pam.d/sshd` with its own version, which may prevent users from logging in, so be sure that the file is backed up and restored after installation: - ``` - timestamp=$(date +%s) - cp /etc/pam.d/sshd pam-ssh-conf-$timestamp - rpm -Uvh /root/rpmbuild/RPMS/x86_64/*.rpm - yes | cp pam-ssh-conf-$timestamp /etc/pam.d/sshd - ``` + ``` + timestamp=$(date +%s) + cp /etc/pam.d/sshd pam-ssh-conf-$timestamp + rpm -Uvh /root/rpmbuild/RPMS/x86_64/*.rpm + yes | cp pam-ssh-conf-$timestamp /etc/pam.d/sshd + ``` 1. Verify the installed version. In another window, attempt to login to the server: - ``` - ssh -v <your-centos-machine> - ``` + ``` + ssh -v <your-centos-machine> + ``` - You should see a line that reads: "debug1: Remote protocol version 2.0, remote software version OpenSSH_7.5" + You should see a line that reads: "debug1: Remote protocol version 2.0, remote software version OpenSSH_7.5" - If not, you may need to restart sshd (e.g. `systemctl restart sshd.service`). + If not, you may need to restart sshd (e.g. `systemctl restart sshd.service`). -1. *IMPORTANT!* Open a new SSH session to your server before exiting to make - sure everything is working! If you need to downgrade, simple install the - older package: +1. *IMPORTANT!* Open a new SSH session to your server before exiting to make + sure everything is working! If you need to downgrade, simple install the + older package: - ``` - # Only run this if you run into a problem logging in - yum downgrade openssh-server openssh openssh-clients - ``` + ``` + # Only run this if you run into a problem logging in + yum downgrade openssh-server openssh openssh-clients + ``` diff --git a/doc/administration/operations/filesystem_benchmarking.md b/doc/administration/operations/filesystem_benchmarking.md index c0c242733a2..b5922d9d99d 100644 --- a/doc/administration/operations/filesystem_benchmarking.md +++ b/doc/administration/operations/filesystem_benchmarking.md @@ -78,34 +78,37 @@ executed, and then read the same 1,000 files. [repository storage path](../repository_storage_paths.md). 1. Create a temporary directory for the test so it's easy to remove the files later: - ```sh - mkdir test; cd test - ``` + ```sh + mkdir test; cd test + ``` + 1. Run the command: - ```sh - time for i in {0..1000}; do echo 'test' > "test${i}.txt"; done - ``` -1. To benchmark read performance, run the command: + ```sh + time for i in {0..1000}; do echo 'test' > "test${i}.txt"; done + ``` - ```sh - time for i in {0..1000}; do cat "test${i}.txt" > /dev/null; done - ``` -1. Remove the test files: +1. To benchmark read performance, run the command: ```sh - cd ../; rm -rf test + time for i in {0..1000}; do cat "test${i}.txt" > /dev/null; done ``` +1. Remove the test files: + + ```sh + cd ../; rm -rf test + ``` + The output of the `time for ...` commands will look similar to the following. The important metric is the `real` time. ```sh $ time for i in {0..1000}; do echo 'test' > "test${i}.txt"; done -real 0m0.116s -user 0m0.025s -sys 0m0.091s +real 0m0.116s +user 0m0.025s +sys 0m0.091s $ time for i in {0..1000}; do cat "test${i}.txt" > /dev/null; done diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md index df795a48169..df208b3f427 100644 --- a/doc/administration/operations/index.md +++ b/doc/administration/operations/index.md @@ -11,7 +11,7 @@ Keep your GitLab instance up and running smoothly. by GitLab to another file system or another server. - [Sidekiq MemoryKiller](sidekiq_memory_killer.md): Configure Sidekiq MemoryKiller to restart Sidekiq. -- [Extra Sidekiq operations](extra_sidekiq_processes.md): Configure an extra set of Sidekiq processes to ensure certain queues always have dedicated workers, no matter the amount of jobs that need to be processed. **[STARTER ONLY]** +- [Extra Sidekiq operations](extra_sidekiq_processes.md): Configure an extra set of Sidekiq processes to ensure certain queues always have dedicated workers, no matter the amount of jobs that need to be processed. **(STARTER ONLY)** - [Unicorn](unicorn.md): Understand Unicorn and unicorn-worker-killer. - Speed up SSH operations by [Authorizing SSH users via a fast, indexed lookup to the GitLab database](fast_ssh_key_lookup.md), and/or diff --git a/doc/administration/operations/unicorn.md b/doc/administration/operations/unicorn.md index 0e2079cb093..ae67d7f08d6 100644 --- a/doc/administration/operations/unicorn.md +++ b/doc/administration/operations/unicorn.md @@ -2,7 +2,7 @@ ## Unicorn -GitLab uses [Unicorn](http://unicorn.bogomips.org/), a pre-forking Ruby web +GitLab uses [Unicorn](https://bogomips.org/unicorn/), a pre-forking Ruby web server, to handle web requests (web browsers and Git HTTP clients). Unicorn is a daemon written in Ruby and C that can load and run a Ruby on Rails application; in our case the Rails application is GitLab Community Edition or diff --git a/doc/administration/packages.md b/doc/administration/packages.md index 0d5f784b71e..c0f8777a8c0 100644 --- a/doc/administration/packages.md +++ b/doc/administration/packages.md @@ -1,4 +1,4 @@ -# GitLab Packages administration **[PREMIUM ONLY]** +# GitLab Packages administration **(PREMIUM ONLY)** GitLab Packages allows organizations to utilize GitLab as a private repository for a variety of common package managers. Users are able to build and publish @@ -11,7 +11,7 @@ The Packages feature allows GitLab to act as a repository for the following: | [Maven Repository](../user/project/packages/maven_repository.md) | The GitLab Maven Repository enables every project in GitLab to have its own space to store [Maven](https://maven.apache.org/) packages. | 11.3+ | | [NPM Registry](../user/project/packages/npm_registry.md) | The GitLab NPM Registry enables every project in GitLab to have its own space to store [NPM](https://www.npmjs.com/) packages. | 11.7+ | -Don't you see your package management system supported yet? +Don't you see your package management system supported yet? Please consider contributing to GitLab. This [development documentation](../development/packages.md) will guide you through the process. @@ -28,9 +28,9 @@ To enable the Packages feature: 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['packages_enabled'] = true - ``` + ```ruby + gitlab_rails['packages_enabled'] = true + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -39,10 +39,11 @@ To enable the Packages feature: 1. After the installation is complete, you will have to configure the `packages` section in `config/gitlab.yml`. Set to `true` to enable it: - ```yaml - packages: - enabled: true - ``` + ```yaml + packages: + enabled: true + ``` + 1. [Restart GitLab] for the changes to take effect. ## Changing the storage path @@ -61,9 +62,9 @@ To change the local storage path: 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['packages_storage_path'] = "/mnt/packages" - ``` + ```ruby + gitlab_rails['packages_storage_path'] = "/mnt/packages" + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -71,11 +72,12 @@ To change the local storage path: 1. Edit the `packages` section in `config/gitlab.yml`: - ```yaml - packages: - enabled: true - storage_path: shared/packages - ``` + ```yaml + packages: + enabled: true + storage_path: shared/packages + ``` + 1. [Restart GitLab] for the changes to take effect. ### Using object storage @@ -88,31 +90,31 @@ upload packages: 1. Edit `/etc/gitlab/gitlab.rb` and add the following lines (uncomment where necessary): - ```ruby - gitlab_rails['packages_enabled'] = true - gitlab_rails['packages_storage_path'] = "/var/opt/gitlab/gitlab-rails/shared/packages" - gitlab_rails['packages_object_store_enabled'] = true - gitlab_rails['packages_object_store_remote_directory'] = "packages" # The bucket name. - gitlab_rails['packages_object_store_direct_upload'] = false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false). - gitlab_rails['packages_object_store_background_upload'] = true # Temporary option to limit automatic upload (Default: true). - gitlab_rails['packages_object_store_proxy_download'] = false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage. - gitlab_rails['packages_object_store_connection'] = { - ## - ## If the provider is AWS S3, uncomment the following - ## - #'provider' => 'AWS', - #'region' => 'eu-west-1', - #'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', - #'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY', - ## - ## If the provider is other than AWS (an S3-compatible one), uncomment the following - ## - #'host' => 's3.amazonaws.com', - #'aws_signature_version' => 4 # For creation of signed URLs. Set to 2 if provider does not support v4. - #'endpoint' => 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces. - #'path_style' => false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'. - } - ``` + ```ruby + gitlab_rails['packages_enabled'] = true + gitlab_rails['packages_storage_path'] = "/var/opt/gitlab/gitlab-rails/shared/packages" + gitlab_rails['packages_object_store_enabled'] = true + gitlab_rails['packages_object_store_remote_directory'] = "packages" # The bucket name. + gitlab_rails['packages_object_store_direct_upload'] = false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false). + gitlab_rails['packages_object_store_background_upload'] = true # Temporary option to limit automatic upload (Default: true). + gitlab_rails['packages_object_store_proxy_download'] = false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage. + gitlab_rails['packages_object_store_connection'] = { + ## + ## If the provider is AWS S3, uncomment the following + ## + #'provider' => 'AWS', + #'region' => 'eu-west-1', + #'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', + #'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY', + ## + ## If the provider is other than AWS (an S3-compatible one), uncomment the following + ## + #'host' => 's3.amazonaws.com', + #'aws_signature_version' => 4 # For creation of signed URLs. Set to 2 if provider does not support v4. + #'endpoint' => 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces. + #'path_style' => false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'. + } + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -120,35 +122,35 @@ upload packages: 1. Edit the `packages` section in `config/gitlab.yml` (uncomment where necessary): - ```yaml - packages: - enabled: true - ## - ## The location where build packages are stored (default: shared/packages). - ## - #storage_path: shared/packages - object_store: - enabled: false - remote_directory: packages # The bucket name. - #direct_upload: false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false). - #background_upload: true # Temporary option to limit automatic upload (Default: true). - #proxy_download: false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage. - connection: - ## - ## If the provider is AWS S3, uncomment the following - ## - #provider: AWS - #region: us-east-1 - #aws_access_key_id: AWS_ACCESS_KEY_ID - #aws_secret_access_key: AWS_SECRET_ACCESS_KEY - ## - ## If the provider is other than AWS (an S3-compatible one), uncomment the following - ## - #host: 's3.amazonaws.com' # default: s3.amazonaws.com. - #aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4. - #endpoint: 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces. - #path_style: false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'. - ``` + ```yaml + packages: + enabled: true + ## + ## The location where build packages are stored (default: shared/packages). + ## + #storage_path: shared/packages + object_store: + enabled: false + remote_directory: packages # The bucket name. + #direct_upload: false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false). + #background_upload: true # Temporary option to limit automatic upload (Default: true). + #proxy_download: false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage. + connection: + ## + ## If the provider is AWS S3, uncomment the following + ## + #provider: AWS + #region: us-east-1 + #aws_access_key_id: AWS_ACCESS_KEY_ID + #aws_secret_access_key: AWS_SECRET_ACCESS_KEY + ## + ## If the provider is other than AWS (an S3-compatible one), uncomment the following + ## + #host: 's3.amazonaws.com' # default: s3.amazonaws.com. + #aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4. + #endpoint: 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces. + #path_style: false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'. + ``` 1. [Restart GitLab] for the changes to take effect. diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index 3a7ca517d56..b5b8f124274 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -4,21 +4,24 @@ description: 'Learn how to administer GitLab Pages.' # GitLab Pages administration -> **Notes:** -> > - [Introduced][ee-80] in GitLab EE 8.3. > - Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5. -> - GitLab Pages [were ported][ce-14605] to Community Edition in GitLab 8.17. -> - This guide is for Omnibus GitLab installations. If you have installed -> GitLab from source, follow the [Pages source installation document](source.md). -> - To learn how to use GitLab Pages, read the [user documentation][pages-userguide]. -> - Support for subgroup project's websites was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/30548) in GitLab 11.8. - -This document describes how to set up the _latest_ GitLab Pages feature. Make -sure to read the [changelog](#changelog) if you are upgrading to a new GitLab +> - GitLab Pages [was ported][ce-14605] to Community Edition in GitLab 8.17. +> - Support for subgroup project's websites was +> [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/30548) in GitLab 11.8. + +GitLab Pages allows for hosting of static sites. It must be configured by an +administrator. Separate [user documentation][pages-userguide] is available. + +Read the [changelog](#changelog) if you are upgrading to a new GitLab version as it may include new features and changes needed to be made in your configuration. +NOTE: **Note:** +This guide is for Omnibus GitLab installations. If you have installed +GitLab from source, see +[GitLab Pages administration for source installations](source.md). + ## Overview GitLab Pages makes use of the [GitLab Pages daemon], a simple HTTP server @@ -121,9 +124,9 @@ The Pages daemon doesn't listen to the outside world. 1. Set the external URL for GitLab Pages in `/etc/gitlab/gitlab.rb`: - ```shell - pages_external_url 'http://example.io' - ``` + ```shell + pages_external_url 'http://example.io' + ``` 1. [Reconfigure GitLab][reconfigure]. @@ -146,16 +149,16 @@ outside world. 1. Place the certificate and key inside `/etc/gitlab/ssl` 1. In `/etc/gitlab/gitlab.rb` specify the following configuration: - ```shell - pages_external_url 'https://example.io' + ```shell + pages_external_url 'https://example.io' - pages_nginx['redirect_http_to_https'] = true - pages_nginx['ssl_certificate'] = "/etc/gitlab/ssl/pages-nginx.crt" - pages_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/pages-nginx.key" - ``` + pages_nginx['redirect_http_to_https'] = true + pages_nginx['ssl_certificate'] = "/etc/gitlab/ssl/pages-nginx.crt" + pages_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/pages-nginx.key" + ``` - where `pages-nginx.crt` and `pages-nginx.key` are the SSL cert and key, - respectively. + where `pages-nginx.crt` and `pages-nginx.key` are the SSL cert and key, + respectively. 1. [Reconfigure GitLab][reconfigure]. @@ -168,9 +171,9 @@ behavior: 1. Edit `/etc/gitlab/gitlab.rb`. 1. Set the `inplace_chroot` to `true` for GitLab Pages: - ```shell - gitlab_pages['inplace_chroot'] = true - ``` + ```shell + gitlab_pages['inplace_chroot'] = true + ``` 1. [Reconfigure GitLab][reconfigure]. @@ -203,16 +206,16 @@ world. Custom domains are supported, but no TLS. 1. Edit `/etc/gitlab/gitlab.rb`: - ```shell - pages_external_url "http://example.io" - nginx['listen_addresses'] = ['192.0.2.1'] - pages_nginx['enable'] = false - gitlab_pages['external_http'] = ['192.0.2.2:80', '[2001::2]:80'] - ``` + ```shell + pages_external_url "http://example.io" + nginx['listen_addresses'] = ['192.0.2.1'] + pages_nginx['enable'] = false + gitlab_pages['external_http'] = ['192.0.2.2:80', '[2001::2]:80'] + ``` - where `192.0.2.1` is the primary IP address that GitLab is listening to and - `192.0.2.2` and `2001::2` are the secondary IPs the GitLab Pages daemon - listens on. If you don't have IPv6, you can omit the IPv6 address. + where `192.0.2.1` is the primary IP address that GitLab is listening to and + `192.0.2.2` and `2001::2` are the secondary IPs the GitLab Pages daemon + listens on. If you don't have IPv6, you can omit the IPv6 address. 1. [Reconfigure GitLab][reconfigure]. @@ -234,19 +237,19 @@ world. Custom domains and TLS are supported. 1. Edit `/etc/gitlab/gitlab.rb`: - ```shell - pages_external_url "https://example.io" - nginx['listen_addresses'] = ['192.0.2.1'] - pages_nginx['enable'] = false - gitlab_pages['cert'] = "/etc/gitlab/ssl/example.io.crt" - gitlab_pages['cert_key'] = "/etc/gitlab/ssl/example.io.key" - gitlab_pages['external_http'] = ['192.0.2.2:80', '[2001::2]:80'] - gitlab_pages['external_https'] = ['192.0.2.2:443', '[2001::2]:443'] - ``` + ```shell + pages_external_url "https://example.io" + nginx['listen_addresses'] = ['192.0.2.1'] + pages_nginx['enable'] = false + gitlab_pages['cert'] = "/etc/gitlab/ssl/example.io.crt" + gitlab_pages['cert_key'] = "/etc/gitlab/ssl/example.io.key" + gitlab_pages['external_http'] = ['192.0.2.2:80', '[2001::2]:80'] + gitlab_pages['external_https'] = ['192.0.2.2:443', '[2001::2]:443'] + ``` - where `192.0.2.1` is the primary IP address that GitLab is listening to and - `192.0.2.2` and `2001::2` are the secondary IPs where the GitLab Pages daemon - listens on. If you don't have IPv6, you can omit the IPv6 address. + where `192.0.2.1` is the primary IP address that GitLab is listening to and + `192.0.2.2` and `2001::2` are the secondary IPs where the GitLab Pages daemon + listens on. If you don't have IPv6, you can omit the IPv6 address. 1. [Reconfigure GitLab][reconfigure]. @@ -284,9 +287,9 @@ Pages access control is disabled by default. To enable it: 1. Enable it in `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_pages['access_control'] = true - ``` + ```ruby + gitlab_pages['access_control'] = true + ``` 1. [Reconfigure GitLab][reconfigure]. 1. Users can now configure it in their [projects' settings](../../user/project/pages/introduction.md#gitlab-pages-access-control-core-only). @@ -299,9 +302,9 @@ pages: 1. Configure in `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_pages['http_proxy'] = 'http://example:8080' - ``` + ```ruby + gitlab_pages['http_proxy'] = 'http://example:8080' + ``` 1. [Reconfigure Gitlab][reconfigure] for the changes to take effect. @@ -316,9 +319,9 @@ Follow the steps below to configure verbose logging of GitLab Pages daemon. If you wish to make it log events with level `DEBUG` you must configure this in `/etc/gitlab/gitlab.rb`: - ```shell - gitlab_pages['log_verbose'] = true - ``` + ```shell + gitlab_pages['log_verbose'] = true + ``` 1. [Reconfigure GitLab][reconfigure]. @@ -331,9 +334,9 @@ are stored. If you wish to store them in another location you must set it up in `/etc/gitlab/gitlab.rb`: - ```shell - gitlab_rails['pages_path'] = "/mnt/storage/pages" - ``` + ```shell + gitlab_rails['pages_path'] = "/mnt/storage/pages" + ``` 1. [Reconfigure GitLab][reconfigure]. @@ -344,19 +347,19 @@ Omnibus GitLab 11.1. 1. By default the listener is configured to listen for requests on `localhost:8090`. - If you wish to disable it you must configure this in - `/etc/gitlab/gitlab.rb`: + If you wish to disable it you must configure this in + `/etc/gitlab/gitlab.rb`: - ```shell - gitlab_pages['listen_proxy'] = nil - ``` + ```shell + gitlab_pages['listen_proxy'] = nil + ``` - If you wish to make it listen on a different port you must configure this also in - `/etc/gitlab/gitlab.rb`: + If you wish to make it listen on a different port you must configure this also in + `/etc/gitlab/gitlab.rb`: - ```shell - gitlab_pages['listen_proxy'] = "localhost:10080" - ``` + ```shell + gitlab_pages['listen_proxy'] = "localhost:10080" + ``` 1. [Reconfigure GitLab][reconfigure]. @@ -378,28 +381,29 @@ Follow the steps below to configure GitLab Pages in a separate server. 1. On `app2` install GitLab omnibus and modify `/etc/gitlab/gitlab.rb` this way: - ```shell - external_url 'http://<ip-address-of-the-server>' - pages_external_url "http://<your-pages-domain>" - postgresql['enable'] = false - redis['enable'] = false - prometheus['enable'] = false - unicorn['enable'] = false - sidekiq['enable'] = false - gitlab_workhorse['enable'] = false - gitaly['enable'] = false - alertmanager['enable'] = false - node_exporter['enable'] = false - gitlab_rails['auto_migrate'] = false - ``` + ```shell + external_url 'http://<ip-address-of-the-server>' + pages_external_url "http://<your-pages-domain>" + postgresql['enable'] = false + redis['enable'] = false + prometheus['enable'] = false + unicorn['enable'] = false + sidekiq['enable'] = false + gitlab_workhorse['enable'] = false + gitaly['enable'] = false + alertmanager['enable'] = false + node_exporter['enable'] = false + gitlab_rails['auto_migrate'] = false + ``` + 1. Run `sudo gitlab-ctl reconfigure`. 1. On `app1` apply the following changes to `/etc/gitlab/gitlab.rb`: - ```shell - gitlab_pages['enable'] = false - pages_external_url "http://<your-pages-domain>" - gitlab_rails['pages_path'] = "/mnt/pages" - ``` + ```shell + gitlab_pages['enable'] = false + pages_external_url "http://<your-pages-domain>" + gitlab_rails['pages_path'] = "/mnt/pages" + ``` 1. Run `sudo gitlab-ctl reconfigure`. diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md index 2100f7cd707..b2cad6cf926 100644 --- a/doc/administration/pages/source.md +++ b/doc/administration/pages/source.md @@ -102,50 +102,50 @@ The Pages daemon doesn't listen to the outside world. 1. Install the Pages daemon: - ``` - cd /home/git - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git - cd gitlab-pages - sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) - sudo -u git -H make - ``` + ``` + cd /home/git + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git + cd gitlab-pages + sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) + sudo -u git -H make + ``` 1. Go to the GitLab installation directory: - ```bash - cd /home/git/gitlab - ``` + ```bash + cd /home/git/gitlab + ``` 1. Edit `gitlab.yml` and under the `pages` setting, set `enabled` to `true` and the `host` to the FQDN under which GitLab Pages will be served: - ```yaml - ## GitLab Pages - pages: - enabled: true - # The location where pages are stored (default: shared/pages). - # path: shared/pages + ```yaml + ## GitLab Pages + pages: + enabled: true + # The location where pages are stored (default: shared/pages). + # path: shared/pages - host: example.io - port: 80 - https: false - ``` + host: example.io + port: 80 + https: false + ``` 1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in order to enable the pages daemon. In `gitlab_pages_options` the `-pages-domain` must match the `host` setting that you set above. - ``` - gitlab_pages_enabled=true - gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090" - ``` + ``` + gitlab_pages_enabled=true + gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090" + ``` 1. Copy the `gitlab-pages` Nginx configuration file: - ```bash - sudo cp lib/support/nginx/gitlab-pages /etc/nginx/sites-available/gitlab-pages.conf - sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages.conf - ``` + ```bash + sudo cp lib/support/nginx/gitlab-pages /etc/nginx/sites-available/gitlab-pages.conf + sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages.conf + ``` 1. Restart NGINX 1. [Restart GitLab][restart] @@ -165,27 +165,27 @@ outside world. 1. Install the Pages daemon: - ``` - cd /home/git - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git - cd gitlab-pages - sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) - sudo -u git -H make - ``` + ``` + cd /home/git + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git + cd gitlab-pages + sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) + sudo -u git -H make + ``` 1. In `gitlab.yml`, set the port to `443` and https to `true`: - ```bash - ## GitLab Pages - pages: - enabled: true - # The location where pages are stored (default: shared/pages). - # path: shared/pages + ```bash + ## GitLab Pages + pages: + enabled: true + # The location where pages are stored (default: shared/pages). + # path: shared/pages - host: example.io - port: 443 - https: true - ``` + host: example.io + port: 443 + https: true + ``` 1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in order to enable the pages daemon. In `gitlab_pages_options` the @@ -193,17 +193,17 @@ outside world. The `-root-cert` and `-root-key` settings are the wildcard TLS certificates of the `example.io` domain: - ``` - gitlab_pages_enabled=true - gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -root-cert /path/to/example.io.crt -root-key /path/to/example.io.key - ``` + ``` + gitlab_pages_enabled=true + gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -root-cert /path/to/example.io.crt -root-key /path/to/example.io.key + ``` 1. Copy the `gitlab-pages-ssl` Nginx configuration file: - ```bash - sudo cp lib/support/nginx/gitlab-pages-ssl /etc/nginx/sites-available/gitlab-pages-ssl.conf - sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages-ssl.conf - ``` + ```bash + sudo cp lib/support/nginx/gitlab-pages-ssl /etc/nginx/sites-available/gitlab-pages-ssl.conf + sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages-ssl.conf + ``` 1. Restart NGINX 1. [Restart GitLab][restart] @@ -231,48 +231,48 @@ world. Custom domains are supported, but no TLS. 1. Install the Pages daemon: - ``` - cd /home/git - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git - cd gitlab-pages - sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) - sudo -u git -H make - ``` + ``` + cd /home/git + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git + cd gitlab-pages + sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) + sudo -u git -H make + ``` 1. Edit `gitlab.yml` to look like the example below. You need to change the `host` to the FQDN under which GitLab Pages will be served. Set `external_http` to the secondary IP on which the pages daemon will listen for connections: - ```yaml - pages: - enabled: true - # The location where pages are stored (default: shared/pages). - # path: shared/pages + ```yaml + pages: + enabled: true + # The location where pages are stored (default: shared/pages). + # path: shared/pages - host: example.io - port: 80 - https: false + host: example.io + port: 80 + https: false - external_http: 192.0.2.2:80 - ``` + external_http: 192.0.2.2:80 + ``` 1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in order to enable the pages daemon. In `gitlab_pages_options` the `-pages-domain` and `-listen-http` must match the `host` and `external_http` settings that you set above respectively: - ``` - gitlab_pages_enabled=true - gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 192.0.2.2:80" - ``` + ``` + gitlab_pages_enabled=true + gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 192.0.2.2:80" + ``` 1. Copy the `gitlab-pages-ssl` Nginx configuration file: - ```bash - sudo cp lib/support/nginx/gitlab-pages /etc/nginx/sites-available/gitlab-pages.conf - sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages.conf - ``` + ```bash + sudo cp lib/support/nginx/gitlab-pages /etc/nginx/sites-available/gitlab-pages.conf + sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages.conf + ``` 1. Edit all GitLab related configs in `/etc/nginx/site-available/` and replace `0.0.0.0` with `192.0.2.1`, where `192.0.2.1` the primary IP where GitLab @@ -297,33 +297,33 @@ world. Custom domains and TLS are supported. 1. Install the Pages daemon: - ``` - cd /home/git - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git - cd gitlab-pages - sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) - sudo -u git -H make - ``` + ``` + cd /home/git + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git + cd gitlab-pages + sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) + sudo -u git -H make + ``` 1. Edit `gitlab.yml` to look like the example below. You need to change the `host` to the FQDN under which GitLab Pages will be served. Set `external_http` and `external_https` to the secondary IP on which the pages daemon will listen for connections: - ```yaml - ## GitLab Pages - pages: - enabled: true - # The location where pages are stored (default: shared/pages). - # path: shared/pages + ```yaml + ## GitLab Pages + pages: + enabled: true + # The location where pages are stored (default: shared/pages). + # path: shared/pages - host: example.io - port: 443 - https: true + host: example.io + port: 443 + https: true - external_http: 192.0.2.2:80 - external_https: 192.0.2.2:443 - ``` + external_http: 192.0.2.2:80 + external_https: 192.0.2.2:443 + ``` 1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in order to enable the pages daemon. In `gitlab_pages_options` the @@ -332,17 +332,17 @@ world. Custom domains and TLS are supported. The `-root-cert` and `-root-key` settings are the wildcard TLS certificates of the `example.io` domain: - ``` - gitlab_pages_enabled=true - gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 192.0.2.2:80 -listen-https 192.0.2.2:443 -root-cert /path/to/example.io.crt -root-key /path/to/example.io.key - ``` + ``` + gitlab_pages_enabled=true + gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 192.0.2.2:80 -listen-https 192.0.2.2:443 -root-cert /path/to/example.io.crt -root-key /path/to/example.io.key + ``` 1. Copy the `gitlab-pages-ssl` Nginx configuration file: - ```bash - sudo cp lib/support/nginx/gitlab-pages-ssl /etc/nginx/sites-available/gitlab-pages-ssl.conf - sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages-ssl.conf - ``` + ```bash + sudo cp lib/support/nginx/gitlab-pages-ssl /etc/nginx/sites-available/gitlab-pages-ssl.conf + sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages-ssl.conf + ``` 1. Edit all GitLab related configs in `/etc/nginx/site-available/` and replace `0.0.0.0` with `192.0.2.1`, where `192.0.2.1` the primary IP where GitLab @@ -359,9 +359,9 @@ are stored. If you wish to store them in another location you must set it up in `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['pages_path'] = "/mnt/storage/pages" - ``` + ```ruby + gitlab_rails['pages_path'] = "/mnt/storage/pages" + ``` 1. [Reconfigure GitLab][reconfigure] @@ -414,10 +414,10 @@ Pages access control is disabled by default. To enable it: 1. Modify your `config/gitlab.yml` file: - ```yaml - pages: - access_control: true - ``` + ```yaml + pages: + access_control: true + ``` 1. [Restart GitLab][restart]. 1. Create a new [system OAuth application](../../integration/oauth_provider.md#adding-an-application-through-the-profile). @@ -426,12 +426,12 @@ Pages access control is disabled by default. To enable it: application, but it does need the "api" scope. 1. Start the Pages daemon with the following additional arguments: - ```shell - -auth-client-secret <OAuth code generated by GitLab> \ - -auth-redirect-uri http://projects.example.io/auth \ - -auth-secret <40 random hex characters> \ - -auth-server <URL of the GitLab instance> - ``` + ```shell + -auth-client-secret <OAuth code generated by GitLab> \ + -auth-redirect-uri http://projects.example.io/auth \ + -auth-secret <40 random hex characters> \ + -auth-server <URL of the GitLab instance> + ``` 1. Users can now configure it in their [projects' settings](../../user/project/pages/introduction.md#gitlab-pages-access-control-core-only). @@ -444,12 +444,12 @@ are stored. If you wish to store them in another location you must set it up in `gitlab.yml` under the `pages` section: - ```yaml - pages: - enabled: true - # The location where pages are stored (default: shared/pages). - path: /mnt/storage/pages - ``` + ```yaml + pages: + enabled: true + # The location where pages are stored (default: shared/pages). + path: /mnt/storage/pages + ``` 1. [Restart GitLab][restart] diff --git a/doc/administration/pseudonymizer.md b/doc/administration/pseudonymizer.md index 036e1d3fe61..716a4259a64 100644 --- a/doc/administration/pseudonymizer.md +++ b/doc/administration/pseudonymizer.md @@ -1,4 +1,4 @@ -# Pseudonymizer **[ULTIMATE]** +# Pseudonymizer **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/5532) in [GitLab Ultimate][ee] 11.1. @@ -22,36 +22,36 @@ To configure the pseudonymizer, you need to: - Provide a manifest file that describes which fields should be included or pseudonymized ([example `manifest.yml` file](https://gitlab.com/gitlab-org/gitlab-ee/tree/master/config/pseudonymizer.yml)). - A default manifest is provided with the GitLab installation. Using a relative file path will be resolved from the Rails root. + A default manifest is provided with the GitLab installation. Using a relative file path will be resolved from the Rails root. Alternatively, you can use an absolute file path. -- Use an object storage and specify the connection parameters in the `pseudonymizer.upload.connection` configuration option. +- Use an object storage and specify the connection parameters in the `pseudonymizer.upload.connection` configuration option. **For Omnibus installations:** 1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with the values you want: - ```ruby - gitlab_rails['pseudonymizer_manifest'] = 'config/pseudonymizer.yml' - gitlab_rails['pseudonymizer_upload_remote_directory'] = 'gitlab-elt' # bucket name - gitlab_rails['pseudonymizer_upload_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-central-1', - 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', - 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY' - } - ``` - - NOTE: **Note:** - If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs. - - ```ruby - gitlab_rails['pseudonymizer_upload_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-central-1', - 'use_iam_profile' => true - } - ``` + ```ruby + gitlab_rails['pseudonymizer_manifest'] = 'config/pseudonymizer.yml' + gitlab_rails['pseudonymizer_upload_remote_directory'] = 'gitlab-elt' # bucket name + gitlab_rails['pseudonymizer_upload_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-central-1', + 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', + 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY' + } + ``` + + NOTE: **Note:** + If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs. + + ```ruby + gitlab_rails['pseudonymizer_upload_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-central-1', + 'use_iam_profile' => true + } + ``` 1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. @@ -63,17 +63,17 @@ To configure the pseudonymizer, you need to: 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - pseudonymizer: - manifest: config/pseudonymizer.yml - upload: - remote_directory: 'gitlab-elt' # bucket name - connection: - provider: AWS - aws_access_key_id: AWS_ACCESS_KEY_ID - aws_secret_access_key: AWS_SECRET_ACCESS_KEY - region: eu-central-1 - ``` + ```yaml + pseudonymizer: + manifest: config/pseudonymizer.yml + upload: + remote_directory: 'gitlab-elt' # bucket name + connection: + provider: AWS + aws_access_key_id: AWS_ACCESS_KEY_ID + aws_secret_access_key: AWS_SECRET_ACCESS_KEY + region: eu-central-1 + ``` 1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect. diff --git a/doc/administration/raketasks/geo.md b/doc/administration/raketasks/geo.md index 9f3b31442f3..691d34ab7fa 100644 --- a/doc/administration/raketasks/geo.md +++ b/doc/administration/raketasks/geo.md @@ -1,8 +1,8 @@ -# Geo Rake Tasks **[PREMIUM ONLY]** +# Geo Rake Tasks **(PREMIUM ONLY)** ## Git housekeeping -There are few tasks you can run to schedule a git housekeeping to start at the +There are few tasks you can run to schedule a git housekeeping to start at the next repository sync in a **Secondary node**: ### Incremental Repack @@ -23,7 +23,7 @@ sudo -u git -H bundle exec rake geo:git:housekeeping:incremental_repack RAILS_EN ### Full Repack -This is equivalent of running `git repack -d -A --pack-kept-objects` on a +This is equivalent of running `git repack -d -A --pack-kept-objects` on a _bare_ repository which will optionally, write a reachability bitmap index when this is enabled in GitLab. diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md index becd480a08f..2b31233d429 100644 --- a/doc/administration/raketasks/maintenance.md +++ b/doc/administration/raketasks/maintenance.md @@ -61,7 +61,7 @@ It will check that each component was set up according to the installation guide You may also have a look at our Troubleshooting Guides: -- [Troubleshooting Guide (GitLab)](http://docs.gitlab.com/ee/README.html#troubleshooting) +- [Troubleshooting Guide (GitLab)](../index.md#troubleshooting) - [Troubleshooting Guide (Omnibus Gitlab)](https://docs.gitlab.com/omnibus/README.html#troubleshooting) **Omnibus Installation** diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md index 6ca23aabdec..138db4dfbb1 100644 --- a/doc/administration/raketasks/project_import_export.md +++ b/doc/administration/raketasks/project_import_export.md @@ -1,15 +1,15 @@ -# Project import/export administration **[CORE ONLY]** +# Project import/export administration **(CORE ONLY)** >**Note:** > -> - [Introduced][ce-3050] in GitLab 8.9. -> - Importing will not be possible if the import instance version is lower -> than that of the exporter. -> - For existing installations, the project import option has to be enabled in -> application settings (`/admin/application_settings`) under 'Import sources'. -> - The exports are stored in a temporary [shared directory][tmp] and are deleted -> every 24 hours by a specific worker. -> - ImportExport can use object storage automatically starting from GitLab 11.3 +> - [Introduced][ce-3050] in GitLab 8.9. +> - Importing will not be possible if the import instance version is lower +> than that of the exporter. +> - For existing installations, the project import option has to be enabled in +> application settings (`/admin/application_settings`) under 'Import sources'. +> - The exports are stored in a temporary [shared directory][tmp] and are deleted +> every 24 hours by a specific worker. +> - ImportExport can use object storage automatically starting from GitLab 11.3 The GitLab Import/Export version can be checked by using: diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md index 42a1a1c2e60..2f83dd17d9f 100644 --- a/doc/administration/raketasks/storage.md +++ b/doc/administration/raketasks/storage.md @@ -1,17 +1,17 @@ # Repository Storage Rake Tasks -This is a collection of rake tasks you can use to help you list and migrate -existing projects and attachments associated with it from Legacy storage to +This is a collection of rake tasks you can use to help you list and migrate +existing projects and attachments associated with it from Legacy storage to the new Hashed storage type. You can read more about the storage types [here][storage-types]. ## Migrate existing projects to Hashed storage -Before migrating your existing projects, you should +Before migrating your existing projects, you should [enable hashed storage][storage-migration] for the new projects as well. -This task will schedule all your existing projects and attachments associated with it to be migrated to the +This task will schedule all your existing projects and attachments associated with it to be migrated to the **Hashed** storage type: **Omnibus Installation** @@ -30,15 +30,15 @@ They both also accept a range as environment variable: ```bash # to migrate any non migrated project from ID 20 to 50. -export ID_FROM=20 +export ID_FROM=20 export ID_TO=50 ``` You can monitor the progress in the **Admin Area > Monitoring > Background Jobs** page. -There is a specific Queue you can watch to see how long it will take to finish: +There is a specific Queue you can watch to see how long it will take to finish: `hashed_storage:hashed_storage_project_migrate` -After it reaches zero, you can confirm every project has been migrated by running the commands bellow. +After it reaches zero, you can confirm every project has been migrated by running the commands bellow. If you find it necessary, you can run this migration script again to schedule missing projects. Any error or warning will be logged in Sidekiq's log file. @@ -55,7 +55,7 @@ If you need to rollback the storage migration for any reason, you can follow the NOTE: **Note:** Hashed Storage will be required in future version of GitLab. -To prevent new projects from being created in the Hashed storage, +To prevent new projects from being created in the Hashed storage, you need to undo the [enable hashed storage][storage-migration] changes. This task will schedule all your existing projects and associated attachments to be rolled back to the @@ -77,15 +77,14 @@ Both commands accept a range as environment variable: ```bash # to rollback any migrated project from ID 20 to 50. -export ID_FROM=20 +export ID_FROM=20 export ID_TO=50 ``` You can monitor the progress in the **Admin Area > Monitoring > Background Jobs** page. On the **Queues** tab, you can watch the `hashed_storage:hashed_storage_project_rollback` queue to see how long the process will take to finish. - -After it reaches zero, you can confirm every project has been rolled back by running the commands bellow. +After it reaches zero, you can confirm every project has been rolled back by running the commands bellow. If some projects weren't rolled back, you can run this rollback script again to schedule further rollbacks. Any error or warning will be logged in Sidekiq's log file. @@ -106,7 +105,7 @@ sudo gitlab-rake gitlab:storage:legacy_projects sudo -u git -H bundle exec rake gitlab:storage:legacy_projects RAILS_ENV=production ``` ------- +--- To list projects using **Legacy** storage: @@ -139,7 +138,7 @@ sudo gitlab-rake gitlab:storage:hashed_projects sudo -u git -H bundle exec rake gitlab:storage:hashed_projects RAILS_ENV=production ``` ------- +--- To list projects using **Hashed** storage: @@ -171,7 +170,7 @@ sudo gitlab-rake gitlab:storage:legacy_attachments sudo -u git -H bundle exec rake gitlab:storage:legacy_attachments RAILS_ENV=production ``` ------- +--- To list project attachments using **Legacy** storage: @@ -203,7 +202,7 @@ sudo gitlab-rake gitlab:storage:hashed_attachments sudo -u git -H bundle exec rake gitlab:storage:hashed_attachments RAILS_ENV=production ``` ------- +--- To list project attachments using **Hashed** storage: diff --git a/doc/administration/raketasks/uploads/sanitize.md b/doc/administration/raketasks/uploads/sanitize.md index 54a423b9571..ae5ccfb9e37 100644 --- a/doc/administration/raketasks/uploads/sanitize.md +++ b/doc/administration/raketasks/uploads/sanitize.md @@ -4,16 +4,16 @@ You need `exiftool` installed on your system. If you installed GitLab: -- Using the Omnibus package, you're all set. -- From source, make sure `exiftool` is installed: +- Using the Omnibus package, you're all set. +- From source, make sure `exiftool` is installed: - ```sh - # Debian/Ubuntu - sudo apt-get install libimage-exiftool-perl + ```sh + # Debian/Ubuntu + sudo apt-get install libimage-exiftool-perl - # RHEL/CentOS - sudo yum install perl-Image-ExifTool - ``` + # RHEL/CentOS + sudo yum install perl-Image-ExifTool + ``` ## Remove EXIF data from existing uploads diff --git a/doc/administration/reply_by_email_postfix_setup.md b/doc/administration/reply_by_email_postfix_setup.md index d57fc67c83e..406f7e8a034 100644 --- a/doc/administration/reply_by_email_postfix_setup.md +++ b/doc/administration/reply_by_email_postfix_setup.md @@ -14,109 +14,109 @@ The instructions make the assumption that you will be using the email address `i 1. Install the `postfix` package if it is not installed already: - ```sh - sudo apt-get install postfix - ``` + ```sh + sudo apt-get install postfix + ``` - When asked about the environment, select 'Internet Site'. When asked to confirm the hostname, make sure it matches `gitlab.example.com`. + When asked about the environment, select 'Internet Site'. When asked to confirm the hostname, make sure it matches gitlab.example.com`. 1. Install the `mailutils` package. - ```sh - sudo apt-get install mailutils - ``` + ```sh + sudo apt-get install mailutils + ``` ## Create user 1. Create a user for incoming email. - ```sh - sudo useradd -m -s /bin/bash incoming - ``` + ```sh + sudo useradd -m -s /bin/bash incoming + ``` 1. Set a password for this user. - ```sh - sudo passwd incoming - ``` + ```sh + sudo passwd incoming + ``` - Be sure not to forget this, you'll need it later. + Be sure not to forget this, you'll need it later. ## Test the out-of-the-box setup 1. Connect to the local SMTP server: - ```sh - telnet localhost 25 - ``` + ```sh + telnet localhost 25 + ``` - You should see a prompt like this: + You should see a prompt like this: - ```sh - Trying 127.0.0.1... - Connected to localhost. - Escape character is '^]'. - 220 gitlab.example.com ESMTP Postfix (Ubuntu) - ``` + ```sh + Trying 127.0.0.1... + Connected to localhost. + Escape character is '^]'. + 220 gitlab.example.com ESMTP Postfix (Ubuntu) + ``` - If you get a `Connection refused` error instead, verify that `postfix` is running: + If you get a `Connection refused` error instead, verify that `postfix` is running: - ```sh - sudo postfix status - ``` + ```sh + sudo postfix status + ``` - If it is not, start it: + If it is not, start it: - ```sh - sudo postfix start - ``` + ```sh + sudo postfix start + ``` 1. Send the new `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt: - ``` - ehlo localhost - mail from: root@localhost - rcpt to: incoming@localhost - data - Subject: Re: Some issue + ``` + ehlo localhost + mail from: root@localhost + rcpt to: incoming@localhost + data + Subject: Re: Some issue - Sounds good! - . - quit - ``` + Sounds good! + . + quit + ``` - _**Note:** The `.` is a literal period on its own line._ + _**Note:** The `.` is a literal period on its own line._ - _**Note:** If you receive an error after entering `rcpt to: incoming@localhost` - then your Postfix `my_network` configuration is not correct. The error will - say 'Temporary lookup failure'. See - [Configure Postfix to receive email from the Internet](#configure-postfix-to-receive-email-from-the-internet)._ + _**Note:** If you receive an error after entering `rcpt to: incoming@localhost` + then your Postfix `my_network` configuration is not correct. The error will + say 'Temporary lookup failure'. See + [Configure Postfix to receive email from the Internet](#configure-postfix-to-receive-email-from-the-internet)._ 1. Check if the `incoming` user received the email: - ```sh - su - incoming - mail - ``` + ```sh + su - incoming + mail + ``` - You should see output like this: + You should see output like this: - ``` - "/var/mail/incoming": 1 message 1 unread - >U 1 root@localhost 59/2842 Re: Some issue - ``` + ``` + "/var/mail/incoming": 1 message 1 unread + >U 1 root@localhost 59/2842 Re: Some issue + ``` - Quit the mail app: + Quit the mail app: - ```sh - q - ``` + ```sh + q + ``` 1. Log out of the `incoming` account and go back to being `root`: - ```sh - logout - ``` + ```sh + logout + ``` ## Configure Postfix to use Maildir-style mailboxes @@ -124,208 +124,212 @@ Courier, which we will install later to add IMAP authentication, requires mailbo 1. Configure Postfix to use Maildir-style mailboxes: - ```sh - sudo postconf -e "home_mailbox = Maildir/" - ``` + ```sh + sudo postconf -e "home_mailbox = Maildir/" + ``` 1. Restart Postfix: - ```sh - sudo /etc/init.d/postfix restart - ``` + ```sh + sudo /etc/init.d/postfix restart + ``` 1. Test the new setup: - 1. Follow steps 1 and 2 of _[Test the out-of-the-box setup](#test-the-out-of-the-box-setup)_. - 1. Check if the `incoming` user received the email: + 1. Follow steps 1 and 2 of _[Test the out-of-the-box setup](#test-the-out-of-the-box-setup)_. + 1. Check if the `incoming` user received the email: - ```sh - su - incoming - MAIL=/home/incoming/Maildir - mail - ``` + ```sh + su - incoming + MAIL=/home/incoming/Maildir + mail + ``` - You should see output like this: + You should see output like this: - ``` - "/home/incoming/Maildir": 1 message 1 unread - >U 1 root@localhost 59/2842 Re: Some issue - ``` + ``` + "/home/incoming/Maildir": 1 message 1 unread + >U 1 root@localhost 59/2842 Re: Some issue + ``` - Quit the mail app: + Quit the mail app: - ```sh - q - ``` + ```sh + q + ``` - _**Note:** If `mail` returns an error `Maildir: Is a directory` then your - version of `mail` doesn't support Maildir style mailboxes. Install - `heirloom-mailx` by running `sudo apt-get install heirloom-mailx`. Then, - try the above steps again, substituting `heirloom-mailx` for the `mail` - command._ + _**Note:** If `mail` returns an error `Maildir: Is a directory` then your + version of `mail` doesn't support Maildir style mailboxes. Install + `heirloom-mailx` by running `sudo apt-get install heirloom-mailx`. Then, + try the above steps again, substituting `heirloom-mailx` for the `mail` + command._ 1. Log out of the `incoming` account and go back to being `root`: - ```sh - logout - ``` + ```sh + logout + ``` ## Install the Courier IMAP server 1. Install the `courier-imap` package: - ```sh - sudo apt-get install courier-imap - ``` + ```sh + sudo apt-get install courier-imap + ``` - And start `imapd`: - ```sh - imapd start - ``` + And start `imapd`: + + ```sh + imapd start + ``` 1. The courier-authdaemon isn't started after installation. Without it, imap authentication will fail: - ```sh - sudo service courier-authdaemon start - ``` - You can also configure courier-authdaemon to start on boot: - ```sh - sudo systemctl enable courier-authdaemon - ``` + + ```sh + sudo service courier-authdaemon start + ``` + + You can also configure courier-authdaemon to start on boot: + + ```sh + sudo systemctl enable courier-authdaemon + ``` ## Configure Postfix to receive email from the internet 1. Let Postfix know about the domains that it should consider local: - ```sh - sudo postconf -e "mydestination = gitlab.example.com, localhost.localdomain, localhost" - ``` + ```sh + sudo postconf -e "mydestination = gitlab.example.com, localhost.localdomain, localhost" + ``` 1. Let Postfix know about the IPs that it should consider part of the LAN: - We'll assume `192.168.1.0/24` is your local LAN. You can safely skip this step if you don't have other machines in the same local network. + We'll assume `192.168.1.0/24` is your local LAN. You can safely skip this step if you don't have other machines in the same local network. - ```sh - sudo postconf -e "mynetworks = 127.0.0.0/8, 192.168.1.0/24" - ``` + ```sh + sudo postconf -e "mynetworks = 127.0.0.0/8, 192.168.1.0/24" + ``` 1. Configure Postfix to receive mail on all interfaces, which includes the internet: - ```sh - sudo postconf -e "inet_interfaces = all" - ``` + ```sh + sudo postconf -e "inet_interfaces = all" + ``` 1. Configure Postfix to use the `+` delimiter for sub-addressing: - ```sh - sudo postconf -e "recipient_delimiter = +" - ``` + ```sh + sudo postconf -e "recipient_delimiter = +" + ``` 1. Restart Postfix: - ```sh - sudo service postfix restart - ``` + ```sh + sudo service postfix restart + ``` ## Test the final setup 1. Test SMTP under the new setup: - 1. Connect to the SMTP server: + 1. Connect to the SMTP server: - ```sh - telnet gitlab.example.com 25 - ``` + ```sh + telnet gitlab.example.com 25 + ``` - You should see a prompt like this: + You should see a prompt like this: - ```sh - Trying 123.123.123.123... - Connected to gitlab.example.com. - Escape character is '^]'. - 220 gitlab.example.com ESMTP Postfix (Ubuntu) - ``` + ```sh + Trying 123.123.123.123... + Connected to gitlab.example.com. + Escape character is '^]'. + 220 gitlab.example.com ESMTP Postfix (Ubuntu) + ``` - If you get a `Connection refused` error instead, make sure your firewall is set up to allow inbound traffic on port 25. + If you get a `Connection refused` error instead, make sure your firewall is set up to allow inbound traffic on port 25. - 1. Send the `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt: + 1. Send the `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt: - ``` - ehlo gitlab.example.com - mail from: root@gitlab.example.com - rcpt to: incoming@gitlab.example.com - data - Subject: Re: Some issue + ``` + ehlo gitlab.example.com + mail from: root@gitlab.example.com + rcpt to: incoming@gitlab.example.com + data + Subject: Re: Some issue - Sounds good! - . - quit - ``` + Sounds good! + . + quit + ``` - (Note: The `.` is a literal period on its own line) + (Note: The `.` is a literal period on its own line) - 1. Check if the `incoming` user received the email: + 1. Check if the `incoming` user received the email: - ```sh - su - incoming - MAIL=/home/incoming/Maildir - mail - ``` + ```sh + su - incoming + MAIL=/home/incoming/Maildir + mail + ``` - You should see output like this: + You should see output like this: - ``` - "/home/incoming/Maildir": 1 message 1 unread - >U 1 root@gitlab.example.com 59/2842 Re: Some issue - ``` + ``` + "/home/incoming/Maildir": 1 message 1 unread + >U 1 root@gitlab.example.com 59/2842 Re: Some issue + ``` - Quit the mail app: + Quit the mail app: - ```sh - q - ``` + ```sh + q + ``` - 1. Log out of the `incoming` account and go back to being `root`: + 1. Log out of the `incoming` account and go back to being `root`: - ```sh - logout - ``` + ```sh + logout + ``` 1. Test IMAP under the new setup: - 1. Connect to the IMAP server: + 1. Connect to the IMAP server: - ```sh - telnet gitlab.example.com 143 - ``` + ```sh + telnet gitlab.example.com 143 + ``` - You should see a prompt like this: + You should see a prompt like this: - ```sh - Trying 123.123.123.123... - Connected to mail.example.gitlab.com. - Escape character is '^]'. - - OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2011 Double Precision, Inc. See COPYING for distribution information. - ``` + ```sh + Trying 123.123.123.123... + Connected to mail.example.gitlab.com. + Escape character is '^]'. + - OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2011 Double Precision, Inc. See COPYING for distribution information. + ``` - 1. Sign in as the `incoming` user to test IMAP, by entering the following into the IMAP prompt: + 1. Sign in as the `incoming` user to test IMAP, by entering the following into the IMAP prompt: - ``` - a login incoming PASSWORD - ``` + ``` + a login incoming PASSWORD + ``` - Replace PASSWORD with the password you set on the `incoming` user earlier. + Replace PASSWORD with the password you set on the `incoming` user earlier. - You should see output like this: + You should see output like this: - ``` - a OK LOGIN Ok. - ``` + ``` + a OK LOGIN Ok. + ``` - 1. Disconnect from the IMAP server: + 1. Disconnect from the IMAP server: - ```sh - a logout - ``` + ```sh + a logout + ``` ## Done! diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md index 4aafc06cfdc..3de860f9240 100644 --- a/doc/administration/repository_storage_paths.md +++ b/doc/administration/repository_storage_paths.md @@ -68,18 +68,18 @@ NOTE: **Note:** This example uses NFS and CephFS. We do not recommend using EFS 1. Edit `gitlab.yml` and add the storage paths: - ```yaml - repositories: - # Paths where repositories can be stored. Give the canonicalized absolute pathname. - # NOTE: REPOS PATHS MUST NOT CONTAIN ANY SYMLINK!!! - storages: # You must have at least a 'default' storage path. - default: - path: /home/git/repositories - nfs: - path: /mnt/nfs/repositories - cephfs: - path: /mnt/cephfs/repositories - ``` + ```yaml + repositories: + # Paths where repositories can be stored. Give the canonicalized absolute pathname. + # NOTE: REPOS PATHS MUST NOT CONTAIN ANY SYMLINK!!! + storages: # You must have at least a 'default' storage path. + default: + path: /home/git/repositories + nfs: + path: /mnt/nfs/repositories + cephfs: + path: /mnt/cephfs/repositories + ``` 1. [Restart GitLab][restart-gitlab] for the changes to take effect. @@ -97,16 +97,16 @@ working, you can remove the `repos_path` line. 1. Edit `/etc/gitlab/gitlab.rb` by appending the rest of the paths to the default one: - ```ruby - git_data_dirs({ - "default" => { "path" => "/var/opt/gitlab/git-data" }, - "nfs" => { "path" => "/mnt/nfs/git-data" }, - "cephfs" => { "path" => "/mnt/cephfs/git-data" } - }) - ``` + ```ruby + git_data_dirs({ + "default" => { "path" => "/var/opt/gitlab/git-data" }, + "nfs" => { "path" => "/mnt/nfs/git-data" }, + "cephfs" => { "path" => "/mnt/cephfs/git-data" } + }) + ``` - Note that Omnibus stores the repositories in a `repositories` subdirectory - of the `git-data` directory. + Note that Omnibus stores the repositories in a `repositories` subdirectory + of the `git-data` directory. ## Choose where new project repositories will be stored diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md index 834b41b3a2c..9dea6074a3f 100644 --- a/doc/administration/repository_storage_types.md +++ b/doc/administration/repository_storage_types.md @@ -6,19 +6,19 @@ Two different storage layouts can be used to store the repositories on disk and their characteristics. GitLab can be configured to use one or multiple repository shard locations -that can be: +that can be: - Mounted to the local disk - Exposed as an NFS shared volume - Acessed via [gitaly] on its own machine. In GitLab, this is configured in `/etc/gitlab/gitlab.rb` by the `git_data_dirs({})` -configuration hash. The storage layouts discussed here will apply to any shard +configuration hash. The storage layouts discussed here will apply to any shard defined in it. The `default` repository shard that is available in any installations that haven't customized it, points to the local folder: `/var/opt/gitlab/git-data`. -Anything discussed below is expected to be part of that folder. +Anything discussed below is expected to be part of that folder. ## Legacy Storage @@ -80,25 +80,20 @@ by another folder with the next 2 characters. They are both stored in a special ### Hashed object pools -CAUTION: **Beta:** -Hashed objects pools are considered beta, and are not ready for production use. -Follow [gitaly#1548](https://gitlab.com/gitlab-org/gitaly/issues/1548) for -updates. +> [Introduced](https://gitlab.com/gitlab-org/gitaly/issues/1606) in GitLab 12.1. -For deduplication of public forks and their parent repository, objects are pooled -in an object pool. These object pools are a third repository where shared objects -are stored. +Forks of public projects are deduplicated by creating a third repository, the object pool, containing the objects from the source project. Using `objects/info/alternates`, the source project and forks use the object pool for shared objects. Objects are moved from the source project to the object pool when housekeeping is run on the source project. ```ruby # object pool paths "@pools/#{hash[0..1]}/#{hash[2..3]}/#{hash}.git" ``` -The object pool feature is behind the `object_pools` feature flag, and can be -enabled for individual projects by executing -`Feature.enable(:object_pools, Project.find(<id>))`. Note that the project has to -be on hashed storage, should not be a fork itself, and hashed storage should be -enabled for all new projects. +Object pools can be disabled using the `object_pools` feature flag, and can be +disabled for individual projects by executing +`Feature.disable(:object_pools, Project.find(<id>))`. Disabling object pools +will not change existing deduplicated forks, but will prevent new forks from +being deduplicated. DANGER: **Danger:** Do not run `git prune` or `git gc` in pool repositories! This can @@ -108,7 +103,7 @@ question. ### How to migrate to Hashed Storage To start a migration, enable Hashed Storage for new projects: - + 1. Go to **Admin > Settings > Repository** and expand the **Repository Storage** section. 2. Select the **Use hashed storage paths for newly created and renamed projects** checkbox. @@ -129,7 +124,7 @@ an Omnibus Gitlab installation: sudo gitlab-rake gitlab:storage:migrate_to_hashed ID_FROM=50 ID_TO=100 ``` -Check the [documentation][rake/migrate-to-hashed] for additional information and instructions for +Check the [documentation][rake/migrate-to-hashed] for additional information and instructions for source-based installation. #### Rollback @@ -140,12 +135,12 @@ projects: 1. Go to **Admin > Settings > Repository** and expand the **Repository Storage** section. 2. Uncheck the **Use hashed storage paths for newly created and renamed projects** checkbox. -To schedule a complete rollback, see the +To schedule a complete rollback, see the [rake task documentation for storage rollback](raketasks/storage.md#rollback-from-hashed-storage-to-legacy-storage) for instructions. The rollback task also supports specifying a range of Project IDs. Here is an example of limiting the rollout to Project IDs 50 to 100, in an Omnibus Gitlab installation: - + ```bash sudo gitlab-rake gitlab:storage:rollback_to_legacy ID_FROM=50 ID_TO=100 ``` diff --git a/doc/administration/restart_gitlab.md b/doc/administration/restart_gitlab.md index cbc3fbd9473..e23f2052d04 100644 --- a/doc/administration/restart_gitlab.md +++ b/doc/administration/restart_gitlab.md @@ -137,9 +137,9 @@ If you are using other init systems, like systemd, you can check the [GitLab Recipes][gl-recipes] repository for some unofficial services. These are **not** officially supported so use them at your own risk. -[omnibus-dl]: https://about.gitlab.com/downloads/ "Download the Omnibus packages" +[omnibus-dl]: https://about.gitlab.com/install/ "Download the Omnibus packages" [install]: ../install/installation.md "Documentation to install GitLab from source" [mailroom]: reply_by_email.md "Used for replying by email in GitLab issues and merge requests" -[chef]: https://www.chef.io/chef/ "Chef official website" +[chef]: https://www.chef.io/products/chef-infra/ "Chef official website" [src-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/support/init.d/gitlab "GitLab init service file" [gl-recipes]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/init "GitLab Recipes repository" diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md index 8f7280d5128..098d946a9fa 100644 --- a/doc/administration/troubleshooting/debug.md +++ b/doc/administration/troubleshooting/debug.md @@ -10,43 +10,43 @@ an SMTP server, but you're not seeing mail delivered. Here's how to check the se 1. Run a Rails console: - ```sh - sudo gitlab-rails console production - ``` + ```sh + sudo gitlab-rails console production + ``` - or for source installs: + or for source installs: - ```sh - bundle exec rails console production - ``` + ```sh + bundle exec rails console production + ``` 1. Look at the ActionMailer `delivery_method` to make sure it matches what you intended. If you configured SMTP, it should say `:smtp`. If you're using Sendmail, it should say `:sendmail`: - ```ruby - irb(main):001:0> ActionMailer::Base.delivery_method - => :smtp - ``` + ```ruby + irb(main):001:0> ActionMailer::Base.delivery_method + => :smtp + ``` 1. If you're using SMTP, check the mail settings: - ```ruby - irb(main):002:0> ActionMailer::Base.smtp_settings - => {:address=>"localhost", :port=>25, :domain=>"localhost.localdomain", :user_name=>nil, :password=>nil, :authentication=>nil, :enable_starttls_auto=>true}``` - ``` + ```ruby + irb(main):002:0> ActionMailer::Base.smtp_settings + => {:address=>"localhost", :port=>25, :domain=>"localhost.localdomain", :user_name=>nil, :password=>nil, :authentication=>nil, :enable_starttls_auto=>true}``` + ``` - In the example above, the SMTP server is configured for the local machine. If this is intended, you may need to check your local mail - logs (e.g. `/var/log/mail.log`) for more details. + In the example above, the SMTP server is configured for the local machine. If this is intended, you may need to check your local mail + logs (e.g. `/var/log/mail.log`) for more details. -1. Send a test message via the console. +1. Send a test message via the console. - ```ruby - irb(main):003:0> Notify.test_email('youremail@email.com', 'Hello World', 'This is a test message').deliver_now - ``` + ```ruby + irb(main):003:0> Notify.test_email('youremail@email.com', 'Hello World', 'This is a test message').deliver_now + ``` - If you do not receive an e-mail and/or see an error message, then check - your mail server settings. + If you do not receive an e-mail and/or see an error message, then check + your mail server settings. ## Advanced Issues @@ -103,37 +103,37 @@ downtime. Otherwise skip to the next section. 1. Run `sudo gdb -p <PID>` to attach to the unicorn process. 1. In the gdb window, type: - ``` - call (void) rb_backtrace() - ``` + ``` + call (void) rb_backtrace() + ``` 1. This forces the process to generate a Ruby backtrace. Check `/var/log/gitlab/unicorn/unicorn_stderr.log` for the backtace. For example, you may see: - ```ruby - from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `block in start' - from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `loop' - from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:36:in `block (2 levels) in start' - from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:44:in `sample' - from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `sample_objects' - from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each_with_object' - from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each' - from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `block in sample_objects' - from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `name' - ``` + ```ruby + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `block in start' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `loop' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:36:in `block (2 levels) in start' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:44:in `sample' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `sample_objects' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each_with_object' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `block in sample_objects' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `name' + ``` 1. To see the current threads, run: - ``` - thread apply all bt - ``` + ``` + thread apply all bt + ``` 1. Once you're done debugging with `gdb`, be sure to detach from the process and exit: - ``` - detach - exit - ``` + ``` + detach + exit + ``` Note that if the unicorn process terminates before you are able to run these commands, gdb will report an error. To buy more time, you can always raise the @@ -162,21 +162,21 @@ separate Rails process to debug the issue: 1. Create a Personal Access Token for your user (Profile Settings -> Access Tokens). 1. Bring up the GitLab Rails console. For omnibus users, run: - ``` - sudo gitlab-rails console - ``` + ``` + sudo gitlab-rails console + ``` 1. At the Rails console, run: - ```ruby - [1] pry(main)> app.get '<URL FROM STEP 2>/?private_token=<TOKEN FROM STEP 3>' - ``` + ```ruby + [1] pry(main)> app.get '<URL FROM STEP 2>/?private_token=<TOKEN FROM STEP 3>' + ``` - For example: + For example: - ```ruby - [1] pry(main)> app.get 'https://gitlab.com/gitlab-org/gitlab-ce/issues/1?private_token=123456' - ``` + ```ruby + [1] pry(main)> app.get 'https://gitlab.com/gitlab-org/gitlab-ce/issues/1?private_token=123456' + ``` 1. In a new window, run `top`. It should show this ruby process using 100% CPU. Write down the PID. 1. Follow step 2 from the previous section on using gdb. diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md index 708b59a273b..277d42d06c6 100644 --- a/doc/administration/uploads.md +++ b/doc/administration/uploads.md @@ -1,11 +1,10 @@ # Uploads administration ->**Notes:** Uploads represent all user data that may be sent to GitLab as a single file. As an example, avatars and notes' attachments are uploads. Uploads are integral to GitLab functionality, and therefore cannot be disabled. -### Using local storage +## Using local storage ->**Notes:** +NOTE: **Note:** This is the default configuration To change the location where the uploads are stored locally, follow the steps @@ -15,7 +14,7 @@ below. **In Omnibus installations:** ->**Notes:** +NOTE: **Note:** For historical reasons, uploads are stored into a base directory, which by default is `uploads/-/system`. It is strongly discouraged to change this configuration option on an existing GitLab installation. _The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads`._ @@ -23,10 +22,10 @@ _The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads`._ 1. To change the storage path for example to `/mnt/storage/uploads`, edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['uploads_storage_path'] = "/mnt/storage/" - gitlab_rails['uploads_base_dir'] = "uploads" - ``` + ```ruby + gitlab_rails['uploads_storage_path'] = "/mnt/storage/" + gitlab_rails['uploads_base_dir'] = "uploads" + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. @@ -40,15 +39,15 @@ _The uploads are stored by default in 1. To change the storage path for example to `/mnt/storage/uploads`, edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - uploads: - storage_path: /mnt/storage - base_dir: uploads - ``` + ```yaml + uploads: + storage_path: /mnt/storage + base_dir: uploads + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. -### Using object storage **[CORE ONLY]** +## Using object storage **(CORE ONLY)** > **Notes:** > @@ -60,7 +59,7 @@ If you don't want to use the local disk where GitLab is installed to store the uploads, you can use an object storage provider like AWS S3 instead. This configuration relies on valid AWS credentials to be configured already. -### Object Storage Settings +## Object Storage Settings For source installations the following settings are nested under `uploads:` and then `object_store:`. On omnibus installs they are prefixed by `uploads_object_store_`. @@ -73,7 +72,7 @@ For source installations the following settings are nested under `uploads:` and | `proxy_download` | Set to true to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` | | `connection` | Various connection options described below | | -#### S3 compatible connection settings +### S3 compatible connection settings The connection settings match those provided by [Fog](https://github.com/fog), and are as follows: @@ -97,27 +96,27 @@ _The uploads are stored by default in 1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with the values you want: - ```ruby - gitlab_rails['uploads_object_store_enabled'] = true - gitlab_rails['uploads_object_store_remote_directory'] = "uploads" - gitlab_rails['uploads_object_store_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-central-1', - 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', - 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY' - } - ``` - - >**Note:** - If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs. - - ```ruby - gitlab_rails['uploads_object_store_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-central-1', - 'use_iam_profile' => true - } - ``` + ```ruby + gitlab_rails['uploads_object_store_enabled'] = true + gitlab_rails['uploads_object_store_remote_directory'] = "uploads" + gitlab_rails['uploads_object_store_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-central-1', + 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID', + 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY' + } + ``` + + NOTE: **Note:** + If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs. + + ```ruby + gitlab_rails['uploads_object_store_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-central-1', + 'use_iam_profile' => true + } + ``` 1. Save the file and [reconfigure GitLab][] for the changes to take effect. 1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate` rake task](raketasks/uploads/migrate.md). @@ -132,17 +131,17 @@ _The uploads are stored by default in 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - uploads: - object_store: - enabled: true - remote_directory: "uploads" # The bucket name - connection: - provider: AWS # Only AWS supported at the moment - aws_access_key_id: AWS_ACESS_KEY_ID - aws_secret_access_key: AWS_SECRET_ACCESS_KEY - region: eu-central-1 - ``` + ```yaml + uploads: + object_store: + enabled: true + remote_directory: "uploads" # The bucket name + connection: + provider: AWS # Only AWS supported at the moment + aws_access_key_id: AWS_ACESS_KEY_ID + aws_secret_access_key: AWS_SECRET_ACCESS_KEY + region: eu-central-1 + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. 1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate` rake task](raketasks/uploads/migrate.md). diff --git a/doc/api/README.md b/doc/api/README.md index 23c69deef23..b2bb55f2fcf 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -37,16 +37,16 @@ The following API resources are available in the project context: | [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) | | [Issues Statistics](issues_statistics.md) | `/projects/:id/issues_statistics` (also available for groups and standalone) | | [Issue boards](boards.md) | `/projects/:id/boards` | -| [Issue links](issue_links.md) **[STARTER]** | `/projects/:id/issues/.../links` | +| [Issue links](issue_links.md) **(STARTER)** | `/projects/:id/issues/.../links` | | [Jobs](jobs.md) | `/projects/:id/jobs`, `/projects/:id/pipelines/.../jobs` | | [Labels](labels.md) | `/projects/:id/labels` | -| [Managed licenses](managed_licenses.md) **[ULTIMATE]** | `/projects/:id/managed_licenses` | +| [Managed licenses](managed_licenses.md) **(ULTIMATE)** | `/projects/:id/managed_licenses` | | [Members](members.md) | `/projects/:id/members` (also available for groups) | -| [Merge request approvals](merge_request_approvals.md) **[STARTER]** | `/projects/:id/approvals`, `/projects/:id/merge_requests/.../approvals` | +| [Merge request approvals](merge_request_approvals.md) **(STARTER)** | `/projects/:id/approvals`, `/projects/:id/merge_requests/.../approvals` | | [Merge requests](merge_requests.md) | `/projects/:id/merge_requests` (also available for groups and standalone) | | [Notes](notes.md) (comments) | `/projects/:id/issues/.../notes`, `/projects/:id/snippets/.../notes`, `/projects/:id/merge_requests/.../notes` (also available for groups) | | [Notification settings](notification_settings.md) | `/projects/:id/notification_settings` (also available for groups and standalone) | -| [Packages](packages.md) **[PREMIUM]** | `/projects/:id/packages` | +| [Packages](packages.md) **(PREMIUM)** | `/projects/:id/packages` | | [Pages domains](pages_domains.md) | `/projects/:id/pages` (also available standalone) | | [Pipelines](pipelines.md) | `/projects/:id/pipelines` | | [Pipeline schedules](pipeline_schedules.md) | `/projects/:id/pipeline_schedules` | @@ -71,7 +71,7 @@ The following API resources are available in the project context: | [Search](search.md) | `/projects/:id/search` (also available for groups and standalone) | | [Services](services.md) | `/projects/:id/services` | | [Tags](tags.md) | `/projects/:id/repository/tags` | -| [Vulnerabilities](vulnerabilities.md) **[ULTIMATE]** | `/projects/:id/vulnerabilities` (also available for groups) | +| [Vulnerabilities](vulnerabilities.md) **(ULTIMATE)** | `/projects/:id/vulnerabilities` (also available for groups) | | [Wikis](wikis.md) | `/projects/:id/wikis` | ### Group resources @@ -82,10 +82,10 @@ The following API resources are available in the group context: |:-----------------------------------------------------------------|:---------------------------------------------------------------------------------| | [Access requests](access_requests.md) | `/groups/:id/access_requests/` (also available for projects) | | [Custom attributes](custom_attributes.md) | `/groups/:id/custom_attributes` (also available for projects and users) | -| [Discussions](discussions.md) (threaded comments) **[ULTIMATE]** | `/groups/:id/epics/.../discussions` (also available for projects) | -| [Epic issues](epic_issues.md) **[ULTIMATE]** | `/groups/:id/epics/.../issues` | -| [Epic links](epic_links.md) **[ULTIMATE]** | `/groups/:id/epics/.../epics` | -| [Epics](epics.md) **[ULTIMATE]** | `/groups/:id/epics` | +| [Discussions](discussions.md) (threaded comments) **(ULTIMATE)** | `/groups/:id/epics/.../discussions` (also available for projects) | +| [Epic issues](epic_issues.md) **(ULTIMATE)** | `/groups/:id/epics/.../issues` | +| [Epic links](epic_links.md) **(ULTIMATE)** | `/groups/:id/epics/.../epics` | +| [Epics](epics.md) **(ULTIMATE)** | `/groups/:id/epics` | | [Groups](groups.md) | `/groups`, `/groups/.../subgroups` | | [Group badges](group_badges.md) | `/groups/:id/badges` | | [Group issue boards](group_boards.md) | `/groups/:id/boards` | @@ -115,12 +115,12 @@ The following API resources are available outside of project and group contexts | [Deploy keys](deploy_keys.md) | `/deploy_keys` (also available for projects) | | [Events](events.md) | `/events`, `/users/:id/events` (also available for projects) | | [Feature flags](features.md) | `/features` | -| [Geo Nodes](geo_nodes.md) **[PREMIUM ONLY]** | `/geo_nodes` | +| [Geo Nodes](geo_nodes.md) **(PREMIUM ONLY)** | `/geo_nodes` | | [Import repository from GitHub](import.md) | `/import/github` | | [Issues](issues.md) | `/issues` (also available for groups and projects) | | [Issues Statistics](issues_statistics.md) | `/issues_statistics` (also available for groups and projects) | | [Keys](keys.md) | `/keys` | -| [License](license.md) **[CORE ONLY]** | `/license` | +| [License](license.md) **(CORE ONLY)** | `/license` | | [Markdown](markdown.md) | `/markdown` | | [Merge requests](merge_requests.md) | `/merge_requests` (also available for groups and projects) | | [Namespaces](namespaces.md) | `/namespaces` | @@ -147,16 +147,17 @@ Endpoints are available for: - [GitLab CI YAML templates](templates/gitlab_ci_ymls.md). - [Open source license templates](templates/licenses.md). -## SCIM **[SILVER ONLY]** +## SCIM **(SILVER ONLY)** [GitLab.com Silver and above](https://about.gitlab.com/pricing/) provides an [SCIM API](scim.md) that implements [the RFC7644 protocol](https://tools.ietf.org/html/rfc7644) and provides the `/Users` endpoint. The base URL is: `/api/scim/v2/groups/:group_path/Users/`. ## Road to GraphQL -Going forward, we will start on moving to -[GraphQL](graphql/index.md) and deprecate the use of -controller-specific endpoints. GraphQL has a number of benefits: +[GraphQL](graphql/index.md) is available in GitLab, which will +allow deprecation of controller-specific endpoints. + +GraphQL has a number of benefits: 1. We avoid having to maintain two different APIs. 1. Callers of the API can request only what they need. @@ -311,9 +312,9 @@ By default, impersonation is enabled. To disable impersonation: 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['impersonation_enabled'] = false - ``` + ```ruby + gitlab_rails['impersonation_enabled'] = false + ``` 1. Save the file and [reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect. @@ -326,10 +327,10 @@ To re-enable impersonation, remove this configuration and reconfigure GitLab. 1. Edit `config/gitlab.yml`: - ```yaml - gitlab: - impersonation_enabled: false - ``` + ```yaml + gitlab: + impersonation_enabled: false + ``` 1. Save the file and [restart](../administration/restart_gitlab.md#installations-from-source) GitLab for the changes to take effect. diff --git a/doc/api/boards.md b/doc/api/boards.md index a96206f5df3..08ec1d832df 100644 --- a/doc/api/boards.md +++ b/doc/api/boards.md @@ -141,7 +141,7 @@ Example response: } ``` -## Create a board **[STARTER]** +## Create a board **(STARTER)** Creates a board. @@ -209,7 +209,7 @@ Example response: } ``` -## Update a board **[STARTER]** +## Update a board **(STARTER)** > [Introduced][ee-5954] in [GitLab Starter](https://about.gitlab.com/pricing/) 11.1. @@ -229,7 +229,6 @@ PUT /projects/:id/boards/:board_id | `labels` | string | no | Comma-separated list of label names which the board should be scoped to | | `weight` | integer | no | The weight range from 0 to 9, to which the board should be scoped to | - ```bash curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/boards/1?name=new_name&milestone_id=43&assignee_id=1&labels=Doing&weight=4 ``` @@ -291,7 +290,7 @@ Example response: } ``` -## Delete a board **[STARTER]** +## Delete a board **(STARTER)** Deletes a board. @@ -405,8 +404,8 @@ POST /projects/:id/boards/:board_id/lists | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | | `board_id` | integer | yes | The ID of a board | | `label_id` | integer | no | The ID of a label | -| `assignee_id` **[PREMIUM]** | integer | no | The ID of a user | -| `milestone_id` **[PREMIUM]** | integer | no | The ID of a milestone | +| `assignee_id` **(PREMIUM)** | integer | no | The ID of a user | +| `milestone_id` **(PREMIUM)** | integer | no | The ID of a milestone | NOTE: **Note**: Label, assignee and milestone arguments are mutually exclusive, diff --git a/doc/api/commits.md b/doc/api/commits.md index 25015fad9e3..6eb4c47415f 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -716,6 +716,7 @@ Example response if commit is signed: ``` Example response if commit is unsigned: + ```json { "message": "404 GPG Signature Not Found" diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md index 1d7523fcc3d..41f6ab436e8 100644 --- a/doc/api/deploy_keys.md +++ b/doc/api/deploy_keys.md @@ -19,13 +19,13 @@ Example response: { "id": 1, "title": "Public key", - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", "created_at": "2013-10-02T10:12:29Z" }, { "id": 3, "title": "Another Public key", - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", "created_at": "2013-10-02T11:12:29Z" } ] diff --git a/doc/api/discussions.md b/doc/api/discussions.md index 9defef4fd53..208b8dca2e2 100644 --- a/doc/api/discussions.md +++ b/doc/api/discussions.md @@ -4,7 +4,7 @@ Discussions are a set of related notes on: - Snippets - Issues -- Epics **[ULTIMATE]** +- Epics **(ULTIMATE)** - Merge requests - Commits @@ -430,7 +430,7 @@ Parameters: curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/snippets/11/discussions/636 ``` -## Epics **[ULTIMATE]** +## Epics **(ULTIMATE)** ### List group epic discussions diff --git a/doc/api/epic_issues.md b/doc/api/epic_issues.md index ec59ea7068e..02317cc6c09 100644 --- a/doc/api/epic_issues.md +++ b/doc/api/epic_issues.md @@ -1,4 +1,4 @@ -# Epic Issues API **[ULTIMATE]** +# Epic Issues API **(ULTIMATE)** Every API call to epic_issues must be authenticated. diff --git a/doc/api/epic_links.md b/doc/api/epic_links.md index 9ad90a6d0f1..6089198e46a 100644 --- a/doc/api/epic_links.md +++ b/doc/api/epic_links.md @@ -1,4 +1,4 @@ -# Epic Links API **[ULTIMATE]** +# Epic Links API **(ULTIMATE)** >**Note:** > This endpoint was [introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9188) in GitLab 11.8. diff --git a/doc/api/epics.md b/doc/api/epics.md index 0541cfaa715..d05eb0a8804 100644 --- a/doc/api/epics.md +++ b/doc/api/epics.md @@ -1,4 +1,4 @@ -# Epics API **[ULTIMATE]** +# Epics API **(ULTIMATE)** Every API call to epic must be authenticated. @@ -302,7 +302,7 @@ POST /groups/:id/epics/:epic_iid/todo | Attribute | Type | Required | Description | |-------------|---------|----------|--------------------------------------| | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | -| `epic_iid ` | integer | yes | The internal ID of a group's epic | +| `epic_iid` | integer | yes | The internal ID of a group's epic | ```bash curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/1/epics/5/todo diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md index ea31abdd87e..ac64cbedf7d 100644 --- a/doc/api/geo_nodes.md +++ b/doc/api/geo_nodes.md @@ -1,4 +1,4 @@ -# Geo Nodes API **[PREMIUM ONLY]** +# Geo Nodes API **(PREMIUM ONLY)** In order to interact with Geo node endpoints, you need to authenticate yourself as an admin. diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md index 6c1cce620ca..bdc7c1959d2 100644 --- a/doc/api/graphql/index.md +++ b/doc/api/graphql/index.md @@ -1,6 +1,8 @@ -# GraphQL API (Alpha) +# GraphQL API -> [Introduced][ce-19008] in GitLab 11.0. +> - [Introduced][ce-19008] in GitLab 11.0 (enabled by feature flag `graphql`). +> - [Always enabled](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/30444) + in GitLab 12.1. [GraphQL](https://graphql.org/) is a query language for APIs that allows clients to request exactly the data they need, making it @@ -23,30 +25,18 @@ programmatically with GitLab. To achieve this, it needs full coverage - anything possible in the REST API should also be possible in the GraphQL API. To help us meet this vision, the frontend should use GraphQL in preference to -the REST API for new features, although the alpha status of GraphQL may prevent -this from being a possibility at times. +the REST API for new features. There are no plans to deprecate the REST API. To reduce the technical burden of supporting two APIs in parallel, they should share implementations as much as possible. -## Enabling the GraphQL feature - -The GraphQL API itself is currently in Alpha, and therefore hidden behind a -feature flag. You can enable the feature using the [features api][features-api] on a self-hosted instance. - -For example: - -```shell -curl --data "value=100" --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/features/graphql -``` - ## Available queries A first iteration of a GraphQL API includes the following queries 1. `project` : Within a project it is also possible to fetch a `mergeRequest` by IID. -1. `group` : Basic group information and epics **[ULTIMATE]** are currently supported. +1. `group` : Basic group information and epics **(ULTIMATE)** are currently supported. 1. `namespace` : Within a namespace it is also possible to fetch `projects`. ### Multiplex queries @@ -57,6 +47,12 @@ info about multiplexed queries is also available for [graphql-ruby](https://graphql-ruby.org/queries/multiplex.html) the library GitLab uses on the backend. +## Reference + +GitLab's GraphQL reference [is available](reference/index.md). + +It is automatically generated from GitLab's GraphQL schema and embedded in a Markdown file. + ## GraphiQL The API can be explored by using the GraphiQL IDE, it is available on your diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md new file mode 100644 index 00000000000..2d3bec4ff67 --- /dev/null +++ b/doc/api/graphql/reference/index.md @@ -0,0 +1,507 @@ +<!--- + This documentation is auto generated by a script. + + Please do not edit this file directly, check compile_docs task on lib/tasks/gitlab/graphql.rake. +---> + +# GraphQL API Resources + +This documentation is self-generated based on GitLab current GraphQL schema. + +The API can be explored interactively using the [GraphiQL IDE](../index.md#graphiql). + +## Objects + +### AddAwardEmojiPayload + +| Name | Type | Description | +| --- | ---- | ---------- | +| `clientMutationId` | String | A unique identifier for the client performing the mutation. | +| `errors` | String! => Array | Reasons why the mutation failed. | +| `awardEmoji` | AwardEmoji | The award emoji after mutation | + +### AwardEmoji + +| Name | Type | Description | +| --- | ---- | ---------- | +| `name` | String! | The emoji name | +| `description` | String! | The emoji description | +| `unicode` | String! | The emoji in unicode | +| `emoji` | String! | The emoji as an icon | +| `unicodeVersion` | String! | The unicode version for this emoji | +| `user` | User! | The user who awarded the emoji | + +### Blob + +| Name | Type | Description | +| --- | ---- | ---------- | +| `id` | ID! | | +| `name` | String! | | +| `type` | EntryType! | | +| `path` | String! | | +| `flatPath` | String! | | +| `webUrl` | String | | +| `lfsOid` | String | | + +### Commit + +| Name | Type | Description | +| --- | ---- | ---------- | +| `id` | ID! | | +| `sha` | String! | | +| `title` | String | | +| `description` | String | | +| `message` | String | | +| `authoredDate` | Time | | +| `webUrl` | String! | | +| `author` | User | | +| `latestPipeline` | Pipeline | Latest pipeline for this commit | + +### DetailedStatus + +| Name | Type | Description | +| --- | ---- | ---------- | +| `group` | String! | | +| `icon` | String! | | +| `favicon` | String! | | +| `detailsPath` | String! | | +| `hasDetails` | Boolean! | | +| `label` | String! | | +| `text` | String! | | +| `tooltip` | String! | | + +### DiffPosition + +| Name | Type | Description | +| --- | ---- | ---------- | +| `headSha` | String! | The sha of the head at the time the comment was made | +| `baseSha` | String | The merge base of the branch the comment was made on | +| `startSha` | String! | The sha of the branch being compared against | +| `filePath` | String! | The path of the file that was changed | +| `oldPath` | String | The path of the file on the start sha. | +| `newPath` | String | The path of the file on the head sha. | +| `positionType` | DiffPositionType! | | +| `oldLine` | Int | The line on start sha that was changed | +| `newLine` | Int | The line on head sha that was changed | +| `x` | Int | The X postion on which the comment was made | +| `y` | Int | The Y position on which the comment was made | +| `width` | Int | The total width of the image | +| `height` | Int | The total height of the image | + +### Discussion + +| Name | Type | Description | +| --- | ---- | ---------- | +| `id` | ID! | | +| `createdAt` | Time! | | + +### Group + +| Name | Type | Description | +| --- | ---- | ---------- | +| `id` | ID! | | +| `name` | String! | | +| `path` | String! | | +| `fullName` | String! | | +| `fullPath` | ID! | | +| `description` | String | | +| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` | +| `visibility` | String | | +| `lfsEnabled` | Boolean | | +| `requestAccessEnabled` | Boolean | | +| `userPermissions` | GroupPermissions! | Permissions for the current user on the resource | +| `webUrl` | String! | | +| `avatarUrl` | String | | +| `parent` | Group | | + +### GroupPermissions + +| Name | Type | Description | +| --- | ---- | ---------- | +| `readGroup` | Boolean! | Whether or not a user can perform `read_group` on this resource | + +### Issue + +| Name | Type | Description | +| --- | ---- | ---------- | +| `userPermissions` | IssuePermissions! | Permissions for the current user on the resource | +| `iid` | ID! | | +| `title` | String! | | +| `titleHtml` | String | The GitLab Flavored Markdown rendering of `title` | +| `description` | String | | +| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` | +| `state` | IssueState! | | +| `reference` | String! | | +| `author` | User! | | +| `milestone` | Milestone | | +| `dueDate` | Time | | +| `confidential` | Boolean! | | +| `discussionLocked` | Boolean! | | +| `upvotes` | Int! | | +| `downvotes` | Int! | | +| `userNotesCount` | Int! | | +| `webPath` | String! | | +| `webUrl` | String! | | +| `relativePosition` | Int | | +| `closedAt` | Time | | +| `createdAt` | Time! | | +| `updatedAt` | Time! | | +| `taskCompletionStatus` | TaskCompletionStatus! | | + +### IssuePermissions + +| Name | Type | Description | +| --- | ---- | ---------- | +| `readIssue` | Boolean! | Whether or not a user can perform `read_issue` on this resource | +| `adminIssue` | Boolean! | Whether or not a user can perform `admin_issue` on this resource | +| `updateIssue` | Boolean! | Whether or not a user can perform `update_issue` on this resource | +| `createNote` | Boolean! | Whether or not a user can perform `create_note` on this resource | +| `reopenIssue` | Boolean! | Whether or not a user can perform `reopen_issue` on this resource | + +### Label + +| Name | Type | Description | +| --- | ---- | ---------- | +| `description` | String | | +| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` | +| `title` | String! | | +| `color` | String! | | +| `textColor` | String! | | + +### MergeRequest + +| Name | Type | Description | +| --- | ---- | ---------- | +| `userPermissions` | MergeRequestPermissions! | Permissions for the current user on the resource | +| `id` | ID! | | +| `iid` | String! | | +| `title` | String! | | +| `titleHtml` | String | The GitLab Flavored Markdown rendering of `title` | +| `description` | String | | +| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` | +| `state` | MergeRequestState! | | +| `createdAt` | Time! | | +| `updatedAt` | Time! | | +| `sourceProject` | Project | | +| `targetProject` | Project! | | +| `project` | Project! | | +| `projectId` | Int! | | +| `sourceProjectId` | Int | | +| `targetProjectId` | Int! | | +| `sourceBranch` | String! | | +| `targetBranch` | String! | | +| `workInProgress` | Boolean! | | +| `mergeWhenPipelineSucceeds` | Boolean | | +| `diffHeadSha` | String | | +| `mergeCommitSha` | String | | +| `userNotesCount` | Int | | +| `shouldRemoveSourceBranch` | Boolean | | +| `forceRemoveSourceBranch` | Boolean | | +| `mergeStatus` | String | | +| `inProgressMergeCommitSha` | String | | +| `mergeError` | String | | +| `allowCollaboration` | Boolean | | +| `shouldBeRebased` | Boolean! | | +| `rebaseCommitSha` | String | | +| `rebaseInProgress` | Boolean! | | +| `mergeCommitMessage` | String | | +| `defaultMergeCommitMessage` | String | | +| `mergeOngoing` | Boolean! | | +| `sourceBranchExists` | Boolean! | | +| `mergeableDiscussionsState` | Boolean | | +| `webUrl` | String | | +| `upvotes` | Int! | | +| `downvotes` | Int! | | +| `subscribed` | Boolean! | | +| `headPipeline` | Pipeline | | +| `taskCompletionStatus` | TaskCompletionStatus! | | + +### MergeRequestPermissions + +| Name | Type | Description | +| --- | ---- | ---------- | +| `readMergeRequest` | Boolean! | Whether or not a user can perform `read_merge_request` on this resource | +| `adminMergeRequest` | Boolean! | Whether or not a user can perform `admin_merge_request` on this resource | +| `updateMergeRequest` | Boolean! | Whether or not a user can perform `update_merge_request` on this resource | +| `createNote` | Boolean! | Whether or not a user can perform `create_note` on this resource | +| `pushToSourceBranch` | Boolean! | Whether or not a user can perform `push_to_source_branch` on this resource | +| `removeSourceBranch` | Boolean! | Whether or not a user can perform `remove_source_branch` on this resource | +| `cherryPickOnCurrentMergeRequest` | Boolean! | Whether or not a user can perform `cherry_pick_on_current_merge_request` on this resource | +| `revertOnCurrentMergeRequest` | Boolean! | Whether or not a user can perform `revert_on_current_merge_request` on this resource | + +### MergeRequestSetWipPayload + +| Name | Type | Description | +| --- | ---- | ---------- | +| `clientMutationId` | String | A unique identifier for the client performing the mutation. | +| `errors` | String! => Array | Reasons why the mutation failed. | +| `mergeRequest` | MergeRequest | The merge request after mutation | + +### Metadata + +| Name | Type | Description | +| --- | ---- | ---------- | +| `version` | String! | | +| `revision` | String! | | + +### Milestone + +| Name | Type | Description | +| --- | ---- | ---------- | +| `description` | String | | +| `title` | String! | | +| `state` | String! | | +| `dueDate` | Time | | +| `startDate` | Time | | +| `createdAt` | Time! | | +| `updatedAt` | Time! | | + +### Namespace + +| Name | Type | Description | +| --- | ---- | ---------- | +| `id` | ID! | | +| `name` | String! | | +| `path` | String! | | +| `fullName` | String! | | +| `fullPath` | ID! | | +| `description` | String | | +| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` | +| `visibility` | String | | +| `lfsEnabled` | Boolean | | +| `requestAccessEnabled` | Boolean | | + +### Note + +| Name | Type | Description | +| --- | ---- | ---------- | +| `userPermissions` | NotePermissions! | Permissions for the current user on the resource | +| `id` | ID! | | +| `project` | Project | The project this note is associated to | +| `author` | User! | The user who wrote this note | +| `resolvedBy` | User | The user that resolved the discussion | +| `system` | Boolean! | Whether or not this note was created by the system or by a user | +| `body` | String! | The content note itself | +| `bodyHtml` | String | The GitLab Flavored Markdown rendering of `note` | +| `createdAt` | Time! | | +| `updatedAt` | Time! | | +| `discussion` | Discussion | The discussion this note is a part of | +| `resolvable` | Boolean! | | +| `resolvedAt` | Time | The time the discussion was resolved | +| `position` | DiffPosition | The position of this note on a diff | + +### NotePermissions + +| Name | Type | Description | +| --- | ---- | ---------- | +| `readNote` | Boolean! | Whether or not a user can perform `read_note` on this resource | +| `createNote` | Boolean! | Whether or not a user can perform `create_note` on this resource | +| `adminNote` | Boolean! | Whether or not a user can perform `admin_note` on this resource | +| `resolveNote` | Boolean! | Whether or not a user can perform `resolve_note` on this resource | +| `awardEmoji` | Boolean! | Whether or not a user can perform `award_emoji` on this resource | + +### PageInfo + +| Name | Type | Description | +| --- | ---- | ---------- | +| `hasNextPage` | Boolean! | When paginating forwards, are there more items? | +| `hasPreviousPage` | Boolean! | When paginating backwards, are there more items? | +| `startCursor` | String | When paginating backwards, the cursor to continue. | +| `endCursor` | String | When paginating forwards, the cursor to continue. | + +### Pipeline + +| Name | Type | Description | +| --- | ---- | ---------- | +| `userPermissions` | PipelinePermissions! | Permissions for the current user on the resource | +| `id` | ID! | | +| `iid` | String! | | +| `sha` | String! | | +| `beforeSha` | String | | +| `status` | PipelineStatusEnum! | | +| `detailedStatus` | DetailedStatus! | | +| `duration` | Int | Duration of the pipeline in seconds | +| `coverage` | Float | Coverage percentage | +| `createdAt` | Time! | | +| `updatedAt` | Time! | | +| `startedAt` | Time | | +| `finishedAt` | Time | | +| `committedAt` | Time | | + +### PipelinePermissions + +| Name | Type | Description | +| --- | ---- | ---------- | +| `updatePipeline` | Boolean! | Whether or not a user can perform `update_pipeline` on this resource | +| `adminPipeline` | Boolean! | Whether or not a user can perform `admin_pipeline` on this resource | +| `destroyPipeline` | Boolean! | Whether or not a user can perform `destroy_pipeline` on this resource | + +### Project + +| Name | Type | Description | +| --- | ---- | ---------- | +| `userPermissions` | ProjectPermissions! | Permissions for the current user on the resource | +| `id` | ID! | | +| `fullPath` | ID! | | +| `path` | String! | | +| `nameWithNamespace` | String! | | +| `name` | String! | | +| `description` | String | | +| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` | +| `tagList` | String | | +| `sshUrlToRepo` | String | | +| `httpUrlToRepo` | String | | +| `webUrl` | String | | +| `starCount` | Int! | | +| `forksCount` | Int! | | +| `createdAt` | Time | | +| `lastActivityAt` | Time | | +| `archived` | Boolean | | +| `visibility` | String | | +| `containerRegistryEnabled` | Boolean | | +| `sharedRunnersEnabled` | Boolean | | +| `lfsEnabled` | Boolean | | +| `mergeRequestsFfOnlyEnabled` | Boolean | | +| `avatarUrl` | String | | +| `issuesEnabled` | Boolean | | +| `mergeRequestsEnabled` | Boolean | | +| `wikiEnabled` | Boolean | | +| `snippetsEnabled` | Boolean | | +| `jobsEnabled` | Boolean | | +| `publicJobs` | Boolean | | +| `openIssuesCount` | Int | | +| `importStatus` | String | | +| `onlyAllowMergeIfPipelineSucceeds` | Boolean | | +| `requestAccessEnabled` | Boolean | | +| `onlyAllowMergeIfAllDiscussionsAreResolved` | Boolean | | +| `printingMergeRequestLinkEnabled` | Boolean | | +| `namespace` | Namespace | | +| `group` | Group | | +| `statistics` | ProjectStatistics | | +| `repository` | Repository | | +| `mergeRequest` | MergeRequest | | +| `issue` | Issue | | + +### ProjectPermissions + +| Name | Type | Description | +| --- | ---- | ---------- | +| `changeNamespace` | Boolean! | Whether or not a user can perform `change_namespace` on this resource | +| `changeVisibilityLevel` | Boolean! | Whether or not a user can perform `change_visibility_level` on this resource | +| `renameProject` | Boolean! | Whether or not a user can perform `rename_project` on this resource | +| `removeProject` | Boolean! | Whether or not a user can perform `remove_project` on this resource | +| `archiveProject` | Boolean! | Whether or not a user can perform `archive_project` on this resource | +| `removeForkProject` | Boolean! | Whether or not a user can perform `remove_fork_project` on this resource | +| `removePages` | Boolean! | Whether or not a user can perform `remove_pages` on this resource | +| `readProject` | Boolean! | Whether or not a user can perform `read_project` on this resource | +| `createMergeRequestIn` | Boolean! | Whether or not a user can perform `create_merge_request_in` on this resource | +| `readWiki` | Boolean! | Whether or not a user can perform `read_wiki` on this resource | +| `readProjectMember` | Boolean! | Whether or not a user can perform `read_project_member` on this resource | +| `createIssue` | Boolean! | Whether or not a user can perform `create_issue` on this resource | +| `uploadFile` | Boolean! | Whether or not a user can perform `upload_file` on this resource | +| `readCycleAnalytics` | Boolean! | Whether or not a user can perform `read_cycle_analytics` on this resource | +| `downloadCode` | Boolean! | Whether or not a user can perform `download_code` on this resource | +| `downloadWikiCode` | Boolean! | Whether or not a user can perform `download_wiki_code` on this resource | +| `forkProject` | Boolean! | Whether or not a user can perform `fork_project` on this resource | +| `createProjectSnippet` | Boolean! | Whether or not a user can perform `create_project_snippet` on this resource | +| `readCommitStatus` | Boolean! | Whether or not a user can perform `read_commit_status` on this resource | +| `requestAccess` | Boolean! | Whether or not a user can perform `request_access` on this resource | +| `createPipeline` | Boolean! | Whether or not a user can perform `create_pipeline` on this resource | +| `createPipelineSchedule` | Boolean! | Whether or not a user can perform `create_pipeline_schedule` on this resource | +| `createMergeRequestFrom` | Boolean! | Whether or not a user can perform `create_merge_request_from` on this resource | +| `createWiki` | Boolean! | Whether or not a user can perform `create_wiki` on this resource | +| `pushCode` | Boolean! | Whether or not a user can perform `push_code` on this resource | +| `createDeployment` | Boolean! | Whether or not a user can perform `create_deployment` on this resource | +| `pushToDeleteProtectedBranch` | Boolean! | Whether or not a user can perform `push_to_delete_protected_branch` on this resource | +| `adminWiki` | Boolean! | Whether or not a user can perform `admin_wiki` on this resource | +| `adminProject` | Boolean! | Whether or not a user can perform `admin_project` on this resource | +| `updatePages` | Boolean! | Whether or not a user can perform `update_pages` on this resource | +| `adminRemoteMirror` | Boolean! | Whether or not a user can perform `admin_remote_mirror` on this resource | +| `createLabel` | Boolean! | Whether or not a user can perform `create_label` on this resource | +| `updateWiki` | Boolean! | Whether or not a user can perform `update_wiki` on this resource | +| `destroyWiki` | Boolean! | Whether or not a user can perform `destroy_wiki` on this resource | +| `createPages` | Boolean! | Whether or not a user can perform `create_pages` on this resource | +| `destroyPages` | Boolean! | Whether or not a user can perform `destroy_pages` on this resource | +| `readPagesContent` | Boolean! | Whether or not a user can perform `read_pages_content` on this resource | + +### ProjectStatistics + +| Name | Type | Description | +| --- | ---- | ---------- | +| `commitCount` | Int! | | +| `storageSize` | Int! | | +| `repositorySize` | Int! | | +| `lfsObjectsSize` | Int! | | +| `buildArtifactsSize` | Int! | | +| `packagesSize` | Int! | | +| `wikiSize` | Int | | + +### RemoveAwardEmojiPayload + +| Name | Type | Description | +| --- | ---- | ---------- | +| `clientMutationId` | String | A unique identifier for the client performing the mutation. | +| `errors` | String! => Array | Reasons why the mutation failed. | +| `awardEmoji` | AwardEmoji | The award emoji after mutation | + +### Repository + +| Name | Type | Description | +| --- | ---- | ---------- | +| `rootRef` | String | | +| `empty` | Boolean! | | +| `exists` | Boolean! | | +| `tree` | Tree | | + +### Submodule + +| Name | Type | Description | +| --- | ---- | ---------- | +| `id` | ID! | | +| `name` | String! | | +| `type` | EntryType! | | +| `path` | String! | | +| `flatPath` | String! | | + +### TaskCompletionStatus + +| Name | Type | Description | +| --- | ---- | ---------- | +| `count` | Int! | | +| `completedCount` | Int! | | + +### ToggleAwardEmojiPayload + +| Name | Type | Description | +| --- | ---- | ---------- | +| `clientMutationId` | String | A unique identifier for the client performing the mutation. | +| `errors` | String! => Array | Reasons why the mutation failed. | +| `awardEmoji` | AwardEmoji | The award emoji after mutation | +| `toggledOn` | Boolean! | True when the emoji was awarded, false when it was removed | + +### Tree + +| Name | Type | Description | +| --- | ---- | ---------- | +| `lastCommit` | Commit | | + +### TreeEntry + +| Name | Type | Description | +| --- | ---- | ---------- | +| `id` | ID! | | +| `name` | String! | | +| `type` | EntryType! | | +| `path` | String! | | +| `flatPath` | String! | | +| `webUrl` | String | | + +### User + +| Name | Type | Description | +| --- | ---- | ---------- | +| `name` | String! | | +| `username` | String! | | +| `avatarUrl` | String! | | +| `webUrl` | String! | | + diff --git a/doc/api/group_boards.md b/doc/api/group_boards.md index 4157b25477f..4d10f83720b 100644 --- a/doc/api/group_boards.md +++ b/doc/api/group_boards.md @@ -236,7 +236,7 @@ Example response: } ``` -## Create a group issue board **[PREMIUM]** +## Create a group issue board **(PREMIUM)** Creates a Group Issue Board. @@ -300,7 +300,7 @@ Example response: } ``` -## Update a group issue board **[PREMIUM]** +## Update a group issue board **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/5954) in GitLab 11.1. @@ -368,7 +368,7 @@ Example response: } ``` -## Delete a group issue board **[PREMIUM]** +## Delete a group issue board **(PREMIUM)** Deletes a Group Issue Board. diff --git a/doc/api/group_clusters.md b/doc/api/group_clusters.md new file mode 100644 index 00000000000..71a05b4d338 --- /dev/null +++ b/doc/api/group_clusters.md @@ -0,0 +1,280 @@ +# Group clusters API + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/30213) +in GitLab 12.1. + +NOTE: **Note:** +User will need at least maintainer access for the group to use these endpoints. + +## List group clusters + +Returns a list of group clusters. + +``` +GET /groups/:id/clusters +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | + +Example request: + +```bash +curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters +``` + +Example response: + +```json +[ + { + "id":18, + "name":"cluster-1", + "domain":"example.com", + "created_at":"2019-01-02T20:18:12.563Z", + "provider_type":"user", + "platform_type":"kubernetes", + "environment_scope":"*", + "cluster_type":"group_type", + "user": + { + "id":1, + "name":"Administrator", + "username":"root", + "state":"active", + "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", + "web_url":"https://gitlab.example.com/root" + }, + "platform_kubernetes": + { + "api_url":"https://104.197.68.152", + "authorization_type":"rbac", + "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" + } + }, + { + "id":19, + "name":"cluster-2", + ... + } +] +``` + +## Get a single group cluster + +Gets a single group cluster. + +``` +GET /groups/:id/clusters/:cluster_id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `cluster_id` | integer | yes | The ID of the cluster | + +Example request: + +```bash +curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters/18 +``` + +Example response: + +```json +{ + "id":18, + "name":"cluster-1", + "domain":"example.com", + "created_at":"2019-01-02T20:18:12.563Z", + "provider_type":"user", + "platform_type":"kubernetes", + "environment_scope":"*", + "cluster_type":"group_type", + "user": + { + "id":1, + "name":"Administrator", + "username":"root", + "state":"active", + "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", + "web_url":"https://gitlab.example.com/root" + }, + "platform_kubernetes": + { + "api_url":"https://104.197.68.152", + "authorization_type":"rbac", + "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" + }, + "group": + { + "id":26, + "name":"group-with-clusters-api", + "web_url":"https://gitlab.example.com/group-with-clusters-api" + } +} +``` + +## Add existing cluster to group + +Adds an existing Kubernetes cluster to the group. + +``` +POST /groups/:id/clusters/user +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `name` | String | yes | The name of the cluster | +| `domain` | String | no | The [base domain](../user/group/clusters/index.md#base-domain) of the cluster | +| `enabled` | Boolean | no | Determines if cluster is active or not, defaults to true | +| `managed` | Boolean | no | Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true | +| `platform_kubernetes_attributes[api_url]` | String | yes | The URL to access the Kubernetes API | +| `platform_kubernetes_attributes[token]` | String | yes | The token to authenticate against Kubernetes | +| `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate | +| `platform_kubernetes_attributes[authorization_type]` | String | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. | +| `environment_scope` | String | no | The associated environment to the cluster. Defaults to `*` **[PREMIUM]** | + +Example request: + +```bash +curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters/user \ +-H "Accept: application/json" \ +-H "Content-Type:application/json" \ +--request POST --data '{"name":"cluster-5", "platform_kubernetes_attributes":{"api_url":"https://35.111.51.20","token":"12345","ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"}}' +``` + +Example response: + +```json +{ + "id":24, + "name":"cluster-5", + "created_at":"2019-01-03T21:53:40.610Z", + "provider_type":"user", + "platform_type":"kubernetes", + "environment_scope":"*", + "cluster_type":"group_type", + "user": + { + "id":1, + "name":"Administrator", + "username":"root", + "state":"active", + "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", + "web_url":"https://gitlab.example.com/root" + }, + "platform_kubernetes": + { + "api_url":"https://35.111.51.20", + "authorization_type":"rbac", + "ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----" + }, + "group": + { + "id":26, + "name":"group-with-clusters-api", + "web_url":"https://gitlab.example.com/root/group-with-clusters-api" + } +} +``` + +## Edit group cluster + +Updates an existing group cluster. + +``` +PUT /groups/:id/clusters/:cluster_id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `cluster_id` | integer | yes | The ID of the cluster | +| `name` | String | no | The name of the cluster | +| `domain` | String | no | The [base domain](../user/group/clusters/index.md#base-domain) of the cluster | +| `platform_kubernetes_attributes[api_url]` | String | no | The URL to access the Kubernetes API | +| `platform_kubernetes_attributes[token]` | String | no | The token to authenticate against Kubernetes | +| `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate | +| `environment_scope` | String | no | The associated environment to the cluster **[PREMIUM]** | + +NOTE: **Note:** +`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added +through the ["Add an existing Kubernetes Cluster"](../user/project/clusters/index.md#adding-an-existing-kubernetes-cluster) option or +through the ["Add existing cluster to group"](#add-existing-cluster-to-group) endpoint. + +Example request: + +```bash +curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters/24 \ +-H "Content-Type:application/json" \ +--request PUT --data '{"name":"new-cluster-name","domain":"new-domain.com","api_url":"https://new-api-url.com"}' +``` + +Example response: + +```json +{ + "id":24, + "name":"new-cluster-name", + "domain":"new-domain.com", + "created_at":"2019-01-03T21:53:40.610Z", + "provider_type":"user", + "platform_type":"kubernetes", + "environment_scope":"*", + "cluster_type":"group_type", + "user": + { + "id":1, + "name":"Administrator", + "username":"root", + "state":"active", + "avatar_url":"https://www.gravatar.com/avatar/4249f4df72b..", + "web_url":"https://gitlab.example.com/root" + }, + "platform_kubernetes": + { + "api_url":"https://new-api-url.com", + "authorization_type":"rbac", + "ca_cert":null + }, + "group": + { + "id":26, + "name":"group-with-clusters-api", + "web_url":"https://gitlab.example.com/group-with-clusters-api" + } +} + +``` + +## Delete group cluster + +Deletes an existing group cluster. + +``` +DELETE /groups/:id/clusters/:cluster_id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) | +| `cluster_id` | integer | yes | The ID of the cluster | + +Example request: + +```bash +curl --request DELETE --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/groups/26/clusters/23' +``` diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md index 98e81217875..a819e06bcd9 100644 --- a/doc/api/group_milestones.md +++ b/doc/api/group_milestones.md @@ -137,7 +137,7 @@ Parameters: [ce-12819]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12819 -## Get all burndown chart events for a single milestone **[STARTER]** +## Get all burndown chart events for a single milestone **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4737) in GitLab 12.1 diff --git a/doc/api/groups.md b/doc/api/groups.md index e1bf296bc41..0d500f783aa 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -434,8 +434,8 @@ Parameters: | `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. | | `request_access_enabled` | boolean | no | Allow users to request member access. | | `parent_id` | integer | no | The parent group ID for creating nested group. | -| `shared_runners_minutes_limit` | integer | no | **[STARTER ONLY]** Pipeline minutes quota for this group. | -| `extra_shared_runners_minutes_limit` | integer | no | **[STARTER ONLY]** Extra pipeline minutes quota for this group. | +| `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group. | +| `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group. | ## Transfer project to group @@ -466,14 +466,14 @@ PUT /groups/:id | `name` | string | no | The name of the group. | | `path` | string | no | The path of the group. | | `description` | string | no | The description of the group. | -| `membership_lock` | boolean | no | **[STARTER]** Prevent adding new members to project membership within this group. | +| `membership_lock` | boolean | no | **(STARTER)** Prevent adding new members to project membership within this group. | | `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. | | `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. | | `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. | | `request_access_enabled` | boolean | no | Allow users to request member access. | -| `file_template_project_id` | integer | no | **[PREMIUM]** The ID of a project to load custom file templates from. | -| `shared_runners_minutes_limit` | integer | no | **[STARTER ONLY]** Pipeline minutes quota for this group. | -| `extra_shared_runners_minutes_limit` | integer | no | **[STARTER ONLY]** Extra pipeline minutes quota for this group. | +| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from. | +| `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group. | +| `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group. | ```bash curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5?name=Experimental" @@ -573,7 +573,7 @@ GET /groups?search=foobar ] ``` -## Sync group with LDAP **[CORE ONLY]** +## Sync group with LDAP **(CORE ONLY)** Syncs the group with its linked LDAP group. Only available to group owners and administrators. @@ -589,7 +589,7 @@ Parameters: Please consult the [Group Members](members.md) documentation. -### Add LDAP group link **[CORE ONLY]** +### Add LDAP group link **(CORE ONLY)** Adds an LDAP group link. @@ -604,7 +604,7 @@ Parameters: - `group_access` (required) - Minimum access level for members of the LDAP group - `provider` (required) - LDAP provider for the LDAP group -### Delete LDAP group link **[CORE ONLY]** +### Delete LDAP group link **(CORE ONLY)** Deletes an LDAP group link. @@ -634,11 +634,13 @@ Parameters: By default, groups only get 20 namespaces at a time because the API results are paginated. To get more (up to 100), pass the following as an argument to the API call: + ``` /groups?per_page=100 ``` And to switch pages add: + ``` /groups?per_page=100&page=2 ``` diff --git a/doc/api/issue_links.md b/doc/api/issue_links.md index 1c7db6a8e4c..280431fa87c 100644 --- a/doc/api/issue_links.md +++ b/doc/api/issue_links.md @@ -1,4 +1,4 @@ -# Issue links API **[STARTER]** +# Issue links API **(STARTER)** ## List issue relations @@ -67,7 +67,6 @@ POST /projects/:id/issues/:issue_iid/links | `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project | | `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue | - ```json { "source_issue" : { @@ -141,14 +140,12 @@ Deletes an issue link, thus removes the two-way relationship. DELETE /projects/:id/issues/:issue_iid/links/:issue_link_id ``` - | Attribute | Type | Required | Description | |-------------|---------|----------|--------------------------------------| | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | | `issue_iid` | integer | yes | The internal ID of a project's issue | | `issue_link_id` | integer/string | yes | The ID of an issue relationship | - ```json { "source_issue" : { diff --git a/doc/api/issues.md b/doc/api/issues.md index 544da1e262c..23126a05b66 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -47,7 +47,7 @@ GET /issues?confidential=true | `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ | | `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. | | `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ | -| `weight` **[STARTER]** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. | +| `weight` **(STARTER)** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. | | `iids[]` | integer array | no | Return only the issues having the given `iid` | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | @@ -57,7 +57,7 @@ GET /issues?confidential=true | `created_before` | datetime | no | Return issues created on or before the given time | | `updated_after` | datetime | no | Return issues updated on or after the given time | | `updated_before` | datetime | no | Return issues updated on or before the given time | -| `confidential ` | Boolean | no | Filter confidential or public issues. | +| `confidential` | Boolean | no | Filter confidential or public issues. | ```bash curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/issues @@ -198,7 +198,7 @@ GET /groups/:id/issues?confidential=true | `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ | | `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. | | `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ | -| `weight` **[STARTER]** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. | +| `weight` **(STARTER)** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Search group issues against their `title` and `description` | @@ -206,7 +206,7 @@ GET /groups/:id/issues?confidential=true | `created_before` | datetime | no | Return issues created on or before the given time | | `updated_after` | datetime | no | Return issues updated on or after the given time | | `updated_before` | datetime | no | Return issues updated on or before the given time | -| `confidential ` | Boolean | no | Filter confidential or public issues. | +| `confidential` | Boolean | no | Filter confidential or public issues. | ```bash curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/4/issues @@ -347,7 +347,7 @@ GET /projects/:id/issues?confidential=true | `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ | | `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. | | `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ | -| `weight` **[STARTER]** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. | +| `weight` **(STARTER)** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Search project issues against their `title` and `description` | @@ -355,7 +355,7 @@ GET /projects/:id/issues?confidential=true | `created_before` | datetime | no | Return issues created on or before the given time | | `updated_after` | datetime | no | Return issues updated on or after the given time | | `updated_before` | datetime | no | Return issues updated on or before the given time | -| `confidential ` | Boolean | no | Filter confidential or public issues. | +| `confidential` | Boolean | no | Filter confidential or public issues. | ```bash @@ -603,7 +603,7 @@ POST /projects/:id/issues | `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` | | `merge_request_to_resolve_discussions_of` | integer | no | The IID of a merge request in which to resolve all issues. This will fill the issue with a default description and mark all discussions as resolved. When passing a description or title, these values will take precedence over the default values.| | `discussion_to_resolve` | string | no | The ID of a discussion to resolve. This will fill in the issue with a default description and mark the discussion as resolved. Use in combination with `merge_request_to_resolve_discussions_of`. | -| `weight` **[STARTER]** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. | +| `weight` **(STARTER)** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. | ```bash curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues?title=Issues%20with%20auth&labels=bug @@ -703,7 +703,7 @@ PUT /projects/:id/issues/:issue_iid | `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it | | `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) | | `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` | -| `weight` **[STARTER]** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. 0 | +| `weight` **(STARTER)** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. 0 | | `discussion_locked` | boolean | no | Flag indicating if the issue's discussion is locked. If the discussion is locked only project members can add or edit comments. | ```bash diff --git a/doc/api/issues_statistics.md b/doc/api/issues_statistics.md index 58a32e879d7..d7edb296be2 100644 --- a/doc/api/issues_statistics.md +++ b/doc/api/issues_statistics.md @@ -43,7 +43,7 @@ GET /issues_statistics?confidential=true | `created_before` | datetime | no | Return issues created on or before the given time | | `updated_after` | datetime | no | Return issues updated on or after the given time | | `updated_before` | datetime | no | Return issues updated on or before the given time | -| `confidential ` | Boolean | no | Filter confidential or public issues. | +| `confidential` | Boolean | no | Filter confidential or public issues. | ```bash curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/issues_statistics @@ -99,7 +99,7 @@ GET /groups/:id/issues_statistics?confidential=true | `created_before` | datetime | no | Return issues created on or before the given time | | `updated_after` | datetime | no | Return issues updated on or after the given time | | `updated_before` | datetime | no | Return issues updated on or before the given time | -| `confidential ` | Boolean | no | Filter confidential or public issues. | +| `confidential` | Boolean | no | Filter confidential or public issues. | ```bash curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/4/issues_statistics @@ -155,8 +155,7 @@ GET /projects/:id/issues_statistics?confidential=true | `created_before` | datetime | no | Return issues created on or before the given time | | `updated_after` | datetime | no | Return issues updated on or after the given time | | `updated_before` | datetime | no | Return issues updated on or before the given time | -| `confidential ` | Boolean | no | Filter confidential or public issues. | - +| `confidential` | Boolean | no | Filter confidential or public issues. | ```bash curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues_statistics diff --git a/doc/api/jobs.md b/doc/api/jobs.md index 223bfed91a9..1add5f432ac 100644 --- a/doc/api/jobs.md +++ b/doc/api/jobs.md @@ -359,7 +359,7 @@ GET /projects/:id/jobs/:job_id/artifacts |-------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------| | `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. | | `job_id` | integer | yes | ID of a job. | -| `job_token` **[PREMIUM]** | string | no | To be used with [triggers] for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | +| `job_token` **(PREMIUM)** | string | no | To be used with [triggers] for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | Example request using the `PRIVATE-TOKEN` header: @@ -368,7 +368,7 @@ curl --output artifacts.zip --header "PRIVATE-TOKEN: <your_access_token>" "https ``` To use this in a [`script` definition](../ci/yaml/README.md#script) inside -`.gitlab-ci.yml` **[PREMIUM]**, you can use either: +`.gitlab-ci.yml` **(PREMIUM)**, you can use either: - The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable. For example, the following job will download the artifacts of the job with ID @@ -425,7 +425,7 @@ Parameters | `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. | | `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. | | `job` | string | yes | The name of the job. | -| `job_token` **[PREMIUM]** | string | no | To be used with [triggers] for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | +| `job_token` **(PREMIUM)** | string | no | To be used with [triggers] for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. | Example request using the `PRIVATE-TOKEN` header: @@ -434,7 +434,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a ``` To use this in a [`script` definition](../ci/yaml/README.md#script) inside -`.gitlab-ci.yml` **[PREMIUM]**, you can use either: +`.gitlab-ci.yml` **(PREMIUM)**, you can use either: - The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable. For example, the following job will download the artifacts of the `test` job @@ -485,7 +485,7 @@ Parameters | Attribute | Type | Required | Description | |-----------------|----------------|----------|------------------------------------------------------------------------------------------------------------------| | `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. | -| `job_id ` | integer | yes | The unique job identifier. | +| `job_id` | integer | yes | The unique job identifier. | | `artifact_path` | string | yes | Path to a file inside the artifacts archive. | Example request: @@ -782,7 +782,6 @@ DELETE /projects/:id/jobs/:job_id/artifacts | `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `job_id` | integer | yes | ID of a job. | - Example request: ```sh diff --git a/doc/api/license.md b/doc/api/license.md index 2a8de64bdbf..12f1d03d576 100644 --- a/doc/api/license.md +++ b/doc/api/license.md @@ -1,4 +1,4 @@ -# License **[CORE ONLY]** +# License **(CORE ONLY)** In order to interact with license endpoints, you need to authenticate yourself as an admin. @@ -131,7 +131,6 @@ Returns: - `201 Created` if the license is successfully added. - `400 Bad Request` if the license couldn't be added, with an error message explaining the reason. - ## Delete a license ``` diff --git a/doc/api/lint.md b/doc/api/lint.md index 71c09d35b8c..b9b49f3df27 100644 --- a/doc/api/lint.md +++ b/doc/api/lint.md @@ -22,30 +22,30 @@ Example responses: - Valid content: - ```json - { - "status": "valid", - "errors": [] - } - ``` + ```json + { + "status": "valid", + "errors": [] + } + ``` - Invalid content: - ```json - { - "status": "invalid", - "errors": [ - "variables config should be a hash of key value pairs" - ] - } - ``` + ```json + { + "status": "invalid", + "errors": [ + "variables config should be a hash of key value pairs" + ] + } + ``` - Without the content attribute: - ```json - { - "error": "content is missing" - } - ``` + ```json + { + "error": "content is missing" + } + ``` [ce-5953]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5953 diff --git a/doc/api/managed_licenses.md b/doc/api/managed_licenses.md index 47b193111b6..1af7567626f 100644 --- a/doc/api/managed_licenses.md +++ b/doc/api/managed_licenses.md @@ -1,4 +1,4 @@ -# Managed Licenses API **[ULTIMATE]** +# Managed Licenses API **(ULTIMATE)** ## List managed licenses @@ -105,7 +105,7 @@ DELETE /projects/:id/managed_licenses/:managed_license_id curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/managed_licenses/4" ``` -When successful, it replies with an HTTP 204 response. +When successful, it replies with an HTTP 204 response. ## Edit an existing managed license diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md index 49aaac06b46..c211916464a 100644 --- a/doc/api/merge_request_approvals.md +++ b/doc/api/merge_request_approvals.md @@ -1,4 +1,4 @@ -# Merge request approvals API **[STARTER]** +# Merge request approvals API **(STARTER)** Configuration for approvals on all Merge Requests (MR) in the project. Must be authenticated for all endpoints. @@ -178,7 +178,6 @@ PUT /projects/:id/approvers } ``` - ## Merge Request-level MR approvals Configuration for approvals on a specific Merge Request. Must be authenticated for all endpoints. @@ -250,7 +249,6 @@ POST /projects/:id/merge_requests/:merge_request_iid/approvals | `merge_request_iid` | integer | yes | The IID of MR | | `approvals_required` | integer | yes | Approvals required before MR can be merged | - ```json { "id": 5, @@ -359,7 +357,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/approve | `id` | integer | yes | The ID of a project | | `merge_request_iid` | integer | yes | The IID of MR | | `sha` | string | no | The HEAD of the MR | -| `approval_password` **[STARTER]** | string | no | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/merge_request_approvals.md#require-authentication-when-approving-a-merge-request-starter) is enabled in the project settings. | +| `approval_password` **(STARTER)** | string | no | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/merge_request_approvals.md#require-authentication-when-approving-a-merge-request-starter) is enabled in the project settings. | The `sha` parameter works in the same way as when [accepting a merge request](merge_requests.md#accept-mr): if it is passed, then it must diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 850e3be42d5..662a4b3e424 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -44,7 +44,7 @@ Parameters: | `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead. | | `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me` | | `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. | -| `approver_ids` **[STARTER]** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. | +| `approver_ids` **(STARTER)** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. | | `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ | | `source_branch` | string | no | Return merge requests with the given source branch | | `target_branch` | string | no | Return merge requests with the given target branch | @@ -206,7 +206,7 @@ Parameters: | `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13060] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ | | `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ | | `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ | -| `approver_ids` **[STARTER]** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. | +| `approver_ids` **(STARTER)** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. | | `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ | | `source_branch` | string | no | Return merge requests with the given source branch | | `target_branch` | string | no | Return merge requests with the given target branch | @@ -358,7 +358,7 @@ Parameters: | `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> | | `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ | | `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ | -| `approver_ids` **[STARTER]** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. | +| `approver_ids` **(STARTER)** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. | | `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ | | `source_branch` | string | no | Return merge requests with the given source branch | | `target_branch` | string | no | Return merge requests with the given target branch | @@ -846,7 +846,7 @@ POST /projects/:id/merge_requests | `allow_maintainer_to_push` | boolean | no | Deprecated, see allow_collaboration | | `squash` | boolean | no | Squash commits into a single commit when merging | -If `approvals_before_merge` **[STARTER]** is not provided, it inherits the value from the +If `approvals_before_merge` **(STARTER)** is not provided, it inherits the value from the target project. If it is provided, then the following conditions must hold in order for it to take effect: @@ -1331,9 +1331,11 @@ If you don't have permissions to accept this merge request - you'll get a `401` If the merge request is already merged or closed - you get `405` and error message 'Method Not Allowed' In case the merge request is not set to be merged when the pipeline succeeds, you'll also get a `406` error. + ``` PUT /projects/:id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds ``` + Parameters: - `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user @@ -2261,6 +2263,6 @@ Example response: [ce-15454]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15454 [ce-18935]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18935 -## Approvals **[STARTER]** +## Approvals **(STARTER)** For approvals, please see [Merge Request Approvals](merge_request_approvals.md) diff --git a/doc/api/milestones.md b/doc/api/milestones.md index 8f69378dac3..a6ded7d3bd2 100644 --- a/doc/api/milestones.md +++ b/doc/api/milestones.md @@ -148,7 +148,7 @@ Parameters: - `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user - `milestone_id` (required) - The ID of a project milestone -## Get all burndown chart events for a single milestone **[STARTER]** +## Get all burndown chart events for a single milestone **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4737) in GitLab 12.1 diff --git a/doc/api/namespaces.md b/doc/api/namespaces.md index 5db7035fd90..2b6eddf78a1 100644 --- a/doc/api/namespaces.md +++ b/doc/api/namespaces.md @@ -68,7 +68,7 @@ the `plan` parameter associated with a namespace: ] ``` -NOTE: **Note:** Only group maintainers/owners are presented with `members_count_with_descendants`, as well as `plan` **[BRONZE ONLY]**. +NOTE: **Note:** Only group maintainers/owners are presented with `members_count_with_descendants`, as well as `plan` **(BRONZE ONLY)**. ## Search for namespace diff --git a/doc/api/notes.md b/doc/api/notes.md index c09129c22d4..acbf0334563 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -5,7 +5,7 @@ Notes are comments on: - Snippets - Issues - Merge requests -- Epics **[ULTIMATE]** +- Epics **(ULTIMATE)** This includes system notes, which are notes about changes to the object (for example, when a milestone changes, there will be a corresponding system note). Label notes are not part of this API, but recorded as separate events in [resource label events](resource_label_events.md). @@ -396,7 +396,7 @@ Parameters: curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/merge_requests/7/notes/1602 ``` -## Epics **[ULTIMATE]** +## Epics **(ULTIMATE)** ### List all epic notes diff --git a/doc/api/notification_settings.md b/doc/api/notification_settings.md index ccc1cccf7a4..c6667784617 100644 --- a/doc/api/notification_settings.md +++ b/doc/api/notification_settings.md @@ -31,7 +31,7 @@ If the `custom` level is used, specific email events can be controlled. Availabl - `merge_merge_request` - `failed_pipeline` - `success_pipeline` -- `new_epic` **[ULTIMATE]** +- `new_epic` **(ULTIMATE)** ## Global notification settings @@ -84,7 +84,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab. | `merge_merge_request` | boolean | no | Enable/disable this notification | | `failed_pipeline` | boolean | no | Enable/disable this notification | | `success_pipeline` | boolean | no | Enable/disable this notification | -| `new_epic` | boolean | no | Enable/disable this notification ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6626) in 11.3) **[ULTIMATE]** | +| `new_epic` | boolean | no | Enable/disable this notification ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6626) in 11.3) **(ULTIMATE)** | Example response: @@ -153,7 +153,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab. | `merge_merge_request` | boolean | no | Enable/disable this notification | | `failed_pipeline` | boolean | no | Enable/disable this notification | | `success_pipeline` | boolean | no | Enable/disable this notification | -| `new_epic` | boolean | no | Enable/disable this notification ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6626) in 11.3) **[ULTIMATE]** | +| `new_epic` | boolean | no | Enable/disable this notification ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6626) in 11.3) **(ULTIMATE)** | Example responses: diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md index dfe62554852..76e3a0fa1a4 100644 --- a/doc/api/oauth2.md +++ b/doc/api/oauth2.md @@ -192,7 +192,7 @@ access_token = client.password.get_token('user@example.com', 'secret') puts access_token.token ``` -## Access GitLab API with `access token` +## Access GitLab API with `access token` The `access token` allows you to make requests to the API on behalf of a user. You can pass the token either as GET parameter: diff --git a/doc/api/packages.md b/doc/api/packages.md index 618e5c3056a..ca90771b085 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -1,4 +1,4 @@ -# Packages API **[PREMIUM]** +# Packages API **(PREMIUM)** This is the API docs of [GitLab Packages](../administration/packages.md). @@ -76,7 +76,7 @@ Example response: > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9305) in GitLab 11.8. -Get a list of package files of a single package. +Get a list of package files of a single package. ``` GET /projects/:id/packages/:package_id/package_files diff --git a/doc/api/pages_domains.md b/doc/api/pages_domains.md index 70fbe24099f..9678203eb40 100644 --- a/doc/api/pages_domains.md +++ b/doc/api/pages_domains.md @@ -1,6 +1,6 @@ # Pages domains API -Endpoints for connecting custom domain(s) and TLS certificates in [GitLab Pages](https://about.gitlab.com/features/pages/). +Endpoints for connecting custom domain(s) and TLS certificates in [GitLab Pages](https://about.gitlab.com/product/pages/). The GitLab Pages feature must be enabled to use these endpoints. Find out more about [administering](../administration/pages/index.md) and [using](../user/project/pages/index.md) the feature. diff --git a/doc/api/project_aliases.md b/doc/api/project_aliases.md index 76343b4cd82..271632b61c3 100644 --- a/doc/api/project_aliases.md +++ b/doc/api/project_aliases.md @@ -1,4 +1,4 @@ -# Project Aliases API **[PREMIUM ONLY]** +# Project Aliases API **(PREMIUM ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3264) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.1. diff --git a/doc/api/project_badges.md b/doc/api/project_badges.md index 3a7b3d8975e..1c382232837 100644 --- a/doc/api/project_badges.md +++ b/doc/api/project_badges.md @@ -91,7 +91,7 @@ POST /projects/:id/badges | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer/string | yes | The ID or [URL-encoded path of the project ](README.md#namespaced-path-encoding) owned by the authenticated user | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | | `link_url` | string | yes | URL of the badge link | | `image_url` | string | yes | URL of the badge image | diff --git a/doc/api/project_clusters.md b/doc/api/project_clusters.md index 327781f6c93..614ea41d572 100644 --- a/doc/api/project_clusters.md +++ b/doc/api/project_clusters.md @@ -167,7 +167,7 @@ Parameters: | `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate | | `platform_kubernetes_attributes[namespace]` | String | no | The unique namespace related to the project | | `platform_kubernetes_attributes[authorization_type]` | String | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. | -| `environment_scope` | String | no | The associated environment to the cluster. Defaults to `*` **[PREMIUM]** | +| `environment_scope` | String | no | The associated environment to the cluster. Defaults to `*` **(PREMIUM)** | Example request: @@ -257,7 +257,7 @@ Parameters: | `platform_kubernetes_attributes[token]` | String | no | The token to authenticate against Kubernetes | | `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate | | `platform_kubernetes_attributes[namespace]` | String | no | The unique namespace related to the project | -| `environment_scope` | String | no | The associated environment to the cluster **[PREMIUM]** | +| `environment_scope` | String | no | The associated environment to the cluster **(PREMIUM)** | NOTE: **Note:** `name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md index 66a749e4811..eab905bbc5f 100644 --- a/doc/api/project_level_variables.md +++ b/doc/api/project_level_variables.md @@ -74,7 +74,7 @@ POST /projects/:id/variables | `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` | | `protected` | boolean | no | Whether the variable is protected | | `masked` | boolean | no | Whether the variable is masked | -| `environment_scope` | string | no | The `environment_scope` of the variable **[PREMIUM]** | +| `environment_scope` | string | no | The `environment_scope` of the variable **(PREMIUM)** | ``` curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables" --form "key=NEW_VARIABLE" --form "value=new value" @@ -108,7 +108,7 @@ PUT /projects/:id/variables/:key | `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` | | `protected` | boolean | no | Whether the variable is protected | | `masked` | boolean | no | Whether the variable is masked | -| `environment_scope` | string | no | The `environment_scope` of the variable **[PREMIUM]** | +| `environment_scope` | string | no | The `environment_scope` of the variable **(PREMIUM)** | ``` curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables/NEW_VARIABLE" --form "value=updated value" diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md index 2b2e40fb276..c1588f2292a 100644 --- a/doc/api/project_snippets.md +++ b/doc/api/project_snippets.md @@ -1,6 +1,6 @@ # Project snippets -### Snippet visibility level +## Snippet visibility level Snippets in GitLab can be either private, internal or public. You can set it with the `visibility` field in the snippet. @@ -14,7 +14,7 @@ Constants for snippet visibility levels are: | `public` | The snippet can be accessed without any authentication | NOTE: **Note:** -From July 2019, the `Internal` visibility setting is disabled for new projects, groups, +From July 2019, the `Internal` visibility setting is disabled for new projects, groups, and snippets on GitLab.com. Existing projects, groups, and snippets using the `Internal` visibility setting keep this setting. You can read more about the change in the [relevant issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/12388). diff --git a/doc/api/project_statistics.md b/doc/api/project_statistics.md index 34d73abfcbf..2732fa47fa0 100644 --- a/doc/api/project_statistics.md +++ b/doc/api/project_statistics.md @@ -14,7 +14,7 @@ GET /projects/:id/statistics | Attribute | Type | Required | Description | | ---------- | ------ | -------- | ----------- | -| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | +| `id` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | Example response: diff --git a/doc/api/project_templates.md b/doc/api/project_templates.md index 0a94a8d47ae..cc932e3ef58 100644 --- a/doc/api/project_templates.md +++ b/doc/api/project_templates.md @@ -16,7 +16,7 @@ Support will be added for [Issue and Merge Request templates](../user/project/de in a future release. Support for [Group-level file templates](../user/group/index.md#group-file-templates-premium) -**[PREMIUM]** was [added](https://gitlab.com/gitlab-org/gitlab-ee/issues/5987) +**(PREMIUM)** was [added](https://gitlab.com/gitlab-org/gitlab-ee/issues/5987) in GitLab 11.5 ## Get all templates of a particular type @@ -27,7 +27,7 @@ GET /projects/:id/templates/:type | Attribute | Type | Required | Description | | ---------- | ------ | -------- | ----------- | -| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | +| `id` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses)` of the template | Example response (licenses): @@ -93,7 +93,7 @@ GET /projects/:id/templates/:type/:key | Attribute | Type | Required | Description | | ---------- | ------ | -------- | ----------- | -| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | +| `id` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses)` of the template | | `key` | string | yes | The key of the template, as obtained from the collection endpoint | | `project` | string | no | The project name to use when expanding placeholders in the template. Only affects licenses | diff --git a/doc/api/projects.md b/doc/api/projects.md index e07d6ce9a42..781192fb92e 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -55,8 +55,8 @@ GET /projects | `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `with_programming_language` | string | no | Limit by projects which use the given programming language | -| `wiki_checksum_failed` | boolean | no | **[PREMIUM]** Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) | -| `repository_checksum_failed` | boolean | no | **[PREMIUM]** Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) | +| `wiki_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) | +| `repository_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) | | `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) | When `simple=true` or the user is unauthenticated this returns something like: @@ -708,11 +708,17 @@ POST /projects | `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) | | `default_branch` | string | no | `master` by default | | `description` | string | no | Short project description | -| `issues_enabled` | boolean | no | Enable issues for this project | -| `merge_requests_enabled` | boolean | no | Enable merge requests for this project | -| `jobs_enabled` | boolean | no | Enable jobs for this project | -| `wiki_enabled` | boolean | no | Enable wiki for this project | -| `snippets_enabled` | boolean | no | Enable snippets for this project | +| `issues_enabled` | boolean | no | (deprecated) Enable issues for this project. Use `issues_access_level` instead | +| `merge_requests_enabled` | boolean | no | (deprecated) Enable merge requests for this project. Use `merge_requests_access_level` instead | +| `jobs_enabled` | boolean | no | (deprecated) Enable jobs for this project. Use `builds_access_level` instead | +| `wiki_enabled` | boolean | no | (deprecated) Enable wiki for this project. Use `wiki_access_level` instead | +| `snippets_enabled` | boolean | no | (deprecated) Enable snippets for this project. Use `snippets_access_level` instead | +| `issues_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `repository_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `merge_requests_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `builds_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` | | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push | | `container_registry_enabled` | boolean | no | Enable container registry for this project | | `shared_runners_enabled` | boolean | no | Enable shared runners for this project | @@ -721,17 +727,23 @@ POST /projects | `public_builds` | boolean | no | If `true`, jobs can be viewed by non-project-members | | `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs | | `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved | -| `merge_method` | string | no | Set the merge method used | +| `merge_method` | string | no | Set the [merge method](#project-merge-method) used | | `lfs_enabled` | boolean | no | Enable LFS | | `request_access_enabled` | boolean | no | Allow users to request member access | | `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project | | `avatar` | mixed | no | Image file for avatar of the project | | `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line | +| `build_git_strategy` | string | no | The Git strategy. Defaults to `fetch` | +| `build_timeout` | integer | no | The maximum amount of time in minutes that a job is able run (in seconds) | +| `auto_cancel_pending_pipelines` | string | no | Auto-cancel pending pipelines (Note: this is not a boolean, but enabled/disabled | +| `build_coverage_regex` | string | no | Test coverage parsing | | `ci_config_path` | string | no | The path to CI config file | +| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project | +| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) | | `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins | -| `approvals_before_merge` | integer | no | **[STARTER]** How many approvers should approve merge requests by default | -| `mirror` | boolean | no | **[STARTER]** Enables pull mirroring in a project | -| `mirror_trigger_builds` | boolean | no | **[STARTER]** Pull mirroring triggers builds | +| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge requests by default | +| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project | +| `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds | | `initialize_with_readme` | boolean | no | `false` by default | NOTE: **Note:** If your HTTP repository is not publicly accessible, @@ -753,11 +765,17 @@ POST /projects/user/:user_id | `path` | string | no | Custom repository name for new project. By default generated based on name | | `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) | | `description` | string | no | Short project description | -| `issues_enabled` | boolean | no | Enable issues for this project | -| `merge_requests_enabled` | boolean | no | Enable merge requests for this project | -| `jobs_enabled` | boolean | no | Enable jobs for this project | -| `wiki_enabled` | boolean | no | Enable wiki for this project | -| `snippets_enabled` | boolean | no | Enable snippets for this project | +| `issues_enabled` | boolean | no | (deprecated) Enable issues for this project. Use `issues_access_level` instead | +| `merge_requests_enabled` | boolean | no | (deprecated) Enable merge requests for this project. Use `merge_requests_access_level` instead | +| `jobs_enabled` | boolean | no | (deprecated) Enable jobs for this project. Use `builds_access_level` instead | +| `wiki_enabled` | boolean | no | (deprecated) Enable wiki for this project. Use `wiki_access_level` instead | +| `snippets_enabled` | boolean | no | (deprecated) Enable snippets for this project. Use `snippets_access_level` instead | +| `issues_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `repository_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `merge_requests_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `builds_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` | | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push | | `container_registry_enabled` | boolean | no | Enable container registry for this project | | `shared_runners_enabled` | boolean | no | Enable shared runners for this project | @@ -766,18 +784,24 @@ POST /projects/user/:user_id | `public_builds` | boolean | no | If `true`, jobs can be viewed by non-project-members | | `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs | | `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved | -| `merge_method` | string | no | Set the merge method used | +| `merge_method` | string | no | Set the [merge method](#project-merge-method) used | | `lfs_enabled` | boolean | no | Enable LFS | | `request_access_enabled` | boolean | no | Allow users to request member access | | `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project | | `avatar` | mixed | no | Image file for avatar of the project | | `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line | +| `build_git_strategy` | string | no | The Git strategy. Defaults to `fetch` | +| `build_timeout` | integer | no | The maximum amount of time in minutes that a job is able run (in seconds) | +| `auto_cancel_pending_pipelines` | string | no | Auto-cancel pending pipelines (Note: this is not a boolean, but enabled/disabled | +| `build_coverage_regex` | string | no | Test coverage parsing | | `ci_config_path` | string | no | The path to CI config file | +| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project | +| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) | | `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins | -| `approvals_before_merge` | integer | no | **[STARTER]** How many approvers should approve merge requests by default | -| `external_authorization_classification_label` | string | no | **[CORE ONLY]** The classification label for the project | -| `mirror` | boolean | no | **[STARTER]** Enables pull mirroring in a project | -| `mirror_trigger_builds` | boolean | no | **[STARTER]** Pull mirroring triggers builds | +| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge requests by default | +| `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project | +| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project | +| `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds | NOTE: **Note:** If your HTTP repository is not publicly accessible, add authentication information to the URL: `https://username:password@gitlab.company.com/group/project.git` @@ -798,11 +822,17 @@ PUT /projects/:id | `path` | string | no | Custom repository name for the project. By default generated based on name | | `default_branch` | string | no | `master` by default | | `description` | string | no | Short project description | -| `issues_enabled` | boolean | no | Enable issues for this project | -| `merge_requests_enabled` | boolean | no | Enable merge requests for this project | -| `jobs_enabled` | boolean | no | Enable jobs for this project | -| `wiki_enabled` | boolean | no | Enable wiki for this project | -| `snippets_enabled` | boolean | no | Enable snippets for this project | +| `issues_enabled` | boolean | no | (deprecated) Enable issues for this project. Use `issues_access_level` instead | +| `merge_requests_enabled` | boolean | no | (deprecated) Enable merge requests for this project. Use `merge_requests_access_level` instead | +| `jobs_enabled` | boolean | no | (deprecated) Enable jobs for this project. Use `builds_access_level` instead | +| `wiki_enabled` | boolean | no | (deprecated) Enable wiki for this project. Use `wiki_access_level` instead | +| `snippets_enabled` | boolean | no | (deprecated) Enable snippets for this project. Use `snippets_access_level` instead | +| `issues_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `repository_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `merge_requests_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `builds_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` | +| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` | | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push | | `container_registry_enabled` | boolean | no | Enable container registry for this project | | `shared_runners_enabled` | boolean | no | Enable shared runners for this project | @@ -811,22 +841,28 @@ PUT /projects/:id | `public_builds` | boolean | no | If `true`, jobs can be viewed by non-project-members | | `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs | | `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved | -| `merge_method` | string | no | Set the merge method used | +| `merge_method` | string | no | Set the [merge method](#project-merge-method) used | | `lfs_enabled` | boolean | no | Enable LFS | | `request_access_enabled` | boolean | no | Allow users to request member access | | `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project | | `avatar` | mixed | no | Image file for avatar of the project | +| `build_git_strategy` | string | no | The Git strategy. Defaults to `fetch` | +| `build_timeout` | integer | no | The maximum amount of time in minutes that a job is able run (in seconds) | +| `auto_cancel_pending_pipelines` | string | no | Auto-cancel pending pipelines (Note: this is not a boolean, but enabled/disabled | +| `build_coverage_regex` | string | no | Test coverage parsing | | `ci_config_path` | string | no | The path to CI config file | | `ci_default_git_depth` | integer | no | Default number of revisions for [shallow cloning](../user/project/pipelines/settings.md#git-shallow-clone) | +| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project | +| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) | | `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins | -| `approvals_before_merge` | integer | no | **[STARTER]** How many approvers should approve merge request by default | -| `external_authorization_classification_label` | string | no | **[CORE ONLY]** The classification label for the project | -| `mirror` | boolean | no | **[STARTER]** Enables pull mirroring in a project | -| `mirror_user_id` | integer | no | **[STARTER]** User responsible for all the activity surrounding a pull mirror event | -| `mirror_trigger_builds` | boolean | no | **[STARTER]** Pull mirroring triggers builds | -| `only_mirror_protected_branches` | boolean | no | **[STARTER]** Only mirror protected branches | -| `mirror_overwrites_diverged_branches` | boolean | no | **[STARTER]** Pull mirror overwrites diverged branches | -| `packages_enabled` | boolean | no | **[PREMIUM ONLY]** Enable or disable packages repository feature | +| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge request by default | +| `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project | +| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project | +| `mirror_user_id` | integer | no | **(STARTER)** User responsible for all the activity surrounding a pull mirror event | +| `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds | +| `only_mirror_protected_branches` | boolean | no | **(STARTER)** Only mirror protected branches | +| `mirror_overwrites_diverged_branches` | boolean | no | **(STARTER)** Pull mirror overwrites diverged branches | +| `packages_enabled` | boolean | no | **(PREMIUM ONLY)** Enable or disable packages repository feature | NOTE: **Note:** If your HTTP repository is not publicly accessible, add authentication information to the URL: `https://username:password@gitlab.company.com/group/project.git` @@ -1612,7 +1648,7 @@ POST /projects/:id/housekeeping | --------- | ---- | -------- | ----------- | | `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | -## Push Rules **[STARTER]** +## Push Rules **(STARTER)** ### Get project push rules @@ -1631,6 +1667,7 @@ GET /projects/:id/push_rule "id": 1, "project_id": 3, "commit_message_regex": "Fixes \d+\..*", + "commit_message_negative_regex": "ssh\:\/\/", "branch_name_regex": "", "deny_delete_tag": false, "created_at": "2012-10-12T17:04:47Z", @@ -1663,18 +1700,19 @@ Adds a push rule to a specified project. POST /projects/:id/push_rule ``` -| Attribute | Type | Required | Description | -| -------------------------------------- | -------------- | -------- | ----------- | -| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | -| `deny_delete_tag` **[STARTER]** | boolean | no | Deny deleting a tag | -| `member_check` **[STARTER]** | boolean | no | Restrict commits by author (email) to existing GitLab users | -| `prevent_secrets` **[STARTER]** | boolean | no | GitLab will reject any files that are likely to contain secrets | -| `commit_message_regex` **[STARTER]** | string | no | All commit messages must match this, e.g. `Fixed \d+\..*` | -| `branch_name_regex` **[STARTER]** | string | no | All branch names must match this, e.g. `(feature|hotfix)\/*` | -| `author_email_regex` **[STARTER]** | string | no | All commit author emails must match this, e.g. `@my-company.com$` | -| `file_name_regex` **[STARTER]** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` | -| `max_file_size` **[STARTER]** | integer | no | Maximum file size (MB) | -| `commit_committer_check` **[PREMIUM]** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. | +| Attribute | Type | Required | Description | +| --------------------------------------------- | -------------- | -------- | ----------- | +| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | +| `deny_delete_tag` **(STARTER)** | boolean | no | Deny deleting a tag | +| `member_check` **(STARTER)** | boolean | no | Restrict commits by author (email) to existing GitLab users | +| `prevent_secrets` **(STARTER)** | boolean | no | GitLab will reject any files that are likely to contain secrets | +| `commit_message_regex` **(STARTER)** | string | no | All commit messages must match this, e.g. `Fixed \d+\..*` | +| `commit_message_negative_regex` **(STARTER)** | string | no | No commit message is allowed to match this, e.g. `ssh\:\/\/` | +| `branch_name_regex` **(STARTER)** | string | no | All branch names must match this, e.g. `(feature|hotfix)\/*` | +| `author_email_regex` **(STARTER)** | string | no | All commit author emails must match this, e.g. `@my-company.com$` | +| `file_name_regex` **(STARTER)** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` | +| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) | +| `commit_committer_check` **(PREMIUM)** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. | ### Edit project push rule @@ -1684,18 +1722,19 @@ Edits a push rule for a specified project. PUT /projects/:id/push_rule ``` -| Attribute | Type | Required | Description | -| -------------------------------------- | -------------- | -------- | ----------- | -| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | -| `deny_delete_tag` **[STARTER]** | boolean | no | Deny deleting a tag | -| `member_check` **[STARTER]** | boolean | no | Restrict commits by author (email) to existing GitLab users | -| `prevent_secrets` **[STARTER]** | boolean | no | GitLab will reject any files that are likely to contain secrets | -| `commit_message_regex` **[STARTER]** | string | no | All commit messages must match this, e.g. `Fixed \d+\..*` | -| `branch_name_regex` **[STARTER]** | string | no | All branch names must match this, e.g. `(feature|hotfix)\/*` | -| `author_email_regex` **[STARTER]** | string | no | All commit author emails must match this, e.g. `@my-company.com$` | -| `file_name_regex` **[STARTER]** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` | -| `max_file_size` **[STARTER]** | integer | no | Maximum file size (MB) | -| `commit_committer_check` **[PREMIUM]** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. | +| Attribute | Type | Required | Description | +| --------------------------------------------- | -------------- | -------- | ----------- | +| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | +| `deny_delete_tag` **(STARTER)** | boolean | no | Deny deleting a tag | +| `member_check` **(STARTER)** | boolean | no | Restrict commits by author (email) to existing GitLab users | +| `prevent_secrets` **(STARTER)** | boolean | no | GitLab will reject any files that are likely to contain secrets | +| `commit_message_regex` **(STARTER)** | string | no | All commit messages must match this, e.g. `Fixed \d+\..*` | +| `commit_message_negative_regex` **(STARTER)** | string | no | No commit message is allowed to match this, e.g. `ssh\:\/\/` | +| `branch_name_regex` **(STARTER)** | string | no | All branch names must match this, e.g. `(feature|hotfix)\/*` | +| `author_email_regex` **(STARTER)** | string | no | All commit author emails must match this, e.g. `@my-company.com$` | +| `file_name_regex` **(STARTER)** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` | +| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) | +| `commit_committer_check` **(PREMIUM)** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. | ### Delete project push rule @@ -1736,7 +1775,7 @@ Read more in the [Project import/export](project_import_export.md) documentation Read more in the [Project members](members.md) documentation. -## Start the pull mirroring process for a Project **[STARTER]** +## Start the pull mirroring process for a Project **(STARTER)** > Introduced in [GitLab Starter](https://about.gitlab.com/pricing) 10.3. diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md index 6e41584afef..9309306ba05 100644 --- a/doc/api/protected_branches.md +++ b/doc/api/protected_branches.md @@ -166,9 +166,9 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitla | `push_access_level` | string | no | Access levels allowed to push (defaults: `40`, maintainer access level) | | `merge_access_level` | string | no | Access levels allowed to merge (defaults: `40`, maintainer access level) | | `unprotect_access_level` | string | no | Access levels allowed to unprotect (defaults: `40`, maintainer access level) | -| `allowed_to_push` | array | no | **[STARTER]** Array of access levels allowed to push, with each described by a hash | -| `allowed_to_merge` | array | no | **[STARTER]** Array of access levels allowed to merge, with each described by a hash | -| `allowed_to_unprotect` | array | no | **[STARTER]**Array of access levels allowed to unprotect, with each described by a hash | +| `allowed_to_push` | array | no | **(STARTER)** Array of access levels allowed to push, with each described by a hash | +| `allowed_to_merge` | array | no | **(STARTER)** Array of access levels allowed to merge, with each described by a hash | +| `allowed_to_unprotect` | array | no | **(STARTER)**Array of access levels allowed to unprotect, with each described by a hash | Example response: @@ -229,7 +229,7 @@ Example response: } ``` -### Example with user / group level access **[STARTER]** +### Example with user / group level access **(STARTER)** Elements in the `allowed_to_push` / `allowed_to_merge` / `allowed_to_unprotect` array should take the form `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}`. Each user must have access to the project and each group must [have this project shared](../user/project/members/share_project_with_groups.md). These access levels allow [more granular control over protected branch access](../user/project/protected_branches.md#restricting-push-and-merge-access-to-certain-users-starter) and were [added to the API in ][ee-3516] in GitLab 10.3 EE. diff --git a/doc/api/protected_tags.md b/doc/api/protected_tags.md index 3adca61a108..fb6fa040244 100644 --- a/doc/api/protected_tags.md +++ b/doc/api/protected_tags.md @@ -5,6 +5,7 @@ **Valid access levels** Currently, these levels are recognized: + ``` 0 => No access 30 => Developer access diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 681dc72c934..4aff79c9c62 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -121,9 +121,9 @@ Parameters: - `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user - `sha` (optional) - The commit SHA to download. A tag, branch reference, or SHA can be used. This defaults to the tip of the default branch if not specified. For example: - ```sh - curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.com/api/v4/projects/<project_id>/repository/archive?sha=<commit_sha> - ``` +```sh +curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.com/api/v4/projects/<project_id>/repository/archive?sha=<commit_sha> +``` ## Compare branches, tags or commits @@ -235,17 +235,17 @@ Example response: ```json { - "id": "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863", - "short_id": "1a0b36b3", - "title": "Initial commit", - "created_at": "2014-02-27T08:03:18.000Z", - "parent_ids": [], - "message": "Initial commit\n", - "author_name": "Dmitriy Zaporozhets", - "author_email": "dmitriy.zaporozhets@gmail.com", - "authored_date": "2014-02-27T08:03:18.000Z", - "committer_name": "Dmitriy Zaporozhets", - "committer_email": "dmitriy.zaporozhets@gmail.com", - "committed_date": "2014-02-27T08:03:18.000Z" + "id": "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863", + "short_id": "1a0b36b3", + "title": "Initial commit", + "created_at": "2014-02-27T08:03:18.000Z", + "parent_ids": [], + "message": "Initial commit\n", + "author_name": "Dmitriy Zaporozhets", + "author_email": "dmitriy.zaporozhets@gmail.com", + "authored_date": "2014-02-27T08:03:18.000Z", + "committer_name": "Dmitriy Zaporozhets", + "committer_email": "dmitriy.zaporozhets@gmail.com", + "committed_date": "2014-02-27T08:03:18.000Z" } ``` diff --git a/doc/api/repository_submodules.md b/doc/api/repository_submodules.md index 2c44c4abc93..5a722a75cb9 100644 --- a/doc/api/repository_submodules.md +++ b/doc/api/repository_submodules.md @@ -43,7 +43,7 @@ Example response: "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" ], "committed_date": "2018-09-20T09:26:24.000-07:00", - "authored_date": "2018-09-20T09:26:24.000-07:00", + "authored_date": "2018-09-20T09:26:24.000-07:00", "status": null } ``` diff --git a/doc/api/resource_label_events.md b/doc/api/resource_label_events.md index f0a7ac4e41d..7ad4d78014c 100644 --- a/doc/api/resource_label_events.md +++ b/doc/api/resource_label_events.md @@ -88,7 +88,7 @@ Parameters: curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/issues/11/resource_label_events/1 ``` -## Epics **[ULTIMATE]** +## Epics **(ULTIMATE)** ### List group epic label events diff --git a/doc/api/runners.md b/doc/api/runners.md index 1318b9ca828..e6962d17a98 100644 --- a/doc/api/runners.md +++ b/doc/api/runners.md @@ -20,10 +20,10 @@ Here's an example of how the two tokens are used in Runner registration: 1. You use that authentication token and add it to the [Runner's configuration file](https://docs.gitlab.com/runner/commands/#configuration-file): - ```toml - [[runners]] - token = "<authentication_token>" - ``` + ```toml + [[runners]] + token = "<authentication_token>" + ``` GitLab and Runner are then connected. diff --git a/doc/api/scim.md b/doc/api/scim.md index 3870ea788e7..ece7f56e394 100644 --- a/doc/api/scim.md +++ b/doc/api/scim.md @@ -1,4 +1,4 @@ -# SCIM API **[SILVER ONLY]** +# SCIM API **(SILVER ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9388) in [GitLab Silver](https://about.gitlab.com/pricing/) 11.10. @@ -6,7 +6,7 @@ The SCIM API implements the [the RFC7644 protocol](https://tools.ietf.org/html/r NOTE: **Note:** [Group SSO](../user/group/saml_sso/index.md) and the feature -flag `:group_scim` must be enabled for the group. For more information, see [SCIM setup documentation](../user/group/saml_sso/scim_setup.md#requirements). +flag `:group_scim` must be enabled for the group. For more information, see [SCIM setup documentation](../user/group/saml_sso/scim_setup.md#requirements). ## Get a list of SAML users diff --git a/doc/api/search.md b/doc/api/search.md index abb77ae05dc..60acf600ac7 100644 --- a/doc/api/search.md +++ b/doc/api/search.md @@ -19,7 +19,7 @@ GET /search Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs, users. -If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). **[STARTER]** +If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). **(STARTER)** The response depends on the requested scope. @@ -283,7 +283,7 @@ Example response: ] ``` -### Scope: wiki_blobs **[STARTER]** +### Scope: wiki_blobs **(STARTER)** This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. @@ -308,7 +308,7 @@ Example response: ] ``` -### Scope: commits **[STARTER]** +### Scope: commits **(STARTER)** This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. @@ -341,7 +341,7 @@ Example response: ] ``` -### Scope: blobs **[STARTER]** +### Scope: blobs **(STARTER)** This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. @@ -415,7 +415,7 @@ GET /groups/:id/search Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, users. -If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). **[STARTER]** +If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). **(STARTER)** The response depends on the requested scope. @@ -617,7 +617,7 @@ Example response: ] ``` -### Scope: wiki_blobs **[STARTER]** +### Scope: wiki_blobs **(STARTER)** This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. @@ -642,7 +642,7 @@ Example response: ] ``` -### Scope: commits **[STARTER]** +### Scope: commits **(STARTER)** This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. @@ -675,7 +675,7 @@ Example response: ] ``` -### Scope: blobs **[STARTER]** +### Scope: blobs **(STARTER)** This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. diff --git a/doc/api/services.md b/doc/api/services.md index 4f35c17e927..df15e6892b0 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -1146,7 +1146,7 @@ Get JetBrains TeamCity CI service settings for a project. GET /projects/:id/services/teamcity ``` -## Jenkins CI **[STARTER]** +## Jenkins CI **(STARTER)** A continuous integration and build server diff --git a/doc/api/settings.md b/doc/api/settings.md index 0f46662a8ae..ff48cac1f47 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -150,7 +150,7 @@ these parameters: - `file_template_project_id` - `geo_node_allowed_ips` -Example responses: **[PREMIUM ONLY]** +Example responses: **(PREMIUM ONLY)** ```json "external_authorization_service_enabled": true, @@ -174,12 +174,12 @@ are listed in the descriptions of the relevant settings. | `after_sign_up_text` | string | no | Text shown to the user after signing up | | `akismet_api_key` | string | required by: `akismet_enabled` | API key for akismet spam protection. | | `akismet_enabled` | boolean | no | (**If enabled, requires:** `akismet_api_key`) Enable or disable akismet spam protection. | -| `allow_group_owners_to_manage_ldap` | boolean | no | **[PREMIUM]** Set to `true` to allow group owners to manage LDAP | +| `allow_group_owners_to_manage_ldap` | boolean | no | **(PREMIUM)** Set to `true` to allow group owners to manage LDAP | | `allow_local_requests_from_hooks_and_services` | boolean | no | Allow requests to the local network from hooks and services. | | `authorized_keys_enabled` | boolean | no | By default, we write to the `authorized_keys` file to support Git over SSH without additional configuration. GitLab can be optimized to authenticate SSH keys via the database file. Only disable this if you have configured your OpenSSH server to use the AuthorizedKeysCommand. | | `auto_devops_domain` | string | no | Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages. | | `auto_devops_enabled` | boolean | no | Enable Auto DevOps for projects by default. It will automatically build, test, and deploy applications based on a predefined CI/CD configuration. | -| `check_namespace_plan` | boolean | no | **[PREMIUM]** Enabling this will make only licensed EE features available to projects if the project namespace's plan includes the feature or if the project is public. | +| `check_namespace_plan` | boolean | no | **(PREMIUM)** Enabling this will make only licensed EE features available to projects if the project namespace's plan includes the feature or if the project is public. | | `clientside_sentry_dsn` | string | required by: `clientside_sentry_enabled` | Clientside Sentry Data Source Name. | | `clientside_sentry_enabled` | boolean | no | (**If enabled, requires:** `clientside_sentry_dsn`) Enable Sentry error reporting for the client side. | | `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes. | @@ -196,31 +196,31 @@ are listed in the descriptions of the relevant settings. | `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. | | `ecdsa_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ECDSA key. Default is `0` (no restriction). `-1` disables ECDSA keys. | | `ed25519_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ED25519 key. Default is `0` (no restriction). `-1` disables ED25519 keys. | -| `elasticsearch_aws` | boolean | no | **[PREMIUM]** Enable the use of AWS hosted Elasticsearch | -| `elasticsearch_aws_access_key` | string | no | **[PREMIUM]** AWS IAM access key | -| `elasticsearch_aws_region` | string | no | **[PREMIUM]** The AWS region the elasticsearch domain is configured | -| `elasticsearch_aws_secret_access_key` | string | no | **[PREMIUM]** AWS IAM secret access key | -| `elasticsearch_experimental_indexer` | boolean | no | **[PREMIUM]** Use the experimental elasticsearch indexer. More info: <https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer> | -| `elasticsearch_indexing` | boolean | no | **[PREMIUM]** Enable Elasticsearch indexing | -| `elasticsearch_search` | boolean | no | **[PREMIUM]** Enable Elasticsearch search | -| `elasticsearch_url` | string | no | **[PREMIUM]** The url to use for connecting to Elasticsearch. Use a comma-separated list to support cluster (e.g., `http://localhost:9200, http://localhost:9201"`). If your Elasticsearch instance is password protected, pass the `username:password` in the URL (e.g., `http://<username>:<password>@<elastic_host>:9200/`). | -| `elasticsearch_limit_indexing` | boolean | no | **[PREMIUM]** Limit Elasticsearch to index certain namespaces and projects | -| `elasticsearch_project_ids` | array of integers | no | **[PREMIUM]** The projects to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. | -| `elasticsearch_namespace_ids` | array of integers | no | **[PREMIUM]** The namespaces to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. | -| `email_additional_text` | string | no | **[PREMIUM]** Additional text added to the bottom of every email for legal/auditing/compliance reasons | +| `elasticsearch_aws` | boolean | no | **(PREMIUM)** Enable the use of AWS hosted Elasticsearch | +| `elasticsearch_aws_access_key` | string | no | **(PREMIUM)** AWS IAM access key | +| `elasticsearch_aws_region` | string | no | **(PREMIUM)** The AWS region the elasticsearch domain is configured | +| `elasticsearch_aws_secret_access_key` | string | no | **(PREMIUM)** AWS IAM secret access key | +| `elasticsearch_experimental_indexer` | boolean | no | **(PREMIUM)** Use the experimental elasticsearch indexer. More info: <https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer> | +| `elasticsearch_indexing` | boolean | no | **(PREMIUM)** Enable Elasticsearch indexing | +| `elasticsearch_search` | boolean | no | **(PREMIUM)** Enable Elasticsearch search | +| `elasticsearch_url` | string | no | **(PREMIUM)** The url to use for connecting to Elasticsearch. Use a comma-separated list to support cluster (e.g., `http://localhost:9200, http://localhost:9201"`). If your Elasticsearch instance is password protected, pass the `username:password` in the URL (e.g., `http://<username>:<password>@<elastic_host>:9200/`). | +| `elasticsearch_limit_indexing` | boolean | no | **(PREMIUM)** Limit Elasticsearch to index certain namespaces and projects | +| `elasticsearch_project_ids` | array of integers | no | **(PREMIUM)** The projects to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. | +| `elasticsearch_namespace_ids` | array of integers | no | **(PREMIUM)** The namespaces to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. | +| `email_additional_text` | string | no | **(PREMIUM)** Additional text added to the bottom of every email for legal/auditing/compliance reasons | | `email_author_in_body` | boolean | no | Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead. | | `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. | | `enforce_terms` | boolean | no | (**If enabled, requires:** `terms`) Enforce application ToS to all users. | -| `external_auth_client_cert` | string | no | **[PREMIUM]** (**If enabled, requires:** `external_auth_client_key`) The certificate to use to authenticate with the external authorization service | -| `external_auth_client_key` | string | required by: `external_auth_client_cert` | **[PREMIUM]** Private key for the certificate when authentication is required for the external authorization service, this is encrypted when stored | -| `external_auth_client_key_pass` | string | no | **[PREMIUM]** Passphrase to use for the private key when authenticating with the external service this is encrypted when stored | -| `external_authorization_service_enabled` | boolean | no | **[PREMIUM]** (**If enabled, requires:** `external_authorization_service_default_label`, `external_authorization_service_timeout` and `external_authorization_service_url` ) Enable using an external authorization service for accessing projects | -| `external_authorization_service_default_label` | string | required by: `external_authorization_service_enabled` | **[PREMIUM]** The default classification label to use when requesting authorization and no classification label has been specified on the project | -| `external_authorization_service_timeout` | float | required by: `external_authorization_service_enabled` | **[PREMIUM]** The timeout after which an authorization request is aborted, in seconds. When a request times out, access is denied to the user. (min: 0.001, max: 10, step: 0.001) | -| `external_authorization_service_url` | string | required by: `external_authorization_service_enabled` | **[PREMIUM]** URL to which authorization requests will be directed | -| `file_template_project_id` | integer | no | **[PREMIUM]** The ID of a project to load custom file templates from | +| `external_auth_client_cert` | string | no | **(PREMIUM)** (**If enabled, requires:** `external_auth_client_key`) The certificate to use to authenticate with the external authorization service | +| `external_auth_client_key` | string | required by: `external_auth_client_cert` | **(PREMIUM)** Private key for the certificate when authentication is required for the external authorization service, this is encrypted when stored | +| `external_auth_client_key_pass` | string | no | **(PREMIUM)** Passphrase to use for the private key when authenticating with the external service this is encrypted when stored | +| `external_authorization_service_enabled` | boolean | no | **(PREMIUM)** (**If enabled, requires:** `external_authorization_service_default_label`, `external_authorization_service_timeout` and `external_authorization_service_url` ) Enable using an external authorization service for accessing projects | +| `external_authorization_service_default_label` | string | required by: `external_authorization_service_enabled` | **(PREMIUM)** The default classification label to use when requesting authorization and no classification label has been specified on the project | +| `external_authorization_service_timeout` | float | required by: `external_authorization_service_enabled` | **(PREMIUM)** The timeout after which an authorization request is aborted, in seconds. When a request times out, access is denied to the user. (min: 0.001, max: 10, step: 0.001) | +| `external_authorization_service_url` | string | required by: `external_authorization_service_enabled` | **(PREMIUM)** URL to which authorization requests will be directed | +| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from | | `first_day_of_week` | integer | no | Start day of the week for calendar views and date pickers. Valid values are `0` (default) for Sunday, `1` for Monday, and `6` for Saturday. | -| `geo_status_timeout` | integer | no | **[PREMIUM]** The amount of seconds after which a request to get a secondary node status will time out. | +| `geo_status_timeout` | integer | no | **(PREMIUM)** The amount of seconds after which a request to get a secondary node status will time out. | | `gitaly_timeout_default` | integer | no | Default Gitaly timeout, in seconds. This timeout is not enforced for git fetch/push operations or Sidekiq jobs. Set to `0` to disable timeouts. | | `gitaly_timeout_fast` | integer | no | Gitaly fast operation timeout, in seconds. Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. Set to `0` to disable timeouts. | | `gitaly_timeout_medium` | integer | no | Medium Gitaly timeout, in seconds. This should be a value between the Fast and the Default timeout. Set to `0` to disable timeouts. | @@ -229,7 +229,7 @@ are listed in the descriptions of the relevant settings. | `help_page_hide_commercial_content` | boolean | no | Hide marketing-related entries from help. | | `help_page_support_url` | string | no | Alternate support URL for help page. | | `help_page_text` | string | no | Custom text displayed on the help page. | -| `help_text` | string | no | **[PREMIUM]** GitLab server administrator information | +| `help_text` | string | no | **(PREMIUM)** GitLab server administrator information | | `hide_third_party_offers` | boolean | no | Do not display offers from third parties within GitLab. | | `home_page_url` | string | no | Redirect to this URL when not logged in. | | `housekeeping_bitmaps_enabled` | boolean | required by: `housekeeping_enabled` | Enable Git pack file bitmap creation. | @@ -252,9 +252,9 @@ are listed in the descriptions of the relevant settings. | `metrics_sample_interval` | integer | required by: `metrics_enabled` | The sampling interval in seconds. | | `metrics_timeout` | integer | required by: `metrics_enabled` | The amount of seconds after which InfluxDB will time out. | | `mirror_available` | boolean | no | Allow mirrors to be set up for projects. If disabled, only admins will be able to set up mirrors in projects. | -| `mirror_capacity_threshold` | integer | no | **[PREMIUM]** Minimum capacity to be available before scheduling more mirrors preemptively | -| `mirror_max_capacity` | integer | no | **[PREMIUM]** Maximum number of mirrors that can be synchronizing at the same time. | -| `mirror_max_delay` | integer | no | **[PREMIUM]** Maximum time (in minutes) between updates that a mirror can have when scheduled to synchronize. | +| `mirror_capacity_threshold` | integer | no | **(PREMIUM)** Minimum capacity to be available before scheduling more mirrors preemptively | +| `mirror_max_capacity` | integer | no | **(PREMIUM)** Maximum number of mirrors that can be synchronizing at the same time. | +| `mirror_max_delay` | integer | no | **(PREMIUM)** Maximum time (in minutes) between updates that a mirror can have when scheduled to synchronize. | | `pages_domain_verification_enabled` | boolean | no | Require users to prove ownership of custom domains. Domain verification is an essential security measure for public GitLab sites. Users are required to demonstrate they control a domain before it is enabled. | | `password_authentication_enabled_for_git` | boolean | no | Enable authentication for Git over HTTP(S) via a GitLab account password. Default is `true`. | | `password_authentication_enabled_for_web` | boolean | no | Enable authentication for the web interface via a GitLab account password. Default is `true`. | @@ -266,12 +266,12 @@ are listed in the descriptions of the relevant settings. | `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to `0` to disable polling. | | `project_export_enabled` | boolean | no | Enable project export. | | `prometheus_metrics_enabled` | boolean | no | Enable prometheus metrics. | -| `pseudonymizer_enabled` | boolean | no | **[PREMIUM]** When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory. +| `pseudonymizer_enabled` | boolean | no | **(PREMIUM)** When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory. | `recaptcha_enabled` | boolean | no | (**If enabled, requires:** `recaptcha_private_key` and `recaptcha_site_key`) Enable recaptcha. | | `recaptcha_private_key` | string | required by: `recaptcha_enabled` | Private key for recaptcha. | | `recaptcha_site_key` | string | required by: `recaptcha_enabled` | Site key for recaptcha. | | `repository_checks_enabled` | boolean | no | GitLab will periodically run `git fsck` in all project and wiki repositories to look for silent disk corruption issues. | -| `repository_size_limit` | integer | no | **[PREMIUM]** Size limit per repository (MB) | +| `repository_size_limit` | integer | no | **(PREMIUM)** Size limit per repository (MB) | | `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. | | `require_two_factor_authentication` | boolean | no | (**If enabled, requires:** `two_factor_grace_period`) Require all users to set up Two-factor authentication. | | `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for groups, projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is `null` which means there is no restriction. | @@ -279,15 +279,15 @@ are listed in the descriptions of the relevant settings. | `send_user_confirmation_email` | boolean | no | Send confirmation email on sign-up. | | `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes | | `shared_runners_enabled` | boolean | no | (**If enabled, requires:** `shared_runners_text` and `shared_runners_minutes`) Enable shared runners for new projects. | -| `shared_runners_minutes` | integer | required by: `shared_runners_enabled` | **[PREMIUM]** Set the maximum number of pipeline minutes that a group can use on shared Runners per month. | +| `shared_runners_minutes` | integer | required by: `shared_runners_enabled` | **(PREMIUM)** Set the maximum number of pipeline minutes that a group can use on shared Runners per month. | | `shared_runners_text` | string | required by: `shared_runners_enabled` | Shared runners text. | | `sign_in_text` | string | no | Text on the login page. | | `signin_enabled` | string | no | (Deprecated: Use `password_authentication_enabled_for_web` instead) Flag indicating if password authentication is enabled for the web interface. | | `signup_enabled` | boolean | no | Enable registration. Default is `true`. | -| `slack_app_enabled` | boolean | no | **[PREMIUM]** (**If enabled, requires:** `slack_app_id`, `slack_app_secret` and `slack_app_secret`) Enable Slack app. | -| `slack_app_id` | string | required by: `slack_app_enabled` | **[PREMIUM]** The app id of the Slack-app. | -| `slack_app_secret` | string | required by: `slack_app_enabled` | **[PREMIUM]** The app secret of the Slack-app. | -| `slack_app_verification_token` | string | required by: `slack_app_enabled` | **[PREMIUM]** The verification token of the Slack-app. | +| `slack_app_enabled` | boolean | no | **(PREMIUM)** (**If enabled, requires:** `slack_app_id`, `slack_app_secret` and `slack_app_secret`) Enable Slack app. | +| `slack_app_id` | string | required by: `slack_app_enabled` | **(PREMIUM)** The app id of the Slack-app. | +| `slack_app_secret` | string | required by: `slack_app_enabled` | **(PREMIUM)** The app secret of the Slack-app. | +| `slack_app_verification_token` | string | required by: `slack_app_enabled` | **(PREMIUM)** The verification token of the Slack-app. | | `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to `0` for unlimited time. | | `terms` | text | required by: `enforce_terms` | (**Required by:** `enforce_terms`) Markdown content for the ToS. | | `throttle_authenticated_api_enabled` | boolean | no | (**If enabled, requires:** `throttle_authenticated_api_period_in_seconds` and `throttle_authenticated_api_requests_per_period`) Enable authenticated API request rate limit. Helps reduce request volume (e.g. from crawlers or abusive bots). | @@ -310,4 +310,4 @@ are listed in the descriptions of the relevant settings. | `user_show_add_ssh_key_message` | boolean | no | When set to `false` disable the "You won't be able to pull or push project code via SSH" warning shown to users with no uploaded SSH key. | | `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. | | `local_markdown_version` | integer | no | Increase this value when any cached markdown should be invalidated. | -| `geo_node_allowed_ips` | string | yes | **[PREMIUM]** Comma-separated list of IPs and CIDRs of allowed secondary nodes. For example, `1.1.1.1, 2.2.2.0/24`. | +| `geo_node_allowed_ips` | string | yes | **(PREMIUM)** Comma-separated list of IPs and CIDRs of allowed secondary nodes. For example, `1.1.1.1, 2.2.2.0/24`. | diff --git a/doc/api/tags.md b/doc/api/tags.md index 3177fec618f..af86ba961f4 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -141,6 +141,7 @@ Parameters: "message": null } ``` + The message will be `null` when creating a lightweight tag otherwise it will contain the annotation. diff --git a/doc/api/users.md b/doc/api/users.md index e1fccc14df3..b43b4de823b 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -164,6 +164,7 @@ the `group_saml` provider option: ... } ] +``` You can lookup users by external UID and provider: @@ -359,8 +360,8 @@ Parameters: - `external` (optional) - Flags the user as external - true or false(default) - `avatar` (optional) - Image file for user's avatar - `private_profile` (optional) - User's profile is private - true or false -- `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]** +- `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)** ## User modification @@ -396,8 +397,8 @@ Parameters: - `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user - `avatar` (optional) - Image file for user's avatar - `private_profile` (optional) - User's profile is private - true or false -- `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]** +- `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)** 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, @@ -592,6 +593,30 @@ Example responses } ``` +## User counts + +Get the counts (same as in top right menu) of the currently signed in user. + +| Attribute | Type | Description | +| --------- | ---- | ----------- | +| `merge_requests` | number | Merge requests that are active and assigned to current user. | + +``` +GET /user_counts +``` + +```bash +curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/user_counts" +``` + +Example response: + +```json +{ + "merge_requests": 4 +} +``` + ## List user projects Please refer to the [List of user projects](projects.md#list-user-projects). diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 5752fb7c078..5f875528a6c 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -9,7 +9,7 @@ The V3 API documentation is still Below are the changes made between V3 and V4. -### 8.17 +## 8.17 - Removed `GET /projects/:search` (use: `GET /projects?search=x`) [!8877](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8877) - `iid` filter has been removed from `GET /projects/:id/issues` [!8967](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8967) @@ -18,7 +18,7 @@ Below are the changes made between V3 and V4. - Project snippets do not return deprecated field `expires_at` [!8723](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8723) - Endpoints under `GET /projects/:id/keys` have been removed (use `GET /projects/:id/deploy_keys`) [!8716](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8716) -### 9.0 +## 9.0 - Status 409 returned for `POST /projects/:id/members` when a member already exists [!9093](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9093) - Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar` [!9328](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9328) diff --git a/doc/api/vulnerabilities.md b/doc/api/vulnerabilities.md index f1b11c7bceb..eaa4c13de55 100644 --- a/doc/api/vulnerabilities.md +++ b/doc/api/vulnerabilities.md @@ -1,4 +1,4 @@ -# Vulnerabilities API **[ULTIMATE]** +# Vulnerabilities API **(ULTIMATE)** Every API call to vulnerabilities must be authenticated. @@ -30,6 +30,7 @@ GET /projects/:id/vulnerabilities?scope=all GET /projects/:id/vulnerabilities?scope=dismissed GET /projects/:id/vulnerabilities?severity=high GET /projects/:id/vulnerabilities?confidence=unknown,experimental +GET /projects/:id/vulnerabilities?pipeline_id=42 ``` | Attribute | Type | Required | Description | @@ -39,6 +40,7 @@ GET /projects/:id/vulnerabilities?confidence=unknown,experimental | `scope` | string | no | Returns vulnerabilities for the given scope: `all` or `dismissed`. Defaults to `dismissed` | | `severity` | string array | no | Returns vulnerabilities belonging to specified severity level: `undefined`, `info`, `unknown`, `low`, `medium`, `high`, or `critical`. Defaults to all' | | `confidence` | string array | no | Returns vulnerabilities belonging to specified confidence level: `undefined`, `ignore`, `unknown`, `experimental`, `low`, `medium`, `high`, or `confirmed`. Defaults to all | +| `pipeline_id` | integer/string | no | Returns vulnerabilities belonging to specified pipeline. | ```bash curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/vulnerabilities diff --git a/doc/ci/README.md b/doc/ci/README.md index d851a56ee0e..7048ceaac41 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -104,27 +104,27 @@ Its feature set is listed on the table below according to DevOps stages. | **Verify** || | [Browser Performance Testing](../user/project/merge_requests/browser_performance_testing.md) | Quickly determine the performance impact of pending code changes. | | [CI services](services/README.md) | Link Docker containers with your base image.| -| [Code Quality](../user/project/merge_requests/code_quality.md) **[STARTER]** | Analyze your source code quality. | -| [GitLab CI/CD for external repositories](ci_cd_for_external_repos/index.md) **[PREMIUM]** | Get the benefits of GitLab CI/CD combined with repositories in GitHub and BitBucket Cloud. | -| [Interactive Web Terminals](interactive_web_terminal/index.md) **[CORE ONLY]** | Open an interactive web terminal to debug the running jobs. | +| [Code Quality](../user/project/merge_requests/code_quality.md) **(STARTER)** | Analyze your source code quality. | +| [GitLab CI/CD for external repositories](ci_cd_for_external_repos/index.md) **(PREMIUM)** | Get the benefits of GitLab CI/CD combined with repositories in GitHub and BitBucket Cloud. | +| [Interactive Web Terminals](interactive_web_terminal/index.md) **(CORE ONLY)** | Open an interactive web terminal to debug the running jobs. | | [JUnit tests](junit_test_reports.md) | Identify script failures directly on merge requests. | | [Using Docker images](docker/using_docker_images.md) | Use GitLab and GitLab Runner with Docker to build and test applications. | |---+---| | **Release** || | [Auto Deploy](../topics/autodevops/index.md#auto-deploy) | Deploy your application to a production environment in a Kubernetes cluster. | | [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. | -| [Canary Deployments](../user/project/canary_deployments.md) **[PREMIUM]** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. | -| [Deploy Boards](../user/project/deploy_boards.md) **[PREMIUM]** | Check the current health and status of each CI/CD environment running on Kubernetes. | -| [Feature Flags](../user/project/operations/feature_flags.md) **[PREMIUM]** | Deploy your features behind Feature Flags. | +| [Canary Deployments](../user/project/canary_deployments.md) **(PREMIUM)** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. | +| [Deploy Boards](../user/project/deploy_boards.md) **(PREMIUM)** | Check the current health and status of each CI/CD environment running on Kubernetes. | +| [Feature Flags](../user/project/operations/feature_flags.md) **(PREMIUM)** | Deploy your features behind Feature Flags. | | [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. | | [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. | | [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes. | |---+---| | **Secure** || -| [Container Scanning](../user/application_security/container_scanning/index.md) **[ULTIMATE]** | Check your Docker containers for known vulnerabilities.| -| [Dependency Scanning](../user/application_security/dependency_scanning/index.md) **[ULTIMATE]** | Analyze your dependencies for known vulnerabilities. | -| [License Management](../user/application_security/license_management/index.md) **[ULTIMATE]** | Search your project dependencies for their licenses. | -| [Security Test reports](../user/project/merge_requests/index.md#security-reports-ultimate) **[ULTIMATE]** | Check for app vulnerabilities. | +| [Container Scanning](../user/application_security/container_scanning/index.md) **(ULTIMATE)** | Check your Docker containers for known vulnerabilities.| +| [Dependency Scanning](../user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. | +| [License Management](../user/application_security/license_management/index.md) **(ULTIMATE)** | Search your project dependencies for their licenses. | +| [Security Test reports](../user/project/merge_requests/index.md#security-reports-ultimate) **(ULTIMATE)** | Check for app vulnerabilities. | ## Examples @@ -133,7 +133,7 @@ on the [CI Examples](examples/README.md) page. GitLab also provides [example projects](https://gitlab.com/gitlab-examples) pre-configured to use GitLab CI/CD. -## Administration **[CORE ONLY]** +## Administration **(CORE ONLY)** As a GitLab administrator, you can change the default behavior of GitLab CI/CD for: diff --git a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md index bbb25c78ec5..b3110b435db 100644 --- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md +++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md @@ -2,7 +2,7 @@ type: howto --- -# Using GitLab CI/CD with a Bitbucket Cloud repository **[PREMIUM]** +# Using GitLab CI/CD with a Bitbucket Cloud repository **(PREMIUM)** GitLab CI/CD can be used with Bitbucket Cloud by: diff --git a/doc/ci/ci_cd_for_external_repos/github_integration.md b/doc/ci/ci_cd_for_external_repos/github_integration.md index 53b36181062..0bb3aa35ed0 100644 --- a/doc/ci/ci_cd_for_external_repos/github_integration.md +++ b/doc/ci/ci_cd_for_external_repos/github_integration.md @@ -2,7 +2,7 @@ type: howto --- -# Using GitLab CI/CD with a GitHub repository **[PREMIUM]** +# Using GitLab CI/CD with a GitHub repository **(PREMIUM)** GitLab CI/CD can be used with **GitHub.com** and **GitHub Enterprise** by creating a [CI/CD project](index.md) to connect your GitHub repository to @@ -109,7 +109,7 @@ your repository: new commits. The web hook URL should be set to the GitLab API to - [trigger pull mirroring](https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project-starter), + [trigger pull mirroring](../../api/projects.md#start-the-pull-mirroring-process-for-a-project-starter), using the GitLab personal access token we just created. ``` diff --git a/doc/ci/ci_cd_for_external_repos/index.md b/doc/ci/ci_cd_for_external_repos/index.md index d46e451c609..02b8eb7daa7 100644 --- a/doc/ci/ci_cd_for_external_repos/index.md +++ b/doc/ci/ci_cd_for_external_repos/index.md @@ -2,7 +2,7 @@ type: index, howto --- -# GitLab CI/CD for external repositories **[PREMIUM]** +# GitLab CI/CD for external repositories **(PREMIUM)** >[Introduced][ee-4642] in [GitLab Premium][eep] 10.6. diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 816bd35018a..2d7fb323d79 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -530,11 +530,11 @@ There are two ways to determine the value of `DOCKER_AUTH_CONFIG`: ``` - **Second way -** In some setups, it's possible that Docker client -will use the available system keystore to store the result of `docker -login`. In that case, it's impossible to read `~/.docker/config.json`, -so you will need to prepare the required base64-encoded version of -`${username}:${password}` manually. Open a terminal and execute the -following command: + will use the available system keystore to store the result of `docker + login`. In that case, it's impossible to read `~/.docker/config.json`, + so you will need to prepare the required base64-encoded version of + `${username}:${password}` manually. Open a terminal and execute the + following command: ```bash echo -n "my_username:my_password" | base64 diff --git a/doc/ci/environments.md b/doc/ci/environments.md index a32dbc11a33..f86ca8f74f2 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -673,7 +673,7 @@ fetch line: fetch = +refs/environments/*:refs/remotes/origin/environments/* ``` -### Scoping environments with specs **[PREMIUM]** +### Scoping environments with specs **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2112) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.4. @@ -692,7 +692,7 @@ with `review/` would have that particular variable. Some GitLab features can behave differently for each environment. For example, you can -[create a secret variable to be injected only into a production environment](variables/README.md#limiting-environment-scopes-of-environment-variables-premium). **[PREMIUM]** +[create a secret variable to be injected only into a production environment](variables/README.md#limiting-environment-scopes-of-environment-variables-premium). **(PREMIUM)** In most cases, these features use the _environment specs_ mechanism, which offers an efficient way to implement scoping within each environment group. @@ -734,7 +734,7 @@ Below are some links you may find interesting: - [The `.gitlab-ci.yml` definition of environments](yaml/README.md#environment) - [A blog post on Deployments & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) - [Review Apps - Use dynamic environments to deploy your code for every branch](review_apps/index.md) -- [Deploy Boards for your applications running on Kubernetes](../user/project/deploy_boards.md) **[PREMIUM]** +- [Deploy Boards for your applications running on Kubernetes](../user/project/deploy_boards.md) **(PREMIUM)** <!-- ## Troubleshooting diff --git a/doc/ci/environments/protected_environments.md b/doc/ci/environments/protected_environments.md index b72ebe838b8..e5213881862 100644 --- a/doc/ci/environments/protected_environments.md +++ b/doc/ci/environments/protected_environments.md @@ -2,7 +2,7 @@ type: concepts, howto --- -# Protected Environments **[PREMIUM]** +# Protected Environments **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6303) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.3. diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index 2b4fe321cb3..5a302392c54 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -42,10 +42,10 @@ The following table lists examples with step-by-step tutorials that are containe Contributions are welcome! You can help your favorite programming language users and GitLab by sending a merge request with a guide for that language. -You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/) +You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community/writers/) to get paid for writing complete articles for GitLab. -## Adding templates to your GitLab installation **[PREMIUM ONLY]** +## Adding templates to your GitLab installation **(PREMIUM ONLY)** If you want to have customized examples and templates for your own self-managed GitLab instance available to your team, your GitLab administrator can [designate an instance template repository](../../user/admin_area/settings/instance_template_repository.md) that contains examples and templates specific to your enterprise. diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md index 2117b342903..c9f700ed190 100644 --- a/doc/ci/examples/artifactory_and_gitlab/index.md +++ b/doc/ci/examples/artifactory_and_gitlab/index.md @@ -13,7 +13,7 @@ date: 2017-08-15 ## Introduction In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/product/continuous-integration/) -to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory](https://www.jfrog.com/artifactory/), and then use it from another Maven application as a dependency. +to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory](https://jfrog.com/artifactory/), and then use it from another Maven application as a dependency. You'll create two different projects: diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md index 538843ab8dc..1d4c9221cf2 100644 --- a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md +++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md @@ -79,10 +79,10 @@ image: java:8 stages: - build - deploy - + before_script: - chmod +x mvnw - + build: stage: build script: ./mvnw package diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md index 7f1beb96bbf..38fcf05f519 100644 --- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md +++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md @@ -32,7 +32,7 @@ through the process of setting up GitLab CI/CD for end-to-end testing Javascript with WebdriverIO, but the general strategy should carry over to other languages. We assume you are familiar with GitLab, [GitLab CI/CD](../../README.md), [Review Apps](../../review_apps/index.md), and running your app locally, e.g., on `localhost:8000`. -### What to test +## What to test In the widely-used [testing pyramid strategy](https://martinfowler.com/bliki/TestPyramid.html), end-to-end tests act more like a safeguard: [most of your code should be covered by @@ -40,9 +40,9 @@ unit tests](https://vincenttunru.com/100-percent-coverage/) that allow you to ea will likely want to [limit the number of end-to-end tests](https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html) to just enough to give you the confidence that the deployment went as intended, that your -infrastructure is up and running, and that your units of code work well together. +infrastructure is up and running, and that your units of code work well together. -### Selenium and WebdriverIO +## Selenium and WebdriverIO [Selenium](http://www.seleniumhq.org/) is a piece of software that can control web browsers, e.g., to make them visit a specific URL or interact with elements on the page. It can be programmatically controlled @@ -65,7 +65,7 @@ describe('A visitor without account', function(){ expect(browser.getUrl()).toMatch('page-that-does-not-exist'); browser.element('.content a[href="/"]').click(); - + expect(browser.getUrl()).not.toMatch('page-that-does-not-exist'); }); }); diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md index d7308a3a5ec..1576efd5a7d 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md @@ -452,6 +452,7 @@ To start using Container Registry on our machine, we first need to login to the ```bash docker login registry.gitlab.com ``` + Then we can build and push our image to GitLab: ```bash diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md index c459bb7001f..1dd3049d53d 100644 --- a/doc/ci/examples/php.md +++ b/doc/ci/examples/php.md @@ -6,7 +6,7 @@ type: tutorial This guide covers basic building instructions for PHP projects. -Two testing scenarios are covered: using the Docker executor and +Two testing scenarios are covered: using the Docker executor and using the Shell executor. ## Test PHP projects using the Docker executor @@ -64,7 +64,7 @@ docker-php-ext-install pdo_mysql You might wonder what `docker-php-ext-install` is. In short, it is a script provided by the official php docker image that you can use to easily install extensions. For more information read the documentation at -<https://hub.docker.com/r/_/php/>. +<https://hub.docker.com/_/php>. Now that we created the script that contains all prerequisites for our build environment, let's add it in `.gitlab-ci.yml`: @@ -96,7 +96,7 @@ Finally, commit your files and push them to GitLab to see your build succeeding The final `.gitlab-ci.yml` should look similar to this: ```yaml -# Select image from https://hub.docker.com/r/_/php/ +# Select image from https://hub.docker.com/_/php image: php:5.6 before_script: @@ -286,7 +286,7 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available Want to hack on it? Simply fork it, commit, and push your changes. Within a few moments the changes will be picked by a public runner and the job will begin. -[php-hub]: https://hub.docker.com/r/_/php/ +[php-hub]: https://hub.docker.com/_/php [phpenv]: https://github.com/phpenv/phpenv [phpenv-installation]: https://github.com/phpenv/phpenv#installation [php-example-repo]: https://gitlab.com/gitlab-examples/php diff --git a/doc/ci/examples/test-clojure-application.md b/doc/ci/examples/test-clojure-application.md index 5cda8702b56..6ea38f22bca 100644 --- a/doc/ci/examples/test-clojure-application.md +++ b/doc/ci/examples/test-clojure-application.md @@ -35,7 +35,7 @@ test: - lein test ``` -In `before_script`, we install JRE and [Leiningen](http://leiningen.org/). +In `before_script`, we install JRE and [Leiningen](https://leiningen.org/). The sample project uses the [migratus](https://github.com/yogthos/migratus) library to manage database migrations, and we have added a database migration as the last step of `before_script`. diff --git a/doc/ci/examples/test-scala-application.md b/doc/ci/examples/test-scala-application.md index bd899240307..7d039ab1aeb 100644 --- a/doc/ci/examples/test-scala-application.md +++ b/doc/ci/examples/test-scala-application.md @@ -46,7 +46,7 @@ deploy: In the above configuration: -- The `before_script` installs [SBT](http://www.scala-sbt.org/) and +- The `before_script` installs [SBT](https://www.scala-sbt.org/) and displays the version that is being used. - The `test` stage executes SBT to compile and test the project. - [sbt-scoverage](https://github.com/scoverage/sbt-scoverage) is used as an SBT diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md index ec25ca1bfc3..a5fed00972f 100644 --- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md @@ -404,14 +404,14 @@ other reasons][ci-reasons] to keep using GitLab CI/CD. The benefits to our teams [phoenix-learning-guide]: https://hexdocs.pm/phoenix/learning.html "Phoenix Learning Guide" [phoenix-install]: https://hexdocs.pm/phoenix/installation.html "Phoenix Installation" [phoenix-mysql]: https://hexdocs.pm/phoenix/ecto.html#using-mysql "Phoenix with MySQL" -[elixir-site]: http://elixir-lang.org/ "Elixir" -[elixir-mix]: http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html "Introduction to mix" -[elixir-docs]: http://elixir-lang.org/getting-started/introduction.html "Elixir Documentation" +[elixir-site]: https://elixir-lang.org/ "Elixir" +[elixir-mix]: https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html "Introduction to mix" +[elixir-docs]: https://elixir-lang.org/getting-started/introduction.html "Elixir Documentation" [elixir-install]: https://elixir-lang.org/install.html "Elixir Installation" -[ecto]: http://hexdocs.pm/ecto "Ecto" +[ecto]: https://hexdocs.pm/ecto/Ecto.html "Ecto" [ecto-repo]: https://hexdocs.pm/ecto/Ecto.html#module-repositories "Ecto Repositories" [mix-ecto]: https://hexdocs.pm/ecto/Mix.Tasks.Ecto.Create.html "mix and Ecto" -[iex]: http://elixir-lang.org/getting-started/introduction.html#interactive-mode "Interactive Mode" +[iex]: https://elixir-lang.org/getting-started/introduction.html#interactive-mode "Interactive Mode" [ci-lint]: https://gitlab.com/ci/lint "CI Lint Tool" [ci-reasons]: https://about.gitlab.com/2015/02/03/7-reasons-why-you-should-be-using-ci/ "7 Reasons Why You Should Be Using CI" [ci-guide]: https://about.gitlab.com/2015/12/14/getting-started-with-gitlab-and-gitlab-ci/ "Getting Started With GitLab And GitLab CI/CD" diff --git a/doc/ci/git_submodules.md b/doc/ci/git_submodules.md index 551044dd76f..cce33c7a6b4 100644 --- a/doc/ci/git_submodules.md +++ b/doc/ci/git_submodules.md @@ -69,24 +69,26 @@ correctly with your CI jobs: 1. Next, if you are using `gitlab-runner` v1.10+, you can set the `GIT_SUBMODULE_STRATEGY` variable to either `normal` or `recursive` to tell the runner to fetch your submodules before the job: - ```yaml - variables: - GIT_SUBMODULE_STRATEGY: recursive - ``` - See the [`.gitlab-ci.yml` reference](yaml/README.md#git-submodule-strategy) - for more details about `GIT_SUBMODULE_STRATEGY`. + + ```yaml + variables: + GIT_SUBMODULE_STRATEGY: recursive + ``` + + See the [`.gitlab-ci.yml` reference](yaml/README.md#git-submodule-strategy) + for more details about `GIT_SUBMODULE_STRATEGY`. 1. If you are using an older version of `gitlab-runner`, then use `git submodule sync/update` in `before_script`: - ```yaml - before_script: - - git submodule sync --recursive - - git submodule update --init --recursive - ``` + ```yaml + before_script: + - git submodule sync --recursive + - git submodule update --init --recursive + ``` - `--recursive` should be used in either both or none (`sync/update`) depending on - whether you have recursive submodules. + `--recursive` should be used in either both or none (`sync/update`) depending on + whether you have recursive submodules. The rationale to set the `sync` and `update` in `before_script` is because of the way Git submodules work. On a fresh Runner workspace, Git will set the diff --git a/doc/ci/interactive_web_terminal/index.md b/doc/ci/interactive_web_terminal/index.md index 1387d4df500..58307660e51 100644 --- a/doc/ci/interactive_web_terminal/index.md +++ b/doc/ci/interactive_web_terminal/index.md @@ -8,7 +8,7 @@ type: reference Interactive web terminals give the user access to a terminal in GitLab for running one-off commands for their CI pipeline. Since this is giving the user -shell access to the environment where [GitLab Runner](https://docs.gitlab.com/runner/) +shell access to the environment where [GitLab Runner](https://docs.gitlab.com/runner/) is deployed, some [security precautions](../../administration/integration/terminal.md#security) were taken to protect the users. @@ -59,7 +59,6 @@ close the terminal window. ![finished job with terminal open](img/finished_job_with_terminal_open.png) -## Interactive Web Terminals for the Web IDE **[ULTIMATE ONLY]** +## Interactive Web Terminals for the Web IDE **(ULTIMATE ONLY)** Read the Web IDE docs to learn how to run [Interactive Terminals through the Web IDE](../../user/project/web_ide/index.md). - diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md index ef9f9a9973c..fc89f0fc94f 100644 --- a/doc/ci/introduction/index.md +++ b/doc/ci/introduction/index.md @@ -177,22 +177,22 @@ according to each stage (Verify, Package, Release). 1. **Verify**: - Automatically build and test your application with Continuous Integration. - - Analyze your source code quality with [GitLab Code Quality](../../user/project/merge_requests/code_quality.md). **[STARTER]** - - Determine the performance impact of code changes with [Browser Performance Testing](../../user/project/merge_requests/browser_performance_testing.md). **[PREMIUM]** - - Perform a series of tests, such as [Container Scanning](../../user/application_security/container_scanning/index.md) **[ULTIMATE]**, [Dependency Scanning](../../user/application_security/dependency_scanning/index.md) **[ULTIMATE]**, and [JUnit tests](../junit_test_reports.md). + - Analyze your source code quality with [GitLab Code Quality](../../user/project/merge_requests/code_quality.md). **(STARTER)** + - Determine the performance impact of code changes with [Browser Performance Testing](../../user/project/merge_requests/browser_performance_testing.md). **(PREMIUM)** + - Perform a series of tests, such as [Container Scanning](../../user/application_security/container_scanning/index.md) **(ULTIMATE)**, [Dependency Scanning](../../user/application_security/dependency_scanning/index.md) **(ULTIMATE)**, and [JUnit tests](../junit_test_reports.md). - Deploy your changes with [Review Apps](../review_apps/index.md) to preview the app changes on every branch. 1. **Package**: - Store Docker images with [Container Registry](../../user/project/container_registry.md). - - Store NPM packages with [NPM Registry](../../user/project/packages/npm_registry.md). **[PREMIUM]** - - Store Maven artifacts with [Maven Repository](../../user/project/packages/maven_repository.md). **[PREMIUM]** + - Store NPM packages with [NPM Registry](../../user/project/packages/npm_registry.md). **(PREMIUM)** + - Store Maven artifacts with [Maven Repository](../../user/project/packages/maven_repository.md). **(PREMIUM)** 1. **Release**: - Continuous Deployment, automatically deploying your app to production. - Continuous Delivery, manually click to deploy your app to production. - Deploy static websites with [GitLab Pages](../../user/project/pages/index.md). - - Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](../../user/project/canary_deployments.md). **[PREMIUM]** - - Deploy your features behind [Feature Flags](../../user/project/operations/feature_flags.md). **[PREMIUM]** + - Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](../../user/project/canary_deployments.md). **(PREMIUM)** + - Deploy your features behind [Feature Flags](../../user/project/operations/feature_flags.md). **(PREMIUM)** - Add release notes to any Git tag with [GitLab Releases](../../user/project/releases/index.md). - - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md). **[PREMIUM]** + - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md). **(PREMIUM)** - Deploy your application to a production environment in a Kubernetes cluster with [Auto Deploy](../../topics/autodevops/index.md#auto-deploy). With GitLab CI/CD you can also: @@ -201,7 +201,7 @@ With GitLab CI/CD you can also: - Deploy your app to different [environments](../environments.md). - Install your own [GitLab Runner](https://docs.gitlab.com/runner/). - [Schedule pipelines](../../user/project/pipelines/schedules.md). -- Check for app vulnerabilities with [Security Test reports](../../user/project/merge_requests/index.md#security-reports-ultimate). **[ULTIMATE]** +- Check for app vulnerabilities with [Security Test reports](../../user/project/merge_requests/index.md#security-reports-ultimate). **(ULTIMATE)** To see all CI/CD features, navigate back to the [CI/CD index](../README.md). diff --git a/doc/ci/merge_request_pipelines/index.md b/doc/ci/merge_request_pipelines/index.md index e70ae0bd154..72a9a876037 100644 --- a/doc/ci/merge_request_pipelines/index.md +++ b/doc/ci/merge_request_pipelines/index.md @@ -76,11 +76,11 @@ when a merge request was created or updated. For example: ![Merge request page](img/merge_request.png) -## Pipelines for Merged Results **[PREMIUM]** +## Pipelines for Merged Results **(PREMIUM)** Read the [documentation on Pipelines for Merged Results](pipelines_for_merged_results/index.md). -### Merge Trains **[PREMIUM]** +### Merge Trains **(PREMIUM)** Read the [documentation on Merge Trains](pipelines_for_merged_results/merge_trains/index.md). diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md index 3c5088089fa..a13857bee25 100644 --- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md +++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md @@ -3,7 +3,7 @@ type: reference last_update: 2019-07-03 --- -# Pipelines for Merged Results **[PREMIUM]** +# Pipelines for Merged Results **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7380) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10. > This feature is disabled by default until we resolve issues with [contention handling](https://gitlab.com/gitlab-org/gitlab-ee/issues/11222), but [can be enabled manually](#enabling-pipelines-for-merged-results). @@ -61,7 +61,7 @@ CAUTION: **Warning:** Make sure your `gitlab-ci.yml` file is [configured properly for pipelines for merge requests](../index.md#configuring-pipelines-for-merge-requests), otherwise pipelines for merged results won't run and your merge requests will be stuck in an unresolved state. -## Merge Trains **[PREMIUM]** +## Merge Trains **(PREMIUM)** Read the [documentation on Merge Trains](merge_trains/index.md). diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md index c5ff6f9ebed..57358434c02 100644 --- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md +++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md @@ -3,7 +3,7 @@ type: reference last_update: 2019-07-03 --- -# Merge Trains **[PREMIUM]** +# Merge Trains **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9186) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.0. > This feature is disabled by default, but [can be enabled manually](#enabling-merge-trains). diff --git a/doc/ci/metrics_reports.md b/doc/ci/metrics_reports.md index f9cfc0892a7..4d3f5a143f7 100644 --- a/doc/ci/metrics_reports.md +++ b/doc/ci/metrics_reports.md @@ -2,9 +2,9 @@ type: reference --- -# Metrics Reports **[PREMIUM]** +# Metrics Reports **(PREMIUM)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9788) in [GitLab Premium](https://about.gitlab.com/pricing) 11.10. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9788) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10. Requires GitLab Runner 11.10 and above. ## Overview diff --git a/doc/ci/multi_project_pipelines.md b/doc/ci/multi_project_pipelines.md index 50c8d82602b..463b9194c58 100644 --- a/doc/ci/multi_project_pipelines.md +++ b/doc/ci/multi_project_pipelines.md @@ -2,7 +2,7 @@ type: reference --- -# Multi-project pipelines **[PREMIUM]** +# Multi-project pipelines **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/2121) in [GitLab Premium 9.3](https://about.gitlab.com/2017/06/22/gitlab-9-3-released/#multi-project-pipeline-graphs). diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index 4a07aa31f8a..06a81c3d0c7 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -234,7 +234,7 @@ Pipeline status and test coverage report badges are available and configurable f For information on adding pipeline badges to projects, see [Pipeline badges](../user/project/pipelines/settings.md#pipeline-badges). -## Multi-project pipelines **[PREMIUM]** +## Multi-project pipelines **(PREMIUM)** Pipelines for different projects can be combined and visualized together. diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 11bcfd5dc2c..0480b83d183 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -233,7 +233,7 @@ CI with various languages. [runner-install]: https://docs.gitlab.com/runner/install/ [blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/ [examples]: ../examples/README.md -[ci]: https://about.gitlab.com/gitlab-ci/ +[ci]: https://about.gitlab.com/product/continuous-integration/ [yaml]: ../yaml/README.md [runner]: ../runners/README.md [enabled]: ../enable_or_disable_ci.md diff --git a/doc/ci/review_apps/img/toolbar_feeback_form.png b/doc/ci/review_apps/img/toolbar_feeback_form.png Binary files differindex d147981a387..fe1c7e6e611 100644 --- a/doc/ci/review_apps/img/toolbar_feeback_form.png +++ b/doc/ci/review_apps/img/toolbar_feeback_form.png diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index 70934e074a0..9b89988bf42 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -27,7 +27,7 @@ In the above example: - Once the review as passed, `topic branch` is merged into `master` where it's deploy to staging. - After been approved in staging, the changes that were merged into `master` are deployed in to production. -### How Review Apps work +## How Review Apps work A Review App is a mapping of a branch with an [environment](../environments.md). Access to the Review App is made available as a link on the [merge request](../../user/project/merge_requests.md) relevant to the branch. @@ -41,27 +41,34 @@ In this example, a branch was: - Successfully built. - Deployed under a dynamic environment that can be reached by clicking on the **View app** button. +After adding Review Apps to your workflow, you follow the branched Git flow. That is: + +1. Push a branch and let the Runner deploy the Review App based on the `script` definition of the dynamic environment job. +1. Wait for the Runner to build and deploy your web application. +1. Click on the link provided in the merge request related to the branch to see the changes live. + ## Configuring Review Apps Review Apps are built on [dynamic environments](../environments.md#configuring-dynamic-environments), which allow you to dynamically create a new environment for each branch. The process of configuring Review Apps is as follows: -1. Set up the infrastructure to host and deploy the Review Apps. +1. Set up the infrastructure to host and deploy the Review Apps (check the [examples](#review-apps-examples) below). 1. [Install](https://docs.gitlab.com/runner/install/) and [configure](https://docs.gitlab.com/runner/commands/) a Runner to do deployment. 1. Set up a job in `.gitlab-ci.yml` that uses the [predefined CI environment variable](../variables/README.md) `${CI_COMMIT_REF_NAME}` to create dynamic environments and restrict it to run only on branches. 1. Optionally, set a job that [manually stops](../environments.md#stopping-an-environment) the Review Apps. -### Examples +## Review Apps examples The following are example projects that demonstrate Review App configuration: - [NGINX](https://gitlab.com/gitlab-examples/review-apps-nginx). - [OpenShift](https://gitlab.com/gitlab-examples/review-apps-openshift). +<i class="fa fa-youtube-play youtube" aria-hidden="true"></i> See also the video [Demo: Cloud Native Development with GitLab](https://www.youtube.com/watch?v=jfIyQEwrocw), which includes a Review Apps example. -### Route Maps +## Route Maps > Introduced in GitLab 8.17. In GitLab 11.5, the file links are available in the merge request widget. @@ -82,7 +89,7 @@ To set up a route map, add a a file inside the repository at `.gitlab/route-map. which contains a YAML array that maps `source` paths (in the repository) to `public` paths (on the website). -#### Route Maps example +### Route Maps example The following is an example of a route map for [Middleman](https://middlemanapp.com), a static site generator (SSG) used to build [GitLab's website](https://about.gitlab.com), @@ -146,51 +153,102 @@ Once you have the route mapping set up, it will take effect in the following loc !["View on env" button in file view](img/view_on_env_blob.png) -## Working with Review Apps - -After adding Review Apps to your workflow, you follow the branched Git flow. That is: - -1. Push a branch and let the Runner deploy the Review App based on the `script` definition of the dynamic environment job. -1. Wait for the Runner to build and deploy your web application. -1. Click on the link that provided in the merge request related to the branch to see the changes live. - -### Visual Reviews **[STARTER]** +## Visual Reviews **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/10761) in GitLab Starter 12.0. -The Visual Reviews feedback form can be added to a Review App to enable reviewers to post comments -directly from the app back to the merge request that spawned the Review App. +With Visual Reviews, you can provide a feedback form to your Review Apps so +that reviewers can post comments directly from the app back to the merge request +that spawned the Review App. -For example, a form like the following can be configured to post the contents of the -text field into the discussion thread of a merge request: +### Configuring Visual Reviews -![feedback form](img/toolbar_feeback_form.png) +The feedback form is served through a script you add to pages in your Review App. +If you have [Developer permissions](../../user/permissions.md) to the project, +you can access it by clicking the **Review** button in the **Pipeline** section +of the merge request. -#### Using Visual Reviews +![review button](img/review_button.png) -If Visual Reviews has been [enabled](#configuring-visual-reviews) for the Review App, the Visual Reviews feedback form is overlaid on the app's pages at the bottom-right corner. +The provided script should be added to the `<head>` of you application and +consists of some project and merge request specific values. Here's what it +looks like: + +```html +<script + data-project-id='11790219' + data-merge-request-id='1' + data-mr-url='https://gitlab.example.com' + data-project-path='sarah/review-app-tester' + id='review-app-toolbar-script' + src='https://gitlab.example.com/assets/webpack/visual_review_toolbar.js'> +</script> +``` -To use the feedback form, you will need to create a [personal access token](../../user/profile/personal_access_tokens.md) with the API scope selected. +Ideally, you should use [environment variables](../variables/predefined_variables.md) +to replace those values at runtime when each review app is created: + +- `data-project-id` is the project ID, which can be found by the `CI_PROJECT_ID` + variable. +- `data-merge-request-id` is the merge request ID, which can be found by the + `CI_MERGE_REQUEST_IID` variable. `CI_MERGE_REQUEST_IID` is available only if + [`only: [merge_requests]`](../merge_request_pipelines/index.md) + is used and the merge request is created. +- `data-mr-url` is the URL of the GitLab instance and will be the same for all + review apps. +- `data-project-path` is the project's path, which can be found by `CI_PROJECT_PATH`. +- `id` is always `review-app-toolbar-script`, you don't need to change that. +- `src` is the source of the review toolbar script, which resides in the + respective GitLab instance and will be the same for all review apps. + +For example, in a Ruby application, you would need to have this script: + +```html +<script + data-project-id="ENV['CI_PROJECT_ID']" + data-merge-request-id="ENV['CI_MERGE_REQUEST_IID']" + data-mr-url='https://gitlab.example.com' + data-project-path="ENV['CI_PROJECT_PATH']" + id='review-app-toolbar-script' + src='https://gitlab.example.com/assets/webpack/visual_review_toolbar.js'> +</script> +``` -Paste the token into the feedback box, when prompted. If you select **Remember me**, your browser stores the token so that future visits to Review Apps at the same URL will not require you to re-enter the token. To clear the token, click **Log out**. +Then, when your app is deployed via GitLab CI/CD, those variables should get +replaced with their real values. -Because tokens must be entered on a per-domain basis and they can only be accessed once, you can save the token to your password manager specifically for the purpose of Visual Reviews. This way, you will not need to create additional tokens for each merge request. +NOTE: **Note:** +Future enhancements [are planned](https://gitlab.com/gitlab-org/gitlab-ee/issues/11322) +to make this process even easier. -Comments can make use of all the [Markdown annotations](../../user/markdown.md) -available in merge request comment boxes. +### Using Visual Reviews -#### Configuring Visual Reviews +After Visual Reviews has been [enabled](#configuring-visual-reviews) for the +Review App, the Visual Reviews feedback form is overlaid on the app's pages at +the bottom-right corner. -The feedback form is served through a script you add to pages in your Review App. -To access the code to include the script, click the **Review** button in the **Pipeline** section of the merge request. +![Visual review feedback form](img/toolbar_feeback_form.png) -![review button](img/review_button.png) +To use the feedback form: + +1. Create a [personal access token](../../user/profile/personal_access_tokens.md) + with the API scope selected. +1. Paste the token into the feedback box when prompted. If you select **Remember me**, + your browser stores the token so that future visits to Review Apps at the same URL + will not require you to re-enter the token. To clear the token, click **Log out**. +1. Make a comment on the visual review. You can make use of all the + [Markdown annotations](../../user/markdown.md) that are also available in + merge request comments. +1. Finally, click **Send feedback**. -The provided script hardcodes the project and merge request IDs. You may want to consider -using features of your programming language to use environment variables or other -means to inject these at runtime. +After you make and submit a comment in the visual review box, it will appear +automatically in the respective merge request. -Future enhancements [are planned](https://gitlab.com/gitlab-org/gitlab-ee/issues/11322) to make this process even easier. +TIP: **Tip:** +Because tokens must be entered on a per-domain basis and they can only be accessed +once, different review apps will not remember your token. You can save the token +to your password manager specifically for the purpose of Visual Reviews. This way, +you will not need to create additional tokens for each merge request. ## Limitations diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md index 211eea26eb0..b72dd6e920a 100644 --- a/doc/ci/services/postgres.md +++ b/doc/ci/services/postgres.md @@ -70,7 +70,7 @@ template1=# CREATE USER runner WITH PASSWORD '$password' CREATEDB; ``` *__Note:__ Notice that we created the user with the privilege to be able to -create databases (`CREATEDB`). In the following steps we will create a database +create databases (`CREATEDB`). In the following steps we will create a database explicitly for that user but having that privilege can be useful if in your testing framework you have tools that drop and create databases.* @@ -114,5 +114,5 @@ available [shared runners](../runners/README.md). Want to hack on it? Simply fork it, commit and push your changes. Within a few moments the changes will be picked by a public runner and the job will begin. -[hub-pg]: https://hub.docker.com/r/_/postgres/ +[hub-pg]: https://hub.docker.com/_/postgres [postgres-example-repo]: https://gitlab.com/gitlab-examples/postgres diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md index 04c541fefe7..d1f9aa03b6b 100644 --- a/doc/ci/triggers/README.md +++ b/doc/ci/triggers/README.md @@ -32,7 +32,7 @@ to protect trigger tokens. You can use the `CI_JOB_TOKEN` [variable][predef] (used to authenticate with the [GitLab Container Registry][registry]) in the following cases. -#### When used with multi-project pipelines **[PREMIUM]** +#### When used with multi-project pipelines **(PREMIUM)** > **Note**: The use of `CI_JOB_TOKEN` for multi-project pipelines was [introduced][ee-2017] @@ -56,7 +56,7 @@ Pipelines triggered that way also expose a special variable: Read more about the [pipelines trigger API][trigapi]. -#### When a pipeline depends on the artifacts of another pipeline **[PREMIUM]** +#### When a pipeline depends on the artifacts of another pipeline **(PREMIUM)** > The use of `CI_JOB_TOKEN` in the artifacts download API was [introduced][ee-2346] in [GitLab Premium][ee] 9.5. diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 1b50273eca2..4d6ca8cff6d 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -273,6 +273,7 @@ export CI_RUNNER_ID="10" export CI_RUNNER_DESCRIPTION="my runner" export CI_RUNNER_TAGS="docker, linux" export CI_SERVER="yes" +export CI_SERVER_HOST="example.com" export CI_SERVER_NAME="GitLab" export CI_SERVER_REVISION="70606bf" export CI_SERVER_VERSION="8.9.0" @@ -389,7 +390,7 @@ Protected variables can be added by going to your project's Once you set them, they will be available for all subsequent pipelines. -### Limiting environment scopes of environment variables **[PREMIUM]** +### Limiting environment scopes of environment variables **(PREMIUM)** You can limit the environment scope of a variable by [defining which environments][envs] it can be available for. @@ -644,6 +645,8 @@ Running on runner-8a2f473d-project-1796893-concurrent-0 via runner-8a2f473d-mach ++ CI_PROJECT_DIR=/builds/gitlab-examples/ci-debug-trace ++ export CI_SERVER=yes ++ CI_SERVER=yes +++ export 'CI_SERVER_HOST=example.com' +++ CI_SERVER_HOST='example.com' ++ export 'CI_SERVER_NAME=GitLab CI' ++ CI_SERVER_NAME='GitLab CI' ++ export CI_SERVER_VERSION= @@ -678,6 +681,8 @@ Running on runner-8a2f473d-project-1796893-concurrent-0 via runner-8a2f473d-mach ++ CI_JOB_NAME=debug_trace ++ export CI_JOB_STAGE=test ++ CI_JOB_STAGE=test +++ export CI_SERVER_HOST=example.com +++ CI_SERVER_HOST=example.com ++ export CI_SERVER_NAME=GitLab ++ CI_SERVER_NAME=GitLab ++ export CI_SERVER_VERSION=8.14.3-ee diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md index e911e97d3c8..49543c57886 100644 --- a/doc/ci/variables/predefined_variables.md +++ b/doc/ci/variables/predefined_variables.md @@ -101,6 +101,7 @@ future GitLab releases.** | `CI_RUNNER_TAGS` | 8.10 | 0.5 | The defined runner tags | | `CI_RUNNER_VERSION` | all | 10.6 | GitLab Runner version that is executing the current job | | `CI_SERVER` | all | all | Mark that job is executed in CI environment | +| `CI_SERVER_HOST` | 12.1 | all | Host component of the GitLab instance URL, without protocol and port (like gitlab.example.com) | | `CI_SERVER_NAME` | all | all | The name of CI server that is used to coordinate jobs | | `CI_SERVER_REVISION` | all | all | GitLab revision that is used to schedule jobs | | `CI_SERVER_VERSION` | all | all | GitLab version that is used to schedule jobs | diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 3e564e4244c..c2ef58acf15 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1057,7 +1057,7 @@ globally and all jobs will use that definition. #### `cache:paths` Use the `paths` directive to choose which files or directories will be cached. -Wildcards can be used as well. +Wildcards can be used that follow the [glob](https://en.wikipedia.org/wiki/Glob_(programming)) patterns and [filepath.Match](https://golang.org/pkg/path/filepath/#Match). Cache all files in `binaries` that end in `.apk` and the `.config` file: @@ -1219,8 +1219,10 @@ be available for download in the GitLab UI. #### `artifacts:paths` -You can only use paths that are within the project workspace. To pass artifacts -between different jobs, see [dependencies](#dependencies). +You can only use paths that are within the project workspace. +Wildcards can be used that follow the [glob](https://en.wikipedia.org/wiki/Glob_(programming)) patterns and [filepath.Match](https://golang.org/pkg/path/filepath/#Match). + +To pass artifacts between different jobs, see [dependencies](#dependencies). Send all files in `binaries` and `.config`: @@ -1475,7 +1477,7 @@ concatenated into a single file. Use a filename pattern (`junit: rspec-*.xml`), an array of filenames (`junit: [rspec-1.xml, rspec-2.xml, rspec-3.xml]`), or a combination thereof (`junit: [rspec.xml, test-results/TEST-*.xml]`). -##### `artifacts:reports:codequality` **[STARTER]** +##### `artifacts:reports:codequality` **(STARTER)** > Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above. @@ -1485,7 +1487,7 @@ as artifacts. The collected Code Quality report will be uploaded to GitLab as an artifact and will be automatically shown in merge requests. -##### `artifacts:reports:sast` **[ULTIMATE]** +##### `artifacts:reports:sast` **(ULTIMATE)** > Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above. @@ -1496,7 +1498,7 @@ The collected SAST report will be uploaded to GitLab as an artifact and will be automatically shown in merge requests, pipeline view and provide data for security dashboards. -##### `artifacts:reports:dependency_scanning` **[ULTIMATE]** +##### `artifacts:reports:dependency_scanning` **(ULTIMATE)** > Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above. @@ -1507,7 +1509,7 @@ The collected Dependency Scanning report will be uploaded to GitLab as an artifa be automatically shown in merge requests, pipeline view and provide data for security dashboards. -##### `artifacts:reports:container_scanning` **[ULTIMATE]** +##### `artifacts:reports:container_scanning` **(ULTIMATE)** > Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above. @@ -1518,7 +1520,7 @@ The collected Container Scanning report will be uploaded to GitLab as an artifac be automatically shown in merge requests, pipeline view and provide data for security dashboards. -##### `artifacts:reports:dast` **[ULTIMATE]** +##### `artifacts:reports:dast` **(ULTIMATE)** > Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above. @@ -1529,7 +1531,7 @@ The collected DAST report will be uploaded to GitLab as an artifact and will be automatically shown in merge requests, pipeline view and provide data for security dashboards. -##### `artifacts:reports:license_management` **[ULTIMATE]** +##### `artifacts:reports:license_management` **(ULTIMATE)** > Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above. @@ -1540,7 +1542,7 @@ The collected License Management report will be uploaded to GitLab as an artifac be automatically shown in merge requests, pipeline view and provide data for security dashboards. -##### `artifacts:reports:performance` **[PREMIUM]** +##### `artifacts:reports:performance` **(PREMIUM)** > Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above. @@ -1550,7 +1552,7 @@ as artifacts. The collected Performance report will be uploaded to GitLab as an artifact and will be automatically shown in merge requests. -##### `artifacts:reports:metrics` **[PREMIUM]** +##### `artifacts:reports:metrics` **(PREMIUM)** > Introduced in GitLab 11.10. @@ -1747,7 +1749,7 @@ test: parallel: 5 ``` -### `trigger` **[PREMIUM]** +### `trigger` **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/8997) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8. @@ -2827,7 +2829,8 @@ Alternatively, one can pass the `ci.skip` [Git push option][push-option] if using Git 2.10 or newer: ```sh -git push -o ci.skip +git push --push-option=ci.skip # using git 2.10+ +git push -o ci.skip # using git 2.18+ ``` <!-- ## Troubleshooting diff --git a/doc/customization/index.md b/doc/customization/index.md index 71e87b3f111..0198059297f 100644 --- a/doc/customization/index.md +++ b/doc/customization/index.md @@ -2,7 +2,7 @@ description: Learn how to customize GitLab's appearance for self-managed installations. --- -# Customizing GitLab's appearance **[CORE ONLY]** +# Customizing GitLab's appearance **(CORE ONLY)** For GitLab self-managed instances, it's possible to customize a few pages. diff --git a/doc/customization/libravatar.md b/doc/customization/libravatar.md index 18aaeb5a712..e618f3be2fe 100644 --- a/doc/customization/libravatar.md +++ b/doc/customization/libravatar.md @@ -6,12 +6,12 @@ Libravatar is a service which delivers your avatar (profile picture) to other we This means that it is not complicated to switch to Libravatar avatar service or even self hosted Libravatar server. -# Configuration +## Configuration In [gitlab.yml gravatar section](https://gitlab.com/gitlab-org/gitlab-ce/blob/672bd3902d86b78d730cea809fce312ec49d39d7/config/gitlab.yml.example#L122) set the configuration options as follows: -## For HTTP +### For HTTP ```yml gravatar: @@ -20,7 +20,7 @@ the configuration options as follows: plain_url: "http://cdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon" ``` -## For HTTPS +### For HTTPS ```yml gravatar: @@ -29,7 +29,7 @@ the configuration options as follows: ssl_url: "https://seccdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon" ``` -## Self-hosted +### Self-hosted If you are [running your own libravatar service](https://wiki.libravatar.org/running_your_own/) the URL will be different in the configuration but the important part is to provide the same placeholders so GitLab can parse the URL correctly. @@ -38,7 +38,7 @@ For example, you host a service on `http://libravatar.example.com` the `plain_ur `http://libravatar.example.com/avatar/%{hash}?s=%{size}&d=identicon` -## Omnibus-gitlab example +### Omnibus-gitlab example In `/etc/gitlab/gitlab.rb`: @@ -67,7 +67,7 @@ For example, you can use `retro` set in which case the URL would look like: `pla ## Usage examples -#### For Microsoft Office 365 +### For Microsoft Office 365 If your users are Office 365-users, the "GetPersonaPhoto" service can be used. Note that this service requires login, so this use case is most useful in a corporate installation, where all users have access to Office 365. diff --git a/doc/customization/system_header_and_footer_messages.md b/doc/customization/system_header_and_footer_messages.md index 7eee79abc77..15830be4e8a 100644 --- a/doc/customization/system_header_and_footer_messages.md +++ b/doc/customization/system_header_and_footer_messages.md @@ -8,8 +8,8 @@ Navigate to the **Admin** area and go to the **Appearance** page. Under **System header and footer** insert your header message and/or footer message. Both background and font color of the header and footer are customizable. -You can also apply the header and footer messages to gitlab emails, -by checking the **Enable header and footer in emails** checkbox. +You can also apply the header and footer messages to gitlab emails, +by checking the **Enable header and footer in emails** checkbox. Note that color settings will only be applied within the app interface and not to emails ![appearance](system_header_and_footer_messages/appearance.png) diff --git a/doc/development/README.md b/doc/development/README.md index 1566173992a..a74770ae383 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -22,7 +22,7 @@ description: 'Learn how to contribute to GitLab.' - [Security process for developers](https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#security-releases-critical-non-critical-as-a-developer) - [Requesting access to Chatops on GitLab.com](chatops_on_gitlabcom.md#requesting-access) (for GitLabbers) -## UX and frontend guides +## UX and Frontend guides - [GitLab Design System](https://design.gitlab.com/) for building GitLab with existing CSS styles and elements - [Frontend guidelines](fe_guide/index.md) diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md index c83a0427c98..7569ccc04c1 100644 --- a/doc/development/api_graphql_styleguide.md +++ b/doc/development/api_graphql_styleguide.md @@ -424,12 +424,8 @@ Will generate a field called `mergeRequestSetWip` that ### Authorizing resources -To authorize resources inside a mutation, we can include the -`Gitlab::Graphql::Authorize::AuthorizeResource` concern in the -mutation. - -This allows us to provide the required abilities on the mutation like -this: +To authorize resources inside a mutation, we first provide the required + abilities on the mutation like this: ```ruby module Mutations diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index 4fc38a460f8..0866d3baeeb 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -53,7 +53,7 @@ allowed. ### Exclude params from parent namespaces! -> By default `declared(params) `includes parameters that were defined in all +> By default `declared(params)`includes parameters that were defined in all parent namespaces. – <https://github.com/ruby-grape/grape#include-parent-namespaces> diff --git a/doc/development/architecture.md b/doc/development/architecture.md index 8319603fea2..b645a72567c 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -484,9 +484,11 @@ When making a request to an HTTP Endpoint (think `/users/sign_in`) the request w Below we describe the different pathing that HTTP vs. SSH Git requests will take. There is some overlap with the Web Request Cycle but also some differences. ### Web Request (80/443) + TODO ### SSH Request (22) + TODO ## System Layout @@ -505,7 +507,9 @@ To summarize here's the [directory structure of the `git` user home directory](. ### Processes - ps aux | grep '^git' +```sh +ps aux | grep '^git' +``` GitLab has several components to operate. As a system user (i.e. any user that is not the `git` user) it requires a persistent database (MySQL/PostreSQL) and redis database. It also uses Apache httpd or Nginx to proxypass Unicorn. As the `git` user it starts Sidekiq and Unicorn (a simple ruby HTTP server running on port `8080` by default). Under the GitLab user there are normally 4 processes: `unicorn_rails master` (1 process), `unicorn_rails worker` (2 processes), `sidekiq` (1 process). diff --git a/doc/development/changelog.md b/doc/development/changelog.md index 33c1c3bd9e4..bd07a01e782 100644 --- a/doc/development/changelog.md +++ b/doc/development/changelog.md @@ -35,7 +35,7 @@ the `author` field. GitLab team members **should not**. - Any user-facing change **should** have a changelog entry. Example: "GitLab now uses system fonts for all text." -- Any change behind a feature flag **should not** have a changelog entry. The entry should be added [in the merge request removing the feature flags](https://docs.gitlab.com/ee/development/feature_flags.html#developing-with-feature-flags). +- Any change behind a feature flag **should not** have a changelog entry. The entry should be added [in the merge request removing the feature flags](feature_flags/development.md). - A fix for a regression introduced and then fixed in the same release (i.e., fixing a bug introduced during a monthly release candidate) **should not** have a changelog entry. @@ -129,6 +129,7 @@ merge_request: author: type: ``` + If you're working on the GitLab EE repository, the entry will be added to `ee/changelogs/unreleased/` instead. @@ -144,7 +145,7 @@ If you're working on the GitLab EE repository, the entry will be added to | [`--type`](#--type-or--t) | `-t` | The category of the change, valid options are: `added`, `fixed`, `changed`, `deprecated`, `removed`, `security`, `performance`, `other` | | `--help` | `-h` | Print help message | -##### `--amend` +#### `--amend` You can pass the **`--amend`** argument to automatically stage the generated file and amend it to the previous commit. @@ -166,7 +167,7 @@ author: type: ``` -##### `--force` or `-f` +#### `--force` or `-f` Use **`--force`** or **`-f`** to overwrite an existing changelog entry if it already exists. @@ -184,7 +185,7 @@ author: type: ``` -##### `--merge-request` or `-m` +#### `--merge-request` or `-m` Use the **`--merge-request`** or **`-m`** argument to provide the `merge_request` value: @@ -199,7 +200,7 @@ author: type: ``` -##### `--dry-run` or `-n` +#### `--dry-run` or `-n` Use the **`--dry-run`** or **`-n`** argument to prevent actually writing or committing anything: @@ -216,7 +217,7 @@ type: $ ls changelogs/unreleased/ ``` -##### `--git-username` or `-u` +#### `--git-username` or `-u` Use the **`--git-username`** or **`-u`** argument to automatically fill in the `author` value with your configured Git `user.name` value: @@ -234,7 +235,7 @@ author: Jane Doe type: ``` -##### `--type` or `-t` +#### `--type` or `-t` Use the **`--type`** or **`-t`** argument to provide the `type` value: diff --git a/doc/development/chaos_endpoints.md b/doc/development/chaos_endpoints.md index 403a5b21827..b3406275937 100644 --- a/doc/development/chaos_endpoints.md +++ b/doc/development/chaos_endpoints.md @@ -15,23 +15,19 @@ Currently, there are four endpoints for simulating the following conditions: ## Enabling chaos endpoints -For obvious reasons, these endpoints are not enabled by default. They can be enabled by setting the `GITLAB_ENABLE_CHAOS_ENDPOINTS` environment variable to `1`. - -For example, if you're using the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit) this can be done with the following command: - -```bash -GITLAB_ENABLE_CHAOS_ENDPOINTS=1 gdk run -``` - -## Securing the chaos endpoints +For obvious reasons, these endpoints are not enabled by default on `production`. +They are enabled by default on **development** environments. DANGER: **Danger:** -It is highly recommended that you secure access to the chaos endpoints using a secret token. This is recommended when enabling these endpoints locally and essential when running in a staging or other shared environment. You should not enable them in production unless you absolutely know what you're doing. +It is required that you secure access to the chaos endpoints using a secret token. +You should not enable them in production unless you absolutely know what you're doing. -A secret token can be set through the `GITLAB_CHAOS_SECRET` environment variable. For example, when using the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit) this can be done with the following command: +A secret token can be set through the `GITLAB_CHAOS_SECRET` environment variable. +For example, when using the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit) +this can be done with the following command: ```bash -GITLAB_ENABLE_CHAOS_ENDPOINTS=1 GITLAB_CHAOS_SECRET=secret gdk run +GITLAB_CHAOS_SECRET=secret gdk run ``` Replace `secret` with your own secret token. @@ -56,10 +52,11 @@ GET /-/chaos/leakmem?memory_mb=1024&duration_s=50 | Attribute | Type | Required | Description | | ------------ | ------- | -------- | ---------------------------------------------------------------------------------- | | `memory_mb` | integer | no | How much memory, in MB, should be leaked. Defaults to 100MB. | -| `duration_s` | integer | no | Minimum duration, in seconds, that the memory should be retained. Defaults to 30s. | +| `duration_s` | integer | no | Minimum duration_s, in seconds, that the memory should be retained. Defaults to 30s. | ```bash curl http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10 --header 'X-Chaos-Secret: secret' +curl http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10&token=secret ``` ## CPU spin @@ -70,23 +67,47 @@ Depending on your rack server setup, your request may timeout after a predermine If you're using Unicorn, this is done by killing the worker process. ``` -GET /-/chaos/cpuspin -GET /-/chaos/cpuspin?duration_s=50 +GET /-/chaos/cpu_spin +GET /-/chaos/cpu_spin?duration_s=50 +``` + +| Attribute | Type | Required | Description | +| ------------ | ------- | -------- | --------------------------------------------------------------------- | +| `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s | + +```bash +curl http://localhost:3000/-/chaos/cpu_spin?duration_s=60 --header 'X-Chaos-Secret: secret' +curl http://localhost:3000/-/chaos/cpu_spin?duration_s=60&token=secret +``` + +## DB spin + +This endpoint attempts to fully utilise a single core, and interleave it with DB request, for the given period. +This endpoint can be used to model yielding execution to another threads when running concurrently. + +Depending on your rack server setup, your request may timeout after a predermined period (normally 60 seconds). +If you're using Unicorn, this is done by killing the worker process. + +``` +GET /-/chaos/db_spin +GET /-/chaos/db_spin?duration_s=50 ``` | Attribute | Type | Required | Description | | ------------ | ------- | -------- | --------------------------------------------------------------------- | +| `interval_s` | float | no | Interval, in seconds, for every DB request. Defaults to 1s | | `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s | ```bash -curl http://localhost:3000/-/chaos/cpuspin?duration_s=60 --header 'X-Chaos-Secret: secret' +curl http://localhost:3000/-/chaos/db_spin?interval_s=1&duration_s=60 --header 'X-Chaos-Secret: secret' +curl http://localhost:3000/-/chaos/db_spin?interval_s=1&duration_s=60&token=secret ``` ## Sleep -This endpoint is similar to the CPU Spin endpoint but simulates off-processor activity, such as network calls to backend services. It will sleep for a given duration. +This endpoint is similar to the CPU Spin endpoint but simulates off-processor activity, such as network calls to backend services. It will sleep for a given duration_s. -As with the CPU Spin endpoint, this may lead to your request timing out if duration exceeds the configured limit. +As with the CPU Spin endpoint, this may lead to your request timing out if duration_s exceeds the configured limit. ``` GET /-/chaos/sleep @@ -99,6 +120,7 @@ GET /-/chaos/sleep?duration_s=50 ```bash curl http://localhost:3000/-/chaos/sleep?duration_s=60 --header 'X-Chaos-Secret: secret' +curl http://localhost:3000/-/chaos/sleep?duration_s=60&token=secret ``` ## Kill @@ -114,4 +136,5 @@ GET /-/chaos/kill ```bash curl http://localhost:3000/-/chaos/kill --header 'X-Chaos-Secret: secret' +curl http://localhost:3000/-/chaos/kill?token=secret ``` diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md index a7b402c3fb0..3b681880401 100644 --- a/doc/development/chatops_on_gitlabcom.md +++ b/doc/development/chatops_on_gitlabcom.md @@ -13,10 +13,10 @@ tasks such as: To request access to Chatops on GitLab.com: 1. Log into <https://ops.gitlab.net/users/sign_in> using the same username as for GitLab.com. -1. Ask [anyone in the `chatops` project](https://gitlab.com/gitlab-com/chatops/project_members) to add you by running `/chatops run member add <username> gitlab-com/chatops --ops`. +1. Ask [anyone in the `chatops` project](https://gitlab.com/gitlab-com/chatops/-/project_members) to add you by running `/chatops run member add <username> gitlab-com/chatops --ops`. ## See also - - [Chatops Usage](https://docs.gitlab.com/ee/ci/chatops/README.html) - - [Understanding EXPLAIN plans](understanding_explain_plans.md) - - [Feature Groups](feature_flags/development.md#feature-groups) +- [Chatops Usage](../ci/chatops/README.md) +- [Understanding EXPLAIN plans](understanding_explain_plans.md) +- [Feature Groups](feature_flags/development.md#feature-groups) diff --git a/doc/development/code_comments.md b/doc/development/code_comments.md index 36962eb46d4..827a610efa2 100644 --- a/doc/development/code_comments.md +++ b/doc/development/code_comments.md @@ -1,11 +1,11 @@ # Code comments -Whenever you add comment to the code that is expected to be addressed at any time -in future, please create a technical debt issue for it. Then put a link to it +Whenever you add comment to the code that is expected to be addressed at any time +in future, please create a technical debt issue for it. Then put a link to it to the code comment you've created. This will allow other developers to quickly check if a comment is still relevant and what needs to be done to address it. -Examples: +Examples: ```rb # Deprecated scope until code_owner column has been migrated to rule_type. diff --git a/doc/development/code_review.md b/doc/development/code_review.md index 6123f9f845a..e60800f1ab7 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -319,7 +319,7 @@ reviewee. ### GitLab-specific concerns GitLab is used in a lot of places. Many users use -our [Omnibus packages](https://about.gitlab.com/installation/), but some use +our [Omnibus packages](https://about.gitlab.com/install/), but some use the [Docker images](https://docs.gitlab.com/omnibus/docker/), some are [installed from source](../install/installation.md), and there are other installation methods available. GitLab.com itself is a large diff --git a/doc/development/contributing/community_roles.md b/doc/development/contributing/community_roles.md index b9c369286d2..3296cb173d7 100644 --- a/doc/development/contributing/community_roles.md +++ b/doc/development/contributing/community_roles.md @@ -4,8 +4,8 @@ GitLab community members and their privileges/responsibilities. | Roles | Responsibilities | Requirements | |-------|------------------|--------------| -| Maintainer | Accepts merge requests on several GitLab projects | Added to the [team page](https://about.gitlab.com/team/). An expert on code reviews and knows the product/code base | -| Reviewer | Performs code reviews on MRs | Added to the [team page](https://about.gitlab.com/team/) | +| Maintainer | Accepts merge requests on several GitLab projects | Added to the [team page](https://about.gitlab.com/company/team/). An expert on code reviews and knows the product/code base | +| Reviewer | Performs code reviews on MRs | Added to the [team page](https://about.gitlab.com/company/team/) | | Developer |Has access to GitLab internal infrastructure & issues (e.g. HR-related) | GitLab employee or a Core Team member (with an NDA) | | Contributor | Can make contributions to all GitLab public projects | Have a GitLab.com account | diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md index 59cf5014da4..853882e8642 100644 --- a/doc/development/contributing/index.md +++ b/doc/development/contributing/index.md @@ -4,7 +4,7 @@ Thank you for your interest in contributing to GitLab. This guide details how to contribute to GitLab in a way that is easy for everyone. For a first-time step-by-step guide to the contribution process, please see -["Contributing to GitLab"](https://about.gitlab.com/contributing/). +["Contributing to GitLab"](https://about.gitlab.com/community/contribute/). Looking for something to work on? Look for issues with the label [`Accepting merge requests`](#i-want-to-contribute). diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md index 27c349c03aa..d0562cd2bbd 100644 --- a/doc/development/contributing/issue_workflow.md +++ b/doc/development/contributing/issue_workflow.md @@ -1,7 +1,7 @@ # Workflow labels To allow for asynchronous issue handling, we use [milestones][milestones-page] -and [labels][labels-page]. Leads and product managers handle most of the +and [labels](https://gitlab.com/gitlab-org/gitlab-ce/-/labels). Leads and product managers handle most of the scheduling into milestones. Labelling is a task for everyone. Most issues will have labels for at least one of the following: @@ -18,7 +18,7 @@ Most issues will have labels for at least one of the following: - Severity: ~S1, ~S2, ~S3, ~S4 All labels, their meaning and priority are defined on the -[labels page][labels-page]. +[labels page](https://gitlab.com/gitlab-org/gitlab-ce/-/labels). If you come across an issue that has none of these, and you're allowed to set labels, you can _always_ add the team and type, and often also the subject. @@ -38,7 +38,7 @@ makes them float to the top, depending on their importance. Type labels are always lowercase, and can have any color, besides blue (which is already reserved for subject labels). -The descriptions on the [labels page][labels-page] explain what falls under each type label. +The descriptions on the [labels page](https://gitlab.com/gitlab-org/gitlab-ce/-/labels) explain what falls under each type label. ## Subject labels @@ -58,9 +58,9 @@ issue is labeled with a subject label corresponding to your expertise. Subject labels are always all-lowercase. -## Team labels +## Team labels -**Important**: Most of the team labels will be soon deprecated in favor of [Group labels](#group-labels). +**Important**: Most of the team labels will be soon deprecated in favor of [Group labels](#group-labels). Team labels specify what team is responsible for this issue. Assigning a team label makes sure issues get the attention of the appropriate @@ -89,7 +89,7 @@ The following team labels are **true** teams per our [organization structure](ht - ~Delivery - ~Documentation -The descriptions on the [labels page][labels-page] explain what falls under the +The descriptions on the [labels page](https://gitlab.com/gitlab-org/gitlab-ce/-/labels) explain what falls under the responsibility of each team. Within those team labels, we also have the ~backend and ~frontend labels to @@ -98,7 +98,6 @@ indicate if an issue needs backend work, frontend work, or both. Team labels are always capitalized so that they show up as the first label for any issue. - ## Stage labels Stage labels specify which [DevOps stage][devops-stages] the issue belongs to. @@ -141,43 +140,42 @@ Group labels specify which [groups][structure-groups] the issue belongs to. The current group labels are: -* ~"group::access" -* ~"group::measure" -* ~"group::source code" -* ~"group::knowledge" -* ~"group::editor" -* ~"group::gitaly" -* ~"group::gitter" -* ~"group::team planning" -* ~"group::enterprise planning" -* ~"group::certify" -* ~"group::ci and runner" -* ~"group::testing" -* ~"group::package" -* ~"group::progressive delivery" -* ~"group::release management" -* ~"group::autodevops and kubernetes" -* ~"group::serverless and paas" -* ~"group::apm" -* ~"group::health" -* ~"group::static analysis" -* ~"group::dynamic analysis" -* ~"group::software composition analysis" -* ~"group::runtime application security" -* ~"group::threat management" -* ~"group::application infrastructure security" -* ~"group::activation" -* ~"group::adoption" -* ~"group::upsell" -* ~"group::retention" -* ~"group::fulfillment" -* ~"group::telemetry" -* ~"group::distribution" -* ~"group::geo" -* ~"group::memory" -* ~"group::ecosystem" +- ~"group::access" +- ~"group::measure" +- ~"group::source code" +- ~"group::knowledge" +- ~"group::editor" +- ~"group::gitaly" +- ~"group::gitter" +- ~"group::team planning" +- ~"group::enterprise planning" +- ~"group::certify" +- ~"group::ci and runner" +- ~"group::testing" +- ~"group::package" +- ~"group::progressive delivery" +- ~"group::release management" +- ~"group::autodevops and kubernetes" +- ~"group::serverless and paas" +- ~"group::apm" +- ~"group::health" +- ~"group::static analysis" +- ~"group::dynamic analysis" +- ~"group::software composition analysis" +- ~"group::runtime application security" +- ~"group::threat management" +- ~"group::application infrastructure security" +- ~"group::activation" +- ~"group::adoption" +- ~"group::upsell" +- ~"group::retention" +- ~"group::fulfillment" +- ~"group::telemetry" +- ~"group::distribution" +- ~"group::geo" +- ~"group::memory" +- ~"group::ecosystem" - These labels are [scoped labels](../../user/project/labels.md#scoped-labels-premium) and thus are mutually exclusive. @@ -192,15 +190,15 @@ can be applied to a single issue. You can find the groups listed in the The current department labels are: -* ~UX -* ~Quality +- ~UX +- ~Quality ## Specialization labels These labels narrow the [specialization](https://about.gitlab.com/company/team/structure/#specialist) on a unit of work. -* ~frontend -* ~backend +- ~frontend +- ~backend ## Release Scoping labels @@ -248,9 +246,9 @@ There can be multiple facets of the impact. The below is a guideline. If a bug seems to fall between two severity labels, assign it to the higher-severity label. - Example(s) of ~S1 - - Data corruption/loss. + - Data corruption/loss. - Security breach. - - Unable to create an issue or merge request. + - Unable to create an issue or merge request. - Unable to add a comment or discussion to the issue or merge request. - Example(s) of ~S2 - Cannot submit changes through the web IDE but the commandline works. @@ -500,7 +498,6 @@ A recent example of this was the issue for [Return to Contributing documentation](index.md) -[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels [ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues [ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues [inferred-labels]: https://gitlab.com/gitlab-org/quality/triage-ops/merge_requests/155 diff --git a/doc/development/contributing/style_guides.md b/doc/development/contributing/style_guides.md index 87e61a7476f..5c6ea1f469d 100644 --- a/doc/development/contributing/style_guides.md +++ b/doc/development/contributing/style_guides.md @@ -1,11 +1,11 @@ # Style guides -1. [Ruby](https://github.com/bbatsov/ruby-style-guide). +1. [Ruby](https://github.com/rubocop-hq/ruby-style-guide). Important sections include [Source Code Layout][rss-source] and [Naming][rss-naming]. Use: - multi-line method chaining style **Option A**: dot `.` on the second line - string literal quoting style **Option A**: single quoted by default -1. [Rails](https://github.com/bbatsov/rails-style-guide) +1. [Rails](https://github.com/rubocop-hq/rails-style-guide) 1. [Newlines styleguide][newlines-styleguide] 1. [Testing][testing] 1. [JavaScript styleguide][js-styleguide] @@ -13,7 +13,7 @@ 1. [Shell commands (Ruby)](../shell_commands.md) created by GitLab contributors to enhance security 1. [Database Migrations](../migration_style_guide.md) -1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) +1. [Markdown](https://cirosantilli.com/markdown-style-guide/) 1. [Documentation styleguide](../documentation/styleguide.md) 1. Interface text should be written subjectively instead of objectively. It should be the GitLab core team addressing a person. It should be written in @@ -25,7 +25,7 @@ 1. [Python](../python_guide/index.md) This is also the style used by linting tools such as -[RuboCop](https://github.com/bbatsov/rubocop) and [Hound CI](https://houndci.com). +[RuboCop](https://github.com/rubocop-hq/rubocop) and [Hound CI](https://houndci.com). --- diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index 418e58b22d5..36a2f47a55b 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -18,7 +18,7 @@ In addition to this page, the following resources to help craft and contribute d ## Source files and rendered web locations -Documentation for GitLab Community Edition (CE) and Enterprise Edition (EE), along with GitLab Runner and Omnibus, is published to [docs.gitlab.com](https://docs.gitlab.com). The documentation for CE and EE is also published within the application at `/help` on the domain of the GitLab instance. +Documentation for GitLab Community Edition (CE) and Enterprise Edition (EE), along with GitLab Runner and Omnibus, is published to [docs.gitlab.com](https://docs.gitlab.com). The documentation for CE and EE is also published within the application at `/help` on the domain of the GitLab instance, though there are [plans](https://gitlab.com/groups/gitlab-org/-/epics/693) to end this practice and instead link out from the GitLab application to docs.gitlab.com URLs. At `/help`, only content for your current edition and version is included, whereas multiple versions' content is available at docs.gitlab.com. @@ -43,7 +43,7 @@ Meanwhile, anyone can contribute [documentation improvements](improvement-workfl ## Markdown and styles -[GitLab docs](https://gitlab.com/gitlab-com/gitlab-docs) uses [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown) +[GitLab docs](https://gitlab.com/gitlab-org/gitlab-docs) uses [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown) as its markdown rendering engine. See the [GitLab Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/) for a complete Kramdown reference. Adhere to the [Documentation Style Guide](styleguide.md). If a style standard is missing, you are welcome to suggest one via a merge request. @@ -274,8 +274,11 @@ Follow this [method for cherry-picking from CE to EE](../automatic_ce_ee_merge.m ## GitLab `/help` -Every GitLab instance includes the documentation, which is available from `/help` -(`http://my-instance.com/help`), e.g., <https://gitlab.com/help>. +Every GitLab instance includes the documentation, which is available at `/help` +(`https://gitlab.example.com/help`). For example, <https://gitlab.com/help>. + +There are [plans](https://gitlab.com/groups/gitlab-org/-/epics/693) to end this +practice and instead link out from the GitLab application to docs.gitlab.com URLs. The documentation available online on docs.gitlab.com is continuously deployed every hour from the `master` branch of CE, EE, Omnibus, and Runner. Therefore, @@ -381,7 +384,7 @@ on how the left-side navigation menu is built and updated. NOTE: **Note:** To preview your changes to documentation locally, follow this -[development guide](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md#development-when-contributing-to-gitlab-documentation) or [these instructions for GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/gitlab_docs.md). +[development guide](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/README.md#development-when-contributing-to-gitlab-documentation) or [these instructions for GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/gitlab_docs.md). The live preview is currently enabled for the following projects: @@ -405,7 +408,7 @@ You will need to push a branch to those repositories, it doesn't work for forks. The `review-docs-deploy*` job will: -1. Create a new branch in the [gitlab-docs](https://gitlab.com/gitlab-com/gitlab-docs) +1. Create a new branch in the [gitlab-docs](https://gitlab.com/gitlab-org/gitlab-docs) project named after the scheme: `$DOCS_GITLAB_REPO_SUFFIX-$CI_ENVIRONMENT_SLUG`, where `DOCS_GITLAB_REPO_SUFFIX` is the suffix for each product, e.g, `ce` for CE, etc. @@ -461,7 +464,7 @@ If you want to know the in-depth details, here's what's really happening: 1. The preview URL is shown both at the job output and in the merge request widget. You also get the link to the remote pipeline. 1. In the docs project, the pipeline is created and it - [skips the test jobs](https://gitlab.com/gitlab-com/gitlab-docs/blob/8d5d5c750c602a835614b02f9db42ead1c4b2f5e/.gitlab-ci.yml#L50-55) + [skips the test jobs](https://gitlab.com/gitlab-org/gitlab-docs/blob/8d5d5c750c602a835614b02f9db42ead1c4b2f5e/.gitlab-ci.yml#L50-55) to lower the build time. 1. Once the docs site is built, the HTML files are uploaded as artifacts. 1. A specific Runner tied only to the docs project, runs the Review App job @@ -485,7 +488,7 @@ Currently, the following tests are in place: that all cURL examples in API docs use the full switches. It's recommended to [check locally](#previewing-the-changes-live) before pushing to GitLab by executing the command `bundle exec nanoc check internal_links` on your local - [`gitlab-docs`](https://gitlab.com/gitlab-com/gitlab-docs) directory. + [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs) directory. 1. [`ee_compat_check`](../automatic_ce_ee_merge.md#avoiding-ce-ee-merge-conflicts-beforehand) (runs on CE only): When you submit a merge request to GitLab Community Edition (CE), there is this additional job that runs against Enterprise Edition (EE) diff --git a/doc/development/documentation/site_architecture/global_nav.md b/doc/development/documentation/site_architecture/global_nav.md index 20eeebf444f..753a636a779 100644 --- a/doc/development/documentation/site_architecture/global_nav.md +++ b/doc/development/documentation/site_architecture/global_nav.md @@ -4,9 +4,9 @@ description: "Learn how GitLab docs' global navigation works and how to add new # Global navigation -> - [Introduced](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/362) +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-docs/merge_requests/362) in GitLab 11.6. -> - [Updated](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/482) in GitLab 12.1. +> - [Updated](https://gitlab.com/gitlab-org/gitlab-docs/merge_requests/482) in GitLab 12.1. The global nav adds to the left sidebar the ability to navigate and explore the contents of GitLab's documentation. @@ -25,7 +25,7 @@ To add a new doc to the nav, first and foremost, check with the technical writin Once you get their approval and their guidance in regards to the position on the nav, read trhough this page to understand how it works, and submit a merge request to the docs site, adding the doc you wish to include in the nav into the -[global nav data file](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/content/_data/global-nav.yaml). +[global nav data file](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/content/_data/global-nav.yaml). Don't forget to ask a technical writer to review your changes before merging. @@ -70,7 +70,7 @@ the data among the nav in containers properly [styled](#css-classes). ### Data file -The [data file](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/content/_data/global-nav.yaml) +The [data file](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/content/_data/global-nav.yaml) is structured in three components: sections, categories, and docs. #### Sections @@ -248,9 +248,9 @@ Examples: ### Layout file (logic) -The [layout](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/layouts/global_nav.html) +The [layout](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/layouts/global_nav.html) is fed by the [data file](#data-file), builds the global nav, and is rendered by the -[default](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/layouts/default.html) layout. +[default](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/layouts/default.html) layout. There are three main considerations on the logic built for the nav: diff --git a/doc/development/documentation/site_architecture/index.md b/doc/development/documentation/site_architecture/index.md index 6dd12b5efa7..1aef0ed855c 100644 --- a/doc/development/documentation/site_architecture/index.md +++ b/doc/development/documentation/site_architecture/index.md @@ -4,14 +4,14 @@ description: "Learn how GitLab's documentation website is architectured." # Documentation site architecture -Learn how we build and architecture [`gitlab-docs`](https://gitlab.com/gitlab-com/gitlab-docs) +Learn how we build and architecture [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs) and deploy it to <https://docs.gitlab.com>. ## Repository While the source of the documentation content is stored in GitLab's respective product repositories, the source that is used to build the documentation site _from that content_ -is located at <https://gitlab.com/gitlab-com/gitlab-docs>. +is located at <https://gitlab.com/gitlab-org/gitlab-docs>. The following diagram illustrates the relationship between the repositories from where content is sourced, the `gitlab-docs` project, and the published output. @@ -43,7 +43,7 @@ from where content is sourced, the `gitlab-docs` project, and the published outp G --> L ``` -See the [README there](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md) +See the [README there](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/README.md) for detailed information. ## Assets @@ -76,7 +76,7 @@ read through the [global navigation](global_nav.md) doc. The docs site is deployed to production with GitLab Pages, and previewed in merge requests with Review Apps. -The deployment aspects will be soon transferred from the [original document](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md) +The deployment aspects will be soon transferred from the [original document](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/README.md) to this page. <!-- diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md index fe676efa94d..025a946da0e 100644 --- a/doc/development/documentation/structure.md +++ b/doc/development/documentation/structure.md @@ -127,7 +127,7 @@ Notes: ## Help and feedback section -The "help and feedback" section (introduced by [!319](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/319)) displayed at the end of each document +The "help and feedback" section (introduced by [!319](https://gitlab.com/gitlab-org/gitlab-docs/merge_requests/319)) displayed at the end of each document can be omitted from the doc by adding a key into the its frontmatter: ```yaml @@ -142,7 +142,7 @@ you must check with a technical writer before doing so. ### Disqus We also have integrated the docs site with Disqus (introduced by -[!151](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/151)), +[!151](https://gitlab.com/gitlab-org/gitlab-docs/merge_requests/151)), allowing our users to post comments. To omit only the comments from the feedback section, use the following diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index 6bfedcb1047..1ca965f8ae9 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -340,7 +340,7 @@ For other punctuation rules, please refer to the links shift too, which eventually leads to dead links. If you think it is compelling to add numbers in headings, make sure to at least discuss it with someone in the Merge Request. -- [Avoid using symbols and special chars](https://gitlab.com/gitlab-com/gitlab-docs/issues/84) +- [Avoid using symbols and special chars](https://gitlab.com/gitlab-org/gitlab-docs/issues/84) in headers. Whenever possible, they should be plain and short text. - Avoid adding things that show ephemeral statuses. For example, if a feature is considered beta or experimental, put this info in a note, not in the heading. @@ -393,7 +393,7 @@ Instead: Example: ```md -For more information, see the [confidential issue](https://docs.gitlab.com/ee/user/project/issues/confidential_issues.html) `https://gitlab.com/gitlab-org/gitlab-ce/issues/<issue_number>`. +For more information, see the [confidential issue](../../user/project/issues/confidential_issues.md) `https://gitlab.com/gitlab-org/gitlab-ce/issues/<issue_number>`. ``` ### Unlinking emails @@ -488,7 +488,7 @@ You can link any up-to-date video that is useful to the GitLab user. ### Embed videos -> [Introduced](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/472) in GitLab 12.1. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-docs/merge_requests/472) in GitLab 12.1. GitLab docs (docs.gitlab.com) support embedded videos. @@ -518,7 +518,7 @@ you have your MR reviewed and approved by a technical writer. ```html leave a blank line here <div class="video-fallback"> - See the video: [Video title](https://www.youtube.com/watch?v=MqL6BMOySIQ). + See the video: <a href="https://www.youtube.com/watch?v=MqL6BMOySIQ">Video title</a>. </div> <figure class="video-container"> <iframe src="https://www.youtube.com/embed/MqL6BMOySIQ" frameborder="0" allowfullscreen="true"> </iframe> @@ -529,7 +529,7 @@ leave a blank line here This is how it renders on docs.gitlab.com: <div class="video-fallback"> - See the video: [What is GitLab](https://www.youtube.com/watch?v=enMumwvLAug). + See the video: <a href="https://www.youtube.com/watch?v=enMumwvLAug">What is GitLab</a>. </div> <figure class="video-container"> <iframe src="https://www.youtube.com/embed/MqL6BMOySIQ" frameborder="0" allowfullscreen="true"> </iframe> @@ -767,24 +767,24 @@ Other text includes deprecation notices and version-specific how-to information. When a feature is available in EE-only tiers, add the corresponding tier according to the feature availability: -- For GitLab Starter and GitLab.com Bronze: `**[STARTER]**`. -- For GitLab Premium and GitLab.com Silver: `**[PREMIUM]**`. -- For GitLab Ultimate and GitLab.com Gold: `**[ULTIMATE]**`. -- For GitLab Core and GitLab.com Free: `**[CORE]**`. +- For GitLab Starter and GitLab.com Bronze: `**(STARTER)**`. +- For GitLab Premium and GitLab.com Silver: `**(PREMIUM)**`. +- For GitLab Ultimate and GitLab.com Gold: `**(ULTIMATE)**`. +- For GitLab Core and GitLab.com Free: `**(CORE)**`. To exclude GitLab.com tiers (when the feature is not available in GitLab.com), add the keyword "only": -- For GitLab Core: `**[CORE ONLY]**`. -- For GitLab Starter: `**[STARTER ONLY]**`. -- For GitLab Premium: `**[PREMIUM ONLY]**`. -- For GitLab Ultimate: `**[ULTIMATE ONLY]**`. +- For GitLab Core: `**(CORE ONLY)**`. +- For GitLab Starter: `**(STARTER ONLY)**`. +- For GitLab Premium: `**(PREMIUM ONLY)**`. +- For GitLab Ultimate: `**(ULTIMATE ONLY)**`. For GitLab.com only tiers (when the feature is not available for self-hosted instances): -- For GitLab Bronze and higher tiers: `**[BRONZE ONLY]**`. -- For GitLab Silver and higher tiers: `**[SILVER ONLY]**`. -- For GitLab Gold: `**[GOLD ONLY]**`. +- For GitLab Bronze and higher tiers: `**(BRONZE ONLY)**`. +- For GitLab Silver and higher tiers: `**(SILVER ONLY)**`. +- For GitLab Gold: `**(GOLD ONLY)**`. The tier should be ideally added to headers, so that the full badge will be displayed. However, it can be also mentioned from paragraphs, list items, and table cells. For these cases, @@ -792,17 +792,17 @@ the tier mention will be represented by an orange question mark that will show t For example: -- `**[STARTER]**` renders as **[STARTER]** -- `**[STARTER ONLY]**` renders as **[STARTER ONLY]** -- `**[SILVER ONLY]**` renders as **[SILVER ONLY]** +- `**(STARTER)**` renders as **(STARTER)** +- `**(STARTER ONLY)**` renders as **(STARTER ONLY)** +- `**(SILVER ONLY)**` renders as **(SILVER ONLY)** The absence of tiers' mentions mean that the feature is available in GitLab Core, GitLab.com Free, and all higher tiers. ### How it works -Introduced by [!244](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/244), -the special markup `**[STARTER]**` will generate a `span` element to trigger the +Introduced by [!244](https://gitlab.com/gitlab-org/gitlab-docs/merge_requests/244), +the special markup `**(STARTER)**` will generate a `span` element to trigger the badges and tooltips (`<span class="badge-trigger starter">`). When the keyword "only" is added, the corresponding GitLab.com badge will not be displayed. diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md index 0abfe4b82a4..9f488fac7d0 100644 --- a/doc/development/documentation/workflow.md +++ b/doc/development/documentation/workflow.md @@ -6,5 +6,5 @@ description: Learn the processes for contributing to GitLab's documentation. Documentation workflows at GitLab differ depending on the reason for the change: -- [Documentation process for feature changes](feature-change-workflow.md) - The documentation is being created or updated as part of the development and release of a new or enhanced feature. This process involves the developer of the feature (who includes new/updated documentation files as part of the same merge request containing the feature's code) and also involves the product manager and technical writer who are listed for the feature's [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages). +- [Documentation process for feature changes](feature-change-workflow.md) - The documentation is being created or updated as part of the development and release of a new or enhanced feature. This process involves the developer of the feature (who includes new/updated documentation files as part of the same merge request containing the feature's code) and also involves the product manager and technical writer who are listed for the feature's [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages). - [Documentation improvement workflow](improvement-workflow.md) - All documentation additions not associated with a feature release. Documentation is being created or updated to improve accuracy, completeness, ease of use, or any reason other than a feature change. Anyone (and everyone) can contribute a merge request for this type of change at any time. diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md index 6f4a36d4066..7131b717353 100644 --- a/doc/development/ee_features.md +++ b/doc/development/ee_features.md @@ -182,52 +182,52 @@ There are a few gotchas with it: pattern](https://en.wikipedia.org/wiki/Template_method_pattern). For example, given this base: - ```ruby - class Base - def execute - return unless enabled? + ```ruby + class Base + def execute + return unless enabled? - # ... - # ... - end + # ... + # ... end - ``` + end + ``` - Instead of just overriding `Base#execute`, we should update it and extract - the behaviour into another method: + Instead of just overriding `Base#execute`, we should update it and extract + the behaviour into another method: - ```ruby - class Base - def execute - return unless enabled? + ```ruby + class Base + def execute + return unless enabled? - do_something - end + do_something + end - private + private - def do_something - # ... - # ... - end + def do_something + # ... + # ... end - ``` + end + ``` - Then we're free to override that `do_something` without worrying about the - guards: + Then we're free to override that `do_something` without worrying about the + guards: - ```ruby - module EE::Base - extend ::Gitlab::Utils::Override + ```ruby + module EE::Base + extend ::Gitlab::Utils::Override - override :do_something - def do_something - # Follow the above pattern to call super and extend it - end + override :do_something + def do_something + # Follow the above pattern to call super and extend it end - ``` + end + ``` - This would require updating CE first, or make sure this is back ported to CE. + This would require updating CE first, or make sure this is back ported to CE. When prepending, place them in the `ee/` specific sub-directory, and wrap class or module in `module EE` to avoid naming conflicts. @@ -446,7 +446,6 @@ The disadvantage of this: port `render_if_exists` to CE. - If we have typos in the partial name, it would be silently ignored. - ##### Caveats The `render_if_exists` view path argument must be relative to `app/views/` and `ee/app/views`. @@ -973,7 +972,7 @@ For regular JS files, the approach is similar. 1. An EE file should be created with the EE only code, and it should extend the CE counterpart. 1. For code inside functions that can't be extended, the code should be moved into a new file and we should use `ee_else_ce` helper: -##### Example: +#### Example: ```javascript import eeCode from 'ee_else_ce/ee_code'; @@ -1000,7 +999,7 @@ styles are usually kept in stylesheet that is common for both CE and EE, and it to isolate such ruleset from rest of CE rules (along with adding comment describing the same) to avoid conflicts during CE to EE merge. -#### Bad +### Bad ```scss .section-body { @@ -1016,7 +1015,7 @@ to avoid conflicts during CE to EE merge. } ``` -#### Good +### Good ```scss .section-body { @@ -1034,13 +1033,13 @@ to avoid conflicts during CE to EE merge. // EE-specific end ``` -### Backporting changes from EE to CE +## Backporting changes from EE to CE Until the work completed to merge the ce and ee codebases, which is tracked on [epic &802](https://gitlab.com/groups/gitlab-org/-/epics/802), there exists times in which some changes for EE require specific changes to the CE code base. Examples of backports include the following: -* Features intended or originally built for EE that are later decided to move to CE -* Sometimes some code in CE may impact the EE feature +- Features intended or originally built for EE that are later decided to move to CE +- Sometimes some code in CE may impact the EE feature Here is a workflow to make sure those changes end up backported safely into CE too. diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md index e5da47ba16e..0965db29557 100644 --- a/doc/development/elasticsearch.md +++ b/doc/development/elasticsearch.md @@ -1,4 +1,4 @@ -# Elasticsearch knowledge **[STARTER ONLY]** +# Elasticsearch knowledge **(STARTER ONLY)** This area is to maintain a compendium of useful information when working with elasticsearch. diff --git a/doc/development/emails.md b/doc/development/emails.md index 8baf343b133..e6af075a282 100644 --- a/doc/development/emails.md +++ b/doc/development/emails.md @@ -26,57 +26,57 @@ See the [Rails guides] for more info. feature and fill in the details for your specific IMAP server and email account: - Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com - - ```yaml - incoming_email: - enabled: true - - # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to. - # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`). - address: "gitlab-incoming+%{key}@gmail.com" - - # Email account username - # With third party providers, this is usually the full email address. - # With self-hosted email servers, this is usually the user part of the email address. - user: "gitlab-incoming@gmail.com" - # Email account password - password: "[REDACTED]" - - # IMAP server host - host: "imap.gmail.com" - # IMAP server port - port: 993 - # Whether the IMAP server uses SSL - ssl: true - # Whether the IMAP server uses StartTLS - start_tls: false - - # The mailbox where incoming mail will end up. Usually "inbox". - mailbox: "inbox" - # The IDLE command timeout. - idle_timeout: 60 - ``` - - As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`. + Configuration for Gmail / Google Apps, assumes mailbox `gitlab-incoming@gmail.com`: + + ```yaml + incoming_email: + enabled: true + + # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to. + # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`). + address: "gitlab-incoming+%{key}@gmail.com" + + # Email account username + # With third party providers, this is usually the full email address. + # With self-hosted email servers, this is usually the user part of the email address. + user: "gitlab-incoming@gmail.com" + # Email account password + password: "[REDACTED]" + + # IMAP server host + host: "imap.gmail.com" + # IMAP server port + port: 993 + # Whether the IMAP server uses SSL + ssl: true + # Whether the IMAP server uses StartTLS + start_tls: false + + # The mailbox where incoming mail will end up. Usually "inbox". + mailbox: "inbox" + # The IDLE command timeout. + idle_timeout: 60 + ``` + + As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`. 1. Run this command in the GitLab root directory to launch `mail_room`: - ```sh - bundle exec mail_room -q -c config/mail_room.yml - ``` + ```sh + bundle exec mail_room -q -c config/mail_room.yml + ``` 1. Verify that everything is configured correctly: - ```sh - bundle exec rake gitlab:incoming_email:check RAILS_ENV=development - ``` + ```sh + bundle exec rake gitlab:incoming_email:check RAILS_ENV=development + ``` 1. Reply by email should now be working. ## Email namespace -As of GitLab 11.7, we support a new format for email handler addresses. This was done to +As of GitLab 11.7, we support a new format for email handler addresses. This was done to support catch-all mailboxes. If you need to implement a feature which requires a new email handler, follow these rules @@ -91,10 +91,10 @@ for the format of the email key: Examples of valid email keys: - - `gitlab-org-gitlab-ce-20-Author_Token12345678-issue` (create a new issue) - - `gitlab-org-gitlab-ce-20-Author_Token12345678-merge-request` (create a new merge request) - - `1234567890abcdef1234567890abcdef-unsubscribe` (unsubscribe from a conversation) - - `1234567890abcdef1234567890abcdef` (reply to a conversation) +- `gitlab-org-gitlab-ce-20-Author_Token12345678-issue` (create a new issue) +- `gitlab-org-gitlab-ce-20-Author_Token12345678-merge-request` (create a new merge request) +- `1234567890abcdef1234567890abcdef-unsubscribe` (unsubscribe from a conversation) +- `1234567890abcdef1234567890abcdef` (reply to a conversation) Please note that the action `-issue-` is used in GitLab Premium as the handler for the Service Desk feature. @@ -103,10 +103,10 @@ Please note that the action `-issue-` is used in GitLab Premium as the handler f Although we continue to support the older legacy format, no new features should use a legacy format. These are the only valid legacy formats for an email handler: - - `path/to/project+namespace` - - `path/to/project+namespace+action` - - `namespace` - - `namespace+action` +- `path/to/project+namespace` +- `path/to/project+namespace+action` +- `namespace` +- `namespace+action` Please note that `path/to/project` is used in GitLab Premium as handler for the Service Desk feature. diff --git a/doc/development/fe_guide/architecture.md b/doc/development/fe_guide/architecture.md index c67389b169e..49b74b5ebcf 100644 --- a/doc/development/fe_guide/architecture.md +++ b/doc/development/fe_guide/architecture.md @@ -11,7 +11,7 @@ Architectural decisions should be accessible to everyone, so please document them in the relevant Merge Request discussion or by updating our documentation when appropriate. -You can find the Frontend Architecture experts on the [team page](https://about.gitlab.com/team). +You can find the Frontend Architecture experts on the [team page](https://about.gitlab.com/company/team). ## Examples diff --git a/doc/development/fe_guide/design_patterns.md b/doc/development/fe_guide/design_patterns.md index 0342d16a87c..2f372f783f5 100644 --- a/doc/development/fe_guide/design_patterns.md +++ b/doc/development/fe_guide/design_patterns.md @@ -53,6 +53,7 @@ When writing a class that needs to manipulate the DOM guarantee a container opti This is useful when we need that class to be instantiated more than once in the same page. Bad: + ```javascript class Foo { constructor() { @@ -63,6 +64,7 @@ new Foo(); ``` Good: + ```javascript class Foo { constructor(opts) { @@ -72,6 +74,7 @@ class Foo { new Foo({ container: '.my-element' }); ``` + You can find an example of the above in this [class][container-class-example]; [container-class-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/mini_pipeline_graph_dropdown.js diff --git a/doc/development/fe_guide/droplab/droplab.md b/doc/development/fe_guide/droplab/droplab.md index 2f8c79abde1..1c6d895b3ab 100644 --- a/doc/development/fe_guide/droplab/droplab.md +++ b/doc/development/fe_guide/droplab/droplab.md @@ -25,6 +25,7 @@ If you do not provide any arguments, it will globally query and instantiate all <!-- ... --> <ul> ``` + ```js const droplab = new DropLab(); droplab.init(); @@ -45,6 +46,7 @@ You can add static list items. <li>Static value 2</li> <ul> ``` + ```js const droplab = new DropLab(); droplab.init(); @@ -62,6 +64,7 @@ a non-global instance of DropLab using the `DropLab.prototype.init` method. <!-- ... --> <ul> ``` + ```js const trigger = document.getElementById('trigger'); const list = document.getElementById('list'); @@ -79,6 +82,7 @@ You can also add hooks to an existing DropLab instance using `DropLab.prototype. <a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a> <ul id="list" data-dropdown><!-- ... --><ul> ``` + ```js const droplab = new DropLab(); @@ -109,6 +113,7 @@ for all `data-dynamic` dropdown lists tracked by that DropLab instance. <li><a href="#" data-id="{{id}}">{{text}}</a></li> </ul> ``` + ```js const droplab = new DropLab(); @@ -131,6 +136,7 @@ the data as the second argument and the `id` of the trigger element as the first <li><a href="#" data-id="{{id}}">{{text}}</a></li> </ul> ``` + ```js const droplab = new DropLab(); @@ -160,6 +166,7 @@ dropdown lists, one of which is dynamic. </ul> </div> ``` + ```js const droplab = new DropLab(); @@ -216,6 +223,7 @@ Some plugins require configuration values, the config object can be passed as th <a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a> <ul id="list" data-dropdown><!-- ... --><ul> ``` + ```js const droplab = new DropLab(); diff --git a/doc/development/fe_guide/droplab/plugins/ajax.md b/doc/development/fe_guide/droplab/plugins/ajax.md index b6a883ce6c4..4b76b207d88 100644 --- a/doc/development/fe_guide/droplab/plugins/ajax.md +++ b/doc/development/fe_guide/droplab/plugins/ajax.md @@ -17,18 +17,19 @@ Add the `Ajax` object to the plugins array of a `DropLab.prototype.init` or `Dro <a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a> <ul id="list" data-dropdown><!-- ... --><ul> ``` + ```js - const droplab = new DropLab(); +const droplab = new DropLab(); - const trigger = document.getElementById('trigger'); - const list = document.getElementById('list'); +const trigger = document.getElementById('trigger'); +const list = document.getElementById('list'); - droplab.addHook(trigger, list, [Ajax], { - Ajax: { - endpoint: '/some-endpoint', - method: 'setData', - }, - }); +droplab.addHook(trigger, list, [Ajax], { + Ajax: { + endpoint: '/some-endpoint', + method: 'setData', + }, +}); ``` Optionally you can set `loadingTemplate` to a HTML string. This HTML string will diff --git a/doc/development/fe_guide/droplab/plugins/filter.md b/doc/development/fe_guide/droplab/plugins/filter.md index 1f188c64fe4..b867394a241 100644 --- a/doc/development/fe_guide/droplab/plugins/filter.md +++ b/doc/development/fe_guide/droplab/plugins/filter.md @@ -17,25 +17,26 @@ Add the `Filter` object to the plugins array of a `DropLab.prototype.init` or `D <li><a href="#" data-id="{{id}}">{{text}}</a></li> <ul> ``` + ```js - const droplab = new DropLab(); - - const trigger = document.getElementById('trigger'); - const list = document.getElementById('list'); - - droplab.init(trigger, list, [Filter], { - Filter: { - template: 'text', - }, - }); - - droplab.addData('trigger', [{ - id: 0, - text: 'Jacob', - }, { - id: 1, - text: 'Jeff', - }]); +const droplab = new DropLab(); + +const trigger = document.getElementById('trigger'); +const list = document.getElementById('list'); + +droplab.init(trigger, list, [Filter], { + Filter: { + template: 'text', + }, +}); + +droplab.addData('trigger', [{ + id: 0, + text: 'Jacob', +}, { + id: 1, + text: 'Jeff', +}]); ``` Above, the input string will be compared against the `test` key of the passed data objects. diff --git a/doc/development/fe_guide/droplab/plugins/input_setter.md b/doc/development/fe_guide/droplab/plugins/input_setter.md index e4050213869..db492da478a 100644 --- a/doc/development/fe_guide/droplab/plugins/input_setter.md +++ b/doc/development/fe_guide/droplab/plugins/input_setter.md @@ -22,33 +22,34 @@ You can also set the `InputSetter` config to an array of objects, which will all <li><a href="#" data-id="{{id}}">{{text}}</a></li> <ul> ``` + ```js - const droplab = new DropLab(); - - const trigger = document.getElementById('trigger'); - const list = document.getElementById('list'); - - const input = document.getElementById('input'); - const div = document.getElementById('div'); - - droplab.init(trigger, list, [InputSetter], { - InputSetter: [{ - input: input, - valueAttribute: 'data-id', - } { - input: div, - valueAttribute: 'data-id', - inputAttribute: 'data-selected-id', - }], - }); - - droplab.addData('trigger', [{ - id: 0, - text: 'Jacob', - }, { - id: 1, - text: 'Jeff', - }]); +const droplab = new DropLab(); + +const trigger = document.getElementById('trigger'); +const list = document.getElementById('list'); + +const input = document.getElementById('input'); +const div = document.getElementById('div'); + +droplab.init(trigger, list, [InputSetter], { + InputSetter: [{ + input: input, + valueAttribute: 'data-id', + } { + input: div, + valueAttribute: 'data-id', + inputAttribute: 'data-selected-id', + }], +}); + +droplab.addData('trigger', [{ + id: 0, + text: 'Jacob', +}, { + id: 1, + text: 'Jeff', +}]); ``` Above, if the second list item was clicked, it would update the `#input` element diff --git a/doc/development/fe_guide/emojis.md b/doc/development/fe_guide/emojis.md index 38794c47965..6d324d4c4a0 100644 --- a/doc/development/fe_guide/emojis.md +++ b/doc/development/fe_guide/emojis.md @@ -3,10 +3,10 @@ GitLab supports native unicode emojis and fallsback to image-based emojis selectively when your platform does not support it. -# How to update Emojis +## How to update Emojis 1. Update the `gemojione` gem - 1. Update `fixtures/emojis/index.json` from [Gemojione](https://github.com/jonathanwiesel/gemojione/blob/master/config/index.json). + 1. Update `fixtures/emojis/index.json` from [Gemojione](https://github.com/bonusly/gemojione/blob/master/config/index.json). In the future, we could grab the file directly from the gem. We should probably make a PR on the Gemojione project to get access to all emojis after being parsed or just a raw path to the `json` file itself. diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md index 9fcd32fddfa..55b719227e5 100644 --- a/doc/development/fe_guide/graphql.md +++ b/doc/development/fe_guide/graphql.md @@ -55,7 +55,6 @@ It is possible to manage an application state with Apollo by passing in a resolvers object when creating the default client. The default state can be set by writing to the cache after setting up the default client. - ```javascript import Vue from 'vue'; import VueApollo from 'vue-apollo'; @@ -115,13 +114,12 @@ defaultClient.query(query) .then(result => console.log(result)); ``` -Read more about the [Apollo] client in the [Apollo documentation][apollo-client-docs]. +Read more about the [Apollo] client in the [Apollo documentation](https://www.apollographql.com/docs/tutorial/client/). [Apollo]: https://www.apollographql.com/ [vue-apollo]: https://github.com/Akryum/vue-apollo/ [vue-apollo-docs]: https://akryum.github.io/vue-apollo/ [feature-flags]: ../feature_flags.md [default-client]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/lib/graphql.js -[apollo-client-docs]: https://www.apollographql.com/docs/tutorial/client.html [vue-test-utils]: https://vue-test-utils.vuejs.org/ [apollo-link-state]: https://www.apollographql.com/docs/link/links/state.html diff --git a/doc/development/fe_guide/security.md b/doc/development/fe_guide/security.md index 83bb449e54d..47ac87fc895 100644 --- a/doc/development/fe_guide/security.md +++ b/doc/development/fe_guide/security.md @@ -1,5 +1,6 @@ # Security -### Resources + +## Resources [Mozilla’s HTTP Observatory CLI][observatory-cli] and the [Qualys SSL Labs Server Test][qualys-ssl] are good resources for finding @@ -56,7 +57,7 @@ Some resources on implementing Subresource Integrity: --> -### Including external resources +## Including external resources External fonts, CSS, and JavaScript should never be used with the exception of Google Analytics and Piwik - and only when the instance has enabled it. Assets @@ -64,7 +65,7 @@ should always be hosted and served locally from the GitLab instance. Embedded resources via `iframes` should never be used except in certain circumstances such as with ReCaptcha, which cannot be used without an `iframe`. -### Avoiding inline scripts and styles +## Avoiding inline scripts and styles In order to protect users from [XSS vulnerabilities][xss], we will disable inline scripts in the future using Content Security Policy. diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md index 6c7572352ec..421b7265613 100644 --- a/doc/development/fe_guide/vue.md +++ b/doc/development/fe_guide/vue.md @@ -34,6 +34,7 @@ new_feature │ └── new_feature_store.js ├── index.js ``` + _For consistency purposes, we recommend you to follow the same structure._ Let's look into each of them: diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md index c67467b7c11..739f4207e27 100644 --- a/doc/development/feature_flags/controls.md +++ b/doc/development/feature_flags/controls.md @@ -5,7 +5,7 @@ GitLab Inc. provided environments such as staging and production, you need to have access to the chatops bot. Chatops bot is currently running on the ops instance, which is different from GitLab.com or dev.gitlab.org. -Follow the Chatops document to [request access](https://docs.gitlab.com/ee/development/chatops_on_gitlabcom.html#requesting-access). +Follow the Chatops document to [request access](../chatops_on_gitlabcom.md#requesting-access). Once you are added to the project test if your access propagated, run: @@ -112,7 +112,7 @@ instances. Make sure to add the ~"feature flag" label to this merge request so release managers are aware the changes are hidden behind a feature flag. If the merge request has to be picked into a stable branch, make sure to also add the appropriate "Pick into X" label (e.g. "Pick into XX.X"). -See [the process document](https://docs.gitlab.com/ee/development/feature_flags/process.html#including-a-feature-behind-feature-flag-in-the-final-release) for further details. +See [the process document](process.md#including-a-feature-behind-feature-flag-in-the-final-release) for further details. When a feature gate has been removed from the code base, the value still exists in the database. diff --git a/doc/development/feature_flags/development.md b/doc/development/feature_flags/development.md index 238052529d9..98773026122 100644 --- a/doc/development/feature_flags/development.md +++ b/doc/development/feature_flags/development.md @@ -57,7 +57,7 @@ the feature flag check will default to `true`. As an example, if you were to ship the backend half of a feature behind a flag, you'd want to explicitly disable that flag until the frontend half is also ready -to be shipped. [You can do this via Chatops](https://docs.gitlab.com/ee/development/feature_flags/controls.html): +to be shipped. [You can do this via Chatops](controls.md): ``` /chatops run feature set some_feature 0 diff --git a/doc/development/geo.md b/doc/development/geo.md index a10f13b069f..685d4e44ad3 100644 --- a/doc/development/geo.md +++ b/doc/development/geo.md @@ -1,4 +1,4 @@ -# Geo (development) **[PREMIUM ONLY]** +# Geo (development) **(PREMIUM ONLY)** Geo connects GitLab instances together. One GitLab instance is designated as a **primary** node and can be run with multiple diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md index 5552d5d37b4..2ade59b76ed 100644 --- a/doc/development/gitaly.md +++ b/doc/development/gitaly.md @@ -237,24 +237,23 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag. 1. Create prometheus metrics: ```go - var findAllTagsRequests = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "gitaly_find_all_tags_requests_total", - Help: "Counter of go vs ruby implementation of FindAllTags", - }, - []string{"implementation"}, - ) + var findAllTagsRequests = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "gitaly_find_all_tags_requests_total", + Help: "Counter of go vs ruby implementation of FindAllTags", + }, + []string{"implementation"}, ) func init() { - prometheus.Register(findAllTagsRequests) + prometheus.Register(findAllTagsRequests) } if featureflag.IsEnabled(ctx, findAllTagsFeatureFlag) { - findAllTagsRequests.WithLabelValues("go").Inc() + findAllTagsRequests.WithLabelValues("go").Inc() // go implementation } else { - findAllTagsRequests.WithLabelValues("ruby").Inc() + findAllTagsRequests.WithLabelValues("ruby").Inc() // ruby implementation } ``` diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md index f961a2fc837..f09339eb3a4 100644 --- a/doc/development/go_guide/index.md +++ b/doc/development/go_guide/index.md @@ -41,7 +41,7 @@ of possible security breaches in our code: Remember to run [SAST](../../user/application_security/sast/index.md) -**[ULTIMATE]** on your project (or at least the [gosec +**(ULTIMATE)** on your project (or at least the [gosec analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/gosec)), and to follow our [Security requirements](../code_review.md#security-requirements). @@ -95,9 +95,9 @@ Dependencies should be kept to the minimum. The introduction of a new dependency should be argued in the merge request, as per our [Approval Guidelines](../code_review.md#approval-guidelines). Both [License Management](../../user/project/merge_requests/license_management.md) -**[ULTIMATE]** and [Dependency +**(ULTIMATE)** and [Dependency Scanning](../../user/application_security/dependency_scanning/index.md) -**[ULTIMATE]** should be activated on all projects to ensure new dependencies +**(ULTIMATE)** should be activated on all projects to ensure new dependencies security status and license compatibility. ### Modules diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md index 1b9ebb50c29..13dda17bb7d 100644 --- a/doc/development/gotchas.md +++ b/doc/development/gotchas.md @@ -101,10 +101,10 @@ end in a prepended module, which is very likely the case in EE. We could see error like this: - ``` - 1.1) Failure/Error: expect_any_instance_of(ApplicationSetting).to receive_messages(messages) - Using `any_instance` to stub a method (elasticsearch_indexing) that has been defined on a prepended module (EE::ApplicationSetting) is not supported. - ``` + ``` + 1.1) Failure/Error: expect_any_instance_of(ApplicationSetting).to receive_messages(messages) + Using `any_instance` to stub a method (elasticsearch_indexing) that has been defined on a prepended module (EE::ApplicationSetting) is not supported. + ``` ### Alternative: `expect_next_instance_of` diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md index 35c5b155594..910d7296057 100644 --- a/doc/development/i18n/proofreader.md +++ b/doc/development/i18n/proofreader.md @@ -106,32 +106,31 @@ are very appreciative of the work done by translators and proofreaders! 1. Contribute translations to GitLab. See instructions for [translating GitLab](translation.md). - Translating GitLab is a community effort that requires team work and - attention to detail. Proofreaders play an important role helping new - contributors, and ensuring the consistency and quality of translations. - Your conduct and contributions as a translator should reflect this before - requesting to be a proofreader. + Translating GitLab is a community effort that requires team work and + attention to detail. Proofreaders play an important role helping new + contributors, and ensuring the consistency and quality of translations. + Your conduct and contributions as a translator should reflect this before + requesting to be a proofreader. 1. Request proofreader permissions by opening a merge request to add yourself to the list of proofreaders. - Open the [proofreader.md source file][proofreader-src] and click **Edit**. + Open the [proofreader.md source file][proofreader-src] and click **Edit**. - Add your language in alphabetical order, and add yourself to the list - including: - - name - - link to your GitLab profile - - link to your CrowdIn profile + Add your language in alphabetical order, and add yourself to the list + including: + - name + - link to your GitLab profile + - link to your CrowdIn profile - In the merge request description, please include links to any projects you - have previously translated. + In the merge request description, please include links to any projects you + have previously translated. 1. Your request to become a proofreader will be considered on the merits of your previous translations by [GitLab team members](https://about.gitlab.com/team/) or [Core team members](https://about.gitlab.com/core-team/) who are fluent in the language or current proofreaders. - When a request is made for the first proofreader for a language and there are no [GitLab team members](https://about.gitlab.com/team/) - or [Core team members](https://about.gitlab.com/core-team/) who speak the language, we will request links to previous translation work in other communities or projects. - + or [Core team members](https://about.gitlab.com/core-team/) who speak the language, we will request links to previous translation work in other communities or projects. [proofreader-src]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/i18n/proofreader.md diff --git a/doc/development/integrations/jira_connect.md b/doc/development/integrations/jira_connect.md index 5bf43d320c6..e1350b02262 100644 --- a/doc/development/integrations/jira_connect.md +++ b/doc/development/integrations/jira_connect.md @@ -4,7 +4,7 @@ The following are required to install and test the app: 1. A Jira Cloud instance - Atlassian provides free instances for development and testing. [Click here to sign up](http://go.atlassian.com/cloud-dev). + Atlassian provides free instances for development and testing. [Click here to sign up](https://developer.atlassian.com/platform/marketplace/getting-started/#free-developer-instances-to-build-and-test-your-app). 1. A GitLab instance available over the internet @@ -15,7 +15,7 @@ The following are required to install and test the app: > This feature is currently behind the `:jira_connect_app` feature flag -# Installing the app in Jira +## Installing the app in Jira 1. Enable Jira development mode to install apps that are not from the Atlassian Marketplace @@ -30,9 +30,11 @@ The following are required to install and test the app: 1. In the **From this URL** field, provide a link to the app descriptor. The host and port must point to your GitLab instance. For example: + ``` https://xxxx.serveo.net/-/jira_connect/app_descriptor.json ``` + 1. Click **Upload**. If the install was successful, you should see the **GitLab for Jira** app under **Manage apps**. diff --git a/doc/development/licensed_feature_availability.md b/doc/development/licensed_feature_availability.md index 6f3dd59b2c3..80ec7b8c0cf 100644 --- a/doc/development/licensed_feature_availability.md +++ b/doc/development/licensed_feature_availability.md @@ -1,18 +1,18 @@ -# Licensed feature availability **[STARTER]** +# Licensed feature availability **(STARTER)** -As of GitLab 9.4, we've been supporting a simplified version of licensed -feature availability checks via `ee/app/models/license.rb`, both for +As of GitLab 9.4, we've been supporting a simplified version of licensed +feature availability checks via `ee/app/models/license.rb`, both for on-premise or GitLab.com plans and features. ## Restricting features scoped by namespaces or projects GitLab.com plans are persisted on user groups and namespaces, therefore, if you're adding a -feature such as [Related issues](../user/project/issues/related_issues.md) or -[Service desk](../user/project/service_desk.md), +feature such as [Related issues](../user/project/issues/related_issues.md) or +[Service desk](../user/project/service_desk.md), it should be restricted on namespace scope. -1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in - `ee/app/models/license.rb`. Note on `ee/app/models/ee/namespace.rb` that _Bronze_ GitLab.com +1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in + `ee/app/models/license.rb`. Note on `ee/app/models/ee/namespace.rb` that _Bronze_ GitLab.com features maps to on-premise _EES_, _Silver_ to _EEP_ and _Gold_ to _EEU_. 2. Check using: @@ -22,12 +22,12 @@ project.feature_available?(:feature_symbol) ## Restricting global features (instance) -However, for features such as [Geo](../administration/geo/replication/index.md) and -[Load balancing](../administration/database_load_balancing.md), which cannot be restricted -to only a subset of projects or namespaces, the check will be made directly in +However, for features such as [Geo](../administration/geo/replication/index.md) and +[Load balancing](../administration/database_load_balancing.md), which cannot be restricted +to only a subset of projects or namespaces, the check will be made directly in the instance license. -1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in +1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in `ee/app/models/license.rb`. 2. Add the same feature symbol to `GLOBAL_FEATURES` 3. Check using: diff --git a/doc/development/logging.md b/doc/development/logging.md index d61441813b2..4f63c84fc0e 100644 --- a/doc/development/logging.md +++ b/doc/development/logging.md @@ -30,8 +30,8 @@ Completed 200 OK in 166ms (Views: 117.4ms | ActiveRecord: 27.2ms) These logs suffer from a number of problems: 1. They often lack timestamps or other contextual information (e.g. project ID, user) -2. They may span multiple lines, which make them hard to find via Elasticsearch. -3. They lack a common structure, which make them hard to parse by log +1. They may span multiple lines, which make them hard to find via Elasticsearch. +1. They lack a common structure, which make them hard to parse by log forwarders, such as Logstash or Fluentd. This also makes them hard to search. @@ -67,46 +67,46 @@ importer progresses. Here's what to do: make it easy for people to search pertinent logs in one place. For example, `geo.log` contains all logs pertaining to GitLab Geo. To create a new file: - 1. Choose a filename (e.g. `importer_json.log`). - 1. Create a new subclass of `Gitlab::JsonLogger`: - - ```ruby - module Gitlab - module Import - class Logger < ::Gitlab::JsonLogger - def self.file_name_noext - 'importer' - end + 1. Choose a filename (e.g. `importer_json.log`). + 1. Create a new subclass of `Gitlab::JsonLogger`: + + ```ruby + module Gitlab + module Import + class Logger < ::Gitlab::JsonLogger + def self.file_name_noext + 'importer' end - end - end - ``` + end + end + end + ``` - 1. In your class where you want to log, you might initialize the logger as an instance variable: + 1. In your class where you want to log, you might initialize the logger as an instance variable: - ```ruby - attr_accessor :logger + ```ruby + attr_accessor :logger - def initialize - @logger = Gitlab::Import::Logger.build - end - ``` + def initialize + @logger = Gitlab::Import::Logger.build + end + ``` - Note that it's useful to memoize this because creating a new logger - each time you log will open a file, adding unnecessary overhead. + Note that it's useful to memoize this because creating a new logger + each time you log will open a file, adding unnecessary overhead. 1. Now insert log messages into your code. When adding logs, make sure to include all the context as key-value pairs: - ```ruby - # BAD - logger.info("Unable to create project #{project.id}") - ``` + ```ruby + # BAD + logger.info("Unable to create project #{project.id}") + ``` - ```ruby - # GOOD - logger.info(message: "Unable to create project", project_id: project.id) - ``` + ```ruby + # GOOD + logger.info(message: "Unable to create project", project_id: project.id) + ``` 1. Be sure to create a common base structure of your log messages. For example, all messages might have `current_user_id` and `project_id` to make it easier @@ -116,16 +116,16 @@ importer progresses. Here's what to do: logs properly if you [mix integer and string types](https://www.elastic.co/guide/en/elasticsearch/guide/current/mapping.html#_avoiding_type_gotchas): - ```ruby - # BAD - logger.info(message: "Import error", error: 1) - logger.info(message: "Import error", error: "I/O failure") - ``` + ```ruby + # BAD + logger.info(message: "Import error", error: 1) + logger.info(message: "Import error", error: "I/O failure") + ``` - ```ruby - # GOOD - logger.info(message: "Import error", error_code: 1, error: "I/O failure") - ``` + ```ruby + # GOOD + logger.info(message: "Import error", error_code: 1, error: "I/O failure") + ``` ## Additional steps with new log files diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index 9b26f691b55..0c7601b415e 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -21,7 +21,7 @@ When downtime is necessary the migration has to be approved by: 1. A Database Specialist An up-to-date list of people holding these titles can be found at -<https://about.gitlab.com/team/>. +<https://about.gitlab.com/company/team/>. When writing your migrations, also consider that databases might have stale data or inconsistencies and guard for that. Try to make as few assumptions as diff --git a/doc/development/new_fe_guide/development/components.md b/doc/development/new_fe_guide/development/components.md index 963ce53423b..cebdc87eab9 100644 --- a/doc/development/new_fe_guide/development/components.md +++ b/doc/development/new_fe_guide/development/components.md @@ -13,7 +13,7 @@ D3 is very popular across many projects outside of GitLab: - [The New York Times](https://archive.nytimes.com/www.nytimes.com/interactive/2012/02/13/us/politics/2013-budget-proposal-graphic.html) - [plot.ly](https://plot.ly/) -- [Droptask](https://www.droptask.com/) +- [Droptask](https://www.ayoa.com/previously-droptask/) Within GitLab, D3 has been used for the following notable features diff --git a/doc/development/new_fe_guide/development/performance.md b/doc/development/new_fe_guide/development/performance.md index 640a8d64176..c54b8305991 100644 --- a/doc/development/new_fe_guide/development/performance.md +++ b/doc/development/new_fe_guide/development/performance.md @@ -2,10 +2,10 @@ ## Monitoring -We have a performance dashboard available in one of our [grafana instances](https://dashboards.gitlab.net/d/1EBTz3Dmz/sitespeed-page-summary?orgId=1). This dashboard automatically aggregates metric data from [sitespeed.io](https://sitespeed.io) every 6 hours. These changes are displayed after a set number of pages are aggregated. +We have a performance dashboard available in one of our [grafana instances](https://dashboards.gitlab.net/d/1EBTz3Dmz/sitespeed-page-summary?orgId=1). This dashboard automatically aggregates metric data from [sitespeed.io](https://www.sitespeed.io/) every 6 hours. These changes are displayed after a set number of pages are aggregated. These pages can be found inside a text file in the gitlab-build-images [repository](https://gitlab.com/gitlab-org/gitlab-build-images) called [gitlab.txt](https://gitlab.com/gitlab-org/gitlab-build-images/blob/master/scripts/gitlab.txt) -Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing urls of pages from this text file. Please have a [frontend monitoring expert](https://about.gitlab.com/team) review your changes before assigning to a maintainer of the `gitlab-build-images` project. The changes will go live on the next scheduled run after the changes are merged into `master`. +Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing urls of pages from this text file. Please have a [frontend monitoring expert](https://about.gitlab.com/company/team) review your changes before assigning to a maintainer of the `gitlab-build-images` project. The changes will go live on the next scheduled run after the changes are merged into `master`. There are 3 recommended high impact metrics to review on each page: diff --git a/doc/development/new_fe_guide/development/testing.md b/doc/development/new_fe_guide/development/testing.md index 8441089418e..2b62c2a41fe 100644 --- a/doc/development/new_fe_guide/development/testing.md +++ b/doc/development/new_fe_guide/development/testing.md @@ -261,7 +261,7 @@ scenario 'successfully', :js do end ``` -The steps of each test are written using capybara methods ([documentation](http://www.rubydoc.info/gems/capybara/2.15.1)). +The steps of each test are written using capybara methods ([documentation](https://www.rubydoc.info/gems/capybara/2.15.1)). Bear in mind <abbr title="XMLHttpRequest">XHR</abbr> calls might require you to use `wait_for_requests` in between steps, like so: @@ -277,7 +277,7 @@ expect(page).not_to have_selector('.card') ### Vuex Helper: `testAction` -We have a helper available to make testing actions easier, as per [official documentation](https://vuex.vuejs.org/en/testing.html): +We have a helper available to make testing actions easier, as per [official documentation](https://vuex.vuejs.org/guide/testing.html): ``` testAction( diff --git a/doc/development/new_fe_guide/style/html.md b/doc/development/new_fe_guide/style/html.md index e8c9c2ccebf..1445da3f0e1 100644 --- a/doc/development/new_fe_guide/style/html.md +++ b/doc/development/new_fe_guide/style/html.md @@ -16,7 +16,7 @@ Button tags requires a `type` attribute according to the [W3C HTML specification ### Button role -If an HTML element has an `onClick` handler but is not a button, it should have `role="button"`. This is [more accessible](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role). +If an HTML element has an `onClick` handler but is not a button, it should have `role="button"`. This is [more accessible](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role). ```html // bad diff --git a/doc/development/new_fe_guide/style/prettier.md b/doc/development/new_fe_guide/style/prettier.md index 4495f38f262..5f44c640d76 100644 --- a/doc/development/new_fe_guide/style/prettier.md +++ b/doc/development/new_fe_guide/style/prettier.md @@ -4,7 +4,7 @@ Our code is automatically formatted with [Prettier](https://prettier.io) to foll ## Editor -The easiest way to include prettier in your workflow is by setting up your preferred editor (all major editors are supported) accordingly. We suggest setting up prettier to run automatically when each file is saved. Find [here](https://prettier.io/docs/en/editors.html) the best way to set it up in your preferred editor. +The easiest way to include prettier in your workflow is by setting up your preferred editor (all major editors are supported) accordingly. We suggest setting up prettier to run automatically when each file is saved. Find [here](https://prettier.io/docs/en/editors.html) the best way to set it up in your preferred editor. Please take care that you only let Prettier format the same file types as the global Yarn script does (.js, .vue, and .scss). In VSCode by example you can easily exclude file formats in your settings file: @@ -28,6 +28,7 @@ Updates all currently staged files (based on `git diff`) with Prettier and saves ``` yarn prettier-staged ``` + Checks all currently staged files (based on `git diff`) with Prettier and log which files would need manual updating to the console. ``` diff --git a/doc/development/newlines_styleguide.md b/doc/development/newlines_styleguide.md index 5f7210020b6..a13adc2f13e 100644 --- a/doc/development/newlines_styleguide.md +++ b/doc/development/newlines_styleguide.md @@ -11,7 +11,7 @@ def method issue.save - render json: issue + render json: issue end ``` @@ -21,7 +21,7 @@ def method issue = Issue.new issue.save - render json: issue + render json: issue end ``` diff --git a/doc/development/packages.md b/doc/development/packages.md index ab0c5f9904d..08aa0b08525 100644 --- a/doc/development/packages.md +++ b/doc/development/packages.md @@ -1,15 +1,15 @@ -# Packages **[PREMIUM]** +# Packages **(PREMIUM)** This document will guide you through adding another [package management system](../administration/packages.md) support to GitLab. See already supported package types in [Packages documentation](../administration/packages.md) Since GitLab packages' UI is pretty generic, it is possible to add new -package system support by solely backend changes. This guide is superficial and does -not cover the way the code should be written. However, you can find a good example -by looking at existing merge requests with Maven and NPM support: +package system support by solely backend changes. This guide is superficial and does +not cover the way the code should be written. However, you can find a good example +by looking at existing merge requests with Maven and NPM support: -- [NPM registry support](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/8673). +- [NPM registry support](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/8673). - [Maven repository](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6607). - [Instance level endpoint for Maven repository](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/8757) @@ -17,44 +17,44 @@ by looking at existing merge requests with Maven and NPM support: The existing database model requires the following: -- Every package belongs to a project. +- Every package belongs to a project. - Every package file belongs to a package. - A package can have one or more package files. - The package model is based on storing information about the package and its version. ## API endpoints -Package systems work with GitLab via API. For example `ee/lib/api/npm_packages.rb` -implements API endpoints to work with NPM clients. So, the first thing to do is to -add a new `ee/lib/api/your_name_packages.rb` file with API endpoints that are -necessary to make the package system client to work. Usually that means having -endpoints like: +Package systems work with GitLab via API. For example `ee/lib/api/npm_packages.rb` +implements API endpoints to work with NPM clients. So, the first thing to do is to +add a new `ee/lib/api/your_name_packages.rb` file with API endpoints that are +necessary to make the package system client to work. Usually that means having +endpoints like: - GET package information. - GET package file content. - PUT upload package. Since the packages belong to a project, it's expected to have project-level endpoint -for uploading and downloading them. For example: +for uploading and downloading them. For example: ``` GET https://gitlab.com/api/v4/projects/<your_project_id>/packages/npm/ PUT https://gitlab.com/api/v4/projects/<your_project_id>/packages/npm/ ``` -Group-level and instance-level endpoints are good to have but are optional. +Group-level and instance-level endpoints are good to have but are optional. NOTE: **Note:** -To avoid name conflict for instance-level endpoints we use +To avoid name conflict for instance-level endpoints we use [the package naming convention](../user/project/packages/npm_registry.md#package-naming-convention) ## Configuration -GitLab has a `packages` section in its configuration file (`gitlab.rb`). -It applies to all package systems supported by GitLab. Usually you don't need -to add anything there. +GitLab has a `packages` section in its configuration file (`gitlab.rb`). +It applies to all package systems supported by GitLab. Usually you don't need +to add anything there. -Packages can be configured to use object storage, therefore your code must support it. +Packages can be configured to use object storage, therefore your code must support it. ## Database @@ -63,6 +63,6 @@ Every time you upload a new package, you can either create a new record of `Pack or add files to existing record. `PackageFile` should be able to store all file-related information like the file `name`, `side`, `sha1`, etc. -If there is specific data necessary to be stored for only one package system support, -consider creating a separate metadata model. See `packages_maven_metadata` table +If there is specific data necessary to be stored for only one package system support, +consider creating a separate metadata model. See `packages_maven_metadata` table and `Packages::MavenMetadatum` model as example for package specific data. diff --git a/doc/development/performance.md b/doc/development/performance.md index c034f4a344b..8b569a677b6 100644 --- a/doc/development/performance.md +++ b/doc/development/performance.md @@ -246,6 +246,7 @@ irb(main):002:0> results.last.attributes.keys irb(main):003:0> results.where(status: "passed").average(:time).to_s => "0.211340155844156" ``` + These results can also be placed into a PostgreSQL database by setting the `RSPEC_PROFILING_POSTGRES_URL` variable. This is used to profile the test suite when running in the CI environment. diff --git a/doc/development/profiling.md b/doc/development/profiling.md index 795523b82aa..e1d1d2e33fa 100644 --- a/doc/development/profiling.md +++ b/doc/development/profiling.md @@ -95,7 +95,9 @@ Sherlock is a custom profiling tool built into GitLab. Sherlock is _only_ available when running GitLab in development mode _and_ when setting the environment variable `ENABLE_SHERLOCK` to a non empty value. For example: - ENABLE_SHERLOCK=1 bundle exec rails s +```sh +ENABLE_SHERLOCK=1 bundle exec rails s +``` Recorded transactions can be found by navigating to `/sherlock/transactions`. @@ -106,7 +108,9 @@ Bullet adds quite a bit of logging noise it's disabled by default. To enable Bullet, set the environment variable `ENABLE_BULLET` to a non-empty value before starting GitLab. For example: - ENABLE_BULLET=true bundle exec rails s +```sh +ENABLE_BULLET=true bundle exec rails s +``` Bullet will log query problems to both the Rails log as well as the Chrome console. diff --git a/doc/development/prometheus_metrics.md b/doc/development/prometheus_metrics.md index 0511e735843..576601372a3 100644 --- a/doc/development/prometheus_metrics.md +++ b/doc/development/prometheus_metrics.md @@ -33,12 +33,10 @@ For example: you might be interested in migrating all dependent data to a differ class ImportCommonMetrics < ActiveRecord::Migration[4.2] include Gitlab::Database::MigrationHelpers - require Rails.root.join('db/importers/common_metrics_importer.rb') - DOWNTIME = false def up - Importers::CommonMetricsImporter.new.execute + ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute end def down diff --git a/doc/development/python_guide/index.md b/doc/development/python_guide/index.md index 6025dc9ebf2..a80bee27d4a 100644 --- a/doc/development/python_guide/index.md +++ b/doc/development/python_guide/index.md @@ -36,7 +36,7 @@ You can read more about it in: <https://github.com/pyenv/pyenv-installer#prerequ Pyenv installation will add required changes to Bash. If you use a different shell, check for any additional steps required for it. -For Fish, you can install a plugin for [Fisherman](https://github.com/fisherman/fisherman): +For Fish, you can install a plugin for [Fisher](https://github.com/jorgebucaran/fisher): ```bash fisher add fisherman/pyenv @@ -76,4 +76,3 @@ pipenv shell After running that command, you can run GitLab on the same shell and it will be using the Python and dependencies installed from the `pipenv install` command. - diff --git a/doc/development/query_recorder.md b/doc/development/query_recorder.md index 2167ed57428..a6b60149ea4 100644 --- a/doc/development/query_recorder.md +++ b/doc/development/query_recorder.md @@ -1,6 +1,6 @@ # QueryRecorder -QueryRecorder is a tool for detecting the [N+1 queries problem](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations) from tests. +QueryRecorder is a tool for detecting the [N+1 queries problem](https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations) from tests. > Implemented in [spec/support/query_recorder.rb](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/support/helpers/query_recorder.rb) via [9c623e3e](https://gitlab.com/gitlab-org/gitlab-ce/commit/9c623e3e5d7434f2e30f7c389d13e5af4ede770a) @@ -86,4 +86,4 @@ QueryRecorder SQL: SELECT COUNT(*) FROM "issues" WHERE "issues"."deleted_at" IS - [Bullet](profiling.md#Bullet) For finding `N+1` query problems - [Performance guidelines](performance.md) -- [Merge request performance guidelines](merge_request_performance_guidelines.md#query-counts)
\ No newline at end of file +- [Merge request performance guidelines](merge_request_performance_guidelines.md#query-counts) diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index 4fc10b6af5c..c97e179910b 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -28,7 +28,7 @@ bin/rake "gitlab:seed:issues[group-path/project-path]" By default, this seeds an average of 2 issues per week for the last 5 weeks per project. -#### Seeding issues for Insights charts **[ULTIMATE]** +#### Seeding issues for Insights charts **(ULTIMATE)** You can seed issues specifically for working with the [Insights charts](../user/group/insights/index.md) with the diff --git a/doc/development/routing.md b/doc/development/routing.md index e9c0ad8d4e8..a25eb48b73c 100644 --- a/doc/development/routing.md +++ b/doc/development/routing.md @@ -7,11 +7,15 @@ support subgroups, GitLab project and group routes use the wildcard character to match project and group routes. For example, we might have a path such as: - /gitlab-com/customer-success/north-america/west/customerA +``` +/gitlab-com/customer-success/north-america/west/customerA +``` However, paths can be ambiguous. Consider the following example: - /gitlab-com/edit +``` +/gitlab-com/edit +``` It's ambiguous whether there is a subgroup named `edit` or whether this is a special endpoint to edit the `gitlab-com` group. @@ -25,8 +29,10 @@ number of [reserved names](../user/reserved_names.md). We have a number of global routes. For example: - /-/health - /-/metrics +``` +/-/health +/-/metrics +``` ## Group routes @@ -34,10 +40,12 @@ Every group route must be under the `/-/` scope. Examples: - gitlab-org/-/edit - gitlab-org/-/activity - gitlab-org/-/security/dashboard - gitlab-org/serverless/-/activity +``` +gitlab-org/-/edit +gitlab-org/-/activity +gitlab-org/-/security/dashboard +gitlab-org/serverless/-/activity +``` To achieve that, use the `scope '-'` method. @@ -48,10 +56,12 @@ client or other software requires something different. Examples: - gitlab-org/gitlab-ce/-/activity - gitlab-org/gitlab-ce/-/jobs/123 - gitlab-org/gitlab-ce/-/settings/repository - gitlab-org/serverless/runtimes/-/settings/repository +``` +gitlab-org/gitlab-ce/-/activity +gitlab-org/gitlab-ce/-/jobs/123 +gitlab-org/gitlab-ce/-/settings/repository +gitlab-org/serverless/runtimes/-/settings/repository +``` Currently, only some project routes are placed under the `/-/` scope. However, you can help us migrate more of them! To migrate project routes: diff --git a/doc/development/sql.md b/doc/development/sql.md index edeca7fb298..a256fd46c09 100644 --- a/doc/development/sql.md +++ b/doc/development/sql.md @@ -94,7 +94,9 @@ on the amount of data indexed). To keep naming of these indexes consistent please use the following naming pattern: - index_TABLE_on_COLUMN_trigram +``` +index_TABLE_on_COLUMN_trigram +``` For example, a GIN/trigram index for `issues.title` would be called `index_issues_on_title_trigram`. diff --git a/doc/development/testing_guide/ci.md b/doc/development/testing_guide/ci.md index 7a7fca46534..87d48726268 100644 --- a/doc/development/testing_guide/ci.md +++ b/doc/development/testing_guide/ci.md @@ -1,6 +1,6 @@ # GitLab tests in the Continuous Integration (CI) context -### Test suite parallelization on the CI +## Test suite parallelization on the CI Our current CI parallelization setup is as follows: @@ -26,7 +26,7 @@ Our current CI parallelization setup is as follows: After that, the next pipeline will use the up-to-date `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file. -### Monitoring +## Monitoring The GitLab test suite is [monitored] for the `master` branch, and any branch that includes `rspec-profile` in their name. diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md index 59eb3ecfd7e..2dc06ba10a5 100644 --- a/doc/development/testing_guide/end_to_end/index.md +++ b/doc/development/testing_guide/end_to_end/index.md @@ -148,7 +148,7 @@ Once you decided where to put [test environment orchestration scenarios] and the [GitLab QA orchestrator README][gitlab-qa-readme], and [the already existing instance-level scenarios][instance-level scenarios]. -Continued reading: +Continued reading: - [Quick Start Guide](quick_start_guide.md) - [Style Guide](style_guide.md) diff --git a/doc/development/testing_guide/end_to_end/quick_start_guide.md b/doc/development/testing_guide/end_to_end/quick_start_guide.md index 041bdf716b3..efcfd44bc22 100644 --- a/doc/development/testing_guide/end_to_end/quick_start_guide.md +++ b/doc/development/testing_guide/end_to_end/quick_start_guide.md @@ -222,7 +222,7 @@ As the pre-conditions for our test suite, the things that needs to happen before - A project being created with an issue and labels already set; - The issue page being opened with only one scoped label applied to it. -> When running end-to-end tests as part of the GitLab's continuous integration process [a license is already set as an environment variable](https://gitlab.com/gitlab-org/gitlab-ee/blob/1a60d926740db10e3b5724713285780a4f470531/qa/qa/ee/strategy.rb#L20). For running tests locally you can set up such license by following the document [what tests can be run?](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md#supported-remote-grid-environment-variables), based on the [supported GitLab environment variables](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md#supported-gitlab-environment-variables). +> When running end-to-end tests as part of the GitLab's continuous integration process [a license is already set as an environment variable](https://gitlab.com/gitlab-org/gitlab-ee/blob/1a60d926740db10e3b5724713285780a4f470531/qa/qa/ee/strategy.rb#L20). For running tests locally you can set up such license by following the document [what tests can be run?](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md), based on the [supported GitLab environment variables](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md#supported-gitlab-environment-variables). #### Implementation @@ -394,15 +394,15 @@ end By defining the `api_get_path` method, we allow the [`ApiFabricator`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/qa/qa/resource/api_fabricator.rb) module to know which path to use to get a single issue. -> This `GET` path can be found in the [public API documentation](https://docs.gitlab.com/ee/api/issues.html#single-issue). +> This `GET` path can be found in the [public API documentation](../../../api/issues.md#single-issue). By defining the `api_post_path` method, we allow the [`ApiFabricator`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/qa/qa/resource/api_fabricator.rb) module to know which path to use to create a new issue in a specific project. -> This `POST` path can be found in the [public API documentation](https://docs.gitlab.com/ee/api/issues.html#new-issue). +> This `POST` path can be found in the [public API documentation](../../../api/issues.md#new-issue). By defining the `api_post_body` method, we allow the [`ApiFabricator.api_post`](https://gitlab.com/gitlab-org/gitlab-ee/blob/a9177ca1812bac57e2b2fa4560e1d5dd8ffac38b/qa/qa/resource/api_fabricator.rb#L68) method to know which data to send when making the `POST` request. -> Notice that we pass both `labels` and `title` attributes in the `api_post_body`, where `labels` receives an array of labels, and [`title` is required](https://docs.gitlab.com/ee/api/issues.html#new-issue). Also, notice that we keep them alphabetically organized. +> Notice that we pass both `labels` and `title` attributes in the `api_post_body`, where `labels` receives an array of labels, and [`title` is required](../../../api/issues.md#new-issue). Also, notice that we keep them alphabetically organized. **Label resource** @@ -441,7 +441,7 @@ By defining the `api_post_path` method, we allow for the [`ApiFabricator `](http By defining the `api_post_body` method, we we allow for the [`ApiFabricator.api_post`](https://gitlab.com/gitlab-org/gitlab-ee/blob/a9177ca1812bac57e2b2fa4560e1d5dd8ffac38b/qa/qa/resource/api_fabricator.rb#L68) method to know which data to send when making the `POST` request. -> Notice that we pass both `color` and `name` attributes in the `api_post_body` since [those are required](https://docs.gitlab.com/ee/api/labels.html#create-a-new-label). Also, notice that we keep them alphabetically organized. +> Notice that we pass both `color` and `name` attributes in the `api_post_body` since [those are required](../../../api/labels.md#create-a-new-label). Also, notice that we keep them alphabetically organized. ### 8. Page Objects diff --git a/doc/development/testing_guide/end_to_end/style_guide.md b/doc/development/testing_guide/end_to_end/style_guide.md index 0272e1810f2..52a8116e01c 100644 --- a/doc/development/testing_guide/end_to_end/style_guide.md +++ b/doc/development/testing_guide/end_to_end/style_guide.md @@ -63,17 +63,17 @@ We follow a simple formula roughly based on hungarian notation. - `_checkbox` - `_radio` - `_content` - + *Note: This list is a work in progress. This list will eventually be the end-all enumeration of all available types. I.e., any element that does not end with something in this list is bad form.* - + #### Examples **Good** ```ruby view '...' do - element :edit_button + element :edit_button element :notes_tab element :squash_checkbox element :username_field @@ -84,15 +84,15 @@ end **Bad** ```ruby -view '...' do +view '...' do # `_confirmation` should be `_field`. what sort of confirmation? a checkbox confirmation? no real way to disambiguate. # an appropriate replacement would be `element :password_confirmation_field` element :password_confirmation - # `clone_options` is too vague. If it's a dropdown menu, it should be `clone_dropdown`. + # `clone_options` is too vague. If it's a dropdown menu, it should be `clone_dropdown`. # If it's a checkbox, it should be `clone_checkbox` element :clone_options - + # how is this url being displayed? is it a textbox? a simple span? element :ssh_clone_url end diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index 98df0b5ea7c..bb44cc595e9 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -501,17 +501,17 @@ The following are examples of tests that work for both Karma and Jest: it('makes a request', () => { const responseBody = getJSONFixture('some/fixture.json'); // loads spec/javascripts/fixtures/some/fixture.json axiosMock.onGet(endpoint).reply(200, responseBody); - + myButton.click(); - + // ... }); it('uses some HTML element', () => { loadFixtures('some/page.html'); // loads spec/javascripts/fixtures/some/page.html and adds it to the DOM - + const element = document.getElementById('#my-id'); - + // ... }); ``` diff --git a/doc/development/testing_guide/index.md b/doc/development/testing_guide/index.md index c4b18391cb2..aadbea1a540 100644 --- a/doc/development/testing_guide/index.md +++ b/doc/development/testing_guide/index.md @@ -11,7 +11,7 @@ importance. ## Overview -GitLab is built on top of [Ruby on Rails][rails], and we're using [RSpec] for all +GitLab is built on top of [Ruby on Rails](https://rubyonrails.org/), and we're using [RSpec] for all the backend tests, with [Capybara] for end-to-end integration testing. On the frontend side, we're using [Karma] and [Jasmine] for JavaScript unit and integration testing. @@ -80,7 +80,6 @@ Everything you should know about how to run end-to-end tests using [Return to Development documentation](../README.md) -[rails]: http://rubyonrails.org/ [RSpec]: https://github.com/rspec/rspec-rails#feature-specs [Capybara]: https://github.com/teamcapybara/capybara [Karma]: http://karma-runner.github.io/ diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md index bfbb7be70e3..11aafd7b639 100644 --- a/doc/development/understanding_explain_plans.md +++ b/doc/development/understanding_explain_plans.md @@ -654,7 +654,6 @@ and related tools such as: - <https://explain.depesz.com/> - <http://tatiyants.com/postgres-query-plan-visualization/> - ## Producing query plans There are a few ways to get the output of a query plan. Of course you diff --git a/doc/development/ux_guide/resources.md b/doc/development/ux_guide/resources.md index baec235a8dd..ae092246d05 100644 --- a/doc/development/ux_guide/resources.md +++ b/doc/development/ux_guide/resources.md @@ -1,5 +1,5 @@ --- -redirect_to: 'https://design.gitlab.com/resources/design-resources' +redirect_to: 'https://design.gitlab.com/resources/design-resources/' --- -The content of this document was moved into the [GitLab Design System](https://design.gitlab.com/). +The content of this document was moved into the [GitLab Design System](https://design.gitlab.com/resources/design-resources/). diff --git a/doc/downgrade_ee_to_ce/README.md b/doc/downgrade_ee_to_ce/README.md index a187b3cbb07..a3f6f2b327c 100644 --- a/doc/downgrade_ee_to_ce/README.md +++ b/doc/downgrade_ee_to_ce/README.md @@ -81,7 +81,7 @@ To downgrade an Omnibus installation, it is sufficient to install the Community Edition package on top of the currently installed one. You can do this manually, by directly [downloading the package](https://packages.gitlab.com/gitlab/gitlab-ce) you need, or by adding our CE package repository and following the -[CE installation instructions](https://about.gitlab.com/installation/?version=ce). +[CE installation instructions](https://about.gitlab.com/install/?version=ce). **Source Installation** diff --git a/doc/gitlab-basics/command-line-commands.md b/doc/gitlab-basics/command-line-commands.md index b7e6844f43a..b8ebbbea9d4 100644 --- a/doc/gitlab-basics/command-line-commands.md +++ b/doc/gitlab-basics/command-line-commands.md @@ -139,6 +139,7 @@ pwd ``` clear ``` + ### Sample Git taskflow If you are completely new to Git, looking through some [sample taskflows](https://rogerdudler.github.io/git-guide/) will help you understand best practices for using these commands as you work. diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md index a9ae4fb23f9..ccba72f0ef8 100644 --- a/doc/gitlab-basics/create-project.md +++ b/doc/gitlab-basics/create-project.md @@ -16,7 +16,7 @@ To create a project in GitLab: - [Import a project](../user/project/import/index.md) from a different repository, if enabled on your GitLab instance. Contact your GitLab admin if this is unavailable. - - Run [CI/CD pipelines for external repositories](../ci/ci_cd_for_external_repos/index.md). **[PREMIUM]** + - Run [CI/CD pipelines for external repositories](../ci/ci_cd_for_external_repos/index.md). **(PREMIUM)** ## Blank projects @@ -69,7 +69,7 @@ TIP: **Tip:** You can improve the existing built-in templates or contribute new ones on the [`project-templates`](https://gitlab.com/gitlab-org/project-templates) and [`pages`](https://gitlab.com/pages) groups. -### Custom project templates **[PREMIUM ONLY]** +### Custom project templates **(PREMIUM ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6860) in [GitLab Premium](https://about.gitlab.com/pricing) 11.2. diff --git a/doc/install/README.md b/doc/install/README.md index 9cc21412898..af98791c8e9 100644 --- a/doc/install/README.md +++ b/doc/install/README.md @@ -4,7 +4,7 @@ description: Read through the GitLab installation methods. type: index --- -# Installation **[CORE ONLY]** +# Installation **(CORE ONLY)** GitLab can be installed in most GNU/Linux distributions and in a number of cloud providers. To get the best experience from GitLab you need to balance diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md index 73eaf758923..fed3b1ca595 100644 --- a/doc/install/aws/index.md +++ b/doc/install/aws/index.md @@ -59,10 +59,11 @@ Here's a list of the AWS services we will use, with links to pricing information - **ElastiCache**: An in-memory cache environment will be used to provide a High Availability Redis configuration. See the [Amazon ElastiCache pricing](https://aws.amazon.com/elasticache/pricing/). - + NOTE: **Note:** Please note that while we will be using EBS for storage, we do not recommend using EFS as it may negatively impact GitLab's performance. You can review the [relevant documentation](../../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs) for more details. ## Creating an IAM EC2 instance role and profile + To minimize the permissions of the user, we'll create a new [IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) role with limited access: @@ -90,7 +91,7 @@ We'll now create a VPC, a virtual networking environment that you'll control: `10.0.0.0/16`. If you don't require dedicated hardware, you can leave "Tenancy" as default. Click **Yes, Create** when ready. - ![Create VPC](img/create_vpc.png) + ![Create VPC](img/create_vpc.png) ### Subnets @@ -107,16 +108,16 @@ RDS instances as well: for example `gitlab-public-10.0.0.0`, select the VPC we created previously, and at the IPv4 CIDR block let's give it a 24 subnet `10.0.0.0/24`: - ![Create subnet](img/create_subnet.png) + ![Create subnet](img/create_subnet.png) 1. Follow the same steps to create all subnets: - | Name tag | Type |Availability Zone | CIDR block | - | -------- | ---- | ---------------- | ---------- | - | gitlab-public-10.0.0.0 | public | us-west-2a | 10.0.0.0 | - | gitlab-private-10.0.1.0 | private | us-west-2a | 10.0.1.0 | - | gitlab-public-10.0.2.0 | public | us-west-2b | 10.0.2.0 | - | gitlab-private-10.0.3.0 | private | us-west-2b | 10.0.3.0 | + | Name tag | Type |Availability Zone | CIDR block | + | -------- | ---- | ---------------- | ---------- | + | gitlab-public-10.0.0.0 | public | us-west-2a | 10.0.0.0 | + | gitlab-private-10.0.1.0 | private | us-west-2a | 10.0.1.0 | + | gitlab-public-10.0.2.0 | public | us-west-2b | 10.0.2.0 | + | gitlab-private-10.0.3.0 | private | us-west-2b | 10.0.3.0 | ### Route Table @@ -139,7 +140,7 @@ create a new one: 1. Select it from the table, and then under the **Actions** dropdown choose "Attach to VPC". - ![Create gateway](img/create_gateway.png) + ![Create gateway](img/create_gateway.png) 1. Choose `gitlab-vpc` from the list and hit **Attach**. @@ -154,14 +155,14 @@ it receive traffic from any destination. as destination. In the target, select the `gitlab-gateway` we created previously. Hit **Save** once done. - ![Associate subnet with gateway](img/associate_subnet_gateway.png) + ![Associate subnet with gateway](img/associate_subnet_gateway.png) Next, we must associate the **public** subnets to the route table: 1. Select the **Subnet Associations** tab and hit **Edit**. 1. Check only the public subnet and hit **Save**. - ![Associate subnet with gateway](img/associate_subnet_gateway_2.png) + ![Associate subnet with gateway](img/associate_subnet_gateway_2.png) --- @@ -178,12 +179,12 @@ The security group is basically the firewall: Inbound Rules tab. You will need to open the SSH, HTTP, and HTTPS ports. Set the source to `0.0.0.0/0`. - ![Create security group](img/create_security_group.png) + ![Create security group](img/create_security_group.png) - TIP: **Tip:** - Based on best practices, you should allow SSH traffic from only a known - host or CIDR block. In that case, change the SSH source to be custom and give - it the IP you want to SSH from. + TIP: **Tip:** + Based on best practices, you should allow SSH traffic from only a known + host or CIDR block. In that case, change the SSH source to be custom and give + it the IP you want to SSH from. 1. When done, click **Save**. @@ -204,7 +205,7 @@ create the actual RDS instance. we defined them in the [subnets section](#subnets)). Click **Create** when ready. - ![RDS Subnet Group](img/rds_subnet_group.png) + ![RDS Subnet Group](img/rds_subnet_group.png) ### Creating the database @@ -214,27 +215,27 @@ Now, it's time to create the database: 1. Select PostgreSQL and click **Next**. 1. Since this is a production server, let's choose "Production". Click **Next**. 1. Let's see the instance specifications: - 1. Leave the license model as is (`postgresql-license`). - 1. For the version, select the latest of the 9.6 series (check the - [database requirements](../../install/requirements.md#postgresql-requirements)) - if there are any updates on this). - 1. For the size, let's select a `t2.medium` instance. - 1. Multi-AZ-deployment is recommended as redundancy, so choose "Create - replica in different zone". Read more at - [High Availability (Multi-AZ)](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html). - 1. A Provisioned IOPS (SSD) storage type is best suited for HA (though you can - choose a General Purpose (SSD) to reduce the costs). Read more about it at - [Storage for Amazon RDS](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html). - -1. The rest of the settings on this page request a DB isntance identifier, username + 1. Leave the license model as is (`postgresql-license`). + 1. For the version, select the latest of the 9.6 series (check the + [database requirements](../../install/requirements.md#postgresql-requirements)) + if there are any updates on this). + 1. For the size, let's select a `t2.medium` instance. + 1. Multi-AZ-deployment is recommended as redundancy, so choose "Create + replica in different zone". Read more at + [High Availability (Multi-AZ)](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html). + 1. A Provisioned IOPS (SSD) storage type is best suited for HA (though you can + choose a General Purpose (SSD) to reduce the costs). Read more about it at + [Storage for Amazon RDS](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html). + +1. The rest of the settings on this page request a DB instance identifier, username and a master password. We've chosen to use `gitlab-db-ha`, `gitlab` and a very secure password respectively. Keep these in hand for later. 1. Click **Next** to proceed to the advanced settings. 1. Make sure to choose our gitlab VPC, our subnet group, set public accessibility to **No**, and to leave it to create a new security group. The only additional - change which will be helpful is the database name for which we can use - `gitlabhq_production`. At the very bottom, there's an option to enable - auto updates to minor versions. You may want to turn it off. + change which will be helpful is the database name for which we can use + `gitlabhq_production`. At the very bottom, there's an option to enable + auto updates to minor versions. You may want to turn it off. 1. When done, click **Create database**. ### Installing the `pg_trgm` extension for PostgreSQL @@ -276,7 +277,7 @@ To set up Redis: Make sure to select our VPC and its [private subnets](#subnets). Click **Create** when ready. - ![ElastiCache subnet](img/ec_subnet.png) + ![ElastiCache subnet](img/ec_subnet.png) 1. Select **Redis** on the left menu and click **Create** to create a new Redis cluster. Depending on your load, you can choose whether to enable @@ -284,16 +285,16 @@ To set up Redis: chance to deploy Redis in multi availability zones. In this guide, we chose not to enable it. 1. In the settings section: - 1. Give the cluster a name (`gitlab-redis`) and a description. - 1. For the version, select the latest of `3.2` series (e.g., `3.2.10`). - 1. Select the node type and the number of replicas. + 1. Give the cluster a name (`gitlab-redis`) and a description. + 1. For the version, select the latest of `3.2` series (e.g., `3.2.10`). + 1. Select the node type and the number of replicas. 1. In the advanced settings section: 1. Select the multi-AZ auto-failover option. 1. Select the subnet group we created previously. 1. Manually select the preferred availability zones, and under "Replica 2" choose a different zone than the other two. - ![Redis availability zones](img/ec_az.png) + ![Redis availability zones](img/ec_az.png) 1. In the security settings, edit the security groups and choose the `gitlab-security-group` we had previously created. @@ -316,11 +317,11 @@ and add a custom TCP rule for port `6379` accessible within itself. On the EC2 dashboard, look for Load Balancer on the left column: 1. Click the **Create Load Balancer** button. - 1. Choose the Application Load Balancer. - 1. Give it a name (`gitlab-loadbalancer`) and set the scheme to "internet-facing". - 1. In the "Listeners" section, make sure it has HTTP and HTTPS. - 1. In the "Availability Zones" section, select the `gitlab-vpc` we have created - and associate the **public subnets**. + 1. Choose the Application Load Balancer. + 1. Give it a name (`gitlab-loadbalancer`) and set the scheme to "internet-facing". + 1. In the "Listeners" section, make sure it has HTTP and HTTPS. + 1. In the "Availability Zones" section, select the `gitlab-vpc` we have created + and associate the **public subnets**. 1. Click **Configure Security Settings** to go to the next section to select the TLS certificate. When done, go to the next step. 1. In the "Security Groups" section, create a new one by giving it a name @@ -355,7 +356,7 @@ Choose the AMI: where `<version>` the latest version as seen on the [releases page](https://about.gitlab.com/releases/). - ![Choose AMI](img/choose_ami.png) + ![Choose AMI](img/choose_ami.png) ### Choose an instance type @@ -504,19 +505,19 @@ The EBS volume will host the Git repositories data: 1. Tell GitLab to store its data in the new directory by editing `/etc/gitlab/gitlab.rb` with your editor: - ```ruby - git_data_dirs({ - "default" => { "path" => "/mnt/gitlab-data" } - }) - ``` + ```ruby + git_data_dirs({ + "default" => { "path" => "/mnt/gitlab-data" } + }) + ``` - where `/mnt/gitlab-data` the location where you will store the Git data. + where `/mnt/gitlab-data` the location where you will store the Git data. 1. Save the file and reconfigure GitLab: - ```sh - sudo gitlab-ctl reconfigure - ``` + ```sh + sudo gitlab-ctl reconfigure + ``` TIP: **Tip:** If you wish to add more than one data volumes to store the Git repositories, @@ -549,15 +550,15 @@ After you SSH into the instance, configure the domain name: 1. Open `/etc/gitlab/gitlab.rb` with your preferred editor. 1. Edit the `external_url` value: - ```ruby - external_url 'http://example.com' - ``` + ```ruby + external_url 'http://example.com' + ``` 1. Reconfigure GitLab: - ```sh - sudo gitlab-ctl reconfigure - ``` + ```sh + sudo gitlab-ctl reconfigure + ``` You should now be able to reach GitLab at the URL you defined. To use HTTPS (recommended), see the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https). @@ -608,9 +609,9 @@ To back up GitLab: 1. SSH into your instance. 1. Take a backup: - ```sh - sudo gitlab-rake gitlab:backup:create - ``` + ```sh + sudo gitlab-rake gitlab:backup:create + ``` ### Restoring GitLab from a backup @@ -626,16 +627,16 @@ released, you can update your GitLab instance: 1. SSH into your instance 1. Take a backup: - ```sh - sudo gitlab-rake gitlab:backup:create - ``` + ```sh + sudo gitlab-rake gitlab:backup:create + ``` 1. Update the repositories and install GitLab: - ```sh - sudo apt update - sudo apt install gitlab-ee - ``` + ```sh + sudo apt update + sudo apt install gitlab-ee + ``` After a few minutes, the new version should be up and running. diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md index b1f79893baf..c0e1b0ebbc8 100644 --- a/doc/install/azure/index.md +++ b/doc/install/azure/index.md @@ -67,18 +67,19 @@ The first items we need to configure are the basic settings of the underlying vi 1. Enter a `User name` - e.g. **"gitlab-admin"** 1. Select an `Authentication type`, either **SSH public key** or **Password**: - > **Note:** if you're unsure which authentication type to use, select **Password** + > **Note:** if you're unsure which authentication type to use, select **Password** + + 1. If you chose **SSH public key** - enter your `SSH public key` into the field provided + _(read the [SSH documentation][GitLab-Docs-SSH] to learn more about how to set up SSH + public keys)_ + 1. If you chose **Password** - enter the password you wish to use _(this is the password that you + will use later in this tutorial to [SSH] into the VM, so make sure it's a strong password/passphrase)_ - 1. If you chose **SSH public key** - enter your `SSH public key` into the field provided - _(read the [SSH documentation][GitLab-Docs-SSH] to learn more about how to set up SSH - public keys)_ - 1. If you chose **Password** - enter the password you wish to use _(this is the password that you - will use later in this tutorial to [SSH] into the VM, so make sure it's a strong password/passphrase)_ 1. Choose the appropriate `Subscription` tier for your Azure account 1. Choose an existing `Resource Group` or create a new one - e.g. **"GitLab-CE-Azure"** - > **Note:** a "Resource group" is a way to group related resources together for easier administration. - > We chose "GitLab-CE-Azure", but your resource group can have the same name as your VM. + > **Note:** a "Resource group" is a way to group related resources together for easier administration. + > We chose "GitLab-CE-Azure", but your resource group can have the same name as your VM. 1. Choose a `Location` - if you're unsure, select the default location @@ -248,6 +249,7 @@ rules in the list: ![Azure - Inbound security rules - List](img/azure-inbound-sec-rules-list.png) ## Connecting to GitLab + Use the domain name you set up earlier (or the public IP address) to visit your new GitLab instance in your browser. If everything has gone according to plan you should be presented with the following page, asking you to set a _new_ password for the administrator account automatically @@ -348,6 +350,7 @@ your VM, you can use the IP address in its place in the following command: ```bash ssh username@your-azure-domain-name.com ``` + Provide your password at the prompt to authenticate. #### SSH from Windows (PuTTY) @@ -411,12 +414,12 @@ Check out our other [Technical Articles][GitLab-Technical-Articles] or browse th - [GitLab Community Edition][CE] - [GitLab Enterprise Edition][EE] - [Microsoft Azure][Azure] - - [Azure - Free Account FAQ][Azure-Free-Account-FAQ] - - [Azure - Marketplace][Azure-Marketplace] - - [Azure Portal][Azure-Portal] - - [Azure - Pricing Calculator][Azure-Pricing-Calculator] - - [Azure - Troubleshoot SSH Connections to an Azure Linux VM][Azure-Troubleshoot-SSH-Connection] - - [Azure - Properly Shutdown an Azure VM][Azure-Properly-Shutdown-VM] + - [Azure - Free Account FAQ][Azure-Free-Account-FAQ] + - [Azure - Marketplace][Azure-Marketplace] + - [Azure Portal][Azure-Portal] + - [Azure - Pricing Calculator][Azure-Pricing-Calculator] + - [Azure - Troubleshoot SSH Connections to an Azure Linux VM][Azure-Troubleshoot-SSH-Connection] + - [Azure - Properly Shutdown an Azure VM][Azure-Properly-Shutdown-VM] - [SSH], [PuTTY] and [Using SSH in PuTTY][Using-SSH-In-Putty] [Original-Blog-Post]: https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/ "How to Set up a GitLab Instance on Microsoft Azure" diff --git a/doc/install/digitaloceandocker.md b/doc/install/digitaloceandocker.md index 63bb941ad47..b6bf7c95527 100644 --- a/doc/install/digitaloceandocker.md +++ b/doc/install/digitaloceandocker.md @@ -36,30 +36,30 @@ The rest of the steps are identical for macOS and Linux. 1. Login to Digital Ocean. 1. Generate a new API token at <https://cloud.digitalocean.com/settings/api/tokens>. - This command will create a new DO droplet called `gitlab-test-env-do` that will act as a docker host. + This command will create a new DO droplet called `gitlab-test-env-do` that will act as a docker host. - NOTE: **Note:** - 4GB is the minimum requirement for a Docker host that will run more than one GitLab instance. + NOTE: **Note:** + 4GB is the minimum requirement for a Docker host that will run more than one GitLab instance. - - RAM: 4GB - - Name: `gitlab-test-env-do` - - Driver: `digitalocean` + - RAM: 4GB + - Name: `gitlab-test-env-do` + - Driver: `digitalocean` 1. Set the DO token: - ```sh - export DOTOKEN=<your generated token> - ``` + ```sh + export DOTOKEN=<your generated token> + ``` 1. Create the machine: - ```sh - docker-machine create \ - --driver digitalocean \ - --digitalocean-access-token=$DOTOKEN \ - --digitalocean-size "4gb" \ - gitlab-test-env-do - ``` + ```sh + docker-machine create \ + --driver digitalocean \ + --digitalocean-access-token=$DOTOKEN \ + --digitalocean-size "4gb" \ + gitlab-test-env-do + ``` Resource: <https://docs.docker.com/machine/drivers/digital-ocean/>. diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md index 77c61acbfd4..be29bcc7cd7 100644 --- a/doc/install/google_cloud_platform/index.md +++ b/doc/install/google_cloud_platform/index.md @@ -30,16 +30,16 @@ To deploy GitLab on GCP you first need to create a virtual machine: 1. Go to <https://console.cloud.google.com/compute/instances> and log in with your Google credentials. 1. Click on **Create** - ![Search for GitLab](img/launch_vm.png) + ![Search for GitLab](img/launch_vm.png) -1. On the next page, you can select the type of VM as well as the +1. On the next page, you can select the type of VM as well as the estimated costs. Provide the name of the instance, desired datacenter, and machine type. Note that GitLab recommends at least 2 vCPU's and 4GB of RAM. - ![Launch on Compute Engine](img/vm_details.png) + ![Launch on Compute Engine](img/vm_details.png) 1. Click **Change** under Boot disk to select the size, type, and desired operating system. GitLab supports a [variety of linux operating systems][req], including Ubuntu and Debian. Click **Select** when finished. - ![Deploy in progress](img/boot_disk.png) + ![Deploy in progress](img/boot_disk.png) 1. As a last step allow HTTP and HTTPS traffic, then click **Create**. The process will finish in a few seconds. @@ -53,13 +53,13 @@ After a few seconds, the instance will be created and available to log in. The n 1. Click on the SSH button to connect to the instance. 1. A new window will appear, with you logged into the instance. - ![GitLab first sign in](img/ssh_terminal.png) + ![GitLab first sign in](img/ssh_terminal.png) -1. Next, follow the instructions for installing GitLab for the operating system you choose, at <https://about.gitlab.com/installation/>. You can use the IP address from the step above, as the hostname. +1. Next, follow the instructions for installing GitLab for the operating system you choose, at <https://about.gitlab.com/install/>. You can use the IP address from the step above, as the hostname. 1. Congratulations! GitLab is now installed and you can access it via your browser. To finish installation, open the URL in your browser and provide the initial administrator password. The username for this account is `root`. - ![GitLab first sign in](img/first_signin.png) + ![GitLab first sign in](img/first_signin.png) ## Next steps @@ -83,31 +83,31 @@ here's how you configure GitLab to be aware of the change: 1. SSH into the VM. You can easily use the **SSH** button in the Google console and a new window will pop up. - ![SSH button](img/vm_created.png) + ![SSH button](img/vm_created.png) - In the future you might want to set up [connecting with an SSH key][ssh] - instead. + In the future you might want to set up [connecting with an SSH key][ssh] + instead. 1. Edit the config file of Omnibus GitLab using your favorite text editor: - ``` - sudo vim /etc/gitlab/gitlab.rb - ``` + ``` + sudo vim /etc/gitlab/gitlab.rb + ``` 1. Set the `external_url` value to the domain name you wish GitLab to have **without** `https`: - ``` - external_url 'http://gitlab.example.com' - ``` + ``` + external_url 'http://gitlab.example.com' + ``` - We will set up HTTPS in the next step, no need to do this now. + We will set up HTTPS in the next step, no need to do this now. 1. Reconfigure GitLab for the changes to take effect: - ``` - sudo gitlab-ctl reconfigure - ``` + ``` + sudo gitlab-ctl reconfigure + ``` 1. You can now visit GitLab using the domain name. @@ -128,9 +128,9 @@ GitLab can be configured to authenticate with other OAuth providers, LDAP, SAML, Kerberos, etc. Here are some documents you might be interested in reading: - [Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/) -- [Integration documentation](https://docs.gitlab.com/ce/integration/) -- [GitLab Pages configuration](https://docs.gitlab.com/ce/administration/pages/index.html) -- [GitLab Container Registry configuration](https://docs.gitlab.com/ce/administration/container_registry.html) +- [Integration documentation](../../integration/README.md) +- [GitLab Pages configuration](../../administration/pages/index.md) +- [GitLab Container Registry configuration](../../administration/container_registry.md) [freetrial]: https://console.cloud.google.com/freetrial "GCP free trial" [ip]: https://cloud.google.com/compute/docs/configure-instance-ip-addresses#promote_ephemeral_ip "Configuring an Instance's IP Addresses" diff --git a/doc/install/installation.md b/doc/install/installation.md index 70e5ab28931..06ec00cecc4 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -167,7 +167,7 @@ cd pcre2-10.33 chmod +x configure ./configure --prefix=/usr --enable-jit make -make install +sudo make install # Download and compile from source cd /tmp @@ -299,57 +299,57 @@ use of extensions and concurrent index removal, you need at least PostgreSQL 9.2 1. Install the database packages: - ```sh - sudo apt-get install -y postgresql postgresql-client libpq-dev postgresql-contrib - ``` + ```sh + sudo apt-get install -y postgresql postgresql-client libpq-dev postgresql-contrib + ``` 1. Create a database user for GitLab: - ```sh - sudo -u postgres psql -d template1 -c "CREATE USER git CREATEDB;" - ``` + ```sh + sudo -u postgres psql -d template1 -c "CREATE USER git CREATEDB;" + ``` 1. Create the `pg_trgm` extension (required for GitLab 8.6+): - ```sh - sudo -u postgres psql -d template1 -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" - ``` + ```sh + sudo -u postgres psql -d template1 -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" + ``` 1. Create the GitLab production database and grant all privileges on database: - ```sh - sudo -u postgres psql -d template1 -c "CREATE DATABASE gitlabhq_production OWNER git;" - ``` + ```sh + sudo -u postgres psql -d template1 -c "CREATE DATABASE gitlabhq_production OWNER git;" + ``` 1. Try connecting to the new database with the new user: - ```sh - sudo -u git -H psql -d gitlabhq_production - ``` + ```sh + sudo -u git -H psql -d gitlabhq_production + ``` 1. Check if the `pg_trgm` extension is enabled: - ```sh - SELECT true AS enabled - FROM pg_available_extensions - WHERE name = 'pg_trgm' - AND installed_version IS NOT NULL; - ``` + ```sh + SELECT true AS enabled + FROM pg_available_extensions + WHERE name = 'pg_trgm' + AND installed_version IS NOT NULL; + ``` - If the extension is enabled this will produce the following output: + If the extension is enabled this will produce the following output: - ``` - enabled - --------- - t - (1 row) - ``` + ``` + enabled + --------- + t + (1 row) + ``` 1. Quit the database session: - ```sh - gitlabhq_production> \q - ``` + ```sh + gitlabhq_production> \q + ``` ## 7. Redis @@ -634,8 +634,8 @@ Gitaly must be running for the next section. gitlab_path=/home/git/gitlab gitaly_path=/home/git/gitaly -sudo -u git -H $gitlab_path/bin/daemon_with_pidfile $gitlab_path/tmp/pids/gitaly.pid \ - $gitaly_path/gitaly $gitaly_path/config.toml >> $gitlab_path/log/gitaly.log 2>&1 & +sudo -u git -H sh -c "$gitlab_path/bin/daemon_with_pidfile $gitlab_path/tmp/pids/gitaly.pid \ + $gitaly_path/gitaly $gitaly_path/config.toml >> $gitlab_path/log/gitaly.log 2>&1 &" ``` ### Initialize Database and Activate Advanced Features @@ -831,26 +831,27 @@ how to configure GitLab with a relative URL. To use GitLab with HTTPS: 1. In `gitlab.yml`: - 1. Set the `port` option in section 1 to `443`. - 1. Set the `https` option in section 1 to `true`. + 1. Set the `port` option in section 1 to `443`. + 1. Set the `https` option in section 1 to `true`. 1. In the `config.yml` of gitlab-shell: - 1. Set `gitlab_url` option to the HTTPS endpoint of GitLab (e.g. `https://git.example.com`). - 1. Set the certificates using either the `ca_file` or `ca_path` option. + 1. Set `gitlab_url` option to the HTTPS endpoint of GitLab (e.g. `https://git.example.com`). + 1. Set the certificates using either the `ca_file` or `ca_path` option. 1. Use the `gitlab-ssl` Nginx example config instead of the `gitlab` config. - 1. Update `YOUR_SERVER_FQDN`. - 1. Update `ssl_certificate` and `ssl_certificate_key`. - 1. Review the configuration file and consider applying other security and performance enhancing features. + 1. Update `YOUR_SERVER_FQDN`. + 1. Update `ssl_certificate` and `ssl_certificate_key`. + 1. Review the configuration file and consider applying other security and performance enhancing features. Using a self-signed certificate is discouraged but if you must use it, follow the normal directions. Then: 1. Generate a self-signed SSL certificate: - ```sh - mkdir -p /etc/nginx/ssl/ - cd /etc/nginx/ssl/ - sudo openssl req -newkey rsa:2048 -x509 -nodes -days 3560 -out gitlab.crt -keyout gitlab.key - sudo chmod o-r gitlab.key - ``` + ```sh + mkdir -p /etc/nginx/ssl/ + cd /etc/nginx/ssl/ + sudo openssl req -newkey rsa:2048 -x509 -nodes -days 3560 -out gitlab.crt -keyout gitlab.key + sudo chmod o-r gitlab.key + ``` + 1. In the `config.yml` of gitlab-shell set `self_signed_cert` to `true`. ### Enable Reply by email diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md index 45d07ec5d11..fbbe2a34952 100644 --- a/doc/install/openshift_and_gitlab/index.md +++ b/doc/install/openshift_and_gitlab/index.md @@ -13,8 +13,8 @@ for details. ## Introduction -[OpenShift Origin][openshift] is an open source container application -platform created by [RedHat], based on [kubernetes] and [Docker]. That means +[OpenShift Origin](https://www.okd.io/) (**Note:** renamed to OKD in Aug 2018) is an open source container application +platform created by [RedHat], based on [kubernetes](https://kubernetes.io/) and [Docker]. That means you can host your own PaaS for free and almost with no hassle. In this tutorial, we will see how to deploy GitLab in OpenShift using GitLab's @@ -27,8 +27,11 @@ For a video demonstration on installing GitLab on OpenShift, check the article [ ## Prerequisites -OpenShift 3 is not yet deployed on RedHat's offered Online platform ([openshift.com]), -so in order to test it, we will use an [all-in-one Virtualbox image][vm] that is +CAUTION: **Caution:** This information is no longer up to date, as the current versions +have changed and products have been renamed. + +OpenShift 3 is not yet deployed on RedHat's offered Online platform, [openshift.com](https://www.openshift.com/), +so in order to test it, we will use an [all-in-one Virtualbox image](https://www.okd.io/minishift/) that is offered by the OpenShift developers and managed by Vagrant. If you haven't done already, go ahead and install the following components as they are essential to test OpenShift easily: @@ -67,17 +70,17 @@ In short: 1. Open a terminal and in a new directory run: - ```sh - vagrant init openshift/origin-all-in-one - ``` + ```sh + vagrant init openshift/origin-all-in-one + ``` 1. This will generate a Vagrantfile based on the all-in-one VM image 1. In the same directory where you generated the Vagrantfile enter: - ```sh - vagrant up - ``` + ```sh + vagrant up + ``` This will download the VirtualBox image and fire up the VM with some preconfigured values as you can see in the Vagrantfile. As you may have noticed, you need @@ -192,22 +195,22 @@ In that case, the OpenShift service might not be running, so in order to fix it: 1. SSH into the VM by going to the directory where the Vagrantfile is and then run: - ```sh - vagrant ssh - ``` + ```sh + vagrant ssh + ``` 1. Run `systemctl` and verify by the output that the `openshift` service is not running (it will be in red color). If that's the case start the service with: - ```sh - sudo systemctl start openshift - ``` + ```sh + sudo systemctl start openshift + ``` 1. Verify the service is up with: - ```sh - systemctl status openshift -l - ``` + ```sh + systemctl status openshift -l + ``` Now you will be able to login using `oc` (like we did before) and visit the web console. @@ -390,55 +393,55 @@ Let's see how to do that using the following steps. 1. Make sure you are in the `gitlab` project: - ```sh - oc project gitlab - ``` + ```sh + oc project gitlab + ``` 1. See what services are used for this project: - ```sh - oc get svc - ``` + ```sh + oc get svc + ``` - The output will be similar to: + The output will be similar to: - ``` - NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE - gitlab-ce 172.30.243.177 <none> 22/TCP,80/TCP 5d - gitlab-ce-postgresql 172.30.116.75 <none> 5432/TCP 5d - gitlab-ce-redis 172.30.105.88 <none> 6379/TCP 5d - ``` + ``` + NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE + gitlab-ce 172.30.243.177 <none> 22/TCP,80/TCP 5d + gitlab-ce-postgresql 172.30.116.75 <none> 5432/TCP 5d + gitlab-ce-redis 172.30.105.88 <none> 6379/TCP 5d + ``` 1. We need to see the replication controllers of the `gitlab-ce` service. Get a detailed view of the current ones: - ```sh - oc describe rc gitlab-ce - ``` + ```sh + oc describe rc gitlab-ce + ``` - This will return a large detailed list of the current replication controllers. - Search for the name of the GitLab controller, usually `gitlab-ce-1` or if - that failed at some point and you spawned another one, it will be named - `gitlab-ce-2`. + This will return a large detailed list of the current replication controllers. + Search for the name of the GitLab controller, usually `gitlab-ce-1` or if + that failed at some point and you spawned another one, it will be named + `gitlab-ce-2`. 1. Scale GitLab using the previous information: - ```sh - oc scale --replicas=2 replicationcontrollers gitlab-ce-2 - ``` + ```sh + oc scale --replicas=2 replicationcontrollers gitlab-ce-2 + ``` 1. Get the new replicas number to make sure scaling worked: - ```sh - oc get rc gitlab-ce-2 - ``` + ```sh + oc get rc gitlab-ce-2 + ``` - which will return something like: + which will return something like: - ``` - NAME DESIRED CURRENT AGE - gitlab-ce-2 2 2 5d - ``` + ``` + NAME DESIRED CURRENT AGE + gitlab-ce-2 2 2 5d + ``` And that's it! We successfully scaled the replicas to 2 using the CLI. @@ -458,7 +461,7 @@ OpenShift's website about [autoscaling]. ## Current limitations -As stated in the [all-in-one VM][vm] page: +As stated in the [all-in-one VM](https://www.okd.io/minishift/) page: > By default, OpenShift will not allow a container to run as root or even a non-random container assigned userid. Most Docker images in the Dockerhub do not @@ -475,13 +478,13 @@ For OpenShift v3.0, you will need to do this manually: 1. Edit the Security Context: - ```sh - oc edit scc anyuid - ``` + ```sh + oc edit scc anyuid + ``` 1. Add `system:serviceaccount:<project>:gitlab-ce-user` to the `users` section. If you changed the Application Name from the default the user will - will be `<app-name>-user` instead of `gitlab-ce-user` + will be `<app-name>-user` instead of `gitlab-ce-user` 1. Save and exit the editor @@ -506,12 +509,8 @@ is capable of. As always, you can refer to the detailed PaaS and managing your applications with the ease of containers. [RedHat]: https://www.redhat.com/en "RedHat website" -[openshift]: https://www.openshift.org "OpenShift Origin website" -[vm]: https://www.openshift.org/vm/ "OpenShift All-in-one VM" [vm-new]: https://app.vagrantup.com/openshift/boxes/origin-all-in-one "Official OpenShift Vagrant box on Vagrant Cloud" [template]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/docker/openshift-template.json "OpenShift template for GitLab" -[openshift.com]: https://openshift.com "OpenShift Online" -[kubernetes]: http://kubernetes.io/ "Kubernetes website" [Docker]: https://www.docker.com "Docker website" [oc]: https://docs.openshift.org/latest/cli_reference/get_started_cli.html "Documentation - oc CLI documentation" [VirtualBox]: https://www.virtualbox.org/wiki/Downloads "VirtualBox downloads" diff --git a/doc/install/pivotal/index.md b/doc/install/pivotal/index.md index f068572f1e9..6a4b361c842 100644 --- a/doc/install/pivotal/index.md +++ b/doc/install/pivotal/index.md @@ -1,4 +1,4 @@ -# GitLab Pivotal Tile **[PREMIUM ONLY]** +# GitLab Pivotal Tile **(PREMIUM ONLY)** CAUTION: **Discontinued:** As of September 13, 2017, the GitLab Enterprise Plus for Pivotal Cloud Foundry diff --git a/doc/install/relative_url.md b/doc/install/relative_url.md index b53624a33bf..bc6364f57f7 100644 --- a/doc/install/relative_url.md +++ b/doc/install/relative_url.md @@ -58,59 +58,59 @@ assumptions are made: Make sure to follow all steps below: -1. (Optional) If you run short on resources, you can temporarily free up some - memory by shutting down the GitLab service with the following command: +1. (Optional) If you run short on resources, you can temporarily free up some + memory by shutting down the GitLab service with the following command: - ```shell - sudo service gitlab stop - ``` + ```shell + sudo service gitlab stop + ``` -1. Create `/home/git/gitlab/config/initializers/relative_url.rb` +1. Create `/home/git/gitlab/config/initializers/relative_url.rb` - ```shell - cp /home/git/gitlab/config/initializers/relative_url.rb.sample \ - /home/git/gitlab/config/initializers/relative_url.rb - ``` + ```shell + cp /home/git/gitlab/config/initializers/relative_url.rb.sample \ + /home/git/gitlab/config/initializers/relative_url.rb + ``` - and change the following line: + and change the following line: - ```ruby - config.relative_url_root = "/gitlab" - ``` + ```ruby + config.relative_url_root = "/gitlab" + ``` -1. Edit `/home/git/gitlab/config/gitlab.yml` and uncomment/change the - following line: +1. Edit `/home/git/gitlab/config/gitlab.yml` and uncomment/change the + following line: - ```yaml - relative_url_root: /gitlab - ``` + ```yaml + relative_url_root: /gitlab + ``` -1. Edit `/home/git/gitlab/config/unicorn.rb` and uncomment/change the - following line: +1. Edit `/home/git/gitlab/config/unicorn.rb` and uncomment/change the + following line: - ```ruby - ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab" - ``` + ```ruby + ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab" + ``` -1. Edit `/home/git/gitlab-shell/config.yml` and append the relative path to - the following line: +1. Edit `/home/git/gitlab-shell/config.yml` and append the relative path to + the following line: - ```yaml - gitlab_url: http://127.0.0.1/gitlab - ``` + ```yaml + gitlab_url: http://127.0.0.1/gitlab + ``` -1. Make sure you have copied the supplied init script and the defaults file - as stated in the [installation guide](installation.md#install-init-script). - Then, edit `/etc/default/gitlab` and set in `gitlab_workhorse_options` the - `-authBackend` setting to read like: +1. Make sure you have copied the supplied init script and the defaults file + as stated in the [installation guide](installation.md#install-init-script). + Then, edit `/etc/default/gitlab` and set in `gitlab_workhorse_options` the + `-authBackend` setting to read like: - ```shell - -authBackend http://127.0.0.1:8080/gitlab - ``` + ```shell + -authBackend http://127.0.0.1:8080/gitlab + ``` - **Note:** - If you are using a custom init script, make sure to edit the above - gitlab-workhorse setting as needed. + **Note:** + If you are using a custom init script, make sure to edit the above + gitlab-workhorse setting as needed. 1. [Restart GitLab][] for the changes to take effect. @@ -118,9 +118,9 @@ Make sure to follow all steps below: To disable the relative URL: -1. Remove `/home/git/gitlab/config/initializers/relative_url.rb` +1. Remove `/home/git/gitlab/config/initializers/relative_url.rb` -1. Follow the same as above starting from 2. and set up the +1. Follow the same as above starting from 2. and set up the GitLab URL to one that doesn't contain a relative path. [omnibus-rel]: https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to set up relative URL in Omnibus GitLab" diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 68c1bcbc801..25ab608de3a 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -30,7 +30,7 @@ For the installations options, see [the main installation page](README.md). - macOS Installation of GitLab on these operating systems is possible, but not supported. -Please see the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/installation/) for more information. +Please see the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/install/) for more information. ### Microsoft Windows diff --git a/doc/integration/akismet.md b/doc/integration/akismet.md index 4f7be70baf2..cb8f25d2895 100644 --- a/doc/integration/akismet.md +++ b/doc/integration/akismet.md @@ -3,7 +3,7 @@ > *Note:* Before 8.11 only issues submitted via the API and for non-project members were submitted to Akismet. -GitLab leverages [Akismet](http://akismet.com) to protect against spam. Currently +GitLab leverages [Akismet](https://akismet.com/) to protect against spam. Currently GitLab uses Akismet to prevent the creation of spam issues on public projects. Issues created via the WebUI or the API can be submitted to Akismet for review. diff --git a/doc/integration/auth0.md b/doc/integration/auth0.md index c67375ede50..5061b863e79 100644 --- a/doc/integration/auth0.md +++ b/doc/integration/auth0.md @@ -16,64 +16,64 @@ application. 1. At the top of the Settings screen, you should see your Domain, Client ID and Client Secret. Take note of these as you'll need to put them in the configuration file. For example: - - Domain: `test1234.auth0.com` - - Client ID: `t6X8L2465bNePWLOvt9yi41i` - - Client Secret: `KbveM3nqfjwCbrhaUy_gDu2dss8TIlHIdzlyf33pB7dEK5u_NyQdp65O_o02hXs2` + - Domain: `test1234.auth0.com` + - Client ID: `t6X8L2465bNePWLOvt9yi41i` + - Client Secret: `KbveM3nqfjwCbrhaUy_gDu2dss8TIlHIdzlyf33pB7dEK5u_NyQdp65O_o02hXs2` 1. Fill in the Allowed Callback URLs: - - `http://YOUR_GITLAB_URL/users/auth/auth0/callback` (or) - - `https://YOUR_GITLAB_URL/users/auth/auth0/callback` + - `http://YOUR_GITLAB_URL/users/auth/auth0/callback` (or) + - `https://YOUR_GITLAB_URL/users/auth/auth0/callback` 1. Fill in the Allowed Origins (CORS): - - `http://YOUR_GITLAB_URL` (or) - - `https://YOUR_GITLAB_URL` + - `http://YOUR_GITLAB_URL` (or) + - `https://YOUR_GITLAB_URL` 1. On your GitLab server, open the configuration file. - For omnibus package: + For omnibus package: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + ```sh + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml + ``` 1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. 1. Add the provider configuration: - For omnibus package: - - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "auth0", - "args" => { client_id: 'YOUR_AUTH0_CLIENT_ID', - client_secret: 'YOUR_AUTH0_CLIENT_SECRET', - domain: 'YOUR_AUTH0_DOMAIN', - scope: 'openid profile email' - } - } - ] - ``` - - For installations from source: - - ```yaml - - { name: 'auth0', - args: { - client_id: 'YOUR_AUTH0_CLIENT_ID', - client_secret: 'YOUR_AUTH0_CLIENT_SECRET', - domain: 'YOUR_AUTH0_DOMAIN', - scope: 'openid profile email' } - } - ``` + For omnibus package: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "auth0", + "args" => { client_id: 'YOUR_AUTH0_CLIENT_ID', + client_secret: 'YOUR_AUTH0_CLIENT_SECRET', + domain: 'YOUR_AUTH0_DOMAIN', + scope: 'openid profile email' + } + } + ] + ``` + + For installations from source: + + ```yaml + - { name: 'auth0', + args: { + client_id: 'YOUR_AUTH0_CLIENT_ID', + client_secret: 'YOUR_AUTH0_CLIENT_SECRET', + domain: 'YOUR_AUTH0_DOMAIN', + scope: 'openid profile email' } + } + ``` 1. Change `YOUR_AUTH0_CLIENT_ID` to the client ID from the Auth0 Console page from step 5. @@ -81,8 +81,8 @@ application. 1. Change `YOUR_AUTH0_CLIENT_SECRET` to the client secret from the Auth0 Console page from step 5. -1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page there should now be an Auth0 icon below the regular sign in form. Click the icon to begin the authentication process. Auth0 will ask the diff --git a/doc/integration/azure.md b/doc/integration/azure.md index 7a6d4bb143f..a9468f201ef 100644 --- a/doc/integration/azure.md +++ b/doc/integration/azure.md @@ -2,21 +2,21 @@ To enable the Microsoft Azure OAuth2 OmniAuth provider you must register your application with Azure. Azure will generate a client ID and secret key for you to use. -1. Sign in to the [Azure Management Portal](https://manage.windowsazure.com). +1. Sign in to the [Azure Management Portal](https://portal.azure.com). -1. Select "Active Directory" on the left and choose the directory you want to use to register GitLab. +1. Select "Active Directory" on the left and choose the directory you want to use to register GitLab. -1. Select "Applications" at the top bar and click the "Add" button the bottom. +1. Select "Applications" at the top bar and click the "Add" button the bottom. -1. Select "Add an application my organization is developing". +1. Select "Add an application my organization is developing". -1. Provide the project information and click the "Next" button. - - Name: 'GitLab' works just fine here. - - Type: 'WEB APPLICATION AND/OR WEB API' +1. Provide the project information and click the "Next" button. + - Name: 'GitLab' works just fine here. + - Type: 'WEB APPLICATION AND/OR WEB API' -1. On the "App properties" page enter the needed URI's and click the "Complete" button. - - SIGN-IN URL: Enter the URL of your GitLab installation (e.g `https://gitlab.mycompany.com/`) - - APP ID URI: Enter the endpoint URL for Microsoft to use, just has to be unique (e.g `https://mycompany.onmicrosoft.com/gitlab`) +1. On the "App properties" page enter the needed URI's and click the "Complete" button. + - SIGN-IN URL: Enter the URL of your GitLab installation (e.g `https://gitlab.mycompany.com/`) + - APP ID URI: Enter the endpoint URL for Microsoft to use, just has to be unique (e.g `https://mycompany.onmicrosoft.com/gitlab`) 1. Select "Configure" in the top menu. @@ -30,59 +30,59 @@ To enable the Microsoft Azure OAuth2 OmniAuth provider you must register your ap 1. You will see lots of endpoint URLs in the form `https://login.microsoftonline.com/TENANT ID/...`, note down the TENANT ID part of one of those endpoints. -1. On your GitLab server, open the configuration file. +1. On your GitLab server, open the configuration file. - For omnibus package: + For omnibus package: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab + ```sh + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + sudo -u git -H editor config/gitlab.yml + ``` -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. -1. Add the provider configuration: +1. Add the provider configuration: - For omnibus package: + For omnibus package: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "azure_oauth2", - "args" => { - "client_id" => "CLIENT ID", - "client_secret" => "CLIENT SECRET", - "tenant_id" => "TENANT ID", - } - } - ] - ``` + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "azure_oauth2", + "args" => { + "client_id" => "CLIENT ID", + "client_secret" => "CLIENT SECRET", + "tenant_id" => "TENANT ID", + } + } + ] + ``` - For installations from source: + For installations from source: - ``` - - { name: 'azure_oauth2', - args: { client_id: "CLIENT ID", - client_secret: "CLIENT SECRET", - tenant_id: "TENANT ID" } } - ``` + ``` + - { name: 'azure_oauth2', + args: { client_id: "CLIENT ID", + client_secret: "CLIENT SECRET", + tenant_id: "TENANT ID" } } + ``` - The `base_azure_url` is optional and can be added for different locales; - e.g. `base_azure_url: "https://login.microsoftonline.de"`. + The `base_azure_url` is optional and can be added for different locales; + e.g. `base_azure_url: "https://login.microsoftonline.de"`. -1. Replace 'CLIENT ID', 'CLIENT SECRET' and 'TENANT ID' with the values you got above. +1. Replace 'CLIENT ID', 'CLIENT SECRET' and 'TENANT ID' with the values you got above. -1. Save the configuration file. +1. Save the configuration file. -1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page there should now be a Microsoft icon below the regular sign in form. Click the icon to begin the authentication process. Microsoft will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in. diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index 68ec8c4b5c2..5d8f2ebcb8b 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -30,97 +30,97 @@ To enable the Bitbucket OmniAuth provider you must register your application with Bitbucket.org. Bitbucket will generate an application ID and secret key for you to use. -1. Sign in to [Bitbucket.org](https://bitbucket.org). -1. Navigate to your individual user settings (**Bitbucket settings**) or a team's - settings (**Manage team**), depending on how you want the application registered. - It does not matter if the application is registered as an individual or a - team, that is entirely up to you. -1. Select **OAuth** in the left menu under "Access Management". -1. Select **Add consumer**. -1. Provide the required details: - - | Item | Description | - | :--- | :---------- | - | **Name** | This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or something else descriptive. | - | **Application description** | Fill this in if you wish. | - | **Callback URL** | The URL to your GitLab installation, e.g., `https://gitlab.example.com/users/auth`. | - | **URL** | The URL to your GitLab installation, e.g., `https://gitlab.example.com`. | - - NOTE: Be sure to append `/users/auth` to the end of the callback URL - to prevent a [OAuth2 convert - redirect](http://tetraph.com/covert_redirect/) vulnerability. - - NOTE: Starting in GitLab 8.15, you MUST specify a callback URL, or you will - see an "Invalid redirect_uri" message. For more details, see [the - Bitbucket documentation](https://confluence.atlassian.com/bitbucket/oauth-faq-338365710.html). - - And grant at least the following permissions: - - ``` - Account: Email, Read - Projects: Read - Repositories: Read - Pull Requests: Read - Issues: Read - Wiki: Read and Write - ``` - - ![Bitbucket OAuth settings page](img/bitbucket_oauth_settings_page.png) - -1. Select **Save**. -1. Select your newly created OAuth consumer and you should now see a Key and - Secret in the list of OAuth consumers. Keep this page open as you continue - the configuration. - - ![Bitbucket OAuth key](img/bitbucket_oauth_keys.png) - -1. On your GitLab server, open the configuration file: - - ``` - # For Omnibus packages - sudo editor /etc/gitlab/gitlab.rb - - # For installations from source - sudo -u git -H editor /home/git/gitlab/config/gitlab.yml - ``` - -1. Add the Bitbucket provider configuration: - - For Omnibus packages: - - ```ruby - gitlab_rails['omniauth_enabled'] = true - - gitlab_rails['omniauth_providers'] = [ - { - "name" => "bitbucket", - "app_id" => "BITBUCKET_APP_KEY", - "app_secret" => "BITBUCKET_APP_SECRET", - "url" => "https://bitbucket.org/" - } - ] - ``` - - For installations from source: - - ```yaml - omniauth: - enabled: true - providers: - - { name: 'bitbucket', - app_id: 'BITBUCKET_APP_KEY', - app_secret: 'BITBUCKET_APP_SECRET', - url: 'https://bitbucket.org/' } - ``` - - --- - - Where `BITBUCKET_APP_KEY` is the Key and `BITBUCKET_APP_SECRET` the Secret - from the Bitbucket application page. - -1. Save the configuration file. -1. For the changes to take effect, [reconfigure GitLab][] if you installed via - Omnibus, or [restart][] if installed from source. +1. Sign in to [Bitbucket.org](https://bitbucket.org). +1. Navigate to your individual user settings (**Bitbucket settings**) or a team's + settings (**Manage team**), depending on how you want the application registered. + It does not matter if the application is registered as an individual or a + team, that is entirely up to you. +1. Select **OAuth** in the left menu under "Access Management". +1. Select **Add consumer**. +1. Provide the required details: + + | Item | Description | + | :--- | :---------- | + | **Name** | This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or something else descriptive. | + | **Application description** | Fill this in if you wish. | + | **Callback URL** | The URL to your GitLab installation, e.g., `https://gitlab.example.com/users/auth`. | + | **URL** | The URL to your GitLab installation, e.g., `https://gitlab.example.com`. | + + NOTE: Be sure to append `/users/auth` to the end of the callback URL + to prevent a [OAuth2 convert + redirect](http://tetraph.com/covert_redirect/) vulnerability. + + NOTE: Starting in GitLab 8.15, you MUST specify a callback URL, or you will + see an "Invalid redirect_uri" message. For more details, see [the + Bitbucket documentation](https://confluence.atlassian.com/bitbucket/oauth-faq-338365710.html). + + And grant at least the following permissions: + + ``` + Account: Email, Read + Projects: Read + Repositories: Read + Pull Requests: Read + Issues: Read + Wiki: Read and Write + ``` + + ![Bitbucket OAuth settings page](img/bitbucket_oauth_settings_page.png) + +1. Select **Save**. +1. Select your newly created OAuth consumer and you should now see a Key and + Secret in the list of OAuth consumers. Keep this page open as you continue + the configuration. + + ![Bitbucket OAuth key](img/bitbucket_oauth_keys.png) + +1. On your GitLab server, open the configuration file: + + ``` + # For Omnibus packages + sudo editor /etc/gitlab/gitlab.rb + + # For installations from source + sudo -u git -H editor /home/git/gitlab/config/gitlab.yml + ``` + +1. Add the Bitbucket provider configuration: + + For Omnibus packages: + + ```ruby + gitlab_rails['omniauth_enabled'] = true + + gitlab_rails['omniauth_providers'] = [ + { + "name" => "bitbucket", + "app_id" => "BITBUCKET_APP_KEY", + "app_secret" => "BITBUCKET_APP_SECRET", + "url" => "https://bitbucket.org/" + } + ] + ``` + + For installations from source: + + ```yaml + omniauth: + enabled: true + providers: + - { name: 'bitbucket', + app_id: 'BITBUCKET_APP_KEY', + app_secret: 'BITBUCKET_APP_SECRET', + url: 'https://bitbucket.org/' } + ``` + + --- + + Where `BITBUCKET_APP_KEY` is the Key and `BITBUCKET_APP_SECRET` the Secret + from the Bitbucket application page. + +1. Save the configuration file. +1. For the changes to take effect, [reconfigure GitLab][] if you installed via + Omnibus, or [restart][] if installed from source. On the sign in page there should now be a Bitbucket icon below the regular sign in form. Click the icon to begin the authentication process. Bitbucket will ask diff --git a/doc/integration/cas.md b/doc/integration/cas.md index c6178fa44f0..f99337376a8 100644 --- a/doc/integration/cas.md +++ b/doc/integration/cas.md @@ -2,63 +2,63 @@ To enable the CAS OmniAuth provider you must register your application with your CAS instance. This requires the service URL GitLab will supply to CAS. It should be something like: `https://gitlab.example.com:443/users/auth/cas3/callback?url`. By default handling for SLO is enabled, you only need to configure CAS for backchannel logout. -1. On your GitLab server, open the configuration file. +1. On your GitLab server, open the configuration file. - For omnibus package: + For omnibus package: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab + ```sh + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + sudo -u git -H editor config/gitlab.yml + ``` -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. -1. Add the provider configuration: +1. Add the provider configuration: - For omnibus package: + For omnibus package: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name"=> "cas3", - "label"=> "cas", - "args"=> { - "url"=> 'CAS_SERVER', - "login_url"=> '/CAS_PATH/login', - "service_validate_url"=> '/CAS_PATH/p3/serviceValidate', - "logout_url"=> '/CAS_PATH/logout' - } - } - ] - ``` + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name"=> "cas3", + "label"=> "cas", + "args"=> { + "url"=> 'CAS_SERVER', + "login_url"=> '/CAS_PATH/login', + "service_validate_url"=> '/CAS_PATH/p3/serviceValidate', + "logout_url"=> '/CAS_PATH/logout' + } + } + ] + ``` - For installations from source: + For installations from source: - ``` - - { name: 'cas3', - label: 'cas', - args: { - url: 'CAS_SERVER', - login_url: '/CAS_PATH/login', - service_validate_url: '/CAS_PATH/p3/serviceValidate', - logout_url: '/CAS_PATH/logout'} } - ``` + ``` + - { name: 'cas3', + label: 'cas', + args: { + url: 'CAS_SERVER', + login_url: '/CAS_PATH/login', + service_validate_url: '/CAS_PATH/p3/serviceValidate', + logout_url: '/CAS_PATH/logout'} } + ``` -1. Change 'CAS_PATH' to the root of your CAS instance (ie. `cas`). +1. Change 'CAS_PATH' to the root of your CAS instance (ie. `cas`). -1. If your CAS instance does not use default TGC lifetimes, update the `cas3.session_duration` to at least the current TGC maximum lifetime. To explicitly disable SLO, regardless of CAS settings, set this to 0. +1. If your CAS instance does not use default TGC lifetimes, update the `cas3.session_duration` to at least the current TGC maximum lifetime. To explicitly disable SLO, regardless of CAS settings, set this to 0. -1. Save the configuration file. +1. Save the configuration file. -1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page there should now be a CAS tab in the sign in form. diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md index 877330b8c44..fff06254da7 100644 --- a/doc/integration/elasticsearch.md +++ b/doc/integration/elasticsearch.md @@ -1,4 +1,4 @@ -# Elasticsearch integration **[STARTER ONLY]** +# Elasticsearch integration **(STARTER ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/109 "Elasticsearch Merge Request") in GitLab [Starter](https://about.gitlab.com/pricing/) 8.4. Support > for [Amazon Elasticsearch](http://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-gsg.html) was [introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1305) in GitLab @@ -12,6 +12,7 @@ special searches: - [Advanced Syntax Search](../user/search/advanced_search_syntax.md) ## Version Requirements + <!-- Please remember to update ee/lib/system_check/app/elasticsearch_check.rb if this changes --> | GitLab version | Elasticsearch version | @@ -424,91 +425,90 @@ Here are some common pitfalls and how to overcome them: - **How can I verify my GitLab instance is using Elasticsearch?** - The easiest method is via the rails console (`sudo gitlab-rails console`) by running the following: + The easiest method is via the rails console (`sudo gitlab-rails console`) by running the following: - ```ruby - u = User.find_by_username('your-username') - s = SearchService.new(u, {:search => 'search_term'}) - pp s.search_objects.class.name - ``` + ```ruby + u = User.find_by_username('your-username') + s = SearchService.new(u, {:search => 'search_term'}) + pp s.search_objects.class.name + ``` - If you see `Elasticsearch::Model::Response::Records`, you are using Elasticsearch. + If you see `Elasticsearch::Model::Response::Records`, you are using Elasticsearch. - **I updated GitLab and now I can't find anything** - We continuously make updates to our indexing strategies and aim to support - newer versions of Elasticsearch. When indexing changes are made, it may - be necessary for you to [reindex](#adding-gitlabs-data-to-the-elasticsearch-index) after updating GitLab. + We continuously make updates to our indexing strategies and aim to support + newer versions of Elasticsearch. When indexing changes are made, it may + be necessary for you to [reindex](#adding-gitlabs-data-to-the-elasticsearch-index) after updating GitLab. - **I indexed all the repositories but I can't find anything** - Make sure you indexed all the database data [as stated above](#adding-gitlabs-data-to-the-elasticsearch-index). + Make sure you indexed all the database data [as stated above](#adding-gitlabs-data-to-the-elasticsearch-index). - Beyond that, check via the [Elasticsearch Search API](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html) to see if the data shows up on the Elasticsearch side. + Beyond that, check via the [Elasticsearch Search API](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html) to see if the data shows up on the Elasticsearch side. - If it shows up via the [Elasticsearch Search API](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html), check that it shows up via the rails console (`sudo gitlab-rails console`): + If it shows up via the [Elasticsearch Search API](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html), check that it shows up via the rails console (`sudo gitlab-rails console`): - ```ruby - u = User.find_by_username('your-username') - s = SearchService.new(u, {:search => 'search_term', :scope => ‘blobs’}) - pp s.search_objects.to_a - ``` + ```ruby + u = User.find_by_username('your-username') + s = SearchService.new(u, {:search => 'search_term', :scope => ‘blobs’}) + pp s.search_objects.to_a + ``` - See [Elasticsearch Index Scopes](elasticsearch.md#elasticsearch-index-scopes) for more information on searching for specific types of data. + See [Elasticsearch Index Scopes](elasticsearch.md#elasticsearch-index-scopes) for more information on searching for specific types of data. - **I indexed all the repositories but then switched Elasticsearch servers and now I can't find anything** - You will need to re-run all the rake tasks to re-index the database, repositories, and wikis. + You will need to re-run all the rake tasks to re-index the database, repositories, and wikis. - **The indexing process is taking a very long time** - The more data present in your GitLab instance, the longer the indexing process takes. + The more data present in your GitLab instance, the longer the indexing process takes. - **No new data is added to the Elasticsearch index when I push code** - When performing the initial indexing of blobs, we lock all projects until the project finishes indexing. It could - happen that an error during the process causes one or multiple projects to remain locked. In order to unlock them, - run the `gitlab:elastic:clear_locked_projects` rake task. + When performing the initial indexing of blobs, we lock all projects until the project finishes indexing. It could + happen that an error during the process causes one or multiple projects to remain locked. In order to unlock them, + run the `gitlab:elastic:clear_locked_projects` rake task. - **"Can't specify parent if no parent field has been configured"** - If you enabled Elasticsearch before GitLab 8.12 and have not rebuilt indexes you will get - exception in lots of different cases: - - ```text - Elasticsearch::Transport::Transport::Errors::BadRequest([400] { - "error": { - "root_cause": [{ - "type": "illegal_argument_exception", - "reason": "Can't specify parent if no parent field has been configured" - }], - "type": "illegal_argument_exception", - "reason": "Can't specify parent if no parent field has been configured" - }, - "status": 400 - }): - ``` - - This is because we changed the index mapping in GitLab 8.12 and the old indexes should be removed and built from scratch again, - see details in the [8-11-to-8-12 update guide](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/8.11-to-8.12.md#11-elasticsearch-index-update-if-you-currently-use-elasticsearch). + If you enabled Elasticsearch before GitLab 8.12 and have not rebuilt indexes you will get + exception in lots of different cases: + + ```text + Elasticsearch::Transport::Transport::Errors::BadRequest([400] { + "error": { + "root_cause": [{ + "type": "illegal_argument_exception", + "reason": "Can't specify parent if no parent field has been configured" + }], + "type": "illegal_argument_exception", + "reason": "Can't specify parent if no parent field has been configured" + }, + "status": 400 + }): + ``` + + This is because we changed the index mapping in GitLab 8.12 and the old indexes should be removed and built from scratch again, + see details in the [8-11-to-8-12 update guide](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/8.11-to-8.12.md#11-elasticsearch-index-update-if-you-currently-use-elasticsearch). - Exception `Elasticsearch::Transport::Transport::Errors::BadRequest` - If you have this exception (just like in the case above but the actual message is different) please check if you have the correct Elasticsearch version and you met the other [requirements](#system-requirements). - There is also an easy way to check it automatically with `sudo gitlab-rake gitlab:check` command. + If you have this exception (just like in the case above but the actual message is different) please check if you have the correct Elasticsearch version and you met the other [requirements](#system-requirements). + There is also an easy way to check it automatically with `sudo gitlab-rake gitlab:check` command. - Exception `Elasticsearch::Transport::Transport::Errors::RequestEntityTooLarge` - ```text - [413] {"Message":"Request size exceeded 10485760 bytes"} - ``` - - This exception is seen when your Elasticsearch cluster is configured to reject - requests above a certain size (10MiB in this case). This corresponds to the - `http.max_content_length` setting in `elasticsearch.yml`. Increase it to a - larger size and restart your Elasticsearch cluster. + ```text + [413] {"Message":"Request size exceeded 10485760 bytes"} + ``` - AWS has [fixed limits](http://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-limits.html) - for this setting ("Maximum Size of HTTP Request Payloads"), based on the size of - the underlying instance. + This exception is seen when your Elasticsearch cluster is configured to reject + requests above a certain size (10MiB in this case). This corresponds to the + `http.max_content_length` setting in `elasticsearch.yml`. Increase it to a + larger size and restart your Elasticsearch cluster. + AWS has [fixed limits](http://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-limits.html) + for this setting ("Maximum Size of HTTP Request Payloads"), based on the size of + the underlying instance. diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md index fe789a80eed..837434da737 100644 --- a/doc/integration/facebook.md +++ b/doc/integration/facebook.md @@ -2,7 +2,7 @@ To enable the Facebook OmniAuth provider you must register your application with Facebook. Facebook will generate an app ID and secret key for you to use. -1. Sign in to the [Facebook Developer Platform](https://developers.facebook.com/). +1. Sign in to the [Facebook Developer Platform](https://developers.facebook.com/). 1. Choose "My Apps" > "Add a New App" @@ -47,53 +47,53 @@ To enable the Facebook OmniAuth provider you must register your application with ![Facebook API Keys](img/facebook_api_keys.png) -1. On your GitLab server, open the configuration file. +1. On your GitLab server, open the configuration file. - For omnibus package: + For omnibus package: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab + ```sh + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + sudo -u git -H editor config/gitlab.yml + ``` -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. -1. Add the provider configuration: +1. Add the provider configuration: - For omnibus package: + For omnibus package: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "facebook", - "app_id" => "YOUR_APP_ID", - "app_secret" => "YOUR_APP_SECRET" - } - ] - ``` + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "facebook", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET" + } + ] + ``` - For installations from source: + For installations from source: - ``` - - { name: 'facebook', app_id: 'YOUR_APP_ID', - app_secret: 'YOUR_APP_SECRET' } - ``` + ``` + - { name: 'facebook', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET' } + ``` -1. Change 'YOUR_APP_ID' to the API key from Facebook page in step 10. +1. Change 'YOUR_APP_ID' to the API key from Facebook page in step 10. -1. Change 'YOUR_APP_SECRET' to the API secret from the Facebook page in step 10. +1. Change 'YOUR_APP_SECRET' to the API secret from the Facebook page in step 10. -1. Save the configuration file. +1. Save the configuration file. -1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page there should now be a Facebook icon below the regular sign in form. Click the icon to begin the authentication process. Facebook will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in. diff --git a/doc/integration/github.md b/doc/integration/github.md index 5b01dd9feb7..c8dbae65465 100644 --- a/doc/integration/github.md +++ b/doc/integration/github.md @@ -7,111 +7,111 @@ You can integrate your GitLab instance with GitHub.com as well as GitHub Enterpr To enable GitHub OmniAuth provider, you must use GitHub's credentials for your GitLab instance. To get the credentials (a pair of Client ID and Client Secret), you must register an application as an OAuth App on GitHub. -1. Sign in to GitHub. +1. Sign in to GitHub. -1. Navigate to your individual user or organization settings, depending on how you want the application registered. It does not matter if the application is registered as an individual or an organization - that is entirely up to you. +1. Navigate to your individual user or organization settings, depending on how you want the application registered. It does not matter if the application is registered as an individual or an organization - that is entirely up to you. - - For individual accounts, select **Developer settings** from the left menu, then select **OAuth Apps**. - - For organization accounts, directly select **OAuth Apps** from the left menu. + - For individual accounts, select **Developer settings** from the left menu, then select **OAuth Apps**. + - For organization accounts, directly select **OAuth Apps** from the left menu. -1. Select **Register an application** (if you don't have any OAuth App) or **New OAuth App** (if you already have OAuth Apps). - ![Register OAuth App](img/github_app_entry.png) +1. Select **Register an application** (if you don't have any OAuth App) or **New OAuth App** (if you already have OAuth Apps). + ![Register OAuth App](img/github_app_entry.png) -1. Provide the required details. - - Application name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or something else descriptive. - - Homepage URL: The URL of your GitLab installation. For example, `https://gitlab.example.com`. - - Application description: Fill this in if you wish. - - Authorization callback URL: `http(s)://${YOUR_DOMAIN}/users/auth`. Please make sure the port is included if your GitLab instance is not configured on default port. - ![Register OAuth App](img/github_register_app.png) +1. Provide the required details. + - Application name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or something else descriptive. + - Homepage URL: The URL of your GitLab installation. For example, `https://gitlab.example.com`. + - Application description: Fill this in if you wish. + - Authorization callback URL: `http(s)://${YOUR_DOMAIN}/users/auth`. Please make sure the port is included if your GitLab instance is not configured on default port. + ![Register OAuth App](img/github_register_app.png) - NOTE: Be sure to append `/users/auth` to the end of the callback URL - to prevent a [OAuth2 convert - redirect](http://tetraph.com/covert_redirect/) vulnerability. + NOTE: Be sure to append `/users/auth` to the end of the callback URL + to prevent a [OAuth2 convert + redirect](http://tetraph.com/covert_redirect/) vulnerability. -1. Select **Register application**. +1. Select **Register application**. -1. You should now see a pair of **Client ID** and **Client Secret** near the top right of the page (see screenshot). - Keep this page open as you continue configuration. - ![GitHub app](img/github_app.png) +1. You should now see a pair of **Client ID** and **Client Secret** near the top right of the page (see screenshot). + Keep this page open as you continue configuration. + ![GitHub app](img/github_app.png) -1. On your GitLab server, open the configuration file. +1. On your GitLab server, open the configuration file. - For omnibus package: + For omnibus package: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab + ```sh + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + sudo -u git -H editor config/gitlab.yml + ``` -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. -1. Add the provider configuration: +1. Add the provider configuration: - For omnibus package: + For omnibus package: - For GitHub.com: + For GitHub.com: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "github", - "app_id" => "YOUR_APP_ID", - "app_secret" => "YOUR_APP_SECRET", - "args" => { "scope" => "user:email" } - } - ] - ``` + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "github", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET", + "args" => { "scope" => "user:email" } + } + ] + ``` - For GitHub Enterprise: + For GitHub Enterprise: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "github", - "app_id" => "YOUR_APP_ID", - "app_secret" => "YOUR_APP_SECRET", - "url" => "https://github.example.com/", - "args" => { "scope" => "user:email" } - } - ] - ``` + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "github", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET", + "url" => "https://github.example.com/", + "args" => { "scope" => "user:email" } + } + ] + ``` - For installation from source: + For installation from source: - For GitHub.com: + For GitHub.com: - ``` - - { name: 'github', app_id: 'YOUR_APP_ID', - app_secret: 'YOUR_APP_SECRET', - args: { scope: 'user:email' } } - ``` + ``` + - { name: 'github', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET', + args: { scope: 'user:email' } } + ``` - For GitHub Enterprise: + For GitHub Enterprise: - ``` - - { name: 'github', app_id: 'YOUR_APP_ID', - app_secret: 'YOUR_APP_SECRET', - url: "https://github.example.com/", - args: { scope: 'user:email' } } - ``` + ``` + - { name: 'github', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET', + url: "https://github.example.com/", + args: { scope: 'user:email' } } + ``` - __Replace `https://github.example.com/` with your GitHub URL.__ + __Replace `https://github.example.com/` with your GitHub URL.__ -1. Change `YOUR_APP_ID` to the Client ID from the GitHub application page from step 6. +1. Change `YOUR_APP_ID` to the Client ID from the GitHub application page from step 6. -1. Change `YOUR_APP_SECRET` to the Client Secret from the GitHub application page from step 6. +1. Change `YOUR_APP_SECRET` to the Client Secret from the GitHub application page from step 6. -1. Save the configuration file. +1. Save the configuration file. -1. [Reconfigure GitLab][] or [restart GitLab][] for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. [Reconfigure GitLab][] or [restart GitLab][] for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page there should now be a GitHub icon below the regular sign in form. Click the icon to begin the authentication process. GitHub will ask the user to sign in and authorize the GitLab application. @@ -127,16 +127,16 @@ and changing the global Git `sslVerify` option to `false` in the GitLab server. For omnibus package: ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "github", - "app_id" => "YOUR_APP_ID", - "app_secret" => "YOUR_APP_SECRET", - "url" => "https://github.example.com/", - "verify_ssl" => false, - "args" => { "scope" => "user:email" } - } - ] +gitlab_rails['omniauth_providers'] = [ + { + "name" => "github", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET", + "url" => "https://github.example.com/", + "verify_ssl" => false, + "args" => { "scope" => "user:email" } + } +] ``` You will also need to disable Git SSL verification on the server hosting GitLab. @@ -148,11 +148,11 @@ omnibus_gitconfig['system'] = { "http" => ["sslVerify = false"] } For installation from source: ``` - - { name: 'github', app_id: 'YOUR_APP_ID', - app_secret: 'YOUR_APP_SECRET', - url: "https://github.example.com/", - verify_ssl: false, - args: { scope: 'user:email' } } +- { name: 'github', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET', + url: "https://github.example.com/", + verify_ssl: false, + args: { scope: 'user:email' } } ``` You will also need to disable Git SSL verification on the server hosting GitLab. diff --git a/doc/integration/gitlab.md b/doc/integration/gitlab.md index 70087576678..46da3d88d90 100644 --- a/doc/integration/gitlab.md +++ b/doc/integration/gitlab.md @@ -5,78 +5,78 @@ Import projects from GitLab.com and login to your GitLab instance with your GitL To enable the GitLab.com OmniAuth provider you must register your application with GitLab.com. GitLab.com will generate an application ID and secret key for you to use. -1. Sign in to GitLab.com +1. Sign in to GitLab.com 1. On the upper right corner, click on your avatar and go to your **Settings**. -1. Select **Applications** in the left menu. +1. Select **Applications** in the left menu. -1. Provide the required details for **Add new application**. - - Name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or something else descriptive. - - Redirect URI: +1. Provide the required details for **Add new application**. + - Name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or something else descriptive. + - Redirect URI: - ``` - http://your-gitlab.example.com/import/gitlab/callback - http://your-gitlab.example.com/users/auth/gitlab/callback - ``` + ``` + http://your-gitlab.example.com/import/gitlab/callback + http://your-gitlab.example.com/users/auth/gitlab/callback + ``` - The first link is required for the importer and second for the authorization. + The first link is required for the importer and second for the authorization. -1. Select **Save application**. +1. Select **Save application**. -1. You should now see a **Application Id** and **Secret** near the top right of the page (see screenshot). - Keep this page open as you continue configuration. - ![GitLab app](img/gitlab_app.png) +1. You should now see a **Application Id** and **Secret** near the top right of the page (see screenshot). + Keep this page open as you continue configuration. + ![GitLab app](img/gitlab_app.png) -1. On your GitLab server, open the configuration file. +1. On your GitLab server, open the configuration file. - For omnibus package: + For omnibus package: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab + ```sh + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + sudo -u git -H editor config/gitlab.yml + ``` -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. -1. Add the provider configuration: +1. Add the provider configuration: - For omnibus package: + For omnibus package: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "gitlab", - "app_id" => "YOUR_APP_ID", - "app_secret" => "YOUR_APP_SECRET", - "args" => { "scope" => "api" } - } - ] - ``` + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "gitlab", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET", + "args" => { "scope" => "api" } + } + ] + ``` - For installations from source: + For installations from source: - ``` - - { name: 'gitlab', app_id: 'YOUR_APP_ID', - app_secret: 'YOUR_APP_SECRET', - args: { scope: 'api' } } - ``` + ``` + - { name: 'gitlab', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET', + args: { scope: 'api' } } + ``` -1. Change 'YOUR_APP_ID' to the Application ID from the GitLab.com application page. +1. Change 'YOUR_APP_ID' to the Application ID from the GitLab.com application page. -1. Change 'YOUR_APP_SECRET' to the secret from the GitLab.com application page. +1. Change 'YOUR_APP_SECRET' to the secret from the GitLab.com application page. -1. Save the configuration file. +1. Save the configuration file. -1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page there should now be a GitLab.com icon below the regular sign in form. Click the icon to begin the authentication process. GitLab.com will ask the user to sign in and authorize the GitLab application. diff --git a/doc/integration/google.md b/doc/integration/google.md index d2b4e119978..4f6999571b6 100644 --- a/doc/integration/google.md +++ b/doc/integration/google.md @@ -10,10 +10,10 @@ In Google's side: 1. Navigate to the [cloud resource manager](https://console.cloud.google.com/cloud-resource-manager) page 1. Select **Create Project** 1. Provide the project information: - - **Project name** - "GitLab" works just fine here. - - **Project ID** - Must be unique to all Google Developer registered applications. - Google provides a randomly generated Project ID by default. You can use - the randomly generated ID or choose a new one. + - **Project name** - "GitLab" works just fine here. + - **Project ID** - Must be unique to all Google Developer registered applications. + Google provides a randomly generated Project ID by default. You can use + the randomly generated ID or choose a new one. 1. Refresh the page and you should see your new project in the list 1. Go to the [Google API Console](https://console.developers.google.com/apis/dashboard) 1. Select the previously created project form the upper left corner @@ -21,17 +21,17 @@ In Google's side: 1. Select **OAuth consent screen** and fill the form with the required information 1. In the **Credentials** tab, select **Create credentials > OAuth client ID** 1. Fill in the required information - - **Application type** - Choose "Web Application" - - **Name** - Use the default one or provide your own - - **Authorized JavaScript origins** -This isn't really used by GitLab but go - ahead and put `https://gitlab.example.com` - - **Authorized redirect URIs** - Enter your domain name followed by the - callback URIs one at a time: - - ``` - https://gitlab.example.com/users/auth/google_oauth2/callback - https://gitlab.example.com/-/google_api/auth/callback - ``` + - **Application type** - Choose "Web Application" + - **Name** - Use the default one or provide your own + - **Authorized JavaScript origins** -This isn't really used by GitLab but go + ahead and put `https://gitlab.example.com` + - **Authorized redirect URIs** - Enter your domain name followed by the + callback URIs one at a time: + + ``` + https://gitlab.example.com/users/auth/google_oauth2/callback + https://gitlab.example.com/-/google_api/auth/callback + ``` 1. You should now be able to see a Client ID and Client secret. Note them down or keep this page open as you will need them later. @@ -45,64 +45,64 @@ On your GitLab server: 1. Open the configuration file. - For Omnibus GitLab: + For Omnibus GitLab: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + ```sh + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml + ``` 1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. 1. Add the provider configuration: - For Omnibus GitLab: + For Omnibus GitLab: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "google_oauth2", - "app_id" => "YOUR_APP_ID", - "app_secret" => "YOUR_APP_SECRET", - "args" => { "access_type" => "offline", "approval_prompt" => '' } - } - ] - ``` + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "google_oauth2", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET", + "args" => { "access_type" => "offline", "approval_prompt" => '' } + } + ] + ``` - For installations from source: + For installations from source: - ```yaml - - { name: 'google_oauth2', app_id: 'YOUR_APP_ID', - app_secret: 'YOUR_APP_SECRET', - args: { access_type: 'offline', approval_prompt: '' } } - ``` + ```yaml + - { name: 'google_oauth2', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET', + args: { access_type: 'offline', approval_prompt: '' } } + ``` 1. Change `YOUR_APP_ID` to the client ID from the Google Developer page 1. Similarly, change `YOUR_APP_SECRET` to the client secret 1. Make sure that you configure GitLab to use an FQDN as Google will not accept raw IP addresses. - For Omnibus packages: + For Omnibus packages: - ```ruby - external_url 'https://gitlab.example.com' - ``` + ```ruby + external_url 'https://gitlab.example.com' + ``` - For installations from source: + For installations from source: - ```yaml - gitlab: - host: https://gitlab.example.com - ``` + ```yaml + gitlab: + host: https://gitlab.example.com + ``` -1. Save the configuration file. -1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. Save the configuration file. +1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page there should now be a Google icon below the regular sign in form. Click the icon to begin the authentication process. Google will ask the diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md index e6496ae3a2e..50cb3d50009 100644 --- a/doc/integration/jenkins.md +++ b/doc/integration/jenkins.md @@ -1,4 +1,4 @@ -# Jenkins CI service **[STARTER]** +# Jenkins CI service **(STARTER)** >**Note:** In GitLab 8.3, Jenkins integration using the diff --git a/doc/integration/jira_development_panel.md b/doc/integration/jira_development_panel.md index 703736eeb3c..60c7bdabf93 100644 --- a/doc/integration/jira_development_panel.md +++ b/doc/integration/jira_development_panel.md @@ -1,4 +1,4 @@ -# GitLab Jira development panel integration **[PREMIUM]** +# GitLab Jira development panel integration **(PREMIUM)** > [Introduced][ee-2381] in [GitLab Premium][eep] 10.0. diff --git a/doc/integration/kerberos.md b/doc/integration/kerberos.md index 44117755b83..b4f2025265e 100644 --- a/doc/integration/kerberos.md +++ b/doc/integration/kerberos.md @@ -1,4 +1,4 @@ -# Kerberos integration **[STARTER ONLY]** +# Kerberos integration **(STARTER ONLY)** GitLab can integrate with [Kerberos][kerb] as an authentication mechanism. @@ -50,20 +50,20 @@ For source installations, make sure the `kerberos` gem group authentication. In most cases, you only need to enable Kerberos and specify the location of the keytab: - ```yaml - omniauth: - enabled: true - allow_single_sign_on: ['kerberos'] + ```yaml + omniauth: + enabled: true + allow_single_sign_on: ['kerberos'] - kerberos: - # Allow the HTTP Negotiate authentication method for Git clients - enabled: true + kerberos: + # Allow the HTTP Negotiate authentication method for Git clients + enabled: true - # Kerberos 5 keytab file. The keytab file must be readable by the GitLab user, - # and should be different from other keytabs in the system. - # (default: use default keytab from Krb5 config) - keytab: /etc/http.keytab - ``` + # Kerberos 5 keytab file. The keytab file must be readable by the GitLab user, + # and should be different from other keytabs in the system. + # (default: use default keytab from Krb5 config) + keytab: /etc/http.keytab + ``` 1. [Restart GitLab] for the changes to take effect. @@ -73,13 +73,13 @@ For source installations, make sure the `kerberos` gem group 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['omniauth_enabled'] = true - gitlab_rails['omniauth_allow_single_sign_on'] = ['kerberos'] + ```ruby + gitlab_rails['omniauth_enabled'] = true + gitlab_rails['omniauth_allow_single_sign_on'] = ['kerberos'] - gitlab_rails['kerberos_enabled'] = true - gitlab_rails['kerberos_keytab'] = "/etc/http.keytab" - ``` + gitlab_rails['kerberos_enabled'] = true + gitlab_rails['kerberos_keytab'] = "/etc/http.keytab" + ``` 1. [Reconfigure GitLab] for the changes to take effect. @@ -149,26 +149,26 @@ keep offering only `basic` authentication. (e.g., `/etc/nginx/sites-available/gitlab-ssl`) and configure NGINX to listen to port `8443` in addition to the standard HTTPS port: - ```conf - server { - listen 0.0.0.0:443 ssl; - listen [::]:443 ipv6only=on ssl default_server; - listen 0.0.0.0:8443 ssl; - listen [::]:8443 ipv6only=on ssl; - ``` + ```conf + server { + listen 0.0.0.0:443 ssl; + listen [::]:443 ipv6only=on ssl default_server; + listen 0.0.0.0:8443 ssl; + listen [::]:8443 ipv6only=on ssl; + ``` 1. Update the Kerberos section of [gitlab.yml]: - ```yaml - kerberos: - # Dedicated port: Git before 2.4 does not fall back to Basic authentication if Negotiate fails. - # To support both Basic and Negotiate methods with older versions of Git, configure - # nginx to proxy GitLab on an extra port (e.g. 8443) and uncomment the following lines - # to dedicate this port to Kerberos authentication. (default: false) - use_dedicated_port: true - port: 8443 - https: true - ``` + ```yaml + kerberos: + # Dedicated port: Git before 2.4 does not fall back to Basic authentication if Negotiate fails. + # To support both Basic and Negotiate methods with older versions of Git, configure + # nginx to proxy GitLab on an extra port (e.g. 8443) and uncomment the following lines + # to dedicate this port to Kerberos authentication. (default: false) + use_dedicated_port: true + port: 8443 + https: true + ``` 1. [Restart GitLab] and NGINX for the changes to take effect. @@ -178,11 +178,11 @@ keep offering only `basic` authentication. 1. Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_rails['kerberos_use_dedicated_port'] = true - gitlab_rails['kerberos_port'] = 8443 - gitlab_rails['kerberos_https'] = true - ``` + ```ruby + gitlab_rails['kerberos_use_dedicated_port'] = true + gitlab_rails['kerberos_port'] = 8443 + gitlab_rails['kerberos_https'] = true + ``` 1. [Reconfigure GitLab] for the changes to take effect. @@ -214,12 +214,12 @@ remove the OmniAuth provider named `kerberos` from your `gitlab.yml` / 1. Edit [gitlab.yml] and remove the `- { name: 'kerberos' }` line under omniauth providers: - ```yaml - omniauth: - # ... - providers: - - { name: 'kerberos' } # <-- remove this line - ``` + ```yaml + omniauth: + # ... + providers: + - { name: 'kerberos' } # <-- remove this line + ``` 1. [Restart GitLab] for the changes to take effect. @@ -230,11 +230,11 @@ remove the OmniAuth provider named `kerberos` from your `gitlab.yml` / 1. Edit `/etc/gitlab/gitlab.rb` and remove the `{ "name" => "kerberos" }` line under `gitlab_rails['omniauth_providers']`: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { "name" => "kerberos" } # <-- remove this entry - ] - ``` + ```ruby + gitlab_rails['omniauth_providers'] = [ + { "name" => "kerberos" } # <-- remove this entry + ] + ``` 1. [Reconfigure GitLab] for the changes to take effect. @@ -290,7 +290,7 @@ remote: HTTP Basic: Access denied fatal: Authentication failed for '<KRB5 path>' ``` -If you are using Git v2.11 or newer and see the above error when cloning, you can +If you are using Git v2.11 or newer and see the above error when cloning, you can set the `http.emptyAuth` Git option to `true` to fix this: ``` diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md index 3e72589ce12..f4119b1d1ce 100644 --- a/doc/integration/oauth2_generic.md +++ b/doc/integration/oauth2_generic.md @@ -1,7 +1,7 @@ # Sign into GitLab with (almost) any OAuth2 provider The `omniauth-oauth2-generic` gem allows Single Sign On between GitLab and your own OAuth2 provider -(or any OAuth2 provider compatible with this gem) +(or any OAuth2 provider compatible with this gem) This strategy is designed to allow configuration of the simple OmniAuth SSO process outlined below: @@ -12,7 +12,7 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc 1. Strategy parses user information from the response, using a **configurable** format 1. GitLab finds or creates the returned user and logs them in -### Limitations of this Strategy: +## Limitations of this Strategy: - It can only be used for Single Sign on, and will not provide any other access granted by any OAuth provider (importing projects or users, etc) @@ -20,15 +20,15 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc - It is not able to fetch user information from more than one URL - It has not been tested with user information formats other than JSON -### Config Instructions +## Config Instructions 1. Register your application in the OAuth2 provider you wish to authenticate with. - The redirect URI you provide when registering the application should be: + The redirect URI you provide when registering the application should be: - ``` - http://your-gitlab.host.com/users/auth/oauth2_generic/callback - ``` + ``` + http://your-gitlab.host.com/users/auth/oauth2_generic/callback + ``` 1. You should now be able to get a Client ID and Client Secret. Where this shows up will differ for each provider. @@ -36,18 +36,18 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc 1. On your GitLab server, open the configuration file. - For Omnibus package: + For Omnibus package: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + ```sh + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml + ``` 1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings @@ -57,9 +57,9 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc 1. Restart GitLab for the changes to take effect -On the sign in page there should now be a new button below the regular sign in form. +On the sign in page there should now be a new button below the regular sign in form. Click the button to begin your provider's authentication process. This will direct the browser to your OAuth2 Provider's authentication page. If everything goes well the user will be returned to your GitLab instance and will be signed in. -[1]: https://gitlab.com/satorix/omniauth-oauth2-generic#gitlab-config-example
\ No newline at end of file +[1]: https://gitlab.com/satorix/omniauth-oauth2-generic#gitlab-config-example diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md index c02a29dffb4..b9dc2e123c5 100644 --- a/doc/integration/oauth_provider.md +++ b/doc/integration/oauth_provider.md @@ -11,7 +11,7 @@ If you want to use: ## Introduction to OAuth -[OAuth] provides to client applications a 'secure delegated access' to server +[OAuth](https://oauth.net/2/) provides to client applications a 'secure delegated access' to server resources on behalf of a resource owner. In fact, OAuth allows an authorization server to issue access tokens to third-party clients with the approval of the resource owner, or the end-user. @@ -85,5 +85,3 @@ application can perform such as `read_user` and `api`. There are many more scope available. At any time you can revoke any access by just clicking **Revoke**. - -[oauth]: http://oauth.net/2/ "OAuth website" diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index bf5debc7694..7a92ed994c7 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -71,57 +71,57 @@ To change these settings: - **For omnibus package** - Open the configuration file: + Open the configuration file: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - and change: + and change: - ```ruby - # Versions prior to 11.4 require this to be set to true - # gitlab_rails['omniauth_enabled'] = nil + ```ruby + # Versions prior to 11.4 require this to be set to true + # gitlab_rails['omniauth_enabled'] = nil - # CAUTION! - # This allows users to login without having a user account first. Define the allowed providers - # using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. - # User accounts will be created automatically when authentication was successful. - gitlab_rails['omniauth_allow_single_sign_on'] = ['saml', 'twitter'] - gitlab_rails['omniauth_auto_link_ldap_user'] = true - gitlab_rails['omniauth_block_auto_created_users'] = true - ``` + # CAUTION! + # This allows users to login without having a user account first. Define the allowed providers + # using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. + # User accounts will be created automatically when authentication was successful. + gitlab_rails['omniauth_allow_single_sign_on'] = ['saml', 'twitter'] + gitlab_rails['omniauth_auto_link_ldap_user'] = true + gitlab_rails['omniauth_block_auto_created_users'] = true + ``` - **For installations from source** - Open the configuration file: + Open the configuration file: - ```sh - cd /home/git/gitlab + ```sh + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + sudo -u git -H editor config/gitlab.yml + ``` - and change the following section: + and change the following section: - ```yaml - ## OmniAuth settings - omniauth: - # Allow login via Twitter, Google, etc. using OmniAuth providers - # Versions prior to 11.4 require this to be set to true - # enabled: true + ```yaml + ## OmniAuth settings + omniauth: + # Allow login via Twitter, Google, etc. using OmniAuth providers + # Versions prior to 11.4 require this to be set to true + # enabled: true - # CAUTION! - # This allows users to login without having a user account first. Define the allowed providers - # using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. - # User accounts will be created automatically when authentication was successful. - allow_single_sign_on: ["saml", "twitter"] + # CAUTION! + # This allows users to login without having a user account first. Define the allowed providers + # using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. + # User accounts will be created automatically when authentication was successful. + allow_single_sign_on: ["saml", "twitter"] - auto_link_ldap_user: true + auto_link_ldap_user: true - # Locks down those users until they have been cleared by the admin (default: true). - block_auto_created_users: true - ``` + # Locks down those users until they have been cleared by the admin (default: true). + block_auto_created_users: true + ``` Now we can choose one or more of the [Supported Providers](#supported-providers) listed above to continue the configuration process. @@ -161,14 +161,14 @@ want their accounts to be upgraded to full internal accounts. **For Omnibus installations** ```ruby - gitlab_rails['omniauth_external_providers'] = ['twitter', 'google_oauth2'] +gitlab_rails['omniauth_external_providers'] = ['twitter', 'google_oauth2'] ``` **For installations from source** ```yaml - omniauth: - external_providers: ['twitter', 'google_oauth2'] +omniauth: + external_providers: ['twitter', 'google_oauth2'] ``` ## Using Custom Omniauth Providers @@ -186,23 +186,31 @@ these cases you can use the Omniauth provider. These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. -- Stop GitLab: +- Stop GitLab: - sudo service gitlab stop + ```sh + sudo service gitlab stop + ``` -- Add the gem to your [Gemfile](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/Gemfile): +- Add the gem to your [Gemfile](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/Gemfile): - gem "omniauth-your-auth-provider" + ```sh + gem "omniauth-your-auth-provider" + ``` -- Install the new Omniauth provider gem by running the following command: +- Install the new Omniauth provider gem by running the following command: - sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment + ```sh + sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment + ``` - > These are the same commands you used during initial installation in the [Install Gems section](../install/installation.md#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`. + > These are the same commands you used during initial installation in the [Install Gems section](../install/installation.md#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`. -- Start GitLab: +- Start GitLab: - sudo service gitlab start + ```sh + sudo service gitlab start + ``` ### Examples @@ -247,8 +255,8 @@ gitlab_rails['omniauth_enabled'] = false **For installations from source** ```yaml - omniauth: - enabled: false +omniauth: + enabled: false ``` ## Keep OmniAuth user profiles up to date @@ -258,14 +266,14 @@ You can enable profile syncing from selected OmniAuth providers and for all or f When authenticating using LDAP, the user's name and email are always synced. ```ruby - gitlab_rails['sync_profile_from_provider'] = ['twitter', 'google_oauth2'] - gitlab_rails['sync_profile_attributes'] = ['name', 'email', 'location'] - ``` +gitlab_rails['sync_profile_from_provider'] = ['twitter', 'google_oauth2'] +gitlab_rails['sync_profile_attributes'] = ['name', 'email', 'location'] +``` **For installations from source** ```yaml - omniauth: - sync_profile_from_provider: ['twitter', 'google_oauth2'] - sync_profile_attributes: ['email', 'location'] +omniauth: + sync_profile_from_provider: ['twitter', 'google_oauth2'] + sync_profile_attributes: ['email', 'location'] ``` diff --git a/doc/integration/openid_connect_provider.md b/doc/integration/openid_connect_provider.md index a7f907254a1..89f4924d717 100644 --- a/doc/integration/openid_connect_provider.md +++ b/doc/integration/openid_connect_provider.md @@ -5,7 +5,7 @@ to sign in to other services. ## Introduction to OpenID Connect -[OpenID Connect] \(OIDC) is a simple identity layer on top of the +[OpenID Connect](https://openid.net/connect/) \(OIDC) is a simple identity layer on top of the OAuth 2.0 protocol. It allows clients to verify the identity of the end-user based on the authentication performed by GitLab, as well as to obtain basic profile information about the end-user in an interoperable and @@ -14,7 +14,7 @@ but does so in a way that is API-friendly, and usable by native and mobile applications. On the client side, you can use [omniauth-openid-connect] for Rails -applications, or any of the other available [client implementations]. +applications, or any of the other available [client implementations](https://openid.net/developers/libraries/#connect). GitLab's implementation uses the [doorkeeper-openid_connect] gem, refer to its README for more details about which parts of the specifications @@ -46,8 +46,6 @@ Currently the following user information is shared with clients: Only the `sub` and `sub_legacy` claims are included in the ID token, all other claims are available from the `/oauth/userinfo` endpoint used by OIDC clients. -[OpenID Connect]: http://openid.net/connect/ "OpenID Connect website" [doorkeeper-openid_connect]: https://github.com/doorkeeper-gem/doorkeeper-openid_connect "Doorkeeper::OpenidConnect website" [OAuth guide]: oauth_provider.md "GitLab as OAuth2 authentication service provider" [omniauth-openid-connect]: https://github.com/jjbohn/omniauth-openid-connect/ "OmniAuth::OpenIDConnect website" -[client implementations]: http://openid.net/developers/libraries#connect "List of available client implementations" diff --git a/doc/integration/salesforce.md b/doc/integration/salesforce.md index 1ef43cfcece..176622e8050 100644 --- a/doc/integration/salesforce.md +++ b/doc/integration/salesforce.md @@ -7,73 +7,77 @@ You can integrate your GitLab instance with [Salesforce](https://www.salesforce. To enable Salesforce OmniAuth provider, you must use Salesforce's credentials for your GitLab instance. To get the credentials (a pair of Client ID and Client Secret), you must [create a Connected App](https://help.salesforce.com/articleView?id=connected_app_create.htm&type=5) on Salesforce. -1. Sign in to [Salesforce](https://login.salesforce.com/). +1. Sign in to [Salesforce](https://login.salesforce.com/). -1. In Setup, enter `App Manager` in the Quick Find box, click **App Manager**, then click **New Connected App**. +1. In Setup, enter `App Manager` in the Quick Find box, click **App Manager**, then click **New Connected App**. -1. Fill in the application details into the following fields: - - **Connected App Name** and **API Name**: Set to any value but consider something like `<Organization>'s GitLab`, `<Your Name>'s GitLab`, or something else that is descriptive. - - **Contact Email**: Enter the contact email for Salesforce to use when contacting you or your support team. - - **Description**: Description for the application. +1. Fill in the application details into the following fields: + - **Connected App Name** and **API Name**: Set to any value but consider something like `<Organization>'s GitLab`, `<Your Name>'s GitLab`, or something else that is descriptive. + - **Contact Email**: Enter the contact email for Salesforce to use when contacting you or your support team. + - **Description**: Description for the application. - ![Salesforce App Details](img/salesforce_app_details.png) -1. Select **API (Enable OAuth Settings)** and click on **Enable OAuth Settings**. -1. Fill in the application details into the following fields: - - **Callback URL**: The callback URL of your GitLab installation. For example, `https://gitlab.example.com/users/auth/salesforce/callback`. - - **Selected OAuth Scopes**: Move **Access your basic information (id, profile, email, address, phone)** and **Allow access to your unique identifier (openid)** to the right column. + ![Salesforce App Details](img/salesforce_app_details.png) + +1. Select **API (Enable OAuth Settings)** and click on **Enable OAuth Settings**. +1. Fill in the application details into the following fields: + - **Callback URL**: The callback URL of your GitLab installation. For example, `https://gitlab.example.com/users/auth/salesforce/callback`. + - **Selected OAuth Scopes**: Move **Access your basic information (id, profile, email, address, phone)** and **Allow access to your unique identifier (openid)** to the right column. + + ![Salesforce Oauth App Details](img/salesforce_oauth_app_details.png) - ![Salesforce Oauth App Details](img/salesforce_oauth_app_details.png) 1. Click **Save**. -1. On your GitLab server, open the configuration file. +1. On your GitLab server, open the configuration file. + + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For omnibus package: + For installations from source: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml + ``` - For installations from source: +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. - ```sh - cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` +1. Add the provider configuration: -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + For omnibus package: -1. Add the provider configuration: + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "salesforce", + "app_id" => "SALESFORCE_CLIENT_ID", + "app_secret" => "SALESFORCE_CLIENT_SECRET" + } + ] + ``` - For omnibus package: + For installation from source: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "salesforce", - "app_id" => "SALESFORCE_CLIENT_ID", - "app_secret" => "SALESFORCE_CLIENT_SECRET" - } - ] - ``` + ``` + - { name: 'salesforce', + app_id: 'SALESFORCE_CLIENT_ID', + app_secret: 'SALESFORCE_CLIENT_SECRET' + } + ``` - For installation from source: +1. Change `SALESFORCE_CLIENT_ID` to the Consumer Key from the Salesforce connected application page. +1. Change `SALESFORCE_CLIENT_SECRET` to the Consumer Secret from the Salesforce connected application page. - ``` - - { name: 'salesforce', - app_id: 'SALESFORCE_CLIENT_ID', - app_secret: 'SALESFORCE_CLIENT_SECRET' - } - ``` -1. Change `SALESFORCE_CLIENT_ID` to the Consumer Key from the Salesforce connected application page. -1. Change `SALESFORCE_CLIENT_SECRET` to the Consumer Secret from the Salesforce connected application page. - ![Salesforce App Secret Details](img/salesforce_app_secret_details.png) + ![Salesforce App Secret Details](img/salesforce_app_secret_details.png) -1. Save the configuration file. -1. [Reconfigure GitLab]( ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure ) or [restart GitLab]( ../administration/restart_gitlab.md#installations-from-source ) for the changes to take effect if you installed GitLab via Omnibus or from source respectively. +1. Save the configuration file. +1. [Reconfigure GitLab]( ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure ) or [restart GitLab]( ../administration/restart_gitlab.md#installations-from-source ) for the changes to take effect if you installed GitLab via Omnibus or from source respectively. On the sign in page, there should now be a Salesforce icon below the regular sign in form. Click the icon to begin the authentication process. Salesforce will ask the user to sign in and authorize the GitLab application. If everything goes well, the user will be returned to GitLab and will be signed in. NOTE: **Note:** -GitLab requires the email address of each new user. Once the user is logged in using Salesforce, GitLab will redirect the user to the profile page where they will have to provide the email and verify the email.
\ No newline at end of file +GitLab requires the email address of each new user. Once the user is logged in using Salesforce, GitLab will redirect the user to the profile page where they will have to provide the email and verify the email. diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md index 616f3a76b2c..27355d25266 100644 --- a/doc/integration/shibboleth.md +++ b/doc/integration/shibboleth.md @@ -5,8 +5,8 @@ This documentation is for enabling shibboleth with omnibus-gitlab package. In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however this is difficult to configure using the bundled Nginx provided in the omnibus-gitlab package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider. To enable the Shibboleth OmniAuth provider you must configure Apache shibboleth module. -Installation and configuration of module it self is out of scope of this document. -Check <https://wiki.shibboleth.net/> for more info. +The installation and configuration of the module itself is out of the scope of this document. +Check <https://wiki.shibboleth.net/confluence/display/SP3/Apache> for more info. You can find Apache config in gitlab-recipes (<https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache>). @@ -14,35 +14,35 @@ The following changes are needed to enable Shibboleth: 1. Protect omniauth-shibboleth callback URL: - ``` - <Location /users/auth/shibboleth/callback> - AuthType shibboleth - ShibRequestSetting requireSession 1 - ShibUseHeaders On - require valid-user - </Location> + ``` + <Location /users/auth/shibboleth/callback> + AuthType shibboleth + ShibRequestSetting requireSession 1 + ShibUseHeaders On + require valid-user + </Location> - Alias /shibboleth-sp /usr/share/shibboleth - <Location /shibboleth-sp> - Satisfy any - </Location> + Alias /shibboleth-sp /usr/share/shibboleth + <Location /shibboleth-sp> + Satisfy any + </Location> - <Location /Shibboleth.sso> - SetHandler shib - </Location> - ``` + <Location /Shibboleth.sso> + SetHandler shib + </Location> + ``` 1. Exclude shibboleth URLs from rewriting. Add `RewriteCond %{REQUEST_URI} !/Shibboleth.sso` and `RewriteCond %{REQUEST_URI} !/shibboleth-sp`. Config should look like this: - ``` - # Apache equivalent of Nginx try files - RewriteEngine on - RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_URI} !/Shibboleth.sso - RewriteCond %{REQUEST_URI} !/shibboleth-sp - RewriteRule .* http://127.0.0.1:8080%{REQUEST_URI} [P,QSA] - RequestHeader set X_FORWARDED_PROTO 'https' - ``` + ``` + # Apache equivalent of Nginx try files + RewriteEngine on + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_URI} !/Shibboleth.sso + RewriteCond %{REQUEST_URI} !/shibboleth-sp + RewriteRule .* http://127.0.0.1:8080%{REQUEST_URI} [P,QSA] + RequestHeader set X_FORWARDED_PROTO 'https' + ``` 1. Edit `/etc/gitlab/gitlab.rb` configuration file to enable OmniAuth and add Shibboleth as an OmniAuth provider. User attributes will be sent from the @@ -60,31 +60,31 @@ The following changes are needed to enable Shibboleth: The file should look like this: - ``` - external_url 'https://gitlab.example.com' - gitlab_rails['internal_api_url'] = 'https://gitlab.example.com' - - # disable Nginx - nginx['enable'] = false - - gitlab_rails['omniauth_allow_single_sign_on'] = true - gitlab_rails['omniauth_block_auto_created_users'] = false - gitlab_rails['omniauth_enabled'] = true - gitlab_rails['omniauth_providers'] = [ - { - "name" => "'shibboleth"', - "label" => "Text for Login Button", - "args" => { - "shib_session_id_field" => "HTTP_SHIB_SESSION_ID", - "shib_application_id_field" => "HTTP_SHIB_APPLICATION_ID", - "uid_field" => 'HTTP_EPPN', - "name_field" => 'HTTP_CN', - "info_fields" => { "email" => 'HTTP_MAIL'} - } - } - ] - - ``` + ``` + external_url 'https://gitlab.example.com' + gitlab_rails['internal_api_url'] = 'https://gitlab.example.com' + + # disable Nginx + nginx['enable'] = false + + gitlab_rails['omniauth_allow_single_sign_on'] = true + gitlab_rails['omniauth_block_auto_created_users'] = false + gitlab_rails['omniauth_enabled'] = true + gitlab_rails['omniauth_providers'] = [ + { + "name" => "'shibboleth"', + "label" => "Text for Login Button", + "args" => { + "shib_session_id_field" => "HTTP_SHIB_SESSION_ID", + "shib_application_id_field" => "HTTP_SHIB_APPLICATION_ID", + "uid_field" => 'HTTP_EPPN', + "name_field" => 'HTTP_CN', + "info_fields" => { "email" => 'HTTP_MAIL'} + } + } + ] + + ``` 1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart](../administration/restart_gitlab.md#installations-from-source) GitLab for the changes to take effect if you installed GitLab via Omnibus or from source respectively. @@ -97,44 +97,44 @@ The order of the first 2 Location directives is important. If they are reversed, you will not get a shibboleth session! ``` - <Location /> - Require all granted - ProxyPassReverse http://127.0.0.1:8181 - ProxyPassReverse http://YOUR_SERVER_FQDN/ - </Location> - - <Location /users/auth/shibboleth/callback> - AuthType shibboleth - ShibRequestSetting requireSession 1 - ShibUseHeaders On - Require shib-session - </Location> - - Alias /shibboleth-sp /usr/share/shibboleth - - <Location /shibboleth-sp> - Require all granted - </Location> - - <Location /Shibboleth.sso> - SetHandler shib - </Location> - - RewriteEngine on - - #Don't escape encoded characters in api requests - RewriteCond %{REQUEST_URI} ^/api/v4/.* - RewriteCond %{REQUEST_URI} !/Shibboleth.sso - RewriteCond %{REQUEST_URI} !/shibboleth-sp - RewriteRule .* http://127.0.0.1:8181%{REQUEST_URI} [P,QSA,NE] - - #Forward all requests to gitlab-workhorse except existing files - RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f [OR] - RewriteCond %{REQUEST_URI} ^/uploads/.* - RewriteCond %{REQUEST_URI} !/Shibboleth.sso - RewriteCond %{REQUEST_URI} !/shibboleth-sp - RewriteRule .* http://127.0.0.1:8181%{REQUEST_URI} [P,QSA] - - RequestHeader set X_FORWARDED_PROTO 'https' - RequestHeader set X-Forwarded-Ssl on +<Location /> + Require all granted + ProxyPassReverse http://127.0.0.1:8181 + ProxyPassReverse http://YOUR_SERVER_FQDN/ +</Location> + +<Location /users/auth/shibboleth/callback> + AuthType shibboleth + ShibRequestSetting requireSession 1 + ShibUseHeaders On + Require shib-session +</Location> + +Alias /shibboleth-sp /usr/share/shibboleth + +<Location /shibboleth-sp> + Require all granted +</Location> + +<Location /Shibboleth.sso> + SetHandler shib +</Location> + +RewriteEngine on + +#Don't escape encoded characters in api requests +RewriteCond %{REQUEST_URI} ^/api/v4/.* +RewriteCond %{REQUEST_URI} !/Shibboleth.sso +RewriteCond %{REQUEST_URI} !/shibboleth-sp +RewriteRule .* http://127.0.0.1:8181%{REQUEST_URI} [P,QSA,NE] + +#Forward all requests to gitlab-workhorse except existing files +RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f [OR] +RewriteCond %{REQUEST_URI} ^/uploads/.* +RewriteCond %{REQUEST_URI} !/Shibboleth.sso +RewriteCond %{REQUEST_URI} !/shibboleth-sp +RewriteRule .* http://127.0.0.1:8181%{REQUEST_URI} [P,QSA] + +RequestHeader set X_FORWARDED_PROTO 'https' +RequestHeader set X-Forwarded-Ssl on ``` diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md index 1cbfd81dfa9..d8096993885 100644 --- a/doc/integration/twitter.md +++ b/doc/integration/twitter.md @@ -2,80 +2,81 @@ To enable the Twitter OmniAuth provider you must register your application with Twitter. Twitter will generate a client ID and secret key for you to use. -1. Sign in to [Twitter Application Management](https://apps.twitter.com/). +1. Sign in to [Twitter Application Management](https://developer.twitter.com/apps). -1. Select "Create new app" +1. Select "Create new app" -1. Fill in the application details. - - Name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or - something else descriptive. - - Description: Create a description. - - Website: The URL to your GitLab installation. `https://gitlab.example.com` - - Callback URL: `https://gitlab.example.com/users/auth/twitter/callback` - - Agree to the "Developer Agreement". +1. Fill in the application details. + - Name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or + something else descriptive. + - Description: Create a description. + - Website: The URL to your GitLab installation. `https://gitlab.example.com` + - Callback URL: `https://gitlab.example.com/users/auth/twitter/callback` + - Agree to the "Developer Agreement". - ![Twitter App Details](img/twitter_app_details.png) -1. Select "Create your Twitter application." + ![Twitter App Details](img/twitter_app_details.png) -1. Select the "Settings" tab. +1. Select "Create your Twitter application." -1. Underneath the Callback URL check the box next to "Allow this application to be used to Sign in with Twitter." +1. Select the "Settings" tab. -1. Select "Update settings" at the bottom to save changes. +1. Underneath the Callback URL check the box next to "Allow this application to be used to Sign in with Twitter." -1. Select the "Keys and Access Tokens" tab. +1. Select "Update settings" at the bottom to save changes. -1. You should now see an API key and API secret (see screenshot). Keep this page open as you continue configuration. +1. Select the "Keys and Access Tokens" tab. - ![Twitter app](img/twitter_app_api_keys.png) +1. You should now see an API key and API secret (see screenshot). Keep this page open as you continue configuration. -1. On your GitLab server, open the configuration file. + ![Twitter app](img/twitter_app_api_keys.png) - For omnibus package: +1. On your GitLab server, open the configuration file. - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + For omnibus package: - For installations from source: + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - ```sh - cd /home/git/gitlab + For installations from source: - sudo -u git -H editor config/gitlab.yml - ``` + ```sh + cd /home/git/gitlab -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + sudo -u git -H editor config/gitlab.yml + ``` -1. Add the provider configuration: +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. - For omnibus package: +1. Add the provider configuration: - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "twitter", - "app_id" => "YOUR_APP_ID", - "app_secret" => "YOUR_APP_SECRET" - } - ] - ``` + For omnibus package: - For installations from source: + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "twitter", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET" + } + ] + ``` - ``` - - { name: 'twitter', app_id: 'YOUR_APP_ID', - app_secret: 'YOUR_APP_SECRET' } - ``` + For installations from source: -1. Change 'YOUR_APP_ID' to the API key from Twitter page in step 11. + ``` + - { name: 'twitter', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET' } + ``` -1. Change 'YOUR_APP_SECRET' to the API secret from the Twitter page in step 11. +1. Change 'YOUR_APP_ID' to the API key from Twitter page in step 11. -1. Save the configuration file. +1. Change 'YOUR_APP_SECRET' to the API secret from the Twitter page in step 11. -1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. Save the configuration file. + +1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page there should now be a Twitter icon below the regular sign in form. Click the icon to begin the authentication process. Twitter will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in. diff --git a/doc/integration/ultra_auth.md b/doc/integration/ultra_auth.md index 69b2a75050d..9ed1bdb4882 100644 --- a/doc/integration/ultra_auth.md +++ b/doc/integration/ultra_auth.md @@ -7,69 +7,78 @@ You can integrate your GitLab instance with [UltraAuth](https://ultraauth.com) t To enable UltraAuth OmniAuth provider, you must use UltraAuth's credentials for your GitLab instance. To get the credentials (a pair of Client ID and Client Secret), you must register an application on UltraAuth. -1. Sign in to [UltraAuth](https://ultraauth.com). -1. Navigate to [Create an App](https://ultraauth.com/select-strategy) and click on "Ruby on Rails". -1. Scroll down the page that is displayed to locate the **Client ID** and **Client Secret**. - Keep this page open as you continue configuration. - ![UltraAuth Credentials: OPENID_CLIENT_ID and OPENID_CLIENT_SECRET](img/ultra_auth_credentials.png) -1. Click on "Edit Callback URL" link. - ![Edit UltraAuth Callback URL](img/ultra_auth_edit_callback_url_highlighted.png) -1. The callback URL will be `http(s)://<your_domain>/users/auth/ultraauth/callback` - ![UltraAuth Callback URL](img/ultra_auth_edit_callback_url.png) -1. Select **Register application**. -1. On your GitLab server, open the configuration file. - - For omnibus package: - - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` - - For installations from source: - - ```sh - cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. -1. Add the provider configuration: - - For omnibus package: - - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "ultraauth", - "app_id" => "OPENID_CLIENT_ID", - "app_secret" => "OPENID_CLIENT_SECRET", - "args" => { - "client_options" => { - "redirect_uri" => "https://example.com/users/auth/ultraauth/callback" - } - } - } - ] - ``` - - For installation from source: - - ``` - - { name: 'ultraauth', - app_id: 'OPENID_CLIENT_ID', - app_secret: 'OPENID_CLIENT_SECRET', - args: { - client_options: { - redirect_uri: 'https://example.com/users/auth/ultraauth/callback' - } - } - } - ``` - __Replace `https://example.com/users/auth/ultraauth/callback` with your application's Callback URL.__ -1. Change `OPENID_CLIENT_ID` to the Client ID from the UltraAuth application page. -1. Change `OPENID_CLIENT_SECRET` to the Client Secret from the UltraAuth application page. -1. Save the configuration file. -1. [Reconfigure GitLab]( ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure ) or [restart GitLab]( ../administration/restart_gitlab.md#installations-from-source ) for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. Sign in to [UltraAuth](https://ultraauth.com). +1. Navigate to [Create an App](https://ultraauth.com/select-strategy) and click on "Ruby on Rails". +1. Scroll down the page that is displayed to locate the **Client ID** and **Client Secret**. + Keep this page open as you continue configuration. + + ![UltraAuth Credentials: OPENID_CLIENT_ID and OPENID_CLIENT_SECRET](img/ultra_auth_credentials.png) + +1. Click on "Edit Callback URL" link. + + ![Edit UltraAuth Callback URL](img/ultra_auth_edit_callback_url_highlighted.png) + +1. The callback URL will be `http(s)://<your_domain>/users/auth/ultraauth/callback` + + ![UltraAuth Callback URL](img/ultra_auth_edit_callback_url.png) + +1. Select **Register application**. +1. On your GitLab server, open the configuration file. + + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For installations from source: + + ```sh + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml + ``` + +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. +1. Add the provider configuration: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "ultraauth", + "app_id" => "OPENID_CLIENT_ID", + "app_secret" => "OPENID_CLIENT_SECRET", + "args" => { + "client_options" => { + "redirect_uri" => "https://example.com/users/auth/ultraauth/callback" + } + } + } + ] + ``` + + For installation from source: + + ``` + - { name: 'ultraauth', + app_id: 'OPENID_CLIENT_ID', + app_secret: 'OPENID_CLIENT_SECRET', + args: { + client_options: { + redirect_uri: 'https://example.com/users/auth/ultraauth/callback' + } + } + } + ``` + + __Replace `https://example.com/users/auth/ultraauth/callback` with your application's Callback URL.__ + +1. Change `OPENID_CLIENT_ID` to the Client ID from the UltraAuth application page. +1. Change `OPENID_CLIENT_SECRET` to the Client Secret from the UltraAuth application page. +1. Save the configuration file. +1. [Reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to take effect if you + installed GitLab via Omnibus or from source respectively. On the sign in page, there should now be an UltraAuth icon below the regular sign in form. Click the icon to begin the authentication process. UltraAuth will ask the user to sign in and authorize the GitLab application. diff --git a/doc/intro/README.md b/doc/intro/README.md index 9a8cd925e48..33b23372280 100644 --- a/doc/intro/README.md +++ b/doc/intro/README.md @@ -41,6 +41,6 @@ Use the built-in continuous integration in GitLab. Install and update your GitLab installation. -- [Install GitLab](https://about.gitlab.com/installation/) +- [Install GitLab](https://about.gitlab.com/install/) - [Update GitLab](https://about.gitlab.com/update/) - [Explore Omnibus GitLab configuration options](https://docs.gitlab.com/omnibus/settings/configuration.html) diff --git a/doc/policy/maintenance.md b/doc/policy/maintenance.md index 72bace3d282..018c273c51a 100644 --- a/doc/policy/maintenance.md +++ b/doc/policy/maintenance.md @@ -9,7 +9,7 @@ patch and security releases. New releases are usually announced on the [GitLab b ## Versioning -GitLab uses [Semantic Versioning](http://semver.org/) for its releases: +GitLab uses [Semantic Versioning](https://semver.org/) for its releases: `(Major).(Minor).(Patch)` in a [pragmatic way](https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e). For example, for GitLab version 10.5.7: diff --git a/doc/push_rules/push_rules.md b/doc/push_rules/push_rules.md index 2142f5a5f69..b1754131e76 100644 --- a/doc/push_rules/push_rules.md +++ b/doc/push_rules/push_rules.md @@ -2,7 +2,7 @@ type: reference, howto --- -# Push Rules **[STARTER]** +# Push Rules **(STARTER)** Gain additional control over what can and can't be pushed to your repository by using regular expressions to reject pushes based on commit contents, branch names or file details. diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md index 0729875daf8..dcc96507676 100644 --- a/doc/raketasks/README.md +++ b/doc/raketasks/README.md @@ -9,10 +9,10 @@ comments: false - [Cleanup](cleanup.md) - [Features](features.md) - [LDAP Maintenance](../administration/raketasks/ldap.md) -- [General Maintenance](maintenance.md) and self-checks +- [General Maintenance](../administration/raketasks/maintenance.md) and self-checks - [User management](user_management.md) - [Webhooks](web_hooks.md) - [Import](import.md) of git repositories in bulk -- [Rebuild authorized_keys file](http://docs.gitlab.com/ce/raketasks/maintenance.html#rebuild-authorized_keys-file) task for administrators +- [Rebuild authorized_keys file](../administration/raketasks/maintenance.md#rebuild-authorized_keys-file) task for administrators - [Migrate Uploads](../administration/raketasks/uploads/migrate.md) - [Sanitize Uploads](../administration/raketasks/uploads/sanitize.md) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 092b4375208..9b1a4105dc3 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -741,9 +741,10 @@ sudo gitlab-rake gitlab:backup:restore BACKUP=1493107454_2018_04_25_10.6.4-ce Next, restore `/etc/gitlab/gitlab-secrets.json` if necessary as mentioned above. -Restart and check GitLab: +Reconfigure, restart and check GitLab: ```shell +sudo gitlab-ctl reconfigure sudo gitlab-ctl restart sudo gitlab-rake gitlab:check SANITIZE=true ``` diff --git a/doc/raketasks/import.md b/doc/raketasks/import.md index b59c06a24ea..8f65fab366e 100644 --- a/doc/raketasks/import.md +++ b/doc/raketasks/import.md @@ -100,7 +100,7 @@ the git repository's config file. This section is formatted as follows: ``` [gitlab] - fullpath = gitlab-org/gitlab-ce + fullpath = gitlab-org/gitlab-ce ``` However, existing repositories were not migrated to include this path. diff --git a/doc/raketasks/web_hooks.md b/doc/raketasks/web_hooks.md index 2c6ae0749dd..a498e9793c1 100644 --- a/doc/raketasks/web_hooks.md +++ b/doc/raketasks/web_hooks.md @@ -1,4 +1,4 @@ -# Webhooks administration **[CORE ONLY]** +# Webhooks administration **(CORE ONLY)** ## Add a webhook for **ALL** projects: diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index a46f7d30892..e8bd35fba5c 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -330,6 +330,7 @@ If the user is blocked via LDAP, `state` will be `ldap_blocked`. "user_id": 41 } ``` + **Group Member Removed:** ```json diff --git a/doc/tools/email.md b/doc/tools/email.md index a2d677484f0..72a5d094bc9 100644 --- a/doc/tools/email.md +++ b/doc/tools/email.md @@ -2,7 +2,7 @@ type: howto, reference --- -# Email from GitLab **[STARTER ONLY]** +# Email from GitLab **(STARTER ONLY)** GitLab provides a simple tool to administrators for emailing all users, or users of a chosen group or project, right from the admin area. Users will receive the email diff --git a/doc/topics/application_development_platform/index.md b/doc/topics/application_development_platform/index.md index 8742606479d..2ea561eb943 100644 --- a/doc/topics/application_development_platform/index.md +++ b/doc/topics/application_development_platform/index.md @@ -9,10 +9,10 @@ The GitLab Application Development Platform aims to: - Reduce and even eliminate the time it takes for an Operations team to provide a full environment for software developers. -- Get developers up and running fast so they can focus on writing +- Get developers up and running fast so they can focus on writing great applications with a robust development feature set. -- Provide best-of-breed security features so that applications developed - with GitLab are not affected by vulnerabilities that may lead to security +- Provide best-of-breed security features so that applications developed + with GitLab are not affected by vulnerabilities that may lead to security problems and unintended use. It is comprised of the following high-level elements: @@ -35,28 +35,28 @@ with various cloud providers. ### Build, test, deploy In order to provide modern DevOps workflows, our Application Development Platform will rely on -[Auto DevOps](https://docs.gitlab.com/ee/topics/autodevops/) to provide those workflows. Auto DevOps works with -any Kubernetes cluster; you're not limited to running on GitLab's infrastructure. Additionally, Auto DevOps offers -an incremental consumption path. Because it is [composable](https://docs.gitlab.com/ee/topics/autodevops/#using-components-of-auto-devops), +[Auto DevOps](../autodevops/index.md) to provide those workflows. Auto DevOps works with +any Kubernetes cluster; you're not limited to running on GitLab's infrastructure. Additionally, Auto DevOps offers +an incremental consumption path. Because it is [composable](../autodevops/index.md#using-components-of-auto-devops), you can use as much or as little of the default pipeline as you'd like, and deeply customize without having to integrate a completely different platform. ### Security -The Application Development Platform helps you ensure that the applications you create are not affected by vulnerabilities +The Application Development Platform helps you ensure that the applications you create are not affected by vulnerabilities that may lead to security problems and unintended use. This can be achieved by making use of the embedded security features of Auto DevOps, -which inform security teams and developers if there is something to consider changing in their apps +which inform security teams and developers if there is something to consider changing in their apps before it is too late to create a preventative fix. The following features are included: -- [Auto SAST (Static Application Security Testing)](https://docs.gitlab.com/ee/topics/autodevops/#auto-sast-ultimate) -- [Auto Dependency Scanning](https://docs.gitlab.com/ee/topics/autodevops/#auto-dependency-scanning-ultimate) -- [Auto Container Scanning](https://docs.gitlab.com/ee/topics/autodevops/#auto-container-scanning-ultimate) -- [Auto DAST (Dynamic Application Security Testing)](https://docs.gitlab.com/ee/topics/autodevops/#auto-dast-ultimate) +- [Auto SAST (Static Application Security Testing)](../autodevops/index.md#auto-sast-ultimate) +- [Auto Dependency Scanning](../autodevops/index.md#auto-dependency-scanning-ultimate) +- [Auto Container Scanning](../autodevops/index.md#auto-container-scanning-ultimate) +- [Auto DAST (Dynamic Application Security Testing)](../autodevops/index.md#auto-dast-ultimate) ### Observability Performance is a critical aspect of the user experience, and ensuring your application is responsive and available is everyone's -responsibility. The Application Development Platform integrates key performance analytics and feedback +responsibility. The Application Development Platform integrates key performance analytics and feedback into GitLab, automatically. The following features are included: -- [Auto Monitoring](https://docs.gitlab.com/ee/topics/autodevops/#auto-monitoring) -- [In-app Kubernetes Pod Logs](https://docs.gitlab.com/ee/user/project/clusters/kubernetes_pod_logs.html)
\ No newline at end of file +- [Auto Monitoring](../autodevops/index.md#auto-monitoring) +- [In-app Kubernetes Pod Logs](../../user/project/clusters/kubernetes_pod_logs.md) diff --git a/doc/topics/authentication/index.md b/doc/topics/authentication/index.md index 228da2d1f57..8b4a2f1630b 100644 --- a/doc/topics/authentication/index.md +++ b/doc/topics/authentication/index.md @@ -17,11 +17,11 @@ This page gathers all the resources for the topic **Authentication** within GitL ## GitLab administrators - [LDAP (Community Edition)](../../administration/auth/ldap.md) -- [LDAP (Enterprise Edition)](../../administration/auth/ldap-ee.md) **[STARTER]** +- [LDAP (Enterprise Edition)](../../administration/auth/ldap-ee.md) **(STARTER)** - [Enforce Two-factor Authentication (2FA)](../../security/two_factor_authentication.md#enforce-two-factor-authentication-2fa) - **Articles:** - [How to Configure LDAP with GitLab CE](../../administration/auth/how_to_configure_ldap_gitlab_ce/index.md) - - [How to Configure LDAP with GitLab EE](../../administration/auth/how_to_configure_ldap_gitlab_ee/index.md) **[STARTER]** + - [How to Configure LDAP with GitLab EE](../../administration/auth/how_to_configure_ldap_gitlab_ee/index.md) **(STARTER)** - [Feature Highlight: LDAP Integration](https://about.gitlab.com/2014/07/10/feature-highlight-ldap-sync/) - [Debugging LDAP](https://about.gitlab.com/handbook/support/workflows/support-engineering/ldap/debugging_ldap.html) - **Integrations:** @@ -30,10 +30,10 @@ This page gathers all the resources for the topic **Authentication** within GitL - [Atlassian Crowd OmniAuth Provider](../../administration/auth/crowd.md) - [CAS OmniAuth Provider](../../integration/cas.md) - [SAML OmniAuth Provider](../../integration/saml.md) - - [SAML for GitLab.com Groups](../../user/group/saml_sso/index.md) **[SILVER ONLY]** - - [SCIM user provisioning for GitLab.com Groups](../../user/group/saml_sso/scim_setup.md) **[SILVER ONLY]** + - [SAML for GitLab.com Groups](../../user/group/saml_sso/index.md) **(SILVER ONLY)** + - [SCIM user provisioning for GitLab.com Groups](../../user/group/saml_sso/scim_setup.md) **(SILVER ONLY)** - [Okta SSO provider](../../administration/auth/okta.md) - - [Kerberos integration (GitLab EE)](../../integration/kerberos.md) **[STARTER]** + - [Kerberos integration (GitLab EE)](../../integration/kerberos.md) **(STARTER)** ## API diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index bd788cb138c..503ad784a77 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -61,15 +61,15 @@ project in a simple and automatic way: 1. [Auto Build](#auto-build) 1. [Auto Test](#auto-test) -1. [Auto Code Quality](#auto-code-quality-starter) **[STARTER]** -1. [Auto SAST (Static Application Security Testing)](#auto-sast-ultimate) **[ULTIMATE]** -1. [Auto Dependency Scanning](#auto-dependency-scanning-ultimate) **[ULTIMATE]** -1. [Auto License Management](#auto-license-management-ultimate) **[ULTIMATE]** -1. [Auto Container Scanning](#auto-container-scanning-ultimate) **[ULTIMATE]** +1. [Auto Code Quality](#auto-code-quality-starter) **(STARTER)** +1. [Auto SAST (Static Application Security Testing)](#auto-sast-ultimate) **(ULTIMATE)** +1. [Auto Dependency Scanning](#auto-dependency-scanning-ultimate) **(ULTIMATE)** +1. [Auto License Management](#auto-license-management-ultimate) **(ULTIMATE)** +1. [Auto Container Scanning](#auto-container-scanning-ultimate) **(ULTIMATE)** 1. [Auto Review Apps](#auto-review-apps) -1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast-ultimate) **[ULTIMATE]** +1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast-ultimate) **(ULTIMATE)** 1. [Auto Deploy](#auto-deploy) -1. [Auto Browser Performance Testing](#auto-browser-performance-testing-premium) **[PREMIUM]** +1. [Auto Browser Performance Testing](#auto-browser-performance-testing-premium) **(PREMIUM)** 1. [Auto Monitoring](#auto-monitoring) As Auto DevOps relies on many different components, it's good to have a basic @@ -169,7 +169,7 @@ Support for `AUTO_DEVOPS_DOMAIN` was [removed in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959). -## Using multiple Kubernetes clusters **[PREMIUM]** +## Using multiple Kubernetes clusters **(PREMIUM)** When using Auto DevOps, you may want to deploy different environments to different Kubernetes clusters. This is possible due to the 1:1 connection that @@ -316,7 +316,7 @@ If a project's repository contains a `Dockerfile`, Auto Build will use If you are also using Auto Review Apps and Auto Deploy and choose to provide your own `Dockerfile`, make sure you expose your application to port `5000` as this is the port assumed by the -[default Helm chart](https://gitlab.com/gitlab-org/charts/auto-deploy-app). +[default Helm chart](https://gitlab.com/gitlab-org/charts/auto-deploy-app). Alternatively you can override the default values by [customizing the Auto Deploy helm chart](#custom-helm-chart) #### Auto Build using Heroku buildpacks @@ -354,7 +354,7 @@ you may succeed with a [custom buildpack](#custom-buildpacks). Check the Auto Test uses tests you already have in your application. If there are no tests, it's up to you to add them. -### Auto Code Quality **[STARTER]** +### Auto Code Quality **(STARTER)** Auto Code Quality uses the [Code Quality image](https://gitlab.com/gitlab-org/security-products/codequality) to run @@ -365,7 +365,7 @@ out. Any differences between the source and target branches are also [shown in the merge request widget](../../user/project/merge_requests/code_quality.md). -### Auto SAST **[ULTIMATE]** +### Auto SAST **(ULTIMATE)** > Introduced in [GitLab Ultimate][ee] 10.3. @@ -380,7 +380,7 @@ check out. Any security warnings are also shown in the merge request widget. Read more how [SAST works](../../user/application_security/sast/index.md). -### Auto Dependency Scanning **[ULTIMATE]** +### Auto Dependency Scanning **(ULTIMATE)** > Introduced in [GitLab Ultimate][ee] 10.7. @@ -397,7 +397,7 @@ check out. Any security warnings are also shown in the merge request widget. Read more about [Dependency Scanning](../../user/application_security/dependency_scanning/index.md). -### Auto License Management **[ULTIMATE]** +### Auto License Management **(ULTIMATE)** > Introduced in [GitLab Ultimate][ee] 11.0. @@ -413,7 +413,7 @@ check out. Any licenses are also shown in the merge request widget. Read more how [License Management works](../../user/application_security/license_management/index.md). -### Auto Container Scanning **[ULTIMATE]** +### Auto Container Scanning **(ULTIMATE)** > Introduced in GitLab 10.4. @@ -452,7 +452,7 @@ be deleted. Review apps are deployed using the [auto-deploy-app](https://gitlab.com/gitlab-org/charts/auto-deploy-app) chart with -Helm. The app will be deployed into the [Kubernetes +Helm, which can be [customized](#custom-helm-chart). The app will be deployed into the [Kubernetes namespace](../../user/project/clusters/index.md#deployment-variables) for the environment. @@ -468,7 +468,7 @@ deploys with Auto DevOps can undo your changes. Also, if you change something and want to undo it by deploying again, Helm may not detect that anything changed in the first place, and thus not realize that it needs to re-apply the old config. -### Auto DAST **[ULTIMATE]** +### Auto DAST **(ULTIMATE)** > Introduced in [GitLab Ultimate][ee] 10.4. @@ -483,7 +483,7 @@ later download and check out. Any security warnings are also shown in the merge request widget. Read how [DAST works](../../user/application_security/dast/index.md). -### Auto Browser Performance Testing **[PREMIUM]** +### Auto Browser Performance Testing **(PREMIUM)** > Introduced in [GitLab Premium][ee] 10.4. @@ -514,7 +514,7 @@ Auto Deploy doesn't include deployments to staging or canary by default, but the enable them. You can make use of [environment variables](#environment-variables) to automatically -scale your pod replicas. +scale your pod replicas and to apply custom arguments to the Auto DevOps `helm upgrade` commands. This is an easy way to [customize the Auto Deploy helm chart](#custom-helm-chart). Apps are deployed using the [auto-deploy-app](https://gitlab.com/gitlab-org/charts/auto-deploy-app) chart with @@ -655,7 +655,10 @@ repo or by specifying a project variable: - **Project variable** - Create a [project variable](../../ci/variables/README.md#gitlab-cicd-environment-variables) `AUTO_DEVOPS_CHART` with the URL of a custom chart to use or create two project variables `AUTO_DEVOPS_CHART_REPOSITORY` with the URL of a custom chart repository and `AUTO_DEVOPS_CHART` with the path to the chart. -### Custom Helm chart per environment **[PREMIUM]** +You can also make use of the `HELM_UPGRADE_EXTRA_ARGS` environment variable to override the default values in the `values.yaml` file in the [default Helm chart](https://gitlab.com/gitlab-org/charts/auto-deploy-app). +To apply your own `values.yaml` file to all Helm upgrade commands in Auto Deploy set `HELM_UPGRADE_EXTRA_ARGS` to `--values my-values.yaml`. + +### Custom Helm chart per environment **(PREMIUM)** You can specify the use of a custom Helm chart per environment by scoping the environment variable to the desired environment. See [Limiting environment scopes of variables](../../ci/variables/README.md#limiting-environment-scopes-of-environment-variables-premium). @@ -761,7 +764,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac | `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/index.md#base-domain) for more information. | | `ROLLOUT_RESOURCE_TYPE` | From GitLab 11.9, this variable allows specification of the resource type being deployed when using a custom helm chart. Default value is `deployment`. | | `ROLLOUT_STATUS_DISABLED` | From GitLab 12.0, this variable allows to disable rollout status check because it doesn't support all resource types, for example, `cronjob`. | -| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, this variable allows extra arguments in `helm` commands when deploying the application. Note that using quotes will not prevent word splitting. | +| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, this variable allows extra arguments in `helm` commands when deploying the application. Note that using quotes will not prevent word splitting. **Tip:** you can use this variable to [customize the Auto Deploy helm chart](https://docs.gitlab.com/ee/topics/autodevops/index.html#custom-helm-chart) by applying custom override values with `--values my-values.yaml`. | TIP: **Tip:** Set up the replica variables using a @@ -903,7 +906,7 @@ If `STAGING_ENABLED` is defined in your project (e.g., set `STAGING_ENABLED` to to a `staging` environment, and a `production_manual` job will be created for you when you're ready to manually deploy to production. -#### Deploy policy for canary environments **[PREMIUM]** +#### Deploy policy for canary environments **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ci-yml/merge_requests/171) in GitLab 11.0. @@ -918,7 +921,7 @@ If `CANARY_ENABLED` is defined in your project (e.g., set `CANARY_ENABLED` to - `production_manual` which is to be used by you when you're ready to manually deploy to production. -#### Incremental rollout to production **[PREMIUM]** +#### Incremental rollout to production **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5415) in GitLab 10.8. @@ -976,7 +979,7 @@ Before GitLab 11.4 this feature was enabled by the presence of the `INCREMENTAL_ROLLOUT_ENABLED` environment variable. This configuration is deprecated and will be removed in the future. -#### Timed incremental rollout to production **[PREMIUM]** +#### Timed incremental rollout to production **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7545) in GitLab 11.4. diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md index 6717e95266e..c1771a57da0 100644 --- a/doc/topics/autodevops/quick_start_guide.md +++ b/doc/topics/autodevops/quick_start_guide.md @@ -159,15 +159,15 @@ In the **test** stage, GitLab runs various checks on the application: - The `test` job runs unit and integration tests by detecting the language and framework ([Auto Test](index.md#auto-test)) - The `code_quality` job checks the code quality and is allowed to fail - ([Auto Code Quality](index.md#auto-code-quality-starter)) **[STARTER]** + ([Auto Code Quality](index.md#auto-code-quality-starter)) **(STARTER)** - The `container_scanning` job checks the Docker container if it has any vulnerabilities and is allowed to fail ([Auto Container Scanning](index.md#auto-container-scanning-ultimate)) - The `dependency_scanning` job checks if the application has any dependencies - susceptible to vulnerabilities and is allowed to fail ([Auto Dependency Scanning](index.md#auto-dependency-scanning-ultimate)) **[ULTIMATE]** + susceptible to vulnerabilities and is allowed to fail ([Auto Dependency Scanning](index.md#auto-dependency-scanning-ultimate)) **(ULTIMATE)** - The `sast` job runs static analysis on the current code to check for potential - security issues and is allowed to fail([Auto SAST](index.md#auto-sast-ultimate)) **[ULTIMATE]** + security issues and is allowed to fail([Auto SAST](index.md#auto-sast-ultimate)) **(ULTIMATE)** - The `license_management` job searches the application's dependencies to determine each of their - licenses and is allowed to fail ([Auto License Management](index.md#auto-license-management-ultimate)) **[ULTIMATE]** + licenses and is allowed to fail ([Auto License Management](index.md#auto-license-management-ultimate)) **(ULTIMATE)** NOTE: **Note:** As you might have noticed, all jobs except `test` are allowed to fail in the @@ -178,7 +178,7 @@ deploys the application in Kubernetes ([Auto Deploy](index.md#auto-deploy)). Lastly, in the **performance** stage, some performance tests will run on the deployed application -([Auto Browser Performance Testing](index.md#auto-browser-performance-testing-premium)). **[PREMIUM]** +([Auto Browser Performance Testing](index.md#auto-browser-performance-testing-premium)). **(PREMIUM)** --- @@ -285,8 +285,8 @@ all within GitLab. Despite its automatic nature, Auto DevOps can also be configu and customized to fit your workflow. Here are some helpful resources for further reading: 1. [Auto DevOps](index.md) -1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters-premium) **[PREMIUM]** -1. [Incremental rollout to production](index.md#incremental-rollout-to-production-premium) **[PREMIUM]** +1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters-premium) **(PREMIUM)** +1. [Incremental rollout to production](index.md#incremental-rollout-to-production-premium) **(PREMIUM)** 1. [Disable jobs you don't need with environment variables](index.md#environment-variables) 1. [Use a static IP for your cluster](../../user/project/clusters/index.md#using-a-static-ip) 1. [Use your own buildpacks to build your application](index.md#custom-buildpacks) diff --git a/doc/university/README.md b/doc/university/README.md index 9d861460618..f696db2df20 100644 --- a/doc/university/README.md +++ b/doc/university/README.md @@ -73,7 +73,7 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres - Being part of our Great Community and Contributing to GitLab 1. [Getting Started with the GitLab Development Kit (GDK)](https://about.gitlab.com/2016/06/08/getting-started-with-gitlab-development-kit/) 1. [Contributing Technical Articles to the GitLab Blog](https://about.gitlab.com/2016/01/26/call-for-writers/) -1. [GitLab Training Workshops](https://docs.gitlab.com/ce/university/training/end-user/) +1. [GitLab Training Workshops](training/end-user/README.md) 1. [GitLab Professional Services](https://about.gitlab.com/services/) ### 1.8 GitLab Training Material diff --git a/doc/university/support/README.md b/doc/university/support/README.md index 2c6e52acfde..fdeba89f9c8 100644 --- a/doc/university/support/README.md +++ b/doc/university/support/README.md @@ -45,7 +45,7 @@ It's important to understand how to install GitLab in the same way that our user Sometimes we need to upgrade customers from old versions of GitLab to latest, so it's good to get some experience of doing that now. -- [Installation Methods](https://about.gitlab.com/installation/): +- [Installation Methods](https://about.gitlab.com/install/): - [Omnibus](https://gitlab.com/gitlab-org/omnibus-gitlab/) - [Docker](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/docker) - [Source](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) diff --git a/doc/university/training/index.md b/doc/university/training/index.md index 4c8ae0d9ce8..61fde9d8336 100644 --- a/doc/university/training/index.md +++ b/doc/university/training/index.md @@ -6,7 +6,7 @@ type: index # GitLab Training Material All GitLab training material is stored in markdown format. Slides are -generated using [Deskset](http://www.decksetapp.com/). +generated using [Deskset](https://www.deckset.com/). All training material is open to public contribution. @@ -35,8 +35,8 @@ This section contains the following topics: ## Additional Resources 1. [GitLab Documentation](https://docs.gitlab.com) -1. [GUI Clients](http://git-scm.com/downloads/guis) -1. [Pro Git book](http://git-scm.com/book) +1. [GUI Clients](https://git-scm.com/downloads/guis) +1. [Pro Git book](https://git-scm.com/book/en/v2) 1. [Platzi Course](https://courses.platzi.com/courses/git-gitlab/) 1. [Code School tutorial](http://try.github.io/) 1. Contact us at `subscribers@gitlab.com` diff --git a/doc/university/training/topics/env_setup.md b/doc/university/training/topics/env_setup.md index 305f5ecb1fb..92d2613c5d2 100644 --- a/doc/university/training/topics/env_setup.md +++ b/doc/university/training/topics/env_setup.md @@ -14,9 +14,11 @@ comments: false - If it's not installed, it will prompt you to install it. - **Linux** + ```bash sudo yum install git-all ``` + ```bash sudo apt-get install git-all ``` diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index 4b13e41ab53..1e424134242 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -13,7 +13,7 @@ NOTE: **Note:** Support for MySQL was removed in GitLab 12.1. This procedure should be performed **before** installing GitLab 12.1. -[pgloader](http://pgloader.io) 3.4.1+ is required. +[pgloader](https://pgloader.io/) 3.4.1+ is required. You can install it directly from your distribution, for example in Debian/Ubuntu: @@ -59,7 +59,7 @@ pgloader within the container as it is not included in the container image. ``` 1. Install pgloader: - + ``` bash apt-get update apt-get -y install pgloader diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index 4300d6d56c7..0506d992d4b 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -100,7 +100,7 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) sudo -u git -H make ``` -### 8. Install/Update `gitlab-elasticsearch-indexer` (optional) **[STARTER ONLY]** +### 8. Install/Update `gitlab-elasticsearch-indexer` (optional) **(STARTER ONLY)** If you're interested in using GitLab's new [elasticsearch repository indexer](../integration/elasticsearch.md#elasticsearch-repository-indexer-beta) (currently in beta) please follow the instructions on the document linked above and enable the diff --git a/doc/update/upgrading_from_ce_to_ee.md b/doc/update/upgrading_from_ce_to_ee.md index 7ae716d2cb3..bea5bcd9dd7 100644 --- a/doc/update/upgrading_from_ce_to_ee.md +++ b/doc/update/upgrading_from_ce_to_ee.md @@ -72,7 +72,7 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` -### 4. Install `gitlab-elasticsearch-indexer` (optional) **[STARTER ONLY]** +### 4. Install `gitlab-elasticsearch-indexer` (optional) **(STARTER ONLY)** If you're interested in using GitLab's new [elasticsearch repository indexer](../integration/elasticsearch.md) (currently in beta) please follow the instructions on the diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md index 023dc7d6de3..d3b0a3c2829 100644 --- a/doc/update/upgrading_from_source.md +++ b/doc/update/upgrading_from_source.md @@ -76,7 +76,7 @@ sudo gem install bundler --no-document --version '< 2' NOTE: Beginning in GitLab 11.8, we only support node 8 or higher, and dropped support for node 6. Be sure to upgrade if necessary. -GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets. +GitLab utilizes [webpack](https://webpack.js.org/) to compile frontend assets. This requires a minimum version of node v8.10.0. You can check which version you are running with `node -v`. If you are running diff --git a/doc/user/admin_area/custom_project_templates.md b/doc/user/admin_area/custom_project_templates.md index e34ba045c54..427f3103cfc 100644 --- a/doc/user/admin_area/custom_project_templates.md +++ b/doc/user/admin_area/custom_project_templates.md @@ -1,6 +1,6 @@ -# Custom instance-level project templates **[PREMIUM ONLY]** +# Custom instance-level project templates **(PREMIUM ONLY)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6860) in [GitLab Premium](https://about.gitlab.com/pricing) 11.2. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6860) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2. When you create a new [project](../project/index.md), creating it based on custom project templates is a convenient bootstrap option. diff --git a/doc/user/admin_area/geo_nodes.md b/doc/user/admin_area/geo_nodes.md index d99b87cbc5c..39753fd885e 100644 --- a/doc/user/admin_area/geo_nodes.md +++ b/doc/user/admin_area/geo_nodes.md @@ -2,7 +2,7 @@ type: howto --- -# Geo nodes admin area **[PREMIUM ONLY]** +# Geo nodes admin area **(PREMIUM ONLY)** You can configure various settings for GitLab Geo nodes. For more information, see [Geo documentation](../../administration/geo/replication/index.md). @@ -61,6 +61,12 @@ which is used by users. Internal URL does not need to be a private address. Internal URL defaults to External URL, but you can customize it under **Admin area > Geo Nodes**. +CAUTION: **Warning:** +We recommend using an HTTPS connection while configuring the Geo nodes. To avoid +breaking communication between **primary** and **secondary** nodes when using +HTTPS, customize your Internal URL to point to a load balancer with TLS +terminated at the load balancer. + ## Multiple secondary nodes behind a load balancer In GitLab 11.11, **secondary** nodes can use identical external URLs as long as @@ -83,4 +89,4 @@ questions that you know someone might ask. Each scenario can be a third-level heading, e.g. `### Getting error message X`. If you have none to add when creating a doc, leave this section in place -but commented out to help encourage others to add to it in the future. -->
\ No newline at end of file +but commented out to help encourage others to add to it in the future. --> diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index d2947ae3371..f5e6bff67c5 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -2,7 +2,7 @@ type: reference --- -# GitLab Admin Area **[CORE ONLY]** +# GitLab Admin Area **(CORE ONLY)** The Admin Area provides a web UI for administering some features of GitLab self-managed instances. @@ -26,9 +26,9 @@ The Admin Area is made up of the following sections: | System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. | | Applications | Create system [OAuth applications](../../integration/oauth_provider.md) for integrations with other services. | | Abuse Reports | Manage [abuse reports](abuse_reports.md) submitted by your users. | -| License **[STARTER ONLY]** | Upload, display, and remove [licenses](license.md). | -| Push Rules **[STARTER]** | Configure pre-defined git [push rules](../../push_rules/push_rules.md) for projects. | -| Geo **[PREMIUM ONLY]** | Configure and maintain [Geo nodes](geo_nodes.md). | +| License **(STARTER ONLY)** | Upload, display, and remove [licenses](license.md). | +| Push Rules **(STARTER)** | Configure pre-defined git [push rules](../../push_rules/push_rules.md) for projects. | +| Geo **(PREMIUM ONLY)** | Configure and maintain [Geo nodes](geo_nodes.md). | | Deploy Keys | Create instance-wide [SSH deploy keys](../../ssh/README.md#deploy-keys). | | Service Templates | Create [service templates](../project/integrations/services_templates.md) for projects. | | Labels | Create and maintain [labels](labels.md) for your GitLab instance. | @@ -289,6 +289,6 @@ The content of each log file is listed in chronological order. To minimize perfo The **Requests Profiles** page contains the token required for profiling. For more details, see [Request Profiling](../../administration/monitoring/performance/request_profiling.md). -### Audit Log **[PREMIUM ONLY]** +### Audit Log **(PREMIUM ONLY)** The **Audit Log** page lists changes made within the GitLab server. With this information you can control, analyze, and track every change. diff --git a/doc/user/admin_area/labels.md b/doc/user/admin_area/labels.md index eba27548f86..1d15be89bd5 100644 --- a/doc/user/admin_area/labels.md +++ b/doc/user/admin_area/labels.md @@ -2,7 +2,7 @@ type: reference --- -# Labels administration **[CORE ONLY]** +# Labels administration **(CORE ONLY)** In the Admin Area, you can manage labels for the GitLab instance. For more details, see [Labels](../project/labels.md). diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md index 8ddb9c3d707..bbd04146eb2 100644 --- a/doc/user/admin_area/license.md +++ b/doc/user/admin_area/license.md @@ -2,7 +2,7 @@ type: howto --- -# Activate all GitLab Enterprise Edition functionality with a license **[STARTER ONLY]** +# Activate all GitLab Enterprise Edition functionality with a license **(STARTER ONLY)** To activate all GitLab Enterprise Edition (EE) functionality, you need to upload a license. Once you've received your license from GitLab Inc., you can upload it diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md index 756a07e0b80..9968b7349dc 100644 --- a/doc/user/admin_area/settings/account_and_limit_settings.md +++ b/doc/user/admin_area/settings/account_and_limit_settings.md @@ -4,7 +4,7 @@ type: reference # Account and limit settings -## Repository size limit **[STARTER]** +## Repository size limit **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/740) in [GitLab Enterprise Edition 8.12](https://about.gitlab.com/2016/09/22/gitlab-8-12-released/#limit-project-size-ee). > Available in [GitLab Starter](https://about.gitlab.com/pricing/). diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md index 84596ff6a2c..ebbb2472752 100644 --- a/doc/user/admin_area/settings/continuous_integration.md +++ b/doc/user/admin_area/settings/continuous_integration.md @@ -2,14 +2,14 @@ type: reference --- -# Continuous Integration and Deployment Admin settings **[CORE ONLY]** +# Continuous Integration and Deployment Admin settings **(CORE ONLY)** In this area, you will find settings for Auto DevOps, Runners and job artifacts. You can find it in the admin area, under **Settings > Continuous Integration and Deployment**. ![Admin area settings button](../img/admin_area_settings_button.png) -## Auto DevOps **[CORE ONLY]** +## Auto DevOps **(CORE ONLY)** To enable (or disable) [Auto DevOps](../../../topics/autodevops/index.md) for all projects: @@ -26,7 +26,7 @@ From now on, every existing project and newly created ones that don't have a If you want to disable it for a specific project, you can do so in [its settings](../../../topics/autodevops/index.md#enablingdisabling-auto-devops). -## Maximum artifacts size **[CORE ONLY]** +## Maximum artifacts size **(CORE ONLY)** The maximum size of the [job artifacts](../../../administration/job_artifacts.md) can be set in the Admin area of your GitLab instance. The value is in *MB* and @@ -38,7 +38,7 @@ To change it: 1. Change the value of maximum artifacts size (in MB). 1. Hit **Save changes** for the changes to take effect. -## Default artifacts expiration **[CORE ONLY]** +## Default artifacts expiration **(CORE ONLY)** The default expiration time of the [job artifacts](../../../administration/job_artifacts.md) can be set in the Admin area of your GitLab instance. The syntax of duration is @@ -54,7 +54,7 @@ This setting is set per job and can be overridden in [`.gitlab-ci.yml`](../../../ci/yaml/README.md#artifactsexpire_in). To disable the expiration, set it to `0`. The default unit is in seconds. -## Shared Runners pipeline minutes quota **[STARTER ONLY]** +## Shared Runners pipeline minutes quota **(STARTER ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1078) in GitLab Starter 8.16. @@ -137,7 +137,7 @@ includes a link to [purchase more minutes](https://customers.gitlab.com/plans). If you are not the owner of the group, you will need to contact them to let them know they need to [purchase more minutes](https://customers.gitlab.com/plans). -## Archive jobs **[CORE ONLY]** +## Archive jobs **(CORE ONLY)** Archiving jobs is useful for reducing the CI/CD footprint on the system by removing some of the capabilities of the jobs (metadata needed to run the job), diff --git a/doc/user/admin_area/settings/email.md b/doc/user/admin_area/settings/email.md index 9555a695b13..1f07a4dfdc6 100644 --- a/doc/user/admin_area/settings/email.md +++ b/doc/user/admin_area/settings/email.md @@ -10,7 +10,7 @@ You can customize some of the content in emails sent from your GitLab instance. The logo in the header of some emails can be customized, see the [logo customization section](../../../customization/branded_page_and_email_header.md). -## Custom additional text **[PREMIUM ONLY]** +## Custom additional text **(PREMIUM ONLY)** > [Introduced][ee-5031] in [GitLab Premium][eep] 10.7. diff --git a/doc/user/admin_area/settings/external_authorization.md b/doc/user/admin_area/settings/external_authorization.md index c1aa04f7bc2..4fde7477490 100644 --- a/doc/user/admin_area/settings/external_authorization.md +++ b/doc/user/admin_area/settings/external_authorization.md @@ -2,10 +2,10 @@ type: reference --- -# External authorization control **[CORE ONLY]** +# External authorization control **(CORE ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4216) in -> [GitLab Premium](https://about.gitlab.com/pricing) 10.6. +> [GitLab Premium](https://about.gitlab.com/pricing/) 10.6. > [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/27056) to > [GitLab Core](https://about.gitlab.com/pricing/) in 11.10. diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md index eed087ae52b..5427d04cd7d 100644 --- a/doc/user/admin_area/settings/index.md +++ b/doc/user/admin_area/settings/index.md @@ -2,7 +2,7 @@ type: index --- -# Admin Area settings **[CORE ONLY]** +# Admin Area settings **(CORE ONLY)** In the Admin Area **Settings** page, you can find various options for your GitLab instance like sign-up restrictions, account limits and quota, metrics, etc. @@ -10,7 +10,7 @@ instance like sign-up restrictions, account limits and quota, metrics, etc. Navigate to it by going to **Admin Area > Settings**. Some of the settings include: -- [Account and limit settings](account_and_limit_settings.md) **[STARTER]** +- [Account and limit settings](account_and_limit_settings.md) **(STARTER)** - [Continuous Integration and Deployment](continuous_integration.md) - [Email](email.md) - [Sign up restrictions](sign_up_restrictions.md) @@ -18,7 +18,7 @@ include: - [Third party offers](third_party_offers.md) - [Usage statistics](usage_statistics.md) - [Visibility and access controls](visibility_and_access_controls.md) -- [Custom templates repository](instance_template_repository.md) **[PREMIUM]** +- [Custom templates repository](instance_template_repository.md) **(PREMIUM)** NOTE: **Note:** You can change the [first day of the week](../../profile/preferences.md) for the entire GitLab instance diff --git a/doc/user/admin_area/settings/instance_template_repository.md b/doc/user/admin_area/settings/instance_template_repository.md index 91286a67c31..f2ba131d17b 100644 --- a/doc/user/admin_area/settings/instance_template_repository.md +++ b/doc/user/admin_area/settings/instance_template_repository.md @@ -2,10 +2,10 @@ type: reference --- -# Instance template repository **[PREMIUM ONLY]** +# Instance template repository **(PREMIUM ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5986) in -> [GitLab Premium](https://about.gitlab.com/pricing) 11.3. +> [GitLab Premium](https://about.gitlab.com/pricing/) 11.3. ## Overview diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md index 652d6ad2cdd..f698e0a1608 100644 --- a/doc/user/admin_area/settings/usage_statistics.md +++ b/doc/user/admin_area/settings/usage_statistics.md @@ -10,7 +10,7 @@ to perform various actions. All statistics are opt-out, you can enable/disable them from the admin panel under **Admin area > Settings > Metrics and profiling > Usage statistics**. -## Version check **[CORE ONLY]** +## Version check **(CORE ONLY)** If enabled, version check will inform you if a new version is available and the importance of it through a status. This is shown on the help page (i.e. `/help`) @@ -33,7 +33,7 @@ secure. If you disable version check, this information will not be collected. Enable or disable the version check at **Admin area > Settings > Usage statistics**. -## Usage ping **[CORE ONLY]** +## Usage ping **(CORE ONLY)** > [Introduced][ee-557] in GitLab Enterprise Edition 8.10. More statistics [were added][ee-735] in GitLab Enterprise Edition @@ -78,7 +78,7 @@ production: &base usage_ping_enabled: false ``` -## Instance statistics visibility **[CORE ONLY]** +## Instance statistics visibility **(CORE ONLY)** Once usage ping is enabled, GitLab will gather data from other instances and will be able to show [usage statistics](../../instance_statistics/index.md) diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md index 63879935fd8..bf59f49b993 100644 --- a/doc/user/admin_area/settings/visibility_and_access_controls.md +++ b/doc/user/admin_area/settings/visibility_and_access_controls.md @@ -12,7 +12,7 @@ GitLab allows administrators to: - Enable or disable repository mirroring. - Prevent non-administrators from deleting projects ([introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5615) in GitLab 12.0). - **[PREMIUM ONLY]** + **(PREMIUM ONLY)** To access the visibility and access control options: diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md index 9dfbe326f1d..696446599c8 100644 --- a/doc/user/application_security/container_scanning/index.md +++ b/doc/user/application_security/container_scanning/index.md @@ -1,4 +1,4 @@ -# Container Scanning **[ULTIMATE]** +# Container Scanning **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3672) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.4. diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md index 2283efe3a44..936703cce32 100644 --- a/doc/user/application_security/dast/index.md +++ b/doc/user/application_security/dast/index.md @@ -1,4 +1,4 @@ -# Dynamic Application Security Testing (DAST) **[ULTIMATE]** +# Dynamic Application Security Testing (DAST) **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4348) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.4. diff --git a/doc/user/application_security/dependency_scanning/analyzers.md b/doc/user/application_security/dependency_scanning/analyzers.md index 937ded287e5..3b4b341739b 100644 --- a/doc/user/application_security/dependency_scanning/analyzers.md +++ b/doc/user/application_security/dependency_scanning/analyzers.md @@ -1,4 +1,4 @@ -# Dependency Scanning Analyzers **[ULTIMATE]** +# Dependency Scanning Analyzers **(ULTIMATE)** Dependency Scanning relies on underlying third party tools that are wrapped into what we call "Analyzers". An analyzer is a diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 9145e034dcb..2fe8a6f9029 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -1,4 +1,4 @@ -# Dependency Scanning **[ULTIMATE]** +# Dependency Scanning **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5105) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.7. diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md index 69fa1ec5da6..91e79f6c23b 100644 --- a/doc/user/application_security/index.md +++ b/doc/user/application_security/index.md @@ -1,4 +1,4 @@ -# GitLab Secure **[ULTIMATE]** +# GitLab Secure **(ULTIMATE)** Check your application for security vulnerabilities that may lead to unauthorized access, data leaks, and denial of services. GitLab will perform static and dynamic tests on the @@ -12,12 +12,12 @@ GitLab can scan and report any vulnerabilities found in your project. | Secure scanning tool | Description | |:-----------------------------------------------------------------------------|:-----------------------------------------------------------------------| -| [Container Scanning](container_scanning/index.md) **[ULTIMATE]** | Scan Docker containers for known vulnerabilities. | -| [Dependency Scanning](dependency_scanning/index.md) **[ULTIMATE]** | Analyze your dependencies for known vulnerabilities. | -| [Dynamic Application Security Testing (DAST)](dast/index.md) **[ULTIMATE]** | Analyze running web applications for known vulnerabilities. | -| [License Management](license_management/index.md) **[ULTIMATE]** | Search your project's dependencies for their licenses. | -| [Security Dashboard](security_dashboard/index.md) **[ULTIMATE]** | View vulnerabilities in all your projects and groups. | -| [Static Application Security Testing (SAST)](sast/index.md) **[ULTIMATE]** | Analyze source code for known vulnerabilities. | +| [Container Scanning](container_scanning/index.md) **(ULTIMATE)** | Scan Docker containers for known vulnerabilities. | +| [Dependency Scanning](dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. | +| [Dynamic Application Security Testing (DAST)](dast/index.md) **(ULTIMATE)** | Analyze running web applications for known vulnerabilities. | +| [License Management](license_management/index.md) **(ULTIMATE)** | Search your project's dependencies for their licenses. | +| [Security Dashboard](security_dashboard/index.md) **(ULTIMATE)** | View vulnerabilities in all your projects and groups. | +| [Static Application Security Testing (SAST)](sast/index.md) **(ULTIMATE)** | Analyze source code for known vulnerabilities. | ## Maintenance and update of the vulnerabilities database diff --git a/doc/user/application_security/license_management/index.md b/doc/user/application_security/license_management/index.md index 957c4ede981..8eb231f8359 100644 --- a/doc/user/application_security/license_management/index.md +++ b/doc/user/application_security/license_management/index.md @@ -1,4 +1,4 @@ -# License Management **[ULTIMATE]** +# License Management **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5483) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.0. @@ -46,12 +46,19 @@ The following languages and package managers are supported. | Language | Package managers | Scan Tool | |------------|-------------------------------------------------------------------|----------------------------------------------------------| -| JavaScript | [Bower](https://bower.io/), [npm](https://www.npmjs.com/) |[License Finder](https://github.com/pivotal/LicenseFinder)| -| Go | [Godep](https://github.com/tools/godep), go get |[License Finder](https://github.com/pivotal/LicenseFinder)| +| JavaScript | [Bower](https://bower.io/), [npm](https://www.npmjs.com/), [yarn](https://yarnpkg.com/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)| +| Go | [Godep](https://github.com/tools/godep), go get ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), gvt ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), glide ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), dep ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), trash ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) and govendor ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), [go mod](https://github.com/golang/go/wiki/Modules) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)| | Java | [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)| | .NET | [Nuget](https://www.nuget.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)| | Python | [pip](https://pip.pypa.io/en/stable/) |[License Finder](https://github.com/pivotal/LicenseFinder)| | Ruby | [gem](https://rubygems.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)| +| Erlang | [rebar](https://www.rebar3.org/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)| +| Objective-C, Swift | [Carthage](https://github.com/Carthage/Carthage) , [CocoaPods v0.39 and below](https://cocoapods.org/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)| +| Elixir | [mix](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)| +| C++/C | [conan](https://conan.io/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)| +| Scala | [sbt](https://www.scala-sbt.org/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)| +| Rust | [cargo](https://crates.io/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)| +| PHP | [composer](https://getcomposer.org/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)| ## Requirements diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md index 9074ac3f4a1..84b45cbe6e6 100644 --- a/doc/user/application_security/sast/index.md +++ b/doc/user/application_security/sast/index.md @@ -1,4 +1,4 @@ -# Static Application Security Testing (SAST) **[ULTIMATE]** +# Static Application Security Testing (SAST) **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3775) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.3. diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md index 19eeb06a259..3b01fe66e03 100644 --- a/doc/user/application_security/security_dashboard/index.md +++ b/doc/user/application_security/security_dashboard/index.md @@ -1,4 +1,4 @@ -# GitLab Security Dashboard **[ULTIMATE]** +# GitLab Security Dashboard **(ULTIMATE)** The Security Dashboard is a good place to get an overview of all the security vulnerabilities in your groups and projects. diff --git a/doc/user/asciidoc.md b/doc/user/asciidoc.md index 0ed9bf3f518..df86b2a1cbe 100644 --- a/doc/user/asciidoc.md +++ b/doc/user/asciidoc.md @@ -1,7 +1,7 @@ # AsciiDoc GitLab uses the [Asciidoctor](https://asciidoctor.org) gem to convert AsciiDoc content to HTML5. -Consult the [Asciidoctor User Manual](https://asciidoctor.org/docs/user-manual) for a complete Asciidoctor reference. +Consult the [Asciidoctor User Manual](https://asciidoctor.org/docs/user-manual/) for a complete Asciidoctor reference. ## Syntax @@ -170,6 +170,7 @@ Attach a block or paragraph to a list item using a list continuation (which you * [x] checked * [ ] not checked ``` + #### Callout ```asciidoc @@ -188,6 +189,7 @@ first term:: description of first term second term:: description of second term ``` + ### Document Structure #### Header @@ -197,6 +199,7 @@ description of second term Author Name <author@example.org> v1.0, 2019-01-01 ``` + #### Sections ```asciidoc @@ -217,6 +220,7 @@ include::basics.adoc[] // define -a allow-uri-read to allow content to be read from URI include::https://example.org/installation.adoc[] ``` + ### Blocks ```asciidoc @@ -273,11 +277,11 @@ source - a listing that is embellished with (colorized) syntax highlighting ---- ``` -```asciidoc +````asciidoc \```language fenced code - a shorthand syntax for the source block \``` -``` +```` ```asciidoc [,attribution,citetitle] @@ -369,4 +373,3 @@ video::300817511[vimeo] // page break <<< ``` - diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md index 20eabdada79..c6bc580fb8f 100644 --- a/doc/user/discussions/index.md +++ b/doc/user/discussions/index.md @@ -5,7 +5,7 @@ The ability to contribute conversationally is offered throughout GitLab. You can leave a comment in the following places: - issues -- epics **[ULTIMATE]** +- epics **(ULTIMATE)** - merge requests - snippets - commits @@ -282,7 +282,7 @@ edit existing comments. Non-team members are restricted from adding or editing c Additionally, locked issues and merge requests can not be reopened. -## Merge Request Reviews **[PREMIUM]** +## Merge Request Reviews **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4213) in GitLab 11.4. @@ -444,7 +444,7 @@ Clicking on the **Reply to comment** button will bring the reply area into focus ![Reply to comment feature](img/reply_to_comment.gif) -Relying to a non-discussion comment will convert the non-discussion comment to a +Replying to a non-discussion comment will convert the non-discussion comment to a threaded discussion once the reply is submitted. This conversion is considered an edit to the original comment, so a note about when it was last edited will appear underneath it. diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index 886fb6e6f55..7858c419e04 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -1,7 +1,7 @@ # GitLab.com settings In this page you will find information about the settings that are used on -[GitLab.com](https://about.gitlab.com/pricing). +[GitLab.com](https://about.gitlab.com/pricing/). ## SSH host keys fingerprints @@ -73,9 +73,9 @@ or over the size limit, you can [reduce your repository size with Git](../projec ## IP range -GitLab.com, CI/CD, and related services are deployed into Google Cloud Platform (GCP). Any -IP based firewall can be configured by looking up all -[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#where_can_i_find_product_name_short_ip_ranges). +GitLab.com, CI/CD, and related services are deployed into Google Cloud Platform (GCP). Any +IP based firewall can be configured by looking up all +[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#where_can_i_find_product_name_short_ip_ranges). [Static endpoints](https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/5071) are being considered. @@ -260,7 +260,7 @@ The list of GitLab.com specific settings (and their defaults) is as follows: | hot_standby_feedback | on | off | | log_autovacuum_min_duration | 0 | -1 | | log_checkpoints | on | off | -| log_line_prefix | `%t [%p]: [%l-1] ` | empty | +| log_line_prefix | `%t [%p]: [%l-1]` | empty | | log_min_duration_statement | 1000 | -1 | | log_temp_files | 0 | -1 | | maintenance_work_mem | 2048MB | 16 MB | @@ -353,12 +353,10 @@ High Performance TCP/HTTP Load Balancer: [4010]: https://gitlab.com/gitlab-com/infrastructure/issues/4010 "Find a good value for maximum timeout for Shared Runners" [4070]: https://gitlab.com/gitlab-com/infrastructure/issues/4070 "Configure per-runner timeout for shared-runners-manager-X on GitLab.com" -## Other admin area settings +## Group and project settings -This area highlights other noteworthy admin area settings on GitLab.com that differ from default settings. This list is not exhaustive. +On GitLab.com, projects, groups, and snippets created +after July 2019 have the `Internal` visibility setting disabled. -NOTE: **Note:** -From July 2019, the `Internal` visibility setting is disabled for new projects, groups, -and snippets on GitLab.com. Existing projects, groups, and snippets using the `Internal` -visibility setting keep this setting. You can read more about the change in the +You can read more about the change in the [relevant issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/12388). diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md index 8d4ffd93f59..0dffc216f8e 100644 --- a/doc/user/group/clusters/index.md +++ b/doc/user/group/clusters/index.md @@ -42,7 +42,7 @@ to the group containing the project if the project's cluster is available and no In the case of sub-groups, GitLab will use the cluster of the closest ancestor group to the project, provided the cluster is not disabled. -## Multiple Kubernetes clusters **[PREMIUM]** +## Multiple Kubernetes clusters **(PREMIUM)** With GitLab Premium, you can associate more than one Kubernetes clusters to your group. That way you can have different clusters for different environments, @@ -82,7 +82,7 @@ the [Auto DevOps](../../../topics/autodevops/index.md) stages. The domain should have a wildcard DNS configured to the Ingress IP address. -## Environment scopes **[PREMIUM]** +## Environment scopes **(PREMIUM)** When adding more than one Kubernetes cluster to your project, you need to differentiate them with an environment scope. The environment scope associates clusters with diff --git a/doc/user/group/contribution_analytics/index.md b/doc/user/group/contribution_analytics/index.md index a555b7723df..2d37fc375db 100644 --- a/doc/user/group/contribution_analytics/index.md +++ b/doc/user/group/contribution_analytics/index.md @@ -2,7 +2,7 @@ type: reference --- -# Contribution Analytics **[STARTER]** +# Contribution Analytics **(STARTER)** > Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3. diff --git a/doc/user/group/custom_project_templates.md b/doc/user/group/custom_project_templates.md index aa088d2fcdb..7cdba8cf2b5 100644 --- a/doc/user/group/custom_project_templates.md +++ b/doc/user/group/custom_project_templates.md @@ -2,9 +2,9 @@ type: reference --- -# Custom group-level project templates **[PREMIUM]** +# Custom group-level project templates **(PREMIUM)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6861) in [GitLab Premium](https://about.gitlab.com/pricing) 11.6. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6861) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.6. When you create a new [project](../project/index.md), creating it based on custom project templates is a convenient bootstrap option. @@ -24,7 +24,7 @@ project in the group will be available to every logged in user. However, private projects will be available only if the user is a member of the project. NOTE: **Note:** -Projects of nested subgroups of a selected template source cannot be used. +Only direct subgroups can be set as the template source. Projects of nested subgroups of a selected template source cannot be used. Repository and database information that are copied over to each new project are identical to the data exported with [GitLab's Project Import/Export](../project/settings/import_export.md). diff --git a/doc/user/group/dependency_proxy/index.md b/doc/user/group/dependency_proxy/index.md index 4fc2d8e9509..771468fbba8 100644 --- a/doc/user/group/dependency_proxy/index.md +++ b/doc/user/group/dependency_proxy/index.md @@ -1,4 +1,4 @@ -# Dependency Proxy **[PREMIUM]** +# Dependency Proxy **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11. diff --git a/doc/user/group/epics/index.md b/doc/user/group/epics/index.md index f53c1dd95d7..601ffd4947b 100644 --- a/doc/user/group/epics/index.md +++ b/doc/user/group/epics/index.md @@ -2,7 +2,7 @@ type: reference, howto --- -# Epics **[ULTIMATE]** +# Epics **(ULTIMATE)** > Introduced in [GitLab Ultimate][ee] 10.2. diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 7240b8e118b..db348c678eb 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -200,7 +200,7 @@ Alternatively, you can [lock the sharing with group feature](#share-with-group-l In GitLab Enterprise Edition, it is possible to manage GitLab group memberships using LDAP groups. See [the GitLab Enterprise Edition documentation](../../integration/ldap.md) for more information. -## Epics **[ULTIMATE]** +## Epics **(ULTIMATE)** > Introduced in [GitLab Ultimate][ee] 10.2. @@ -210,13 +210,13 @@ milestones. [Learn more about Epics.](epics/index.md) -## Group Security Dashboard **[ULTIMATE]** +## Group Security Dashboard **(ULTIMATE)** Get an overview of the vulnerabilities of all the projects in a group and its subgroups. [Learn more about the Group Security Dashboard.](security_dashboard/index.md) -## Insights **[ULTIMATE]** +## Insights **(ULTIMATE)** > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/725) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0. @@ -307,7 +307,7 @@ To enable this feature, navigate to the group settings page. Select ![Checkbox for share with group lock](img/share_with_group_lock.png) -#### Member Lock **[STARTER]** +#### Member Lock **(STARTER)** Member lock lets a group owner prevent any new project membership to all of the projects within a group, allowing tighter control over project membership. @@ -327,7 +327,7 @@ This will disable the option for all users who previously had permissions to operate project memberships, so no new users can be added. Furthermore, any request to add a new user to a project through API will not be possible. -#### IP access restriction **[ULTIMATE]** +#### IP access restriction **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1985) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0. @@ -346,7 +346,7 @@ Restriction currently applies to UI, API access is not restricted. To avoid accidental lock-out, admins and group owners are are able to access the group regardless of the IP restriction. -#### Group file templates **[PREMIUM]** +#### Group file templates **(PREMIUM)** Group file templates allow you to share a set of templates for common file types with every project in a group. It is analogous to the @@ -370,7 +370,7 @@ To enable this feature, navigate to the group settings page, expand the ![Group file template settings](img/group_file_template_settings.png) -#### Group-level project templates **[PREMIUM]** +#### Group-level project templates **(PREMIUM)** Define project templates at a group level by setting a group as the template source. [Learn more about group-level project templates](custom_project_templates.md). @@ -382,10 +382,10 @@ Define project templates at a group level by setting a group as the template sou - **Webhooks**: Configure [webhooks](../project/integrations/webhooks.md) for your group. - **Kubernetes cluster integration**: Connect your GitLab group with [Kubernetes clusters](clusters/index.md). - **Audit Events**: View [Audit Events](../../administration/audit_events.md) - for the group. **[STARTER ONLY]** + for the group. **(STARTER ONLY)** - **Pipelines quota**: Keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group. -#### Storage usage quota **[STARTER]** +#### Storage usage quota **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/13294) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.0. @@ -393,17 +393,17 @@ A group owner can check the aggregated storage usage for all the project in a gr ![Group storage usage quota](img/group_storage_usage_quota.png) -## User contribution analysis **[STARTER]** +## User contribution analysis **(STARTER)** With [GitLab Contribution Analytics](contribution_analytics/index.md), you have an overview of the contributions (pushes, merge requests, and issues) performed by your group members. -## Issues analytics **[PREMIUM]** +## Issues analytics **(PREMIUM)** With [GitLab Issues Analytics](issues_analytics/index.md), you can see a bar chart of the number of issues created each month in your groups. -## Dependency Proxy **[PREMIUM]** +## Dependency Proxy **(PREMIUM)** Use GitLab as a [dependency proxy](dependency_proxy/index.md) for upstream Docker images. diff --git a/doc/user/group/insights/index.md b/doc/user/group/insights/index.md index e6ba47939b3..f0e7f7239c1 100644 --- a/doc/user/group/insights/index.md +++ b/doc/user/group/insights/index.md @@ -2,7 +2,7 @@ type: reference, howto --- -# Insights **[ULTIMATE]** +# Insights **(ULTIMATE)** > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/725) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0. diff --git a/doc/user/group/issues_analytics/index.md b/doc/user/group/issues_analytics/index.md index 46d5c1e2e09..dc4b057789f 100644 --- a/doc/user/group/issues_analytics/index.md +++ b/doc/user/group/issues_analytics/index.md @@ -2,7 +2,7 @@ type: reference --- -# Issues Analytics **[PREMIUM]** +# Issues Analytics **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7478) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.5. diff --git a/doc/user/group/roadmap/index.md b/doc/user/group/roadmap/index.md index 683c715c8d5..a72cd990706 100644 --- a/doc/user/group/roadmap/index.md +++ b/doc/user/group/roadmap/index.md @@ -2,9 +2,9 @@ type: reference --- -# Roadmap **[ULTIMATE]** +# Roadmap **(ULTIMATE)** -> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing) 10.5. +> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.5. An Epic within a group containing **Start date** and/or **Due date** can be visualized in a form of a timeline (e.g. a Gantt chart). The Epics Roadmap page @@ -30,7 +30,7 @@ Roadmaps can also be [visualized inside an epic](../epics/index.md#roadmap-in-ep ## Timeline duration -> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing) 11.0. +> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.0. Roadmap supports the following date ranges: diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md index 26893f7e31e..54923ab69ff 100644 --- a/doc/user/group/saml_sso/index.md +++ b/doc/user/group/saml_sso/index.md @@ -2,7 +2,7 @@ type: reference, howto --- -# SAML SSO for GitLab.com Groups **[SILVER ONLY]** +# SAML SSO for GitLab.com Groups **(SILVER ONLY)** > Introduced in [GitLab.com Silver](https://about.gitlab.com/pricing/) 11.0. diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index 5aef463d782..2d408766db8 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -2,7 +2,7 @@ type: howto, reference --- -# SCIM provisioning using SAML SSO for Groups **[SILVER ONLY]** +# SCIM provisioning using SAML SSO for Groups **(SILVER ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9388) in [GitLab.com Silver](https://about.gitlab.com/pricing/) 11.10. diff --git a/doc/user/index.md b/doc/user/index.md index 899026a801f..501d74c76d1 100644 --- a/doc/user/index.md +++ b/doc/user/index.md @@ -173,7 +173,7 @@ Learn what is [Git](../topics/git/index.md) and its best practices. See [various statistics](instance_statistics/index.md) of your GitLab instance. -## Operations Dashboard **[PREMIUM]** +## Operations Dashboard **(PREMIUM)** See [Operations Dashboard](operations_dashboard/index.md) for a summary of each project's operational health. diff --git a/doc/user/markdown.md b/doc/user/markdown.md index eaae9964367..0d3bbeff4e5 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -25,7 +25,7 @@ You can use GFM in the following areas: - Snippets (the snippet must be named with a `.md` extension) - Wiki pages - Markdown documents inside repositories -- Epics **[ULTIMATE]** +- Epics **(ULTIMATE)** You can also use other rich text files in GitLab. You might have to install a dependency to do so. Please see the [`gitlab-markup` gem project](https://gitlab.com/gitlab-org/gitlab-markup) @@ -40,7 +40,7 @@ repositories are also processed with CommonMark. As of 11.8, the [Redcarpet Ruby has been removed and all issues and comments, including those from pre-11.1, are now processed using the [CommonMark Ruby Library](https://github.com/gjtorikian/commonmarker). -The documentation website had its [markdown engine migrated from Redcarpet to Kramdown](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/108) +The documentation website had its [markdown engine migrated from Redcarpet to Kramdown](https://gitlab.com/gitlab-org/gitlab-docs/merge_requests/108) in October 2018. You may have older issues, merge requests, or Markdown documents in your @@ -368,7 +368,7 @@ GFM will recognize the following: | issue | ``#123`` | `namespace/project#123` | `project#123` | | merge request | `!123` | `namespace/project!123` | `project!123` | | snippet | `$123` | `namespace/project$123` | `project$123` | -| epic **[ULTIMATE]** | `&123` | `group1/subgroup&123` | | +| epic **(ULTIMATE)** | `&123` | `group1/subgroup&123` | | | label by ID | `~123` | `namespace/project~123` | `project~123` | | one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` | | multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` | diff --git a/doc/user/operations_dashboard/index.md b/doc/user/operations_dashboard/index.md index 54bf3ff8a40..8c4d387190a 100644 --- a/doc/user/operations_dashboard/index.md +++ b/doc/user/operations_dashboard/index.md @@ -1,4 +1,4 @@ -# Operations Dashboard **[PREMIUM]** +# Operations Dashboard **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5781) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.5. [Moved](https://gitlab.com/gitlab-org/gitlab-ee/issues/9218) to [GitLab Premium](https://about.gitlab.com/pricing/) in 11.10. diff --git a/doc/user/permissions.md b/doc/user/permissions.md index aac548020be..d496bc39f9a 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -43,10 +43,10 @@ The following table depicts the various user permission levels in a project. |---------------------------------------------------|---------|------------|-------------|----------|--------| | Download project | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | | Leave comments | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | -| View Insights charts **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ | -| View approved/blacklisted licenses **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ | -| View license management reports **[ULTIMATE]** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | -| View Security reports **[ULTIMATE]** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | +| View Insights charts **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ | +| View approved/blacklisted licenses **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ | +| View license management reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | +| View Security reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | | View project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | | Pull project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | | View GitLab Pages protected by [access control](project/pages/introduction.md#gitlab-pages-access-control-core-only) | ✓ | ✓ | ✓ | ✓ | ✓ | @@ -62,8 +62,8 @@ The following table depicts the various user permission levels in a project. | Label issues | | ✓ | ✓ | ✓ | ✓ | | Lock issue discussions | | ✓ | ✓ | ✓ | ✓ | | Manage issue tracker | | ✓ | ✓ | ✓ | ✓ | -| Manage related issues **[STARTER]** | | ✓ | ✓ | ✓ | ✓ | -| Create issue from vulnerability **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ | +| Manage related issues **(STARTER)** | | ✓ | ✓ | ✓ | ✓ | +| Create issue from vulnerability **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ | | Manage labels | | ✓ | ✓ | ✓ | ✓ | | Create code snippets | | ✓ | ✓ | ✓ | ✓ | | See a commit status | | ✓ | ✓ | ✓ | ✓ | @@ -72,8 +72,8 @@ The following table depicts the various user permission levels in a project. | See a list of merge requests | | ✓ | ✓ | ✓ | ✓ | | View project statistics | | ✓ | ✓ | ✓ | ✓ | | View Error Tracking list | | ✓ | ✓ | ✓ | ✓ | -| Pull from [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **[PREMIUM]** | | ✓ | ✓ | ✓ | ✓ | -| Publish to [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **[PREMIUM]** | | | ✓ | ✓ | ✓ || +| Pull from [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ | +| Publish to [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **(PREMIUM)** | | | ✓ | ✓ | ✓ || | Create new branches | | | ✓ | ✓ | ✓ | | Push to non-protected branches | | | ✓ | ✓ | ✓ | | Force push to non-protected branches | | | ✓ | ✓ | ✓ | @@ -91,13 +91,13 @@ The following table depicts the various user permission levels in a project. | Update a container registry | | | ✓ | ✓ | ✓ | | Remove a container registry image | | | ✓ | ✓ | ✓ | | Create/edit/delete project milestones | | | ✓ | ✓ | ✓ | -| Use security dashboard **[ULTIMATE]** | | | ✓ | ✓ | ✓ | -| Dismiss vulnerability **[ULTIMATE]** | | | ✓ | ✓ | ✓ | +| Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ | +| Dismiss vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ | | Apply code change suggestions | | | ✓ | ✓ | ✓ | | Create and edit wiki pages | | | ✓ | ✓ | ✓ | | Rewrite/remove Git tags | | | ✓ | ✓ | ✓ | | Use environment terminals | | | | ✓ | ✓ | -| Run Web IDE's Interactive Web Terminals **[ULTIMATE ONLY]** | | | | ✓ | ✓ | +| Run Web IDE's Interactive Web Terminals **(ULTIMATE ONLY)** | | | | ✓ | ✓ | | Add new team members | | | | ✓ | ✓ | | Enable/disable branch protection | | | | ✓ | ✓ | | Push to protected branches | | | | ✓ | ✓ | @@ -113,7 +113,7 @@ The following table depicts the various user permission levels in a project. | Manage GitLab Pages domains and certificates | | | | ✓ | ✓ | | Remove GitLab Pages | | | | ✓ | ✓ | | Manage clusters | | | | ✓ | ✓ | -| Manage license policy **[ULTIMATE]** | | | | ✓ | ✓ | +| Manage license policy **(ULTIMATE)** | | | | ✓ | ✓ | | Edit comments (posted by any user) | | | | ✓ | ✓ | | Manage Error Tracking | | | | ✓ | ✓ | | Delete wiki pages | | | | ✓ | ✓ | @@ -167,7 +167,7 @@ and drag issues around. Read though the [documentation on Issue Boards permissions](project/issue_board.md#permissions) to learn more. -### File Locking permissions **[PREMIUM]** +### File Locking permissions **(PREMIUM)** The user that locks a file or directory is the only one that can edit and push their changes back to the repository where the locked objects are located. @@ -202,9 +202,9 @@ group. | Action | Guest | Reporter | Developer | Maintainer | Owner | |-------------------------------------------------|-------|----------|-----------|------------|-------| | Browse group | ✓ | ✓ | ✓ | ✓ | ✓ | -| View Insights charts **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ | -| View group epic **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ | -| Create/edit group epic **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ | +| View Insights charts **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ | +| View group epic **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ | +| Create/edit group epic **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ | | Manage group labels | | ✓ | ✓ | ✓ | ✓ | | Create project in group | | | ✓ | ✓ | ✓ | | Create/edit/delete group milestones | | | ✓ | ✓ | ✓ | @@ -213,7 +213,7 @@ group. | Edit group | | | | | ✓ | | Manage group members | | | | | ✓ | | Remove group | | | | | ✓ | -| Delete group epic **[ULTIMATE]** | | | | | ✓ | +| Delete group epic **(ULTIMATE)** | | | | | ✓ | | View group Audit Events | | | | | ✓ | - (1): Groups can be set to allow either Owners or Owners and Maintainers to create subgroups @@ -266,7 +266,7 @@ Here are some examples: Please be aware that this regex could lead to a DOS attack, [see](https://en.wikipedia.org/wiki/ReDoS?) ReDos on Wikipedia. -## Auditor users **[PREMIUM ONLY]** +## Auditor users **(PREMIUM ONLY)** >[Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998) in [GitLab Premium](https://about.gitlab.com/pricing/) 8.17. diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md index b61216b7b67..b1fde3b577b 100644 --- a/doc/user/profile/preferences.md +++ b/doc/user/profile/preferences.md @@ -79,7 +79,7 @@ You have 8 options here that you can use for your default dashboard view: - Your [Todos](../../workflow/todos.md) - Assigned Issues - Assigned Merge Requests -- Operations Dashboard **[PREMIUM]** +- Operations Dashboard **(PREMIUM)** ### Project overview content diff --git a/doc/user/project/canary_deployments.md b/doc/user/project/canary_deployments.md index 9bb282f1b78..5068d2757be 100644 --- a/doc/user/project/canary_deployments.md +++ b/doc/user/project/canary_deployments.md @@ -1,4 +1,4 @@ -# Canary Deployments **[PREMIUM]** +# Canary Deployments **(PREMIUM)** > [Introduced][ee-1659] in [GitLab Premium][eep] 9.1. diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index d21455fb5ca..56f8257fbe7 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -5,6 +5,9 @@ Connect your project to Google Kubernetes Engine (GKE) or an existing Kubernetes cluster in a few steps. +NOTE: **Scalable app deployment with GitLab and Google Cloud Platform** +[Watch the webcast](https://about.gitlab.com/webcast/scalable-app-deploy/) and learn how to spin up a Kubernetes cluster managed by Google Cloud Platform (GCP) in a few clicks. + ## Overview With one or more Kubernetes clusters associated to your project, you can use @@ -432,7 +435,7 @@ record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) such as `*.example.co in order to be able to reach your apps. If your external endpoint is an IP address, use an A record. If your external endpoint is a hostname, use a CNAME record. -## Multiple Kubernetes clusters **[PREMIUM]** +## Multiple Kubernetes clusters **(PREMIUM)** > Introduced in [GitLab Premium][ee] 10.3. @@ -444,7 +447,7 @@ Simply add another cluster, like you did the first time, and make sure to [set an environment scope](#setting-the-environment-scope-premium) that will differentiate the new cluster with the rest. -## Setting the environment scope **[PREMIUM]** +## Setting the environment scope **(PREMIUM)** When adding more than one Kubernetes cluster to your project, you need to differentiate them with an environment scope. The environment scope associates clusters with [environments](../../../ci/environments.md) similar to how the @@ -547,7 +550,7 @@ in a way that causes this error. Ensure you deselect the [GitLab-managed cluster](#gitlab-managed-clusters) option if you want to manage namespaces and service accounts yourself. -## Monitoring your Kubernetes cluster **[ULTIMATE]** +## Monitoring your Kubernetes cluster **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4701) in [GitLab Ultimate][ee] 10.6. @@ -585,7 +588,7 @@ and add a Kubernetes cluster again. Here's what you can do with GitLab if you enable the Kubernetes integration. -### Deploy Boards **[PREMIUM]** +### Deploy Boards **(PREMIUM)** GitLab's Deploy Boards offer a consolidated view of the current health and status of each CI [environment](../../../ci/environments.md) running on Kubernetes, @@ -595,7 +598,7 @@ workflow they already use without any need to access Kubernetes. [Read more about Deploy Boards](../deploy_boards.md) -### Canary Deployments **[PREMIUM]** +### Canary Deployments **(PREMIUM)** Leverage [Kubernetes' Canary deployments](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments) and visualize your canary deployments right inside the Deploy Board, without @@ -603,7 +606,7 @@ the need to leave GitLab. [Read more about Canary Deployments](../canary_deployments.md) -### Pod logs **[ULTIMATE]** +### Pod logs **(ULTIMATE)** GitLab makes it easy to view the logs of running pods in connected Kubernetes clusters. By displaying the logs directly in GitLab, developers can avoid having to manage console tools or jump to a different interface. diff --git a/doc/user/project/clusters/kubernetes_pod_logs.md b/doc/user/project/clusters/kubernetes_pod_logs.md index 25d8abebf07..864cd75823c 100644 --- a/doc/user/project/clusters/kubernetes_pod_logs.md +++ b/doc/user/project/clusters/kubernetes_pod_logs.md @@ -1,4 +1,4 @@ -# Kubernetes Pod Logs **[ULTIMATE]** +# Kubernetes Pod Logs **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4752) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.0. diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md index a06c3d3c662..a8473f76733 100644 --- a/doc/user/project/clusters/serverless/index.md +++ b/doc/user/project/clusters/serverless/index.md @@ -102,12 +102,15 @@ You must do the following: 1. Ensure GitLab can manage Knative: - For a non-GitLab managed cluster, ensure that the service account for the token provided can manage resources in the `serving.knative.dev` API group. - - For a GitLab managed cluster, - GitLab uses a service account with the `edit` cluster role. This account needs - the ability to manage resources in the `serving.knative.dev` API group. - We suggest you do this with an [aggregated ClusterRole](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles) - adding rules to the default `edit` cluster role: - First, save the following YAML as `knative-serving-only-role.yaml`: + - For a GitLab managed cluster, if you added the cluster in [GitLab 12.1 or later](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/30235), + then GitLab will already have the required access and you can proceed to the next step. + + Otherwise, you need to manually grant GitLab's service account the ability to manage + resources in the `serving.knative.dev` API group. Since every GitLab service account + has the `edit` cluster role, the simplest way to do this is with an + [aggregated ClusterRole](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles) + adding rules to the default `edit` cluster role: First, save the following YAML as + `knative-serving-only-role.yaml`: ```yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -143,6 +146,9 @@ You must do the following: kubectl apply -f knative-serving-only-role.yaml ``` + If you would rather grant permissions on a per service account basis, you can do this + using a `Role` and `RoleBinding` specific to the service account and namespace. + 1. Follow the steps to deploy [functions](#deploying-functions) or [serverless applications](#deploying-serverless-applications) onto your cluster. @@ -376,13 +382,13 @@ cluster. By default, a GitLab serverless deployment will be served over `http`. In order to serve over `https` you must manually obtain and install TLS certificates. -The simplest way to accomplish this is to +The simplest way to accomplish this is to use [Certbot to manually obtain Let's Encrypt certificates](https://knative.dev/docs/serving/using-a-tls-cert/#using-certbot-to-manually-obtain-let-s-encrypt-certificates). Certbot is a free, open source software tool for automatically using Let’s Encrypt certificates on manually-administrated websites to enable HTTPS. NOTE: **Note:** The instructions below relate to installing and running Certbot on a Linux server and may not work on other operating systems. -1. Install Certbot by running the +1. Install Certbot by running the [`certbot-auto` wrapper script](https://certbot.eff.org/docs/install.html#certbot-auto). On the command line of your server, run the following commands: @@ -594,7 +600,7 @@ The instructions below relate to installing and running Certbot on a Linux serve Where `cert.pem` and `cert.pk` are your certificate and private key files. Note that the `istio-ingressgateway-certs` secret name is required. 1. Configure Knative to use the new secret that you created for HTTPS - connections. Run the + connections. Run the following command to open the Knative shared `gateway` in edit mode: ```sh @@ -641,4 +647,4 @@ The instructions below relate to installing and running Certbot on a Linux serve After your changes are running on your Knative cluster, you can begin using the HTTPS protocol for secure access your deployed Knative services. In the event a mistake is made during this process and you need to update the cert, you will need to edit the gateway `knative-ingress-gateway` - to switch back to `PASSTHROUGH` mode. Once corrections are made, edit the file again so the gateway will use the new certificates.
\ No newline at end of file + to switch back to `PASSTHROUGH` mode. Once corrections are made, edit the file again so the gateway will use the new certificates. diff --git a/doc/user/project/code_owners.md b/doc/user/project/code_owners.md index c76847616b3..96c4f16fe04 100644 --- a/doc/user/project/code_owners.md +++ b/doc/user/project/code_owners.md @@ -1,4 +1,4 @@ -# Code Owners **[STARTER]** +# Code Owners **(STARTER)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6916) in [GitLab Starter](https://about.gitlab.com/pricing/) 11.3. @@ -57,8 +57,8 @@ Example `CODEOWNERS` file: # Files with a `#` can still be accesssed by escaping the pound sign \#file_with_pound.rb @owner-file-with-pound -# Multiple codeowners can be specified, separated by whitespace -CODEOWNERS @multiple @owners @tab-separated +# Multiple codeowners can be specified, separated by spaces or tabs +CODEOWNERS @multiple @code @owners # Both usernames or email addresses can be used to match # users. Everything else will be ignored. For example this will diff --git a/doc/user/project/deploy_boards.md b/doc/user/project/deploy_boards.md index 0d51e8ae19a..cb1faa771bc 100644 --- a/doc/user/project/deploy_boards.md +++ b/doc/user/project/deploy_boards.md @@ -1,4 +1,4 @@ -# Deploy Boards **[PREMIUM]** +# Deploy Boards **(PREMIUM)** > [Introduced][ee-1589] in [GitLab Premium][ee] 9.0. diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md index 5e11e7c0203..72594733cd3 100644 --- a/doc/user/project/deploy_tokens/index.md +++ b/doc/user/project/deploy_tokens/index.md @@ -41,7 +41,7 @@ the following table. ## Deploy token custom username -> [Introduced][https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/29639] in GitLab 12.1. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/29639) in GitLab 12.1. The default username format is `gitlab+deploy-token-#{n}`. Some tools or platforms may not support this format, in such case you can specify custom username to be used when creating the deploy token. diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md index 7520237251a..196874fdc86 100644 --- a/doc/user/project/description_templates.md +++ b/doc/user/project/description_templates.md @@ -55,7 +55,7 @@ changes you made after picking the template and return it to its initial status. ![Description templates](img/description_templates.png) -## Setting a default template for issues and merge requests **[STARTER]** +## Setting a default template for issues and merge requests **(STARTER)** > **Notes:** > diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md index 40603790c12..dec679fc975 100644 --- a/doc/user/project/file_lock.md +++ b/doc/user/project/file_lock.md @@ -1,4 +1,4 @@ -# File Locking **[PREMIUM]** +# File Locking **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/440) in [GitLab Premium](https://about.gitlab.com/pricing/) 8.9. diff --git a/doc/user/project/import/gemnasium.md b/doc/user/project/import/gemnasium.md index 3b071ff590f..0afa32e4133 100644 --- a/doc/user/project/import/gemnasium.md +++ b/doc/user/project/import/gemnasium.md @@ -1,4 +1,4 @@ -# Gemnasium **[ULTIMATE]** +# Gemnasium **(ULTIMATE)** This guide describes how to migrate from Gemnasium.com to your own GitLab instance or GitLab.com. diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md index e194d57e2e0..cdb7f837158 100644 --- a/doc/user/project/import/github.md +++ b/doc/user/project/import/github.md @@ -121,10 +121,10 @@ Depending your GitLab tier, [project mirroring](../../../workflow/repository_mir your imported project in sync with its GitHub copy. Additionally, you can configure GitLab to send pipeline status updates back GitHub with the -[GitHub Project Integration](../integrations/github.md). **[PREMIUM]** +[GitHub Project Integration](../integrations/github.md). **(PREMIUM)** If you import your project using [CI/CD for external repo](../../../ci/ci_cd_for_external_repos/index.md), then both -of the above are automatically configured. **[PREMIUM]** +of the above are automatically configured. **(PREMIUM)** ## Improving the speed of imports on self-hosted instances diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md index 2b6927bd780..334be713aa5 100644 --- a/doc/user/project/import/index.md +++ b/doc/user/project/import/index.md @@ -20,7 +20,7 @@ In addition to the specific migration documentation above, you can import any Git repository via HTTP from the New Project page. Be aware that if the repository is too large the import can timeout. -There is also the option of [connecting your external repository to get CI/CD benefits](../../../ci/ci_cd_for_external_repos/index.md). **[PREMIUM]** +There is also the option of [connecting your external repository to get CI/CD benefits](../../../ci/ci_cd_for_external_repos/index.md). **(PREMIUM)** ## Migrating from self-hosted GitLab to GitLab.com diff --git a/doc/user/project/import/phabricator.md b/doc/user/project/import/phabricator.md index 5c624e3aff6..b8f89caba24 100644 --- a/doc/user/project/import/phabricator.md +++ b/doc/user/project/import/phabricator.md @@ -15,6 +15,12 @@ Currently, only the following basic fields are imported: - Created at - Closed at +## Users + +The assignee and author of a user are deducted from a Task's owner and +author: If a user with the same username has access to the namespace +of the project being imported into, then the user will be linked. + ## Enabling this feature While this feature is incomplete, a feature flag is required to enable it so that diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md index 4825b005a85..7359487e1bf 100644 --- a/doc/user/project/import/svn.md +++ b/doc/user/project/import/svn.md @@ -29,7 +29,7 @@ directly in a filesystem level. 1. Install Oracle JRE 1.8 or newer. On Debian-based Linux distributions you can follow [this article](http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html). -1. Download SubGit from <https://subgit.com/download/>. +1. Download SubGit from <https://subgit.com/download>. 1. Unpack the downloaded SubGit zip archive to the `/opt` directory. The `subgit` command will be available at `/opt/subgit-VERSION/bin/subgit`. diff --git a/doc/user/project/import/tfs.md b/doc/user/project/import/tfs.md index 8727c2ff6c3..b4597a4da60 100644 --- a/doc/user/project/import/tfs.md +++ b/doc/user/project/import/tfs.md @@ -1,6 +1,6 @@ # Migrating from TFS -[TFS](https://www.visualstudio.com/tfs/) is a set of tools developed by Microsoft +[TFS](https://visualstudio.microsoft.com/tfs/) is a set of tools developed by Microsoft which also includes a centralized version control system (TFVC) similar to Git. In this document, we emphasize on the TFVC to Git migration. @@ -18,10 +18,10 @@ The following list illustrates the main differences between TFVC and Git: a committed file(s) is stored in its entirety (snapshot). That means that's very easy in Git to revert or undo a whole change. -_Check also Microsoft's documentation on the -[comparison of Git and TFVC](https://www.visualstudio.com/en-us/docs/tfvc/comparison-git-tfvc) -and the Wikipedia article on -[comparing the different version control software](https://en.wikipedia.org/wiki/Comparison_of_version_control_software)._ +Check also Microsoft's documentation on the +[comparison of Git and TFVC](https://docs.microsoft.com/en-us/azure/devops/repos/tfvc/comparison-git-tfvc?view=azure-devops) +and the Wikipedia +[comparison of version control software](https://en.wikipedia.org/wiki/Comparison_of_version_control_software). ## Why migrate diff --git a/doc/user/project/index.md b/doc/user/project/index.md index 06286951e20..f332281fa82 100644 --- a/doc/user/project/index.md +++ b/doc/user/project/index.md @@ -17,7 +17,7 @@ When you create a project in GitLab, you'll have access to a large number of - [Issue tracker](issues/index.md): Discuss implementations with your team within issues - [Issue Boards](issue_board.md): Organize and prioritize your workflow - - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **[STARTER]** + - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **(STARTER)** - [Repositories](repository/index.md): Host your code in a fully integrated platform - [Branches](repository/branches/index.md): use Git branching strategies to @@ -34,11 +34,11 @@ When you create a project in GitLab, you'll have access to a large number of - [Issue tracker](issues/index.md): Discuss implementations with your team within issues - [Issue Boards](issue_board.md): Organize and prioritize your workflow - - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **[STARTER]** + - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **(STARTER)** - [Merge Requests](merge_requests/index.md): Apply your branching strategy and get reviewed by your team - [Merge Request Approvals](merge_requests/merge_request_approvals.md): Ask for approval before - implementing a change **[STARTER]** + implementing a change **(STARTER)** - [Fix merge conflicts from the UI](merge_requests/resolve_conflicts.md): Your Git diff tool right from GitLab's UI - [Review Apps](../../ci/review_apps/index.md): Live preview the results @@ -75,7 +75,7 @@ When you create a project in GitLab, you'll have access to a large number of - [Kubernetes cluster integration](clusters/index.md): Connecting your GitLab project with a Kubernetes cluster - [Feature Flags](operations/feature_flags.md): Feature flags allow you to ship a project in - different flavors by dynamically toggling certain functionality **[PREMIUM]** + different flavors by dynamically toggling certain functionality **(PREMIUM)** - [GitLab Pages](pages/index.md): Build, test, and deploy your static website with GitLab Pages @@ -84,18 +84,18 @@ When you create a project in GitLab, you'll have access to a large number of - [Wiki](wiki/index.md): document your GitLab project in an integrated Wiki. - [Snippets](../snippets.md): store, share and collaborate on code snippets. - [Cycle Analytics](cycle_analytics.md): review your development lifecycle. -- [Insights](insights/index.md): configure the Insights that matter for your projects. **[ULTIMATE]** -- [Security Dashboard](security_dashboard.md): Security Dashboard. **[ULTIMATE]** +- [Insights](insights/index.md): configure the Insights that matter for your projects. **(ULTIMATE)** +- [Security Dashboard](security_dashboard.md): Security Dashboard. **(ULTIMATE)** - [Syntax highlighting](highlighting.md): an alternative to customize your code blocks, overriding GitLab's default choice of language. - [Badges](badges.md): badges for the project overview. - [Releases](releases/index.md): a way to track deliverables in your project as snapshot in time of the source, build output, and other metadata or artifacts associated with a released version of your code. -- [Maven packages](packages/maven_repository.md): your private Maven repository in GitLab. **[PREMIUM]** -- [NPM packages](packages/npm_registry.md): your private NPM package registry in GitLab. **[PREMIUM]** -- [Code owners](code_owners.md): specify code owners for certain files **[STARTER]** -- [License Management](../application_security/license_management/index.md): approve and blacklist licenses for projects. **[ULTIMATE]** +- [Maven packages](packages/maven_repository.md): your private Maven repository in GitLab. **(PREMIUM)** +- [NPM packages](packages/npm_registry.md): your private NPM package registry in GitLab. **(PREMIUM)** +- [Code owners](code_owners.md): specify code owners for certain files **(STARTER)** +- [License Management](../application_security/license_management/index.md): approve and blacklist licenses for projects. **(ULTIMATE)** ### Project integrations @@ -131,7 +131,7 @@ Read through the documentation on [project settings](settings/index.md). - [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data) - [Importing and exporting projects between GitLab instances](settings/import_export.md) -## CI/CD for external repositories **[PREMIUM]** +## CI/CD for external repositories **(PREMIUM)** Instead of importing a repository directly to GitLab, you can connect your repository as a CI/CD project. @@ -193,7 +193,7 @@ password <personal_access_token> To quickly access a project from the GitLab UI using the project ID, visit the `/projects/:id` URL in your browser or other tool accessing the project. -## Project aliases **[PREMIUM ONLY]** +## Project aliases **(PREMIUM ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3264) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.1. diff --git a/doc/user/project/insights/index.md b/doc/user/project/insights/index.md index 1c6ad0b8b2b..76a6a96eec5 100644 --- a/doc/user/project/insights/index.md +++ b/doc/user/project/insights/index.md @@ -1,4 +1,4 @@ -# Insights **[ULTIMATE]** +# Insights **(ULTIMATE)** > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/725) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0. diff --git a/doc/user/project/integrations/github.md b/doc/user/project/integrations/github.md index 680fcdb78bb..d0399f9193b 100644 --- a/doc/user/project/integrations/github.md +++ b/doc/user/project/integrations/github.md @@ -1,4 +1,4 @@ -# GitHub project integration **[PREMIUM]** +# GitHub project integration **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3836) in GitLab Premium 10.6. @@ -14,7 +14,7 @@ and is automatically configured on [GitHub import](../../../integration/github.m ### Complete these steps on GitHub -This integration requires a [GitHub API token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/) +This integration requires a [GitHub API token](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line) with `repo:status` access granted: 1. Go to your "Personal access tokens" page at <https://github.com/settings/tokens> diff --git a/doc/user/project/integrations/img/jira_api_token.png b/doc/user/project/integrations/img/jira_api_token.png Binary files differindex 4fa7a46854e..29689271bf7 100644 --- a/doc/user/project/integrations/img/jira_api_token.png +++ b/doc/user/project/integrations/img/jira_api_token.png diff --git a/doc/user/project/integrations/img/jira_api_token_menu.png b/doc/user/project/integrations/img/jira_api_token_menu.png Binary files differindex 14037bd0b47..1aca1d78f36 100644 --- a/doc/user/project/integrations/img/jira_api_token_menu.png +++ b/doc/user/project/integrations/img/jira_api_token_menu.png diff --git a/doc/user/project/integrations/img/jira_issue_reference.png b/doc/user/project/integrations/img/jira_issue_reference.png Binary files differindex 72c81460df7..a3e80c1b054 100644 --- a/doc/user/project/integrations/img/jira_issue_reference.png +++ b/doc/user/project/integrations/img/jira_issue_reference.png diff --git a/doc/user/project/integrations/img/jira_merge_request_close.png b/doc/user/project/integrations/img/jira_merge_request_close.png Binary files differindex 0f82ceba557..1c089c94207 100644 --- a/doc/user/project/integrations/img/jira_merge_request_close.png +++ b/doc/user/project/integrations/img/jira_merge_request_close.png diff --git a/doc/user/project/integrations/img/jira_service_close_comment.png b/doc/user/project/integrations/img/jira_service_close_comment.png Binary files differdeleted file mode 100644 index 9af0d38f098..00000000000 --- a/doc/user/project/integrations/img/jira_service_close_comment.png +++ /dev/null diff --git a/doc/user/project/integrations/img/jira_service_close_issue.png b/doc/user/project/integrations/img/jira_service_close_issue.png Binary files differindex c85b1d1dd97..73d6498192c 100644 --- a/doc/user/project/integrations/img/jira_service_close_issue.png +++ b/doc/user/project/integrations/img/jira_service_close_issue.png diff --git a/doc/user/project/integrations/img/jira_service_page.png b/doc/user/project/integrations/img/jira_service_page.png Binary files differindex 377b69d9d06..80dd65ea24e 100644 --- a/doc/user/project/integrations/img/jira_service_page.png +++ b/doc/user/project/integrations/img/jira_service_page.png diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md index 8f2e5a55b5f..ca990ee6c32 100644 --- a/doc/user/project/integrations/jira.md +++ b/doc/user/project/integrations/jira.md @@ -39,21 +39,17 @@ a GitLab project with any single Jira project. If you have one Jira instance, you can pre-fill the settings page with a default template. See the [Services Templates][services-templates] docs. -Configuration happens via user name and password. Connecting to a Jira Server -via CAS is not possible. - -In order to enable the Jira service in GitLab, you need to first configure the -project in Jira and then enter the correct values in GitLab. +In order to enable the Jira service in GitLab, you need to first configure the project in Jira and then enter the correct values in GitLab. ### Configuring Jira -When connecting to **Jira Server**, which supports basic authentication, a **username and password** are required. Check the link below and proceed to the next step: +#### Jira Server -- [Setting up a user in Jira Server](jira_server_configuration.md) +When connecting to **Jira Server**, which supports basic authentication, a **username and password** are required. Note that connecting to a Jira server via CAS is not possible. [Set up a user in Jira Server](jira_server_configuration.md) first and then proceed to [Configuring GitLab](#configuring-gitlab). -When connecting to **Jira Cloud**, which supports authentication via API token, an **email and API token**, are required. Check the link below and proceed to the next step: +#### Jira Cloud -- [Setting up a user in Jira Cloud](jira_cloud_configuration.md) +When connecting to **Jira Cloud**, which supports authentication via API token, an **email and API token**, are required. [Set up a user in Jira Cloud](jira_cloud_configuration.md) first and then proceed to [Configuring GitLab](#configuring-gitlab). ### Configuring GitLab @@ -68,7 +64,7 @@ When connecting to **Jira Cloud**, which supports authentication via API token, > to enable Basic Auth. The cookie being added to each request is `OBBasicAuth` with > a value of `fromDialog`. -To enable Jira integration in a project, navigate to the +To enable the Jira integration in a project, navigate to the [Integrations page](project_services.md#accessing-the-project-services), click the **Jira** service, and fill in the required details on the page as described in the table below. @@ -127,6 +123,12 @@ ENTITY_TITLE ![example of mentioning or closing the Jira issue](img/jira_issue_reference.png) +For example, the following commit will reference the Jira issue with `PROJECT-1` as its ID: + +```bash +git commit -m "PROJECT-1 Fix spelling and grammar" +``` + ### Closing Jira Issues Jira issues can be closed directly from GitLab by using trigger words in @@ -142,7 +144,7 @@ the same goal: - `Closes PROJECT-1` - `Fixes PROJECT-1` -where `PROJECT-1` is the issue ID of the Jira project. +where `PROJECT-1` is the ID of the Jira issue. > **Notes:** > @@ -174,8 +176,6 @@ with a link to the commit that resolved the issue. ![The GitLab integration closes Jira issue](img/jira_service_close_issue.png) -![The GitLab integration creates a comment and a link on Jira issue.](img/jira_service_close_comment.png) - ## Troubleshooting If these features do not work as expected, it is likely due to a problem with the way the integration settings were configured. diff --git a/doc/user/project/integrations/jira_cloud_configuration.md b/doc/user/project/integrations/jira_cloud_configuration.md index 614f05d5b7e..5a5ba2dd168 100644 --- a/doc/user/project/integrations/jira_cloud_configuration.md +++ b/doc/user/project/integrations/jira_cloud_configuration.md @@ -3,16 +3,18 @@ An API token is needed when integrating with Jira Cloud, follow the steps below to create one: -1. Log in to <https://id.atlassian.com> with your email. -1. **Click API tokens**, then **Create API token**. +1. Log in to <https://id.atlassian.com/manage/api-tokens> with your email address. + + NOTE: **Note** + It is important that the user associated with this email address has *write* access + to projects in Jira. + +2. Click **Create API token**. ![Jira API token](img/jira_api_token_menu.png) ![Jira API token](img/jira_api_token.png) -1. Make sure to write down your new API token as you will need it in the next [steps](jira.md#configuring-gitlab). - -NOTE: **Note** -It is important that the user associated with this email has 'write' access to projects in Jira. +1. Click **Copy to clipboard**, or click **View** and write down the new API token. It is required when [configuring GitLab](jira.md#configuring-gitlab). -The Jira configuration is complete. You are going to need this newly created token and the email you used to log in, when [configuring GitLab in the next section](jira.md#configuring-gitlab). +The Jira configuration is complete. You need the newly created token, and the associated email address, when [configuring GitLab](jira.md#configuring-gitlab) in the next section. diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md index 0e4c71a9d3e..f63a2c2758b 100644 --- a/doc/user/project/integrations/project_services.md +++ b/doc/user/project/integrations/project_services.md @@ -20,10 +20,10 @@ Below, you will find a list of the currently supported ones accompanied with com Click on the service links to see further configuration instructions and details. -| Service | Description | +| Service | Description | | ------- | ----------- | -| Asana | Asana - Teamwork without email | -| Assembla | Project Management Software (Source Commits Endpoint) | +| Asana | Asana - Teamwork without email | +| Assembla | Project Management Software (Source Commits Endpoint) | | [Atlassian Bamboo CI](bamboo.md) | A continuous integration and build server | | Buildkite | Continuous integration and deployments | | [Bugzilla](bugzilla.md) | Bugzilla issue tracker | @@ -34,12 +34,12 @@ Click on the service links to see further configuration instructions and details | [Emails on push](emails_on_push.md) | Email the commits and diff of each push to a list of recipients | | External Wiki | Replaces the link to the internal wiki with a link to an external wiki | | Flowdock | Flowdock is a collaboration web app for technical teams | -| [GitHub](github.md) **[PREMIUM]** | Sends pipeline notifications to GitHub | +| [GitHub](github.md) **(PREMIUM)** | Sends pipeline notifications to GitHub | | [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat | | [HipChat](hipchat.md) | Private group chat and IM | | [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway | | [Jira](jira.md) | Jira issue tracker | -| [Jenkins](../../../integration/jenkins.md) **[STARTER]** | An extendable open source continuous integration server | +| [Jenkins](../../../integration/jenkins.md) **(STARTER)** | An extendable open source continuous integration server | | JetBrains TeamCity CI | A continuous integration and build server | | [Mattermost slash commands](mattermost_slash_commands.md) | Mattermost chat and ChatOps slash commands | | [Mattermost Notifications](mattermost.md) | Receive event notifications in Mattermost | @@ -47,7 +47,7 @@ Click on the service links to see further configuration instructions and details | Packagist | Update your project on Packagist, the main Composer repository | | Pipelines emails | Email the pipeline status to a list of recipients | | [Slack Notifications](slack.md) | Send GitLab events (e.g. issue created) to Slack as notifications | -| [Slack slash commands](slack_slash_commands.md) **[CORE ONLY]** | Use slash commands in Slack to control GitLab | +| [Slack slash commands](slack_slash_commands.md) **(CORE ONLY)** | Use slash commands in Slack to control GitLab | | [GitLab Slack application](gitlab_slack_application.md) **[FREE ONLY]** | Use Slack's official application | | PivotalTracker | Project Management Software (Source Commits Endpoint) | | [Prometheus](prometheus.md) | Monitor the performance of your deployed apps | diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index aab7131e353..765aa91b00f 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -18,6 +18,7 @@ Once enabled, GitLab will automatically detect metrics from known services in th ## Enabling Prometheus Integration ### Managed Prometheus on Kubernetes + > **Note**: [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28916) in GitLab 10.5 GitLab can seamlessly deploy and manage Prometheus on a [connected Kubernetes cluster](../clusters/index.md), making monitoring of your apps easy. @@ -39,9 +40,9 @@ Once you have a connected Kubernetes cluster with Helm installed, deploying a ma #### About managed Prometheus deployments -Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/). +Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/). -The Prometheus server will [automatically detect and monitor](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#%3Ckubernetes_sd_config%3E) nodes, pods, and endpoints. To configure a resource to be monitored by Prometheus, simply set the following [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/): +The Prometheus server will [automatically detect and monitor](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) nodes, pods, and endpoints. To configure a resource to be monitored by Prometheus, simply set the following [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/): - `prometheus.io/scrape` to `true` to enable monitoring of the resource. - `prometheus.io/port` to define the port of the metrics endpoint. @@ -66,9 +67,9 @@ Integration with Prometheus requires the following: Installing and configuring Prometheus to monitor applications is fairly straight forward. -1. [Install Prometheus](https://prometheus.io/docs/introduction/install/) +1. [Install Prometheus](https://prometheus.io/docs/prometheus/latest/installation/) 1. Set up one of the [supported monitoring targets](prometheus_library/index.md) -1. Configure the Prometheus server to [collect their metrics](https://prometheus.io/docs/operating/configuration/#scrape_config) +1. Configure the Prometheus server to [collect their metrics](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config) #### Configuration in GitLab @@ -93,7 +94,7 @@ GitLab will automatically scan the Prometheus server for metrics from known serv You can view the performance dashboard for an environment by [clicking on the monitoring button](../../../ci/environments.md#monitoring-environments). -### Adding additional metrics **[PREMIUM]** +### Adding additional metrics **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3799) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.6. @@ -120,6 +121,69 @@ GitLab supports a limited set of [CI variables](../../../ci/variables/README.htm To specify a variable in a query, enclose it in curly braces with a leading percent. For example: `%{ci_environment_slug}`. +### Defining Dashboards for Prometheus Metrics per Project + +All projects include a GitLab-defined system dashboard, which includes a few key metrics. Optionally, additional dashboards can also be defined by including configuration files in the project repository under `.gitlab/dashboards`. Configuration files nested under subdirectories will not be available in the UI. Each file should define the layout of the dashboard and the prometheus queries used to populate data. Dashboards can be selected from the dropdown in the UI. + +#### Relationship to Custom Metrics + +[Custom Metrics](#adding-additional-metrics-premium) are defined through the UI and, at this point, are unique from metrics defined in dashboard configuration files. Custom Metrics will appear on the system dashboard, as well as support alerting, whereas metrics defined in configuration files do not yet support alerts. + +#### Dashboard Configuration + +Dashboards have several components. A dashboard has many panel groups, which are comprised of panels, which support one or more metrics. The dashboard should be saved with the `.yml` extension. + +Sample YML Configuration +``` +dashboard: 'Dashboard Title' +priority: 2 +panel_groups: + - group: 'Group Title' + panels: + - type: area-chart + title: "Chart Title" + y_label: "Y-Axis" + metrics: + - id: metric_of_ages + query_range: 'http_requests_total' + label: "Metric of Ages" + unit: "count" +``` + +The above sample dashboard would display a single area chart. The following sections outline the details of expected properties. + +##### Dashboard Properties +| Property | Type | Required? | Meaning | +| ------ | ------ | ------ | ------ | +| `dashboard` | string | required | Heading for the dashboard. Only one dashboard should be defined per file. | +| `priority` | number | optional, default to definition order | Order to appear in dashboard dropdown, higher priority should be higher in the dropdown. Numbers do not need to be consecutive. | +| `panel_groups` | array | required | The panel groups which should be on the dashboard. | + +##### Panel Group Properties +| Property | Type | Required? | Meaning | +| ------ | ------ | ------ | ------ | +| `group` | string | required | Heading for the panel group. | +| `priority` | number | optional, defaults to order in file | Order to appear on the dashboard, higher priority will be higher on the page. Numbers do not need to be consecutive. | +| `panels` | array | required | The panels which should be in the panel group. | + +##### Panel Properties +| Property | Type | Required? | Meaning | +| ------ | ------ | ------ | ------- | +| `type` | enum | optional, defaults to `area-chart` | Specifies the chart type to use. Only `area-chart` is currently supported. | +| `title` | string | required | Heading for the panel. | +| `y_label` | string | optional, but highly encouraged | Y-Axis label for the panel. | +| `weight` | number | optional, defaults to order in file | Order to appear within the grouping, higher priority will be higher on the page. Numbers do not need to be consecutive. | +| `metrics` | array | required | The metrics which should be displayed in the panel. | + +##### Metric Properties +| Property | Type | Required? | Meaning | +| ------ | ------ | ------ | ------ | +| `id` | string | optional | Used for associating dashboard metrics with database records. Must be unique across dashboard configuration files. Required for [alerting](#setting-up-alerts-for-prometheus-metrics-ultimate) (support not yet enabled, see [relevant issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/60319)). | +| `unit` | string | required | Defines the unit of the query's return data. | +| `label` | string | optional, but highly encouraged | Defines the legend-label for the query. Should be unique within the panel's metrics. | +| `query` | string | required unless `query_range` is defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be utilized. | +| `query_range` | string | required unless `query` is defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query_range` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be utilized. | + ### Setting up alerts for Prometheus metrics **[ULTIMATE]** #### Managed Prometheus instances @@ -156,7 +220,7 @@ receivers: ... ``` -### Taking action on incidents **[ULTIMATE]** +### Taking action on incidents **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4925) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.11. diff --git a/doc/user/project/integrations/prometheus_library/cloudwatch.md b/doc/user/project/integrations/prometheus_library/cloudwatch.md index 01da7e00d74..5049733abdd 100644 --- a/doc/user/project/integrations/prometheus_library/cloudwatch.md +++ b/doc/user/project/integrations/prometheus_library/cloudwatch.md @@ -20,7 +20,7 @@ The [Prometheus service](../prometheus.md) must be enabled. To get started with Cloudwatch monitoring, you should install and configure the [Cloudwatch exporter](https://github.com/prometheus/cloudwatch_exporter) which retrieves and parses the specified Cloudwatch metrics and translates them into a Prometheus monitoring endpoint. -Right now, the only AWS resource supported is the Elastic Load Balancer, whose Cloudwatch metrics can be found [here](http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-cloudwatch-metrics.html). +Right now, the only AWS resource supported is the Elastic Load Balancer, whose Cloudwatch metrics are [documented here](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-cloudwatch-metrics.html). A sample Cloudwatch Exporter configuration file, configured for basic AWS ELB monitoring, is [available for download](../samples/cloudwatch.yml). diff --git a/doc/user/project/integrations/prometheus_library/kubernetes.md b/doc/user/project/integrations/prometheus_library/kubernetes.md index 8b1cf1a251a..0d300d3c418 100644 --- a/doc/user/project/integrations/prometheus_library/kubernetes.md +++ b/doc/user/project/integrations/prometheus_library/kubernetes.md @@ -36,7 +36,7 @@ In order to isolate and only display relevant CPU and Memory metrics for a given Instead, the [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) name should begin with [CI_ENVIRONMENT_SLUG](../../../../ci/variables/README.md#predefined-environment-variables). It can be followed by a `-` and additional content if desired. For example, a deployment name of `review-homepage-5620p5` would match the `review/homepage` environment. -## Displaying Canary metrics **[PREMIUM]** +## Displaying Canary metrics **(PREMIUM)** > Introduced in [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15201). diff --git a/doc/user/project/integrations/slack.md b/doc/user/project/integrations/slack.md index bb8d276c2fc..508e72b5753 100644 --- a/doc/user/project/integrations/slack.md +++ b/doc/user/project/integrations/slack.md @@ -6,7 +6,7 @@ The Slack Notifications Service allows your GitLab project to send events (e.g. ## Slack Configuration -1. Sign in to your Slack team and [start a new Incoming WebHooks configuration](https://my.slack.com/services/new/incoming-webhook/). +1. Sign in to your Slack team and [start a new Incoming WebHooks configuration](https://my.slack.com/services/new/incoming-webhook). 1. Select the Slack channel where notifications will be sent to by default. Click the **Add Incoming WebHooks integration** button to add the configuration. 1. Copy the **Webhook URL**, which we'll use later in the GitLab configuration. diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md index 371e78ca3a4..813066c51ac 100644 --- a/doc/user/project/integrations/slack_slash_commands.md +++ b/doc/user/project/integrations/slack_slash_commands.md @@ -1,4 +1,4 @@ -# Slack slash commands **[CORE ONLY]** +# Slack slash commands **(CORE ONLY)** > Introduced in GitLab 8.15. diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md index 04a9d9568ca..30940b65454 100644 --- a/doc/user/project/integrations/webhooks.md +++ b/doc/user/project/integrations/webhooks.md @@ -24,7 +24,7 @@ to the webhook URL. In most cases, you'll need to set up your own [webhook receiver](#example-webhook-receiver) to receive information from GitLab, and send it to another app, according to your needs. -We already have a [built-in receiver](https://docs.gitlab.com/ce/project_services/slack.html) +We already have a [built-in receiver](slack.md) for sending [Slack](https://api.slack.com/incoming-webhooks) notifications _per project_. ## Overview diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md index 31020de5208..3eb5581912a 100644 --- a/doc/user/project/issue_board.md +++ b/doc/user/project/issue_board.md @@ -28,11 +28,11 @@ Issue Boards** (version introduced in GitLab 8.11 - August 2016). ### Advanced features of Issue Boards With [GitLab Starter](https://about.gitlab.com/pricing/), you can create -[multiple issue boards](#multiple-issue-boards-starter) for a given project. **[STARTER]** +[multiple issue boards](#multiple-issue-boards-starter) for a given project. **(STARTER)** With [GitLab Premium](https://about.gitlab.com/pricing/), you can also create multiple issue boards for your groups, and add lists for [assignees](#assignee-lists-premium) and -[milestones](#milestone-lists-premium). **[PREMIUM]** +[milestones](#milestone-lists-premium). **(PREMIUM)** Check all the [advanced features of Issue Boards](#gitlab-enterprise-features-for-issue-boards) below. @@ -163,7 +163,7 @@ on the following sections. For a collection of [features per tier](#summary-of-features-per-tier), check the summary below. -### Multiple Issue Boards **[STARTER]** +### Multiple Issue Boards **(STARTER)** > Introduced in [GitLab Enterprise Edition 8.13](https://about.gitlab.com/2016/10/22/gitlab-8-13-released/#multiple-issue-boards-ee). @@ -188,7 +188,7 @@ NOTE: **Note:** The Multiple Issue Boards feature is available for **projects in GitLab Starter Edition** and for **groups in GitLab Premium Edition**. -### Configurable Issue Boards **[STARTER]** +### Configurable Issue Boards **(STARTER)** > Introduced in [GitLab Starter Edition 10.2](https://about.gitlab.com/2017/11/22/gitlab-10-2-released/#issue-boards-configuration). @@ -208,7 +208,7 @@ If you don't have editing permission in a board, you're still able to see the co ![Viewing board configuration](img/issue_board_view_scope.png) -### Focus mode **[STARTER]** +### Focus mode **(STARTER)** > Introduced in [GitLab Starter 9.1](https://about.gitlab.com/2017/04/22/gitlab-9-1-released/#issue-boards-focus-mode-ees-eep). @@ -216,7 +216,7 @@ Click the button at the top right to toggle focus mode on and off. In focus mode ![Board focus mode](img/issue_board_focus_mode.gif) -### Sum of Issue Weights **[STARTER]** +### Sum of Issue Weights **(STARTER)** The top of each list indicates the sum of issue weights for the issues that belong to that list. This is useful when using boards for capacity allocation, @@ -224,7 +224,7 @@ especially in combination with [assignee lists](#assignee-lists-premium). ![Issue Board summed weights](img/issue_board_summed_weights.png) -### Group Issue Boards **[PREMIUM]** +### Group Issue Boards **(PREMIUM)** > Introduced in [GitLab Premium 10.0](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards). @@ -240,7 +240,7 @@ one group issue board per group was made available in GitLab 10.6 Core. ![Group issue board](img/group_issue_board.png) -### Assignee lists **[PREMIUM]** +### Assignee lists **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5784) in GitLab 11.0 Premium. @@ -259,7 +259,7 @@ To remove an assignee list, just as with a label list, click the trash icon. ![Assignee lists](img/issue_board_assignee_lists.png) -### Milestone lists **[PREMIUM]** +### Milestone lists **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6469) in GitLab 11.2 Premium. diff --git a/doc/user/project/issues/csv_export.md b/doc/user/project/issues/csv_export.md index 56b94585672..0b7a7af5927 100644 --- a/doc/user/project/issues/csv_export.md +++ b/doc/user/project/issues/csv_export.md @@ -1,4 +1,4 @@ -# Export Issues to CSV **[STARTER]** +# Export Issues to CSV **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1126) in [GitLab Starter 9.0](https://about.gitlab.com/2017/03/22/gitlab-9-0-released/#export-issues-ees-eep). diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md index f69b841e908..e917697e973 100644 --- a/doc/user/project/issues/index.md +++ b/doc/user/project/issues/index.md @@ -75,7 +75,7 @@ must be set. While you can view and manage the full details of an issue on the [issue page](#issue-page), you can also work with multiple issues at a time using the [Issues List](#issues-list), -[Issue Boards](#issue-boards), Issue references, and [Epics](#epics-ultimate)**[ULTIMATE]**. +[Issue Boards](#issue-boards), Issue references, and [Epics](#epics-ultimate)**(ULTIMATE)**. Key actions for Issues include: @@ -111,7 +111,7 @@ For sorting by issue priority, see [Label Priority](../labels.md#label-priority) ![Issue board](img/issue_board.png) [Issue boards](../issue_board.md) are Kanban boards with columns that display issues based on their labels -or their assignees**[PREMIUM]**. They offer the flexibility to manage issues using +or their assignees**(PREMIUM)**. They offer the flexibility to manage issues using highly customizable workflows. You can reorder issues within a column. If you drag an issue card to another column, its @@ -119,13 +119,13 @@ associated label or assignee will change to match that of the new column. The en board can also be filtered to only include issues from a certain milestone or an overarching label. -### Epics **[ULTIMATE]** +### Epics **(ULTIMATE)** [Epics](../../group/epics/index.md) let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones. -### Related issues **[STARTER]** +### Related issues **(STARTER)** You can mark two issues as related, so that when viewing one, the other is always listed in its [Related Issues](related_issues.md) section. This can help display important @@ -158,7 +158,7 @@ requires [GraphQL](../../../api/graphql/index.md) to be enabled. - [Bulk edit issues](../bulk_editing.md) - From the Issues List, select multiple issues in order to change their status, assignee, milestone, or labels in bulk. - [Import issues](csv_import.md) -- [Export issues](csv_export.md) **[STARTER]** +- [Export issues](csv_export.md) **(STARTER)** - [Issues API](../../../api/issues.md) - Configure an [external issue tracker](../../../integration/external-issue-tracker.md) such as Jira, Redmine, or Bugzilla. diff --git a/doc/user/project/issues/issue_data_and_actions.md b/doc/user/project/issues/issue_data_and_actions.md index b34263f0eec..9c4d0de46d3 100644 --- a/doc/user/project/issues/issue_data_and_actions.md +++ b/doc/user/project/issues/issue_data_and_actions.md @@ -59,7 +59,7 @@ TIP: **Tip:** If a user is not member of that project, it can only be assigned to them if they created the issue themselves. -##### 3.1. Multiple Assignees **[STARTER]** +##### 3.1. Multiple Assignees **(STARTER)** Often multiple people work on the same issue together, which can be especially difficult to track in large teams where there is shared ownership of an issue. @@ -67,7 +67,7 @@ to track in large teams where there is shared ownership of an issue. In [GitLab Starter](https://about.gitlab.com/pricing/), you can [assign multiple people](multiple_assignees_for_issues.md) to an issue. -#### 4. Epic **[ULTIMATE]** +#### 4. Epic **(ULTIMATE)** You can assign issues to an [Epic](../../group/epics/index.md), which allows better management of groups of related issues. @@ -102,7 +102,7 @@ TIP: **Tip:** If a label doesn't exist yet, you can click **Edit**, and it opens a dropdown menu from which you can select **Create new label**. -#### 9. Weight **[STARTER]** +#### 9. Weight **(STARTER)** [Assign a weight](../../../workflow/issue_weight.md) to an issue. Larger values are used to indicate more effort is required to complete the issue. Only @@ -180,7 +180,7 @@ TIP: **Tip:** Avoid mentioning `@all` in issues and merge requests, as it sends an email notification to all the members of that project's group, which can be interpreted as spam. -#### 18. Related Issues **[STARTER]** +#### 18. Related Issues **(STARTER)** Issues that were mentioned as [related issues](related_issues.md) are listed here. You can also click the `+` to add more related issues. diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md index 91dbf0d848e..709588959c1 100644 --- a/doc/user/project/issues/managing_issues.md +++ b/doc/user/project/issues/managing_issues.md @@ -48,7 +48,7 @@ create issues for the same project. ![Create issue from group-level issue tracker](img/create_issue_from_group_level_issue_tracker.png) -### New issue via Service Desk **[PREMIUM]** +### New issue via Service Desk **(PREMIUM)** Enable [Service Desk](../service_desk.md) for your project and offer email support. By doing so, when your customer sends a new email, a new issue can be created in @@ -115,7 +115,7 @@ The "Move issue" button is at the bottom of the right-sidebar when viewing the i If you have advanced technical skills you can also bulk move all the issues from one project to another in the rails console. The below script will move all the issues from one project to another that are not in status **closed**. -To access rails console run `sudo gitlab-rails console` on the GitLab server and run the below script. Please be sure to change **project**, **admin_user** and **target_project** to your values. We do also recommend [creating a backup](https://docs.gitlab.com/ee/raketasks/backup_restore.html#creating-a-backup-of-the-gitlab-system) before attempting any changes in the console. +To access rails console run `sudo gitlab-rails console` on the GitLab server and run the below script. Please be sure to change **project**, **admin_user** and **target_project** to your values. We do also recommend [creating a backup](../../../raketasks/backup_restore.md#creating-a-backup-of-the-gitlab-system) before attempting any changes in the console. ```ruby project = Project.find_by_full_path('full path of the project where issues are moved from') @@ -209,7 +209,7 @@ as well as `#22` and `#23` in group/otherproject. `#17` won't be closed as it do not match the pattern. It works with multi-line commit messages as well as one-liners when used from the command line with `git commit -m`. -#### Customizing the issue closing pattern **[CORE ONLY]** +#### Customizing the issue closing pattern **(CORE ONLY)** In order to change the default issue closing pattern, GitLab administrators must edit the [`gitlab.rb` or `gitlab.yml` file](../../../administration/issue_closing_pattern.md) diff --git a/doc/user/project/issues/multiple_assignees_for_issues.md b/doc/user/project/issues/multiple_assignees_for_issues.md index d1db0790d69..c9efe9f5031 100644 --- a/doc/user/project/issues/multiple_assignees_for_issues.md +++ b/doc/user/project/issues/multiple_assignees_for_issues.md @@ -1,4 +1,4 @@ -# Multiple Assignees for Issues **[STARTER]** +# Multiple Assignees for Issues **(STARTER)** > **Note:** [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1904) diff --git a/doc/user/project/issues/related_issues.md b/doc/user/project/issues/related_issues.md index e679ebf86e6..9c72fe33d0d 100644 --- a/doc/user/project/issues/related_issues.md +++ b/doc/user/project/issues/related_issues.md @@ -1,4 +1,4 @@ -# Related issues **[STARTER]** +# Related issues **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1797) in [GitLab Starter](https://about.gitlab.com/pricing/) 9.4. diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md index 3eca1313a18..fdfeb4aa4cd 100644 --- a/doc/user/project/labels.md +++ b/doc/user/project/labels.md @@ -11,7 +11,7 @@ In GitLab, you can create project and group labels: - **Project labels** can be assigned to issues or merge requests in that project only. - **Group labels** can be assigned to any issue or merge request of any project in that group or any subgroups of the group. -## Scoped labels **[PREMIUM]** +## Scoped labels **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9175) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10. @@ -38,7 +38,7 @@ For example, `nested::key1::value1` and `nested::key1::value2` cannot both exist `nested::key1::value1` and `nested::key2::value1` can both exist on the same issue, as these are considered to use two different label scopes, `nested::key1` and `nested::key2`. -### Workflows with scoped labels **[PREMIUM]** +### Workflows with scoped labels **(PREMIUM)** Suppose you wanted a custom field in issues to track the platform operating system that your features target, where each issue should only target one platform. You @@ -144,9 +144,9 @@ From the group epic list page, you can [filter](../search/index.md#issues-and-me ### Filtering in issue boards - From [project boards](issue_board.md), you can filter by both group labels and project labels in the [search and filter bar](../search/index.md#issue-boards). -- From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [search and filter bar](../search/index.md#issue-boards). **[PREMIUM]** -- From [project boards](issue_board.md), you can filter by both group labels and project labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **[STARTER]** -- From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **[STARTER]** +- From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [search and filter bar](../search/index.md#issue-boards). **(PREMIUM)** +- From [project boards](issue_board.md), you can filter by both group labels and project labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **(STARTER)** +- From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **(STARTER)** ## Subscribing to labels diff --git a/doc/user/project/merge_requests/browser_performance_testing.md b/doc/user/project/merge_requests/browser_performance_testing.md index f6c4f767e3c..49b9826a52a 100644 --- a/doc/user/project/merge_requests/browser_performance_testing.md +++ b/doc/user/project/merge_requests/browser_performance_testing.md @@ -2,7 +2,7 @@ type: reference, howto --- -# Browser Performance Testing **[PREMIUM]** +# Browser Performance Testing **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3507) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.3. @@ -19,7 +19,7 @@ tool for measuring the performance of web sites, and has built a simple which outputs the results in a file called `performance.json`. This plugin outputs the performance score for each page that is analyzed. -The [Sitespeed.io performance score](http://examples.sitespeed.io/6.0/2017-11-23-23-43-35/help.html) +The [Sitespeed.io performance score](https://examples.sitespeed.io/6.0/2017-11-23-23-43-35/help.html) is a composite value based on best practices, and we will be expanding support for [additional metrics](https://gitlab.com/gitlab-org/gitlab-ee/issues/4370) in a future release. diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md index c3c2bdd94b3..ad1d79ae5b1 100644 --- a/doc/user/project/merge_requests/code_quality.md +++ b/doc/user/project/merge_requests/code_quality.md @@ -2,7 +2,7 @@ type: reference, howto --- -# Code Quality **[STARTER]** +# Code Quality **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1984) in [GitLab Starter](https://about.gitlab.com/pricing/) 9.3. diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index 169b10572b0..c299a8ecb96 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -37,16 +37,16 @@ With GitLab merge requests, you can: With **[GitLab Enterprise Edition][ee]**, you can also: -- Prepare a full review and submit it once it's ready with [Merge Request Reviews](../../discussions/index.md#merge-request-reviews-premium) **[PREMIUM]** -- View the deployment process across projects with [Multi-Project Pipelines](../../../ci/multi_project_pipelines.md) **[PREMIUM]** -- Request [approvals](merge_request_approvals.md) from your managers **[STARTER]** -- Analyze the impact of your changes with [Code Quality reports](code_quality.md) **[STARTER]** -- Manage the licenses of your dependencies with [License Management](../../application_security/license_management/index.md) **[ULTIMATE]** -- Analyze your source code for vulnerabilities with [Static Application Security Testing](../../application_security/sast/index.md) **[ULTIMATE]** -- Analyze your running web applications for vulnerabilities with [Dynamic Application Security Testing](../../application_security/dast/index.md) **[ULTIMATE]** -- Analyze your dependencies for vulnerabilities with [Dependency Scanning](../../application_security/dependency_scanning/index.md) **[ULTIMATE]** -- Analyze your Docker images for vulnerabilities with [Container Scanning](../../application_security/container_scanning/index.md) **[ULTIMATE]** -- Determine the performance impact of changes with [Browser Performance Testing](#browser-performance-testing-premium) **[PREMIUM]** +- Prepare a full review and submit it once it's ready with [Merge Request Reviews](../../discussions/index.md#merge-request-reviews-premium) **(PREMIUM)** +- View the deployment process across projects with [Multi-Project Pipelines](../../../ci/multi_project_pipelines.md) **(PREMIUM)** +- Request [approvals](merge_request_approvals.md) from your managers **(STARTER)** +- Analyze the impact of your changes with [Code Quality reports](code_quality.md) **(STARTER)** +- Manage the licenses of your dependencies with [License Management](../../application_security/license_management/index.md) **(ULTIMATE)** +- Analyze your source code for vulnerabilities with [Static Application Security Testing](../../application_security/sast/index.md) **(ULTIMATE)** +- Analyze your running web applications for vulnerabilities with [Dynamic Application Security Testing](../../application_security/dast/index.md) **(ULTIMATE)** +- Analyze your dependencies for vulnerabilities with [Dependency Scanning](../../application_security/dependency_scanning/index.md) **(ULTIMATE)** +- Analyze your Docker images for vulnerabilities with [Container Scanning](../../application_security/container_scanning/index.md) **(ULTIMATE)** +- Determine the performance impact of changes with [Browser Performance Testing](#browser-performance-testing-premium) **(PREMIUM)** ## Use cases @@ -54,9 +54,9 @@ A. Consider you are a software developer working in a team: 1. You checkout a new branch, and submit your changes through a merge request 1. You gather feedback from your team -1. You work on the implementation optimizing code with [Code Quality reports](code_quality.md) **[STARTER]** +1. You work on the implementation optimizing code with [Code Quality reports](code_quality.md) **(STARTER)** 1. You verify your changes with [JUnit test reports](../../../ci/junit_test_reports.md) in GitLab CI/CD -1. You avoid using dependencies whose license is not compatible with your project with [License Management reports](license_management.md) **[ULTIMATE]** +1. You avoid using dependencies whose license is not compatible with your project with [License Management reports](license_management.md) **(ULTIMATE)** 1. You request the [approval](#merge-request-approvals-starter) from your manager 1. Your manager pushes a commit with their final review, [approves the merge request](merge_request_approvals.md), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Starter) 1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#whenmanual) for GitLab CI/CD @@ -68,7 +68,7 @@ B. Consider you're a web developer writing a webpage for your company's website: 1. You gather feedback from your reviewers 1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md) 1. You request your web designers for their implementation -1. You request the [approval](merge_request_approvals.md) from your manager **[STARTER]** +1. You request the [approval](merge_request_approvals.md) from your manager **(STARTER)** 1. Once approved, your merge request is [squashed and merged](squash_and_merge.md), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) 1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production @@ -172,7 +172,7 @@ in a Merge Request. To do so, click the **...** button in the gutter of the Merg ![Comment on any diff file line](img/comment-on-any-diff-line.png) -## Perform a Review **[PREMIUM]** +## Perform a Review **(PREMIUM)** Start a review in order to create multiple comments on a diff and publish them once you're ready. Starting a review allows you to get all your thoughts in order and ensure you haven't missed anything @@ -197,7 +197,7 @@ can easily apply them to the codebase directly from the UI. Read through the documentation on [Suggest changes](../../discussions/index.md#suggest-changes) to learn more. -## Multiple assignees **[STARTER]** +## Multiple assignees **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/2004) in [GitLab Starter 11.11](https://about.gitlab.com/pricing). @@ -274,7 +274,8 @@ branch already exists, the patches will be applied on top of it. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/26752) in GitLab 11.10. NOTE: **Note:** -Git push options are only available with Git 2.10 or newer. +Git push options are only available with Git 2.10 or newer. With Git older than 2.18 +`git push --push-option=...` should be used instead of `git push -o ...`. GitLab supports using [Git push options](https://git-scm.com/docs/git-push#Documentation/git-push.txt--oltoptiongt) @@ -364,7 +365,7 @@ have been marked as a **Work In Progress**. [Learn more about setting a merge request as "Work In Progress".](work_in_progress_merge_requests.md) -## Merge request approvals **[STARTER]** +## Merge request approvals **(STARTER)** > Included in [GitLab Starter][products]. @@ -375,7 +376,7 @@ list of approvers that will need to approve every merge request in a project. [Read more about merge request approvals.](merge_request_approvals.md) -## Code Quality **[STARTER]** +## Code Quality **(STARTER)** > Introduced in [GitLab Starter][products] 9.3. @@ -385,7 +386,7 @@ can show the Code Climate report right in the merge request widget area. [Read more about Code Quality reports.](code_quality.md) -## Browser Performance Testing **[PREMIUM]** +## Browser Performance Testing **(PREMIUM)** > Introduced in [GitLab Premium][products] 10.3. @@ -395,7 +396,7 @@ GitLab runs the [Sitespeed.io container][sitespeed-container] and displays the d [Read more about Browser Performance Testing.](browser_performance_testing.md) -## Security reports **[ULTIMATE]** +## Security reports **(ULTIMATE)** GitLab can scan and report any vulnerabilities found in your project. diff --git a/doc/user/project/merge_requests/merge_request_approvals.md b/doc/user/project/merge_requests/merge_request_approvals.md index 8e8ec26daf2..0f392676316 100644 --- a/doc/user/project/merge_requests/merge_request_approvals.md +++ b/doc/user/project/merge_requests/merge_request_approvals.md @@ -2,7 +2,7 @@ type: reference, concepts --- -# Merge request approvals **[STARTER]** +# Merge request approvals **(STARTER)** > Introduced in [GitLab Enterprise Edition 7.12](https://about.gitlab.com/2015/06/22/gitlab-7-12-released/#merge-request-approvers-ee-only). @@ -64,7 +64,7 @@ suitable to your workflow: [overridden per merge request](#overriding-the-merge-request-approvals-default-settings) - Choose whether [approvals will be reset with new pushed commits](#resetting-approvals-on-push) -## Editing approvals **[PREMIUM]** +## Editing approvals **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1979) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8. @@ -85,7 +85,7 @@ request approval rules: ![Approvals premium project edit](img/approvals_premium_project_edit.png) -## Multiple approval rules **[PREMIUM]** +## Multiple approval rules **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1979) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8. @@ -165,7 +165,7 @@ are other conditions that may block it, such as merge conflicts, [pending discussions](../../discussions/index.md#only-allow-merge-requests-to-be-merged-if-all-discussions-are-resolved) or a [failed CI/CD pipeline](merge_when_pipeline_succeeds.md). -## Code Owners approvals **[PREMIUM]** +## Code Owners approvals **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4418) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.9. @@ -237,7 +237,7 @@ If you are contributing to a forked project, things are a little different. Read what happens when the [source and target branches are not the same](#merge-requests-with-different-source-branch-and-target-branch-projects). -## Overriding merge request approvals default settings **[PREMIUM]** +## Overriding merge request approvals default settings **(PREMIUM)** In GitLab Premium, when the approval rules are [set at the project level](#editing-approvals-premium), and **Can override approvers and approvals required per merge request** is checked, there are a few more @@ -295,7 +295,7 @@ enabling [**Prevent approval of merge requests by their committers**](#prevent-a 1. Tick the checkbox **Prevent approval of merge requests by their committers**. 1. Click **Save changes**. -## Require authentication when approving a merge request **[STARTER]** +## Require authentication when approving a merge request **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5981) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.0. diff --git a/doc/user/project/milestones/burndown_charts.md b/doc/user/project/milestones/burndown_charts.md index 7ffeb032d7f..84e08b4eeb8 100644 --- a/doc/user/project/milestones/burndown_charts.md +++ b/doc/user/project/milestones/burndown_charts.md @@ -1,4 +1,4 @@ -# Burndown Charts **[STARTER]** +# Burndown Charts **(STARTER)** > **Notes:** > - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1540) in [GitLab Starter](https://about.gitlab.com/pricing/) 9.1 for project milestones. diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md index 6cd866b5c0d..b0745b1e2c5 100644 --- a/doc/user/project/milestones/index.md +++ b/doc/user/project/milestones/index.md @@ -84,9 +84,9 @@ From the project issue/merge request list pages and the group issue/merge reques ### Filtering in issue boards - From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [search and filter bar](../../search/index.md#issue-boards). -- From [group issue boards](../issue_board.md#group-issue-boards-premium), you can filter by only group milestones in the [search and filter bar](../../search/index.md#issue-boards). **[PREMIUM]** -- From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **[STARTER]** -- From [group issue boards](../issue_board.md#group-issue-boards-premium) you can filter by only group milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **[STARTER]** +- From [group issue boards](../issue_board.md#group-issue-boards-premium), you can filter by only group milestones in the [search and filter bar](../../search/index.md#issue-boards). **(PREMIUM)** +- From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **(STARTER)** +- From [group issue boards](../issue_board.md#group-issue-boards-premium) you can filter by only group milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **(STARTER)** ### Special milestone filters @@ -124,13 +124,13 @@ These features are only available for project milestones and not group milestone - Participants and labels that are used in issues and merge requests that have the milestone assigned are displayed. - [Burndown chart](#project-burndown-charts-starter). -### Project Burndown Charts **[STARTER]** +### Project Burndown Charts **(STARTER)** For project milestones in [GitLab Starter](https://about.gitlab.com/pricing), a [burndown chart](burndown_charts.md) is in the milestone view, showing the progress of completing a milestone. ![burndown chart](img/burndown_chart.png) -### Group Burndown Charts **[PREMIUM]** +### Group Burndown Charts **(PREMIUM)** For group milestones in [GitLab Premium](https://about.gitlab.com/pricing), a [burndown chart](burndown_charts.md) is in the milestone view, showing the progress of completing a milestone. diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md index c07c4099f22..d35a8568394 100644 --- a/doc/user/project/new_ci_build_permissions_model.md +++ b/doc/user/project/new_ci_build_permissions_model.md @@ -185,7 +185,7 @@ The [Job environment variable][jobenv] `CI_JOB_TOKEN` can be used to authenticate any clones of dependent repositories. For example: ``` -git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/myuser/mydependentrepo +git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/<user>/<mydependentrepo>.git ``` It can also be used for system-wide authentication diff --git a/doc/user/project/operations/feature_flags.md b/doc/user/project/operations/feature_flags.md index a5db3d70bb9..fdc1e978291 100644 --- a/doc/user/project/operations/feature_flags.md +++ b/doc/user/project/operations/feature_flags.md @@ -1,4 +1,4 @@ -# Feature Flags **[PREMIUM]** +# Feature Flags **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845) in GitLab 11.4. diff --git a/doc/user/project/operations/index.md b/doc/user/project/operations/index.md index 84711d1146f..2da9c3e70cf 100644 --- a/doc/user/project/operations/index.md +++ b/doc/user/project/operations/index.md @@ -7,6 +7,6 @@ your applications: - Deploy to different [environments](../../../ci/environments.md). - Connect your project to a [Kubernetes cluster](../clusters/index.md). - Discover and view errors generated by your applications with [Error Tracking](error_tracking.md). -- Create, toggle, and remove [Feature Flags](feature_flags.md). **[PREMIUM]** -- [Trace](tracing.md) the performance and health of a deployed application. **[ULTIMATE]** +- Create, toggle, and remove [Feature Flags](feature_flags.md). **(PREMIUM)** +- [Trace](tracing.md) the performance and health of a deployed application. **(ULTIMATE)** - Add a [button to the Monitoring dashboard](linking_to_an_external_dashboard.md) linking directly to your existing external dashboards. diff --git a/doc/user/project/operations/tracing.md b/doc/user/project/operations/tracing.md index 0416e096cf2..b92d2e49839 100644 --- a/doc/user/project/operations/tracing.md +++ b/doc/user/project/operations/tracing.md @@ -1,4 +1,4 @@ -# Tracing **[ULTIMATE]** +# Tracing **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/7903) in GitLab Ultimate 11.5. @@ -17,8 +17,8 @@ systems. ### Deploying Jaeger To learn more about deploying Jaeger, read the official -[Getting Started documentation](https://www.jaegertracing.io/docs/latest/getting-started/). -There is an easy to use [all-in-one Docker image](https://www.jaegertracing.io/docs/latest/getting-started/#AllinoneDockerimage), +[Getting Started documentation](https://www.jaegertracing.io/docs/1.13/getting-started/). +There is an easy to use [all-in-one Docker image](https://www.jaegertracing.io/docs/1.13/getting-started/#AllinoneDockerimage), as well as deployment options for [Kubernetes](https://github.com/jaegertracing/jaeger-kubernetes) and [OpenShift](https://github.com/jaegertracing/jaeger-openshift). @@ -27,8 +27,8 @@ and [OpenShift](https://github.com/jaegertracing/jaeger-openshift). GitLab provides an easy way to open the Jaeger UI from within your project: 1. [Set up Jaeger](#deploying-jaeger) and configure your application using one of the - [client libraries](https://www.jaegertracing.io/docs/latest/client-libraries/). + [client libraries](https://www.jaegertracing.io/docs/1.13/client-libraries/). 1. Navigate to your project's **Settings > Operations** and provide the Jaeger URL. 1. Click **Save changes** for the changes to take effect. 1. You can now visit **Operations > Tracing** in your project's sidebar and - GitLab will redirect you to the configured Jaeger URL.
\ No newline at end of file + GitLab will redirect you to the configured Jaeger URL. diff --git a/doc/user/project/packages/maven_repository.md b/doc/user/project/packages/maven_repository.md index 9b7af738696..27c052fb2bc 100644 --- a/doc/user/project/packages/maven_repository.md +++ b/doc/user/project/packages/maven_repository.md @@ -1,4 +1,4 @@ -# GitLab Maven Repository **[PREMIUM]** +# GitLab Maven Repository **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5811) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.3. @@ -12,7 +12,7 @@ project can have its own space to store its Maven artifacts. NOTE: **Note:** This option is available only if your GitLab administrator has -[enabled support for the Maven repository](../../../administration/packages.md).**[PREMIUM ONLY]** +[enabled support for the Maven repository](../../../administration/packages.md).**(PREMIUM ONLY)** After the Packages feature is enabled, the Maven Repository will be available for all new projects by default. To enable it for existing projects, or if you want diff --git a/doc/user/project/packages/npm_registry.md b/doc/user/project/packages/npm_registry.md index 2e274573434..481b1ce0337 100644 --- a/doc/user/project/packages/npm_registry.md +++ b/doc/user/project/packages/npm_registry.md @@ -1,4 +1,4 @@ -# GitLab NPM Registry **[PREMIUM]** +# GitLab NPM Registry **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.7. @@ -11,16 +11,11 @@ project can have its own space to store NPM packages. NOTE: **Note:** Only [scoped](https://docs.npmjs.com/misc/scope) packages are supported. - -NOTE: **Note:** -As `@group/subgroup/project` is not a valid NPM package name, publishing a package -within a subgroup is not supported yet. - ## Enabling the NPM Registry NOTE: **Note:** This option is available only if your GitLab administrator has -[enabled support for the NPM registry](../../../administration/packages.md).**[PREMIUM ONLY]** +[enabled support for the NPM registry](../../../administration/packages.md).**(PREMIUM ONLY)** After the NPM registry is enabled, it will be available for all new projects by default. To enable it for existing projects, or if you want to disable it: @@ -36,12 +31,15 @@ get familiar with the package naming convention. ## Package naming convention -**Only packages that have the same path as the project** are supported. For - example: +**Packages must be scoped in the root namespace of the project**. The package +name may be anything but it is preferred that the project name be used unless +it is not possible due to a naming collision. For example: | Project | Package | Supported | | ---------------------- | ----------------------- | --------- | | `foo/bar` | `@foo/bar` | Yes | +| `foo/bar/baz` | `@foo/baz` | Yes | +| `foo/bar/buz` | `@foo/anything` | Yes | | `gitlab-org/gitlab-ce` | `@gitlab-org/gitlab-ce` | Yes | | `gitlab-org/gitlab-ce` | `@foo/bar` | No | @@ -113,6 +111,9 @@ npm publish You can then navigate to your project's **Packages** page and see the uploaded packages or even delete them. +If you attempt to publish a package with a name that already exists within +a given scope, you will receive a `403 Forbidden!` error. + ## Uploading a package with the same version twice If you upload a package with a same name and version twice, GitLab will show diff --git a/doc/user/project/pages/getting_started_part_four.md b/doc/user/project/pages/getting_started_part_four.md index 8baf41dba78..d844d4222b1 100644 --- a/doc/user/project/pages/getting_started_part_four.md +++ b/doc/user/project/pages/getting_started_part_four.md @@ -380,7 +380,7 @@ What you can do with GitLab CI is pretty much up to your creativity. Once you get used to it, you start creating awesome scripts that automate most of tasks you'd do manually in the past. Read through the -[documentation of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html) +[documentation of GitLab CI](../../../ci/yaml/README.md) to understand how to go even further on your scripts. - On this blog post, understand the concept of diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md index 4ea3bd9be9b..9451b5349c0 100644 --- a/doc/user/project/pages/introduction.md +++ b/doc/user/project/pages/introduction.md @@ -67,7 +67,7 @@ Some static site generators provide plugins for that functionality so that you don't have to create and edit HTML files manually. For example, Jekyll has the [redirect-from plugin](https://github.com/jekyll/jekyll-redirect-from). -## GitLab Pages Access Control **[CORE ONLY]** +## GitLab Pages Access Control **(CORE ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/33422) in GitLab 11.5. diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md index f9feae36dbc..1966b136e9d 100644 --- a/doc/user/project/pipelines/job_artifacts.md +++ b/doc/user/project/pipelines/job_artifacts.md @@ -187,10 +187,13 @@ information in the UI. DANGER: **Warning:** This is a destructive action that leads to data loss. Use with caution. -If you are either the owner of a given job or have Master -[permissions](../../permissions.md#gitlab-cicd-permissions) -on the project, you can erase a single job via the UI which will also remove the -artifacts and the job's trace. +You can erase a single job via the UI, which will also remove the job's +artifacts and trace, if you are: + +- The owner of the job. +- A [Maintainer](../../permissions.md#gitlab-cicd-permissions) of the project. + +To erase a job: 1. Navigate to a job's page. 1. Click the trash icon at the top right of the job's trace. diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md index 99cede557a0..20a03dff2da 100644 --- a/doc/user/project/protected_branches.md +++ b/doc/user/project/protected_branches.md @@ -66,7 +66,7 @@ dropdown list in the "Already protected" area. If you don't choose any of those options while creating a protected branch, they are set to "Maintainers" by default. -## Restricting push and merge access to certain users **[STARTER]** +## Restricting push and merge access to certain users **(STARTER)** > This feature was [introduced][ce-5081] in [GitLab Starter][ee] 8.11. diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index d20b44d4b92..8b8aa51b6dd 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -26,9 +26,9 @@ discussions, and descriptions: | `/award :emoji:` | Toggle emoji award | ✓ | ✓ | | `/assign me` | Assign yourself | ✓ | ✓ | | `/assign @user` | Assign one user | ✓ | ✓ | -| `/assign @user1 @user2` | Assign multiple users **[STARTER]** | ✓ | ✓ | -| `/unassign @user1 @user2` | Remove assignee(s) **[STARTER]** | ✓ | ✓ | -| `/reassign @user1 @user2` | Change assignee **[STARTER]** | ✓ | ✓ | +| `/assign @user1 @user2` | Assign multiple users **(STARTER)** | ✓ | ✓ | +| `/unassign @user1 @user2` | Remove assignee(s) **(STARTER)** | ✓ | ✓ | +| `/reassign @user1 @user2` | Change assignee **(STARTER)** | ✓ | ✓ | | `/unassign` | Remove current assignee | ✓ | ✓ | | `/milestone %milestone` | Set milestone | ✓ | ✓ | | `/remove_milestone` | Remove milestone | ✓ | ✓ | @@ -44,11 +44,11 @@ discussions, and descriptions: | `/unlock` | Unlock the discussion | ✓ | ✓ | | <code>/due <in 2 days | this Friday | December 31st></code>| Set due date | ✓ | | | `/remove_due_date` | Remove due date | ✓ | | -| <code>/weight <0 | 1 | 2 | ...></code> | Set weight **[STARTER]** | ✓ | | -| `/clear_weight` | Clears weight **[STARTER]** | ✓ | | -| <code>/epic <&epic | group&epic | Epic URL></code> | Add to epic **[ULTIMATE]** | ✓ | | -| `/remove_epic` | Removes from epic **[ULTIMATE]** | ✓ | | -| `/promote` | Promote issue to epic **[ULTIMATE]** | ✓ | | +| <code>/weight <0 | 1 | 2 | ...></code> | Set weight **(STARTER)** | ✓ | | +| `/clear_weight` | Clears weight **(STARTER)** | ✓ | | +| <code>/epic <&epic | group&epic | Epic URL></code> | Add to epic **(ULTIMATE)** | ✓ | | +| `/remove_epic` | Removes from epic **(ULTIMATE)** | ✓ | | +| `/promote` | Promote issue to epic **(ULTIMATE)** | ✓ | | | `/confidential` | Make confidential | ✓ | | | `/duplicate <#issue>` | Mark this issue as a duplicate of another issue | ✓ | | `/move <path/to/project>` | Move this issue to another project | ✓ | | @@ -57,7 +57,7 @@ discussions, and descriptions: | `/approve` | Approve the merge request | | ✓ | | `/merge` | Merge (when pipeline succeeds) | | ✓ | | `/create_merge_request <branch name>` | Create a new merge request starting from the current issue | ✓ | | -| `/relate #issue1 #issue2` | Mark issues as related **[STARTER]** | ✓ | | +| `/relate #issue1 #issue2` | Mark issues as related **(STARTER)** | ✓ | | ## Quick actions for commit messages @@ -67,7 +67,7 @@ The following quick actions are applicable for commit messages: |:------------------------|:------------------------------------------| | `/tag v1.2.3 <message>` | Tags this commit with an optional message | -## Quick actions for Epics **[ULTIMATE]** +## Quick actions for Epics **(ULTIMATE)** The following quick actions are applicable for epics threads and description: diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md index 3260a355fdc..cf0a986887e 100644 --- a/doc/user/project/repository/gpg_signed_commits/index.md +++ b/doc/user/project/repository/gpg_signed_commits/index.md @@ -261,7 +261,7 @@ To remove a GPG key from your account: 1. Navigate to the **GPG keys** tab. 1. Click on the trash icon besides the GPG key you want to delete. -## Rejecting commits that are not signed **[PREMIUM]** +## Rejecting commits that are not signed **(PREMIUM)** You can configure your project to reject commits that aren't GPG-signed via [push rules](../../../../push_rules/push_rules.md). diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md index 5e0f4c69b5e..5b5685b3418 100644 --- a/doc/user/project/repository/index.md +++ b/doc/user/project/repository/index.md @@ -221,7 +221,7 @@ Select branches to compare using the [branch filter search box](branches/index.m Find it under your project's **Repository > Compare**. -## Locked files **[PREMIUM]** +## Locked files **(PREMIUM)** Use [File Locking](../file_lock.md) to lock your files to prevent any conflicting changes. diff --git a/doc/user/project/service_desk.md b/doc/user/project/service_desk.md index 1a582164d03..f899120f0c2 100644 --- a/doc/user/project/service_desk.md +++ b/doc/user/project/service_desk.md @@ -1,4 +1,4 @@ -# Service Desk **[PREMIUM]** +# Service Desk **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/149) in [GitLab Premium 9.1](https://about.gitlab.com/2017/04/22/gitlab-9-1-released/#service-desk-eep). diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index 819515d7a4c..98bcc7cc09f 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -74,6 +74,13 @@ The following items will NOT be exported: - CI variables - Webhooks - Any encrypted tokens +- Merge Request Approvers +- Push Rules +- Awards + +NOTE: **Note:** +For more details on the specific data persisted in a project export, see the +[`import_export.yml`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/lib/gitlab/import_export/import_export.yml) file. ## Exporting a project and its data diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index 01763c49207..06b431decad 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -42,13 +42,13 @@ Set up your project's merge request settings: - Set up the merge request method (merge commit, [fast-forward merge](../merge_requests/fast_forward_merge.html)). - Merge request [description templates](../description_templates.md#description-templates). -- Enable [merge request approvals](../merge_requests/merge_request_approvals.md). **[STARTER]** +- Enable [merge request approvals](../merge_requests/merge_request_approvals.md). **(STARTER)** - Enable [merge only of pipeline succeeds](../merge_requests/merge_when_pipeline_succeeds.md). - Enable [merge only when all discussions are resolved](../../discussions/index.md#only-allow-merge-requests-to-be-merged-if-all-discussions-are-resolved). ![project's merge request settings](img/merge_requests_settings.png) -### Service Desk **[PREMIUM]** +### Service Desk **(PREMIUM)** Enable [Service Desk](../service_desk.md) for your project to offer customer support. @@ -135,6 +135,6 @@ namespace if needed. Configure Error Tracking to discover and view [Sentry errors within GitLab](../operations/error_tracking.md). -### Jaeger tracing **[ULTIMATE]** +### Jaeger tracing **(ULTIMATE)** Add the URL of a Jaeger server to allow your users to [easily access the Jaeger UI from within GitLab](../operations/tracing.md). diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md index 7d85f4adfed..3d92508ad04 100644 --- a/doc/user/project/web_ide/index.md +++ b/doc/user/project/web_ide/index.md @@ -129,7 +129,7 @@ below. } ``` -## Interactive Web Terminals for the Web IDE **[ULTIMATE ONLY]** +## Interactive Web Terminals for the Web IDE **(ULTIMATE ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5426) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.6. @@ -151,7 +151,7 @@ to work: - The Runner needs to have [`[session_server]` configured properly](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section). - If you are using a reverse proxy with your GitLab instance, web terminals need to be - [enabled](../../../administration/integration/terminal.md#enabling-and-disabling-terminal-support). **[ULTIMATE ONLY]** + [enabled](../../../administration/integration/terminal.md#enabling-and-disabling-terminal-support). **(ULTIMATE ONLY)** If you have the terminal open and the job has finished with its tasks, the terminal will block the job from finishing for the duration configured in diff --git a/doc/user/search/advanced_global_search.md b/doc/user/search/advanced_global_search.md index f80f4183802..52da6d65000 100644 --- a/doc/user/search/advanced_global_search.md +++ b/doc/user/search/advanced_global_search.md @@ -1,4 +1,4 @@ -# Advanced Global Search **[STARTER ONLY]** +# Advanced Global Search **(STARTER ONLY)** > - [Introduced][ee-109] in GitLab [Starter][ee] 8.4. > - This is the user documentation. To install and configure Elasticsearch, diff --git a/doc/user/search/advanced_search_syntax.md b/doc/user/search/advanced_search_syntax.md index d302cb7a809..ad9f065b19b 100644 --- a/doc/user/search/advanced_search_syntax.md +++ b/doc/user/search/advanced_search_syntax.md @@ -1,4 +1,4 @@ -# Advanced Syntax Search **[STARTER ONLY]** +# Advanced Syntax Search **(STARTER ONLY)** > **Notes:** > - Introduced in [GitLab Enterprise Starter][ee] 9.2 diff --git a/doc/user/search/index.md b/doc/user/search/index.md index d6e2f036cf2..c34b9ae3d7e 100644 --- a/doc/user/search/index.md +++ b/doc/user/search/index.md @@ -143,14 +143,14 @@ and **Labels**, select multiple issues to add to a list of your choice: ![search and select issues to add to board](img/search_issues_board.png) -## Advanced Global Search **[STARTER]** +## Advanced Global Search **(STARTER)** Leverage Elasticsearch for faster, more advanced code search across your entire GitLab instance. [Learn how to use the Advanced Global Search.](advanced_global_search.md) -## Advanced Syntax Search **[STARTER]** +## Advanced Syntax Search **(STARTER)** Use advanced queries for more targeted search results. diff --git a/doc/workflow/README.md b/doc/workflow/README.md index 45bd8c29a48..6ad61932868 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -13,15 +13,15 @@ comments: false - [Groups](../user/group/index.md) - Issues - The GitLab Issue Tracker is an advanced and complete tool for tracking the evolution of a new idea or the process of solving a problem. - - [Exporting Issues](../user/project/issues/csv_export.md) **[STARTER]** Export issues as a CSV, emailed as an attachment. + - [Exporting Issues](../user/project/issues/csv_export.md) **(STARTER)** Export issues as a CSV, emailed as an attachment. - [Confidential issues](../user/project/issues/confidential_issues.md) - [Due date for issues](../user/project/issues/due_dates.md) - [Issue Board](../user/project/issue_board.md) - [Keyboard shortcuts](shortcuts.md) - [File finder](file_finder.md) -- [File lock](../user/project/file_lock.md) **[PREMIUM]** +- [File lock](../user/project/file_lock.md) **(PREMIUM)** - [Labels](../user/project/labels.md) -- [Issue weight](issue_weight.md) **[STARTER]** +- [Issue weight](issue_weight.md) **(STARTER)** - [Notification emails](notifications.md) - [Projects](../user/project/index.md) - [Project forking workflow](forking_workflow.md) @@ -44,9 +44,9 @@ comments: false - [Merge requests versions](../user/project/merge_requests/versions.md) - ["Work In Progress" merge requests](../user/project/merge_requests/work_in_progress_merge_requests.md) - [Fast-forward merge requests](../user/project/merge_requests/fast_forward_merge.md) - - [Merge request approvals](../user/project/merge_requests/merge_request_approvals.md) **[STARTER]** -- [Repository mirroring](repository_mirroring.md) **[STARTER]** -- [Service Desk](../user/project/service_desk.md) **[PREMIUM]** + - [Merge request approvals](../user/project/merge_requests/merge_request_approvals.md) **(STARTER)** +- [Repository mirroring](repository_mirroring.md) **(STARTER)** +- [Service Desk](../user/project/service_desk.md) **(PREMIUM)** - [Manage large binaries with Git LFS](lfs/manage_large_binaries_with_git_lfs.md) - [Importing from SVN, GitHub, Bitbucket, etc](importing/README.md) - [Todos](todos.md) diff --git a/doc/workflow/issue_weight.md b/doc/workflow/issue_weight.md index 267160dae2a..afb623e1967 100644 --- a/doc/workflow/issue_weight.md +++ b/doc/workflow/issue_weight.md @@ -1,4 +1,4 @@ -# Issue Weight **[STARTER]** +# Issue Weight **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/76) > in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3. diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md index 6d941135bf2..55183408a10 100644 --- a/doc/workflow/lfs/lfs_administration.md +++ b/doc/workflow/lfs/lfs_administration.md @@ -54,7 +54,7 @@ to offload local hard disk R/W operations, and free up disk space significantly. GitLab is tightly integrated with `Fog`, so you can refer to its [documentation](http://fog.io/about/provider_documentation.html) to check which storage services can be integrated with GitLab. You can also use external object storage in a private local network. For example, -[Minio](https://www.minio.io/) is a standalone object storage service, is easy to set up, and works well with GitLab instances. +[Minio](https://min.io/) is a standalone object storage service, is easy to set up, and works well with GitLab instances. GitLab provides two different options for the uploading mechanism: "Direct upload" and "Background upload". @@ -148,20 +148,20 @@ On Omnibus installations, the settings are prefixed by `lfs_object_store_`: 1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with the values you want: - ```ruby - gitlab_rails['lfs_object_store_enabled'] = true - gitlab_rails['lfs_object_store_remote_directory'] = "lfs-objects" - gitlab_rails['lfs_object_store_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-central-1', - 'aws_access_key_id' => '1ABCD2EFGHI34JKLM567N', - 'aws_secret_access_key' => 'abcdefhijklmnopQRSTUVwxyz0123456789ABCDE', - # The below options configure an S3 compatible host instead of AWS - 'host' => 'localhost', - 'endpoint' => 'http://127.0.0.1:9000', - 'path_style' => true - } - ``` + ```ruby + gitlab_rails['lfs_object_store_enabled'] = true + gitlab_rails['lfs_object_store_remote_directory'] = "lfs-objects" + gitlab_rails['lfs_object_store_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-central-1', + 'aws_access_key_id' => '1ABCD2EFGHI34JKLM567N', + 'aws_secret_access_key' => 'abcdefhijklmnopQRSTUVwxyz0123456789ABCDE', + # The below options configure an S3 compatible host instead of AWS + 'host' => 'localhost', + 'endpoint' => 'http://127.0.0.1:9000', + 'path_style' => true + } + ``` 1. Save the file and [reconfigure GitLab]s for the changes to take effect. 1. Migrate any existing local LFS objects to the object storage: @@ -182,22 +182,22 @@ For source installations the settings are nested under `lfs:` and then 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - ```yaml - lfs: - enabled: true - object_store: - enabled: false - remote_directory: lfs-objects # Bucket name - connection: - provider: AWS - aws_access_key_id: 1ABCD2EFGHI34JKLM567N - aws_secret_access_key: abcdefhijklmnopQRSTUVwxyz0123456789ABCDE - region: eu-central-1 - # Use the following options to configure an AWS compatible host such as Minio - host: 'localhost' - endpoint: 'http://127.0.0.1:9000' - path_style: true - ``` + ```yaml + lfs: + enabled: true + object_store: + enabled: false + remote_directory: lfs-objects # Bucket name + connection: + provider: AWS + aws_access_key_id: 1ABCD2EFGHI34JKLM567N + aws_secret_access_key: abcdefhijklmnopQRSTUVwxyz0123456789ABCDE + region: eu-central-1 + # Use the following options to configure an AWS compatible host such as Minio + host: 'localhost' + endpoint: 'http://127.0.0.1:9000' + path_style: true + ``` 1. Save the file and [restart GitLab][] for the changes to take effect. 1. Migrate any existing local LFS objects to the object storage: diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md index d49d29c805a..1e8674f863d 100644 --- a/doc/workflow/notifications.md +++ b/doc/workflow/notifications.md @@ -84,11 +84,11 @@ In most of the below cases, the notification will be sent to: - Participants: - the author and assignee of the issue/merge request - authors of comments on the issue/merge request - - anyone mentioned by `@username` in the title or description of the issue, merge request or epic **[ULTIMATE]** + - anyone mentioned by `@username` in the title or description of the issue, merge request or epic **(ULTIMATE)** - anyone with notification level "Participating" or higher that is mentioned by `@username` - in any of the comments on the issue, merge request, or epic **[ULTIMATE]** + in any of the comments on the issue, merge request, or epic **(ULTIMATE)** - Watchers: users with notification level "Watch" -- Subscribers: anyone who manually subscribed to the issue, merge request, or epic **[ULTIMATE]** +- Subscribers: anyone who manually subscribed to the issue, merge request, or epic **(ULTIMATE)** - Custom: Users with notification level "custom" who turned on notifications for any of the events present in the table below | Event | Sent to | @@ -111,9 +111,9 @@ In most of the below cases, the notification will be sent to: | New comment | The above, plus anyone mentioned by `@username` in the comment, with notification level "Mention" or higher | | Failed pipeline | The author of the pipeline | | Successful pipeline | The author of the pipeline, if they have the custom notification setting for successful pipelines set | -| New epic **[ULTIMATE]** | | -| Close epic **[ULTIMATE]** | | -| Reopen epic **[ULTIMATE]** | | +| New epic **(ULTIMATE)** | | +| Close epic **(ULTIMATE)** | | +| Reopen epic **(ULTIMATE)** | | In addition, if the title or description of an Issue or Merge Request is changed, notifications will be sent to any **new** mentions by `@username` as diff --git a/doc/workflow/repository_mirroring.md b/doc/workflow/repository_mirroring.md index 5a24c254ed1..87ca46e94be 100644 --- a/doc/workflow/repository_mirroring.md +++ b/doc/workflow/repository_mirroring.md @@ -13,7 +13,7 @@ Repository mirroring is useful when you want to use a repository outside of GitL There are two kinds of repository mirroring supported by GitLab: - Push: for mirroring a GitLab repository to another location. -- Pull: for mirroring a repository from another location to GitLab. **[STARTER]** +- Pull: for mirroring a repository from another location to GitLab. **(STARTER)** When the mirror repository is updated, all new branches, tags, and commits will be visible in the project's activity feed. @@ -30,12 +30,12 @@ The following are some possible use cases for repository mirroring: - You migrated to GitLab but still need to keep your project in another source. In that case, you can simply set it up to mirror to GitLab (pull) and all the essential history of commits, tags, - and branches will be available in your GitLab instance. **[STARTER]** + and branches will be available in your GitLab instance. **(STARTER)** - You have old projects in another source that you don't use actively anymore, but don't want to remove for archiving purposes. In that case, you can create a push mirror so that your active GitLab repository can push its changes to the old location. -## Pushing to a remote repository **[CORE]** +## Pushing to a remote repository **(CORE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/249) in GitLab Enterprise > Edition 8.7. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8. @@ -65,7 +65,7 @@ Changes pushed to files in the repository are automatically pushed to the remote In the case of a diverged branch, you will see an error indicated at the **Mirroring repositories** section. -### Push only protected branches **[CORE]** +### Push only protected branches **(CORE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3350) in > [GitLab Starter](https://about.gitlab.com/pricing/) 10.3. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8. @@ -75,7 +75,7 @@ You can choose to only push your protected branches from GitLab to your remote r To use this option, check the **Only mirror protected branches** box when creating a repository mirror. -## Setting up a push mirror from GitLab to GitHub **[CORE]** +## Setting up a push mirror from GitLab to GitHub **(CORE)** To set up a mirror from GitLab to GitHub, you need to follow these steps: @@ -96,7 +96,7 @@ The repository will push soon. To force a push, click the appropriate button. 1. Fill in **Password** field with the GitLab personal access token created on the destination GitLab instance. 1. Click the **Mirror repository** button. -## Pulling from a remote repository **[STARTER]** +## Pulling from a remote repository **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51) in GitLab Enterprise Edition 8.2. > [Added Git LFS support](https://gitlab.com/gitlab-org/gitlab-ee/issues/10871) in [GitLab Starter](https://about.gitlab.com/pricing/) 11.11. @@ -243,7 +243,7 @@ If you need to change the key at any time, you can remove and re-add the mirror to generate a new key. You'll have to update the other repository with the new key to keep the mirror running. -### Overwrite diverged branches **[STARTER]** +### Overwrite diverged branches **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4559) in > [GitLab Starter](https://about.gitlab.com/pricing/) 10.6. @@ -256,7 +256,7 @@ For mirrored branches, enabling this option results in the loss of local changes To use this option, check the **Overwrite diverged branches** box when creating a repository mirror. -### Only mirror protected branches **[STARTER]** +### Only mirror protected branches **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3326) in > [GitLab Starter](https://about.gitlab.com/pricing/) 10.3. @@ -266,7 +266,7 @@ Non-protected branches are not mirrored and can diverge. To use this option, check the **Only mirror protected branches** box when creating a repository mirror. -### Hard failure **[STARTER]** +### Hard failure **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3117) in > [GitLab Starter](https://about.gitlab.com/pricing/) 10.2. @@ -280,25 +280,25 @@ failed. This will become visible in either the: When a project is hard failed, it will no longer get picked up for mirroring. A user can resume the project mirroring again by [Forcing an update](#forcing-an-update-core). -### Trigger update using API **[STARTER]** +### Trigger update using API **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3453) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.3. Pull mirroring uses polling to detect new branches and commits added upstream, often minutes -afterwards. If you notify GitLab by [API](https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project-starter), +afterwards. If you notify GitLab by [API](../api/projects.md#start-the-pull-mirroring-process-for-a-project-starter), updates will be pulled immediately. -For more information, see [Start the pull mirroring process for a Project](https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project-starter). +For more information, see [Start the pull mirroring process for a Project](../api/projects.md#start-the-pull-mirroring-process-for-a-project-starter). -## Forcing an update **[CORE]** +## Forcing an update **(CORE)** While mirrors are scheduled to update automatically, you can always force an update by using the update button which is available on the **Mirroring repositories** section of the **Repository Settings** page. ![Repository mirroring force update user interface](img/repository_mirroring_force_update.png) -## Bidirectional mirroring **[STARTER]** +## Bidirectional mirroring **(STARTER)** CAUTION: **Caution:** Bidirectional mirroring may cause conflicts. @@ -395,7 +395,7 @@ else fi ``` -### Mirroring with Perforce Helix via Git Fusion **[STARTER]** +### Mirroring with Perforce Helix via Git Fusion **(STARTER)** CAUTION: **Warning:** Bidirectional mirroring should not be used as a permanent configuration. Refer to diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md index 5068b5d4d20..fd67ea8ce87 100644 --- a/doc/workflow/shortcuts.md +++ b/doc/workflow/shortcuts.md @@ -85,7 +85,7 @@ You can see GitLab's keyboard shortcuts by using <kbd>shift</kbd> + <kbd>?</kbd> | <kbd>]</kbd> or <kbd>j</kbd> | Move to next file | | <kbd>[</kbd> or <kbd>k</kbd> | Move to previous file | -## Epics **[ULTIMATE]** +## Epics **(ULTIMATE)** | Keyboard Shortcut | Description | | ----------------- | ----------- | diff --git a/doc/workflow/timezone.md b/doc/workflow/timezone.md index da51c0f2c93..60a4d0f19de 100644 --- a/doc/workflow/timezone.md +++ b/doc/workflow/timezone.md @@ -2,13 +2,12 @@ The global time zone configuration parameter can be changed in `config/gitlab.yml`: -``` +```text # time_zone: 'UTC' ``` Uncomment and customize if you want to change the default time zone of the GitLab application. - ## Viewing available timezones To see all available time zones, run `bundle exec rake time:zones:all`. @@ -26,14 +25,13 @@ To obtain a list of timezones, log in to your GitLab application server and run To update, add the timezone that best applies to your location. For example: -``` +```ruby gitlab_rails['time_zone'] = 'America/New_York' ``` After adding the configuration parameter, reconfigure and restart your GitLab instance: -``` +```sh gitlab-ctl reconfigure gitlab-ctl restart ``` - diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md index f501a222cd5..1f8900c734b 100644 --- a/doc/workflow/todos.md +++ b/doc/workflow/todos.md @@ -33,7 +33,7 @@ A To Do displays on your To-Do List when: - You are `@mentioned` in the description or comment of an: - Issue - Merge Request - - Epic **[ULTIMATE]** + - Epic **(ULTIMATE)** - You are `@mentioned` in a comment on a commit - A job in the CI pipeline running for your merge request failed, but this job is not allowed to fail @@ -75,7 +75,7 @@ You can also add the following to your To-Do List by clicking the **Add a To Do* - Issue - Merge Request -- Epic **[ULTIMATE]** +- Epic **(ULTIMATE)** ![Adding a To Do from the issuable sidebar](img/todos_add_todo_sidebar.png) @@ -85,7 +85,7 @@ Any action to the following will mark the corresponding To Do as done: - Issue - Merge Request -- Epic **[ULTIMATE]** +- Epic **(ULTIMATE)** Actions that dismiss To-Do items include: @@ -104,7 +104,7 @@ To prevent other users from closing issues without you being notified, if someon - Issue - Merge request -- Epic **[ULTIMATE]** +- Epic **(ULTIMATE)** There is just one To Do for each of these, so mentioning a user a hundred times in an issue will only trigger one To Do. @@ -119,7 +119,7 @@ You can also mark a To Do as done by clicking the **Mark as done** button in the - Issue - Merge Request -- Epic **[ULTIMATE]** +- Epic **(ULTIMATE)** ![Mark as done from the issuable sidebar](img/todos_mark_done_sidebar.png) @@ -135,7 +135,7 @@ There are four kinds of filters you can use on your To-Do List. | Project | Filter by project | | Group | Filter by group | | Author | Filter by the author that triggered the To Do | -| Type | Filter by issue, merge request, or epic **[ULTIMATE]** | +| Type | Filter by issue, merge request, or epic **(ULTIMATE)** | | Action | Filter by the action that triggered the To Do | You can also filter by more than one of these at the same time. The possible Actions are `Any Action`, `Assigned`, `Mentioned`, `Added`, `Pipelines`, and `Directly Addressed`, [as described above](#what-triggers-a-to-do). diff --git a/jest.config.js b/jest.config.js index 84481642250..e4ac71a1a17 100644 --- a/jest.config.js +++ b/jest.config.js @@ -15,9 +15,18 @@ if (process.env.CI) { ]); } +let testMatch = ['<rootDir>/spec/frontend/**/*_spec.js', '<rootDir>/ee/spec/frontend/**/*_spec.js']; + +// workaround for eslint-import-resolver-jest only resolving in test files +// see https://github.com/JoinColony/eslint-import-resolver-jest#note +const isESLint = module.parent.filename.includes('/eslint-import-resolver-jest/'); +if (isESLint) { + testMatch = testMatch.map(path => path.replace('_spec.js', '')); +} + // eslint-disable-next-line import/no-commonjs module.exports = { - testMatch: ['<rootDir>/spec/frontend/**/*_spec.js', '<rootDir>/ee/spec/frontend/**/*_spec.js'], + testMatch, moduleFileExtensions: ['js', 'json', 'vue'], moduleNameMapper: { '^~(/.*)$': '<rootDir>/app/assets/javascripts$1', diff --git a/lib/api/api.rb b/lib/api/api.rb index 42499c5b41e..574934d7f90 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -111,6 +111,7 @@ module API mount ::API::Features mount ::API::Files mount ::API::GroupBoards + mount ::API::GroupClusters mount ::API::GroupLabels mount ::API::GroupMilestones mount ::API::Groups @@ -166,6 +167,7 @@ module API mount ::API::Templates mount ::API::Todos mount ::API::Triggers + mount ::API::UserCounts mount ::API::Users mount ::API::Variables mount ::API::Version diff --git a/lib/api/entities.rb b/lib/api/entities.rb index b9aa387ba61..0a9515f1dd2 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -201,6 +201,7 @@ module API # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555 projects_relation.preload(:project_feature, :route) .preload(:import_state, :tags) + .preload(:auto_devops) .preload(namespace: [:route, :owner]) end # rubocop: enable CodeReuse/ActiveRecord @@ -247,12 +248,20 @@ module API expose :container_registry_enabled # Expose old field names with the new permissions methods to keep API compatible + # TODO: remove in API v5, replaced by *_access_level expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) } expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) } expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) } expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } + expose(:issues_access_level) { |project, options| project.project_feature.string_access_level(:issues) } + expose(:repository_access_level) { |project, options| project.project_feature.string_access_level(:repository) } + expose(:merge_requests_access_level) { |project, options| project.project_feature.string_access_level(:merge_requests) } + expose(:wiki_access_level) { |project, options| project.project_feature.string_access_level(:wiki) } + expose(:builds_access_level) { |project, options| project.project_feature.string_access_level(:builds) } + expose(:snippets_access_level) { |project, options| project.project_feature.string_access_level(:snippets) } + expose :shared_runners_enabled expose :lfs_enabled?, as: :lfs_enabled expose :creator_id @@ -267,6 +276,12 @@ module API expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :ci_default_git_depth expose :public_builds, as: :public_jobs + expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options| + project.build_allow_git_fetch ? 'fetch' : 'clone' + end + expose :build_timeout + expose :auto_cancel_pending_pipelines + expose :build_coverage_regex expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) } expose :shared_with_groups do |project, options| SharedGroup.represent(project.project_group_links, options) @@ -280,6 +295,10 @@ module API options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project) } expose :external_authorization_classification_label + expose :auto_devops_enabled?, as: :auto_devops_enabled + expose :auto_devops_deploy_strategy do |project, options| + project.auto_devops.nil? ? 'continuous' : project.auto_devops.deploy_strategy + end # rubocop: disable CodeReuse/ActiveRecord def self.preload_relation(projects_relation, options = {}) @@ -289,6 +308,7 @@ module API # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555 super(projects_relation).preload(:group) .preload(:ci_cd_settings) + .preload(:auto_devops) .preload(project_group_links: { group: :route }, fork_network: :root_project, fork_network_member: :forked_from_project, @@ -491,7 +511,7 @@ module API end end - class ProjectEntity < Grape::Entity + class IssuableEntity < Grape::Entity expose :id, :iid expose(:project_id) { |entity| entity&.project.try(:id) } expose :title, :description @@ -544,7 +564,7 @@ module API end end - class IssueBasic < ProjectEntity + class IssueBasic < IssuableEntity expose :closed_at expose :closed_by, using: Entities::UserBasic @@ -650,14 +670,14 @@ module API end end - class MergeRequestSimple < ProjectEntity + class MergeRequestSimple < IssuableEntity expose :title expose :web_url do |merge_request, options| Gitlab::UrlBuilder.build(merge_request) end end - class MergeRequestBasic < ProjectEntity + class MergeRequestBasic < IssuableEntity expose :merged_by, using: Entities::UserBasic do |merge_request, _options| merge_request.metrics&.merged_by end @@ -1666,5 +1686,9 @@ module API class ClusterProject < Cluster expose :project, using: Entities::BasicProjectDetails end + + class ClusterGroup < Cluster + expose :group, using: Entities::BasicGroupDetails + end end end diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb new file mode 100644 index 00000000000..db0f8081140 --- /dev/null +++ b/lib/api/group_clusters.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +module API + class GroupClusters < Grape::API + include PaginationParams + + before { authenticate! } + + # EE::API::GroupClusters will + # override these methods + helpers do + params :create_params_ee do + end + + params :update_params_ee do + end + end + + params do + requires :id, type: String, desc: 'The ID of the group' + end + resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + desc 'Get all clusters from the group' do + success Entities::Cluster + end + params do + use :pagination + end + get ':id/clusters' do + authorize! :read_cluster, user_group + + present paginate(clusters_for_current_user), with: Entities::Cluster + end + + desc 'Get specific cluster for the group' do + success Entities::ClusterGroup + end + params do + requires :cluster_id, type: Integer, desc: 'The cluster ID' + end + get ':id/clusters/:cluster_id' do + authorize! :read_cluster, cluster + + present cluster, with: Entities::ClusterGroup + end + + desc 'Adds an existing cluster' do + success Entities::ClusterGroup + end + params do + requires :name, type: String, desc: 'Cluster name' + optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true' + optional :domain, type: String, desc: 'Cluster base domain' + optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true' + requires :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do + requires :api_url, type: String, allow_blank: false, desc: 'URL to access the Kubernetes API' + requires :token, type: String, desc: 'Token to authenticate against Kubernetes' + optional :ca_cert, type: String, desc: 'TLS certificate (needed if API is using a self-signed TLS certificate)' + optional :namespace, type: String, desc: 'Unique namespace related to Group' + optional :authorization_type, type: String, values: Clusters::Platforms::Kubernetes.authorization_types.keys, default: 'rbac', desc: 'Cluster authorization type, defaults to RBAC' + end + use :create_params_ee + end + post ':id/clusters/user' do + authorize! :add_cluster, user_group + + user_cluster = ::Clusters::CreateService + .new(current_user, create_cluster_user_params) + .execute + + if user_cluster.persisted? + present user_cluster, with: Entities::ClusterGroup + else + render_validation_error!(user_cluster) + end + end + + desc 'Update an existing cluster' do + success Entities::ClusterGroup + end + params do + requires :cluster_id, type: Integer, desc: 'The cluster ID' + optional :name, type: String, desc: 'Cluster name' + optional :domain, type: String, desc: 'Cluster base domain' + optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do + optional :api_url, type: String, desc: 'URL to access the Kubernetes API' + optional :token, type: String, desc: 'Token to authenticate against Kubernetes' + optional :ca_cert, type: String, desc: 'TLS certificate (needed if API is using a self-signed TLS certificate)' + optional :namespace, type: String, desc: 'Unique namespace related to Group' + end + use :update_params_ee + end + put ':id/clusters/:cluster_id' do + authorize! :update_cluster, cluster + + update_service = Clusters::UpdateService.new(current_user, update_cluster_params) + + if update_service.execute(cluster) + present cluster, with: Entities::ClusterGroup + else + render_validation_error!(cluster) + end + end + + desc 'Remove a cluster' do + success Entities::ClusterGroup + end + params do + requires :cluster_id, type: Integer, desc: 'The Cluster ID' + end + delete ':id/clusters/:cluster_id' do + authorize! :admin_cluster, cluster + + destroy_conditionally!(cluster) + end + end + + helpers do + def clusters_for_current_user + @clusters_for_current_user ||= ClustersFinder.new(user_group, current_user, :all).execute + end + + def cluster + @cluster ||= clusters_for_current_user.find(params[:cluster_id]) + end + + def create_cluster_user_params + declared_params.merge({ + provider_type: :user, + platform_type: :kubernetes, + clusterable: user_group + }) + end + + def update_cluster_params + declared_params(include_missing: false).without(:cluster_id) + end + end + end +end diff --git a/lib/api/helpers/graphql_helpers.rb b/lib/api/helpers/graphql_helpers.rb index 94010ab1bc2..bd60470fbd6 100644 --- a/lib/api/helpers/graphql_helpers.rb +++ b/lib/api/helpers/graphql_helpers.rb @@ -7,8 +7,6 @@ module API # should be in app/graphql/ or lib/gitlab/graphql/ module GraphqlHelpers def conditionally_graphql!(fallback:, query:, context: {}, transform: nil) - return fallback.call unless Feature.enabled?(:graphql) - result = GitlabSchema.execute(query, context: context) if transform diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb index c318f5b9127..9afe6c5b027 100644 --- a/lib/api/helpers/internal_helpers.rb +++ b/lib/api/helpers/internal_helpers.rb @@ -72,7 +72,7 @@ module API result == 'PONG' rescue => e - Rails.logger.warn("GitLab: An unexpected error occurred in pinging to Redis: #{e}") + Rails.logger.warn("GitLab: An unexpected error occurred in pinging to Redis: #{e}") # rubocop:disable Gitlab/RailsLogger false end diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb index 2a9b17ad22a..71bbc218f94 100644 --- a/lib/api/helpers/pagination.rb +++ b/lib/api/helpers/pagination.rb @@ -205,7 +205,9 @@ module API limited_total_count = pagination_data.total_count_with_limit if limited_total_count > Kaminari::ActiveRecordRelationMethods::MAX_COUNT_LIMIT - pagination_data.without_count + # The call to `total_count_with_limit` memoizes `@arel` because of a call to `references_eager_loaded_tables?` + # We need to call `reset` because `without_count` relies on `@arel` being unmemoized + pagination_data.reset.without_count else pagination_data end diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index f242f1fea0e..0e21a7a66fd 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -8,12 +8,26 @@ module API params :optional_project_params_ce do optional :description, type: String, desc: 'The description of the project' + optional :build_git_strategy, type: String, values: %w(fetch clone), desc: 'The Git strategy. Defaults to `fetch`' + optional :build_timeout, type: Integer, desc: 'Build timeout' + optional :auto_cancel_pending_pipelines, type: String, values: %w(disabled enabled), desc: 'Auto-cancel pending pipelines' + optional :build_coverage_regex, type: String, desc: 'Test coverage parsing' optional :ci_config_path, type: String, desc: 'The path to CI config file. Defaults to `.gitlab-ci.yml`' + + # TODO: remove in API v5, replaced by *_access_level optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled' optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled' optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled' optional :jobs_enabled, type: Boolean, desc: 'Flag indication if jobs are enabled' optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled' + + optional :issues_access_level, type: String, values: %w(disabled private enabled), desc: 'Issues access level. One of `disabled`, `private` or `enabled`' + optional :repository_access_level, type: String, values: %w(disabled private enabled), desc: 'Repository access level. One of `disabled`, `private` or `enabled`' + optional :merge_requests_access_level, type: String, values: %w(disabled private enabled), desc: 'Merge requests access level. One of `disabled`, `private` or `enabled`' + optional :wiki_access_level, type: String, values: %w(disabled private enabled), desc: 'Wiki access level. One of `disabled`, `private` or `enabled`' + optional :builds_access_level, type: String, values: %w(disabled private enabled), desc: 'Builds access level. One of `disabled`, `private` or `enabled`' + optional :snippets_access_level, type: String, values: %w(disabled private enabled), desc: 'Snippets access level. One of `disabled`, `private` or `enabled`' + optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project' optional :resolve_outdated_diff_discussions, type: Boolean, desc: 'Automatically resolve merge request diffs discussions on lines changed with a push' optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project' @@ -30,6 +44,8 @@ module API optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md" optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project' optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning' + optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled' + optional :auto_devops_deploy_strategy, type: String, values: %w(continuous manual timed_incremental), desc: 'Auto Deploy strategy' end params :optional_project_params_ee do @@ -48,15 +64,20 @@ module API def self.update_params_at_least_one_of [ - :jobs_enabled, - :resolve_outdated_diff_discussions, + :auto_devops_enabled, + :auto_devops_deploy_strategy, + :auto_cancel_pending_pipelines, + :build_coverage_regex, + :build_git_strategy, + :build_timeout, + :builds_access_level, :ci_config_path, :container_registry_enabled, :default_branch, :description, - :issues_enabled, + :issues_access_level, :lfs_enabled, - :merge_requests_enabled, + :merge_requests_access_level, :merge_method, :name, :only_allow_merge_if_all_discussions_are_resolved, @@ -64,14 +85,24 @@ module API :path, :printing_merge_request_link_enabled, :public_builds, + :repository_access_level, :request_access_enabled, + :resolve_outdated_diff_discussions, :shared_runners_enabled, - :snippets_enabled, + :snippets_access_level, :tag_list, :visibility, - :wiki_enabled, + :wiki_access_level, :avatar, - :external_authorization_classification_label + :external_authorization_classification_label, + + # TODO: remove in API v5, replaced by *_access_level + :issues_enabled, + :jobs_enabled, + :merge_requests_enabled, + :wiki_enabled, + :jobs_enabled, + :snippets_enabled ] end end diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb index ff73a49d5e8..100463fcb95 100644 --- a/lib/api/helpers/runner.rb +++ b/lib/api/helpers/runner.rb @@ -7,8 +7,7 @@ module API JOB_TOKEN_PARAM = :token def runner_registration_token_valid? - ActiveSupport::SecurityUtils.variable_size_secure_compare(params[:token], - Gitlab::CurrentSettings.runners_registration_token) + ActiveSupport::SecurityUtils.secure_compare(params[:token], Gitlab::CurrentSettings.runners_registration_token) end def authenticate_runner! diff --git a/lib/api/import_github.rb b/lib/api/import_github.rb index e7504051808..21d4928193e 100644 --- a/lib/api/import_github.rb +++ b/lib/api/import_github.rb @@ -28,7 +28,7 @@ module API desc 'Import a GitHub project' do detail 'This feature was introduced in GitLab 11.3.4.' - success Entities::ProjectEntity + success ::ProjectEntity end params do requires :personal_access_token, type: String, desc: 'GitHub personal access token' diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb index dcc8d94fb79..4f093e9be08 100644 --- a/lib/api/project_clusters.rb +++ b/lib/api/project_clusters.rb @@ -65,7 +65,7 @@ module API use :create_params_ee end post ':id/clusters/user' do - authorize! :add_cluster, user_project, 'Instance does not support multiple Kubernetes clusters' + authorize! :add_cluster, user_project user_cluster = ::Clusters::CreateService .new(current_user, create_cluster_user_params) diff --git a/lib/api/user_counts.rb b/lib/api/user_counts.rb new file mode 100644 index 00000000000..8df4b381bbf --- /dev/null +++ b/lib/api/user_counts.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module API + class UserCounts < Grape::API + resource :user_counts do + desc 'Return the user specific counts' do + detail 'Open MR Count' + end + get do + unauthorized! unless current_user + + { + merge_requests: current_user.assigned_open_merge_requests_count + } + end + end + end +end diff --git a/lib/banzai/filter/ascii_doc_sanitization_filter.rb b/lib/banzai/filter/ascii_doc_sanitization_filter.rb new file mode 100644 index 00000000000..a78bb60103c --- /dev/null +++ b/lib/banzai/filter/ascii_doc_sanitization_filter.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Banzai + module Filter + # Sanitize HTML produced by AsciiDoc/Asciidoctor. + # + # Extends Banzai::Filter::BaseSanitizationFilter with specific rules. + class AsciiDocSanitizationFilter < Banzai::Filter::BaseSanitizationFilter + # Classes used by Asciidoctor to style components + ADMONITION_CLASSES = %w(fa icon-note icon-tip icon-warning icon-caution icon-important).freeze + CALLOUT_CLASSES = ['conum'].freeze + CHECKLIST_CLASSES = %w(fa fa-check-square-o fa-square-o).freeze + + LIST_CLASSES = %w(checklist none no-bullet unnumbered unstyled).freeze + + ELEMENT_CLASSES_WHITELIST = { + span: %w(big small underline overline line-through).freeze, + div: ['admonitionblock'].freeze, + td: ['icon'].freeze, + i: ADMONITION_CLASSES + CALLOUT_CLASSES + CHECKLIST_CLASSES, + ul: LIST_CLASSES, + ol: LIST_CLASSES + }.freeze + + def customize_whitelist(whitelist) + # Allow marks + whitelist[:elements].push('mark') + + # Allow any classes in `span`, `i`, `div`, `td`, `ul` and `ol` elements + # but then remove any unknown classes + whitelist[:attributes]['span'] = %w(class) + whitelist[:attributes]['div'].push('class') + whitelist[:attributes]['td'] = %w(class) + whitelist[:attributes]['i'] = %w(class) + whitelist[:attributes]['ul'] = %w(class) + whitelist[:attributes]['ol'] = %w(class) + whitelist[:transformers].push(self.class.remove_element_classes) + + whitelist + end + + class << self + def remove_element_classes + lambda do |env| + node = env[:node] + + return unless (classes_whitelist = ELEMENT_CLASSES_WHITELIST[node.name.to_sym]) + return unless node.has_attribute?('class') + + classes = node['class'].strip.split(' ') + allowed_classes = (classes & classes_whitelist) + if allowed_classes.empty? + node.remove_attribute('class') + else + node['class'] = allowed_classes.join(' ') + end + end + end + end + end + end +end diff --git a/lib/banzai/filter/base_sanitization_filter.rb b/lib/banzai/filter/base_sanitization_filter.rb new file mode 100644 index 00000000000..420e92cb1e8 --- /dev/null +++ b/lib/banzai/filter/base_sanitization_filter.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +module Banzai + module Filter + # Sanitize HTML produced by markup languages (Markdown, AsciiDoc...). + # Specific rules are implemented in dedicated filters: + # + # - Banzai::Filter::SanitizationFilter (Markdown) + # - Banzai::Filter::AsciiDocSanitizationFilter (AsciiDoc/Asciidoctor) + # + # Extends HTML::Pipeline::SanitizationFilter with common rules. + class BaseSanitizationFilter < HTML::Pipeline::SanitizationFilter + include Gitlab::Utils::StrongMemoize + + UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze + + def whitelist + strong_memoize(:whitelist) do + whitelist = super.deep_dup + + # Allow span elements + whitelist[:elements].push('span') + + # Allow data-math-style attribute in order to support LaTeX formatting + whitelist[:attributes]['code'] = %w(data-math-style) + whitelist[:attributes]['pre'] = %w(data-math-style) + + # Allow html5 details/summary elements + whitelist[:elements].push('details') + whitelist[:elements].push('summary') + + # Allow abbr elements with title attribute + whitelist[:elements].push('abbr') + whitelist[:attributes]['abbr'] = %w(title) + + # Disallow `name` attribute globally, allow on `a` + whitelist[:attributes][:all].delete('name') + whitelist[:attributes]['a'].push('name') + + # Allow any protocol in `a` elements + # and then remove links with unsafe protocols + whitelist[:protocols].delete('a') + whitelist[:transformers].push(self.class.remove_unsafe_links) + + # Remove `rel` attribute from `a` elements + whitelist[:transformers].push(self.class.remove_rel) + + customize_whitelist(whitelist) + end + end + + def customize_whitelist(whitelist) + raise NotImplementedError + end + + class << self + def remove_unsafe_links + lambda do |env| + node = env[:node] + + return unless node.name == 'a' + return unless node.has_attribute?('href') + + begin + node['href'] = node['href'].strip + uri = Addressable::URI.parse(node['href']) + + return unless uri.scheme + + # Remove all invalid scheme characters before checking against the + # list of unsafe protocols. + # + # See https://tools.ietf.org/html/rfc3986#section-3.1 + scheme = uri.scheme + .strip + .downcase + .gsub(/[^A-Za-z0-9\+\.\-]+/, '') + + node.remove_attribute('href') if UNSAFE_PROTOCOLS.include?(scheme) + rescue Addressable::URI::InvalidURIError + node.remove_attribute('href') + end + end + end + + def remove_rel + lambda do |env| + if env[:node_name] == 'a' + env[:node].remove_attribute('rel') + end + end + end + end + end + end +end diff --git a/lib/banzai/filter/inline_embeds_filter.rb b/lib/banzai/filter/inline_embeds_filter.rb new file mode 100644 index 00000000000..97394fd8f82 --- /dev/null +++ b/lib/banzai/filter/inline_embeds_filter.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Banzai + module Filter + # HTML filter that inserts a node for each occurence of + # a given link format. To transform references to DB + # resources in place, prefer to inherit from AbstractReferenceFilter. + class InlineEmbedsFilter < HTML::Pipeline::Filter + # Find every relevant link, create a new node based on + # the link, and insert this node after any html content + # surrounding the link. + def call + return doc unless Feature.enabled?(:gfm_embedded_metrics, context[:project]) + + doc.xpath(xpath_search).each do |node| + next unless element = element_to_embed(node) + + # We want this to follow any surrounding content. For example, + # if a link is inline in a paragraph. + node.parent.children.last.add_next_sibling(element) + end + + doc + end + + # Implement in child class. + # + # Return a Nokogiri::XML::Element to embed in the + # markdown. + def create_element(params) + end + + # Implement in child class unless overriding #embed_params + # + # Returns the regex pattern used to filter + # to only matching urls. + def link_pattern + end + + # Returns the xpath query string used to select nodes + # from the html document on which the embed is based. + # + # Override to select nodes other than links. + def xpath_search + 'descendant-or-self::a[@href]' + end + + # Creates a new element based on the parameters + # obtained from the target link + def element_to_embed(node) + return unless params = embed_params(node) + + create_element(params) + end + + # Returns a hash of named parameters based on the + # provided regex with string keys. + # + # Override to select nodes other than links. + def embed_params(node) + url = node['href'] + + link_pattern.match(url) { |m| m.named_captures } + end + end + end +end diff --git a/lib/banzai/filter/inline_metrics_filter.rb b/lib/banzai/filter/inline_metrics_filter.rb new file mode 100644 index 00000000000..0120cc37d6f --- /dev/null +++ b/lib/banzai/filter/inline_metrics_filter.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Banzai + module Filter + # HTML filter that inserts a placeholder element for each + # reference to a metrics dashboard. + class InlineMetricsFilter < Banzai::Filter::InlineEmbedsFilter + # Placeholder element for the frontend to use as an + # injection point for charts. + def create_element(params) + doc.document.create_element( + 'div', + class: 'js-render-metrics', + 'data-dashboard-url': metrics_dashboard_url(params) + ) + end + + # Endpoint FE should hit to collect the appropriate + # chart information + def metrics_dashboard_url(params) + Gitlab::Metrics::Dashboard::Url.build_dashboard_url( + params['namespace'], + params['project'], + params['environment'], + embedded: true + ) + end + + # Search params for selecting metrics links. A few + # simple checks is enough to boost performance without + # the cost of doing a full regex match. + def xpath_search + "descendant-or-self::a[contains(@href,'metrics') and \ + starts-with(@href, '#{Gitlab.config.gitlab.url}')]" + end + + # Regular expression matching metrics urls + def link_pattern + Gitlab::Metrics::Dashboard::Url.regex + end + end + end +end diff --git a/lib/banzai/filter/inline_metrics_redactor_filter.rb b/lib/banzai/filter/inline_metrics_redactor_filter.rb new file mode 100644 index 00000000000..ff91be2cbb7 --- /dev/null +++ b/lib/banzai/filter/inline_metrics_redactor_filter.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +module Banzai + module Filter + # HTML filter that removes embeded elements that the current user does + # not have permission to view. + class InlineMetricsRedactorFilter < HTML::Pipeline::Filter + include Gitlab::Utils::StrongMemoize + + METRICS_CSS_CLASS = '.js-render-metrics' + + # Finds all embeds based on the css class the FE + # uses to identify the embedded content, removing + # only unnecessary nodes. + def call + return doc unless Feature.enabled?(:gfm_embedded_metrics, context[:project]) + + nodes.each do |node| + path = paths_by_node[node] + user_has_access = user_access_by_path[path] + + node.remove unless user_has_access + end + + doc + end + + private + + def user + context[:current_user] + end + + # Returns all nodes which the FE will identify as + # a metrics dashboard placeholder element + # + # @return [Nokogiri::XML::NodeSet] + def nodes + @nodes ||= doc.css(METRICS_CSS_CLASS) + end + + # Maps a node to the full path of a project. + # Memoized so we only need to run the regex to get + # the project full path from the url once per node. + # + # @return [Hash<Nokogiri::XML::Node, String>] + def paths_by_node + strong_memoize(:paths_by_node) do + nodes.each_with_object({}) do |node, paths| + paths[node] = path_for_node(node) + end + end + end + + # Gets a project's full_path from the dashboard url + # in the placeholder node. The FE will use the attr + # `data-dashboard-url`, so we want to check against that + # attribute directly in case a user has manually + # created a metrics element (rather than supporting + # an alternate attr in InlineMetricsFilter). + # + # @return [String] + def path_for_node(node) + url = node.attribute('data-dashboard-url').to_s + + Gitlab::Metrics::Dashboard::Url.regex.match(url) do |m| + "#{$~[:namespace]}/#{$~[:project]}" + end + end + + # Maps a project's full path to a Project object. + # Contains all of the Projects referenced in the + # metrics placeholder elements of the current document + # + # @return [Hash<String, Project>] + def projects_by_path + strong_memoize(:projects_by_path) do + Project.eager_load(:route, namespace: [:route]) + .where_full_path_in(paths_by_node.values.uniq) + .index_by(&:full_path) + end + end + + # Returns a mapping representing whether the current user + # has permission to view the metrics for the project. + # Determined in a batch + # + # @return [Hash<Project, Boolean>] + def user_access_by_path + strong_memoize(:user_access_by_path) do + projects_by_path.each_with_object({}) do |(path, project), access| + access[path] = Ability.allowed?(user, :read_environment, project) + end + end + end + end + end +end diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index a4a06eae7b7..f57e57890f8 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -2,23 +2,13 @@ module Banzai module Filter - # Sanitize HTML + # Sanitize HTML produced by Markdown. # - # Extends HTML::Pipeline::SanitizationFilter with a custom whitelist. - class SanitizationFilter < HTML::Pipeline::SanitizationFilter - include Gitlab::Utils::StrongMemoize - - UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze + # Extends Banzai::Filter::BaseSanitizationFilter with specific rules. + class SanitizationFilter < Banzai::Filter::BaseSanitizationFilter + # Styles used by Markdown for table alignment TABLE_ALIGNMENT_PATTERN = /text-align: (?<alignment>center|left|right)/.freeze - def whitelist - strong_memoize(:whitelist) do - customize_whitelist(super.deep_dup) - end - end - - private - def customize_whitelist(whitelist) # Allow table alignment; we whitelist specific text-align values in a # transformer below @@ -26,36 +16,9 @@ module Banzai whitelist[:attributes]['td'] = %w(style) whitelist[:css] = { properties: ['text-align'] } - # Allow span elements - whitelist[:elements].push('span') - - # Allow data-math-style attribute in order to support LaTeX formatting - whitelist[:attributes]['code'] = %w(data-math-style) - whitelist[:attributes]['pre'] = %w(data-math-style) - - # Allow html5 details/summary elements - whitelist[:elements].push('details') - whitelist[:elements].push('summary') - - # Allow abbr elements with title attribute - whitelist[:elements].push('abbr') - whitelist[:attributes]['abbr'] = %w(title) - # Allow the 'data-sourcepos' from CommonMark on all elements whitelist[:attributes][:all].push('data-sourcepos') - # Disallow `name` attribute globally, allow on `a` - whitelist[:attributes][:all].delete('name') - whitelist[:attributes]['a'].push('name') - - # Allow any protocol in `a` elements - # and then remove links with unsafe protocols - whitelist[:protocols].delete('a') - whitelist[:transformers].push(self.class.remove_unsafe_links) - - # Remove `rel` attribute from `a` elements - whitelist[:transformers].push(self.class.remove_rel) - # Remove any `style` properties not required for table alignment whitelist[:transformers].push(self.class.remove_unsafe_table_style) @@ -69,43 +32,6 @@ module Banzai end class << self - def remove_unsafe_links - lambda do |env| - node = env[:node] - - return unless node.name == 'a' - return unless node.has_attribute?('href') - - begin - node['href'] = node['href'].strip - uri = Addressable::URI.parse(node['href']) - - return unless uri.scheme - - # Remove all invalid scheme characters before checking against the - # list of unsafe protocols. - # - # See https://tools.ietf.org/html/rfc3986#section-3.1 - scheme = uri.scheme - .strip - .downcase - .gsub(/[^A-Za-z0-9\+\.\-]+/, '') - - node.remove_attribute('href') if UNSAFE_PROTOCOLS.include?(scheme) - rescue Addressable::URI::InvalidURIError - node.remove_attribute('href') - end - end - end - - def remove_rel - lambda do |env| - if env[:node_name] == 'a' - env[:node].remove_attribute('rel') - end - end - end - def remove_unsafe_table_style lambda do |env| node = env[:node] diff --git a/lib/banzai/pipeline/ascii_doc_pipeline.rb b/lib/banzai/pipeline/ascii_doc_pipeline.rb index 6be489c6572..d25b74b23b2 100644 --- a/lib/banzai/pipeline/ascii_doc_pipeline.rb +++ b/lib/banzai/pipeline/ascii_doc_pipeline.rb @@ -5,7 +5,7 @@ module Banzai class AsciiDocPipeline < BasePipeline def self.filters FilterArray[ - Filter::SanitizationFilter, + Filter::AsciiDocSanitizationFilter, Filter::SyntaxHighlightFilter, Filter::ExternalLinkFilter, Filter::PlantumlFilter, diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index d67f461be57..2c1006f708a 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -25,6 +25,7 @@ module Banzai Filter::VideoLinkFilter, Filter::ImageLazyLoadFilter, Filter::ImageLinkFilter, + Filter::InlineMetricsFilter, Filter::TableOfContentsFilter, Filter::AutolinkFilter, Filter::ExternalLinkFilter, diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb index 7eaad6d7560..5c199453638 100644 --- a/lib/banzai/pipeline/post_process_pipeline.rb +++ b/lib/banzai/pipeline/post_process_pipeline.rb @@ -13,6 +13,7 @@ module Banzai def self.internal_link_filters [ Filter::RedactorFilter, + Filter::InlineMetricsRedactorFilter, Filter::RelativeLinkFilter, Filter::IssuableStateFilter, Filter::SuggestionFilter diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb index c7239a5eaa6..81f32ef5bcf 100644 --- a/lib/banzai/renderer.rb +++ b/lib/banzai/renderer.rb @@ -55,11 +55,16 @@ module Banzai # Perform multiple render from an Array of Markdown String into an # Array of HTML-safe String of HTML. # - # As the rendered Markdown String can be already cached read all the data - # from the cache using Rails.cache.read_multi operation. If the Markdown String - # is not in the cache or it's not cacheable (no cache_key entry is provided in - # the context) the Markdown String is rendered and stored in the cache so the - # next render call gets the rendered HTML-safe String from the cache. + # The redis cache is completely obviated if we receive a `:rendered` key in the + # context, as it is assumed the item has been pre-rendered somewhere else and there + # is no need to cache it. + # + # If no `:rendered` key is present in the context, as the rendered Markdown String + # can be already cached, read all the data from the cache using + # Rails.cache.read_multi operation. If the Markdown String is not in the cache + # or it's not cacheable (no cache_key entry is provided in the context) the + # Markdown String is rendered and stored in the cache so the next render call + # gets the rendered HTML-safe String from the cache. # # For further explanation see #render method comments. # @@ -76,19 +81,34 @@ module Banzai # => [{ text: '### Hello', # context: { cache_key: [note, :note] } }] def self.cache_collection_render(texts_and_contexts) - items_collection = texts_and_contexts.each_with_index do |item, index| + items_collection = texts_and_contexts.each do |item| context = item[:context] - cache_key = full_cache_multi_key(context.delete(:cache_key), context[:pipeline]) - item[:cache_key] = cache_key if cache_key + if context.key?(:rendered) + item[:rendered] = context.delete(:rendered) + else + # If the attribute didn't come in pre-rendered, let's prepare it for caching it in redis + cache_key = full_cache_multi_key(context.delete(:cache_key), context[:pipeline]) + item[:cache_key] = cache_key if cache_key + end end - cacheable_items, non_cacheable_items = items_collection.partition { |item| item.key?(:cache_key) } + cacheable_items, non_cacheable_items = items_collection.group_by do |item| + if item.key?(:rendered) + # We're not really doing anything here as these don't need any processing, but leaving it just in case + # as they could have a cache_key and we don't want them to be re-rendered + :rendered + elsif item.key?(:cache_key) + :cacheable + else + :non_cacheable + end + end.values_at(:cacheable, :non_cacheable) items_in_cache = [] items_not_in_cache = [] - unless cacheable_items.empty? + if cacheable_items.present? items_in_cache = Rails.cache.read_multi(*cacheable_items.map { |item| item[:cache_key] }) items_not_in_cache = cacheable_items.reject do |item| item[:rendered] = items_in_cache[item[:cache_key]] @@ -96,7 +116,7 @@ module Banzai end end - (items_not_in_cache + non_cacheable_items).each do |item| + (items_not_in_cache + Array.wrap(non_cacheable_items)).each do |item| item[:rendered] = render(item[:text], item[:context]) Rails.cache.write(item[:cache_key], item[:rendered]) if item[:cache_key] end diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 4317992d933..82e0c7ceeaa 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -94,6 +94,7 @@ module Gitlab end end + # rubocop:disable Gitlab/RailsLogger def rate_limit!(ip, success:, login:) rate_limiter = Gitlab::Auth::IpRateLimiter.new(ip) return unless rate_limiter.enabled? @@ -114,6 +115,7 @@ module Gitlab end end end + # rubocop:enable Gitlab/RailsLogger private diff --git a/lib/gitlab/auth/ldap/adapter.rb b/lib/gitlab/auth/ldap/adapter.rb index 15b9d5ad6e9..bcb0ecccdf9 100644 --- a/lib/gitlab/auth/ldap/adapter.rb +++ b/lib/gitlab/auth/ldap/adapter.rb @@ -55,7 +55,7 @@ module Gitlab response = ldap.get_operation_result unless response.code.zero? - Rails.logger.warn("LDAP search error: #{response.message}") + Rails.logger.warn("LDAP search error: #{response.message}") # rubocop:disable Gitlab/RailsLogger end [] @@ -67,7 +67,7 @@ module Gitlab retries += 1 error_message = connection_error_message(error) - Rails.logger.warn(error_message) + Rails.logger.warn(error_message) # rubocop:disable Gitlab/RailsLogger if retries < MAX_SEARCH_RETRIES renew_connection_adapter diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb index 47d63eb53cf..354f91306f9 100644 --- a/lib/gitlab/auth/ldap/config.rb +++ b/lib/gitlab/auth/ldap/config.rb @@ -240,7 +240,7 @@ module Gitlab begin custom_options[:cert] = OpenSSL::X509::Certificate.new(custom_options[:cert]) rescue OpenSSL::X509::CertificateError => e - Rails.logger.error "LDAP TLS Options 'cert' is invalid for provider #{provider}: #{e.message}" + Rails.logger.error "LDAP TLS Options 'cert' is invalid for provider #{provider}: #{e.message}" # rubocop:disable Gitlab/RailsLogger end end @@ -248,7 +248,7 @@ module Gitlab begin custom_options[:key] = OpenSSL::PKey.read(custom_options[:key]) rescue OpenSSL::PKey::PKeyError => e - Rails.logger.error "LDAP TLS Options 'key' is invalid for provider #{provider}: #{e.message}" + Rails.logger.error "LDAP TLS Options 'key' is invalid for provider #{provider}: #{e.message}" # rubocop:disable Gitlab/RailsLogger end end diff --git a/lib/gitlab/auth/ldap/person.rb b/lib/gitlab/auth/ldap/person.rb index c1517222956..11a4052a109 100644 --- a/lib/gitlab/auth/ldap/person.rb +++ b/lib/gitlab/auth/ldap/person.rb @@ -45,7 +45,7 @@ module Gitlab def self.normalize_dn(dn) ::Gitlab::Auth::LDAP::DN.new(dn).to_normalized_s rescue ::Gitlab::Auth::LDAP::DN::FormatError => e - Rails.logger.info("Returning original DN \"#{dn}\" due to error during normalization attempt: #{e.message}") + Rails.logger.info("Returning original DN \"#{dn}\" due to error during normalization attempt: #{e.message}") # rubocop:disable Gitlab/RailsLogger dn end @@ -57,13 +57,13 @@ module Gitlab def self.normalize_uid(uid) ::Gitlab::Auth::LDAP::DN.normalize_value(uid) rescue ::Gitlab::Auth::LDAP::DN::FormatError => e - Rails.logger.info("Returning original UID \"#{uid}\" due to error during normalization attempt: #{e.message}") + Rails.logger.info("Returning original UID \"#{uid}\" due to error during normalization attempt: #{e.message}") # rubocop:disable Gitlab/RailsLogger uid end def initialize(entry, provider) - Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" } + Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" } # rubocop:disable Gitlab/RailsLogger @entry = entry @provider = provider end diff --git a/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb b/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb index cb2bdea755c..c912628d0fc 100644 --- a/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb +++ b/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb @@ -9,7 +9,7 @@ module Gitlab end def perform(start_id, stop_id) - Rails.logger.info("Setting commits_count for merge request diffs: #{start_id} - #{stop_id}") + Rails.logger.info("Setting commits_count for merge request diffs: #{start_id} - #{stop_id}") # rubocop:disable Gitlab/RailsLogger update = ' commits_count = ( diff --git a/lib/gitlab/background_migration/archive_legacy_traces.rb b/lib/gitlab/background_migration/archive_legacy_traces.rb index 7ee783b8489..3c26982729d 100644 --- a/lib/gitlab/background_migration/archive_legacy_traces.rb +++ b/lib/gitlab/background_migration/archive_legacy_traces.rb @@ -14,7 +14,7 @@ module Gitlab build.trace.archive! rescue => e - Rails.logger.error "Failed to archive live trace. id: #{build.id} message: #{e.message}" + Rails.logger.error "Failed to archive live trace. id: #{build.id} message: #{e.message}" # rubocop:disable Gitlab/RailsLogger end end end diff --git a/lib/gitlab/background_migration/calculate_wiki_sizes.rb b/lib/gitlab/background_migration/calculate_wiki_sizes.rb index 886c41a2b9d..e62f5edd0e7 100644 --- a/lib/gitlab/background_migration/calculate_wiki_sizes.rb +++ b/lib/gitlab/background_migration/calculate_wiki_sizes.rb @@ -10,7 +10,7 @@ module Gitlab .includes(project: [:route, :group, namespace: [:owner]]).find_each do |statistics| statistics.refresh!(only: [:wiki_size]) rescue => e - Rails.logger.error "Failed to update wiki statistics. id: #{statistics.id} message: #{e.message}" + Rails.logger.error "Failed to update wiki statistics. id: #{statistics.id} message: #{e.message}" # rubocop:disable Gitlab/RailsLogger end end end diff --git a/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb b/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb index 0e93b2cb2fa..6046d33aeac 100644 --- a/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb +++ b/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb @@ -32,7 +32,7 @@ module Gitlab ) end rescue => e - Rails.logger.error "Failed to update pages domain certificate valid time. id: #{domain.id}, message: #{e.message}" + Rails.logger.error "Failed to update pages domain certificate valid time. id: #{domain.id}, message: #{e.message}" # rubocop:disable Gitlab/RailsLogger end end end diff --git a/lib/gitlab/background_migration/fix_cross_project_label_links.rb b/lib/gitlab/background_migration/fix_cross_project_label_links.rb index bf5d7f5f322..20a98c8e141 100644 --- a/lib/gitlab/background_migration/fix_cross_project_label_links.rb +++ b/lib/gitlab/background_migration/fix_cross_project_label_links.rb @@ -108,7 +108,7 @@ module Gitlab next unless matching_label - Rails.logger.info "#{resource.class.name.demodulize} #{resource.id}: replacing #{label.label_id} with #{matching_label.id}" + Rails.logger.info "#{resource.class.name.demodulize} #{resource.id}: replacing #{label.label_id} with #{matching_label.id}" # rubocop:disable Gitlab/RailsLogger LabelLink.update(label.label_link_id, label_id: matching_label.id) end end diff --git a/lib/gitlab/background_migration/fix_user_namespace_names.rb b/lib/gitlab/background_migration/fix_user_namespace_names.rb new file mode 100644 index 00000000000..1a207121be0 --- /dev/null +++ b/lib/gitlab/background_migration/fix_user_namespace_names.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + # This migration fixes the namespaces.name for all user-namespaces that have names + # that aren't equal to the users name. + # Then it uses the updated names of the namespaces to update the associated routes + # For more info see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23272 + class FixUserNamespaceNames + def perform(from_id, to_id) + fix_namespace_names(from_id, to_id) + fix_namespace_route_names(from_id, to_id) + end + + def fix_namespace_names(from_id, to_id) + ActiveRecord::Base.connection.execute <<~UPDATE_NAMESPACES + WITH namespaces_to_update AS ( + SELECT + namespaces.id, + users.name AS correct_name + FROM + namespaces + INNER JOIN users ON namespaces.owner_id = users.id + WHERE + namespaces.type IS NULL + AND namespaces.id BETWEEN #{from_id} AND #{to_id} + AND namespaces.name != users.name + ) + UPDATE + namespaces + SET + name = correct_name + FROM + namespaces_to_update + WHERE + namespaces.id = namespaces_to_update.id + UPDATE_NAMESPACES + end + + def fix_namespace_route_names(from_id, to_id) + ActiveRecord::Base.connection.execute <<~ROUTES_UPDATE + WITH routes_to_update AS ( + SELECT + routes.id, + users.name AS correct_name + FROM + routes + INNER JOIN namespaces ON routes.source_id = namespaces.id + INNER JOIN users ON namespaces.owner_id = users.id + WHERE + namespaces.type IS NULL + AND routes.source_type = 'Namespace' + AND namespaces.id BETWEEN #{from_id} AND #{to_id} + AND (routes.name != users.name OR routes.name IS NULL) + ) + UPDATE + routes + SET + name = correct_name + FROM + routes_to_update + WHERE + routes_to_update.id = routes.id + ROUTES_UPDATE + end + end + end +end diff --git a/lib/gitlab/background_migration/fix_user_project_route_names.rb b/lib/gitlab/background_migration/fix_user_project_route_names.rb new file mode 100644 index 00000000000..b84ff32e712 --- /dev/null +++ b/lib/gitlab/background_migration/fix_user_project_route_names.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + # This migration fixes the routes.name for all user-projects that have names + # that don't start with the users name. + # For more info see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23272 + class FixUserProjectRouteNames + def perform(from_id, to_id) + ActiveRecord::Base.connection.execute <<~ROUTES_UPDATE + WITH routes_to_update AS ( + SELECT + routes.id, + users.name || ' / ' || projects.name AS correct_name + FROM + routes + INNER JOIN projects ON routes.source_id = projects.id + INNER JOIN namespaces ON projects.namespace_id = namespaces.id + INNER JOIN users ON namespaces.owner_id = users.id + WHERE + routes.source_type = 'Project' + AND routes.id BETWEEN #{from_id} AND #{to_id} + AND namespaces.type IS NULL + AND (routes.name NOT LIKE users.name || '%' OR routes.name IS NULL) + ) + UPDATE + routes + SET + name = routes_to_update.correct_name + FROM + routes_to_update + WHERE + routes_to_update.id = routes.id + ROUTES_UPDATE + end + end + end +end diff --git a/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb b/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb index a4c6540c61b..eb4bc0aaf28 100644 --- a/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb +++ b/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb @@ -18,6 +18,14 @@ module Gitlab execute("INSERT INTO merge_request_assignees (merge_request_id, user_id) #{select_sql}") end + def perform_all_sync(batch_size:) + MergeRequest.each_batch(of: batch_size) do |batch| + range = batch.pluck('MIN(id)', 'MAX(id)').first + + perform(*range) + end + end + private def merge_request_assignees_not_exists_clause diff --git a/lib/gitlab/background_migration/populate_untracked_uploads.rb b/lib/gitlab/background_migration/populate_untracked_uploads.rb index 755b5ee725a..d2924d10225 100644 --- a/lib/gitlab/background_migration/populate_untracked_uploads.rb +++ b/lib/gitlab/background_migration/populate_untracked_uploads.rb @@ -42,7 +42,7 @@ module Gitlab #{e.message} #{e.backtrace.join("\n ")} MSG - Rails.logger.error(msg) + Rails.logger.error(msg) # rubocop:disable Gitlab/RailsLogger false end end diff --git a/lib/gitlab/background_migration/prepare_untracked_uploads.rb b/lib/gitlab/background_migration/prepare_untracked_uploads.rb index 1ee44a3a5a9..cce2a82c098 100644 --- a/lib/gitlab/background_migration/prepare_untracked_uploads.rb +++ b/lib/gitlab/background_migration/prepare_untracked_uploads.rb @@ -111,7 +111,7 @@ module Gitlab cmd = %W[#{ionice} -c Idle] + cmd if ionice log_msg = "PrepareUntrackedUploads find command: \"#{cmd.join(' ')}\"" - Rails.logger.info log_msg + Rails.logger.info log_msg # rubocop:disable Gitlab/RailsLogger cmd end diff --git a/lib/gitlab/batch_pop_queueing.rb b/lib/gitlab/batch_pop_queueing.rb new file mode 100644 index 00000000000..61011abddf5 --- /dev/null +++ b/lib/gitlab/batch_pop_queueing.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +module Gitlab + ## + # This class is a queuing system for processing expensive tasks in an atomic manner + # with batch poping to let you optimize the total processing time. + # + # In usual queuing system, the first item started being processed immediately + # and the following items wait until the next items have been popped from the queue. + # On the other hand, this queueing system, the former part is same, however, + # it pops the enqueued items as batch. This is especially useful when you want to + # drop redandant items from the queue in order to process important items only, + # thus it's more efficient than the traditional queueing system. + # + # Caveats: + # - The order of the items are not guaranteed because of `sadd` (Redis Sets). + # + # Example: + # ``` + # class TheWorker + # def perform + # result = Gitlab::BatchPopQueueing.new('feature', 'queue').safe_execute([item]) do |items_in_queue| + # item = extract_the_most_important_item_from(items_in_queue) + # expensive_process(item) + # end + # + # if result[:status] == :finished && result[:new_items].present? + # item = extract_the_most_important_item_from(items_in_queue) + # TheWorker.perform_async(item.id) + # end + # end + # end + # ``` + # + class BatchPopQueueing + attr_reader :namespace, :queue_id + + EXTRA_QUEUE_EXPIRE_WINDOW = 1.hour + MAX_COUNTS_OF_POP_ALL = 1000 + + # Initialize queue + # + # @param [String] namespace The namespace of the exclusive lock and queue key. Typically, it's a feature name. + # @param [String] queue_id The identifier of the queue. + # @return [Boolean] + def initialize(namespace, queue_id) + raise ArgumentError if namespace.empty? || queue_id.empty? + + @namespace, @queue_id = namespace, queue_id + end + + ## + # Execute the given block in an exclusive lock. + # If there is the other thread has already working on the block, + # it enqueues the items without processing the block. + # + # @param [Array<String>] new_items New items to be added to the queue. + # @param [Time] lock_timeout The timeout of the exclusive lock. Generally, this value should be longer than the maximum prosess timing of the given block. + # @return [Hash] + # - status => One of the `:enqueued` or `:finished`. + # - new_items => Newly enqueued items during the given block had been processed. + # + # NOTE: If an exception is raised in the block, the poppped items will not be recovered. + # We should NOT re-enqueue the items in this case because it could end up in an infinite loop. + def safe_execute(new_items, lock_timeout: 10.minutes, &block) + enqueue(new_items, lock_timeout + EXTRA_QUEUE_EXPIRE_WINDOW) + + lease = Gitlab::ExclusiveLease.new(lock_key, timeout: lock_timeout) + + return { status: :enqueued } unless uuid = lease.try_obtain + + begin + all_args = pop_all + + yield all_args if block_given? + + { status: :finished, new_items: peek_all } + ensure + Gitlab::ExclusiveLease.cancel(lock_key, uuid) + end + end + + private + + def lock_key + @lock_key ||= "batch_pop_queueing:lock:#{namespace}:#{queue_id}" + end + + def queue_key + @queue_key ||= "batch_pop_queueing:queue:#{namespace}:#{queue_id}" + end + + def enqueue(items, expire_time) + Gitlab::Redis::Queues.with do |redis| + redis.sadd(queue_key, items) + redis.expire(queue_key, expire_time.to_i) + end + end + + def pop_all + Gitlab::Redis::Queues.with do |redis| + redis.spop(queue_key, MAX_COUNTS_OF_POP_ALL) + end + end + + def peek_all + Gitlab::Redis::Queues.with do |redis| + redis.smembers(queue_key) + end + end + end +end diff --git a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml index 8a84744aa2d..005ea4b7a46 100644 --- a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml @@ -22,6 +22,7 @@ code_quality: reports: codequality: gl-code-quality-report.json expire_in: 1 week + dependencies: [] only: - branches - tags diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml index dcf8254ef94..108f0119ae1 100644 --- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml @@ -246,7 +246,6 @@ rollout 100%: auto_database_url=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${CI_ENVIRONMENT_SLUG}-postgres:5432/${POSTGRES_DB} export DATABASE_URL=${DATABASE_URL-$auto_database_url} export TILLER_NAMESPACE=$KUBE_NAMESPACE - # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products function get_replicas() { track="${1:-stable}" diff --git a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml index b9fee2d5731..25ea20e454f 100644 --- a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml @@ -1,5 +1,5 @@ # Select image from https://hub.docker.com/_/php/ -image: php:7.1.1 +image: php:latest # Select what we should cache between builds cache: diff --git a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml index d1a34c515fa..5ad624bb15f 100644 --- a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml @@ -23,6 +23,7 @@ container_scanning: DOCKER_HOST: tcp://${DOCKER_SERVICE}:2375/ # https://hub.docker.com/r/arminc/clair-local-scan/tags CLAIR_LOCAL_SCAN_VERSION: v2.0.8_fe9b059d930314b54c78f75afe265955faf4fdc1 + CLAIR_EXECUTABLE_VERSION: v11 ## Disable the proxy for clair-local-scan, otherwise Container Scanning will ## fail when a proxy is used. NO_PROXY: ${DOCKER_SERVICE},localhost @@ -41,7 +42,7 @@ container_scanning: - docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:${CLAIR_LOCAL_SCAN_VERSION} - apk add -U wget ca-certificates - docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} - - wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64 + - wget https://github.com/arminc/clair-scanner/releases/download/${CLAIR_EXECUTABLE_VERSION}/clair-scanner_linux_amd64 - mv clair-scanner_linux_amd64 clair-scanner - chmod +x clair-scanner - touch clair-whitelist.yml diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml index 8dd9775c583..f176771775e 100644 --- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml @@ -40,6 +40,7 @@ dependency_scanning: DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \ DS_PULL_ANALYZER_IMAGE_TIMEOUT \ DS_RUN_ANALYZER_TIMEOUT \ + DS_PYTHON_VERSION \ ) \ --volume "$PWD:/code" \ --volume /var/run/docker.sock:/var/run/docker.sock \ diff --git a/lib/gitlab/cleanup/orphan_job_artifact_files.rb b/lib/gitlab/cleanup/orphan_job_artifact_files.rb index ee7164b3e55..808814c39e0 100644 --- a/lib/gitlab/cleanup/orphan_job_artifact_files.rb +++ b/lib/gitlab/cleanup/orphan_job_artifact_files.rb @@ -17,7 +17,7 @@ module Gitlab @limit = limit @dry_run = dry_run @niceness = niceness || DEFAULT_NICENESS - @logger = logger || Rails.logger + @logger = logger || Rails.logger # rubocop:disable Gitlab/RailsLogger @total_found = @total_cleaned = 0 new_batch! diff --git a/lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb b/lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb index 5c30258c0fc..53e0c83046e 100644 --- a/lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb +++ b/lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb @@ -22,7 +22,7 @@ module Gitlab attr_reader :batch_size, :dry_run attr_accessor :artifact_files - def initialize(batch_size:, dry_run: true, logger: Rails.logger) + def initialize(batch_size:, dry_run: true, logger: Rails.logger) # rubocop:disable Gitlab/RailsLogger @batch_size = batch_size @dry_run = dry_run @logger = logger diff --git a/lib/gitlab/cleanup/project_upload_file_finder.rb b/lib/gitlab/cleanup/project_upload_file_finder.rb index 2ee8b60e76a..5aace564c2d 100644 --- a/lib/gitlab/cleanup/project_upload_file_finder.rb +++ b/lib/gitlab/cleanup/project_upload_file_finder.rb @@ -49,7 +49,7 @@ module Gitlab cmd = %W[#{ionice} -c Idle] + cmd if ionice log_msg = "find command: \"#{cmd.join(' ')}\"" - Rails.logger.info log_msg + Rails.logger.info log_msg # rubocop:disable Gitlab/RailsLogger cmd end diff --git a/lib/gitlab/cleanup/project_uploads.rb b/lib/gitlab/cleanup/project_uploads.rb index 82a405362c2..056e075cb21 100644 --- a/lib/gitlab/cleanup/project_uploads.rb +++ b/lib/gitlab/cleanup/project_uploads.rb @@ -8,7 +8,7 @@ module Gitlab attr_reader :logger def initialize(logger: nil) - @logger = logger || Rails.logger + @logger = logger || Rails.logger # rubocop:disable Gitlab/RailsLogger end def run!(dry_run: true) diff --git a/lib/gitlab/cleanup/remote_uploads.rb b/lib/gitlab/cleanup/remote_uploads.rb index 03298d960a4..42c93b7aecb 100644 --- a/lib/gitlab/cleanup/remote_uploads.rb +++ b/lib/gitlab/cleanup/remote_uploads.rb @@ -7,7 +7,7 @@ module Gitlab BATCH_SIZE = 100 def initialize(logger: nil) - @logger = logger || Rails.logger + @logger = logger || Rails.logger # rubocop:disable Gitlab/RailsLogger end def run!(dry_run: false) diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 469a7fd9f7b..32d5e4b9ea3 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -45,7 +45,7 @@ module Gitlab # need to be added to the application settings. To prevent Rake tasks # and other callers from failing, use any loaded settings and return # defaults for missing columns. - if ActiveRecord::Migrator.needs_migration? + if ActiveRecord::Base.connection.migration_context.needs_migration? db_attributes = current_settings&.attributes || {} fake_application_settings(db_attributes) elsif current_settings.present? diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb index 304d60996a6..0cacef5b278 100644 --- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -5,11 +5,11 @@ module Gitlab class BaseEventFetcher include BaseQuery - attr_reader :projections, :query, :stage, :order + attr_reader :projections, :query, :stage, :order, :project, :options MAX_EVENTS = 50 - def initialize(project:, stage:, options:) + def initialize(project: nil, stage:, options:) @project = project @stage = stage @options = options @@ -40,13 +40,13 @@ module Gitlab end def events_query - diff_fn = subtract_datetimes_diff(base_query, @options[:start_time_attrs], @options[:end_time_attrs]) + diff_fn = subtract_datetimes_diff(base_query, options[:start_time_attrs], options[:end_time_attrs]) base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *projections).order(order.desc).take(MAX_EVENTS) end def default_order - [@options[:start_time_attrs]].flatten.first + [options[:start_time_attrs]].flatten.first end def serialize(_event) @@ -59,13 +59,21 @@ module Gitlab def allowed_ids @allowed_ids ||= allowed_ids_finder_class - .new(@options[:current_user], project_id: @project.id) + .new(options[:current_user], allowed_ids_source) .execute.where(id: event_result_ids).pluck(:id) end def event_result_ids event_result.map { |event| event['id'] } end + + def allowed_ids_source + { project_id: project.id } + end + + def projects + [project] + end end end end diff --git a/lib/gitlab/cycle_analytics/base_query.rb b/lib/gitlab/cycle_analytics/base_query.rb index 36231b187cd..39fc1759cfc 100644 --- a/lib/gitlab/cycle_analytics/base_query.rb +++ b/lib/gitlab/cycle_analytics/base_query.rb @@ -10,7 +10,7 @@ module Gitlab private def base_query - @base_query ||= stage_query(@project.id) # rubocop:disable Gitlab/ModuleWithInstanceVariables + @base_query ||= stage_query(projects.map(&:id)) end def stage_query(project_ids) @@ -18,7 +18,7 @@ module Gitlab .join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])) .project(issue_table[:project_id].as("project_id")) .where(issue_table[:project_id].in(project_ids)) - .where(issue_table[:created_at].gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables + .where(issue_table[:created_at].gteq(options[:from])) # Load merge_requests query = query.join(mr_table, Arel::Nodes::OuterJoin) diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index e2d6a301734..98b86e54340 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -5,7 +5,9 @@ module Gitlab class BaseStage include BaseQuery - def initialize(project:, options:) + attr_reader :project, :options + + def initialize(project: nil, options:) @project = project @options = options end @@ -14,8 +16,8 @@ module Gitlab event_fetcher.fetch end - def as_json - AnalyticsStageSerializer.new.represent(self) + def as_json(serializer: AnalyticsStageSerializer) + serializer.new.represent(self) end def title @@ -23,21 +25,14 @@ module Gitlab end def median - BatchLoader.for(@project.id).batch(key: name) do |project_ids, loader| - cte_table = Arel::Table.new("cte_table_for_#{name}") - - # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). - # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). - # We compute the (end_time - start_time) interval, and give it an alias based on the current - # cycle analytics stage. - interval_query = Arel::Nodes::As.new(cte_table, - subtract_datetimes(stage_query(project_ids), start_time_attrs, end_time_attrs, name.to_s)) + return if project.nil? + BatchLoader.for(project.id).batch(key: name) do |project_ids, loader| if project_ids.one? - loader.call(@project.id, median_datetime(cte_table, interval_query, name)) + loader.call(project.id, median_query(project_ids)) else begin - median_datetimes(cte_table, interval_query, name, :project_id)&.each do |project_id, median| + median_datetimes(cte_table, interval_query(project_ids), name, :project_id)&.each do |project_id, median| loader.call(project_id, median) end rescue NotSupportedError @@ -47,20 +42,42 @@ module Gitlab end end + def median_query(project_ids) + # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). + # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). + # We compute the (end_time - start_time) interval, and give it an alias based on the current + # cycle analytics stage. + + median_datetime(cte_table, interval_query(project_ids), name) + end + def name raise NotImplementedError.new("Expected #{self.name} to implement name") end + def cte_table + Arel::Table.new("cte_table_for_#{name}") + end + + def interval_query(project_ids) + Arel::Nodes::As.new(cte_table, + subtract_datetimes(stage_query(project_ids), start_time_attrs, end_time_attrs, name.to_s)) + end + private def event_fetcher - @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(project: @project, + @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(project: project, stage: name, options: event_options) end def event_options - @options.merge(start_time_attrs: start_time_attrs, end_time_attrs: end_time_attrs) + options.merge(start_time_attrs: start_time_attrs, end_time_attrs: end_time_attrs) + end + + def projects + [project] end end end diff --git a/lib/gitlab/cycle_analytics/code_event_fetcher.rb b/lib/gitlab/cycle_analytics/code_event_fetcher.rb index 6c348f1862d..9e7ca529579 100644 --- a/lib/gitlab/cycle_analytics/code_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/code_event_fetcher.rb @@ -20,7 +20,7 @@ module Gitlab private def serialize(event) - AnalyticsMergeRequestSerializer.new(project: @project).represent(event) + AnalyticsMergeRequestSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb index 8a870f2e2a3..bb3520ae920 100644 --- a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb @@ -18,7 +18,7 @@ module Gitlab private def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event) + AnalyticsIssueSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/issue_helper.rb b/lib/gitlab/cycle_analytics/issue_helper.rb index c9266341378..ac836b8bf0f 100644 --- a/lib/gitlab/cycle_analytics/issue_helper.rb +++ b/lib/gitlab/cycle_analytics/issue_helper.rb @@ -7,7 +7,7 @@ module Gitlab query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])) .project(issue_table[:project_id].as("project_id")) .where(issue_table[:project_id].in(project_ids)) - .where(issue_table[:created_at].gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables + .where(issue_table[:created_at].gteq(options[:from])) .where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil))) query diff --git a/lib/gitlab/cycle_analytics/permissions.rb b/lib/gitlab/cycle_analytics/permissions.rb index afefd09b614..03ba98b4dfb 100644 --- a/lib/gitlab/cycle_analytics/permissions.rb +++ b/lib/gitlab/cycle_analytics/permissions.rb @@ -23,7 +23,7 @@ module Gitlab end def get - ::CycleAnalytics::STAGES.each do |stage| + ::CycleAnalytics::Base::STAGES.each do |stage| @stage_permission_hash[stage] = authorized_stage?(stage) end diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb index d924f956dcd..49a6b099f34 100644 --- a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb @@ -18,7 +18,7 @@ module Gitlab private def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event) + AnalyticsIssueSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/plan_helper.rb b/lib/gitlab/cycle_analytics/plan_helper.rb index 30fc2ce6d40..ae578d45ad5 100644 --- a/lib/gitlab/cycle_analytics/plan_helper.rb +++ b/lib/gitlab/cycle_analytics/plan_helper.rb @@ -7,7 +7,7 @@ module Gitlab query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])) .project(issue_table[:project_id].as("project_id")) .where(issue_table[:project_id].in(project_ids)) - .where(issue_table[:created_at].gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables + .where(issue_table[:created_at].gteq(options[:from])) .where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil))) .where(issue_metrics_table[:first_mentioned_in_commit_at].not_eq(nil)) diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb index 6bcbe0412a9..949119d69a0 100644 --- a/lib/gitlab/cycle_analytics/production_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb @@ -18,7 +18,7 @@ module Gitlab private def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event) + AnalyticsIssueSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/production_helper.rb b/lib/gitlab/cycle_analytics/production_helper.rb index aff65b150fb..778757a9ede 100644 --- a/lib/gitlab/cycle_analytics/production_helper.rb +++ b/lib/gitlab/cycle_analytics/production_helper.rb @@ -6,7 +6,7 @@ module Gitlab def stage_query(project_ids) super(project_ids) .where(mr_metrics_table[:first_deployed_to_production_at] - .gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables + .gteq(options[:from])) end end end diff --git a/lib/gitlab/cycle_analytics/review_event_fetcher.rb b/lib/gitlab/cycle_analytics/review_event_fetcher.rb index b6354b5ffad..d31736e755d 100644 --- a/lib/gitlab/cycle_analytics/review_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/review_event_fetcher.rb @@ -19,7 +19,7 @@ module Gitlab private def serialize(event) - AnalyticsMergeRequestSerializer.new(project: @project).represent(event) + AnalyticsMergeRequestSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/test_helper.rb b/lib/gitlab/cycle_analytics/test_helper.rb index 32fca7fa898..d9124d62c7c 100644 --- a/lib/gitlab/cycle_analytics/test_helper.rb +++ b/lib/gitlab/cycle_analytics/test_helper.rb @@ -14,7 +14,7 @@ module Gitlab private def branch - @branch ||= @options[:branch] # rubocop:disable Gitlab/ModuleWithInstanceVariables + @branch ||= options[:branch] end end end diff --git a/lib/gitlab/cycle_analytics/usage_data.rb b/lib/gitlab/cycle_analytics/usage_data.rb index 913ee373f54..644300caead 100644 --- a/lib/gitlab/cycle_analytics/usage_data.rb +++ b/lib/gitlab/cycle_analytics/usage_data.rb @@ -32,7 +32,7 @@ module Gitlab def medians_per_stage projects.each_with_object({}) do |project, hsh| - ::CycleAnalytics.new(project, options).all_medians_per_stage.each do |stage_name, median| + ::CycleAnalytics::ProjectLevel.new(project, options: options).all_medians_by_stage.each do |stage_name, median| hsh[stage_name] ||= [] hsh[stage_name] << median end diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index 34c1e6ad8ca..3e4c720b49a 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -128,7 +128,7 @@ module Gitlab order = "#{field} IS NULL, #{order}" if direction == 'ASC' end - order + Arel.sql(order) end def self.nulls_first_order(field, direction = 'ASC') @@ -142,7 +142,7 @@ module Gitlab order = "#{field} IS NULL, #{order}" if direction == 'DESC' end - order + Arel.sql(order) end def self.random @@ -284,17 +284,28 @@ module Gitlab end # inside_transaction? will return true if the caller is running within a transaction. Handles special cases - # when running inside a test environment, in which the entire test is running with a DatabaseCleaner transaction + # when running inside a test environment, where tests may be wrapped in transactions def self.inside_transaction? - ActiveRecord::Base.connection.open_transactions > open_transactions_baseline + if Rails.env.test? + ActiveRecord::Base.connection.open_transactions > open_transactions_baseline + else + ActiveRecord::Base.connection.open_transactions > 0 + end end - def self.open_transactions_baseline - if ::Rails.env.test? - return DatabaseCleaner.connections.count { |conn| conn.strategy.is_a?(DatabaseCleaner::ActiveRecord::Transaction) } - end + # These methods that access @open_transactions_baseline are not thread-safe. + # These are fine though because we only call these in RSpec's main thread. If we decide to run + # specs multi-threaded, we would need to use something like ThreadGroup to keep track of this value + def self.set_open_transactions_baseline + @open_transactions_baseline = ActiveRecord::Base.connection.open_transactions + end + + def self.reset_open_transactions_baseline + @open_transactions_baseline = 0 + end - 0 + def self.open_transactions_baseline + @open_transactions_baseline ||= 0 end private_class_method :open_transactions_baseline @@ -310,7 +321,7 @@ module Gitlab gitlab_database_transaction_seconds.observe(labels, duration_seconds) rescue Prometheus::Client::LabelSetValidator::LabelSetError => err # Ensure that errors in recording these metrics don't affect the operation of the application - Rails.logger.error("Unable to observe database transaction duration: #{err}") + Rails.logger.error("Unable to observe database transaction duration: #{err}") # rubocop:disable Gitlab/RailsLogger end # MonkeyPatch for ActiveRecord::Base for adding observability diff --git a/lib/gitlab/database/grant.rb b/lib/gitlab/database/grant.rb index 862ab96c887..26adf4e221b 100644 --- a/lib/gitlab/database/grant.rb +++ b/lib/gitlab/database/grant.rb @@ -24,7 +24,7 @@ module Gitlab begin from(nil) - .pluck("has_table_privilege(#{quoted_table}, 'TRIGGER')") + .pluck(Arel.sql("has_table_privilege(#{quoted_table}, 'TRIGGER')")) .first rescue ActiveRecord::StatementInvalid # This error is raised when using a non-existing table name. In this diff --git a/lib/gitlab/database/median.rb b/lib/gitlab/database/median.rb index 1455e410d4b..b8d895dee7d 100644 --- a/lib/gitlab/database/median.rb +++ b/lib/gitlab/database/median.rb @@ -158,7 +158,7 @@ module Gitlab Arel::Nodes::Window.new.order(arel_table[column_sym]) ).as('row_id') - count = arel_table.project("COUNT(1)").as('ct') + count = arel_table.where(arel_table[column_sym].gteq(zero_interval)).project("COUNT(1)").as('ct') [column_row, row_id, count] end diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb index e2cbf91f281..0c5f33e1b2a 100644 --- a/lib/gitlab/database/migration_helpers.rb +++ b/lib/gitlab/database/migration_helpers.rb @@ -61,7 +61,7 @@ module Gitlab end if index_exists?(table_name, column_name, options) - Rails.logger.warn "Index not created because it already exists (this may be due to an aborted migration or similar): table_name: #{table_name}, column_name: #{column_name}" + Rails.logger.warn "Index not created because it already exists (this may be due to an aborted migration or similar): table_name: #{table_name}, column_name: #{column_name}" # rubocop:disable Gitlab/RailsLogger return end @@ -91,7 +91,7 @@ module Gitlab end unless index_exists?(table_name, column_name, options) - Rails.logger.warn "Index not removed because it does not exist (this may be due to an aborted migration or similar): table_name: #{table_name}, column_name: #{column_name}" + Rails.logger.warn "Index not removed because it does not exist (this may be due to an aborted migration or similar): table_name: #{table_name}, column_name: #{column_name}" # rubocop:disable Gitlab/RailsLogger return end @@ -121,7 +121,7 @@ module Gitlab end unless index_exists_by_name?(table_name, index_name) - Rails.logger.warn "Index not removed because it does not exist (this may be due to an aborted migration or similar): table_name: #{table_name}, index_name: #{index_name}" + Rails.logger.warn "Index not removed because it does not exist (this may be due to an aborted migration or similar): table_name: #{table_name}, index_name: #{index_name}" # rubocop:disable Gitlab/RailsLogger return end @@ -149,6 +149,8 @@ module Gitlab # column - The name of the column to create the foreign key on. # on_delete - The action to perform when associated data is removed, # defaults to "CASCADE". + # + # rubocop:disable Gitlab/RailsLogger def add_concurrent_foreign_key(source, target, column:, on_delete: :cascade, name: nil) # Transactions would result in ALTER TABLE locks being held for the # duration of the transaction, defeating the purpose of this method. @@ -208,6 +210,7 @@ module Gitlab execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{key_name};") end end + # rubocop:enable Gitlab/RailsLogger def foreign_key_exists?(source, target = nil, column: nil) foreign_keys(source).any? do |key| diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb index 6bbad707f0f..3e8a9b89998 100644 --- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb +++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb @@ -70,7 +70,7 @@ module Gitlab unless gitlab_shell.mv_namespace(repository_storage, old_full_path, new_full_path) message = "Exception moving on shard #{repository_storage} from #{old_full_path} to #{new_full_path}" - Rails.logger.error message + Rails.logger.error message # rubocop:disable Gitlab/RailsLogger end end end diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects.rb index 580be9fe267..4dc7a62797a 100644 --- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects.rb +++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects.rb @@ -56,7 +56,7 @@ module Gitlab unless gitlab_shell.mv_repository(project.repository_storage, old_path, new_path) - Rails.logger.error "Error moving #{old_path} to #{new_path}" + Rails.logger.error "Error moving #{old_path} to #{new_path}" # rubocop:disable Gitlab/RailsLogger end end diff --git a/lib/gitlab/database_importers/common_metrics.rb b/lib/gitlab/database_importers/common_metrics.rb new file mode 100644 index 00000000000..f964ae8a275 --- /dev/null +++ b/lib/gitlab/database_importers/common_metrics.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Gitlab + module DatabaseImporters + module CommonMetrics + end + end +end diff --git a/lib/gitlab/database_importers/common_metrics/importer.rb b/lib/gitlab/database_importers/common_metrics/importer.rb new file mode 100644 index 00000000000..6c61e05674e --- /dev/null +++ b/lib/gitlab/database_importers/common_metrics/importer.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +module Gitlab + module DatabaseImporters + module CommonMetrics + class Importer + MissingQueryId = Class.new(StandardError) + + attr_reader :content + + def initialize(filename = 'common_metrics.yml') + @content = YAML.load_file(Rails.root.join('config', 'prometheus', filename)) + end + + def execute + CommonMetrics::PrometheusMetric.reset_column_information + + process_content do |id, attributes| + find_or_build_metric!(id) + .update!(**attributes) + end + end + + private + + def process_content(&blk) + content['panel_groups'].map do |group| + process_group(group, &blk) + end + end + + def process_group(group, &blk) + attributes = { + group: find_group_title_key(group['group']) + } + + group['panels'].map do |panel| + process_panel(panel, attributes, &blk) + end + end + + def process_panel(panel, attributes, &blk) + attributes = attributes.merge( + title: panel['title'], + y_label: panel['y_label']) + + panel['metrics'].map do |metric_details| + process_metric_details(metric_details, attributes, &blk) + end + end + + def process_metric_details(metric_details, attributes, &blk) + attributes = attributes.merge( + legend: metric_details['label'], + query: metric_details['query_range'], + unit: metric_details['unit']) + + yield(metric_details['id'], attributes) + end + + def find_or_build_metric!(id) + raise MissingQueryId unless id + + CommonMetrics::PrometheusMetric.common.find_by(identifier: id) || + CommonMetrics::PrometheusMetric.new(common: true, identifier: id) + end + + def find_group_title_key(title) + CommonMetrics::PrometheusMetricEnums.groups[find_group_title(title)] + end + + def find_group_title(title) + CommonMetrics::PrometheusMetricEnums.group_titles.invert[title] + end + end + end + end +end diff --git a/lib/gitlab/database_importers/common_metrics/prometheus_metric.rb b/lib/gitlab/database_importers/common_metrics/prometheus_metric.rb new file mode 100644 index 00000000000..b4a392cbea9 --- /dev/null +++ b/lib/gitlab/database_importers/common_metrics/prometheus_metric.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Gitlab + module DatabaseImporters + module CommonMetrics + class PrometheusMetric < ApplicationRecord + enum group: PrometheusMetricEnums.groups + scope :common, -> { where(common: true) } + end + end + end +end diff --git a/lib/gitlab/database_importers/common_metrics/prometheus_metric_enums.rb b/lib/gitlab/database_importers/common_metrics/prometheus_metric_enums.rb new file mode 100644 index 00000000000..c9e957ec7c0 --- /dev/null +++ b/lib/gitlab/database_importers/common_metrics/prometheus_metric_enums.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Gitlab + module DatabaseImporters + module CommonMetrics + module PrometheusMetricEnums + def self.groups + { + # built-in groups + nginx_ingress_vts: -1, + ha_proxy: -2, + aws_elb: -3, + nginx: -4, + kubernetes: -5, + nginx_ingress: -6, + + # custom groups + business: 0, + response: 1, + system: 2 + } + end + + def self.group_titles + { + business: _('Business metrics (Custom)'), + response: _('Response metrics (Custom)'), + system: _('System metrics (Custom)'), + nginx_ingress_vts: _('Response metrics (NGINX Ingress VTS)'), + nginx_ingress: _('Response metrics (NGINX Ingress)'), + ha_proxy: _('Response metrics (HA Proxy)'), + aws_elb: _('Response metrics (AWS ELB)'), + nginx: _('Response metrics (NGINX)'), + kubernetes: _('System metrics (Kubernetes)') + } + end + end + end + end +end diff --git a/lib/gitlab/email/hook/disable_email_interceptor.rb b/lib/gitlab/email/hook/disable_email_interceptor.rb index 6b6b1d85109..58dc1527c7a 100644 --- a/lib/gitlab/email/hook/disable_email_interceptor.rb +++ b/lib/gitlab/email/hook/disable_email_interceptor.rb @@ -7,7 +7,7 @@ module Gitlab def self.delivering_email(message) message.perform_deliveries = false - Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}" + Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}" # rubocop:disable Gitlab/RailsLogger end end end diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb index 5a61a7f5d60..88729babb2b 100644 --- a/lib/gitlab/encoding_helper.rb +++ b/lib/gitlab/encoding_helper.rb @@ -59,7 +59,7 @@ module Gitlab begin CharlockHolmes::Converter.convert(message, detect[:encoding], 'UTF-8') rescue ArgumentError => e - Rails.logger.warn("Ignoring error converting #{detect[:encoding]} into UTF8: #{e.message}") + Rails.logger.warn("Ignoring error converting #{detect[:encoding]} into UTF8: #{e.message}") # rubocop:disable Gitlab/RailsLogger '' end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 060a29be782..b7b7578cef9 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -936,7 +936,7 @@ module Gitlab gitaly_repository_client.cleanup if exists? end rescue Gitlab::Git::CommandError => e # Don't fail if we can't cleanup - Rails.logger.error("Unable to clean repository on storage #{storage} with relative path #{relative_path}: #{e.message}") + Rails.logger.error("Unable to clean repository on storage #{storage} with relative path #{relative_path}: #{e.message}") # rubocop:disable Gitlab/RailsLogger Gitlab::Metrics.counter( :failed_repository_cleanup_total, 'Number of failed repository cleanup events' diff --git a/lib/gitlab/git/rugged_impl/blob.rb b/lib/gitlab/git/rugged_impl/blob.rb index 11ee4ebda4b..86c9f33d82a 100644 --- a/lib/gitlab/git/rugged_impl/blob.rb +++ b/lib/gitlab/git/rugged_impl/blob.rb @@ -11,10 +11,11 @@ module Gitlab module Blob module ClassMethods extend ::Gitlab::Utils::Override + include Gitlab::Git::RuggedImpl::UseRugged override :tree_entry def tree_entry(repository, sha, path, limit) - if Feature.enabled?(:rugged_tree_entry) + if use_rugged?(repository, :rugged_tree_entry) rugged_tree_entry(repository, sha, path, limit) else super diff --git a/lib/gitlab/git/rugged_impl/commit.rb b/lib/gitlab/git/rugged_impl/commit.rb index bce4fa14fb4..971a33b2e99 100644 --- a/lib/gitlab/git/rugged_impl/commit.rb +++ b/lib/gitlab/git/rugged_impl/commit.rb @@ -12,6 +12,7 @@ module Gitlab module Commit module ClassMethods extend ::Gitlab::Utils::Override + include Gitlab::Git::RuggedImpl::UseRugged def rugged_find(repo, commit_id) obj = repo.rev_parse_target(commit_id) @@ -34,7 +35,7 @@ module Gitlab override :find_commit def find_commit(repo, commit_id) - if Feature.enabled?(:rugged_find_commit) + if use_rugged?(repo, :rugged_find_commit) rugged_find(repo, commit_id) else super @@ -43,7 +44,7 @@ module Gitlab override :batch_by_oid def batch_by_oid(repo, oids) - if Feature.enabled?(:rugged_list_commits_by_oid) + if use_rugged?(repo, :rugged_list_commits_by_oid) rugged_batch_by_oid(repo, oids) else super @@ -52,6 +53,7 @@ module Gitlab end extend ::Gitlab::Utils::Override + include Gitlab::Git::RuggedImpl::UseRugged override :init_commit def init_commit(raw_commit) @@ -65,7 +67,7 @@ module Gitlab override :commit_tree_entry def commit_tree_entry(path) - if Feature.enabled?(:rugged_commit_tree_entry) + if use_rugged?(@repository, :rugged_commit_tree_entry) rugged_tree_entry(path) else super diff --git a/lib/gitlab/git/rugged_impl/repository.rb b/lib/gitlab/git/rugged_impl/repository.rb index e91b0ddcd31..9268abdfed9 100644 --- a/lib/gitlab/git/rugged_impl/repository.rb +++ b/lib/gitlab/git/rugged_impl/repository.rb @@ -11,6 +11,7 @@ module Gitlab module RuggedImpl module Repository extend ::Gitlab::Utils::Override + include Gitlab::Git::RuggedImpl::UseRugged FEATURE_FLAGS = %i(rugged_find_commit rugged_tree_entries rugged_tree_entry rugged_commit_is_ancestor rugged_commit_tree_entry rugged_list_commits_by_oid).freeze @@ -46,7 +47,7 @@ module Gitlab override :ancestor? def ancestor?(from, to) - if Feature.enabled?(:rugged_commit_is_ancestor) + if use_rugged?(self, :rugged_commit_is_ancestor) rugged_is_ancestor?(from, to) else super diff --git a/lib/gitlab/git/rugged_impl/tree.rb b/lib/gitlab/git/rugged_impl/tree.rb index 9c37bb01961..f3721a3f1b7 100644 --- a/lib/gitlab/git/rugged_impl/tree.rb +++ b/lib/gitlab/git/rugged_impl/tree.rb @@ -11,10 +11,11 @@ module Gitlab module Tree module ClassMethods extend ::Gitlab::Utils::Override + include Gitlab::Git::RuggedImpl::UseRugged override :tree_entries def tree_entries(repository, sha, path, recursive) - if Feature.enabled?(:rugged_tree_entries) + if use_rugged?(repository, :rugged_tree_entries) tree_entries_with_flat_path_from_rugged(repository, sha, path, recursive) else super diff --git a/lib/gitlab/git/rugged_impl/use_rugged.rb b/lib/gitlab/git/rugged_impl/use_rugged.rb new file mode 100644 index 00000000000..99091b03cd1 --- /dev/null +++ b/lib/gitlab/git/rugged_impl/use_rugged.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Gitlab + module Git + module RuggedImpl + module UseRugged + def use_rugged?(repo, feature_key) + feature = Feature.get(feature_key) + return feature.enabled? if Feature.persisted?(feature) + + Gitlab::GitalyClient.can_use_disk?(repo.storage) + end + end + end + end +end diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 47976389af6..091351e5cb2 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -30,14 +30,10 @@ module Gitlab SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION' MAXIMUM_GITALY_CALLS = 30 CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze + GITALY_METADATA_FILENAME = '.gitaly-metadata' MUTEX = Mutex.new - define_histogram :gitaly_controller_action_duration_seconds do - docstring "Gitaly endpoint histogram by controller and action combination" - base_labels Gitlab::Metrics::Transaction::BASE_LABELS.merge(gitaly_service: nil, rpc: nil) - end - def self.stub(name, storage) MUTEX.synchronize do @stubs ||= {} @@ -71,7 +67,7 @@ module Gitlab File.read(cert_file).scan(PEM_REGEX).map do |cert| OpenSSL::X509::Certificate.new(cert).to_pem rescue OpenSSL::OpenSSLError => e - Rails.logger.error "Could not load certificate #{cert_file} #{e}" + Rails.logger.error "Could not load certificate #{cert_file} #{e}" # rubocop:disable Gitlab/RailsLogger Gitlab::Sentry.track_exception(e, extra: { cert_file: cert_file }) nil end.compact @@ -161,10 +157,6 @@ module Gitlab # Keep track, separately, for the performance bar self.query_time += duration - gitaly_controller_action_duration_seconds.observe( - current_transaction_labels.merge(gitaly_service: service.to_s, rpc: rpc.to_s), - duration) - if peek_enabled? add_call_details(feature: "#{service}##{rpc}", duration: duration, request: request_hash, rpc: rpc, backtrace: Gitlab::Profiler.clean_backtrace(caller)) @@ -387,6 +379,46 @@ module Gitlab 0 end + def self.storage_metadata_file_path(storage) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + File.join( + Gitlab.config.repositories.storages[storage].legacy_disk_path, GITALY_METADATA_FILENAME + ) + end + end + + def self.can_use_disk?(storage) + false + # cached_value = MUTEX.synchronize do + # @can_use_disk ||= {} + # @can_use_disk[storage] + # end + + # return cached_value unless cached_value.nil? + + # gitaly_filesystem_id = filesystem_id(storage) + # direct_filesystem_id = filesystem_id_from_disk(storage) + + # MUTEX.synchronize do + # @can_use_disk[storage] = gitaly_filesystem_id.present? && + # gitaly_filesystem_id == direct_filesystem_id + # end + end + + def self.filesystem_id(storage) + response = Gitlab::GitalyClient::ServerService.new(storage).info + storage_status = response.storage_statuses.find { |status| status.storage_name == storage } + storage_status.filesystem_id + end + + def self.filesystem_id_from_disk(storage) + metadata_file = File.read(storage_metadata_file_path(storage)) + metadata_hash = JSON.parse(metadata_file) + metadata_hash['gitaly_filesystem_id'] + rescue Errno::ENOENT, JSON::ParserError + nil + end + def self.timeout(timeout_name) Gitlab::CurrentSettings.current_application_settings[timeout_name] end diff --git a/lib/gitlab/github_import/importer/lfs_objects_importer.rb b/lib/gitlab/github_import/importer/lfs_objects_importer.rb index 6046e30d4ef..30763492235 100644 --- a/lib/gitlab/github_import/importer/lfs_objects_importer.rb +++ b/lib/gitlab/github_import/importer/lfs_objects_importer.rb @@ -29,7 +29,7 @@ module Gitlab yield object end rescue StandardError => e - Rails.logger.error("The Lfs import process failed. #{e.message}") + Rails.logger.error("The Lfs import process failed. #{e.message}") # rubocop:disable Gitlab/RailsLogger end end end diff --git a/lib/gitlab/github_import/importer/pull_requests_importer.rb b/lib/gitlab/github_import/importer/pull_requests_importer.rb index a52866c4b08..929fceaacf2 100644 --- a/lib/gitlab/github_import/importer/pull_requests_importer.rb +++ b/lib/gitlab/github_import/importer/pull_requests_importer.rb @@ -40,7 +40,7 @@ module Gitlab pname = project.path_with_namespace - Rails.logger + Rails.logger # rubocop:disable Gitlab/RailsLogger .info("GitHub importer finished updating repository for #{pname}") repository_updates_counter.increment diff --git a/lib/gitlab/global_id.rb b/lib/gitlab/global_id.rb new file mode 100644 index 00000000000..cc82b6c5897 --- /dev/null +++ b/lib/gitlab/global_id.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Gitlab + module GlobalId + def self.build(object = nil, model_name: nil, id: nil, params: nil) + if object + model_name ||= object.class.name + id ||= object.id + end + + ::URI::GID.build(app: GlobalID.app, model_name: model_name, model_id: id, params: params) + end + end +end diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index 92917028851..41ec8741eb1 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -38,6 +38,11 @@ module Gitlab gon.current_user_fullname = current_user.name gon.current_user_avatar_url = current_user.avatar_url end + + # Flag controls a GFM feature used across many routes. + # Pushing the flag from one place simplifies control + # and facilitates easy removal. + push_frontend_feature_flag(:gfm_embedded_metrics) end # Exposes the state of a feature flag to the frontend code. diff --git a/lib/gitlab/graphql.rb b/lib/gitlab/graphql.rb index 8a59e83974f..74c04e5380e 100644 --- a/lib/gitlab/graphql.rb +++ b/lib/gitlab/graphql.rb @@ -3,9 +3,5 @@ module Gitlab module Graphql StandardGraphqlError = Class.new(StandardError) - - def self.enabled? - Feature.enabled?(:graphql, default_enabled: true) - end end end diff --git a/lib/gitlab/graphql/docs/helper.rb b/lib/gitlab/graphql/docs/helper.rb new file mode 100644 index 00000000000..ac2a78c0f28 --- /dev/null +++ b/lib/gitlab/graphql/docs/helper.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +return if Rails.env.production? + +module Gitlab + module Graphql + module Docs + # Helper with functions to be used by HAML templates + # This includes graphql-docs gem helpers class. + # You can check the included module on: https://github.com/gjtorikian/graphql-docs/blob/v1.6.0/lib/graphql-docs/helpers.rb + module Helper + include GraphQLDocs::Helpers + + def auto_generated_comment + <<-MD.strip_heredoc + <!--- + This documentation is auto generated by a script. + + Please do not edit this file directly, check compile_docs task on lib/tasks/gitlab/graphql.rake. + ---> + MD + end + + # Some fields types are arrays of other types and are displayed + # on docs wrapped in square brackets, for example: [String!]. + # This makes GitLab docs renderer thinks they are links so here + # we change them to be rendered as: String! => Array. + def render_field_type(type) + array_type = type[/\[(.+)\]/, 1] + + if array_type + "#{array_type} => Array" + else + type + end + end + + # We are ignoring connections and built in types for now, + # they should be added when queries are generated. + def objects + graphql_object_types.select do |object_type| + !object_type[:name]["Connection"] && + !object_type[:name]["Edge"] && + !object_type[:name]["__"] + end + end + end + end + end +end diff --git a/lib/gitlab/graphql/docs/renderer.rb b/lib/gitlab/graphql/docs/renderer.rb new file mode 100644 index 00000000000..f47a372aa19 --- /dev/null +++ b/lib/gitlab/graphql/docs/renderer.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +return if Rails.env.production? + +module Gitlab + module Graphql + module Docs + # Gitlab renderer for graphql-docs. + # Uses HAML templates to parse markdown and generate .md files. + # It uses graphql-docs helpers and schema parser, more information in https://github.com/gjtorikian/graphql-docs. + # + # Arguments: + # schema - the GraphQL schema defition. For GitLab should be: GitlabSchema.graphql_definition + # output_dir: The folder where the markdown files will be saved + # template: The path of the haml template to be parsed + class Renderer + include Gitlab::Graphql::Docs::Helper + + def initialize(schema, output_dir:, template:) + @output_dir = output_dir + @template = template + @layout = Haml::Engine.new(File.read(template)) + @parsed_schema = GraphQLDocs::Parser.new(schema, {}).parse + end + + def render + contents = @layout.render(self) + + write_file(contents) + end + + private + + def write_file(contents) + filename = File.join(@output_dir, 'index.md') + + FileUtils.mkdir_p(@output_dir) + File.write(filename, contents) + end + end + end + end +end diff --git a/lib/gitlab/graphql/docs/templates/default.md.haml b/lib/gitlab/graphql/docs/templates/default.md.haml new file mode 100644 index 00000000000..cc22d43ab4f --- /dev/null +++ b/lib/gitlab/graphql/docs/templates/default.md.haml @@ -0,0 +1,25 @@ +-# haml-lint:disable UnnecessaryStringOutput + += auto_generated_comment + +:plain + # GraphQL API Resources + + This documentation is self-generated based on GitLab current GraphQL schema. + + The API can be explored interactively using the [GraphiQL IDE](../index.md#graphiql). + + ## Objects +\ +- objects.each do |type| + - unless type[:fields].empty? + = "### #{type[:name]}" + \ + ~ "| Name | Type | Description |" + ~ "| --- | ---- | ---------- |" + - type[:fields].each do |field| + = "| `#{field[:name]}` | #{render_field_type(field[:type][:info])} | #{field[:description]} |" + \ + + + diff --git a/lib/gitlab/hashed_storage/migrator.rb b/lib/gitlab/hashed_storage/migrator.rb index 1f0deebea39..6a8e16f5a85 100644 --- a/lib/gitlab/hashed_storage/migrator.rb +++ b/lib/gitlab/hashed_storage/migrator.rb @@ -62,6 +62,7 @@ module Gitlab # Flag a project to be migrated to Hashed Storage # # @param [Project] project that will be migrated + # rubocop:disable Gitlab/RailsLogger def migrate(project) Rails.logger.info "Starting storage migration of #{project.full_path} (ID=#{project.id})..." @@ -69,10 +70,12 @@ module Gitlab rescue => err Rails.logger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}") end + # rubocop:enable Gitlab/RailsLogger # Flag a project to be rolled-back to Legacy Storage # # @param [Project] project that will be rolled-back + # rubocop:disable Gitlab/RailsLogger def rollback(project) Rails.logger.info "Starting storage rollback of #{project.full_path} (ID=#{project.id})..." @@ -80,6 +83,7 @@ module Gitlab rescue => err Rails.logger.error("#{err.message} rolling-back storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}") end + # rubocop:enable Gitlab/RailsLogger # Returns whether we have any pending storage migration # diff --git a/lib/gitlab/hashed_storage/rake_helper.rb b/lib/gitlab/hashed_storage/rake_helper.rb index 87a31a37e3f..14727b03ce9 100644 --- a/lib/gitlab/hashed_storage/rake_helper.rb +++ b/lib/gitlab/hashed_storage/rake_helper.rb @@ -19,8 +19,12 @@ module Gitlab ENV['ID_TO'] end + def self.using_ranges? + !range_from.nil? && !range_to.nil? + end + def self.range_single_item? - !range_from.nil? && range_from == range_to + using_ranges? && range_from == range_to end # rubocop: disable CodeReuse/ActiveRecord diff --git a/lib/gitlab/health_checks/simple_abstract_check.rb b/lib/gitlab/health_checks/simple_abstract_check.rb index 3588260d6eb..5a1e8c2a1dd 100644 --- a/lib/gitlab/health_checks/simple_abstract_check.rb +++ b/lib/gitlab/health_checks/simple_abstract_check.rb @@ -18,7 +18,7 @@ module Gitlab def metrics result, elapsed = with_timing(&method(:check)) - Rails.logger.error("#{human_name} check returned unexpected result #{result}") unless successful?(result) + Rails.logger.error("#{human_name} check returned unexpected result #{result}") unless successful?(result) # rubocop:disable Gitlab/RailsLogger [ metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0), metric("#{metric_prefix}_success", successful?(result) ? 1 : 0), diff --git a/lib/gitlab/http.rb b/lib/gitlab/http.rb index db2b4dde244..58bce613a98 100644 --- a/lib/gitlab/http.rb +++ b/lib/gitlab/http.rb @@ -10,9 +10,9 @@ module Gitlab RedirectionTooDeep = Class.new(StandardError) HTTP_ERRORS = [ - SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, - Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, - Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError, + SocketError, OpenSSL::SSL::SSLError, OpenSSL::OpenSSLError, + Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, + Net::OpenTimeout, Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError, Gitlab::HTTP::RedirectionTooDeep ].freeze diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb index deb2f59f05f..0b534a5bafc 100644 --- a/lib/gitlab/import_export/merge_request_parser.rb +++ b/lib/gitlab/import_export/merge_request_parser.rb @@ -43,7 +43,7 @@ module Gitlab target_ref = Gitlab::Git::BRANCH_REF_PREFIX + @merge_request.source_branch unless @project.repository.fetch_source_branch!(@project.repository, @diff_head_sha, target_ref) - Rails.logger.warn("Import/Export warning: Failed to create #{target_ref} for MR: #{@merge_request.iid}") + Rails.logger.warn("Import/Export warning: Failed to create #{target_ref} for MR: #{@merge_request.iid}") # rubocop:disable Gitlab/RailsLogger end end diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb index 20caadb89c0..dec99c23a2d 100644 --- a/lib/gitlab/import_export/project_tree_restorer.rb +++ b/lib/gitlab/import_export/project_tree_restorer.rb @@ -20,7 +20,7 @@ module Gitlab json = IO.read(@path) @tree_hash = ActiveSupport::JSON.decode(json) rescue => e - Rails.logger.error("Import/Export error: #{e.message}") + Rails.logger.error("Import/Export error: #{e.message}") # rubocop:disable Gitlab/RailsLogger raise Gitlab::ImportExport::Error.new('Incorrect JSON format') end diff --git a/lib/gitlab/import_export/saver.rb b/lib/gitlab/import_export/saver.rb index 72f575db095..bea7a7cce65 100644 --- a/lib/gitlab/import_export/saver.rb +++ b/lib/gitlab/import_export/saver.rb @@ -18,7 +18,7 @@ module Gitlab if compress_and_save remove_export_path - Rails.logger.info("Saved project export #{archive_file}") + Rails.logger.info("Saved project export #{archive_file}") # rubocop:disable Gitlab/RailsLogger save_upload else diff --git a/lib/gitlab/import_export/version_checker.rb b/lib/gitlab/import_export/version_checker.rb index 6d978d00ea5..86ea7a30e69 100644 --- a/lib/gitlab/import_export/version_checker.rb +++ b/lib/gitlab/import_export/version_checker.rb @@ -36,7 +36,7 @@ module Gitlab def different_version?(version) Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version) rescue => e - Rails.logger.error("Import/Export error: #{e.message}") + Rails.logger.error("Import/Export error: #{e.message}") # rubocop:disable Gitlab/RailsLogger raise Gitlab::ImportExport::Error.new('Incorrect VERSION format') end end diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb index de14df56555..1350924cd76 100644 --- a/lib/gitlab/kubernetes/kube_client.rb +++ b/lib/gitlab/kubernetes/kube_client.rb @@ -59,6 +59,13 @@ module Gitlab # RBAC methods delegates to the apis/rbac.authorization.k8s.io api # group client + delegate :create_role, + :get_role, + :update_role, + to: :rbac_client + + # RBAC methods delegates to the apis/rbac.authorization.k8s.io api + # group client delegate :create_role_binding, :get_role_binding, :update_role_binding, diff --git a/lib/gitlab/kubernetes/role.rb b/lib/gitlab/kubernetes/role.rb new file mode 100644 index 00000000000..096f60f0372 --- /dev/null +++ b/lib/gitlab/kubernetes/role.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Gitlab + module Kubernetes + class Role + def initialize(name:, namespace:, rules:) + @name = name + @namespace = namespace + @rules = rules + end + + def generate + ::Kubeclient::Resource.new( + metadata: { name: name, namespace: namespace }, + rules: rules + ) + end + + private + + attr_reader :name, :namespace, :rules + end + end +end diff --git a/lib/gitlab/kubernetes/role_binding.rb b/lib/gitlab/kubernetes/role_binding.rb index cb0cb42d007..0404fb4453c 100644 --- a/lib/gitlab/kubernetes/role_binding.rb +++ b/lib/gitlab/kubernetes/role_binding.rb @@ -3,9 +3,10 @@ module Gitlab module Kubernetes class RoleBinding - def initialize(name:, role_name:, namespace:, service_account_name:) + def initialize(name:, role_name:, role_kind:, namespace:, service_account_name:) @name = name @role_name = role_name + @role_kind = role_kind @namespace = namespace @service_account_name = service_account_name end @@ -20,7 +21,7 @@ module Gitlab private - attr_reader :name, :role_name, :namespace, :service_account_name + attr_reader :name, :role_name, :role_kind, :namespace, :service_account_name def metadata { name: name, namespace: namespace } @@ -29,7 +30,7 @@ module Gitlab def role_ref { apiGroup: 'rbac.authorization.k8s.io', - kind: 'ClusterRole', + kind: role_kind, name: role_name } end diff --git a/lib/gitlab/lets_encrypt.rb b/lib/gitlab/lets_encrypt.rb index cdf24f24647..08ad2ab91b0 100644 --- a/lib/gitlab/lets_encrypt.rb +++ b/lib/gitlab/lets_encrypt.rb @@ -2,15 +2,8 @@ module Gitlab module LetsEncrypt - def self.enabled?(pages_domain = nil) - return false unless Gitlab::CurrentSettings.lets_encrypt_terms_of_service_accepted - - return false unless Feature.enabled?(:pages_auto_ssl) - - # If no domain is passed, just check whether we're enabled globally - return true unless pages_domain - - !!pages_domain.project && Feature.enabled?(:pages_auto_ssl_for_project, pages_domain.project) + def self.enabled? + Gitlab::CurrentSettings.lets_encrypt_terms_of_service_accepted end end end diff --git a/lib/gitlab/markdown_cache/active_record/extension.rb b/lib/gitlab/markdown_cache/active_record/extension.rb index f3abe631779..233d3bf1ac7 100644 --- a/lib/gitlab/markdown_cache/active_record/extension.rb +++ b/lib/gitlab/markdown_cache/active_record/extension.rb @@ -26,10 +26,6 @@ module Gitlab attrs end - def changed_markdown_fields - changed_attributes.keys.map(&:to_s) & cached_markdown_fields.markdown_fields.map(&:to_s) - end - def write_markdown_field(field_name, value) write_attribute(field_name, value) end diff --git a/lib/gitlab/markdown_cache/redis/extension.rb b/lib/gitlab/markdown_cache/redis/extension.rb index 97fc23343b4..af3237f4ba6 100644 --- a/lib/gitlab/markdown_cache/redis/extension.rb +++ b/lib/gitlab/markdown_cache/redis/extension.rb @@ -36,8 +36,8 @@ module Gitlab false end - def changed_markdown_fields - [] + def changed_attributes + {} end def cached_markdown diff --git a/lib/gitlab/metrics/dashboard/url.rb b/lib/gitlab/metrics/dashboard/url.rb new file mode 100644 index 00000000000..b197e7ca86b --- /dev/null +++ b/lib/gitlab/metrics/dashboard/url.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# Manages url matching for metrics dashboards. +module Gitlab + module Metrics + module Dashboard + class Url + class << self + # Matches urls for a metrics dashboard. This could be + # either the /metrics endpoint or the /metrics_dashboard + # endpoint. + # + # EX - https://<host>/<namespace>/<project>/environments/<env_id>/metrics + def regex + %r{ + (?<url> + #{Regexp.escape(Gitlab.config.gitlab.url)} + \/#{Project.reference_pattern} + (?:\/\-)? + \/environments + \/(?<environment>\d+) + \/metrics + (?<query> + \?[a-z0-9_=-]+ + (&[a-z0-9_=-]+)* + )? + (?<anchor>\#[a-z0-9_-]+)? + ) + }x + end + + # Builds a metrics dashboard url based on the passed in arguments + def build_dashboard_url(*args) + Gitlab::Routing.url_helpers.metrics_dashboard_namespace_project_environment_url(*args) + end + end + end + end + end +end diff --git a/lib/gitlab/metrics/samplers/base_sampler.rb b/lib/gitlab/metrics/samplers/base_sampler.rb index 6a062e93f0f..d7d848d2833 100644 --- a/lib/gitlab/metrics/samplers/base_sampler.rb +++ b/lib/gitlab/metrics/samplers/base_sampler.rb @@ -19,7 +19,7 @@ module Gitlab def safe_sample sample rescue => e - Rails.logger.warn("#{self.class}: #{e}, stopping") + Rails.logger.warn("#{self.class}: #{e}, stopping") # rubocop:disable Gitlab/RailsLogger stop end diff --git a/lib/gitlab/metrics/samplers/puma_sampler.rb b/lib/gitlab/metrics/samplers/puma_sampler.rb index 25e40c70230..4e835f37c04 100644 --- a/lib/gitlab/metrics/samplers/puma_sampler.rb +++ b/lib/gitlab/metrics/samplers/puma_sampler.rb @@ -43,7 +43,7 @@ module Gitlab def puma_stats Puma.stats rescue NoMethodError - Rails.logger.info "PumaSampler: stats are not available yet, waiting for Puma to boot" + Rails.logger.info "PumaSampler: stats are not available yet, waiting for Puma to boot" # rubocop:disable Gitlab/RailsLogger nil end diff --git a/lib/gitlab/metrics/samplers/unicorn_sampler.rb b/lib/gitlab/metrics/samplers/unicorn_sampler.rb index 9af7e0afed4..355f938704e 100644 --- a/lib/gitlab/metrics/samplers/unicorn_sampler.rb +++ b/lib/gitlab/metrics/samplers/unicorn_sampler.rb @@ -54,7 +54,16 @@ module Gitlab end def unicorn_workers_count - `pgrep -f '[u]nicorn_rails worker.+ #{Rails.root.to_s}'`.split.count + http_servers.sum(&:worker_processes) # rubocop: disable CodeReuse/ActiveRecord + end + + # Traversal of ObjectSpace is expensive, on fully loaded application + # it takes around 80ms. The instances of HttpServers are not a subject + # to change so we can cache the list of servers. + def http_servers + return [] unless defined?(::Unicorn::HttpServer) + + @http_servers ||= ObjectSpace.each_object(::Unicorn::HttpServer).to_a end end end diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb index 5c2f07b95e2..51f48095cb5 100644 --- a/lib/gitlab/metrics/system.rb +++ b/lib/gitlab/metrics/system.rb @@ -63,10 +63,6 @@ module Gitlab def self.monotonic_time Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) end - - def self.clk_tck - @clk_tck ||= `getconf CLK_TCK`.to_i - end end end end diff --git a/lib/gitlab/middleware/read_only/controller.rb b/lib/gitlab/middleware/read_only/controller.rb index 817db12ac55..53e55269c6e 100644 --- a/lib/gitlab/middleware/read_only/controller.rb +++ b/lib/gitlab/middleware/read_only/controller.rb @@ -25,7 +25,7 @@ module Gitlab def call if disallowed_request? && Gitlab::Database.read_only? - Rails.logger.debug('GitLab ReadOnly: preventing possible non read-only operation') + Rails.logger.debug('GitLab ReadOnly: preventing possible non read-only operation') # rubocop:disable Gitlab/RailsLogger if json_request? return [403, { 'Content-Type' => APPLICATION_JSON }, [{ 'message' => ERROR_MESSAGE }.to_json]] diff --git a/lib/gitlab/pages_client.rb b/lib/gitlab/pages_client.rb index d74fdba2241..281eafb142f 100644 --- a/lib/gitlab/pages_client.rb +++ b/lib/gitlab/pages_client.rb @@ -110,7 +110,7 @@ module Gitlab end rescue Errno::EACCES => ex # TODO stop rescuing this exception in GitLab 11.0 https://gitlab.com/gitlab-org/gitlab-ce/issues/45672 - Rails.logger.error("Could not write pages admin token file: #{ex}") + Rails.logger.error("Could not write pages admin token file: #{ex}") # rubocop:disable Gitlab/RailsLogger rescue Errno::EEXIST # Another process wrote the token file concurrently with us. Use their token, not ours. end diff --git a/lib/gitlab/patch/active_record_query_cache.rb b/lib/gitlab/patch/active_record_query_cache.rb new file mode 100644 index 00000000000..71d66bdbe02 --- /dev/null +++ b/lib/gitlab/patch/active_record_query_cache.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +# Fixes a bug where the query cache isn't aware of the shared +# ActiveRecord connection used in tests +# https://github.com/rails/rails/issues/36587 + +# To be removed with https://gitlab.com/gitlab-org/gitlab-ce/issues/64413 + +module Gitlab + module Patch + module ActiveRecordQueryCache + # rubocop:disable Gitlab/ModuleWithInstanceVariables + def enable_query_cache! + @query_cache_enabled[connection_cache_key(current_thread)] = true + connection.enable_query_cache! if active_connection? + end + + def disable_query_cache! + @query_cache_enabled.delete connection_cache_key(current_thread) + connection.disable_query_cache! if active_connection? + end + + def query_cache_enabled + @query_cache_enabled[connection_cache_key(current_thread)] + end + + def active_connection? + @thread_cached_conns[connection_cache_key(current_thread)] + end + + private + + def current_thread + @lock_thread || Thread.current + end + # rubocop:enable Gitlab/ModuleWithInstanceVariables + end + end +end diff --git a/lib/gitlab/phabricator_import/cache/map.rb b/lib/gitlab/phabricator_import/cache/map.rb index fa8b37b20ca..6a2841b6a8e 100644 --- a/lib/gitlab/phabricator_import/cache/map.rb +++ b/lib/gitlab/phabricator_import/cache/map.rb @@ -9,9 +9,15 @@ module Gitlab def get_gitlab_model(phabricator_id) cached_info = get(phabricator_id) - return unless cached_info[:classname] && cached_info[:database_id] - cached_info[:classname].constantize.find_by_id(cached_info[:database_id]) + if cached_info[:classname] && cached_info[:database_id] + object = cached_info[:classname].constantize.find_by_id(cached_info[:database_id]) + else + object = yield if block_given? + set_gitlab_model(object, phabricator_id) if object + end + + object end def set_gitlab_model(object, phabricator_id) diff --git a/lib/gitlab/phabricator_import/conduit/user.rb b/lib/gitlab/phabricator_import/conduit/user.rb new file mode 100644 index 00000000000..fc8c3f7cde9 --- /dev/null +++ b/lib/gitlab/phabricator_import/conduit/user.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true +module Gitlab + module PhabricatorImport + module Conduit + class User + MAX_PAGE_SIZE = 100 + + def initialize(phabricator_url:, api_token:) + @client = Client.new(phabricator_url, api_token) + end + + def users(phids) + phids.each_slice(MAX_PAGE_SIZE).map { |limited_phids| get_page(limited_phids) } + end + + private + + def get_page(phids) + UsersResponse.new(get_users(phids)) + end + + def get_users(phids) + client.get('user.search', + params: { constraints: { phids: phids } }) + end + + attr_reader :client + end + end + end +end diff --git a/lib/gitlab/phabricator_import/conduit/users_response.rb b/lib/gitlab/phabricator_import/conduit/users_response.rb new file mode 100644 index 00000000000..3dfb29a7be5 --- /dev/null +++ b/lib/gitlab/phabricator_import/conduit/users_response.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module PhabricatorImport + module Conduit + class UsersResponse + def initialize(conduit_response) + @conduit_response = conduit_response + end + + def users + @users ||= conduit_response.data.map do |user_json| + Gitlab::PhabricatorImport::Representation::User.new(user_json) + end + end + + private + + attr_reader :conduit_response + end + end + end +end diff --git a/lib/gitlab/phabricator_import/issues/task_importer.rb b/lib/gitlab/phabricator_import/issues/task_importer.rb index 40d4392cbc1..77ee11c7cdd 100644 --- a/lib/gitlab/phabricator_import/issues/task_importer.rb +++ b/lib/gitlab/phabricator_import/issues/task_importer.rb @@ -8,9 +8,7 @@ module Gitlab end def execute - # TODO: get the user from the project namespace from the username loaded by Phab-id - # https://gitlab.com/gitlab-org/gitlab-ce/issues/60565 - issue.author = User.ghost + issue.author = user_finder.find(task.author_phid) || User.ghost # TODO: Reformat the description with attachments, escaping accidental # links and add attachments @@ -19,6 +17,10 @@ module Gitlab save! + if owner = user_finder.find(task.owner_phid) + issue.assignees << owner + end + issue end @@ -41,6 +43,10 @@ module Gitlab project.issues.new end + def user_finder + @issue_finder ||= Gitlab::PhabricatorImport::UserFinder.new(project, task.phids) + end + def find_issue_by_phabricator_id(phabricator_id) object_map.get_gitlab_model(phabricator_id) end diff --git a/lib/gitlab/phabricator_import/representation/task.rb b/lib/gitlab/phabricator_import/representation/task.rb index 6aedc71b626..ba93fb37a8e 100644 --- a/lib/gitlab/phabricator_import/representation/task.rb +++ b/lib/gitlab/phabricator_import/representation/task.rb @@ -11,6 +11,18 @@ module Gitlab json['phid'] end + def author_phid + json['fields']['authorPHID'] + end + + def owner_phid + json['fields']['ownerPHID'] + end + + def phids + @phids ||= [author_phid, owner_phid] + end + def issue_attributes @issue_attributes ||= { title: issue_title, diff --git a/lib/gitlab/phabricator_import/representation/user.rb b/lib/gitlab/phabricator_import/representation/user.rb new file mode 100644 index 00000000000..7fd7cecc6ae --- /dev/null +++ b/lib/gitlab/phabricator_import/representation/user.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Gitlab + module PhabricatorImport + module Representation + class User + def initialize(json) + @json = json + end + + def phabricator_id + json['phid'] + end + + def username + json['fields']['username'] + end + + private + + attr_reader :json + end + end + end +end diff --git a/lib/gitlab/phabricator_import/user_finder.rb b/lib/gitlab/phabricator_import/user_finder.rb new file mode 100644 index 00000000000..4b50431e0e0 --- /dev/null +++ b/lib/gitlab/phabricator_import/user_finder.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Gitlab + module PhabricatorImport + class UserFinder + def initialize(project, phids) + @project, @phids = project, phids + @loaded_phids = Set.new + end + + def find(phid) + found_user = object_map.get_gitlab_model(phid) do + find_user_for_phid(phid) + end + + loaded_phids << phid + + found_user + end + + private + + attr_reader :project, :phids, :loaded_phids + + def object_map + @object_map ||= Gitlab::PhabricatorImport::Cache::Map.new(project) + end + + def find_user_for_phid(phid) + phabricator_user = phabricator_users.find { |u| u.phabricator_id == phid } + return unless phabricator_user + + project.authorized_users.find_by_username(phabricator_user.username) + end + + def phabricator_users + @user_responses ||= client.users(users_to_request).flat_map(&:users) + end + + def users_to_request + phids - loaded_phids.to_a + end + + def client + @client ||= + Gitlab::PhabricatorImport::Conduit::User + .new(phabricator_url: project.import_data.data['phabricator_url'], + api_token: project.import_data.credentials[:api_token]) + end + end + end +end diff --git a/lib/gitlab/reference_counter.rb b/lib/gitlab/reference_counter.rb index d2dbc6f5ef5..1c43de35816 100644 --- a/lib/gitlab/reference_counter.rb +++ b/lib/gitlab/reference_counter.rb @@ -22,6 +22,7 @@ module Gitlab end end + # rubocop:disable Gitlab/RailsLogger def decrease redis_cmd do |redis| current_value = redis.decr(key) @@ -32,6 +33,7 @@ module Gitlab end end end + # rubocop:enable Gitlab/RailsLogger private @@ -39,7 +41,7 @@ module Gitlab Gitlab::Redis::SharedState.with { |redis| yield(redis) } true rescue => e - Rails.logger.warn("GitLab: An unexpected error occurred in writing to Redis: #{e}") + Rails.logger.warn("GitLab: An unexpected error occurred in writing to Redis: #{e}") # rubocop:disable Gitlab/RailsLogger false end end diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index 7a1a2eaf6c0..e43147a3f37 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -4,14 +4,6 @@ module Gitlab module Regex extend self - def namespace_name_regex - @namespace_name_regex ||= /\A[\p{Alnum}\p{Pd}_\. ]*\z/.freeze - end - - def namespace_name_regex_message - "can contain only letters, digits, '_', '.', dash and space." - end - def project_name_regex @project_name_regex ||= /\A[\p{Alnum}\u{00A9}-\u{1f9c0}_][\p{Alnum}\p{Pd}\u{00A9}-\u{1f9c0}_\. ]*\z/.freeze end diff --git a/lib/gitlab/repository_cache_adapter.rb b/lib/gitlab/repository_cache_adapter.rb index 931298b5117..e40c366ed02 100644 --- a/lib/gitlab/repository_cache_adapter.rb +++ b/lib/gitlab/repository_cache_adapter.rb @@ -145,7 +145,7 @@ module Gitlab def expire_method_caches(methods) methods.each do |name| unless cached_methods.include?(name.to_sym) - Rails.logger.error "Requested to expire non-existent method '#{name}' for Repository" + Rails.logger.error "Requested to expire non-existent method '#{name}' for Repository" # rubocop:disable Gitlab/RailsLogger next end diff --git a/lib/gitlab/sanitizers/exif.rb b/lib/gitlab/sanitizers/exif.rb index 0928ccdc324..bb4e4ce7bbc 100644 --- a/lib/gitlab/sanitizers/exif.rb +++ b/lib/gitlab/sanitizers/exif.rb @@ -48,7 +48,7 @@ module Gitlab attr_reader :logger - def initialize(logger: Rails.logger) + def initialize(logger: Rails.logger) # rubocop:disable Gitlab/RailsLogger @logger = logger end diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 93182607616..0fa17b3f559 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -78,7 +78,7 @@ module Gitlab true rescue => err # Once the Rugged codes gets removes this can be improved - Rails.logger.error("Failed to add repository #{storage}/#{disk_path}: #{err}") + Rails.logger.error("Failed to add repository #{storage}/#{disk_path}: #{err}") # rubocop:disable Gitlab/RailsLogger false end @@ -153,7 +153,7 @@ module Gitlab !!rm_directory(storage, "#{name}.git") rescue ArgumentError => e - Rails.logger.warn("Repository does not exist: #{e} at: #{name}.git") + Rails.logger.warn("Repository does not exist: #{e} at: #{name}.git") # rubocop:disable Gitlab/RailsLogger false end @@ -238,7 +238,7 @@ module Gitlab def remove_keys_not_found_in_db return unless self.authorized_keys_enabled? - Rails.logger.info("Removing keys not found in DB") + Rails.logger.info("Removing keys not found in DB") # rubocop:disable Gitlab/RailsLogger batch_read_key_ids do |ids_in_file| ids_in_file.uniq! @@ -248,7 +248,7 @@ module Gitlab ids_to_remove = ids_in_file - keys_in_db.pluck(:id) ids_to_remove.each do |id| - Rails.logger.info("Removing key-#{id} not found in DB") + Rails.logger.info("Removing key-#{id} not found in DB") # rubocop:disable Gitlab/RailsLogger remove_key("key-#{id}") end end @@ -368,7 +368,7 @@ module Gitlab return true if status.zero? - Rails.logger.error("gitlab-shell failed with error #{status}: #{output}") + Rails.logger.error("gitlab-shell failed with error #{status}: #{output}") # rubocop:disable Gitlab/RailsLogger false end @@ -465,7 +465,7 @@ module Gitlab end def logger - Rails.logger + Rails.logger # rubocop:disable Gitlab/RailsLogger end end end diff --git a/lib/gitlab/sql/pattern.rb b/lib/gitlab/sql/pattern.rb index fd108b4c124..f6edbfced7f 100644 --- a/lib/gitlab/sql/pattern.rb +++ b/lib/gitlab/sql/pattern.rb @@ -9,14 +9,16 @@ module Gitlab REGEX_QUOTED_WORD = /(?<=\A| )"[^"]+"(?= |\z)/.freeze class_methods do - def fuzzy_search(query, columns) - matches = columns.map { |col| fuzzy_arel_match(col, query) }.compact.reduce(:or) + def fuzzy_search(query, columns, use_minimum_char_limit: true) + matches = columns.map do |col| + fuzzy_arel_match(col, query, use_minimum_char_limit: use_minimum_char_limit) + end.compact.reduce(:or) where(matches) end - def to_pattern(query) - if partial_matching?(query) + def to_pattern(query, use_minimum_char_limit: true) + if partial_matching?(query, use_minimum_char_limit: use_minimum_char_limit) "%#{sanitize_sql_like(query)}%" else sanitize_sql_like(query) @@ -27,7 +29,9 @@ module Gitlab MIN_CHARS_FOR_PARTIAL_MATCHING end - def partial_matching?(query) + def partial_matching?(query, use_minimum_char_limit: true) + return true unless use_minimum_char_limit + query.length >= min_chars_for_partial_matching end @@ -35,14 +39,14 @@ module Gitlab # query - The text to search for. # lower_exact_match - When set to `true` we'll fall back to using # `LOWER(column) = query` instead of using `ILIKE`. - def fuzzy_arel_match(column, query, lower_exact_match: false) + def fuzzy_arel_match(column, query, lower_exact_match: false, use_minimum_char_limit: true) query = query.squish return unless query.present? - words = select_fuzzy_words(query) + words = select_fuzzy_words(query, use_minimum_char_limit: use_minimum_char_limit) if words.any? - words.map { |word| arel_table[column].matches(to_pattern(word)) }.reduce(:and) + words.map { |word| arel_table[column].matches(to_pattern(word, use_minimum_char_limit: use_minimum_char_limit)) }.reduce(:and) else # No words of at least 3 chars, but we can search for an exact # case insensitive match with the query as a whole @@ -56,7 +60,7 @@ module Gitlab end end - def select_fuzzy_words(query) + def select_fuzzy_words(query, use_minimum_char_limit: true) quoted_words = query.scan(REGEX_QUOTED_WORD) query = quoted_words.reduce(query) { |q, quoted_word| q.sub(quoted_word, '') } @@ -67,7 +71,7 @@ module Gitlab words.concat(quoted_words) - words.select { |word| partial_matching?(word) } + words.select { |word| partial_matching?(word, use_minimum_char_limit: use_minimum_char_limit) } end end end diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb index 9a8df719827..f6b2e2acf16 100644 --- a/lib/gitlab/url_blocker.rb +++ b/lib/gitlab/url_blocker.rb @@ -18,7 +18,6 @@ module Gitlab # enforce_sanitization - Raises error if URL includes any HTML/CSS/JS tags and argument is true. # # Returns an array with [<uri>, <original-hostname>]. - # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/ParameterLists def validate!( url, @@ -30,7 +29,6 @@ module Gitlab enforce_user: false, enforce_sanitization: false, dns_rebind_protection: true) - # rubocop:enable Metrics/CyclomaticComplexity # rubocop:enable Metrics/ParameterLists return [nil, nil] if url.nil? @@ -38,36 +36,31 @@ module Gitlab # Param url can be a string, URI or Addressable::URI uri = parse_url(url) - validate_html_tags!(uri) if enforce_sanitization + validate_uri( + uri: uri, + schemes: schemes, + ports: ports, + enforce_sanitization: enforce_sanitization, + enforce_user: enforce_user, + ascii_only: ascii_only + ) hostname = uri.hostname port = get_port(uri) - unless internal?(uri) - validate_scheme!(uri.scheme, schemes) - validate_port!(port, ports) if ports.any? - validate_user!(uri.user) if enforce_user - validate_hostname!(hostname) - validate_unicode_restriction!(uri) if ascii_only - end - - begin - addrs_info = Addrinfo.getaddrinfo(hostname, port, nil, :STREAM).map do |addr| - addr.ipv6_v4mapped? ? addr.ipv6_to_ipv4 : addr - end - rescue SocketError - return [uri, nil] - end + address_info = get_address_info(hostname, port) + return [uri, nil] unless address_info - protected_uri_with_hostname = enforce_uri_hostname(addrs_info, uri, hostname, dns_rebind_protection) + protected_uri_with_hostname = enforce_uri_hostname(address_info, uri, hostname, dns_rebind_protection) # Allow url from the GitLab instance itself but only for the configured hostname and ports return protected_uri_with_hostname if internal?(uri) - validate_localhost!(addrs_info) unless allow_localhost - validate_loopback!(addrs_info) unless allow_localhost - validate_local_network!(addrs_info) unless allow_local_network - validate_link_local!(addrs_info) unless allow_local_network + validate_local_request( + address_info: address_info, + allow_localhost: allow_localhost, + allow_local_network: allow_local_network + ) protected_uri_with_hostname end @@ -101,11 +94,44 @@ module Gitlab [uri, hostname] end + def validate_uri(uri:, schemes:, ports:, enforce_sanitization:, enforce_user:, ascii_only:) + validate_html_tags(uri) if enforce_sanitization + + return if internal?(uri) + + validate_scheme(uri.scheme, schemes) + validate_port(get_port(uri), ports) if ports.any? + validate_user(uri.user) if enforce_user + validate_hostname(uri.hostname) + validate_unicode_restriction(uri) if ascii_only + end + + def get_address_info(hostname, port) + Addrinfo.getaddrinfo(hostname, port, nil, :STREAM).map do |addr| + addr.ipv6_v4mapped? ? addr.ipv6_to_ipv4 : addr + end + rescue SocketError + end + + def validate_local_request(address_info:, allow_localhost:, allow_local_network:) + return if allow_local_network && allow_localhost + + unless allow_localhost + validate_localhost(address_info) + validate_loopback(address_info) + end + + unless allow_local_network + validate_local_network(address_info) + validate_link_local(address_info) + end + end + def get_port(uri) uri.port || uri.default_port end - def validate_html_tags!(uri) + def validate_html_tags(uri) uri_str = uri.to_s sanitized_uri = ActionController::Base.helpers.sanitize(uri_str, tags: []) if sanitized_uri != uri_str @@ -125,7 +151,7 @@ module Gitlab CGI.unescape(url.to_s) =~ /\n|\r/ end - def validate_port!(port, ports) + def validate_port(port, ports) return if port.blank? # Only ports under 1024 are restricted return if port >= 1024 @@ -134,20 +160,20 @@ module Gitlab raise BlockedUrlError, "Only allowed ports are #{ports.join(', ')}, and any over 1024" end - def validate_scheme!(scheme, schemes) + def validate_scheme(scheme, schemes) if scheme.blank? || (schemes.any? && !schemes.include?(scheme)) raise BlockedUrlError, "Only allowed schemes are #{schemes.join(', ')}" end end - def validate_user!(value) + def validate_user(value) return if value.blank? return if value =~ /\A\p{Alnum}/ raise BlockedUrlError, "Username needs to start with an alphanumeric character" end - def validate_hostname!(value) + def validate_hostname(value) return if value.blank? return if IPAddress.valid?(value) return if value =~ /\A\p{Alnum}/ @@ -155,13 +181,13 @@ module Gitlab raise BlockedUrlError, "Hostname or IP address invalid" end - def validate_unicode_restriction!(uri) + def validate_unicode_restriction(uri) return if uri.to_s.ascii_only? raise BlockedUrlError, "URI must be ascii only #{uri.to_s.dump}" end - def validate_localhost!(addrs_info) + def validate_localhost(addrs_info) local_ips = ["::", "0.0.0.0"] local_ips.concat(Socket.ip_address_list.map(&:ip_address)) @@ -170,19 +196,19 @@ module Gitlab raise BlockedUrlError, "Requests to localhost are not allowed" end - def validate_loopback!(addrs_info) + def validate_loopback(addrs_info) return unless addrs_info.any? { |addr| addr.ipv4_loopback? || addr.ipv6_loopback? } raise BlockedUrlError, "Requests to loopback addresses are not allowed" end - def validate_local_network!(addrs_info) + def validate_local_network(addrs_info) return unless addrs_info.any? { |addr| addr.ipv4_private? || addr.ipv6_sitelocal? || addr.ipv6_unique_local? } raise BlockedUrlError, "Requests to the local network are not allowed" end - def validate_link_local!(addrs_info) + def validate_link_local(addrs_info) netmask = IPAddr.new('169.254.0.0/16') return unless addrs_info.any? { |addr| addr.ipv6_linklocal? || netmask.include?(addr.ip_address) } diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb index 722e3e04d1c..eea7daa3d8e 100644 --- a/lib/mattermost/session.rb +++ b/lib/mattermost/session.rb @@ -41,7 +41,7 @@ module Mattermost begin yield self rescue Errno::ECONNREFUSED => e - Rails.logger.error(e.message + "\n" + e.backtrace.join("\n")) + Rails.logger.error(e.message + "\n" + e.backtrace.join("\n")) # rubocop:disable Gitlab/RailsLogger raise Mattermost::NoSessionError ensure destroy diff --git a/lib/microsoft_teams/notifier.rb b/lib/microsoft_teams/notifier.rb index c7dec09ba6b..a7dcd322e27 100644 --- a/lib/microsoft_teams/notifier.rb +++ b/lib/microsoft_teams/notifier.rb @@ -20,7 +20,7 @@ module MicrosoftTeams result = true if response rescue Gitlab::HTTP::Error, StandardError => error - Rails.logger.info("#{self.class.name}: Error while connecting to #{@webhook}: #{error.message}") + Rails.logger.info("#{self.class.name}: Error while connecting to #{@webhook}: #{error.message}") # rubocop:disable Gitlab/RailsLogger end result diff --git a/lib/peek/views/redis.rb b/lib/peek/views/redis.rb index ad3c3c9fe01..73de8672fa4 100644 --- a/lib/peek/views/redis.rb +++ b/lib/peek/views/redis.rb @@ -37,6 +37,8 @@ end module Peek module Views module RedisDetailed + REDACTED_MARKER = "<redacted>" + def results super.merge(details: details) end @@ -57,10 +59,12 @@ module Peek end def format_command(cmd) + if cmd.length >= 2 && cmd.first =~ /^auth$/i + cmd[-1] = REDACTED_MARKER # Scrub out the value of the SET calls to avoid binary # data or large data from spilling into the view - if cmd.length >= 2 && cmd.first =~ /set/i - cmd[-1] = "<redacted>" + elsif cmd.length >= 3 && cmd.first =~ /set/i + cmd[2..-1] = REDACTED_MARKER end cmd.join(' ') diff --git a/lib/rspec_flaky/listener.rb b/lib/rspec_flaky/listener.rb index 19cc0baa2d3..bf15130d17e 100644 --- a/lib/rspec_flaky/listener.rb +++ b/lib/rspec_flaky/listener.rb @@ -32,6 +32,7 @@ module RspecFlaky flaky_examples[current_example.uid] = flaky_example end + # rubocop:disable Gitlab/RailsLogger def dump_summary(_) RspecFlaky::Report.new(flaky_examples).write(RspecFlaky::Config.flaky_examples_report_path) # write_report_file(flaky_examples, RspecFlaky::Config.flaky_examples_report_path) @@ -45,6 +46,7 @@ module RspecFlaky # write_report_file(new_flaky_examples, RspecFlaky::Config.new_flaky_examples_report_path) end end + # rubocop:enable Gitlab/RailsLogger private diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake index 105ef417df3..88172e26c67 100644 --- a/lib/tasks/gitlab/cleanup.rake +++ b/lib/tasks/gitlab/cleanup.rake @@ -147,6 +147,7 @@ namespace :gitlab do ENV['NICENESS'].presence end + # rubocop:disable Gitlab/RailsLogger def logger return @logger if defined?(@logger) @@ -159,5 +160,6 @@ namespace :gitlab do Rails.logger end end + # rubocop:enable Gitlab/RailsLogger end end diff --git a/lib/tasks/gitlab/graphql.rake b/lib/tasks/gitlab/graphql.rake new file mode 100644 index 00000000000..c53d55ceea2 --- /dev/null +++ b/lib/tasks/gitlab/graphql.rake @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +return if Rails.env.production? + +namespace :gitlab do + OUTPUT_DIR = Rails.root.join("doc/api/graphql/reference").freeze + TEMPLATES_DIR = 'lib/gitlab/graphql/docs/templates/'.freeze + + namespace :graphql do + desc 'GitLab | Generate GraphQL docs' + task compile_docs: :environment do + renderer = Gitlab::Graphql::Docs::Renderer.new(GitlabSchema.graphql_definition, render_options) + + renderer.render + + puts "Documentation compiled." + end + end +end + +def render_options + { + output_dir: OUTPUT_DIR, + template: Rails.root.join(TEMPLATES_DIR, 'default.md.haml') + } +end diff --git a/lib/tasks/gitlab/storage.rake b/lib/tasks/gitlab/storage.rake index 954f827f716..ccc96b7edfb 100644 --- a/lib/tasks/gitlab/storage.rake +++ b/lib/tasks/gitlab/storage.rake @@ -3,50 +3,44 @@ namespace :gitlab do desc 'GitLab | Storage | Migrate existing projects to Hashed Storage' task migrate_to_hashed: :environment do if Gitlab::Database.read_only? - warn 'This task requires database write access. Exiting.' - - next + abort 'This task requires database write access. Exiting.' end storage_migrator = Gitlab::HashedStorage::Migrator.new helper = Gitlab::HashedStorage::RakeHelper if storage_migrator.rollback_pending? - warn "There is already a rollback operation in progress, " \ + abort "There is already a rollback operation in progress, " \ "running a migration at the same time may have unexpected consequences." - - next end if helper.range_single_item? project = Project.with_unmigrated_storage.find_by(id: helper.range_from) unless project - warn "There are no projects requiring storage migration with ID=#{helper.range_from}" - - next + abort "There are no projects requiring storage migration with ID=#{helper.range_from}" end puts "Enqueueing storage migration of #{project.full_path} (ID=#{project.id})..." storage_migrator.migrate(project) + else + legacy_projects_count = if helper.using_ranges? + Project.with_unmigrated_storage.id_in(helper.range_from..helper.range_to).count + else + Project.with_unmigrated_storage.count + end + + if legacy_projects_count == 0 + abort 'There are no projects requiring storage migration. Nothing to do!' + end - next - end - - legacy_projects_count = Project.with_unmigrated_storage.count - - if legacy_projects_count == 0 - warn 'There are no projects requiring storage migration. Nothing to do!' - - next - end - - print "Enqueuing migration of #{legacy_projects_count} projects in batches of #{helper.batch_size}" + print "Enqueuing migration of #{legacy_projects_count} projects in batches of #{helper.batch_size}" - helper.project_id_batches_migration do |start, finish| - storage_migrator.bulk_schedule_migration(start: start, finish: finish) + helper.project_id_batches_migration do |start, finish| + storage_migrator.bulk_schedule_migration(start: start, finish: finish) - print '.' + print '.' + end end puts ' Done!' @@ -55,50 +49,44 @@ namespace :gitlab do desc 'GitLab | Storage | Rollback existing projects to Legacy Storage' task rollback_to_legacy: :environment do if Gitlab::Database.read_only? - warn 'This task requires database write access. Exiting.' - - next + abort 'This task requires database write access. Exiting.' end storage_migrator = Gitlab::HashedStorage::Migrator.new helper = Gitlab::HashedStorage::RakeHelper if storage_migrator.migration_pending? - warn "There is already a migration operation in progress, " \ + abort "There is already a migration operation in progress, " \ "running a rollback at the same time may have unexpected consequences." - - next end if helper.range_single_item? project = Project.with_storage_feature(:repository).find_by(id: helper.range_from) unless project - warn "There are no projects that can be rolledback with ID=#{helper.range_from}" - - next + abort "There are no projects that can be rolledback with ID=#{helper.range_from}" end puts "Enqueueing storage rollback of #{project.full_path} (ID=#{project.id})..." storage_migrator.rollback(project) + else + hashed_projects_count = if helper.using_ranges? + Project.with_storage_feature(:repository).id_in(helper.range_from..helper.range_to).count + else + Project.with_storage_feature(:repository).count + end + + if hashed_projects_count == 0 + abort 'There are no projects that can have storage rolledback. Nothing to do!' + end - next - end - - hashed_projects_count = Project.with_storage_feature(:repository).count - - if hashed_projects_count == 0 - warn 'There are no projects that can have storage rolledback. Nothing to do!' - - next - end - - print "Enqueuing rollback of #{hashed_projects_count} projects in batches of #{helper.batch_size}" + print "Enqueuing rollback of #{hashed_projects_count} projects in batches of #{helper.batch_size}" - helper.project_id_batches_rollback do |start, finish| - storage_migrator.bulk_schedule_rollback(start: start, finish: finish) + helper.project_id_batches_rollback do |start, finish| + storage_migrator.bulk_schedule_rollback(start: start, finish: finish) - print '.' + print '.' + end end puts ' Done!' diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f8de4a5ac71..9e27cdd85e8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -124,9 +124,6 @@ msgstr "" msgid "%{commit_author_link} authored %{commit_timeago}" msgstr "" -msgid "%{counter_repositories} repositories, %{counter_wikis} wikis, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS" -msgstr "" - msgid "%{count} more" msgstr "" @@ -156,6 +153,9 @@ msgstr "" msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects." msgstr "" +msgid "%{icon}You are about to add %{usersTag} people to the discussion. Proceed with caution." +msgstr "" + msgid "%{issuableType} will be removed! Are you sure?" msgstr "" @@ -1905,6 +1905,9 @@ msgstr "" msgid "Cancel this job" msgstr "" +msgid "Cancelling Preview" +msgstr "" + msgid "Cannot be merged automatically" msgstr "" @@ -3260,6 +3263,9 @@ msgstr "" msgid "Create new..." msgstr "" +msgid "Create project" +msgstr "" + msgid "Create project label" msgstr "" @@ -3646,6 +3652,9 @@ msgstr "" msgid "Deployed" msgstr "" +msgid "Deployed %{deployedSince}" +msgstr "" + msgid "Deployed to" msgstr "" @@ -3796,6 +3805,9 @@ msgstr "" msgid "Download artifacts" msgstr "" +msgid "Download as" +msgstr "" + msgid "Download asset" msgstr "" @@ -4051,6 +4063,9 @@ msgstr "" msgid "Enter the merge request title" msgstr "" +msgid "Enter zen mode" +msgstr "" + msgid "Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. Additionally, they can be masked so they are hidden in job logs, though they must match certain regexp requirements to do so. You can use environment variables for passwords, secret keys, or whatever you want." msgstr "" @@ -4420,6 +4435,9 @@ msgstr "" msgid "Expand all" msgstr "" +msgid "Expand dropdown" +msgstr "" + msgid "Expand sidebar" msgstr "" @@ -4615,6 +4633,9 @@ msgstr "" msgid "Failure" msgstr "" +msgid "Fast-forward merge is not possible. Rebase the source branch onto the target branch or merge target branch into source branch to allow this merge request to be merged." +msgstr "" + msgid "Fast-forward merge without a merge commit" msgstr "" @@ -4797,6 +4818,9 @@ msgstr "" msgid "Friday" msgstr "" +msgid "From" +msgstr "" + msgid "From %{providerTitle}" msgstr "" @@ -5369,6 +5393,12 @@ msgstr "" msgid "ImageDiffViewer|Swipe" msgstr "" +msgid "ImageViewerDimensions|H" +msgstr "" + +msgid "ImageViewerDimensions|W" +msgstr "" + msgid "Impersonation has been disabled" msgstr "" @@ -6276,6 +6306,9 @@ msgstr "" msgid "Markdown enabled" msgstr "" +msgid "Markdown is supported" +msgstr "" + msgid "Marks this issue as a duplicate of %{duplicate_reference}." msgstr "" @@ -6722,6 +6755,9 @@ msgstr "" msgid "New Pipeline Schedule" msgstr "" +msgid "New Project" +msgstr "" + msgid "New Snippet" msgstr "" @@ -7360,6 +7396,12 @@ msgstr "" msgid "Pipeline" msgstr "" +msgid "Pipeline %{label}" +msgstr "" + +msgid "Pipeline %{label} for \"%{dataTitle}\"" +msgstr "" + msgid "Pipeline Schedule" msgstr "" @@ -8389,6 +8431,54 @@ msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "ProjectsNew|Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository." +msgstr "" + +msgid "ProjectsNew|Blank" +msgstr "" + +msgid "ProjectsNew|Blank project" +msgstr "" + +msgid "ProjectsNew|Contact an administrator to enable options for importing your project." +msgstr "" + +msgid "ProjectsNew|Create from template" +msgstr "" + +msgid "ProjectsNew|Creating project & repository." +msgstr "" + +msgid "ProjectsNew|Description format" +msgstr "" + +msgid "ProjectsNew|Import" +msgstr "" + +msgid "ProjectsNew|Import project" +msgstr "" + +msgid "ProjectsNew|Initialize repository with a README" +msgstr "" + +msgid "ProjectsNew|No import options available" +msgstr "" + +msgid "ProjectsNew|Please wait a moment, this page will automatically refresh when ready." +msgstr "" + +msgid "ProjectsNew|Project description %{tag_start}(optional)%{tag_end}" +msgstr "" + +msgid "ProjectsNew|Template" +msgstr "" + +msgid "ProjectsNew|Visibility Level" +msgstr "" + +msgid "ProjectsNew|Want to house several dependent projects under the same namespace? %{link_start}Create a group.%{link_end}" +msgstr "" + msgid "PrometheusService|%{exporters} with %{metrics} were found" msgstr "" @@ -8584,6 +8674,12 @@ msgstr "" msgid "Real-time features" msgstr "" +msgid "Rebase" +msgstr "" + +msgid "Rebase in progress" +msgstr "" + msgid "Receive notifications about your own activity" msgstr "" @@ -8832,6 +8928,9 @@ msgstr "" msgid "Repository storage" msgstr "" +msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects}" +msgstr "" + msgid "Request Access" msgstr "" @@ -9248,6 +9347,9 @@ msgstr "" msgid "Select members to invite" msgstr "" +msgid "Select merge moment" +msgstr "" + msgid "Select private project" msgstr "" @@ -9697,6 +9799,9 @@ msgstr "" msgid "Something went wrong while resolving this discussion. Please try again." msgstr "" +msgid "Something went wrong while stopping this environment. Please try again." +msgstr "" + msgid "Something went wrong, unable to search projects" msgstr "" @@ -10752,6 +10857,9 @@ msgstr "" msgid "This is a \"Ghost User\", created to hold all issues authored by users that have since been deleted. This user cannot be removed." msgstr "" +msgid "This is a Work in Progress" +msgstr "" + msgid "This is a confidential issue." msgstr "" @@ -10770,6 +10878,9 @@ msgstr "" msgid "This is your current session" msgstr "" +msgid "This issue is %{confidentialLinkStart}confidential%{linkEnd} and %{lockedLinkStart}locked%{linkEnd}." +msgstr "" + msgid "This issue is confidential" msgstr "" @@ -11179,10 +11290,10 @@ msgstr "" msgid "To preserve performance only <strong>%{display_size} of %{real_size}</strong> files are displayed." msgstr "" -msgid "To protect this issues confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private." +msgid "To protect this issue's confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private." msgstr "" -msgid "To protect this issues confidentiality, a private fork of this project was selected." +msgid "To protect this issue's confidentiality, a private fork of this project was selected." msgstr "" msgid "To see all the user's personal access tokens you must impersonate them first." @@ -11389,6 +11500,9 @@ msgstr "" msgid "U2F only works with HTTPS-enabled websites. Contact your administrator for more details." msgstr "" +msgid "Unable to apply suggestions to a deleted line." +msgstr "" + msgid "Unable to connect to Prometheus server" msgstr "" @@ -11497,6 +11611,9 @@ msgstr "" msgid "Unsubscribe from %{type}" msgstr "" +msgid "Until" +msgstr "" + msgid "Unverified" msgstr "" @@ -12573,6 +12690,9 @@ msgstr "" msgid "branch name" msgstr "" +msgid "by" +msgstr "" + msgid "cannot be changed if a personal project has container registry tags." msgstr "" @@ -12603,6 +12723,9 @@ msgstr "" msgid "could not read private key, is the passphrase correct?" msgstr "" +msgid "created" +msgstr "" + msgid "customize" msgstr "" @@ -12763,6 +12886,15 @@ msgstr "" msgid "mrWidgetCommitsAdded|1 merge commit" msgstr "" +msgid "mrWidgetNothingToMerge|Currently there are no changes in this merge request's source branch. Please push new commits or use a different branch." +msgstr "" + +msgid "mrWidgetNothingToMerge|Interested parties can even contribute by pushing commits if they want to." +msgstr "" + +msgid "mrWidgetNothingToMerge|Merge requests are a place to propose changes you have made to a project and discuss those changes with others." +msgstr "" + msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch" msgstr "" @@ -12880,6 +13012,9 @@ msgstr "" msgid "mrWidget|Request to merge" msgstr "" +msgid "mrWidget|Resolve WIP status" +msgstr "" + msgid "mrWidget|Resolve conflicts" msgstr "" @@ -12943,6 +13078,9 @@ msgstr "" msgid "mrWidget|This project is archived, write access has been disabled" msgstr "" +msgid "mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged" +msgstr "" + msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes." msgstr "" @@ -13028,6 +13166,9 @@ msgstr "" msgid "project" msgstr "" +msgid "project avatar" +msgstr "" + msgid "quick actions" msgstr "" @@ -13040,6 +13181,9 @@ msgstr "" msgid "remaining" msgstr "" +msgid "remove" +msgstr "" + msgid "remove due date" msgstr "" @@ -13110,12 +13254,21 @@ msgstr[1] "" msgid "to list" msgstr "" +msgid "toggle collapse" +msgstr "" + +msgid "toggle dropdown" +msgstr "" + msgid "triggered" msgstr "" msgid "updated" msgstr "" +msgid "user avatar" +msgstr "" + msgid "username" msgstr "" diff --git a/package.json b/package.json index 47e7dd9c9c9..44aa850860e 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,8 @@ "@babel/plugin-syntax-import-meta": "^7.2.0", "@babel/preset-env": "^7.4.4", "@gitlab/csslab": "^1.9.0", - "@gitlab/svgs": "^1.66.0", - "@gitlab/ui": "^5.3.2", + "@gitlab/svgs": "^1.67.0", + "@gitlab/ui": "^5.6.0", "apollo-cache-inmemory": "^1.5.1", "apollo-client": "^2.5.1", "apollo-link": "^1.2.11", diff --git a/qa/.rspec_parallel b/qa/.rspec_parallel new file mode 100644 index 00000000000..e5927927eaa --- /dev/null +++ b/qa/.rspec_parallel @@ -0,0 +1,5 @@ +--color +--format documentation +--format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log +--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log +--require spec_helper diff --git a/qa/Gemfile b/qa/Gemfile index 12994b85322..c46be8a0362 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -1,5 +1,6 @@ source 'https://rubygems.org' +gem 'gitlab-qa' gem 'pry-byebug', '~> 3.5.1', platform: :mri gem 'capybara', '~> 2.16.1' gem 'capybara-screenshot', '~> 1.0.18' @@ -11,3 +12,4 @@ gem 'nokogiri', '~> 1.10.3' gem 'rspec-retry', '~> 0.6.1' gem 'faker', '~> 1.6', '>= 1.6.6' gem 'knapsack', '~> 1.17' +gem 'parallel_tests', '~> 2.29' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index 6b0635ed0e2..73aabf2c6ad 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -35,6 +35,7 @@ GEM faker (1.9.3) i18n (>= 0.7) ffi (1.9.25) + gitlab-qa (4.0.0) http-cookie (1.0.3) domain_name (~> 0.5) i18n (0.9.1) @@ -53,6 +54,9 @@ GEM netrc (0.11.0) nokogiri (1.10.3) mini_portile2 (~> 2.4.0) + parallel (1.17.0) + parallel_tests (2.29.0) + parallel pry (0.11.3) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -104,8 +108,10 @@ DEPENDENCIES capybara (~> 2.16.1) capybara-screenshot (~> 1.0.18) faker (~> 1.6, >= 1.6.6) + gitlab-qa knapsack (~> 1.17) nokogiri (~> 1.10.3) + parallel_tests (~> 2.29) pry-byebug (~> 3.5.1) rake (~> 12.3.0) rspec (~> 3.7) diff --git a/qa/README.md b/qa/README.md index 124a79a36b4..bab19665dac 100644 --- a/qa/README.md +++ b/qa/README.md @@ -75,14 +75,14 @@ You can also supply specific tests to run as another parameter. For example, to run the repository-related specs, you can execute: ``` -bundle exec bin/qa Test::Instance::All http://localhost -- qa/specs/features/browser_ui/3_create/repository +bundle exec bin/qa Test::Instance::All http://localhost:3000 -- qa/specs/features/browser_ui/3_create/repository ``` Since the arguments would be passed to `rspec`, you could use all `rspec` options there. For example, passing `--backtrace` and also line number: ``` -bundle exec bin/qa Test::Instance::All http://localhost -- qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb:6 --backtrace +bundle exec bin/qa Test::Instance::All http://localhost:3000 -- qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb:6 --backtrace ``` Note that the separator `--` is required; all subsequent options will be @@ -140,7 +140,7 @@ tests that are expected to fail while a fix is in progress (similar to how can be used). ``` -bundle exec bin/qa Test::Instance::All http://localhost -- --tag quarantine +bundle exec bin/qa Test::Instance::All http://localhost:3000 -- --tag quarantine ``` If `quarantine` is used with other tags, tests will only be run if they have at @@ -159,7 +159,7 @@ option `--enable-feature FEATURE_FLAG`. For example, to enable the feature flag that enforces Gitaly request limits, you would use the command: ``` -bundle exec bin/qa Test::Instance::All http://localhost --enable-feature gitaly_enforce_requests_limits +bundle exec bin/qa Test::Instance::All http://localhost:3000 --enable-feature gitaly_enforce_requests_limits ``` This will instruct the QA framework to enable the `gitaly_enforce_requests_limits` @@ -360,6 +360,7 @@ module QA module Specs autoload :Config, 'qa/specs/config' autoload :Runner, 'qa/specs/runner' + autoload :ParallelRunner, 'qa/specs/parallel_runner' module Helpers autoload :Quarantine, 'qa/specs/helpers/quarantine' diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index d0fe2987b0a..130e5e33ab4 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -25,19 +25,10 @@ module QA end end - def retry_until(max_attempts: 3, reload: false) - attempts = 0 - - while attempts < max_attempts - result = yield - return result if result - - refresh if reload - - attempts += 1 + def retry_until(max_attempts: 3, reload: false, sleep_interval: 0) + QA::Support::Retrier.retry_until(max_attempts: max_attempts, reload: reload, sleep_interval: sleep_interval) do + yield end - - false end def retry_on_exception(max_attempts: 3, reload: false, sleep_interval: 0.5) diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb index 07e191f1c9b..fe324574f4d 100644 --- a/qa/qa/page/component/note.rb +++ b/qa/qa/page/component/note.rb @@ -10,6 +10,10 @@ module QA element :discussion_option end + base.view 'app/assets/javascripts/notes/components/note_actions.vue' do + element :note_edit_button + end + base.view 'app/assets/javascripts/notes/components/note_form.vue' do element :reply_input element :reply_comment_button @@ -49,6 +53,12 @@ module QA def expand_replies click_element :expand_replies end + + def edit_comment(text) + click_element :note_edit_button + fill_element :reply_input, text + click_element :reply_comment_button + end end end end diff --git a/qa/qa/page/element.rb b/qa/qa/page/element.rb index 7a01320901d..9e6fd2fdd4f 100644 --- a/qa/qa/page/element.rb +++ b/qa/qa/page/element.rb @@ -28,7 +28,7 @@ module QA end def selector_css - ".#{selector}" + %Q([data-qa-selector="#{@name}"],.#{selector}) end def expression @@ -40,7 +40,7 @@ module QA end def matches?(line) - !!(line =~ expression) + !!(line =~ /["']#{name}['"]|#{expression}/) end end end diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index 5eb24d2d2ba..c98d85d7911 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -5,14 +5,14 @@ module QA module Main class Menu < Page::Base view 'app/views/layouts/header/_current_user_dropdown.html.haml' do - element :user_sign_out_link, 'link_to _("Sign out")' # rubocop:disable QA/ElementWithPattern + element :sign_out_link element :settings_link, 'link_to s_("CurrentUser|Settings")' # rubocop:disable QA/ElementWithPattern end view 'app/views/layouts/header/_default.html.haml' do element :navbar, required: true element :user_avatar, required: true - element :user_menu, '.dropdown-menu' # rubocop:disable QA/ElementWithPattern + element :user_menu, required: true end view 'app/views/layouts/nav/_dashboard.html.haml' do @@ -53,7 +53,7 @@ module QA def sign_out within_user_menu do - click_link 'Sign out' + click_element :sign_out_link end end @@ -82,7 +82,7 @@ module QA private def within_top_menu - page.within('.qa-navbar') do + within_element(:navbar) do yield end end @@ -91,7 +91,7 @@ module QA within_top_menu do click_element :user_avatar - page.within('.dropdown-menu') do + within_element(:user_menu) do yield end end diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb index defd85a5740..0918445d119 100644 --- a/qa/qa/page/project/new.rb +++ b/qa/qa/page/project/new.rb @@ -18,7 +18,7 @@ module QA element :project_name, 'text_field :name' # rubocop:disable QA/ElementWithPattern element :project_path, 'text_field :path' # rubocop:disable QA/ElementWithPattern element :project_description, 'text_area :description' # rubocop:disable QA/ElementWithPattern - element :project_create_button, "submit 'Create project'" # rubocop:disable QA/ElementWithPattern + element :project_create_button, "submit _('Create project')" # rubocop:disable QA/ElementWithPattern element :visibility_radios, 'visibility_level:' # rubocop:disable QA/ElementWithPattern end diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb index 9c57a0f5afb..51b2af8b4ef 100644 --- a/qa/qa/resource/issue.rb +++ b/qa/qa/resource/issue.rb @@ -16,6 +16,10 @@ module QA attribute :labels attribute :title + def initialize + @labels = [] + end + def fabricate! project.visit! @@ -38,7 +42,7 @@ module QA def api_post_body { - labels: [labels], + labels: labels, title: title } end diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index ed0779b93cc..2987bb1a213 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -13,6 +13,8 @@ module QA NotRespondingError = Class.new(RuntimeError) + CAPYBARA_MAX_WAIT_TIME = 10 + def initialize self.class.configure! end @@ -43,6 +45,8 @@ module QA end end + Capybara.server_port = 9887 + ENV['TEST_ENV_NUMBER'].to_i + return if Capybara.drivers.include?(:chrome) Capybara.register_driver QA::Runtime::Env.browser do |app| @@ -119,7 +123,7 @@ module QA Capybara.configure do |config| config.default_driver = QA::Runtime::Env.browser config.javascript_driver = QA::Runtime::Env.browser - config.default_max_wait_time = 10 + config.default_max_wait_time = CAPYBARA_MAX_WAIT_TIME # https://github.com/mattheworiordan/capybara-screenshot/issues/164 config.save_path = ::File.expand_path('../../tmp', __dir__) end diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index 96f337dc081..e78b9bece19 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'gitlab/qa' + module QA module Runtime module Env @@ -7,6 +9,8 @@ module QA attr_writer :personal_access_token, :ldap_username, :ldap_password + ENV_VARIABLES = Gitlab::QA::Runtime::Env::ENV_VARIABLES + # The environment variables used to indicate if the environment under test # supports the given feature SUPPORTED_FEATURES = { @@ -173,8 +177,8 @@ module QA ENV.fetch("GCLOUD_ACCOUNT_EMAIL") end - def gcloud_zone - ENV.fetch('GCLOUD_ZONE') + def gcloud_region + ENV.fetch('GCLOUD_REGION') end def has_gcloud_credentials? @@ -201,6 +205,10 @@ module QA enabled?(ENV[SUPPORTED_FEATURES[feature]], default: true) end + def runtime_scenario_attributes + ENV['QA_RUNTIME_SCENARIO_ATTRIBUTES'] + end + private def remote_grid_credentials diff --git a/qa/qa/runtime/logger.rb b/qa/qa/runtime/logger.rb index bd5c4fe5bf5..7f73f1bd01b 100644 --- a/qa/qa/runtime/logger.rb +++ b/qa/qa/runtime/logger.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'logger' +require 'forwardable' module QA module Runtime diff --git a/qa/qa/runtime/scenario.rb b/qa/qa/runtime/scenario.rb index 5067322804b..3662ebe671b 100644 --- a/qa/qa/runtime/scenario.rb +++ b/qa/qa/runtime/scenario.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'json' + module QA module Runtime ## @@ -24,6 +26,10 @@ module QA end end + def from_env(var) + JSON.parse(Runtime::Env.runtime_scenario_attributes).each { |k, v| define(k, v) } + end + def method_missing(name, *) raise ArgumentError, "Scenario attribute `#{name}` not defined!" end diff --git a/qa/qa/scenario/shared_attributes.rb b/qa/qa/scenario/shared_attributes.rb index 40d5c6b1ff1..52f50ec8c27 100644 --- a/qa/qa/scenario/shared_attributes.rb +++ b/qa/qa/scenario/shared_attributes.rb @@ -7,6 +7,7 @@ module QA attribute :gitlab_address, '--address URL', 'Address of the instance to test' attribute :enable_feature, '--enable-feature FEATURE_FLAG', 'Enable a feature before running tests' + attribute :parallel, '--parallel', 'Execute tests in parallel' end end end diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb index 16a89591eb2..40263e94065 100644 --- a/qa/qa/service/kubernetes_cluster.rb +++ b/qa/qa/service/kubernetes_cluster.rb @@ -28,17 +28,17 @@ module QA create #{cluster_name} #{auth_options} --enable-basic-auth - --zone #{Runtime::Env.gcloud_zone} + --region #{Runtime::Env.gcloud_region} && gcloud container clusters get-credentials - --zone #{Runtime::Env.gcloud_zone} + --region #{Runtime::Env.gcloud_region} #{cluster_name} CMD @api_url = `kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}'` @admin_user = "#{cluster_name}-admin" - master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --zone #{Runtime::Env.gcloud_zone} --format 'json(masterAuth.username, masterAuth.password)'`) + master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --region #{Runtime::Env.gcloud_region} --format 'json(masterAuth.username, masterAuth.password)'`) shell <<~CMD.tr("\n", ' ') kubectl config set-credentials #{@admin_user} --username #{master_auth['masterAuth']['username']} @@ -66,10 +66,10 @@ module QA def remove! shell <<~CMD.tr("\n", ' ') gcloud container clusters delete - --zone #{Runtime::Env.gcloud_zone} - #{cluster_name} - --quiet --async - CMD + --region #{Runtime::Env.gcloud_region} + #{cluster_name} + --quiet --async + CMD end private diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb index a9de64e357a..2363836d5e3 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb @@ -7,15 +7,15 @@ module QA Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } - source_group = Resource::Group.fabricate! do |group| + source_group = Resource::Group.fabricate_via_api! do |group| group.path = 'source-group' end - target_group = Resource::Group.fabricate! do |group| + target_group = Resource::Group.fabricate_via_api! do |group| group.path = 'target-group' end - project = Resource::Project.fabricate! do |project| + project = Resource::Project.fabricate_via_api! do |project| project.group = source_group project.name = 'transfer-project' project.initialize_with_readme = true diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb index cf225a639b6..5cd6bac3f5a 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb @@ -11,8 +11,10 @@ module QA expect(menu).to have_personal_area end - Page::Main::Menu.perform do |menu| - menu.sign_out + Support::Retrier.retry_until(reload: false, sleep_interval: 0.5) do + Page::Main::Menu.perform(&:sign_out) + + Page::Main::Login.perform(&:has_sign_in_tab?) end Page::Main::Login.perform do |form| diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb index d1747280227..f51c16f472c 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb @@ -9,7 +9,7 @@ module QA user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) - project = Resource::Project.fabricate! do |resource| + project = Resource::Project.fabricate_via_api! do |resource| resource.name = 'add-member-project' end project.visit! diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb index 013cea0a40e..5eceeb9661c 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb @@ -9,12 +9,12 @@ module QA Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.perform(&:sign_in_using_credentials) - user = Resource::User.fabricate! do |user| + user = Resource::User.fabricate_via_api! do |user| user.name = "eve <img src=x onerror=alert(2)<img src=x onerror=alert(1)>" user.password = "test1234" end - project = Resource::Project.fabricate! do |resource| + project = Resource::Project.fabricate_via_api! do |resource| resource.name = 'xss-test-for-mentions-project' end project.visit! @@ -24,10 +24,11 @@ module QA page.add_member(user.username) end - Resource::Issue.fabricate_via_browser_ui! do |issue| + issue = Resource::Issue.fabricate_via_api! do |issue| issue.title = issue_title issue.project = project end + issue.visit! Page::Project::Issue::Show.perform do |show_page| show_page.select_all_activities_filter diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb new file mode 100644 index 00000000000..a62a51b11f4 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module QA + context 'Plan' do + describe 'Issue comments' do + it 'user comments on an issue and edits the comment' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + issue = Resource::Issue.fabricate_via_api! do |issue| + issue.title = 'issue title' + end + issue.visit! + + Page::Project::Issue::Show.perform do |issue_show_page| + first_version_of_comment = 'First version of the comment' + second_version_of_comment = 'Second version of the comment' + + issue_show_page.comment(first_version_of_comment) + + expect(issue_show_page).to have_content(first_version_of_comment) + + issue_show_page.edit_comment(second_version_of_comment) + + expect(issue_show_page).to have_content(second_version_of_comment) + expect(issue_show_page).not_to have_content(first_version_of_comment) + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb index 2b3d9b1711d..36cbd1b81f0 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb @@ -56,7 +56,7 @@ module QA project.visit! end - it 'branches are correctly listed after CRUD operations' do + it 'lists branches correctly after CRUD operations' do Page::Project::Menu.perform(&:go_to_repository_branches) expect(page).to have_content(master_branch) diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb index 247cde38e52..11fd570d131 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module QA - context 'Create', :requires_admin do + # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/113 + context 'Create', :requires_admin, :quarantine do describe 'push after setting the file size limit via admin/application_settings' do before(:context) do @project = Resource::Project.fabricate_via_api! do |p| diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb index b060f15168c..b2c70547421 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb @@ -7,12 +7,12 @@ module QA Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.perform(&:sign_in_using_credentials) - project = Resource::Project.fabricate! do |project| + project = Resource::Project.fabricate_via_api! do |project| project.name = 'project-with-ci-variables' project.description = 'project with CI variables' end - Resource::CiVariable.fabricate! do |resource| + Resource::CiVariable.fabricate_via_api! do |resource| resource.project = project resource.key = 'VARIABLE_KEY' resource.value = 'some_CI_variable' diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb index caa9be341b4..791dc62e32f 100644 --- a/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb +++ b/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb @@ -10,7 +10,7 @@ module QA deploy_token_name = 'deploy token name' one_week_from_now = Date.today + 7 - deploy_token = Resource::DeployToken.fabricate! do |resource| + deploy_token = Resource::DeployToken.fabricate_via_browser_ui! do |resource| resource.name = deploy_token_name resource.expires_at = one_week_from_now end diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb index 5ca9ddb6b19..99f0838b864 100644 --- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb +++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb @@ -9,6 +9,52 @@ module QA Page::Main::Login.perform(&:sign_in_using_credentials) end + def disable_optional_jobs(project) + # Disable code_quality check in Auto DevOps pipeline as it takes + # too long and times out the test + Resource::CiVariable.fabricate_via_api! do |resource| + resource.project = project + resource.key = 'CODE_QUALITY_DISABLED' + resource.value = '1' + resource.masked = false + end + + Resource::CiVariable.fabricate_via_api! do |resource| + resource.project = project + resource.key = 'LICENSE_MANAGEMENT_DISABLED' + resource.value = '1' + resource.masked = false + end + + Resource::CiVariable.fabricate_via_api! do |resource| + resource.project = project + resource.key = 'SAST_DISABLED' + resource.value = '1' + resource.masked = false + end + + Resource::CiVariable.fabricate_via_api! do |resource| + resource.project = project + resource.key = 'DEPENDENCY_SCANNING_DISABLED' + resource.value = '1' + resource.masked = false + end + + Resource::CiVariable.fabricate_via_api! do |resource| + resource.project = project + resource.key = 'CONTAINER_SCANNING_DISABLED' + resource.value = '1' + resource.masked = false + end + + Resource::CiVariable.fabricate_via_api! do |resource| + resource.project = project + resource.key = 'DAST_DISABLED' + resource.value = '1' + resource.masked = false + end + end + # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/108 describe 'Auto DevOps support', :orchestrated, :kubernetes, :quarantine do context 'when rbac is enabled' do @@ -28,14 +74,7 @@ module QA p.description = 'Project with Auto DevOps' end - # Disable code_quality check in Auto DevOps pipeline as it takes - # too long and times out the test - Resource::CiVariable.fabricate! do |resource| - resource.project = @project - resource.key = 'CODE_QUALITY_DISABLED' - resource.value = '1' - resource.masked = false - end + disable_optional_jobs(@project) # Set an application secret CI variable (prefixed with K8S_SECRET_) Resource::CiVariable.fabricate! do |resource| diff --git a/qa/qa/specs/parallel_runner.rb b/qa/qa/specs/parallel_runner.rb new file mode 100644 index 00000000000..b92fdb610b6 --- /dev/null +++ b/qa/qa/specs/parallel_runner.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'open3' + +module QA + module Specs + module ParallelRunner + module_function + + def run(args) + unless args.include?('--') + index = args.index { |opt| opt.include?('features') } + + args.insert(index, '--') if index + end + + env = {} + Runtime::Env::ENV_VARIABLES.each_key do |key| + env[key] = ENV[key] if ENV[key] + end + env['QA_RUNTIME_SCENARIO_ATTRIBUTES'] = Runtime::Scenario.attributes.to_json + env['GITLAB_QA_ACCESS_TOKEN'] = Runtime::API::Client.new(:gitlab).personal_access_token unless env['GITLAB_QA_ACCESS_TOKEN'] + + cmd = "bundle exec parallel_test -t rspec --combine-stderr --serialize-stdout -- #{args.flatten.join(' ')}" + ::Open3.popen2e(env, cmd) do |_, out, wait| + out.each { |line| puts line } + + exit wait.value.exitstatus + end + end + end + end +end diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb index f1cb9378de8..6aa08cf77b4 100644 --- a/qa/qa/specs/runner.rb +++ b/qa/qa/specs/runner.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true +require 'knapsack' require 'rspec/core' require 'rspec/expectations' -require 'knapsack' module QA module Specs @@ -17,44 +17,56 @@ module QA @options = [] end - def perform - args = [] - args.push('--tty') if tty + def paths_from_knapsack + allocator = Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator + + QA::Runtime::Logger.info '' + QA::Runtime::Logger.info 'Report specs:' + QA::Runtime::Logger.info allocator.report_node_tests.join(', ') + QA::Runtime::Logger.info '' + QA::Runtime::Logger.info 'Leftover specs:' + QA::Runtime::Logger.info allocator.leftover_node_tests.join(', ') + QA::Runtime::Logger.info '' + + ['--', allocator.node_tests] + end + + def rspec_tags + tags_for_rspec = [] if tags.any? - tags.each { |tag| args.push(['--tag', tag.to_s]) } + tags.each { |tag| tags_for_rspec.push(['--tag', tag.to_s]) } else - args.push(%w[--tag ~orchestrated]) unless (%w[-t --tag] & options).any? + tags_for_rspec.push(%w[--tag ~orchestrated]) unless (%w[-t --tag] & options).any? end - args.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled? + tags_for_rspec.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled? QA::Runtime::Env.supported_features.each_key do |key| - args.push(["--tag", "~requires_#{key}"]) unless QA::Runtime::Env.can_test? key + tags_for_rspec.push(%W[--tag ~requires_#{key}]) unless QA::Runtime::Env.can_test? key end - args.push(options) + tags_for_rspec + end - Runtime::Browser.configure! + def perform + args = [] + args.push('--tty') if tty + args.push(rspec_tags) + args.push(options) if Runtime::Env.knapsack? - allocator = Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator - - QA::Runtime::Logger.info '' - QA::Runtime::Logger.info 'Report specs:' - QA::Runtime::Logger.info allocator.report_node_tests.join(', ') - QA::Runtime::Logger.info '' - QA::Runtime::Logger.info 'Leftover specs:' - QA::Runtime::Logger.info allocator.leftover_node_tests.join(', ') - QA::Runtime::Logger.info '' - - args.push(['--', allocator.node_tests]) + args.push(paths_from_knapsack) else args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} } end - RSpec::Core::Runner.run(args.flatten, $stderr, $stdout).tap do |status| - abort if status.nonzero? + if Runtime::Scenario.attributes[:parallel] + ParallelRunner.run(args.flatten) + else + RSpec::Core::Runner.run(args.flatten, $stderr, $stdout).tap do |status| + abort if status.nonzero? + end end end end diff --git a/qa/qa/support/retrier.rb b/qa/qa/support/retrier.rb index 8be4e9f5365..230cec8f8d2 100644 --- a/qa/qa/support/retrier.rb +++ b/qa/qa/support/retrier.rb @@ -23,6 +23,25 @@ module QA raise end end + + def retry_until(max_attempts: 3, reload: false, sleep_interval: 0) + QA::Runtime::Logger.debug("with retry_until: max_attempts #{max_attempts}; sleep_interval #{sleep_interval}; reload:#{reload}") + attempts = 0 + + while attempts < max_attempts + QA::Runtime::Logger.debug("Attempt number #{attempts + 1}") + result = yield + return result if result + + sleep sleep_interval + + refresh if reload + + attempts += 1 + end + + false + end end end end diff --git a/qa/qa/tools/generate_perf_testdata.rb b/qa/qa/tools/generate_perf_testdata.rb index b0477951967..26bcb2fe958 100644 --- a/qa/qa/tools/generate_perf_testdata.rb +++ b/qa/qa/tools/generate_perf_testdata.rb @@ -59,8 +59,8 @@ module QA group_search_response = create_a_group_api_req(@group_name, @visibility) group = JSON.parse(group_search_response.body) @urls[:group_page] = group["web_url"] - group["id"] STDOUT.puts "Created a group: #{@urls[:group_page]}" + group["id"] end def create_project(group_id) @@ -196,6 +196,7 @@ module QA project_path = "#{@group_name}%2F#{@project_name}" branch_name = "branch_with_many_commits-#{SecureRandom.hex(8)}" file_name = "file_for_many_commits.txt" + create_a_branch_api_req(branch_name, project_path) create_a_new_file_api_req(file_name, branch_name, project_path, "Initial commit for new file", "Initial file content") create_mr_response = create_a_merge_request_api_req(project_path, branch_name, "master", "MR with many commits-#{SecureRandom.hex(8)}") @@ -203,7 +204,7 @@ module QA 100.times do |i| update_file_api_req(file_name, branch_name, project_path, Faker::Lorem.sentences(5).join(" "), Faker::Lorem.sentences(500).join("\n")) end - STDOUT.puts "Created an MR with many commits: #{@urls[:mr_with_many_commits]}" + STDOUT.puts "Using branch: #{branch_name}, created an MR with many commits: #{@urls[:mr_with_many_commits]}" end private @@ -211,56 +212,88 @@ module QA # API Requests def create_a_discussion_on_issue_api_req(project_path_or_id, issue_id, body) - post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/discussions").url, "body=\"#{body}\"" + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/discussions").url, "body=\"#{body}\"" + end end def update_a_discussion_on_issue_api_req(project_path_or_id, mr_iid, discussion_id, resolved_status) - put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions/#{discussion_id}").url, "resolved=#{resolved_status}" + call_api(expected_response_code: 200) do + put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions/#{discussion_id}").url, "resolved=#{resolved_status}" + end end def create_a_discussion_on_mr_api_req(project_path_or_id, mr_iid, body) - post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions").url, - "body=\"#{body}\"" + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions").url, "body=\"#{body}\"" + end end def create_a_label_api_req(project_path_or_id, name, color) - post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/labels").url, "name=#{name}&color=#{color}" + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/labels").url, "name=#{name}&color=#{color}" + end end def create_a_todo_api_req(project_path_or_id, issue_id) - post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/todo").url, nil + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/todo").url, nil + end end def create_an_issue_api_req(project_path_or_id, title, description) - post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues").url, "title=#{title}&description=#{description}" + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues").url, "title=#{title}&description=#{description}" + end end def update_an_issue_api_req(project_path_or_id, issue_id, description, labels_list) - put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}").url, "description=#{description}&labels=#{labels_list}" + call_api(expected_response_code: 200) do + put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}").url, "description=#{description}&labels=#{labels_list}" + end end def create_a_project_api_req(project_name, group_id, visibility) - post Runtime::API::Request.new(@api_client, "/projects").url, "name=#{project_name}&namespace_id=#{group_id}&visibility=#{visibility}" + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/projects").url, "name=#{project_name}&namespace_id=#{group_id}&visibility=#{visibility}" + end end def create_a_group_api_req(group_name, visibility) - post Runtime::API::Request.new(@api_client, "/groups").url, "name=#{group_name}&path=#{group_name}&visibility=#{visibility}" + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/groups").url, "name=#{group_name}&path=#{group_name}&visibility=#{visibility}" + end end def create_a_branch_api_req(branch_name, project_path_or_id) - post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/branches").url, "branch=#{branch_name}&ref=master" + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/branches").url, "branch=#{branch_name}&ref=master" + end end def create_a_new_file_api_req(file_path, branch_name, project_path_or_id, commit_message, content) - post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\"" + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\"" + end end def create_a_merge_request_api_req(project_path_or_id, source_branch, target_branch, mr_title) - post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests").url, "source_branch=#{source_branch}&target_branch=#{target_branch}&title=#{mr_title}" + call_api(expected_response_code: 201) do + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests").url, "source_branch=#{source_branch}&target_branch=#{target_branch}&title=#{mr_title}" + end end def update_file_api_req(file_path, branch_name, project_path_or_id, commit_message, content) - put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\"" + call_api(expected_response_code: 200) do + put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\"" + end + end + + def call_api(expected_response_code: 200) + response = yield + raise "API call failed with response code: #{response.code} and body: #{response.body}" unless response.code == expected_response_code + + response end end end diff --git a/qa/spec/page/element_spec.rb b/qa/spec/page/element_spec.rb index f746fe06e40..20d4a00c020 100644 --- a/qa/spec/page/element_spec.rb +++ b/qa/spec/page/element_spec.rb @@ -11,7 +11,7 @@ describe QA::Page::Element do describe '#selector_css' do it 'transforms element name into QA-specific clickable css selector' do expect(described_class.new(:sign_in_button).selector_css) - .to eq '.qa-sign-in-button' + .to include('.qa-sign-in-button') end end @@ -49,6 +49,10 @@ describe QA::Page::Element do it 'does not match if QA selector is not there' do expect(subject.matches?('some_name selector')).to be false end + + it 'matches when element name is specified' do + expect(subject.matches?('data:{qa:{selector:"some_name"}}')).to be true + end end describe 'attributes' do @@ -106,4 +110,11 @@ describe QA::Page::Element do end end end + + describe 'data-qa selectors' do + subject { described_class.new(:my_element) } + it 'properly translates to a data-qa-selector' do + expect(subject.selector_css).to include(%q([data-qa-selector="my_element"])) + end + end end diff --git a/qa/spec/page/logging_spec.rb b/qa/spec/page/logging_spec.rb index 0f1ed039149..92a4f7b40e6 100644 --- a/qa/spec/page/logging_spec.rb +++ b/qa/spec/page/logging_spec.rb @@ -91,26 +91,26 @@ describe QA::Support::Page::Logging do it 'logs has_element?' do expect { subject.has_element?(:element) } - .to output(/has_element\? :element \(wait: 2\) returned: true/).to_stdout_from_any_process + .to output(/has_element\? :element \(wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned: true/).to_stdout_from_any_process end it 'logs has_element? with text' do expect { subject.has_element?(:element, text: "some text") } - .to output(/has_element\? :element with text \"some text\" \(wait: 2\) returned: true/).to_stdout_from_any_process + .to output(/has_element\? :element with text \"some text\" \(wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned: true/).to_stdout_from_any_process end it 'logs has_no_element?' do allow(page).to receive(:has_no_css?).and_return(true) expect { subject.has_no_element?(:element) } - .to output(/has_no_element\? :element \(wait: 2\) returned: true/).to_stdout_from_any_process + .to output(/has_no_element\? :element \(wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned: true/).to_stdout_from_any_process end it 'logs has_no_element? with text' do allow(page).to receive(:has_no_css?).and_return(true) expect { subject.has_no_element?(:element, text: "more text") } - .to output(/has_no_element\? :element with text \"more text\" \(wait: 2\) returned: true/).to_stdout_from_any_process + .to output(/has_no_element\? :element with text \"more text\" \(wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned: true/).to_stdout_from_any_process end it 'logs has_text?' do diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb index f25dbf3a8ab..21bfd2876a9 100644 --- a/qa/spec/spec_helper.rb +++ b/qa/spec/spec_helper.rb @@ -8,6 +8,10 @@ if ENV['CI'] && QA::Runtime::Env.knapsack? && !ENV['NO_KNAPSACK'] Knapsack::Adapters::RSpecAdapter.bind end +QA::Runtime::Browser.configure! + +QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes) if QA::Runtime::Env.runtime_scenario_attributes + %w[helpers shared_examples].each do |d| Dir[::File.join(__dir__, d, '**', '*.rb')].each { |f| require f } end diff --git a/qa/spec/specs/parallel_runner_spec.rb b/qa/spec/specs/parallel_runner_spec.rb new file mode 100644 index 00000000000..67d94a1f648 --- /dev/null +++ b/qa/spec/specs/parallel_runner_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +describe QA::Specs::ParallelRunner do + include Helpers::StubENV + + before do + allow(QA::Runtime::Scenario).to receive(:attributes).and_return(parallel: true) + stub_env('GITLAB_QA_ACCESS_TOKEN', 'skip_token_creation') + end + + it 'passes args to parallel_tests' do + expect_cli_arguments(['--tag', '~orchestrated', *QA::Specs::Runner::DEFAULT_TEST_PATH_ARGS]) + + subject.run(['--tag', '~orchestrated', *QA::Specs::Runner::DEFAULT_TEST_PATH_ARGS]) + end + + it 'passes a given test path to parallel_tests and adds a separator' do + expect_cli_arguments(%w[-- qa/specs/features/foo]) + + subject.run(%w[qa/specs/features/foo]) + end + + it 'passes tags and test paths to parallel_tests and adds a separator' do + expect_cli_arguments(%w[--tag smoke -- qa/specs/features/foo qa/specs/features/bar]) + + subject.run(%w[--tag smoke qa/specs/features/foo qa/specs/features/bar]) + end + + it 'passes tags and test paths with separators to parallel_tests' do + expect_cli_arguments(%w[-- --tag smoke -- qa/specs/features/foo qa/specs/features/bar]) + + subject.run(%w[-- --tag smoke -- qa/specs/features/foo qa/specs/features/bar]) + end + + it 'passes supported environment variables' do + # Test only env vars starting with GITLAB because some of the others + # affect how the runner behaves, and we're not concerned with those + # behaviors in this test + gitlab_env_vars = QA::Runtime::Env::ENV_VARIABLES.reject { |v| !v.start_with?('GITLAB') } + + gitlab_env_vars.each do |k, v| + stub_env(k, v) + end + + gitlab_env_vars['QA_RUNTIME_SCENARIO_ATTRIBUTES'] = '{"parallel":true}' + + expect_cli_arguments([], gitlab_env_vars) + + subject.run([]) + end + + def expect_cli_arguments(arguments, env = { 'QA_RUNTIME_SCENARIO_ATTRIBUTES' => '{"parallel":true}' }) + cmd = "bundle exec parallel_test -t rspec --combine-stderr --serialize-stdout -- #{arguments.join(' ')}" + expect(Open3).to receive(:popen2e) + .with(hash_including(env), cmd) + .and_return(0) + end +end diff --git a/qa/spec/specs/runner_spec.rb b/qa/spec/specs/runner_spec.rb index f94145d148e..6c533c6dc7d 100644 --- a/qa/spec/specs/runner_spec.rb +++ b/qa/spec/specs/runner_spec.rb @@ -58,11 +58,11 @@ describe QA::Specs::Runner do end end - context 'when "-- qa/specs/features/foo" is set as options' do - subject { described_class.new.tap { |runner| runner.options = %w[-- qa/specs/features/foo] } } + context 'when "--tag smoke" and "qa/specs/features/foo" are set as options' do + subject { described_class.new.tap { |runner| runner.options = %w[--tag smoke qa/specs/features/foo] } } - it 'passes the given tests path and excludes the orchestrated tag' do - expect_rspec_runner_arguments(['--tag', '~orchestrated', '--', 'qa/specs/features/foo']) + it 'focuses on the given tag and includes the path without excluding the orchestrated tag' do + expect_rspec_runner_arguments(['--tag', 'smoke', 'qa/specs/features/foo']) subject.perform end diff --git a/rubocop/cop/gitlab/rails_logger.rb b/rubocop/cop/gitlab/rails_logger.rb new file mode 100644 index 00000000000..d1a06a9a100 --- /dev/null +++ b/rubocop/cop/gitlab/rails_logger.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require_relative '../../code_reuse_helpers' + +module RuboCop + module Cop + module Gitlab + class RailsLogger < ::RuboCop::Cop::Cop + include CodeReuseHelpers + + # This cop checks for the Rails.logger in the codebase + # + # @example + # + # # bad + # Rails.logger.error("Project #{project.full_path} could not be saved") + # + # # good + # Gitlab::AppLogger.error("Project %{project_path} could not be saved" % { project_path: project.full_path }) + MSG = 'Use a structured JSON logger instead of `Rails.logger`. ' \ + 'https://docs.gitlab.com/ee/development/logging.html'.freeze + + def_node_matcher :rails_logger?, <<~PATTERN + (send (const nil? :Rails) :logger ... ) + PATTERN + + WHITELISTED_DIRECTORIES = %w[ + spec + ].freeze + + def on_send(node) + return if in_whitelisted_directory?(node) + return unless rails_logger?(node) + + add_offense(node, location: :expression) + end + + def in_whitelisted_directory?(node) + path = file_path_for_node(node) + + WHITELISTED_DIRECTORIES.any? do |directory| + path.start_with?( + File.join(rails_root, directory), + File.join(rails_root, 'ee', directory) + ) + end + end + end + end + end +end diff --git a/rubocop/cop/qa/element_with_pattern.rb b/rubocop/cop/qa/element_with_pattern.rb index d14eeaaeaf3..d48f4725488 100644 --- a/rubocop/cop/qa/element_with_pattern.rb +++ b/rubocop/cop/qa/element_with_pattern.rb @@ -30,7 +30,7 @@ module RuboCop return if args.first.nil? args.first.each_node(:str) do |arg| - add_offense(arg, message: MESSAGE % "qa-#{element_name.value.to_s.tr('_', '-')}") + add_offense(arg, message: MESSAGE % "data-qa-selector=#{element_name.value}") end end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index 27c63d92ae5..ba61a634d97 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -3,6 +3,7 @@ require_relative 'cop/gitlab/predicate_memoization' require_relative 'cop/gitlab/httparty' require_relative 'cop/gitlab/finder_with_find_by' require_relative 'cop/gitlab/union' +require_relative 'cop/gitlab/rails_logger' require_relative 'cop/include_action_view_context' require_relative 'cop/include_sidekiq_worker' require_relative 'cop/safe_params' diff --git a/scripts/gather-test-memory-data b/scripts/gather-test-memory-data new file mode 100755 index 00000000000..9992a83e6a6 --- /dev/null +++ b/scripts/gather-test-memory-data @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby + +require 'csv' + +def join_csv_files(output_path, input_paths) + return if input_paths.empty? + + input_csvs = input_paths.map do |input_path| + CSV.read(input_path, headers: true) + end + + CSV.open(output_path, "w", headers: input_csvs.first.headers, write_headers: true) do |output_csv| + input_csvs.each do |input_csv| + input_csv.each do |line| + output_csv << line + end + end + end +end + +join_csv_files('tmp/memory_test/report.csv', Dir['tmp/memory_test/*.csv'].sort) diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index 2bf654b1e24..bc47884ee45 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -132,13 +132,14 @@ function install_external_dns() { echoinfo "Installing external-dns Helm chart" helm repo update # Default requested: CPU => 0, memory => 0 - helm install stable/external-dns \ + helm install stable/external-dns --version '^2.2.1' \ -n "${release_name}" \ --namespace "${KUBE_NAMESPACE}" \ --set provider="aws" \ - --set aws.secretKey="${REVIEW_APPS_AWS_SECRET_KEY}" \ - --set aws.accessKey="${REVIEW_APPS_AWS_ACCESS_KEY}" \ + --set aws.credentials.secretKey="${REVIEW_APPS_AWS_SECRET_KEY}" \ + --set aws.credentials.accessKey="${REVIEW_APPS_AWS_ACCESS_KEY}" \ --set aws.zoneType="public" \ + --set aws.batchChangeSize=400 \ --set domainFilters[0]="${domain}" \ --set txtOwnerId="${KUBE_NAMESPACE}" \ --set rbac.create="true" \ diff --git a/scripts/trigger-build-docs b/scripts/trigger-build-docs index dfc8ee6050a..83841512f1c 100755 --- a/scripts/trigger-build-docs +++ b/scripts/trigger-build-docs @@ -13,7 +13,7 @@ end # # The remote docs project # -GITLAB_DOCS_REPO = 'gitlab-com/gitlab-docs'.freeze +GITLAB_DOCS_REPO = 'gitlab-org/gitlab-docs'.freeze # # Truncate the remote docs branch name otherwise we hit the filesystem @@ -31,7 +31,7 @@ end # to avoid race conditions, since a triggered pipeline will also run right # after the branch creation. This only happens the very first time a branch # is created and will be skipped in subsequent runs. Read more in -# https://gitlab.com/gitlab-com/gitlab-docs/issues/154. +# https://gitlab.com/gitlab-org/gitlab-docs/issues/154. # def create_remote_branch Gitlab.create_branch(GITLAB_DOCS_REPO, docs_branch, 'master') @@ -81,7 +81,7 @@ def slug end # -# Overriding vars in https://gitlab.com/gitlab-com/gitlab-docs/blob/master/.gitlab-ci.yml +# Overriding vars in https://gitlab.com/gitlab-org/gitlab-docs/blob/master/.gitlab-ci.yml # def param_name "BRANCH_#{slug.upcase}" @@ -105,8 +105,8 @@ def trigger_pipeline puts "" puts app_url puts "" - puts "=> For more information, read the documentation" - puts "=> https://docs.gitlab.com/ee/development/writing_documentation.html#previewing-the-changes-live" + puts "=> For more information, see the documentation" + puts "=> https://docs.gitlab.com/ee/development/documentation/index.html#previewing-the-changes-live" puts "" puts "=> If something doesn't work, drop a line in the #docs chat channel." puts "" diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb index 78c5e2a2656..bbeda7dae0f 100644 --- a/spec/controllers/admin/runners_controller_spec.rb +++ b/spec/controllers/admin/runners_controller_spec.rb @@ -23,10 +23,11 @@ describe Admin::RunnersController do control_count = ActiveRecord::QueryRecorder.new { get :index }.count - create(:ci_runner, :tagged_only) + create_list(:ci_runner, 5, :tagged_only) # There is still an N+1 query for `runner.builds.count` - expect { get :index }.not_to exceed_query_limit(control_count + 1) + # We also need to add 1 because it takes 2 queries to preload tags + expect { get :index }.not_to exceed_query_limit(control_count + 6) expect(response).to have_gitlab_http_status(200) expect(response.body).to have_content('tag1') diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb index c84bb913cad..6cad060d888 100644 --- a/spec/controllers/boards/issues_controller_spec.rb +++ b/spec/controllers/boards/issues_controller_spec.rb @@ -164,6 +164,201 @@ describe Boards::IssuesController do end end + describe 'PUT move_multiple' do + let(:todo) { create(:group_label, group: group, name: 'Todo') } + let(:development) { create(:group_label, group: group, name: 'Development') } + let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user } + let(:guest) { create(:group_member, :guest, user: create(:user), group: group ).user } + let(:project) { create(:project, group: group) } + let(:group) { create(:group) } + let(:board) { create(:board, project: project) } + let(:list1) { create(:list, board: board, label: todo, position: 0) } + let(:list2) { create(:list, board: board, label: development, position: 1) } + let(:issue1) { create(:labeled_issue, project: project, labels: [todo], author: user, relative_position: 10) } + let(:issue2) { create(:labeled_issue, project: project, labels: [todo], author: user, relative_position: 20) } + let(:issue3) { create(:labeled_issue, project: project, labels: [todo], author: user, relative_position: 30) } + let(:issue4) { create(:labeled_issue, project: project, labels: [development], author: user, relative_position: 100) } + + let(:move_params) do + { + board_id: board.id, + ids: [issue1.id, issue2.id, issue3.id], + from_list_id: list1.id, + to_list_id: list2.id, + move_before_id: issue4.id, + move_after_id: nil + } + end + + before do + project.add_maintainer(user) + project.add_guest(guest) + end + + shared_examples 'move issues endpoint provider' do + before do + sign_in(signed_in_user) + end + + it 'moves issues as expected' do + put :bulk_move, params: move_issues_params + expect(response).to have_gitlab_http_status(expected_status) + + list_issues user: requesting_user, board: board, list: list2 + expect(response).to have_gitlab_http_status(200) + + expect(response).to match_response_schema('entities/issue_boards') + + responded_issues = json_response['issues'] + expect(responded_issues.length).to eq expected_issue_count + + ids_in_order = responded_issues.pluck('id') + expect(ids_in_order).to eq(expected_issue_ids_in_order) + end + end + + context 'when items are moved to another list' do + it_behaves_like 'move issues endpoint provider' do + let(:signed_in_user) { user } + let(:move_issues_params) { move_params } + let(:requesting_user) { user } + let(:expected_status) { 200 } + let(:expected_issue_count) { 4 } + let(:expected_issue_ids_in_order) { [issue4.id, issue1.id, issue2.id, issue3.id] } + end + end + + context 'when moving just one issue' do + it_behaves_like 'move issues endpoint provider' do + let(:signed_in_user) { user } + let(:move_issues_params) do + move_params.dup.tap do |hash| + hash[:ids] = [issue2.id] + end + end + let(:requesting_user) { user } + let(:expected_status) { 200 } + let(:expected_issue_count) { 2 } + let(:expected_issue_ids_in_order) { [issue4.id, issue2.id] } + end + end + + context 'when user is not allowed to move issue' do + it_behaves_like 'move issues endpoint provider' do + let(:signed_in_user) { guest } + let(:move_issues_params) do + move_params.dup.tap do |hash| + hash[:ids] = [issue2.id] + end + end + let(:requesting_user) { user } + let(:expected_status) { 403 } + let(:expected_issue_count) { 1 } + let(:expected_issue_ids_in_order) { [issue4.id] } + end + end + + context 'when issues should be moved visually above existing issue in list' do + it_behaves_like 'move issues endpoint provider' do + let(:signed_in_user) { user } + let(:move_issues_params) do + move_params.dup.tap do |hash| + hash[:move_after_id] = issue4.id + hash[:move_before_id] = nil + end + end + let(:requesting_user) { user } + let(:expected_status) { 200 } + let(:expected_issue_count) { 4 } + let(:expected_issue_ids_in_order) { [issue1.id, issue2.id, issue3.id, issue4.id] } + end + end + + context 'when destination list is empty' do + before do + # Remove issue from list + issue4.labels -= [development] + issue4.save! + end + + it_behaves_like 'move issues endpoint provider' do + let(:signed_in_user) { user } + let(:move_issues_params) do + move_params.dup.tap do |hash| + hash[:move_before_id] = nil + end + end + let(:requesting_user) { user } + let(:expected_status) { 200 } + let(:expected_issue_count) { 3 } + let(:expected_issue_ids_in_order) { [issue1.id, issue2.id, issue3.id] } + end + end + + context 'when no position arguments are given' do + it_behaves_like 'move issues endpoint provider' do + let(:signed_in_user) { user } + let(:move_issues_params) do + move_params.dup.tap do |hash| + hash[:move_before_id] = nil + end + end + let(:requesting_user) { user } + let(:expected_status) { 200 } + let(:expected_issue_count) { 4 } + let(:expected_issue_ids_in_order) { [issue1.id, issue2.id, issue3.id, issue4.id] } + end + end + + context 'when move_before_id and move_after_id are given' do + let(:issue5) { create(:labeled_issue, project: project, labels: [development], author: user, relative_position: 90) } + + it_behaves_like 'move issues endpoint provider' do + let(:signed_in_user) { user } + let(:move_issues_params) do + move_params.dup.tap do |hash| + hash[:move_before_id] = issue5.id + hash[:move_after_id] = issue4.id + end + end + let(:requesting_user) { user } + let(:expected_status) { 200 } + let(:expected_issue_count) { 5 } + let(:expected_issue_ids_in_order) { [issue5.id, issue1.id, issue2.id, issue3.id, issue4.id] } + end + end + + context 'when request contains too many issues' do + it_behaves_like 'move issues endpoint provider' do + let(:signed_in_user) { user } + let(:move_issues_params) do + move_params.dup.tap do |hash| + hash[:ids] = (0..51).to_a + end + end + let(:requesting_user) { user } + let(:expected_status) { 422 } + let(:expected_issue_count) { 1 } + let(:expected_issue_ids_in_order) { [issue4.id] } + end + end + + context 'when request is malformed' do + it_behaves_like 'move issues endpoint provider' do + let(:signed_in_user) { user } + let(:move_issues_params) do + move_params.dup.tap do |hash| + hash[:ids] = 'foobar' + end + end + let(:requesting_user) { user } + let(:expected_status) { 400 } + let(:expected_issue_count) { 1 } + let(:expected_issue_ids_in_order) { [issue4.id] } + end + end + end + def list_issues(user:, board:, list: nil) sign_in(user) diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb index 95417936df4..b9ee69a617b 100644 --- a/spec/controllers/projects/deployments_controller_spec.rb +++ b/spec/controllers/projects/deployments_controller_spec.rb @@ -41,34 +41,26 @@ describe Projects::DeploymentsController do describe 'GET #metrics' do let(:deployment) { create(:deployment, :success, project: project, environment: environment) } - before do - allow(controller).to receive(:deployment).and_return(deployment) - end - context 'when metrics are disabled' do - before do - allow(deployment).to receive(:has_metrics?).and_return false - end - it 'responds with not found' do - get :metrics, params: deployment_params(id: deployment.id) + get :metrics, params: deployment_params(id: deployment.to_param) expect(response).to be_not_found end end context 'when metrics are enabled' do - before do - allow(deployment).to receive(:has_metrics?).and_return true - end - context 'when environment has no metrics' do before do - expect(deployment).to receive(:metrics).and_return(nil) + expect_next_instance_of(DeploymentMetrics) do |deployment_metrics| + allow(deployment_metrics).to receive(:has_metrics?).and_return(true) + + expect(deployment_metrics).to receive(:metrics).and_return(nil) + end end it 'returns a empty response 204 resposne' do - get :metrics, params: deployment_params(id: deployment.id) + get :metrics, params: deployment_params(id: deployment.to_param) expect(response).to have_gitlab_http_status(204) expect(response.body).to eq('') end @@ -84,11 +76,15 @@ describe Projects::DeploymentsController do end before do - expect(deployment).to receive(:metrics).and_return(empty_metrics) + expect_next_instance_of(DeploymentMetrics) do |deployment_metrics| + allow(deployment_metrics).to receive(:has_metrics?).and_return(true) + + expect(deployment_metrics).to receive(:metrics).and_return(empty_metrics) + end end it 'returns a metrics JSON document' do - get :metrics, params: deployment_params(id: deployment.id) + get :metrics, params: deployment_params(id: deployment.to_param) expect(response).to be_ok expect(json_response['success']).to be(true) @@ -96,54 +92,32 @@ describe Projects::DeploymentsController do expect(json_response['last_update']).to eq(42) end end - - context 'when metrics service does not implement deployment metrics' do - before do - allow(deployment).to receive(:metrics).and_raise(NotImplementedError) - end - - it 'responds with not found' do - get :metrics, params: deployment_params(id: deployment.id) - - expect(response).to be_not_found - end - end end end describe 'GET #additional_metrics' do let(:deployment) { create(:deployment, :success, project: project, environment: environment) } - before do - allow(controller).to receive(:deployment).and_return(deployment) - end - context 'when metrics are disabled' do - before do - allow(deployment).to receive(:has_metrics?).and_return false - end - it 'responds with not found' do - get :metrics, params: deployment_params(id: deployment.id) + get :metrics, params: deployment_params(id: deployment.to_param) expect(response).to be_not_found end end context 'when metrics are enabled' do - let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) } - - before do - allow(deployment).to receive(:prometheus_adapter).and_return(prometheus_adapter) - end - context 'when environment has no metrics' do before do - expect(deployment).to receive(:additional_metrics).and_return({}) + expect_next_instance_of(DeploymentMetrics) do |deployment_metrics| + allow(deployment_metrics).to receive(:has_metrics?).and_return(true) + + expect(deployment_metrics).to receive(:additional_metrics).and_return({}) + end end it 'returns a empty response 204 response' do - get :additional_metrics, params: deployment_params(id: deployment.id, format: :json) + get :additional_metrics, params: deployment_params(id: deployment.to_param, format: :json) expect(response).to have_gitlab_http_status(204) expect(response.body).to eq('') end @@ -159,11 +133,15 @@ describe Projects::DeploymentsController do end before do - expect(deployment).to receive(:additional_metrics).and_return(empty_metrics) + expect_next_instance_of(DeploymentMetrics) do |deployment_metrics| + allow(deployment_metrics).to receive(:has_metrics?).and_return(true) + + expect(deployment_metrics).to receive(:additional_metrics).and_return(empty_metrics) + end end it 'returns a metrics JSON document' do - get :additional_metrics, params: deployment_params(id: deployment.id, format: :json) + get :additional_metrics, params: deployment_params(id: deployment.to_param, format: :json) expect(response).to be_ok expect(json_response['success']).to be(true) diff --git a/spec/controllers/projects/environments/prometheus_api_controller_spec.rb b/spec/controllers/projects/environments/prometheus_api_controller_spec.rb index fdef9bc5638..45328482ad7 100644 --- a/spec/controllers/projects/environments/prometheus_api_controller_spec.rb +++ b/spec/controllers/projects/environments/prometheus_api_controller_spec.rb @@ -176,7 +176,7 @@ describe Projects::Environments::PrometheusApiController do def environment_params(params = {}) { id: environment.id.to_s, - namespace_id: project.namespace.name, + namespace_id: project.namespace.full_path, project_id: project.name, proxy_path: 'query', query: '1' diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 0eca663a683..cc6adc0a6c6 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -878,16 +878,79 @@ describe Projects::MergeRequestsController do expect(control_count).to be <= 137 end - def get_ci_environments_status(extra_params = {}) - params = { - namespace_id: merge_request.project.namespace.to_param, - project_id: merge_request.project, - id: merge_request.iid, - format: 'json' - } + it 'has no N+1 SQL issues for environments', :request_store, retry: 0 do + # First run to insert test data from lets, which does take up some 30 queries + get_ci_environments_status + + control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { get_ci_environments_status }.count + + environment2 = create(:environment, project: forked) + create(:deployment, :succeed, environment: environment2, sha: sha, ref: 'master', deployable: build) + + # TODO address the last 11 queries + # See https://gitlab.com/gitlab-org/gitlab-ce/issues/63952 (5 queries) + # And https://gitlab.com/gitlab-org/gitlab-ce/issues/64105 (6 queries) + leeway = 11 + expect { get_ci_environments_status }.not_to exceed_all_query_limit(control_count + leeway) + end + end + + context 'when a merge request has multiple environments with deployments' do + let(:sha) { merge_request.diff_head_sha } + let(:ref) { merge_request.source_branch } + + let!(:build) { create(:ci_build, pipeline: pipeline) } + let!(:pipeline) { create(:ci_pipeline, sha: sha, project: project) } + let!(:environment) { create(:environment, name: 'env_a', project: project) } + let!(:another_environment) { create(:environment, name: 'env_b', project: project) } + + before do + merge_request.update_head_pipeline + + create(:deployment, :succeed, environment: environment, sha: sha, ref: ref, deployable: build) + create(:deployment, :succeed, environment: another_environment, sha: sha, ref: ref, deployable: build) + end + + it 'exposes multiple environment statuses' do + get_ci_environments_status + + expect(json_response.count).to eq 2 + end + + context 'when route map is not present in the project' do + it 'does not have N+1 Gitaly requests for environments', :request_store do + expect(merge_request).to be_present - get :ci_environments_status, params: params.merge(extra_params) + expect { get_ci_environments_status } + .to change { Gitlab::GitalyClient.get_request_count }.by_at_most(1) + end end + + context 'when there is route map present in a project' do + before do + allow_any_instance_of(EnvironmentStatus) + .to receive(:has_route_map?) + .and_return(true) + end + + it 'does not have N+1 Gitaly requests for diff files', :request_store do + expect(merge_request.merge_request_diff.merge_request_diff_files).to be_many + + expect { get_ci_environments_status } + .to change { Gitlab::GitalyClient.get_request_count }.by_at_most(1) + end + end + end + + def get_ci_environments_status(extra_params = {}) + params = { + namespace_id: merge_request.project.namespace.to_param, + project_id: merge_request.project, + id: merge_request.iid, + format: 'json' + } + + get :ci_environments_status, params: params.merge(extra_params) end end diff --git a/spec/factories/services.rb b/spec/factories/services.rb index ecb481ed84a..cd1d2c33373 100644 --- a/spec/factories/services.rb +++ b/spec/factories/services.rb @@ -100,4 +100,16 @@ FactoryBot.define do type 'HipchatService' token 'test_token' end + + trait :without_properties_callback do + after(:build) do |service| + allow(service).to receive(:handle_properties) + end + + after(:create) do |service| + # we have to remove the stub because the behaviour of + # handle_properties method is tested after the creation + allow(service).to receive(:handle_properties).and_call_original + end + end end diff --git a/spec/features/admin/admin_sees_project_statistics_spec.rb b/spec/features/admin/admin_sees_project_statistics_spec.rb index b5323a1c76d..ecd0aab925b 100644 --- a/spec/features/admin/admin_sees_project_statistics_spec.rb +++ b/spec/features/admin/admin_sees_project_statistics_spec.rb @@ -15,7 +15,7 @@ describe "Admin > Admin sees project statistics" do let(:project) { create(:project, :repository) } it "shows project statistics" do - expect(page).to have_content("Storage: 0 Bytes (0 Bytes repositories, 0 Bytes wikis, 0 Bytes build artifacts, 0 Bytes LFS)") + expect(page).to have_content("Storage: 0 Bytes (Repository: 0 Bytes / Wikis: 0 Bytes / Build Artifacts: 0 Bytes / LFS: 0 Bytes)") end end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 4a9037afb43..518b3625348 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -400,35 +400,16 @@ describe 'Admin updates settings' do .to have_content "The form contains the following error: Polling interval multiplier must be greater than or equal to 0" end - context 'When pages_auto_ssl is enabled' do - before do - stub_feature_flags(pages_auto_ssl: true) - visit preferences_admin_application_settings_path - end - - it "Change Pages Let's Encrypt settings" do - page.within('.as-pages') do - fill_in 'Email', with: 'my@test.example.com' - check "I have read and agree to the Let's Encrypt Terms of Service" - click_button 'Save changes' - end - - expect(current_settings.lets_encrypt_notification_email).to eq 'my@test.example.com' - expect(current_settings.lets_encrypt_terms_of_service_accepted).to eq true - end - end - - context 'When pages_auto_ssl is disabled' do - before do - stub_feature_flags(pages_auto_ssl: false) - visit preferences_admin_application_settings_path + it "Change Pages Let's Encrypt settings" do + visit preferences_admin_application_settings_path + page.within('.as-pages') do + fill_in 'Email', with: 'my@test.example.com' + check "I have read and agree to the Let's Encrypt Terms of Service" + click_button 'Save changes' end - it "Doesn't show Let's Encrypt options" do - page.within('.as-pages') do - expect(page).not_to have_content('Email') - end - end + expect(current_settings.lets_encrypt_notification_email).to eq 'my@test.example.com' + expect(current_settings.lets_encrypt_terms_of_service_accepted).to eq true end end diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb index 8fb2e37e269..c3310a4a132 100644 --- a/spec/features/dashboard/milestones_spec.rb +++ b/spec/features/dashboard/milestones_spec.rb @@ -29,5 +29,19 @@ describe 'Dashboard > Milestones' do expect(page).to have_content(milestone.title) expect(page).to have_content(group.name) end + + describe 'new milestones dropdown', :js do + it 'takes user to a new milestone page', :js do + find('.new-project-item-select-button').click + + page.within('.select2-results') do + first('.select2-result-label').click + end + + find('.new-project-item-link').click + + expect(current_path).to eq(new_group_milestone_path(group)) + end + end end end diff --git a/spec/features/merge_request/user_creates_merge_request_spec.rb b/spec/features/merge_request/user_creates_merge_request_spec.rb index d05ef2a8f12..2a70cbb2a72 100644 --- a/spec/features/merge_request/user_creates_merge_request_spec.rb +++ b/spec/features/merge_request/user_creates_merge_request_spec.rb @@ -78,7 +78,7 @@ describe "User creates a merge request", :js do click_button("Submit merge request") - expect(page).to have_content(title).and have_content("Request to merge #{user.namespace.name}:#{source_branch} into master") + expect(page).to have_content(title).and have_content("Request to merge #{user.namespace.path}:#{source_branch} into master") end end end diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb index e0fa9dbb5fa..e4b02408b49 100644 --- a/spec/features/projects/files/user_edits_files_spec.rb +++ b/spec/features/projects/files/user_edits_files_spec.rb @@ -162,6 +162,7 @@ describe 'Projects > Files > User edits files', :js do expect_fork_status + expect(page).to have_css('.ide-sidebar-project-title', text: "#{project2.name} #{user.namespace.full_path}/#{project2.path}") expect(page).to have_css('.ide .multi-file-tab', text: '.gitignore') end diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb index 7c71b4c52e0..26b94bf58b2 100644 --- a/spec/features/projects/fork_spec.rb +++ b/spec/features/projects/fork_spec.rb @@ -50,7 +50,7 @@ describe 'Project fork' do click_link('New merge request') end - expect(current_path).to have_content(/#{user.namespace.name}/i) + expect(current_path).to have_content(/#{user.namespace.path}/i) end it 'shows avatars when Gravatar is disabled' do diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb index 1cc47cd6bd1..7ddd5c12cdf 100644 --- a/spec/features/search/user_uses_header_search_field_spec.rb +++ b/spec/features/search/user_uses_header_search_field_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'User uses header search field' do +describe 'User uses header search field', :js do include FilteredSearchHelpers let(:project) { create(:project) } @@ -11,57 +11,12 @@ describe 'User uses header search field' do sign_in(user) end - context 'when user is in a global scope', :js do + shared_examples 'search field examples' do before do - visit(root_path) - page.find('#search').click + visit(url) end - context 'when clicking issues' do - it 'shows assigned issues' do - find('.search-input-container .dropdown-menu').click_link('Issues assigned to me') - - expect(page).to have_selector('.filtered-search') - expect_tokens([assignee_token(user.name)]) - expect_filtered_search_input_empty - end - - it 'shows created issues' do - find('.search-input-container .dropdown-menu').click_link("Issues I've created") - - expect(page).to have_selector('.filtered-search') - expect_tokens([author_token(user.name)]) - expect_filtered_search_input_empty - end - end - - context 'when clicking merge requests' do - let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignees: [user]) } - - it 'shows assigned merge requests' do - find('.search-input-container .dropdown-menu').click_link('Merge requests assigned to me') - - expect(page).to have_selector('.filtered-search') - expect_tokens([assignee_token(user.name)]) - expect_filtered_search_input_empty - end - - it 'shows created merge requests' do - find('.search-input-container .dropdown-menu').click_link("Merge requests I've created") - - expect(page).to have_selector('.filtered-search') - expect_tokens([author_token(user.name)]) - expect_filtered_search_input_empty - end - end - end - - context 'when user is in a project scope' do - before do - visit(project_path(project)) - end - - it 'starts searching by pressing the enter key', :js do + it 'starts searching by pressing the enter key' do fill_in('search', with: 'gitlab') find('#search').native.send_keys(:enter) @@ -70,30 +25,31 @@ describe 'User uses header search field' do end end - context 'when clicking the search field', :js do + context 'when clicking the search field' do before do page.find('#search').click + wait_for_all_requests end it 'shows category search dropdown' do - expect(page).to have_selector('.dropdown-header', text: /#{project.name}/i) + expect(page).to have_selector('.dropdown-header', text: /#{scope_name}/i) end context 'when clicking issues' do let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) } it 'shows assigned issues' do - find('.dropdown-menu').click_link('Issues assigned to me') + find('.search-input-container .dropdown-menu').click_link('Issues assigned to me') - expect(page).to have_selector('.filtered-search') + expect(page).to have_selector('.issues-list .issue') expect_tokens([assignee_token(user.name)]) expect_filtered_search_input_empty end it 'shows created issues' do - find('.dropdown-menu').click_link("Issues I've created") + find('.search-input-container .dropdown-menu').click_link("Issues I've created") - expect(page).to have_selector('.filtered-search') + expect(page).to have_selector('.issues-list .issue') expect_tokens([author_token(user.name)]) expect_filtered_search_input_empty end @@ -103,33 +59,77 @@ describe 'User uses header search field' do let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignees: [user]) } it 'shows assigned merge requests' do - find('.dropdown-menu').click_link('Merge requests assigned to me') + find('.search-input-container .dropdown-menu').click_link('Merge requests assigned to me') - expect(page).to have_selector('.merge-requests-holder') + expect(page).to have_selector('.mr-list .merge-request') expect_tokens([assignee_token(user.name)]) expect_filtered_search_input_empty end it 'shows created merge requests' do - find('.dropdown-menu').click_link("Merge requests I've created") + find('.search-input-container .dropdown-menu').click_link("Merge requests I've created") - expect(page).to have_selector('.merge-requests-holder') + expect(page).to have_selector('.mr-list .merge-request') expect_tokens([author_token(user.name)]) expect_filtered_search_input_empty end end end - context 'when entering text into the search field', :js do + context 'when entering text into the search field' do before do page.within('.search-input-wrap') do - fill_in('search', with: project.name[0..3]) + fill_in('search', with: scope_name.first(4)) end end it 'does not display the category search dropdown' do - expect(page).not_to have_selector('.dropdown-header', text: /#{project.name}/i) + expect(page).not_to have_selector('.dropdown-header', text: /#{scope_name}/i) end end end + + context 'when user is in a global scope' do + include_examples 'search field examples' do + let(:url) { root_path } + let(:scope_name) { 'All GitLab' } + end + end + + context 'when user is in a project scope' do + include_examples 'search field examples' do + let(:url) { project_path(project) } + let(:scope_name) { project.name } + end + end + + context 'when user is in a group scope' do + let(:group) { create(:group) } + let(:project) { create(:project, namespace: group) } + + before do + group.add_maintainer(user) + end + + include_examples 'search field examples' do + let(:url) { group_path(group) } + let(:scope_name) { group.name } + end + end + + context 'when user is in a subgroup scope' do + let(:group) { create(:group) } + let(:subgroup) { create(:group, :public, parent: group) } + let(:project) { create(:project, namespace: subgroup) } + + before do + group.add_owner(user) + subgroup.add_owner(user) + end + + include_examples 'search field examples' do + let(:url) { group_path(subgroup) } + let(:scope_name) { subgroup.name } + end + end end diff --git a/spec/fixtures/api/schemas/current-board.json b/spec/fixtures/api/schemas/current-board.json new file mode 100644 index 00000000000..2ddc038e908 --- /dev/null +++ b/spec/fixtures/api/schemas/current-board.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "allOf": [ + { "$ref": "board.json" }, + { + "required" : [ + "id", + "name" + ], + "properties": { + "id": { "type": "integer" }, + "name": { "type": "string" } + } + } + ] +} diff --git a/spec/fixtures/phabricator_responses/user.search.json b/spec/fixtures/phabricator_responses/user.search.json new file mode 100644 index 00000000000..f3ec653a23e --- /dev/null +++ b/spec/fixtures/phabricator_responses/user.search.json @@ -0,0 +1,62 @@ +{ + "result": { + "data": [ + { + "id": 1, + "type": "USER", + "phid": "PHID-USER-hohoho", + "fields": { + "username": "jane", + "realName": "Jane Doe", + "roles": [ + "admin", + "verified", + "approved", + "activated" + ], + "dateCreated": 1405970599, + "dateModified": 1406705963, + "policy": { + "view": "public", + "edit": "no-one" + } + }, + "attachments": {} + }, + { + "id": 2, + "type": "USER", + "phid": "PHID-USER-hihihi", + "fields": { + "username": "john", + "realName": "John Doe", + "roles": [ + "admin", + "verified", + "approved", + "activated" + ], + "dateCreated": 1403609184, + "dateModified": 1559138722, + "policy": { + "view": "public", + "edit": "no-one" + } + }, + "attachments": {} + } + ], + "maps": {}, + "query": { + "queryKey": null + }, + "cursor": { + "limit": "100", + "after": null, + "before": null, + "order": null + } + }, + "error_code": null, + "error_info": null +} diff --git a/spec/frontend/api_spec.js b/spec/frontend/api_spec.js index 0188d12a57d..7004373be0e 100644 --- a/spec/frontend/api_spec.js +++ b/spec/frontend/api_spec.js @@ -412,6 +412,22 @@ describe('Api', () => { }); }); + describe('user counts', () => { + it('fetches single user counts', done => { + const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/user_counts`; + mock.onGet(expectedUrl).reply(200, { + merge_requests: 4, + }); + + Api.userCounts() + .then(({ data }) => { + expect(data.merge_requests).toBe(4); + }) + .then(done) + .catch(done.fail); + }); + }); + describe('user status', () => { it('fetches single user status', done => { const userId = '123456'; diff --git a/spec/frontend/commons/nav/user_merge_requests_spec.js b/spec/frontend/commons/nav/user_merge_requests_spec.js new file mode 100644 index 00000000000..4da6d53557a --- /dev/null +++ b/spec/frontend/commons/nav/user_merge_requests_spec.js @@ -0,0 +1,113 @@ +import { + openUserCountsBroadcast, + closeUserCountsBroadcast, + refreshUserMergeRequestCounts, +} from '~/commons/nav/user_merge_requests'; +import Api from '~/api'; + +jest.mock('~/api'); + +const TEST_COUNT = 1000; +const MR_COUNT_CLASS = 'merge-requests-count'; + +describe('User Merge Requests', () => { + let channelMock; + let newBroadcastChannelMock; + + beforeEach(() => { + global.gon.current_user_id = 123; + + channelMock = { + postMessage: jest.fn(), + close: jest.fn(), + }; + newBroadcastChannelMock = jest.fn().mockImplementation(() => channelMock); + + global.BroadcastChannel = newBroadcastChannelMock; + setFixtures(`<div class="${MR_COUNT_CLASS}">0</div>`); + }); + + const findMRCountText = () => document.body.querySelector(`.${MR_COUNT_CLASS}`).textContent; + + describe('refreshUserMergeRequestCounts', () => { + beforeEach(() => { + Api.userCounts.mockReturnValue( + Promise.resolve({ + data: { merge_requests: TEST_COUNT }, + }), + ); + }); + + describe('with open broadcast channel', () => { + beforeEach(() => { + openUserCountsBroadcast(); + + return refreshUserMergeRequestCounts(); + }); + + it('updates the top count of merge requests', () => { + expect(findMRCountText()).toEqual(TEST_COUNT.toLocaleString()); + }); + + it('calls the API', () => { + expect(Api.userCounts).toHaveBeenCalled(); + }); + + it('posts count to BroadcastChannel', () => { + expect(channelMock.postMessage).toHaveBeenCalledWith(TEST_COUNT); + }); + }); + + describe('without open broadcast channel', () => { + beforeEach(() => refreshUserMergeRequestCounts()); + + it('does not post anything', () => { + expect(channelMock.postMessage).not.toHaveBeenCalled(); + }); + }); + }); + + describe('openUserCountsBroadcast', () => { + beforeEach(() => { + openUserCountsBroadcast(); + }); + + it('creates BroadcastChannel that updates DOM on message received', () => { + expect(findMRCountText()).toEqual('0'); + + channelMock.onmessage({ data: TEST_COUNT }); + + expect(findMRCountText()).toEqual(TEST_COUNT.toLocaleString()); + }); + + it('closes if called while already open', () => { + expect(channelMock.close).not.toHaveBeenCalled(); + + openUserCountsBroadcast(); + + expect(channelMock.close).toHaveBeenCalled(); + }); + }); + + describe('closeUserCountsBroadcast', () => { + describe('when not opened', () => { + it('does nothing', () => { + expect(channelMock.close).not.toHaveBeenCalled(); + }); + }); + + describe('when opened', () => { + beforeEach(() => { + openUserCountsBroadcast(); + }); + + it('closes', () => { + expect(channelMock.close).not.toHaveBeenCalled(); + + closeUserCountsBroadcast(); + + expect(channelMock.close).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap b/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap index a241c764df7..47bdc677068 100644 --- a/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap +++ b/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap @@ -2,7 +2,7 @@ exports[`Confidential merge request project form group component renders empty state when response is empty 1`] = ` <div - class="form-group" + class="confidential-merge-request-fork-group form-group" > <label> Project @@ -19,7 +19,7 @@ exports[`Confidential merge request project form group component renders empty s <br /> <span> - To protect this issues confidentiality, + To protect this issue's confidentiality, <a class="help-link" href="https://test.com" @@ -30,7 +30,7 @@ exports[`Confidential merge request project form group component renders empty s </span> <gllink-stub - class="help-link" + class="w-auto p-0 d-inline-block text-primary bg-transparent" href="/help" target="_blank" > @@ -52,7 +52,7 @@ exports[`Confidential merge request project form group component renders empty s exports[`Confidential merge request project form group component renders fork dropdown 1`] = ` <div - class="form-group" + class="confidential-merge-request-fork-group form-group" > <label> Project @@ -69,7 +69,7 @@ exports[`Confidential merge request project form group component renders fork dr <br /> <span> - To protect this issues confidentiality, + To protect this issue's confidentiality, <a class="help-link" href="https://test.com" @@ -80,7 +80,7 @@ exports[`Confidential merge request project form group component renders fork dr </span> <gllink-stub - class="help-link" + class="w-auto p-0 d-inline-block text-primary bg-transparent" href="/help" target="_blank" > diff --git a/spec/frontend/error_tracking_settings/mock.js b/spec/frontend/error_tracking_settings/mock.js index 42233f82d54..8c5bfd08beb 100644 --- a/spec/frontend/error_tracking_settings/mock.js +++ b/spec/frontend/error_tracking_settings/mock.js @@ -1,5 +1,5 @@ import createStore from '~/error_tracking_settings/store'; -import { TEST_HOST } from '../helpers/test_constants'; +import { TEST_HOST } from 'helpers/test_constants'; const defaultStore = createStore(); diff --git a/spec/frontend/filterable_list_spec.js b/spec/frontend/filterable_list_spec.js new file mode 100644 index 00000000000..67d18611661 --- /dev/null +++ b/spec/frontend/filterable_list_spec.js @@ -0,0 +1,53 @@ +import FilterableList from '~/filterable_list'; +import { getJSONFixture, setHTMLFixture } from './helpers/fixtures'; + +describe('FilterableList', () => { + let List; + let form; + let filter; + let holder; + + beforeEach(() => { + setHTMLFixture(` + <form id="project-filter-form"> + <input name="name" class="js-projects-list-filter" /> + </div> + <div class="js-projects-list-holder"></div> + `); + getJSONFixture('static/projects.json'); + form = document.querySelector('form#project-filter-form'); + filter = document.querySelector('.js-projects-list-filter'); + holder = document.querySelector('.js-projects-list-holder'); + List = new FilterableList(form, filter, holder); + }); + + it('processes input parameters', () => { + expect(List.filterForm).toEqual(form); + expect(List.listFilterElement).toEqual(filter); + expect(List.listHolderElement).toEqual(holder); + }); + + describe('getPagePath', () => { + it('returns properly constructed base endpoint', () => { + List.filterForm.action = '/foo/bar/'; + List.listFilterElement.value = 'blah'; + + expect(List.getPagePath()).toEqual('/foo/bar/?name=blah'); + }); + + it('properly appends custom parameters to existing URL', () => { + List.filterForm.action = '/foo/bar?alpha=beta'; + List.listFilterElement.value = 'blah'; + + expect(List.getPagePath()).toEqual('/foo/bar?alpha=beta&name=blah'); + }); + }); + + describe('getFilterEndpoint', () => { + it('returns getPagePath by default', () => { + jest.spyOn(List, 'getPagePath').mockReturnValue('blah/blah/foo'); + + expect(List.getFilterEndpoint()).toEqual(List.getPagePath()); + }); + }); +}); diff --git a/spec/frontend/ide/lib/files_spec.js b/spec/frontend/ide/lib/files_spec.js index aa1fa0373db..08a31318544 100644 --- a/spec/frontend/ide/lib/files_spec.js +++ b/spec/frontend/ide/lib/files_spec.js @@ -12,6 +12,7 @@ const createEntries = paths => { const { name, parent } = splitParent(path); const parentEntry = acc[parent]; + const previewMode = viewerInformationForPath(name); acc[path] = { ...decorateData({ @@ -22,7 +23,8 @@ const createEntries = paths => { path, url: createUrl(`/${TEST_PROJECT_ID}/${type}/${TEST_BRANCH_ID}/-/${escapeFileUrl(path)}`), type, - previewMode: viewerInformationForPath(path), + previewMode, + binary: (previewMode && previewMode.binary) || false, parentPath: parent, parentTreeUrl: parentEntry ? parentEntry.url diff --git a/spec/frontend/notes/components/discussion_notes_replies_wrapper_spec.js b/spec/frontend/notes/components/discussion_notes_replies_wrapper_spec.js new file mode 100644 index 00000000000..279ca017b44 --- /dev/null +++ b/spec/frontend/notes/components/discussion_notes_replies_wrapper_spec.js @@ -0,0 +1,51 @@ +import { mount } from '@vue/test-utils'; +import DiscussionNotesRepliesWrapper from '~/notes/components/discussion_notes_replies_wrapper.vue'; + +const TEST_CHILDREN = '<li>Hello!</li><li>World!</li>'; + +// We have to wrap our SUT with a TestComponent because multiple roots are possible +// because it's a functional component. +const TestComponent = { + components: { DiscussionNotesRepliesWrapper }, + template: `<ul><discussion-notes-replies-wrapper v-bind="$attrs">${TEST_CHILDREN}</discussion-notes-replies-wrapper></ul>`, +}; + +describe('DiscussionNotesRepliesWrapper', () => { + let wrapper; + + const createComponent = (props = {}) => { + wrapper = mount(TestComponent, { + propsData: props, + sync: false, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when normal discussion', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders children directly', () => { + expect(wrapper.html()).toEqual(`<ul>${TEST_CHILDREN}</ul>`); + }); + }); + + describe('when diff discussion', () => { + beforeEach(() => { + createComponent({ + isDiffDiscussion: true, + }); + }); + + it('wraps children with notes', () => { + const notes = wrapper.find('li.discussion-collapsible ul.notes'); + + expect(notes.exists()).toBe(true); + expect(notes.html()).toEqual(`<ul class="notes">${TEST_CHILDREN}</ul>`); + }); + }); +}); diff --git a/spec/frontend/projects/projects_filterable_list_spec.js b/spec/frontend/projects/projects_filterable_list_spec.js new file mode 100644 index 00000000000..e756fb3ab56 --- /dev/null +++ b/spec/frontend/projects/projects_filterable_list_spec.js @@ -0,0 +1,31 @@ +import ProjectsFilterableList from '~/projects/projects_filterable_list'; +import { getJSONFixture, setHTMLFixture } from '../helpers/fixtures'; + +describe('ProjectsFilterableList', () => { + let List; + let form; + let filter; + let holder; + + beforeEach(() => { + setHTMLFixture(` + <form id="project-filter-form"> + <input name="name" class="js-projects-list-filter" /> + </div> + <div class="js-projects-list-holder"></div> + `); + getJSONFixture('static/projects.json'); + form = document.querySelector('form#project-filter-form'); + filter = document.querySelector('.js-projects-list-filter'); + holder = document.querySelector('.js-projects-list-holder'); + List = new ProjectsFilterableList(form, filter, holder); + }); + + describe('getFilterEndpoint', () => { + it('updates converts getPagePath for projects', () => { + jest.spyOn(List, 'getPagePath').mockReturnValue('blah/projects?'); + + expect(List.getFilterEndpoint()).toEqual('blah/projects.json?'); + }); + }); +}); diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index 93b86b9b812..dec6b23d72a 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -143,6 +143,26 @@ describe GitlabSchema do end end + context 'for classes that are not ActiveRecord subclasses and have implemented .lazy_find' do + it 'returns the correct record' do + note = create(:discussion_note_on_merge_request) + + result = described_class.object_from_id(note.to_global_id) + + expect(result.__sync).to eq(note) + end + + it 'batchloads the queries' do + note1 = create(:discussion_note_on_merge_request) + note2 = create(:discussion_note_on_merge_request) + + expect do + [described_class.object_from_id(note1.to_global_id), + described_class.object_from_id(note2.to_global_id)].map(&:__sync) + end.not_to exceed_query_limit(1) + end + end + context 'for other classes' do # We cannot use an anonymous class here as `GlobalID` expects `.name` not # to return `nil` diff --git a/spec/graphql/types/diff_refs_type_spec.rb b/spec/graphql/types/diff_refs_type_spec.rb new file mode 100644 index 00000000000..91017c827ad --- /dev/null +++ b/spec/graphql/types/diff_refs_type_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe GitlabSchema.types['DiffRefs'] do + it { expect(described_class.graphql_name).to eq('DiffRefs') } + + it { expect(described_class).to have_graphql_fields(:base_sha, :head_sha, :start_sha) } +end diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb index f73bd062369..59bd0123d88 100644 --- a/spec/graphql/types/merge_request_type_spec.rb +++ b/spec/graphql/types/merge_request_type_spec.rb @@ -13,7 +13,7 @@ describe GitlabSchema.types['MergeRequest'] do description_html state created_at updated_at source_project target_project project project_id source_project_id target_project_id source_branch target_branch work_in_progress merge_when_pipeline_succeeds diff_head_sha - merge_commit_sha user_notes_count should_remove_source_branch + merge_commit_sha user_notes_count should_remove_source_branch diff_refs force_remove_source_branch merge_status in_progress_merge_commit_sha merge_error allow_collaboration should_be_rebased rebase_commit_sha rebase_in_progress merge_commit_message default_merge_commit_message diff --git a/spec/graphql/types/notes/diff_position_type_spec.rb b/spec/graphql/types/notes/diff_position_type_spec.rb index 2f8724d7f0d..345bca8f702 100644 --- a/spec/graphql/types/notes/diff_position_type_spec.rb +++ b/spec/graphql/types/notes/diff_position_type_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe GitlabSchema.types['DiffPosition'] do it 'exposes the expected fields' do - expected_fields = [:head_sha, :base_sha, :start_sha, :file_path, :old_path, + expected_fields = [:diff_refs, :file_path, :old_path, :new_path, :position_type, :old_line, :new_line, :x, :y, :width, :height] diff --git a/spec/graphql/types/notes/discussion_type_spec.rb b/spec/graphql/types/notes/discussion_type_spec.rb index 2a1eb0efd35..ba7fc961212 100644 --- a/spec/graphql/types/notes/discussion_type_spec.rb +++ b/spec/graphql/types/notes/discussion_type_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe GitlabSchema.types['Discussion'] do - it { is_expected.to have_graphql_fields(:id, :created_at, :notes) } + it { is_expected.to have_graphql_fields(:id, :created_at, :notes, :reply_id) } it { is_expected.to require_graphql_authorizations(:read_note) } end diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb index aa0442ab847..94998d302f9 100644 --- a/spec/helpers/avatars_helper_spec.rb +++ b/spec/helpers/avatars_helper_spec.rb @@ -16,7 +16,7 @@ describe AvatarsHelper do shared_examples 'resource with a custom avatar' do |source_type| it 'returns a custom avatar image' do expect(public_send("#{source_type}_icon", *helper_args)) - .to eq "<img src=\"#{resource.avatar.url}\" alt=\"Banana sample\" />" + .to eq "<img src=\"#{resource.avatar.url}\" />" end end diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 6808ed86c9a..1f236429347 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -29,14 +29,15 @@ describe BlobHelper do let(:project) { create(:project, :repository, namespace: namespace) } before do - allow(self).to receive(:current_user).and_return(nil) - allow(self).to receive(:can_collaborate_with_project?).and_return(true) + allow(helper).to receive(:current_user).and_return(nil) + allow(helper).to receive(:can?).and_return(true) + allow(helper).to receive(:can_collaborate_with_project?).and_return(true) end it 'verifies blob is text' do expect(helper).not_to receive(:blob_text_viewable?) - button = edit_blob_button(project, 'refs/heads/master', 'README.md') + button = helper.edit_blob_button(project, 'refs/heads/master', 'README.md') expect(button).to start_with('<button') end @@ -46,25 +47,25 @@ describe BlobHelper do expect(project.repository).not_to receive(:blob_at) - edit_blob_button(project, 'refs/heads/master', 'README.md', blob: blob) + helper.edit_blob_button(project, 'refs/heads/master', 'README.md', blob: blob) end it 'returns a link with the proper route' do stub_feature_flags(web_ide_default: false) - link = edit_blob_button(project, 'master', 'README.md') + link = helper.edit_blob_button(project, 'master', 'README.md') expect(Capybara.string(link).find_link('Edit')[:href]).to eq("/#{project.full_path}/edit/master/README.md") end it 'returns a link with a Web IDE route' do - link = edit_blob_button(project, 'master', 'README.md') + link = helper.edit_blob_button(project, 'master', 'README.md') expect(Capybara.string(link).find_link('Edit')[:href]).to eq("/-/ide/project/#{project.full_path}/edit/master/-/README.md") end it 'returns a link with the passed link_opts on the expected route' do stub_feature_flags(web_ide_default: false) - link = edit_blob_button(project, 'master', 'README.md', link_opts: { mr_id: 10 }) + link = helper.edit_blob_button(project, 'master', 'README.md', link_opts: { mr_id: 10 }) expect(Capybara.string(link).find_link('Edit')[:href]).to eq("/#{project.full_path}/edit/master/README.md?mr_id=10") end @@ -203,6 +204,13 @@ describe BlobHelper do describe '#ide_edit_path' do let(:project) { create(:project) } + let(:current_user) { create(:user) } + let(:can_push_code) { true } + + before do + allow(helper).to receive(:current_user).and_return(current_user) + allow(helper).to receive(:can?).and_return(can_push_code) + end around do |example| old_script_name = Rails.application.routes.default_url_options[:script_name] @@ -243,5 +251,21 @@ describe BlobHelper do expect(helper.ide_edit_path(project, "testing/slashes", "readme.md/")).to eq("/-/ide/project/#{project.namespace.path}/#{project.path}/edit/testing/slashes/-/readme.md/") end + + context 'when user is not logged in' do + let(:current_user) { nil } + + it 'returns IDE path inside the project' do + expect(helper.ide_edit_path(project, "master", "")).to eq("/-/ide/project/#{project.namespace.path}/#{project.path}/edit/master") + end + end + + context 'when user cannot push to the project' do + let(:can_push_code) { false } + + it "returns IDE path with the user's fork" do + expect(helper.ide_edit_path(project, "master", "")).to eq("/-/ide/project/#{current_user.namespace.full_path}/#{project.path}/edit/master") + end + end end end diff --git a/spec/helpers/boards_helper_spec.rb b/spec/helpers/boards_helper_spec.rb index b22947911b9..f014537eb54 100644 --- a/spec/helpers/boards_helper_spec.rb +++ b/spec/helpers/boards_helper_spec.rb @@ -1,10 +1,12 @@ require 'spec_helper' describe BoardsHelper do + set(:project) { create(:project) } + describe '#build_issue_link_base' do context 'project board' do it 'returns correct path for project board' do - @project = create(:project) + @project = project @board = create(:board, project: @project) expect(build_issue_link_base).to eq("/#{@project.namespace.path}/#{@project.path}/issues") @@ -31,7 +33,6 @@ describe BoardsHelper do describe '#board_data' do let(:user) { create(:user) } - let(:project) { create(:project) } let(:board) { create(:board, project: project) } before do @@ -46,4 +47,15 @@ describe BoardsHelper do expect(helper.board_data[:lists_endpoint]).to eq(board_lists_path(board)) end end + + describe '#current_board_json' do + let(:board_json) { helper.current_board_json } + + it 'can serialise with a basic set of attributes' do + board = create(:board, project: project) + assign(:board, board) + + expect(board_json).to match_schema('current-board') + end + end end diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb index e6aacb5b92b..d25f0c6de4a 100644 --- a/spec/helpers/emails_helper_spec.rb +++ b/spec/helpers/emails_helper_spec.rb @@ -103,7 +103,7 @@ describe EmailsHelper do appearance = create :appearance, header_logo: fixture_file_upload('spec/fixtures/dk.png') expect(header_logo).to eq( - %{<img style="height: 50px" src="/uploads/-/system/appearance/header_logo/#{appearance.id}/dk.png" alt="Dk" />} + %{<img style="height: 50px" src="/uploads/-/system/appearance/header_logo/#{appearance.id}/dk.png" />} ) end end diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index 6c6410cee9b..f6e1720e113 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -268,7 +268,7 @@ describe MarkupHelper do end end - describe 'markup' do + describe '#markup' do let(:content) { 'Noël' } it 'preserves encoding' do @@ -302,6 +302,77 @@ describe MarkupHelper do end end + describe '#markup_unsafe' do + subject { helper.markup_unsafe(file_name, text, context) } + + let(:file_name) { 'foo.bar' } + let(:text) { 'Noël' } + let(:project_base) { build(:project, :repository) } + let(:context) { { project: project_base } } + + context 'when text is missing' do + let(:text) { nil } + + it 'returns an empty string' do + is_expected.to eq('') + end + end + + context 'when file is a markdown file' do + let(:file_name) { 'foo.md' } + + it 'returns html (rendered by Banzai)' do + expected_html = '<p data-sourcepos="1:1-1:5" dir="auto">Noël</p>' + + expect(Banzai).to receive(:render).with(text, context) { expected_html } + + is_expected.to eq(expected_html) + end + + context 'when renderer returns an error' do + before do + allow(Banzai).to receive(:render).and_raise("An error") + end + + it 'returns html (rendered by ActionView:TextHelper)' do + is_expected.to eq('<p>Noël</p>') + end + end + end + + context 'when file is asciidoc file' do + let(:file_name) { 'foo.adoc' } + + it 'returns html (rendered by Gitlab::Asciidoc)' do + expected_html = "<div>\n<p>Noël</p>\n</div>" + + expect(Gitlab::Asciidoc).to receive(:render).with(text, context) { expected_html } + + is_expected.to eq(expected_html) + end + end + + context 'when file is a regular text file' do + let(:file_name) { 'foo.txt' } + + it 'returns html (rendered by ActionView::TagHelper)' do + is_expected.to eq('<pre class="plain-readme">Noël</pre>') + end + end + + context 'when file has an unknown type' do + let(:file_name) { 'foo' } + + it 'returns html (rendered by Gitlab::OtherMarkup)' do + expected_html = 'Noël' + + expect(Gitlab::OtherMarkup).to receive(:render).with(file_name, text, context) { expected_html } + + is_expected.to eq(expected_html) + end + end + end + describe '#first_line_in_markdown' do shared_examples_for 'common markdown examples' do let(:project_base) { build(:project, :repository) } diff --git a/spec/helpers/sorting_helper_spec.rb b/spec/helpers/sorting_helper_spec.rb index f405268d198..5397a47b3dd 100644 --- a/spec/helpers/sorting_helper_spec.rb +++ b/spec/helpers/sorting_helper_spec.rb @@ -4,6 +4,11 @@ require 'spec_helper' describe SortingHelper do include ApplicationHelper include IconsHelper + include ExploreHelper + + def set_sorting_url(option) + allow(self).to receive(:request).and_return(double(path: 'http://test.com', query_parameters: { label_name: option })) + end describe '#issuable_sort_option_title' do it 'returns correct title for issuable_sort_option_overrides key' do @@ -21,7 +26,7 @@ describe SortingHelper do describe '#issuable_sort_direction_button' do before do - allow(self).to receive(:request).and_return(double(path: 'http://test.com', query_parameters: { label_name: 'test_label' })) + set_sorting_url 'test_label' end it 'keeps label filter param' do @@ -44,4 +49,145 @@ describe SortingHelper do expect(issuable_sort_direction_button('due_date')).to include('sort-lowest') end end + + def stub_controller_path(value) + allow(helper.controller).to receive(:controller_path).and_return(value) + end + + def project_common_options + { + sort_value_latest_activity => sort_title_latest_activity, + sort_value_recently_created => sort_title_created_date, + sort_value_name => sort_title_name, + sort_value_stars_desc => sort_title_stars + } + end + + describe 'with `admin/projects` controller' do + before do + stub_controller_path 'admin/projects' + end + + describe '#projects_sort_options_hash' do + it 'returns a hash of available sorting options' do + admin_options = project_common_options.merge({ + sort_value_oldest_activity => sort_title_oldest_activity, + sort_value_oldest_created => sort_title_oldest_created, + sort_value_recently_created => sort_title_recently_created, + sort_value_stars_desc => sort_title_most_stars, + sort_value_largest_repo => sort_title_largest_repo + }) + + expect(projects_sort_options_hash).to eq(admin_options) + end + end + end + + describe 'with `projects` controller' do + before do + stub_controller_path 'projects' + end + + describe '#projects_sort_options_hash' do + it 'returns a hash of available sorting options' do + expect(projects_sort_options_hash).to include(project_common_options) + end + end + + describe '#projects_reverse_sort_options_hash' do + context 'returns a reversed hash of available sorting options' do + using RSpec::Parameterized::TableSyntax + + where(:sort_key, :reverse_sort_title) do + sort_value_latest_activity | sort_value_oldest_activity + sort_value_recently_created | sort_value_oldest_created + sort_value_name | sort_value_name_desc + sort_value_stars_desc | sort_value_stars_asc + sort_value_oldest_activity | sort_value_latest_activity + sort_value_oldest_created | sort_value_recently_created + sort_value_name_desc | sort_value_name + sort_value_stars_asc | sort_value_stars_desc + end + + with_them do + it do + reverse_hash = projects_reverse_sort_options_hash + + expect(reverse_hash).to include(sort_key) + expect(reverse_hash[sort_key]).to eq(reverse_sort_title) + end + end + end + end + + describe '#project_sort_direction_button' do + context 'returns the correct icon for each sort option' do + using RSpec::Parameterized::TableSyntax + + sort_lowest_icon = 'sort-lowest' + sort_highest_icon = 'sort-highest' + + where(:selected_sort, :icon) do + sort_value_latest_activity | sort_highest_icon + sort_value_recently_created | sort_highest_icon + sort_value_name_desc | sort_highest_icon + sort_value_stars_desc | sort_highest_icon + sort_value_oldest_activity | sort_lowest_icon + sort_value_oldest_created | sort_lowest_icon + sort_value_name | sort_lowest_icon + sort_value_stars_asc | sort_lowest_icon + end + + with_them do + it do + set_sorting_url selected_sort + + expect(project_sort_direction_button(selected_sort)).to include(icon) + end + end + end + + it 'returns the correct link to reverse the current sort option' do + sort_options_links = projects_reverse_sort_options_hash + + sort_options_links.each do |selected_sort, reverse_sort| + set_sorting_url selected_sort + + expect(project_sort_direction_button(selected_sort)).to include(reverse_sort) + end + end + end + + describe '#projects_sort_option_titles' do + it 'returns a hash of titles for the sorting options' do + options = project_common_options.merge({ + sort_value_oldest_activity => sort_title_latest_activity, + sort_value_oldest_created => sort_title_created_date, + sort_value_name_desc => sort_title_name, + sort_value_stars_asc => sort_title_stars + }) + + expect(projects_sort_option_titles).to eq(options) + end + end + + describe 'with project_list_filter_bar off' do + before do + stub_feature_flags(project_list_filter_bar: false) + end + + describe '#projects_sort_options_hash' do + it 'returns a hash of available sorting options' do + options = project_common_options.merge({ + sort_value_oldest_activity => sort_title_oldest_activity, + sort_value_oldest_created => sort_title_oldest_created, + sort_value_recently_created => sort_title_recently_created, + sort_value_stars_desc => sort_title_most_stars + }) + + expect(projects_sort_options_hash).to eq(options) + end + end + end + end end diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb index 62c00964524..4bd0fbb76ca 100644 --- a/spec/helpers/storage_helper_spec.rb +++ b/spec/helpers/storage_helper_spec.rb @@ -31,7 +31,7 @@ describe StorageHelper do build_artifacts_size: 30.megabytes)) end - let(:message) { '10 KB repositories, 10 Bytes wikis, 30 MB build artifacts, 20 GB LFS' } + let(:message) { 'Repository: 10 KB / Wikis: 10 Bytes / Build Artifacts: 30 MB / LFS: 20 GB' } it 'works on ProjectStatistics' do expect(helper.storage_counters_details(project.statistics)).to eq(message) diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js index 0b3890b68d6..9b61dbe7975 100644 --- a/spec/javascripts/diffs/components/inline_diff_view_spec.js +++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js @@ -10,6 +10,7 @@ describe('InlineDiffView', () => { let component; const getDiffFileMock = () => Object.assign({}, diffFileMockData); const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)]; + const notesLength = getDiscussionsMockData()[0].notes.length; beforeEach(done => { const diffFile = getDiffFileMock(); @@ -40,7 +41,7 @@ describe('InlineDiffView', () => { Vue.nextTick(() => { expect(el.querySelectorAll('.notes_holder').length).toEqual(1); - expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(6); + expect(el.querySelectorAll('.notes_holder .note').length).toEqual(notesLength + 1); expect(el.innerText.indexOf('comment 5')).toBeGreaterThan(-1); component.$store.dispatch('setInitialNotes', []); diff --git a/spec/javascripts/helpers/vuex_action_helper.js b/spec/javascripts/helpers/vuex_action_helper.js index 88652202a8e..c5de31a4138 100644 --- a/spec/javascripts/helpers/vuex_action_helper.js +++ b/spec/javascripts/helpers/vuex_action_helper.js @@ -89,9 +89,7 @@ export default ( payload, ); - return new Promise(resolve => { - setImmediate(resolve); - }) + return new Promise(setImmediate) .then(() => result) .catch(error => { validateResults(); diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js index f832096701f..7dc5cb24981 100644 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ b/spec/javascripts/ide/components/repo_editor_spec.js @@ -30,6 +30,7 @@ describe('RepoEditor', () => { Vue.set(vm.$store.state.entries, f.path, f); spyOn(vm, 'getFileData').and.returnValue(Promise.resolve()); + spyOn(vm, 'getRawFileData').and.returnValue(Promise.resolve()); vm.$mount(); @@ -407,6 +408,44 @@ describe('RepoEditor', () => { }); }); + describe('initEditor', () => { + beforeEach(() => { + spyOn(vm.editor, 'createInstance'); + spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true); + }); + + it('is being initialised for files without content even if shouldHideEditor is `true`', done => { + vm.file.content = ''; + vm.file.raw = ''; + + vm.initEditor(); + vm.$nextTick() + .then(() => { + expect(vm.getFileData).toHaveBeenCalled(); + expect(vm.getRawFileData).toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); + + it('does not initialize editor for files already with content', done => { + expect(vm.getFileData.calls.count()).toEqual(1); + expect(vm.getRawFileData.calls.count()).toEqual(1); + + vm.file.content = 'foo'; + + vm.initEditor(); + vm.$nextTick() + .then(() => { + expect(vm.getFileData.calls.count()).toEqual(1); + expect(vm.getRawFileData.calls.count()).toEqual(1); + expect(vm.editor.createInstance).not.toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); + }); + it('calls removePendingTab when old file is pending', done => { spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true); spyOn(vm, 'removePendingTab'); @@ -416,6 +455,7 @@ describe('RepoEditor', () => { vm.$nextTick() .then(() => { vm.file = file('testing'); + vm.file.content = 'foo'; // need to prevent full cycle of initEditor return vm.$nextTick(); }) diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js index 18ee4330f69..7714f66c9a4 100644 --- a/spec/javascripts/ide/stores/mutations/file_spec.js +++ b/spec/javascripts/ide/stores/mutations/file_spec.js @@ -83,6 +83,26 @@ describe('IDE store file mutations', () => { expect(localFile.raw).toBeNull(); expect(localFile.baseRaw).toBeNull(); }); + + it('sets extra file data to all arrays concerned', () => { + localState.stagedFiles = [localFile]; + localState.changedFiles = [localFile]; + localState.openFiles = [localFile]; + + const rawPath = 'foo/bar/blah.md'; + + mutations.SET_FILE_DATA(localState, { + data: { + raw_path: rawPath, + }, + file: localFile, + }); + + expect(localState.stagedFiles[0].rawPath).toEqual(rawPath); + expect(localState.changedFiles[0].rawPath).toEqual(rawPath); + expect(localState.openFiles[0].rawPath).toEqual(rawPath); + expect(localFile.rawPath).toEqual(rawPath); + }); }); describe('SET_FILE_RAW_DATA', () => { diff --git a/spec/javascripts/issuable_spec.js b/spec/javascripts/issuable_spec.js index 25543053eba..4d57bfb1b33 100644 --- a/spec/javascripts/issuable_spec.js +++ b/spec/javascripts/issuable_spec.js @@ -2,14 +2,14 @@ import $ from 'jquery'; import MockAdaptor from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import IssuableIndex from '~/issuable_index'; +import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar'; describe('Issuable', () => { - let Issuable; describe('initBulkUpdate', () => { it('should not set bulkUpdateSidebar', () => { - Issuable = new IssuableIndex('issue_'); + new IssuableIndex('issue_'); // eslint-disable-line no-new - expect(Issuable.bulkUpdateSidebar).not.toBeDefined(); + expect(issuableInitBulkUpdateSidebar.bulkUpdateSidebar).toBeNull(); }); it('should set bulkUpdateSidebar', () => { @@ -17,9 +17,9 @@ describe('Issuable', () => { element.classList.add('issues-bulk-update'); document.body.appendChild(element); - Issuable = new IssuableIndex('issue_'); + new IssuableIndex('issue_'); // eslint-disable-line no-new - expect(Issuable.bulkUpdateSidebar).toBeDefined(); + expect(issuableInitBulkUpdateSidebar.bulkUpdateSidebar).toBeDefined(); }); }); @@ -36,7 +36,7 @@ describe('Issuable', () => { input.setAttribute('id', 'issuable_email'); document.body.appendChild(input); - Issuable = new IssuableIndex('issue_'); + new IssuableIndex('issue_'); // eslint-disable-line no-new mock = new MockAdaptor(axios); diff --git a/spec/javascripts/monitoring/charts/single_stat_spec.js b/spec/javascripts/monitoring/charts/single_stat_spec.js index 12b73002f97..127a4a7955a 100644 --- a/spec/javascripts/monitoring/charts/single_stat_spec.js +++ b/spec/javascripts/monitoring/charts/single_stat_spec.js @@ -1,5 +1,6 @@ import { shallowMount } from '@vue/test-utils'; import SingleStatChart from '~/monitoring/components/charts/single_stat.vue'; +import { graphDataPrometheusQuery } from '../mock_data'; describe('Single Stat Chart component', () => { let singleStatChart; @@ -7,9 +8,7 @@ describe('Single Stat Chart component', () => { beforeEach(() => { singleStatChart = shallowMount(SingleStatChart, { propsData: { - title: 'Time to render', - value: 1, - unit: 'sec', + graphData: graphDataPrometheusQuery, }, }); }); @@ -19,9 +18,9 @@ describe('Single Stat Chart component', () => { }); describe('computed', () => { - describe('valueWithUnit', () => { + describe('engineeringNotation', () => { it('should interpolate the value and unit props', () => { - expect(singleStatChart.vm.valueWithUnit).toBe('1sec'); + expect(singleStatChart.vm.engineeringNotation).toBe('91MB'); }); }); }); diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js index 7bbb215475a..85e660d3925 100644 --- a/spec/javascripts/monitoring/mock_data.js +++ b/spec/javascripts/monitoring/mock_data.js @@ -935,3 +935,75 @@ export const dashboardGitResponse = [ default: false, }, ]; + +export const graphDataPrometheusQuery = { + title: 'Super Chart A2', + type: 'single-stat', + weight: 2, + metrics: [ + { + id: 'metric_a1', + metric_id: 2, + query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024', + unit: 'MB', + label: 'Total Consumption', + prometheus_endpoint_path: + '/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024', + }, + ], + queries: [ + { + metricId: null, + id: 'metric_a1', + metric_id: 2, + query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024', + unit: 'MB', + label: 'Total Consumption', + prometheus_endpoint_path: + '/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024', + result: [ + { + metric: { job: 'prometheus' }, + value: ['2019-06-26T21:03:20.881Z', 91], + }, + ], + }, + ], +}; + +export const graphDataPrometheusQueryRange = { + title: 'Super Chart A1', + type: 'area', + weight: 2, + metrics: [ + { + id: 'metric_a1', + metric_id: 2, + query_range: + 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024', + unit: 'MB', + label: 'Total Consumption', + prometheus_endpoint_path: + '/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024', + }, + ], + queries: [ + { + metricId: null, + id: 'metric_a1', + metric_id: 2, + query_range: + 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024', + unit: 'MB', + label: 'Total Consumption', + prometheus_endpoint_path: + '/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024', + result: [ + { + metric: {}, + values: [[1495700554.925, '8.0390625'], [1495700614.925, '8.0390625']], + }, + ], + }, + ], +}; diff --git a/spec/javascripts/monitoring/store/mutations_spec.js b/spec/javascripts/monitoring/store/mutations_spec.js index 91580366531..43776b1b7f2 100644 --- a/spec/javascripts/monitoring/store/mutations_spec.js +++ b/spec/javascripts/monitoring/store/mutations_spec.js @@ -115,12 +115,14 @@ describe('Monitoring mutations', () => { environmentsEndpoint: 'environments.json', deploymentsEndpoint: 'deployments.json', dashboardEndpoint: 'dashboard.json', + projectPath: '/gitlab-org/gitlab-ce', }); expect(stateCopy.metricsEndpoint).toEqual('additional_metrics.json'); expect(stateCopy.environmentsEndpoint).toEqual('environments.json'); expect(stateCopy.deploymentsEndpoint).toEqual('deployments.json'); expect(stateCopy.dashboardEndpoint).toEqual('dashboard.json'); + expect(stateCopy.projectPath).toEqual('/gitlab-org/gitlab-ce'); }); }); diff --git a/spec/javascripts/monitoring/utils_spec.js b/spec/javascripts/monitoring/utils_spec.js index e3c455d1686..5570d57b8b2 100644 --- a/spec/javascripts/monitoring/utils_spec.js +++ b/spec/javascripts/monitoring/utils_spec.js @@ -1,5 +1,6 @@ -import { getTimeDiff } from '~/monitoring/utils'; +import { getTimeDiff, graphDataValidatorForValues } from '~/monitoring/utils'; import { timeWindows } from '~/monitoring/constants'; +import { graphDataPrometheusQuery, graphDataPrometheusQueryRange } from './mock_data'; describe('getTimeDiff', () => { it('defaults to an 8 hour (28800s) difference', () => { @@ -27,3 +28,27 @@ describe('getTimeDiff', () => { }); }); }); + +describe('graphDataValidatorForValues', () => { + /* + * When dealing with a metric using the query format, e.g. + * query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024' + * the validator will look for the `value` key instead of `values` + */ + it('validates data with the query format', () => { + const validGraphData = graphDataValidatorForValues(true, graphDataPrometheusQuery); + + expect(validGraphData).toBe(true); + }); + + /* + * When dealing with a metric using the query?range format, e.g. + * query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024', + * the validator will look for the `values` key instead of `value` + */ + it('validates data with the query_range format', () => { + const validGraphData = graphDataValidatorForValues(false, graphDataPrometheusQueryRange); + + expect(validGraphData).toBe(true); + }); +}); diff --git a/spec/javascripts/notes/components/comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js index 362963ddaf4..88c86746992 100644 --- a/spec/javascripts/notes/components/comment_form_spec.js +++ b/spec/javascripts/notes/components/comment_form_spec.js @@ -251,6 +251,21 @@ describe('issue_comment_form component', () => { }); }); }); + + describe('when toggling state', () => { + it('should update MR count', done => { + spyOn(vm, 'closeIssue').and.returnValue(Promise.resolve()); + + const updateMrCountSpy = spyOnDependency(CommentForm, 'refreshUserMergeRequestCounts'); + vm.toggleIssueState(); + + Vue.nextTick(() => { + expect(updateMrCountSpy).toHaveBeenCalled(); + + done(); + }); + }); + }); }); describe('issue is confidential', () => { diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js index 65f72a135aa..c461c28a37b 100644 --- a/spec/javascripts/notes/stores/actions_spec.js +++ b/spec/javascripts/notes/stores/actions_spec.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import $ from 'jquery'; import _ from 'underscore'; +import Api from '~/api'; import { TEST_HOST } from 'spec/test_constants'; import { headersInterceptor } from 'spec/helpers/vue_resource_helper'; import actionsModule, * as actions from '~/notes/stores/actions'; @@ -8,7 +9,6 @@ import * as mutationTypes from '~/notes/stores/mutation_types'; import * as notesConstants from '~/notes/constants'; import createStore from '~/notes/stores'; import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub'; -import service from '~/notes/services/notes_service'; import testAction from '../../helpers/vuex_action_helper'; import { resetStore } from '../helpers'; import { @@ -18,6 +18,8 @@ import { noteableDataMock, individualNote, } from '../mock_data'; +import AxiosMockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; const TEST_ERROR_MESSAGE = 'Test error message'; @@ -335,28 +337,24 @@ describe('Actions Notes Store', () => { }); describe('deleteNote', () => { - const interceptor = (request, next) => { - next( - request.respondWith(JSON.stringify({}), { - status: 200, - }), - ); - }; + const endpoint = `${TEST_HOST}/note`; + let axiosMock; beforeEach(() => { - Vue.http.interceptors.push(interceptor); + axiosMock = new AxiosMockAdapter(axios); + axiosMock.onDelete(endpoint).replyOnce(200, {}); $('body').attr('data-page', ''); }); afterEach(() => { - Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor); + axiosMock.restore(); $('body').attr('data-page', ''); }); it('commits DELETE_NOTE and dispatches updateMergeRequestWidget', done => { - const note = { path: `${gl.TEST_HOST}`, id: 1 }; + const note = { path: endpoint, id: 1 }; testAction( actions.deleteNote, @@ -381,7 +379,7 @@ describe('Actions Notes Store', () => { }); it('dispatches removeDiscussionsFromDiff on merge request page', done => { - const note = { path: `${gl.TEST_HOST}`, id: 1 }; + const note = { path: endpoint, id: 1 }; $('body').attr('data-page', 'projects:merge_requests:show'); @@ -846,9 +844,9 @@ describe('Actions Notes Store', () => { let flashContainer; beforeEach(() => { - spyOn(service, 'applySuggestion'); + spyOn(Api, 'applySuggestion'); dispatch.and.returnValue(Promise.resolve()); - service.applySuggestion.and.returnValue(Promise.resolve()); + Api.applySuggestion.and.returnValue(Promise.resolve()); flashContainer = {}; }); @@ -877,7 +875,7 @@ describe('Actions Notes Store', () => { it('when service fails, flashes error message', done => { const response = { response: { data: { message: TEST_ERROR_MESSAGE } } }; - service.applySuggestion.and.returnValue(Promise.reject(response)); + Api.applySuggestion.and.returnValue(Promise.reject(response)); testSubmitSuggestion(done, () => { expect(commit).not.toHaveBeenCalled(); diff --git a/spec/javascripts/registry/components/collapsible_container_spec.js b/spec/javascripts/registry/components/collapsible_container_spec.js index 55017b3e26b..2a5d8dd11da 100644 --- a/spec/javascripts/registry/components/collapsible_container_spec.js +++ b/spec/javascripts/registry/components/collapsible_container_spec.js @@ -77,7 +77,7 @@ describe('collapsible registry container', () => { spyOn(vm, 'deleteItem').and.returnValue(Promise.resolve()); Vue.nextTick(() => { - document.querySelector('#confirm-repo-deletion-modal .btn-danger').click(); + document.querySelector(`#${vm.modalId} .btn-danger`).click(); expect(vm.deleteItem).toHaveBeenCalledWith(vm.repo); done(); diff --git a/spec/javascripts/registry/components/table_registry_spec.js b/spec/javascripts/registry/components/table_registry_spec.js index 6a0b16f592e..31ac970378e 100644 --- a/spec/javascripts/registry/components/table_registry_spec.js +++ b/spec/javascripts/registry/components/table_registry_spec.js @@ -51,7 +51,7 @@ describe('table registry', () => { spyOn(vm, 'deleteItem').and.returnValue(Promise.resolve()); Vue.nextTick(() => { - document.querySelector('#confirm-image-deletion-modal .btn-danger').click(); + document.querySelector(`#${vm.modalId} .btn-danger`).click(); expect(vm.deleteItem).toHaveBeenCalledWith(firstImage); expect(vm.itemToBeDeleted).toBeNull(); diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 2cc476ed52a..ce453d7c483 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -3,12 +3,12 @@ */ import $ from 'jquery'; +import 'core-js/features/set-immediate'; import 'vendor/jasmine-jquery'; import '~/commons'; import Vue from 'vue'; import VueResource from 'vue-resource'; import Translate from '~/vue_shared/translate'; -import CheckEE from '~/vue_shared/mixins/is_ee'; import jasmineDiff from 'jasmine-diff'; import { config as testUtilsConfig } from '@vue/test-utils'; @@ -48,7 +48,6 @@ Vue.config.errorHandler = function(err) { Vue.use(VueResource); Vue.use(Translate); -Vue.use(CheckEE); // enable test fixtures jasmine.getFixtures().fixturesPath = FIXTURES_PATH; diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index bb76616be56..ba3ba01944d 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -58,9 +58,11 @@ const createComponent = (customConfig = {}) => { describe('ReadyToMerge', () => { let vm; + let updateMrCountSpy; beforeEach(() => { vm = createComponent(); + updateMrCountSpy = spyOnDependency(ReadyToMerge, 'refreshUserMergeRequestCounts'); }); afterEach(() => { @@ -461,6 +463,7 @@ describe('ReadyToMerge', () => { expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested'); expect(eventHub.$emit).toHaveBeenCalledWith('FetchActionsContent'); expect(vm.initiateRemoveSourceBranchPolling).toHaveBeenCalled(); + expect(updateMrCountSpy).toHaveBeenCalled(); expect(cpc).toBeFalsy(); expect(spc).toBeTruthy(); diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb index c788da55cd2..b0a00392957 100644 --- a/spec/lib/api/helpers/pagination_spec.rb +++ b/spec/lib/api/helpers/pagination_spec.rb @@ -114,7 +114,7 @@ describe API::Helpers::Pagination do expect(paginated_relation.order_values).to be_present expect(paginated_relation.order_values.size).to eq(1) expect(paginated_relation.order_values.first).to be_descending - expect(paginated_relation.order_values.first.expr.name).to eq :id + expect(paginated_relation.order_values.first.expr.name).to eq 'id' end end @@ -151,9 +151,9 @@ describe API::Helpers::Pagination do expect(paginated_relation.order_values).to be_present expect(paginated_relation.order_values.size).to eq(2) expect(paginated_relation.order_values.first).to be_descending - expect(paginated_relation.order_values.first.expr.name).to eq :name + expect(paginated_relation.order_values.first.expr.name).to eq 'name' expect(paginated_relation.order_values.second).to be_descending - expect(paginated_relation.order_values.second.expr.name).to eq :id + expect(paginated_relation.order_values.second.expr.name).to eq 'id' end it 'returns the right records (first page)' do @@ -341,7 +341,7 @@ describe API::Helpers::Pagination do 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 + expect(paginated_relation.order_values.first.expr.name).to eq 'id' end it 'is present it does not add anything' do @@ -349,7 +349,7 @@ describe API::Helpers::Pagination do 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 + expect(paginated_relation.order_values.first.expr.name).to eq 'created_at' end end end diff --git a/spec/lib/banzai/filter/inline_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_metrics_filter_spec.rb new file mode 100644 index 00000000000..772c94e3180 --- /dev/null +++ b/spec/lib/banzai/filter/inline_metrics_filter_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Banzai::Filter::InlineMetricsFilter do + include FilterSpecHelper + + let(:input) { %(<a href="#{url}">example</a>) } + let(:doc) { filter(input) } + + context 'when the document has an external link' do + let(:url) { 'https://foo.com' } + + it 'leaves regular non-metrics links unchanged' do + expect(doc.to_s).to eq input + end + end + + context 'when the document has a metrics dashboard link' do + let(:params) { ['foo', 'bar', 12] } + let(:url) { urls.metrics_namespace_project_environment_url(*params) } + + it 'leaves the original link unchanged' do + expect(doc.at_css('a').to_s).to eq input + end + + it 'appends a metrics charts placeholder with dashboard url after metrics links' do + node = doc.at_css('.js-render-metrics') + expect(node).to be_present + + dashboard_url = urls.metrics_dashboard_namespace_project_environment_url(*params, embedded: true) + expect(node.attribute('data-dashboard-url').to_s).to eq dashboard_url + end + + context 'when the metrics dashboard link is part of a paragraph' do + let(:paragraph) { %(This is an <a href="#{url}">example</a> of metrics.) } + let(:input) { %(<p>#{paragraph}</p>) } + + it 'appends the charts placeholder after the enclosing paragraph' do + expect(doc.at_css('p').to_s).to include paragraph + expect(doc.at_css('.js-render-metrics')).to be_present + end + + context 'when the feature is disabled' do + before do + stub_feature_flags(gfm_embedded_metrics: false) + end + + it 'does nothing' do + expect(doc.to_s).to eq input + end + end + end + end +end diff --git a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb new file mode 100644 index 00000000000..fb2186e9d12 --- /dev/null +++ b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Banzai::Filter::InlineMetricsRedactorFilter do + include FilterSpecHelper + + set(:project) { create(:project) } + + let(:url) { urls.metrics_dashboard_project_environment_url(project, 1, embedded: true) } + let(:input) { %(<a href="#{url}">example</a>) } + let(:doc) { filter(input) } + + context 'when the feature is disabled' do + before do + stub_feature_flags(gfm_embedded_metrics: false) + end + + it 'does nothing' do + expect(doc.to_s).to eq input + end + end + + context 'without a metrics charts placeholder' do + it 'leaves regular non-metrics links unchanged' do + expect(doc.to_s).to eq input + end + end + + context 'with a metrics charts placeholder' do + let(:input) { %(<div class="js-render-metrics" data-dashboard-url="#{url}"></div>) } + + context 'no user is logged in' do + it 'redacts the placeholder' do + expect(doc.to_s).to be_empty + end + end + + context 'the user does not have permission do see charts' do + let(:doc) { filter(input, current_user: build(:user)) } + + it 'redacts the placeholder' do + expect(doc.to_s).to be_empty + end + end + + context 'the user has requisite permissions' do + let(:user) { create(:user) } + let(:doc) { filter(input, current_user: user) } + + it 'leaves the placeholder' do + project.add_maintainer(user) + + expect(doc.to_s).to eq input + end + end + end +end diff --git a/spec/lib/banzai/renderer_spec.rb b/spec/lib/banzai/renderer_spec.rb index aa828e2f0e9..a099f7482c1 100644 --- a/spec/lib/banzai/renderer_spec.rb +++ b/spec/lib/banzai/renderer_spec.rb @@ -19,6 +19,24 @@ describe Banzai::Renderer do object end + describe '#cache_collection_render' do + let(:merge_request) { fake_object(fresh: true) } + let(:context) { { cache_key: [merge_request, 'field'], rendered: merge_request.field_html } } + + context 'when an item has a rendered field' do + before do + allow(merge_request).to receive(:field).and_return('This is the field') + allow(merge_request).to receive(:field_html).and_return('This is the field') + end + + it 'does not touch redis if the field is in the cache' do + expect(Rails).not_to receive(:cache) + + described_class.cache_collection_render([{ text: merge_request.field, context: context }]) + end + end + end + describe '#render_field' do let(:renderer) { described_class } diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index 8f2434acd26..ff002acbd35 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -45,28 +45,117 @@ module Gitlab end context "XSS" do - links = { - 'links' => { + items = { + 'link with extra attribute' => { input: 'link:mylink"onmouseover="alert(1)[Click Here]', output: "<div>\n<p><a href=\"mylink\">Click Here</a></p>\n</div>" }, - 'images' => { + 'link with unsafe scheme' => { + input: 'link:data://danger[Click Here]', + output: "<div>\n<p><a>Click Here</a></p>\n</div>" + }, + 'image with onerror' => { input: 'image:https://localhost.com/image.png[Alt text" onerror="alert(7)]', output: "<div>\n<p><span><img src=\"https://localhost.com/image.png\" alt='Alt text\" onerror=\"alert(7)'></span></p>\n</div>" }, - 'pre' => { + 'fenced code with inline script' => { input: '```mypre"><script>alert(3)</script>', output: "<div>\n<div>\n<pre class=\"code highlight js-syntax-highlight plaintext\" lang=\"plaintext\" v-pre=\"true\"><code><span id=\"LC1\" class=\"line\" lang=\"plaintext\">\"></span></code></pre>\n</div>\n</div>" } } - links.each do |name, data| + items.each do |name, data| it "does not convert dangerous #{name} into HTML" do expect(render(data[:input], context)).to include(data[:output]) end end end + context 'with admonition' do + it 'preserves classes' do + input = <<~ADOC + NOTE: An admonition paragraph, like this note, grabs the reader’s attention. + ADOC + + output = <<~HTML + <div class="admonitionblock"> + <table> + <tr> + <td class="icon"> + <i class="fa icon-note" title="Note"></i> + </td> + <td> + An admonition paragraph, like this note, grabs the reader’s attention. + </td> + </tr> + </table> + </div> + HTML + + expect(render(input, context)).to include(output.strip) + end + end + + context 'with checklist' do + it 'preserves classes' do + input = <<~ADOC + * [x] checked + * [ ] not checked + ADOC + + output = <<~HTML + <div> + <ul class="checklist"> + <li> + <p><i class="fa fa-check-square-o"></i> checked</p> + </li> + <li> + <p><i class="fa fa-square-o"></i> not checked</p> + </li> + </ul> + </div> + HTML + + expect(render(input, context)).to include(output.strip) + end + end + + context 'with marks' do + it 'preserves classes' do + input = <<~ADOC + Werewolves are allergic to #cassia cinnamon#. + + Did the werewolves read the [.small]#small print#? + + Where did all the [.underline.small]#cores# run off to? + + We need [.line-through]#ten# make that twenty VMs. + + [.big]##O##nce upon an infinite loop. + ADOC + + output = <<~HTML + <div> + <p>Werewolves are allergic to <mark>cassia cinnamon</mark>.</p> + </div> + <div> + <p>Did the werewolves read the <span class="small">small print</span>?</p> + </div> + <div> + <p>Where did all the <span class="underline small">cores</span> run off to?</p> + </div> + <div> + <p>We need <span class="line-through">ten</span> make that twenty VMs.</p> + </div> + <div> + <p><span class="big">O</span>nce upon an infinite loop.</p> + </div> + HTML + + expect(render(input, context)).to include(output.strip) + end + end + context 'with fenced block' do it 'highlights syntax' do input = <<~ADOC diff --git a/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb b/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb new file mode 100644 index 00000000000..5938ecca459 --- /dev/null +++ b/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::BackgroundMigration::FixUserNamespaceNames, :migration, schema: 20190620112608 do + let(:namespaces) { table(:namespaces) } + let(:users) { table(:users) } + let(:user) { users.create(name: "The user's full name", projects_limit: 10, username: 'not-null', email: '1') } + + context 'updating the namespace names' do + it 'updates a user namespace within range' do + user2 = users.create(name: "Other user's full name", projects_limit: 10, username: 'also-not-null', email: '2') + user_namespace1 = namespaces.create( + id: 2, + owner_id: user.id, + name: "Should be the user's name", + path: user.username + ) + user_namespace2 = namespaces.create( + id: 3, + owner_id: user2.id, + name: "Should also be the user's name", + path: user.username + ) + + described_class.new.perform(1, 5) + + expect(user_namespace1.reload.name).to eq("The user's full name") + expect(user_namespace2.reload.name).to eq("Other user's full name") + end + + it 'does not update namespaces out of range' do + user_namespace = namespaces.create( + id: 6, + owner_id: user.id, + name: "Should be the user's name", + path: user.username + ) + + expect { described_class.new.perform(1, 5) } + .not_to change { user_namespace.reload.name } + end + + it 'does not update groups owned by the users' do + user_group = namespaces.create( + id: 2, + owner_id: user.id, + name: 'A group name', + path: 'the-path', + type: 'Group' + ) + + expect { described_class.new.perform(1, 5) } + .not_to change { user_group.reload.name } + end + end + + context 'namespace route names' do + let(:routes) { table(:routes) } + let(:namespace) do + namespaces.create( + id: 2, + owner_id: user.id, + name: "Will be updated to the user's name", + path: user.username + ) + end + + it "updates the route name if it didn't match the namespace" do + route = routes.create(path: namespace.path, name: 'Incorrect name', source_type: 'Namespace', source_id: namespace.id) + + described_class.new.perform(1, 5) + + expect(route.reload.name).to eq("The user's full name") + end + + it 'updates the route name if it was nil match the namespace' do + route = routes.create(path: namespace.path, name: nil, source_type: 'Namespace', source_id: namespace.id) + + described_class.new.perform(1, 5) + + expect(route.reload.name).to eq("The user's full name") + end + + it "doesn't update group routes" do + route = routes.create(path: 'group-path', name: 'Group name', source_type: 'Group', source_id: namespace.id) + + expect { described_class.new.perform(1, 5) } + .not_to change { route.reload.name } + end + + it "doesn't touch routes for namespaces out of range" do + user_namespace = namespaces.create( + id: 6, + owner_id: user.id, + name: "Should be the user's name", + path: user.username + ) + + expect { described_class.new.perform(1, 5) } + .not_to change { user_namespace.reload.name } + end + end +end diff --git a/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb b/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb new file mode 100644 index 00000000000..d1d6d8411d1 --- /dev/null +++ b/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::BackgroundMigration::FixUserProjectRouteNames, :migration, schema: 20190620112608 do + let(:namespaces) { table(:namespaces) } + let(:users) { table(:users) } + let(:routes) { table(:routes) } + let(:projects) { table(:projects) } + + let(:user) { users.create(name: "The user's full name", projects_limit: 10, username: 'not-null', email: '1') } + + let(:namespace) do + namespaces.create( + owner_id: user.id, + name: "Should eventually be the user's name", + path: user.username + ) + end + + let(:project) do + projects.create(namespace_id: namespace.id, name: 'Project Name') + end + + it "updates the route for a project if it did not match the user's name" do + route = routes.create( + id: 1, + path: "#{user.username}/#{project.path}", + source_id: project.id, + source_type: 'Project', + name: 'Completely wrong' + ) + + described_class.new.perform(1, 5) + + expect(route.reload.name).to eq("The user's full name / Project Name") + end + + it 'updates the route for a project if the name was nil' do + route = routes.create( + id: 1, + path: "#{user.username}/#{project.path}", + source_id: project.id, + source_type: 'Project', + name: nil + ) + + described_class.new.perform(1, 5) + + expect(route.reload.name).to eq("The user's full name / Project Name") + end + + it 'does not update routes that were are out of the range' do + route = routes.create( + id: 6, + path: "#{user.username}/#{project.path}", + source_id: project.id, + source_type: 'Project', + name: 'Completely wrong' + ) + + expect { described_class.new.perform(1, 5) } + .not_to change { route.reload.name } + end + + it 'does not update routes for projects in groups owned by the user' do + group = namespaces.create( + owner_id: user.id, + name: 'A group', + path: 'a-path', + type: '' + ) + project = projects.create(namespace_id: group.id, name: 'Project Name') + route = routes.create( + id: 1, + path: "#{group.path}/#{project.path}", + source_id: project.id, + source_type: 'Project', + name: 'Completely wrong' + ) + + expect { described_class.new.perform(1, 5) } + .not_to change { route.reload.name } + end + + it 'does not update routes for namespaces' do + route = routes.create( + id: 1, + path: namespace.path, + source_id: namespace.id, + source_type: 'Namespace', + name: 'Completely wrong' + ) + + expect { described_class.new.perform(1, 5) } + .not_to change { route.reload.name } + end +end diff --git a/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb b/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb index 4a81a37d341..ad4fa4fe03a 100644 --- a/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb +++ b/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb @@ -27,14 +27,19 @@ describe Gitlab::BackgroundMigration::PopulateMergeRequestAssigneesTable, :migra merge_requests.create(params) end + before do + create_merge_request(2, assignee_id: user.id) + create_merge_request(3, assignee_id: user_2.id) + create_merge_request(4, assignee_id: user_3.id) + + # Test filtering MRs without assignees + create_merge_request(5, assignee_id: nil) + # Test filtering already migrated row + merge_request_assignees.create!(merge_request_id: 2, user_id: user_3.id) + end + describe '#perform' do it 'creates merge_request_assignees rows according to merge_requests' do - create_merge_request(2, assignee_id: user.id) - create_merge_request(3, assignee_id: user_2.id) - create_merge_request(4, assignee_id: user_3.id) - # Test filtering already migrated row - merge_request_assignees.create!(merge_request_id: 2, user_id: user_3.id) - subject.perform(1, 4) rows = merge_request_assignees.order(:id).map { |row| row.attributes.slice('merge_request_id', 'user_id') } @@ -53,4 +58,13 @@ describe Gitlab::BackgroundMigration::PopulateMergeRequestAssigneesTable, :migra end end end + + describe '#perform_all_sync' do + it 'executes peform for all merge requests in batches' do + expect(subject).to receive(:perform).with(2, 4).ordered + expect(subject).to receive(:perform).with(5, 5).ordered + + subject.perform_all_sync(batch_size: 3) + end + end end diff --git a/spec/lib/gitlab/batch_pop_queueing_spec.rb b/spec/lib/gitlab/batch_pop_queueing_spec.rb new file mode 100644 index 00000000000..28984d52024 --- /dev/null +++ b/spec/lib/gitlab/batch_pop_queueing_spec.rb @@ -0,0 +1,147 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::BatchPopQueueing do + include ExclusiveLeaseHelpers + using RSpec::Parameterized::TableSyntax + + describe '#initialize' do + where(:namespace, :queue_id, :expect_error, :error_type) do + 'feature' | '1' | false | nil + :feature | '1' | false | nil + nil | '1' | true | NoMethodError + 'feature' | nil | true | NoMethodError + '' | '1' | true | ArgumentError + 'feature' | '' | true | ArgumentError + 'feature' | 1 | true | NoMethodError + end + + with_them do + it do + if expect_error + expect { described_class.new(namespace, queue_id) }.to raise_error(error_type) + else + expect { described_class.new(namespace, queue_id) }.not_to raise_error + end + end + end + end + + describe '#safe_execute', :clean_gitlab_redis_queues do + subject { queue.safe_execute(new_items, lock_timeout: lock_timeout) } + + let(:queue) { described_class.new(namespace, queue_id) } + let(:namespace) { 'feature' } + let(:queue_id) { '1' } + let(:lock_timeout) { 10.minutes } + let(:new_items) { %w[A B] } + let(:lock_key) { queue.send(:lock_key) } + let(:queue_key) { queue.send(:queue_key) } + + it 'enqueues new items always' do + Gitlab::Redis::Queues.with do |redis| + expect(redis).to receive(:sadd).with(queue_key, new_items) + expect(redis).to receive(:expire).with(queue_key, (lock_timeout + described_class::EXTRA_QUEUE_EXPIRE_WINDOW).to_i) + end + + subject + end + + it 'yields the new items with exclusive lease' do + uuid = 'test' + expect_to_obtain_exclusive_lease(lock_key, uuid, timeout: lock_timeout) + expect_to_cancel_exclusive_lease(lock_key, uuid) + + expect { |b| queue.safe_execute(new_items, lock_timeout: lock_timeout, &b) } + .to yield_with_args(match_array(new_items)) + end + + it 'returns the result and no items in the queue' do + expect(subject[:status]).to eq(:finished) + expect(subject[:new_items]).to be_empty + + Gitlab::Redis::Queues.with do |redis| + expect(redis.llen(queue_key)).to be(0) + end + end + + context 'when new items are enqueued during the process' do + it 'returns the result with newly added items' do + result = queue.safe_execute(new_items) do + queue.safe_execute(['C']) + end + + expect(result[:status]).to eq(:finished) + expect(result[:new_items]).to eq(['C']) + + Gitlab::Redis::Queues.with do |redis| + expect(redis.scard(queue_key)).to be(1) + end + end + end + + context 'when interger items are enqueued' do + let(:new_items) { [1, 2, 3] } + + it 'yields as String values' do + expect { |b| queue.safe_execute(new_items, lock_timeout: lock_timeout, &b) } + .to yield_with_args(%w[1 2 3]) + end + end + + context 'when the queue key does not exist in Redis' do + before do + allow(queue).to receive(:enqueue) { } + end + + it 'yields empty array' do + expect { |b| queue.safe_execute(new_items, lock_timeout: lock_timeout, &b) } + .to yield_with_args([]) + end + end + + context 'when the other process has already been working on the queue' do + before do + stub_exclusive_lease_taken(lock_key, timeout: lock_timeout) + end + + it 'does not yield the block' do + expect { |b| queue.safe_execute(new_items, lock_timeout: lock_timeout, &b) } + .not_to yield_control + end + + it 'returns the result' do + expect(subject[:status]).to eq(:enqueued) + end + end + + context 'when a duplicate item is enqueued' do + it 'returns the poped items to the queue and raise an error' do + expect { |b| queue.safe_execute(%w[1 1 2 2], &b) } + .to yield_with_args(match_array(%w[1 2])) + end + end + + context 'when there are two queues' do + it 'enqueues items to each queue' do + queue_1 = described_class.new(namespace, '1') + queue_2 = described_class.new(namespace, '2') + + result_2 = nil + + result_1 = queue_1.safe_execute(['A']) do |_| + result_2 = queue_2.safe_execute(['B']) do |_| + queue_1.safe_execute(['C']) + queue_2.safe_execute(['D']) + end + end + + expect(result_1[:status]).to eq(:finished) + expect(result_1[:new_items]).to eq(['C']) + expect(result_2[:status]).to eq(:finished) + expect(result_2[:new_items]).to eq(['D']) + end + end + end +end diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb index 483c5ea9cff..972dd7e0d2b 100644 --- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb +++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb @@ -26,6 +26,7 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do end it 'loads 10 projects without hitting Gitaly call limit', :request_store do + allow(Gitlab::GitalyClient).to receive(:can_access_disk?).and_return(false) projects = Gitlab::GitalyClient.allow_n_plus_1_calls do (1..10).map { create(:project, :repository) } end diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb index 909dbffa38f..db31e5280a3 100644 --- a/spec/lib/gitlab/current_settings_spec.rb +++ b/spec/lib/gitlab/current_settings_spec.rb @@ -80,7 +80,7 @@ describe Gitlab::CurrentSettings do # during the initialization phase of the test suite, so instead let's mock the internals of it expect(ActiveRecord::Base.connection).not_to receive(:active?) expect(ActiveRecord::Base.connection).not_to receive(:cached_table_exists?) - expect(ActiveRecord::Migrator).not_to receive(:needs_migration?) + expect_any_instance_of(ActiveRecord::MigrationContext).not_to receive(:needs_migration?) expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0) end end @@ -109,7 +109,7 @@ describe Gitlab::CurrentSettings do context 'with pending migrations' do before do - expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true) + expect_any_instance_of(ActiveRecord::MigrationContext).to receive(:needs_migration?).and_return(true) end shared_examples 'a non-persisted ApplicationSetting object' do diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index f8b103c0fab..5ee02650e49 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -7,7 +7,7 @@ describe 'cycle analytics events' do let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } let(:events) do - CycleAnalytics.new(project, { from: from_date, current_user: user })[stage].events + CycleAnalytics::ProjectLevel.new(project, options: { from: from_date, current_user: user })[stage].events end before do diff --git a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb index eacde22cd56..8633a63849f 100644 --- a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb @@ -3,6 +3,40 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::TestStage do let(:stage_name) { :test } + let(:project) { create(:project) } + let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } it_behaves_like 'base stage' + + describe '#median' do + before do + issue_1 = create(:issue, project: project, created_at: 90.minutes.ago) + issue_2 = create(:issue, project: project, created_at: 60.minutes.ago) + issue_3 = create(:issue, project: project, created_at: 60.minutes.ago) + mr_1 = create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) + mr_2 = create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') + mr_3 = create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') + mr_4 = create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'C') + mr_5 = create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'D') + mr_1.metrics.update!(latest_build_started_at: 32.minutes.ago, latest_build_finished_at: 2.minutes.ago) + mr_2.metrics.update!(latest_build_started_at: 62.minutes.ago, latest_build_finished_at: 32.minutes.ago) + mr_3.metrics.update!(latest_build_started_at: nil, latest_build_finished_at: nil) + mr_4.metrics.update!(latest_build_started_at: nil, latest_build_finished_at: nil) + mr_5.metrics.update!(latest_build_started_at: nil, latest_build_finished_at: nil) + + create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1) + create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2) + create(:merge_requests_closing_issues, merge_request: mr_3, issue: issue_3) + create(:merge_requests_closing_issues, merge_request: mr_4, issue: issue_3) + create(:merge_requests_closing_issues, merge_request: mr_5, issue: issue_3) + end + + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.median).to eq(ISSUES_MEDIAN) + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb index a785b17f682..8122e85a981 100644 --- a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb @@ -34,7 +34,7 @@ describe Gitlab::CycleAnalytics::UsageData do expect(result).to have_key(:avg_cycle_analytics) - CycleAnalytics::STAGES.each do |stage| + CycleAnalytics::Base::STAGES.each do |stage| expect(result[:avg_cycle_analytics]).to have_key(stage) stage_values = result[:avg_cycle_analytics][stage] diff --git a/spec/db/importers/common_metrics_importer_spec.rb b/spec/lib/gitlab/database_importers/common_metrics/importer_spec.rb index a717c8cd04d..57c8bafd488 100644 --- a/spec/db/importers/common_metrics_importer_spec.rb +++ b/spec/lib/gitlab/database_importers/common_metrics/importer_spec.rb @@ -1,25 +1,8 @@ # frozen_string_literal: true require 'rails_helper' -require Rails.root.join("db", "importers", "common_metrics_importer.rb") -describe Importers::PrometheusMetric do - let(:existing_group_titles) do - ::PrometheusMetric::GROUP_DETAILS.each_with_object({}) do |(key, value), memo| - memo[key] = value[:group_title] - end - end - - it 'group enum equals ::PrometheusMetric' do - expect(described_class.groups).to eq(::PrometheusMetric.groups) - end - - it 'GROUP_TITLES equals ::PrometheusMetric' do - expect(described_class::GROUP_TITLES).to eq(existing_group_titles) - end -end - -describe Importers::CommonMetricsImporter do +describe Gitlab::DatabaseImporters::CommonMetrics::Importer do subject { described_class.new } context "does import common_metrics.yml" do @@ -104,7 +87,7 @@ describe Importers::CommonMetricsImporter do let(:query_identifier) { } it 'raises exception' do - expect { subject.execute }.to raise_error(described_class::MissingQueryId) + expect { subject.execute }.to raise_error(Gitlab::DatabaseImporters::CommonMetrics::Importer::MissingQueryId) end end diff --git a/spec/lib/gitlab/database_importers/common_metrics/prometheus_metric_spec.rb b/spec/lib/gitlab/database_importers/common_metrics/prometheus_metric_spec.rb new file mode 100644 index 00000000000..94f544e59b3 --- /dev/null +++ b/spec/lib/gitlab/database_importers/common_metrics/prometheus_metric_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Gitlab::DatabaseImporters::CommonMetrics::PrometheusMetric do + it 'group enum equals ::PrometheusMetric' do + expect(described_class.groups).to eq(::PrometheusMetric.groups) + end + + it '.group_titles equals ::PrometheusMetric' do + existing_group_titles = ::PrometheusMetricEnums.group_details.each_with_object({}) do |(key, value), memo| + memo[key] = value[:group_title] + end + expect(Gitlab::DatabaseImporters::CommonMetrics::PrometheusMetricEnums.group_titles).to eq(existing_group_titles) + end +end diff --git a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb index 48139c2f9dc..7833b9f387d 100644 --- a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb @@ -105,6 +105,7 @@ describe Gitlab::Email::Handler::CreateIssueHandler do context "when the issue could not be saved" do before do allow_any_instance_of(Issue).to receive(:persisted?).and_return(false) + allow_any_instance_of(Issue).to receive(:ensure_metrics).and_return(nil) end it "raises an InvalidIssueError" do diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 25052a79916..3f0e6b34291 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -408,7 +408,7 @@ describe Gitlab::Git::Commit, :seed_helper do context 'when oids is empty' do it 'makes no Gitaly request' do - expect(Gitlab::GitalyClient).not_to receive(:call) + expect(Gitlab::GitalyClient).not_to receive(:call).with(repository.storage, :commit_service, :list_commits_by_oid) described_class.batch_by_oid(repository, []) end diff --git a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb new file mode 100644 index 00000000000..e7ef9d08f80 --- /dev/null +++ b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'json' +require 'tempfile' + +describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + let(:feature_flag_name) { 'feature-flag-name' } + let(:feature_flag) { Feature.get(feature_flag_name) } + let(:temp_gitaly_metadata_file) { create_temporary_gitaly_metadata_file } + + before(:all) do + create_gitaly_metadata_file + end + + subject(:wrapper) do + klazz = Class.new { include Gitlab::Git::RuggedImpl::UseRugged } + klazz.new + end + + before do + Gitlab::GitalyClient.instance_variable_set(:@can_use_disk, {}) + end + + context 'when feature flag is not persisted' do + before do + allow(Feature).to receive(:persisted?).with(feature_flag).and_return(false) + end + + it 'returns true when gitaly matches disk' do + pending('temporary disabled because of https://gitlab.com/gitlab-org/gitlab-ce/issues/64338') + expect(subject.use_rugged?(repository, feature_flag_name)).to be true + end + + it 'returns false when disk access fails' do + allow(Gitlab::GitalyClient).to receive(:storage_metadata_file_path).and_return("/fake/path/doesnt/exist") + + expect(subject.use_rugged?(repository, feature_flag_name)).to be false + end + + it "returns false when gitaly doesn't match disk" do + allow(Gitlab::GitalyClient).to receive(:storage_metadata_file_path).and_return(temp_gitaly_metadata_file) + + expect(subject.use_rugged?(repository, feature_flag_name)).to be_falsey + + File.delete(temp_gitaly_metadata_file) + end + + it "doesn't lead to a second rpc call because gitaly client should use the cached value" do + pending('temporary disabled because of https://gitlab.com/gitlab-org/gitlab-ce/issues/64338') + expect(subject.use_rugged?(repository, feature_flag_name)).to be true + + expect(Gitlab::GitalyClient).not_to receive(:filesystem_id) + + subject.use_rugged?(repository, feature_flag_name) + end + end + + context 'when feature flag is persisted' do + before do + allow(Feature).to receive(:persisted?).with(feature_flag).and_return(true) + end + + it 'returns false when the feature flag is off' do + allow(feature_flag).to receive(:enabled?).and_return(false) + + expect(subject.use_rugged?(repository, feature_flag_name)).to be_falsey + end + + it "returns true when feature flag is on" do + allow(feature_flag).to receive(:enabled?).and_return(true) + allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(false) + + expect(subject.use_rugged?(repository, feature_flag_name)).to be true + end + end + + def create_temporary_gitaly_metadata_file + tmp = Tempfile.new('.gitaly-metadata') + gitaly_metadata = { + "gitaly_filesystem_id" => "some-value" + } + tmp.write(gitaly_metadata.to_json) + tmp.flush + tmp.close + tmp.path + end + + def create_gitaly_metadata_file + File.open(File.join(SEED_STORAGE_PATH, '.gitaly-metadata'), 'w+') do |f| + gitaly_metadata = { + "gitaly_filesystem_id" => SecureRandom.uuid + } + f.write(gitaly_metadata.to_json) + end + end +end diff --git a/spec/lib/gitlab/global_id_spec.rb b/spec/lib/gitlab/global_id_spec.rb new file mode 100644 index 00000000000..d35b5da0b75 --- /dev/null +++ b/spec/lib/gitlab/global_id_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::GlobalId do + describe '.build' do + set(:object) { create(:issue) } + + it 'returns a standard GlobalId if only object is passed' do + expect(described_class.build(object).to_s).to eq(object.to_global_id.to_s) + end + + it 'returns a GlobalId from params' do + expect(described_class.build(model_name: 'MyModel', id: 'myid').to_s).to eq( + 'gid://gitlab/MyModel/myid' + ) + end + + it 'returns a GlobalId from object and `id` param' do + expect(described_class.build(object, id: 'myid').to_s).to eq( + 'gid://gitlab/Issue/myid' + ) + end + + it 'returns a GlobalId from object and `model_name` param' do + expect(described_class.build(object, model_name: 'MyModel').to_s).to eq( + "gid://gitlab/MyModel/#{object.id}" + ) + end + + it 'returns an error if model_name and id are not able to be determined' do + expect { described_class.build(id: 'myid') }.to raise_error(URI::InvalidComponentError) + expect { described_class.build(model_name: 'MyModel') }.to raise_error(URI::InvalidComponentError) + expect { described_class.build }.to raise_error(URI::InvalidComponentError) + end + end +end diff --git a/spec/lib/gitlab/kubernetes/kube_client_spec.rb b/spec/lib/gitlab/kubernetes/kube_client_spec.rb index 978e64c4407..97ebb5f1554 100644 --- a/spec/lib/gitlab/kubernetes/kube_client_spec.rb +++ b/spec/lib/gitlab/kubernetes/kube_client_spec.rb @@ -176,6 +176,9 @@ describe Gitlab::Kubernetes::KubeClient do let(:rbac_client) { client.rbac_client } [ + :create_role, + :get_role, + :update_role, :create_cluster_role_binding, :get_cluster_role_binding, :update_cluster_role_binding diff --git a/spec/lib/gitlab/kubernetes/role_binding_spec.rb b/spec/lib/gitlab/kubernetes/role_binding_spec.rb index 50acee254cb..4c200eb545f 100644 --- a/spec/lib/gitlab/kubernetes/role_binding_spec.rb +++ b/spec/lib/gitlab/kubernetes/role_binding_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' describe Gitlab::Kubernetes::RoleBinding, '#generate' do let(:role_name) { 'edit' } + let(:role_kind) { 'ClusterRole' } let(:namespace) { 'my-namespace' } let(:service_account_name) { 'my-service-account' } @@ -20,7 +21,7 @@ describe Gitlab::Kubernetes::RoleBinding, '#generate' do let(:role_ref) do { apiGroup: 'rbac.authorization.k8s.io', - kind: 'ClusterRole', + kind: role_kind, name: role_name } end @@ -37,6 +38,7 @@ describe Gitlab::Kubernetes::RoleBinding, '#generate' do described_class.new( name: "gitlab-#{namespace}", role_name: role_name, + role_kind: role_kind, namespace: namespace, service_account_name: service_account_name ).generate diff --git a/spec/lib/gitlab/kubernetes/role_spec.rb b/spec/lib/gitlab/kubernetes/role_spec.rb new file mode 100644 index 00000000000..3a5cd3b6704 --- /dev/null +++ b/spec/lib/gitlab/kubernetes/role_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Kubernetes::Role do + let(:role) { described_class.new(name: name, namespace: namespace, rules: rules) } + let(:name) { 'example-name' } + let(:namespace) { 'example-namespace' } + + let(:rules) do + [{ + apiGroups: %w(hello.world), + resources: %w(oil diamonds coffee), + verbs: %w(say do walk run) + }] + end + + describe '#generate' do + subject { role.generate } + + let(:resource) do + ::Kubeclient::Resource.new( + metadata: { name: name, namespace: namespace }, + rules: rules + ) + end + + it { is_expected.to eq(resource) } + end +end diff --git a/spec/lib/gitlab/lets_encrypt_spec.rb b/spec/lib/gitlab/lets_encrypt_spec.rb index 674b114e9d3..65aea0937f1 100644 --- a/spec/lib/gitlab/lets_encrypt_spec.rb +++ b/spec/lib/gitlab/lets_encrypt_spec.rb @@ -10,21 +10,10 @@ describe ::Gitlab::LetsEncrypt do end describe '.enabled?' do - let(:project) { create(:project) } - let(:pages_domain) { create(:pages_domain, project: project) } - - subject { described_class.enabled?(pages_domain) } + subject { described_class.enabled? } context 'when terms of service are accepted' do it { is_expected.to eq(true) } - - context 'when feature flag is disabled' do - before do - stub_feature_flags(pages_auto_ssl: false) - end - - it { is_expected.to eq(false) } - end end context 'when terms of service are not accepted' do @@ -34,23 +23,5 @@ describe ::Gitlab::LetsEncrypt do it { is_expected.to eq(false) } end - - context 'when feature flag for project is disabled' do - before do - stub_feature_flags(pages_auto_ssl_for_project: false) - end - - it 'returns false' do - is_expected.to eq(false) - end - end - - context 'when domain has not project' do - let(:pages_domain) { create(:pages_domain) } - - it 'returns false' do - is_expected.to eq(false) - end - end end end diff --git a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb index 18052b1991c..c5fc74afea5 100644 --- a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb +++ b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb @@ -9,12 +9,13 @@ describe Gitlab::MarkdownCache::ActiveRecord::Extension do cache_markdown_field :title, whitelisted: true cache_markdown_field :description, pipeline: :single_line - attr_accessor :author, :project + attribute :author + attribute :project end end let(:cache_version) { Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16 } - let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) } + let(:thing) { klass.create(title: markdown, title_html: html, cached_markdown_version: cache_version) } let(:markdown) { '`Foo`' } let(:html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Foo</code></p>' } @@ -37,7 +38,7 @@ describe Gitlab::MarkdownCache::ActiveRecord::Extension do end context 'a changed markdown field' do - let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) } + let(:thing) { klass.create(title: markdown, title_html: html, cached_markdown_version: cache_version) } before do thing.title = updated_markdown diff --git a/spec/lib/gitlab/metrics/dashboard/url_spec.rb b/spec/lib/gitlab/metrics/dashboard/url_spec.rb new file mode 100644 index 00000000000..34bc6359414 --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/url_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Metrics::Dashboard::Url do + describe '#regex' do + it 'returns a regular expression' do + expect(described_class.regex).to be_a Regexp + end + + it 'matches a metrics dashboard link with named params' do + url = Gitlab::Routing.url_helpers.metrics_namespace_project_environment_url('foo', 'bar', 1, start: 123345456, anchor: 'title') + + expected_params = { + 'url' => url, + 'namespace' => 'foo', + 'project' => 'bar', + 'environment' => '1', + 'query' => '?start=123345456', + 'anchor' => '#title' + } + + expect(described_class.regex).to match url + + described_class.regex.match(url) do |m| + expect(m.named_captures).to eq expected_params + end + end + + it 'does not match other gitlab urls that contain the term metrics' do + url = Gitlab::Routing.url_helpers.active_common_namespace_project_prometheus_metrics_url('foo', 'bar', :json) + + expect(described_class.regex).not_to match url + end + + it 'does not match other gitlab urls' do + url = Gitlab.config.gitlab.url + + expect(described_class.regex).not_to match url + end + + it 'does not match non-gitlab urls' do + url = 'https://www.super_awesome_site.com/' + + expect(described_class.regex).not_to match url + end + end + + describe '#build_dashboard_url' do + it 'builds the url for the dashboard endpoint' do + url = described_class.build_dashboard_url('foo', 'bar', 1) + + expect(url).to match described_class.regex + end + end +end diff --git a/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb index 090e456644f..4b697b2ba0f 100644 --- a/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb +++ b/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do subject { described_class.new(1.second) } describe '#sample' do - let(:unicorn) { double('unicorn') } + let(:unicorn) { Module.new } let(:raindrops) { double('raindrops') } let(:stats) { double('stats') } @@ -78,19 +78,32 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do end end - context 'additional metrics' do - let(:unicorn_workers) { 2 } - + context 'unicorn workers' do before do - allow(unicorn).to receive(:listener_names).and_return([""]) - allow(::Gitlab::Metrics::System).to receive(:cpu_time).and_return(3.14) - allow(subject).to receive(:unicorn_workers_count).and_return(unicorn_workers) + allow(unicorn).to receive(:listener_names).and_return([]) end - it "sets additional metrics" do - expect(subject.metrics[:unicorn_workers]).to receive(:set).with({}, unicorn_workers) + context 'without http server' do + it "does set unicorn_workers to 0" do + expect(subject.metrics[:unicorn_workers]).to receive(:set).with({}, 0) - subject.sample + subject.sample + end + end + + context 'with http server' do + let(:http_server_class) { Struct.new(:worker_processes) } + let!(:http_server) { http_server_class.new(5) } + + before do + stub_const('Unicorn::HttpServer', http_server_class) + end + + it "sets additional metrics" do + expect(subject.metrics[:unicorn_workers]).to receive(:set).with({}, 5) + + subject.sample + end end end end diff --git a/spec/lib/gitlab/phabricator_import/cache/map_spec.rb b/spec/lib/gitlab/phabricator_import/cache/map_spec.rb index 52c7a02219f..b6629fad453 100644 --- a/spec/lib/gitlab/phabricator_import/cache/map_spec.rb +++ b/spec/lib/gitlab/phabricator_import/cache/map_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::PhabricatorImport::Cache::Map, :clean_gitlab_redis_cache do @@ -28,6 +30,21 @@ describe Gitlab::PhabricatorImport::Cache::Map, :clean_gitlab_redis_cache do expect(ttl).to be > 10.seconds end + + it 'sets the object in redis once if a block was given and nothing was cached' do + issue = create(:issue, project: project) + + expect(map.get_gitlab_model('does not exist') { issue }).to eq(issue) + + expect { |b| map.get_gitlab_model('does not exist', &b) } + .not_to yield_control + end + + it 'does not cache `nil` objects' do + expect(map).not_to receive(:set_gitlab_model) + + map.get_gitlab_model('does not exist') { nil } + end end describe '#set_gitlab_model' do diff --git a/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb b/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb new file mode 100644 index 00000000000..e88eec2c393 --- /dev/null +++ b/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe Gitlab::PhabricatorImport::Conduit::User do + let(:user_client) do + described_class.new(phabricator_url: 'https://see-ya-later.phabricator', api_token: 'api-token') + end + + describe '#users' do + let(:fake_client) { double('Phabricator client') } + + before do + allow(user_client).to receive(:client).and_return(fake_client) + end + + it 'calls the api with the correct params' do + expected_params = { + constraints: { phids: ['phid-1', 'phid-2'] } + } + + expect(fake_client).to receive(:get).with('user.search', + params: expected_params) + + user_client.users(['phid-1', 'phid-2']) + end + + it 'returns an array of parsed responses' do + response = Gitlab::PhabricatorImport::Conduit::Response + .new(fixture_file('phabricator_responses/user.search.json')) + + allow(fake_client).to receive(:get).and_return(response) + + expect(user_client.users(%w[some phids])).to match_array([an_instance_of(Gitlab::PhabricatorImport::Conduit::UsersResponse)]) + end + + it 'performs multiple requests if more phids than the maximum page size are passed' do + stub_const('Gitlab::PhabricatorImport::Conduit::User::MAX_PAGE_SIZE', 1) + first_params = { constraints: { phids: ['phid-1'] } } + second_params = { constraints: { phids: ['phid-2'] } } + + expect(fake_client).to receive(:get).with('user.search', + params: first_params).once + expect(fake_client).to receive(:get).with('user.search', + params: second_params).once + + user_client.users(['phid-1', 'phid-2']) + end + end +end diff --git a/spec/lib/gitlab/phabricator_import/conduit/users_response_spec.rb b/spec/lib/gitlab/phabricator_import/conduit/users_response_spec.rb new file mode 100644 index 00000000000..00778ad90fd --- /dev/null +++ b/spec/lib/gitlab/phabricator_import/conduit/users_response_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe Gitlab::PhabricatorImport::Conduit::UsersResponse do + let(:conduit_response) do + Gitlab::PhabricatorImport::Conduit::Response + .new(JSON.parse(fixture_file('phabricator_responses/user.search.json'))) + end + + subject(:response) { described_class.new(conduit_response) } + + describe '#users' do + it 'builds the correct users representation' do + tasks = response.users + + usernames = tasks.map(&:username) + + expect(usernames).to contain_exactly('jane', 'john') + end + end +end diff --git a/spec/lib/gitlab/phabricator_import/issues/importer_spec.rb b/spec/lib/gitlab/phabricator_import/issues/importer_spec.rb index 2412cf76f79..667321409da 100644 --- a/spec/lib/gitlab/phabricator_import/issues/importer_spec.rb +++ b/spec/lib/gitlab/phabricator_import/issues/importer_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::PhabricatorImport::Issues::Importer do - set(:project) { create(:project) } + let(:project) { create(:project) } let(:response) do Gitlab::PhabricatorImport::Conduit::TasksResponse.new( @@ -15,7 +15,6 @@ describe Gitlab::PhabricatorImport::Issues::Importer do before do client = instance_double(Gitlab::PhabricatorImport::Conduit::Maniphest) - allow(client).to receive(:tasks).and_return(response) allow(importer).to receive(:client).and_return(client) end @@ -34,20 +33,29 @@ describe Gitlab::PhabricatorImport::Issues::Importer do importer.execute end - it 'schedules the next batch if there is one' do - expect(Gitlab::PhabricatorImport::ImportTasksWorker) - .to receive(:schedule).with(project.id, response.pagination.next_page) + context 'stubbed task import' do + before do + # Stub out the actual importing so we don't perform aditional requests + expect_next_instance_of(Gitlab::PhabricatorImport::Issues::TaskImporter) do |task_importer| + allow(task_importer).to receive(:execute) + end.at_least(1) + end - importer.execute - end + it 'schedules the next batch if there is one' do + expect(Gitlab::PhabricatorImport::ImportTasksWorker) + .to receive(:schedule).with(project.id, response.pagination.next_page) - it 'does not reschedule when there is no next page' do - allow(response.pagination).to receive(:has_next_page?).and_return(false) + importer.execute + end - expect(Gitlab::PhabricatorImport::ImportTasksWorker) - .not_to receive(:schedule) + it 'does not reschedule when there is no next page' do + allow(response.pagination).to receive(:has_next_page?).and_return(false) - importer.execute + expect(Gitlab::PhabricatorImport::ImportTasksWorker) + .not_to receive(:schedule) + + importer.execute + end end end end diff --git a/spec/lib/gitlab/phabricator_import/issues/task_importer_spec.rb b/spec/lib/gitlab/phabricator_import/issues/task_importer_spec.rb index 1625604e754..06ed264e781 100644 --- a/spec/lib/gitlab/phabricator_import/issues/task_importer_spec.rb +++ b/spec/lib/gitlab/phabricator_import/issues/task_importer_spec.rb @@ -12,6 +12,8 @@ describe Gitlab::PhabricatorImport::Issues::TaskImporter do 'description' => { 'raw' => '# This is markdown\n it can contain more text.' }, + 'authorPHID' => 'PHID-USER-456', + 'ownerPHID' => 'PHID-USER-123', 'dateCreated' => '1518688921', 'dateClosed' => '1518789995' } @@ -19,9 +21,18 @@ describe Gitlab::PhabricatorImport::Issues::TaskImporter do ) end + subject(:importer) { described_class.new(project, task) } + describe '#execute' do + let(:fake_user_finder) { instance_double(Gitlab::PhabricatorImport::UserFinder) } + + before do + allow(fake_user_finder).to receive(:find) + allow(importer).to receive(:user_finder).and_return(fake_user_finder) + end + it 'creates the issue with the expected attributes' do - issue = described_class.new(project, task).execute + issue = importer.execute expect(issue.project).to eq(project) expect(issue).to be_persisted @@ -34,21 +45,38 @@ describe Gitlab::PhabricatorImport::Issues::TaskImporter do end it 'does not recreate the issue when called multiple times' do - expect { described_class.new(project, task).execute } + expect { importer.execute } .to change { project.issues.reload.size }.from(0).to(1) - expect { described_class.new(project, task).execute } + expect { importer.execute } .not_to change { project.issues.reload.size } end it 'does not trigger a save when the object did not change' do existing_issue = create(:issue, task.issue_attributes.merge(author: User.ghost)) - importer = described_class.new(project, task) allow(importer).to receive(:issue).and_return(existing_issue) expect(existing_issue).not_to receive(:save!) importer.execute end + + it 'links the author if the author can be found' do + author = create(:user) + expect(fake_user_finder).to receive(:find).with('PHID-USER-456').and_return(author) + + issue = importer.execute + + expect(issue.author).to eq(author) + end + + it 'links an assignee if the user can be found' do + assignee = create(:user) + expect(fake_user_finder).to receive(:find).with('PHID-USER-123').and_return(assignee) + + issue = importer.execute + + expect(issue.assignees).to include(assignee) + end end end diff --git a/spec/lib/gitlab/phabricator_import/representation/task_spec.rb b/spec/lib/gitlab/phabricator_import/representation/task_spec.rb index dfbd8c546eb..5603a6961d6 100644 --- a/spec/lib/gitlab/phabricator_import/representation/task_spec.rb +++ b/spec/lib/gitlab/phabricator_import/representation/task_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::PhabricatorImport::Representation::Task do @@ -7,6 +9,8 @@ describe Gitlab::PhabricatorImport::Representation::Task do 'phid' => 'the-phid', 'fields' => { 'name' => 'Title'.ljust(257, '.'), # A string padded to 257 chars + 'authorPHID' => 'a phid', + 'ownerPHID' => 'another user phid', 'description' => { 'raw' => '# This is markdown\n it can contain more text.' }, @@ -30,4 +34,16 @@ describe Gitlab::PhabricatorImport::Representation::Task do expect(task.issue_attributes).to eq(expected_attributes) end end + + describe '#author_phid' do + it 'returns the correct field' do + expect(task.author_phid).to eq('a phid') + end + end + + describe '#owner_phid' do + it 'returns the correct field' do + expect(task.owner_phid).to eq('another user phid') + end + end end diff --git a/spec/lib/gitlab/phabricator_import/representation/user_spec.rb b/spec/lib/gitlab/phabricator_import/representation/user_spec.rb new file mode 100644 index 00000000000..f52467a0cf1 --- /dev/null +++ b/spec/lib/gitlab/phabricator_import/representation/user_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::PhabricatorImport::Representation::User do + subject(:user) do + described_class.new( + { + 'phid' => 'the-phid', + 'fields' => { + 'username' => 'the-username' + } + } + ) + end + + describe '#phabricator_id' do + it 'returns the phabricator id' do + expect(user.phabricator_id).to eq('the-phid') + end + end + + describe '#username' do + it 'returns the username' do + expect(user.username).to eq('the-username') + end + end +end diff --git a/spec/lib/gitlab/phabricator_import/user_finder_spec.rb b/spec/lib/gitlab/phabricator_import/user_finder_spec.rb new file mode 100644 index 00000000000..096321cda5f --- /dev/null +++ b/spec/lib/gitlab/phabricator_import/user_finder_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe Gitlab::PhabricatorImport::UserFinder, :clean_gitlab_redis_cache do + let(:project) { create(:project, namespace: create(:group)) } + subject(:finder) { described_class.new(project, ['first-phid', 'second-phid']) } + + before do + project.namespace.add_developer(existing_user) + end + + describe '#find' do + let!(:existing_user) { create(:user, username: 'existing-user') } + let(:cache) { Gitlab::PhabricatorImport::Cache::Map.new(project) } + + before do + allow(finder).to receive(:object_map).and_return(cache) + end + + context 'for a cached phid' do + before do + cache.set_gitlab_model(existing_user, 'first-phid') + end + + it 'returns the existing user' do + expect(finder.find('first-phid')).to eq(existing_user) + end + + it 'does not perform a find using the API' do + expect(finder).not_to receive(:find_user_for_phid) + + finder.find('first-phid') + end + + it 'excludes the phid from the request if one needs to be made' do + client = instance_double(Gitlab::PhabricatorImport::Conduit::User) + allow(finder).to receive(:client).and_return(client) + + expect(client).to receive(:users).with(['second-phid']).and_return([]) + + finder.find('first-phid') + finder.find('second-phid') + end + end + + context 'when the phid is not cached' do + let(:response) do + [ + instance_double( + Gitlab::PhabricatorImport::Conduit::UsersResponse, + users: [instance_double(Gitlab::PhabricatorImport::Representation::User, phabricator_id: 'second-phid', username: 'existing-user')] + ), + instance_double( + Gitlab::PhabricatorImport::Conduit::UsersResponse, + users: [instance_double(Gitlab::PhabricatorImport::Representation::User, phabricator_id: 'first-phid', username: 'other-user')] + ) + ] + end + let(:client) do + client = instance_double(Gitlab::PhabricatorImport::Conduit::User) + allow(client).to receive(:users).and_return(response) + + client + end + + before do + allow(finder).to receive(:client).and_return(client) + end + + it 'loads the users from the API once' do + expect(client).to receive(:users).and_return(response).once + + expect(finder.find('second-phid')).to eq(existing_user) + expect(finder.find('first-phid')).to be_nil + end + + it 'adds found users to the cache' do + expect { finder.find('second-phid') } + .to change { cache.get_gitlab_model('second-phid') } + .from(nil).to(existing_user) + end + + it 'only returns users that are members of the project' do + create(:user, username: 'other-user') + + expect(finder.find('first-phid')).to eq(nil) + end + end + end +end diff --git a/spec/lib/gitlab/sql/pattern_spec.rb b/spec/lib/gitlab/sql/pattern_spec.rb index 5b5052de372..98838712eae 100644 --- a/spec/lib/gitlab/sql/pattern_spec.rb +++ b/spec/lib/gitlab/sql/pattern_spec.rb @@ -10,6 +10,12 @@ describe Gitlab::SQL::Pattern do it 'returns exact matching pattern' do expect(to_pattern).to eq('12') end + + context 'and ignore_minimum_char_limit is true' do + it 'returns partial matching pattern' do + expect(User.to_pattern(query, use_minimum_char_limit: false)).to eq('%12%') + end + end end context 'when a query with a escape character is shorter than 3 chars' do @@ -18,6 +24,12 @@ describe Gitlab::SQL::Pattern do it 'returns sanitized exact matching pattern' do expect(to_pattern).to eq('\_2') end + + context 'and ignore_minimum_char_limit is true' do + it 'returns sanitized partial matching pattern' do + expect(User.to_pattern(query, use_minimum_char_limit: false)).to eq('%\_2%') + end + end end context 'when a query is equal to 3 chars' do diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index 253366e0789..f8b0cbfb6f6 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -353,7 +353,7 @@ describe Gitlab::UrlBlocker do end end - describe '#validate_hostname!' do + describe '#validate_hostname' do let(:ip_addresses) do [ '2001:db8:1f70::999:de8:7648:6e8', @@ -378,7 +378,7 @@ describe Gitlab::UrlBlocker do it 'does not raise error for valid Ip addresses' do ip_addresses.each do |ip| - expect { described_class.send(:validate_hostname!, ip) }.not_to raise_error + expect { described_class.send(:validate_hostname, ip) }.not_to raise_error end end end diff --git a/spec/lib/peek/views/redis_detailed_spec.rb b/spec/lib/peek/views/redis_detailed_spec.rb new file mode 100644 index 00000000000..da13b6df53b --- /dev/null +++ b/spec/lib/peek/views/redis_detailed_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Peek::Views::RedisDetailed do + let(:redis_detailed_class) do + Class.new do + include Peek::Views::RedisDetailed + end + end + + subject { redis_detailed_class.new } + + using RSpec::Parameterized::TableSyntax + + where(:cmd, :expected) do + [:auth, 'test'] | 'auth <redacted>' + [:set, 'key', 'value'] | 'set key <redacted>' + [:set, 'bad'] | 'set bad' + [:hmset, 'key1', 'value1', 'key2', 'value2'] | 'hmset key1 <redacted>' + [:get, 'key'] | 'get key' + end + + with_them do + it 'scrubs Redis commands', :request_store do + subject.detail_store << { cmd: cmd, duration: 1.second } + + expect(subject.details.count).to eq(1) + expect(subject.details.first) + .to eq({ + cmd: expected, + duration: 1000 + }) + end + end +end diff --git a/spec/migrations/active_record/schema_spec.rb b/spec/migrations/active_record/schema_spec.rb index fbf5d387d0e..bc246f88685 100644 --- a/spec/migrations/active_record/schema_spec.rb +++ b/spec/migrations/active_record/schema_spec.rb @@ -15,7 +15,7 @@ describe ActiveRecord::Schema do it '> schema version equals last migration timestamp' do defined_schema_version = File.open(Rails.root.join('db', 'schema.rb')) do |file| file.find { |line| line =~ /ActiveRecord::Schema.define/ } - end.match(/(\d+)/)[0].to_i + end.match(/(\d{4}_\d{2}_\d{2}_\d{6})/)[0].to_i expect(defined_schema_version).to eq(latest_migration_timestamp) end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index d98db024f73..78862de0657 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2013,6 +2013,7 @@ describe Ci::Build do { key: 'CI', value: 'true', public: true, masked: false }, { key: 'GITLAB_CI', value: 'true', public: true, masked: false }, { key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true, masked: false }, + { key: 'CI_SERVER_HOST', value: Gitlab.config.gitlab.host, public: true, masked: false }, { key: 'CI_SERVER_NAME', value: 'GitLab', public: true, masked: false }, { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true, masked: false }, { key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s, public: true, masked: false }, diff --git a/spec/models/clusters/clusters_hierarchy_spec.rb b/spec/models/clusters/clusters_hierarchy_spec.rb new file mode 100644 index 00000000000..0470ebe17ea --- /dev/null +++ b/spec/models/clusters/clusters_hierarchy_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Clusters::ClustersHierarchy do + describe '#base_and_ancestors' do + def base_and_ancestors(clusterable) + described_class.new(clusterable).base_and_ancestors + end + + context 'project in nested group with clusters at every level' do + let!(:cluster) { create(:cluster, :project, projects: [project]) } + let!(:child) { create(:cluster, :group, groups: [child_group]) } + let!(:parent) { create(:cluster, :group, groups: [parent_group]) } + let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group]) } + + let(:ancestor_group) { create(:group) } + let(:parent_group) { create(:group, parent: ancestor_group) } + let(:child_group) { create(:group, parent: parent_group) } + let(:project) { create(:project, group: child_group) } + + it 'returns clusters for project' do + expect(base_and_ancestors(project)).to eq([cluster, child, parent, ancestor]) + end + + it 'returns clusters for child_group' do + expect(base_and_ancestors(child_group)).to eq([child, parent, ancestor]) + end + + it 'returns clusters for parent_group' do + expect(base_and_ancestors(parent_group)).to eq([parent, ancestor]) + end + + it 'returns clusters for ancestor_group' do + expect(base_and_ancestors(ancestor_group)).to eq([ancestor]) + end + end + + context 'project in a namespace' do + let!(:cluster) { create(:cluster, :project) } + + it 'returns clusters for project' do + expect(base_and_ancestors(cluster.project)).to eq([cluster]) + end + end + + context 'project in nested group with clusters at some levels' do + let!(:child) { create(:cluster, :group, groups: [child_group]) } + let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group]) } + + let(:ancestor_group) { create(:group) } + let(:parent_group) { create(:group, parent: ancestor_group) } + let(:child_group) { create(:group, parent: parent_group) } + let(:project) { create(:project, group: child_group) } + + it 'returns clusters for project' do + expect(base_and_ancestors(project)).to eq([child, ancestor]) + end + + it 'returns clusters for child_group' do + expect(base_and_ancestors(child_group)).to eq([child, ancestor]) + end + + it 'returns clusters for parent_group' do + expect(base_and_ancestors(parent_group)).to eq([ancestor]) + end + + it 'returns clusters for ancestor_group' do + expect(base_and_ancestors(ancestor_group)).to eq([ancestor]) + end + end + end +end diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index b96ca89c893..4a524b585e1 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -139,8 +139,8 @@ describe CommitRange do end describe '#has_been_reverted?' do - let(:issue) { create(:issue) } - let(:user) { issue.author } + let(:user) { create(:user) } + let(:issue) { create(:issue, author: user, project: project) } it 'returns true if the commit has been reverted' do create(:note_on_issue, @@ -149,9 +149,11 @@ describe CommitRange do note: commit1.revert_description(user), project: issue.project) - expect_any_instance_of(Commit).to receive(:reverts_commit?) - .with(commit1, user) - .and_return(true) + expect_next_instance_of(Commit) do |commit| + expect(commit).to receive(:reverts_commit?) + .with(commit1, user) + .and_return(true) + end expect(commit1.has_been_reverted?(user, issue.notes_with_associations)).to eq(true) end diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb index 0e5fb2b5153..9a12c3d6965 100644 --- a/spec/models/concerns/cache_markdown_field_spec.rb +++ b/spec/models/concerns/cache_markdown_field_spec.rb @@ -198,6 +198,36 @@ describe CacheMarkdownField, :clean_gitlab_redis_cache do end end end + + describe '#updated_cached_html_for' do + let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) } + + context 'when the markdown cache is outdated' do + before do + thing.cached_markdown_version += 1 + end + + it 'calls #refresh_markdown_cache' do + expect(thing).to receive(:refresh_markdown_cache) + + expect(thing.updated_cached_html_for(:description)).to eq(html) + end + end + + context 'when the markdown field does not exist' do + it 'returns nil' do + expect(thing.updated_cached_html_for(:something)).to eq(nil) + end + end + + context 'when the markdown cache is up to date' do + it 'does not call #refresh_markdown_cache' do + expect(thing).not_to receive(:refresh_markdown_cache) + + expect(thing.updated_cached_html_for(:description)).to eq(html) + end + end + end end context 'for Active record classes' do diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb index 2378f400540..e2fc8a5d127 100644 --- a/spec/models/concerns/deployment_platform_spec.rb +++ b/spec/models/concerns/deployment_platform_spec.rb @@ -5,7 +5,7 @@ require 'rails_helper' describe DeploymentPlatform do let(:project) { create(:project) } - describe '#deployment_platform' do + shared_examples '#deployment_platform' do subject { project.deployment_platform } context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service' do @@ -84,4 +84,20 @@ describe DeploymentPlatform do end end end + + context 'legacy implementation' do + before do + stub_feature_flags(clusters_cte: false) + end + + include_examples '#deployment_platform' + end + + context 'CTE implementation' do + before do + stub_feature_flags(clusters_cte: true) + end + + include_examples '#deployment_platform' + end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 64f02978d79..68224a56515 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -223,6 +223,16 @@ describe Issuable do expect(issuable_class.full_search(searchable_issue2.description.downcase)).to eq([searchable_issue2]) end + it 'returns issues with a fuzzy matching description for a query shorter than 3 chars if told to do so' do + search = searchable_issue2.description.downcase.scan(/\w+/).sample[-1] + + expect(issuable_class.full_search(search, use_minimum_char_limit: false)).to include(searchable_issue2) + end + + it 'returns issues with a fuzzy matching title for a query shorter than 3 chars if told to do so' do + expect(issuable_class.full_search('i', use_minimum_char_limit: false)).to include(searchable_issue) + end + context 'when matching columns is "title"' do it 'returns issues with a matching title' do expect(issuable_class.full_search(searchable_issue.title, matched_columns: 'title')) diff --git a/spec/models/concerns/project_api_compatibility_spec.rb b/spec/models/concerns/project_api_compatibility_spec.rb new file mode 100644 index 00000000000..8cecd4fe7bc --- /dev/null +++ b/spec/models/concerns/project_api_compatibility_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ProjectAPICompatibility do + let(:project) { create(:project) } + + # git_strategy + it "converts build_git_strategy=fetch to build_allow_git_fetch=true" do + project.update!(build_git_strategy: 'fetch') + expect(project.build_allow_git_fetch).to eq(true) + end + + it "converts build_git_strategy=clone to build_allow_git_fetch=false" do + project.update!(build_git_strategy: 'clone') + expect(project.build_allow_git_fetch).to eq(false) + end + + # auto_devops_enabled + it "converts auto_devops_enabled=false to auto_devops_enabled?=false" do + expect(project.auto_devops_enabled?).to eq(true) + project.update!(auto_devops_enabled: false) + expect(project.auto_devops_enabled?).to eq(false) + end + + it "converts auto_devops_enabled=true to auto_devops_enabled?=true" do + expect(project.auto_devops_enabled?).to eq(true) + project.update!(auto_devops_enabled: true) + expect(project.auto_devops_enabled?).to eq(true) + end + + # auto_devops_deploy_strategy + it "converts auto_devops_deploy_strategy=timed_incremental to auto_devops.deploy_strategy=timed_incremental" do + expect(project.auto_devops).to be_nil + project.update!(auto_devops_deploy_strategy: 'timed_incremental') + expect(project.auto_devops.deploy_strategy).to eq('timed_incremental') + end +end diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb index 5aa43b58217..1fe176ab5af 100644 --- a/spec/models/concerns/project_features_compatibility_spec.rb +++ b/spec/models/concerns/project_features_compatibility_spec.rb @@ -4,7 +4,8 @@ require 'spec_helper' describe ProjectFeaturesCompatibility do let(:project) { create(:project) } - let(:features) { %w(issues wiki builds merge_requests snippets) } + let(:features_except_repository) { %w(issues wiki builds merge_requests snippets) } + let(:features) { features_except_repository + ['repository'] } # We had issues_enabled, snippets_enabled, builds_enabled, merge_requests_enabled and issues_enabled fields on projects table # All those fields got moved to a new table called project_feature and are now integers instead of booleans @@ -12,30 +13,37 @@ describe ProjectFeaturesCompatibility do # So we can keep it compatible it "converts fields from 'true' to ProjectFeature::ENABLED" do - features.each do |feature| + features_except_repository.each do |feature| project.update_attribute("#{feature}_enabled".to_sym, "true") expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED) end end it "converts fields from 'false' to ProjectFeature::DISABLED" do - features.each do |feature| + features_except_repository.each do |feature| project.update_attribute("#{feature}_enabled".to_sym, "false") expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED) end end it "converts fields from true to ProjectFeature::ENABLED" do - features.each do |feature| + features_except_repository.each do |feature| project.update_attribute("#{feature}_enabled".to_sym, true) expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED) end end it "converts fields from false to ProjectFeature::DISABLED" do - features.each do |feature| + features_except_repository.each do |feature| project.update_attribute("#{feature}_enabled".to_sym, false) expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED) end end + + it "accepts private as ProjectFeature::PRIVATE" do + features.each do |feature| + project.update!("#{feature}_access_level".to_sym => 'private') + expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::PRIVATE) + end + end end diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb index 7faa196623f..3d026932f59 100644 --- a/spec/models/concerns/reactive_caching_spec.rb +++ b/spec/models/concerns/reactive_caching_spec.rb @@ -47,30 +47,12 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do subject(:go!) { instance.result } - context 'when cache is empty' do - it { is_expected.to be_nil } - - it 'enqueues a background worker to bootstrap the cache' do - expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666) - - go! - end - - it 'updates the cache lifespan' do - expect(reactive_cache_alive?(instance)).to be_falsy - - go! - - expect(reactive_cache_alive?(instance)).to be_truthy - end - end - - context 'when the cache is full' do + shared_examples 'a cacheable value' do |cached_value| before do - stub_reactive_cache(instance, 4) + stub_reactive_cache(instance, cached_value) end - it { is_expected.to eq(4) } + it { is_expected.to eq(cached_value) } it 'does not enqueue a background worker' do expect(ReactiveCachingWorker).not_to receive(:perform_async) @@ -90,9 +72,7 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do end it { is_expected.to be_nil } - end - context 'when cache was invalidated' do it 'refreshes cache' do expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666) @@ -101,12 +81,34 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do end end - context 'when cache contains non-nil but blank value' do - before do - stub_reactive_cache(instance, false) + context 'when cache is empty' do + it { is_expected.to be_nil } + + it 'enqueues a background worker to bootstrap the cache' do + expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666) + + go! end - it { is_expected.to eq(false) } + it 'updates the cache lifespan' do + expect(reactive_cache_alive?(instance)).to be_falsy + + go! + + expect(reactive_cache_alive?(instance)).to be_truthy + end + end + + context 'when the cache is full' do + it_behaves_like 'a cacheable value', 4 + end + + context 'when the cache contains non-nil but blank value' do + it_behaves_like 'a cacheable value', false + end + + context 'when the cache contains nil value' do + it_behaves_like 'a cacheable value', nil end end @@ -206,8 +208,9 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do expect(read_reactive_cache(instance)).to eq("preexisting") end - it 'enqueues a repeat worker' do - expect_reactive_cache_update_queued(instance) + it 'does not enqueue a repeat worker' do + expect(ReactiveCachingWorker) + .not_to receive(:perform_in) expect { go! }.to raise_error("foo") end diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb index 1fb0dd5030c..31163a5bb5c 100644 --- a/spec/models/concerns/routable_spec.rb +++ b/spec/models/concerns/routable_spec.rb @@ -15,23 +15,46 @@ describe Group, 'Routable' do end describe 'Callbacks' do - it 'creates route record on create' do - expect(group.route.path).to eq(group.path) - expect(group.route.name).to eq(group.name) - end + context 'for a group' do + it 'creates route record on create' do + expect(group.route.path).to eq(group.path) + expect(group.route.name).to eq(group.name) + end + + it 'updates route record on path change' do + group.update(path: 'wow', name: 'much') - it 'updates route record on path change' do - group.update(path: 'wow', name: 'much') + expect(group.route.path).to eq('wow') + expect(group.route.name).to eq('much') + end + + it 'ensure route path uniqueness across different objects' do + create(:group, parent: group, path: 'xyz') + duplicate = build(:project, namespace: group, path: 'xyz') - expect(group.route.path).to eq('wow') - expect(group.route.name).to eq('much') + expect { duplicate.save! }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Path has already been taken') + end end - it 'ensure route path uniqueness across different objects' do - create(:group, parent: group, path: 'xyz') - duplicate = build(:project, namespace: group, path: 'xyz') + context 'for a user' do + let(:user) { create(:user, username: 'jane', name: "Jane Doe") } - expect { duplicate.save! }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Path has already been taken') + it 'creates the route for a record on create' do + expect(user.namespace.name).to eq('Jane Doe') + expect(user.namespace.path).to eq('jane') + end + + it 'updates routes and nested routes on name change' do + project = create(:project, path: 'work-stuff', name: 'Work stuff', namespace: user.namespace) + + user.update!(username: 'jaen', name: 'Jaen Did') + project.reload + + expect(user.namespace.name).to eq('Jaen Did') + expect(user.namespace.path).to eq('jaen') + expect(project.full_name).to eq('Jaen Did / Work stuff') + expect(project.full_path).to eq('jaen/work-stuff') + end end end diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb index b22a0340015..db6e70973ae 100644 --- a/spec/models/cycle_analytics/code_spec.rb +++ b/spec/models/cycle_analytics/code_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#code' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } context 'with deployment' do generate_cycle_analytics_spec( diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb index 07d60be091a..4ccbdf29df6 100644 --- a/spec/models/cycle_analytics/issue_spec.rb +++ b/spec/models/cycle_analytics/issue_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#issue' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :issue, @@ -23,7 +24,7 @@ describe 'CycleAnalytics#issue' do ["list label added to issue", -> (context, data) do if data[:issue].persisted? - data[:issue].update(label_ids: [context.create(:label, lists: [context.create(:list)]).id]) + data[:issue].update(label_ids: [context.create(:list).label_id]) end end]], post_fn: -> (context, data) do diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb index 3d22a284264..c99c38e9cf3 100644 --- a/spec/models/cycle_analytics/plan_spec.rb +++ b/spec/models/cycle_analytics/plan_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#plan' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :plan, @@ -24,7 +25,7 @@ describe 'CycleAnalytics#plan' do end], ["list label added to issue", -> (context, data) do - data[:issue].update(label_ids: [context.create(:label, lists: [context.create(:list)]).id]) + data[:issue].update(label_ids: [context.create(:list).label_id]) end]], end_time_conditions: [["issue mentioned in a commit", -> (context, data) do diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb index 383727cd8f7..ddd199362d1 100644 --- a/spec/models/cycle_analytics/production_spec.rb +++ b/spec/models/cycle_analytics/production_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#production' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :production, diff --git a/spec/models/cycle_analytics_spec.rb b/spec/models/cycle_analytics/project_level_spec.rb index 5d8b5b573cf..77bd0bfeb9c 100644 --- a/spec/models/cycle_analytics_spec.rb +++ b/spec/models/cycle_analytics/project_level_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CycleAnalytics do +describe CycleAnalytics::ProjectLevel do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } @@ -11,9 +11,9 @@ describe CycleAnalytics do let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") } let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) } - subject { described_class.new(project, from: from_date) } + subject { described_class.new(project, options: { from: from_date }) } - describe '#all_medians_per_stage' do + describe '#all_medians_by_stage' do before do allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue]) @@ -26,7 +26,7 @@ describe CycleAnalytics do hsh[stage_name] = subject[stage_name].median.presence end - expect(subject.all_medians_per_stage).to eq(values) + expect(subject.all_medians_by_stage).to eq(values) end end end diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb index 1af5f9cc1f4..63c481ed465 100644 --- a/spec/models/cycle_analytics/review_spec.rb +++ b/spec/models/cycle_analytics/review_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#review' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :review, diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb index 8375944f03c..c134b97553f 100644 --- a/spec/models/cycle_analytics/staging_spec.rb +++ b/spec/models/cycle_analytics/staging_spec.rb @@ -9,7 +9,7 @@ describe 'CycleAnalytics#staging' do let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :staging, diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb index b78258df564..a6ea73b2699 100644 --- a/spec/models/cycle_analytics/test_spec.rb +++ b/spec/models/cycle_analytics/test_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#test' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :test, diff --git a/spec/models/deployment_metrics_spec.rb b/spec/models/deployment_metrics_spec.rb new file mode 100644 index 00000000000..0aadb1f3a5e --- /dev/null +++ b/spec/models/deployment_metrics_spec.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe DeploymentMetrics do + describe '#has_metrics?' do + subject { described_class.new(deployment.project, deployment).has_metrics? } + + context 'when deployment is failed' do + let(:deployment) { create(:deployment, :failed) } + + it { is_expected.to be_falsy } + end + + context 'when deployment is success' do + let(:deployment) { create(:deployment, :success) } + + context 'without a monitoring service' do + it { is_expected.to be_falsy } + end + + context 'with a Prometheus Service' do + let(:prometheus_service) { instance_double(PrometheusService, can_query?: true) } + + before do + allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service + end + + it { is_expected.to be_truthy } + end + + context 'with a Prometheus Service that cannot query' do + let(:prometheus_service) { instance_double(PrometheusService, can_query?: false) } + + before do + allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service + end + + it { is_expected.to be_falsy } + end + + context 'with a cluster Prometheus' do + let(:deployment) { create(:deployment, :success, :on_cluster) } + let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: deployment.cluster) } + + before do + expect(deployment.cluster.application_prometheus).to receive(:can_query?).and_return(true) + end + + it { is_expected.to be_truthy } + end + + context 'fallback deployment platform' do + let(:cluster) { create(:cluster, :provided_by_user, environment_scope: '*', projects: [deployment.project]) } + let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) } + + before do + expect(deployment.project).to receive(:deployment_platform).and_return(cluster.platform) + expect(cluster.application_prometheus).to receive(:can_query?).and_return(true) + end + + it { is_expected.to be_truthy } + end + end + end + + describe '#metrics' do + let(:deployment) { create(:deployment, :success) } + let(:prometheus_adapter) { instance_double(PrometheusService, can_query?: true) } + let(:deployment_metrics) { described_class.new(deployment.project, deployment) } + + subject { deployment_metrics.metrics } + + context 'metrics are disabled' do + it { is_expected.to eq({}) } + end + + context 'metrics are enabled' do + let(:simple_metrics) do + { + success: true, + metrics: {}, + last_update: 42 + } + end + + before do + allow(deployment_metrics).to receive(:prometheus_adapter).and_return(prometheus_adapter) + expect(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics) + end + + it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) } + end + end + + describe '#additional_metrics' do + let(:project) { create(:project, :repository) } + let(:deployment) { create(:deployment, :succeed, project: project) } + let(:deployment_metrics) { described_class.new(deployment.project, deployment) } + + subject { deployment_metrics.additional_metrics } + + context 'metrics are disabled' do + it { is_expected.to eq({}) } + end + + context 'metrics are enabled' do + let(:simple_metrics) do + { + success: true, + metrics: {}, + last_update: 42 + } + end + + let(:prometheus_adapter) { instance_double('prometheus_adapter', can_query?: true) } + + before do + allow(deployment_metrics).to receive(:prometheus_adapter).and_return(prometheus_adapter) + expect(prometheus_adapter).to receive(:query).with(:additional_metrics_deployment, deployment).and_return(simple_metrics) + end + + it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) } + end + end +end diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index 713fb647708..d4e631f109b 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -295,125 +295,6 @@ describe Deployment do end end - describe '#has_metrics?' do - subject { deployment.has_metrics? } - - context 'when deployment is failed' do - let(:deployment) { create(:deployment, :failed) } - - it { is_expected.to be_falsy } - end - - context 'when deployment is success' do - let(:deployment) { create(:deployment, :success) } - - context 'without a monitoring service' do - it { is_expected.to be_falsy } - end - - context 'with a Prometheus Service' do - let(:prometheus_service) { double(:prometheus_service, can_query?: true) } - - before do - allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service - end - - it { is_expected.to be_truthy } - end - - context 'with a Prometheus Service that cannot query' do - let(:prometheus_service) { double(:prometheus_service, can_query?: false) } - - before do - allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service - end - - it { is_expected.to be_falsy } - end - - context 'with a cluster Prometheus' do - let(:deployment) { create(:deployment, :success, :on_cluster) } - let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: deployment.cluster) } - - before do - expect(deployment.cluster.application_prometheus).to receive(:can_query?).and_return(true) - end - - it { is_expected.to be_truthy } - end - - context 'fallback deployment platform' do - let(:cluster) { create(:cluster, :provided_by_user, environment_scope: '*', projects: [deployment.project]) } - let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) } - - before do - expect(deployment.project).to receive(:deployment_platform).and_return(cluster.platform) - expect(cluster.application_prometheus).to receive(:can_query?).and_return(true) - end - - it { is_expected.to be_truthy } - end - end - end - - describe '#metrics' do - let(:deployment) { create(:deployment, :success) } - let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) } - - subject { deployment.metrics } - - context 'metrics are disabled' do - it { is_expected.to eq({}) } - end - - context 'metrics are enabled' do - let(:simple_metrics) do - { - success: true, - metrics: {}, - last_update: 42 - } - end - - before do - allow(deployment).to receive(:prometheus_adapter).and_return(prometheus_adapter) - allow(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics) - end - - it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) } - end - end - - describe '#additional_metrics' do - let(:project) { create(:project, :repository) } - let(:deployment) { create(:deployment, :succeed, project: project) } - - subject { deployment.additional_metrics } - - context 'metrics are disabled' do - it { is_expected.to eq({}) } - end - - context 'metrics are enabled' do - let(:simple_metrics) do - { - success: true, - metrics: {}, - last_update: 42 - } - end - - let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) } - - before do - allow(deployment).to receive(:prometheus_adapter).and_return(prometheus_adapter) - allow(prometheus_adapter).to receive(:query).with(:additional_metrics_deployment, deployment).and_return(simple_metrics) - end - - it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) } - end - end - describe '#stop_action' do let(:build) { create(:ci_build) } @@ -441,32 +322,4 @@ describe Deployment do end end end - - describe '#deployment_platform_cluster' do - let(:deployment) { create(:deployment) } - let(:project) { deployment.project } - let(:environment) { deployment.environment } - - subject { deployment.deployment_platform_cluster } - - before do - expect(project).to receive(:deployment_platform) - .with(environment: environment.name).and_call_original - end - - context 'project has no deployment platform' do - before do - expect(project.clusters).to be_empty - end - - it { is_expected.to be_nil } - end - - context 'project has a deployment platform' do - let!(:cluster) { create(:cluster, projects: [project]) } - let!(:platform) { create(:cluster_platform_kubernetes, cluster: cluster) } - - it { is_expected.to eq cluster } - end - end end diff --git a/spec/models/discussion_spec.rb b/spec/models/discussion_spec.rb index 22d4dab0617..950bdec4d00 100644 --- a/spec/models/discussion_spec.rb +++ b/spec/models/discussion_spec.rb @@ -10,6 +10,20 @@ describe Discussion do let(:second_note) { create(:diff_note_on_merge_request, in_reply_to: first_note) } let(:third_note) { create(:diff_note_on_merge_request) } + describe '.lazy_find' do + let!(:note1) { create(:discussion_note_on_merge_request).to_discussion } + let!(:note2) { create(:discussion_note_on_merge_request, in_reply_to: note1).to_discussion } + + subject { [note1, note2].map { |note| described_class.lazy_find(note.discussion_id) } } + + it 'batches requests' do + expect do + [described_class.lazy_find(note1.id), + described_class.lazy_find(note2.id)].map(&:__sync) + end.not_to exceed_query_limit(1) + end + end + describe '.build' do it 'returns a discussion of the right type' do discussion = described_class.build([first_note, second_note], merge_request) diff --git a/spec/models/environment_status_spec.rb b/spec/models/environment_status_spec.rb index c503c35305f..e2836420df9 100644 --- a/spec/models/environment_status_spec.rb +++ b/spec/models/environment_status_spec.rb @@ -11,11 +11,10 @@ describe EnvironmentStatus do let(:merge_request) { create(:merge_request, :deployed_review_app, deployment: deployment) } let(:sha) { deployment.sha } - subject(:environment_status) { described_class.new(environment, merge_request, sha) } + subject(:environment_status) { described_class.new(project, environment, merge_request, sha) } it { is_expected.to delegate_method(:id).to(:environment) } it { is_expected.to delegate_method(:name).to(:environment) } - it { is_expected.to delegate_method(:project).to(:environment) } it { is_expected.to delegate_method(:deployed_at).to(:deployment) } it { is_expected.to delegate_method(:status).to(:deployment) } diff --git a/spec/models/issue/metrics_spec.rb b/spec/models/issue/metrics_spec.rb index 07858fe8a70..7aa0d97b194 100644 --- a/spec/models/issue/metrics_spec.rb +++ b/spec/models/issue/metrics_spec.rb @@ -32,7 +32,7 @@ describe Issue::Metrics do context "list labels" do it "records the first time an issue is associated with a list label" do - list_label = create(:label, lists: [create(:list)]) + list_label = create(:list).label time = Time.now Timecop.freeze(time) { subject.update(label_ids: [list_label.id]) } metrics = subject.metrics @@ -43,9 +43,9 @@ describe Issue::Metrics do it "does not record the second time an issue is associated with a list label" do time = Time.now - first_list_label = create(:label, lists: [create(:list)]) + first_list_label = create(:list).label Timecop.freeze(time) { subject.update(label_ids: [first_list_label.id]) } - second_list_label = create(:label, lists: [create(:list)]) + second_list_label = create(:list).label Timecop.freeze(time + 5.hours) { subject.update(label_ids: [second_list_label.id]) } metrics = subject.metrics diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index fe6d68aff3f..9b0c232f370 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -3220,4 +3220,34 @@ describe MergeRequest do it { is_expected.to be_truthy } end end + + describe '#cleanup_refs' do + subject { merge_request.cleanup_refs(only: only) } + + let(:merge_request) { build(:merge_request) } + + context 'when removing all refs' do + let(:only) { :all } + + it 'deletes all refs from the target project' do + expect(merge_request.target_project.repository) + .to receive(:delete_refs) + .with(merge_request.ref_path, merge_request.merge_ref_path, merge_request.train_ref_path) + + subject + end + end + + context 'when removing only train ref' do + let(:only) { :train } + + it 'deletes train ref from the target project' do + expect(merge_request.target_project.repository) + .to receive(:delete_refs) + .with(merge_request.train_ref_path) + + subject + end + end + end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 7a1ab20186a..03003e3dd7d 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -177,6 +177,7 @@ describe Note do pipeline: :note, cache_key: [note1, "note"], project: note1.project, + rendered: note1.note_html, author: note1.author } }]).and_call_original @@ -189,6 +190,7 @@ describe Note do pipeline: :note, cache_key: [note2, "note"], project: note2.project, + rendered: note2.note_html, author: note2.author } }]).and_call_original diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index 661957cf08b..973c67937b7 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -53,24 +53,33 @@ describe PagesDomain do end let(:pages_domain) do - build(:pages_domain, certificate: certificate, key: key).tap do |pd| + build(:pages_domain, certificate: certificate, key: key, + auto_ssl_enabled: auto_ssl_enabled).tap do |pd| allow(pd).to receive(:project).and_return(project) pd.valid? end end - where(:pages_https_only, :certificate, :key, :errors_on) do + where(:pages_https_only, :certificate, :key, :auto_ssl_enabled, :errors_on) do attributes = attributes_for(:pages_domain) cert, key = attributes.fetch_values(:certificate, :key) - true | nil | nil | %i(certificate key) - true | cert | nil | %i(key) - true | nil | key | %i(certificate key) - true | cert | key | [] - false | nil | nil | [] - false | cert | nil | %i(key) - false | nil | key | %i(key) - false | cert | key | [] + true | nil | nil | false | %i(certificate key) + true | nil | nil | true | [] + true | cert | nil | false | %i(key) + true | cert | nil | true | %i(key) + true | nil | key | false | %i(certificate key) + true | nil | key | true | %i(key) + true | cert | key | false | [] + true | cert | key | true | [] + false | nil | nil | false | [] + false | nil | nil | true | [] + false | cert | nil | false | %i(key) + false | cert | nil | true | %i(key) + false | nil | key | false | %i(key) + false | nil | key | true | %i(key) + false | cert | key | false | [] + false | cert | key | true | [] end with_them do diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb index d5b0f94f461..74c85a13c88 100644 --- a/spec/models/project_services/bugzilla_service_spec.rb +++ b/spec/models/project_services/bugzilla_service_spec.rb @@ -44,7 +44,9 @@ describe BugzillaService do # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084 context 'when data are stored in properties' do let(:properties) { access_params.merge(title: title, description: description) } - let(:service) { create(:bugzilla_service, properties: properties) } + let(:service) do + create(:bugzilla_service, :without_properties_callback, properties: properties) + end include_examples 'issue tracker fields' end @@ -60,7 +62,7 @@ describe BugzillaService do context 'when data are stored in both properties and separated fields' do let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } let(:service) do - create(:bugzilla_service, title: title, description: description, properties: properties) + create(:bugzilla_service, :without_properties_callback, title: title, description: description, properties: properties) end include_examples 'issue tracker fields' diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb index 56b0bda6626..5259357a254 100644 --- a/spec/models/project_services/custom_issue_tracker_service_spec.rb +++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb @@ -58,7 +58,9 @@ describe CustomIssueTrackerService do # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084 context 'when data are stored in properties' do let(:properties) { access_params.merge(title: title, description: description) } - let(:service) { create(:custom_issue_tracker_service, properties: properties) } + let(:service) do + create(:custom_issue_tracker_service, :without_properties_callback, properties: properties) + end include_examples 'issue tracker fields' end @@ -74,7 +76,7 @@ describe CustomIssueTrackerService do context 'when data are stored in both properties and separated fields' do let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } let(:service) do - create(:custom_issue_tracker_service, title: title, description: description, properties: properties) + create(:custom_issue_tracker_service, :without_properties_callback, title: title, description: description, properties: properties) end include_examples 'issue tracker fields' diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index 22df19d943f..a771d1bf27f 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -101,6 +101,15 @@ describe DroneCiService, :use_clean_rails_memory_store_caching do is_expected.to eq(:error) end + Gitlab::HTTP::HTTP_ERRORS.each do |http_error| + it "sets commit status to :error with a #{http_error.name} error" do + WebMock.stub_request(:get, commit_status_path) + .to_raise(http_error) + + is_expected.to eq(:error) + end + end + { "killed" => :canceled, "failure" => :failed, diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb index a3726f09dc5..0c4fc290a13 100644 --- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb +++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb @@ -61,7 +61,9 @@ describe GitlabIssueTrackerService do # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084 context 'when data are stored in properties' do let(:properties) { access_params.merge(title: title, description: description) } - let(:service) { create(:gitlab_issue_tracker_service, properties: properties) } + let(:service) do + create(:gitlab_issue_tracker_service, :without_properties_callback, properties: properties) + end include_examples 'issue tracker fields' end @@ -77,7 +79,7 @@ describe GitlabIssueTrackerService do context 'when data are stored in both properties and separated fields' do let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } let(:service) do - create(:gitlab_issue_tracker_service, title: title, description: description, properties: properties) + create(:gitlab_issue_tracker_service, :without_properties_callback, title: title, description: description, properties: properties) end include_examples 'issue tracker fields' diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 9b122d85293..235cf314af5 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -145,7 +145,9 @@ describe JiraService do # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084 context 'when data are stored in properties' do let(:properties) { access_params.merge(title: title, description: description) } - let(:service) { create(:jira_service, properties: properties) } + let(:service) do + create(:jira_service, :without_properties_callback, properties: properties) + end include_examples 'issue tracker fields' end @@ -161,7 +163,7 @@ describe JiraService do context 'when data are stored in both properties and separated fields' do let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } let(:service) do - create(:jira_service, title: title, description: description, properties: properties) + create(:jira_service, :without_properties_callback, title: title, description: description, properties: properties) end include_examples 'issue tracker fields' diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb index 806e3695962..c1ee6546b12 100644 --- a/spec/models/project_services/redmine_service_spec.rb +++ b/spec/models/project_services/redmine_service_spec.rb @@ -50,7 +50,9 @@ describe RedmineService do # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084 context 'when data are stored in properties' do let(:properties) { access_params.merge(title: title, description: description) } - let(:service) { create(:redmine_service, properties: properties) } + let(:service) do + create(:redmine_service, :without_properties_callback, properties: properties) + end include_examples 'issue tracker fields' end @@ -66,7 +68,7 @@ describe RedmineService do context 'when data are stored in both properties and separated fields' do let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } let(:service) do - create(:redmine_service, title: title, description: description, properties: properties) + create(:redmine_service, :without_properties_callback, title: title, description: description, properties: properties) end include_examples 'issue tracker fields' diff --git a/spec/models/project_services/youtrack_service_spec.rb b/spec/models/project_services/youtrack_service_spec.rb index b47ef6702b4..c48bf487af0 100644 --- a/spec/models/project_services/youtrack_service_spec.rb +++ b/spec/models/project_services/youtrack_service_spec.rb @@ -47,7 +47,9 @@ describe YoutrackService do # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084 context 'when data are stored in properties' do let(:properties) { access_params.merge(title: title, description: description) } - let(:service) { create(:youtrack_service, properties: properties) } + let(:service) do + create(:youtrack_service, :without_properties_callback, properties: properties) + end include_examples 'issue tracker fields' end @@ -63,7 +65,7 @@ describe YoutrackService do context 'when data are stored in both properties and separated fields' do let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } let(:service) do - create(:youtrack_service, title: title, description: description, properties: properties) + create(:youtrack_service, :without_properties_callback, title: title, description: description, properties: properties) end include_examples 'issue tracker fields' diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb index 1cb49d83ffa..db3e4902c64 100644 --- a/spec/models/project_statistics_spec.rb +++ b/spec/models/project_statistics_spec.rb @@ -135,6 +135,49 @@ describe ProjectStatistics do expect(statistics.wiki_size).to eq(0) end end + + context 'when the column is namespace relatable' do + let(:namespace) { create(:group) } + let(:project) { create(:project, namespace: namespace) } + + context 'when the feature flag is off' do + it 'does not schedule the aggregation worker' do + stub_feature_flags(update_statistics_namespace: false, namespace: namespace) + + expect(Namespaces::ScheduleAggregationWorker) + .not_to receive(:perform_async) + + statistics.refresh!(only: [:lfs_objects_size]) + end + end + + context 'when the feature flag is on' do + it 'schedules the aggregation worker' do + expect(Namespaces::ScheduleAggregationWorker) + .to receive(:perform_async) + + statistics.refresh!(only: [:lfs_objects_size]) + end + end + + context 'when no argument is passed' do + it 'schedules the aggregation worker' do + expect(Namespaces::ScheduleAggregationWorker) + .to receive(:perform_async) + + statistics.refresh! + end + end + end + + context 'when the column is not namespace relatable' do + it 'does not schedules an aggregation worker' do + expect(Namespaces::ScheduleAggregationWorker) + .not_to receive(:perform_async) + + statistics.refresh!(only: [:commit_count]) + end + end end describe '#update_commit_count' do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 3d967aa4ab8..12dff440ce2 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2426,16 +2426,15 @@ describe Repository do # Gets the commit oid, and warms the cache oid = project.commit.id - expect(Gitlab::Git::Commit).not_to receive(:find).once + expect(Gitlab::Git::Commit).to receive(:find).once - project.commit_by(oid: oid) + 2.times { project.commit_by(oid: oid) } end it 'caches nil values' do expect(Gitlab::Git::Commit).to receive(:find).once - project.commit_by(oid: '1' * 40) - project.commit_by(oid: '1' * 40) + 2.times { project.commit_by(oid: '1' * 40) } end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a4d177da0be..e4f84172215 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2931,7 +2931,7 @@ describe User do let(:user) { create(:user, username: username) } context 'when the user is updated' do - context 'when the username is changed' do + context 'when the username or name is changed' do let(:new_username) { 'bar' } it 'changes the namespace (just to compare to when username is not changed)' do @@ -2942,16 +2942,24 @@ describe User do end.to change { user.namespace.updated_at } end - it 'updates the namespace name' do + it 'updates the namespace path when the username was changed' do user.update!(username: new_username) - expect(user.namespace.name).to eq(new_username) + expect(user.namespace.path).to eq(new_username) end - it 'updates the namespace path' do - user.update!(username: new_username) + it 'updates the namespace name if the name was changed' do + user.update!(name: 'New name') - expect(user.namespace.path).to eq(new_username) + expect(user.namespace.name).to eq('New name') + end + + it 'updates nested routes for the namespace if the name was changed' do + project = create(:project, namespace: user.namespace) + + user.update!(name: 'New name') + + expect(project.route.reload.name).to include('New name') end context 'when there is a validation error (namespace name taken) while updating namespace' do diff --git a/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb new file mode 100644 index 00000000000..b04fcb9aece --- /dev/null +++ b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Adding a DiffNote' do + include GraphqlHelpers + + set(:current_user) { create(:user) } + let(:noteable) { create(:merge_request, source_project: project, target_project: project) } + let(:project) { create(:project, :repository) } + let(:diff_refs) { noteable.diff_refs } + let(:mutation) do + variables = { + noteable_id: GitlabSchema.id_from_object(noteable).to_s, + body: 'Body text', + position: { + paths: { + old_path: 'files/ruby/popen.rb', + new_path: 'files/ruby/popen2.rb' + }, + new_line: 14, + base_sha: diff_refs.base_sha, + head_sha: diff_refs.head_sha, + start_sha: diff_refs.start_sha + } + } + + graphql_mutation(:create_diff_note, variables) + end + + def mutation_response + graphql_mutation_response(:create_diff_note) + end + + it_behaves_like 'a Note mutation when the user does not have permission' + + context 'when the user has permission' do + before do + project.add_developer(current_user) + end + + it_behaves_like 'a Note mutation that creates a Note' + + it_behaves_like 'a Note mutation when there are active record validation errors', model: DiffNote + + context do + let(:diff_refs) { build(:merge_request).diff_refs } # Allow fake diff refs so arguments are valid + + it_behaves_like 'a Note mutation when the given resource id is not for a Noteable' + end + + it 'returns the note with the correct position' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['note']['body']).to eq('Body text') + mutation_position_response = mutation_response['note']['position'] + expect(mutation_position_response['positionType']).to eq('text') + expect(mutation_position_response['filePath']).to eq('files/ruby/popen2.rb') + expect(mutation_position_response['oldPath']).to eq('files/ruby/popen.rb') + expect(mutation_position_response['newPath']).to eq('files/ruby/popen2.rb') + expect(mutation_position_response['newLine']).to eq(14) + end + end +end diff --git a/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb new file mode 100644 index 00000000000..3ba6c689024 --- /dev/null +++ b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Adding an image DiffNote' do + include GraphqlHelpers + + set(:current_user) { create(:user) } + let(:noteable) { create(:merge_request, source_project: project, target_project: project) } + let(:project) { create(:project, :repository) } + let(:diff_refs) { noteable.diff_refs } + let(:mutation) do + variables = { + noteable_id: GitlabSchema.id_from_object(noteable).to_s, + body: 'Body text', + position: { + paths: { + old_path: 'files/images/any_image.png', + new_path: 'files/images/any_image2.png' + }, + width: 100, + height: 200, + x: 1, + y: 2, + base_sha: diff_refs.base_sha, + head_sha: diff_refs.head_sha, + start_sha: diff_refs.start_sha + } + } + + graphql_mutation(:create_image_diff_note, variables) + end + + def mutation_response + graphql_mutation_response(:create_image_diff_note) + end + + it_behaves_like 'a Note mutation when the user does not have permission' + + context 'when the user has permission' do + before do + project.add_developer(current_user) + end + + it_behaves_like 'a Note mutation that creates a Note' + + it_behaves_like 'a Note mutation when there are active record validation errors', model: DiffNote + + context do + let(:diff_refs) { build(:merge_request).diff_refs } # Allow fake diff refs so arguments are valid + + it_behaves_like 'a Note mutation when the given resource id is not for a Noteable' + end + + it 'returns the note with the correct position' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['note']['body']).to eq('Body text') + mutation_position_response = mutation_response['note']['position'] + expect(mutation_position_response['filePath']).to eq('files/images/any_image2.png') + expect(mutation_position_response['oldPath']).to eq('files/images/any_image.png') + expect(mutation_position_response['newPath']).to eq('files/images/any_image2.png') + expect(mutation_position_response['positionType']).to eq('image') + expect(mutation_position_response['width']).to eq(100) + expect(mutation_position_response['height']).to eq(200) + expect(mutation_position_response['x']).to eq(1) + expect(mutation_position_response['y']).to eq(2) + end + end +end diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb new file mode 100644 index 00000000000..14aaa430ac9 --- /dev/null +++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Adding a Note' do + include GraphqlHelpers + + set(:current_user) { create(:user) } + let(:noteable) { create(:merge_request, source_project: project, target_project: project) } + let(:project) { create(:project) } + let(:discussion) { nil } + let(:mutation) do + variables = { + noteable_id: GitlabSchema.id_from_object(noteable).to_s, + discussion_id: (GitlabSchema.id_from_object(discussion).to_s if discussion), + body: 'Body text' + } + + graphql_mutation(:create_note, variables) + end + + def mutation_response + graphql_mutation_response(:create_note) + end + + it_behaves_like 'a Note mutation when the user does not have permission' + + context 'when the user has permission' do + before do + project.add_developer(current_user) + end + + it_behaves_like 'a Note mutation that creates a Note' + + it_behaves_like 'a Note mutation when there are active record validation errors' + + it_behaves_like 'a Note mutation when the given resource id is not for a Noteable' + + it 'returns the note' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['note']['body']).to eq('Body text') + end + + describe 'creating Notes in reply to a discussion' do + context 'when the user does not have permission to create notes on the discussion' do + let(:discussion) { create(:discussion_note).to_discussion } + + it_behaves_like 'a mutation that returns top-level errors', + errors: ["The discussion does not exist or you don't have permission to perform this action"] + end + + context 'when the user has permission to create notes on the discussion' do + let(:discussion) { create(:discussion_note, project: project).to_discussion } + + it 'creates a Note in a discussion' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['note']['discussion']['id']).to eq(discussion.to_global_id.to_s) + end + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/notes/destroy_spec.rb b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb new file mode 100644 index 00000000000..337a6e6f6e6 --- /dev/null +++ b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Destroying a Note' do + include GraphqlHelpers + + let!(:note) { create(:note) } + let(:mutation) do + variables = { + id: GitlabSchema.id_from_object(note).to_s + } + + graphql_mutation(:destroy_note, variables) + end + + def mutation_response + graphql_mutation_response(:destroy_note) + end + + context 'when the user does not have permission' do + let(:current_user) { create(:user) } + + it_behaves_like 'a mutation that returns top-level errors', + errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action'] + + it 'does not destroy the Note' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.not_to change { Note.count } + end + end + + context 'when the user has permission' do + let(:current_user) { note.author } + + it_behaves_like 'a Note mutation when the given resource id is not for a Note' + + it 'destroys the Note' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to change { Note.count }.by(-1) + end + + it 'returns an empty Note' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response).to have_key('note') + expect(mutation_response['note']).to be_nil + end + end +end diff --git a/spec/requests/api/graphql/mutations/notes/update_spec.rb b/spec/requests/api/graphql/mutations/notes/update_spec.rb new file mode 100644 index 00000000000..958f640995a --- /dev/null +++ b/spec/requests/api/graphql/mutations/notes/update_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Updating a Note' do + include GraphqlHelpers + + let!(:note) { create(:note, note: original_body) } + let(:original_body) { 'Initial body text' } + let(:updated_body) { 'Updated body text' } + let(:mutation) do + variables = { + id: GitlabSchema.id_from_object(note).to_s, + body: updated_body + } + + graphql_mutation(:update_note, variables) + end + + def mutation_response + graphql_mutation_response(:update_note) + end + + context 'when the user does not have permission' do + let(:current_user) { create(:user) } + + it_behaves_like 'a mutation that returns top-level errors', + errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action'] + + it 'does not update the Note' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(note.reload.note).to eq(original_body) + end + end + + context 'when the user has permission' do + let(:current_user) { note.author } + + it_behaves_like 'a Note mutation when the given resource id is not for a Note' + + it 'updates the Note' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(note.reload.note).to eq(updated_body) + end + + it 'returns the updated Note' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['note']['body']).to eq(updated_body) + end + + context 'when there are ActiveRecord validation errors' do + let(:updated_body) { '' } + + it_behaves_like 'a mutation that returns errors in the response', errors: ["Note can't be blank"] + + it 'does not update the Note' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(note.reload.note).to eq(original_body) + end + + it 'returns the Note with its original body' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['note']['body']).to eq(original_body) + end + end + end +end diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb index 67371cb35b6..54401ec4085 100644 --- a/spec/requests/api/graphql_spec.rb +++ b/spec/requests/api/graphql_spec.rb @@ -6,16 +6,6 @@ describe 'GraphQL' do let(:query) { graphql_query_for('echo', 'text' => 'Hello world' ) } - context 'graphql is disabled by feature flag' do - before do - stub_feature_flags(graphql: false) - end - - it 'does not generate a route for GraphQL' do - expect { post_graphql(query) }.to raise_error(ActionController::RoutingError) - end - end - context 'logging' do shared_examples 'logging a graphql query' do let(:expected_params) do diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb new file mode 100644 index 00000000000..46e3dd650cc --- /dev/null +++ b/spec/requests/api/group_clusters_spec.rb @@ -0,0 +1,452 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe API::GroupClusters do + include KubernetesHelpers + + let(:current_user) { create(:user) } + let(:developer_user) { create(:user) } + let(:group) { create(:group, :private) } + + before do + group.add_developer(developer_user) + group.add_maintainer(current_user) + end + + describe 'GET /groups/:id/clusters' do + let!(:extra_cluster) { create(:cluster, :provided_by_gcp, :group) } + + let!(:clusters) do + create_list(:cluster, 5, :provided_by_gcp, :group, :production_environment, + groups: [group]) + end + + context 'non-authorized user' do + it 'responds with 403' do + get api("/groups/#{group.id}/clusters", developer_user) + + expect(response).to have_gitlab_http_status(403) + end + end + + context 'authorized user' do + before do + get api("/groups/#{group.id}/clusters", current_user) + end + + it 'responds with 200' do + expect(response).to have_gitlab_http_status(200) + end + + it 'includes pagination headers' do + expect(response).to include_pagination_headers + end + + it 'only include authorized clusters' do + cluster_ids = json_response.map { |cluster| cluster['id'] } + + expect(cluster_ids).to match_array(clusters.pluck(:id)) + expect(cluster_ids).not_to include(extra_cluster.id) + end + end + end + + describe 'GET /groups/:id/clusters/:cluster_id' do + let(:cluster_id) { cluster.id } + + let(:platform_kubernetes) do + create(:cluster_platform_kubernetes, :configured) + end + + let(:cluster) do + create(:cluster, :group, :provided_by_gcp, :with_domain, + platform_kubernetes: platform_kubernetes, + user: current_user, + groups: [group]) + end + + context 'non-authorized user' do + it 'responds with 403' do + get api("/groups/#{group.id}/clusters/#{cluster_id}", developer_user) + + expect(response).to have_gitlab_http_status(403) + end + end + + context 'authorized user' do + before do + get api("/groups/#{group.id}/clusters/#{cluster_id}", current_user) + end + + it 'returns specific cluster' do + expect(json_response['id']).to eq(cluster.id) + end + + it 'returns cluster information' do + expect(json_response['provider_type']).to eq('gcp') + expect(json_response['platform_type']).to eq('kubernetes') + expect(json_response['environment_scope']).to eq('*') + expect(json_response['cluster_type']).to eq('group_type') + expect(json_response['domain']).to eq('example.com') + end + + it 'returns group information' do + cluster_group = json_response['group'] + + expect(cluster_group['id']).to eq(group.id) + expect(cluster_group['name']).to eq(group.name) + expect(cluster_group['web_url']).to eq(group.web_url) + end + + it 'returns kubernetes platform information' do + platform = json_response['platform_kubernetes'] + + expect(platform['api_url']).to eq('https://kubernetes.example.com') + expect(platform['ca_cert']).to be_present + end + + it 'returns user information' do + user = json_response['user'] + + expect(user['id']).to eq(current_user.id) + expect(user['username']).to eq(current_user.username) + end + + it 'returns GCP provider information' do + gcp_provider = json_response['provider_gcp'] + + expect(gcp_provider['cluster_id']).to eq(cluster.id) + expect(gcp_provider['status_name']).to eq('created') + expect(gcp_provider['gcp_project_id']).to eq('test-gcp-project') + expect(gcp_provider['zone']).to eq('us-central1-a') + expect(gcp_provider['machine_type']).to eq('n1-standard-2') + expect(gcp_provider['num_nodes']).to eq(3) + expect(gcp_provider['endpoint']).to eq('111.111.111.111') + end + + context 'when cluster has no provider' do + let(:cluster) do + create(:cluster, :group, :provided_by_user, + groups: [group]) + end + + it 'does not include GCP provider info' do + expect(json_response['provider_gcp']).not_to be_present + end + end + + context 'with non-existing cluster' do + let(:cluster_id) { 123 } + + it 'returns 404' do + expect(response).to have_gitlab_http_status(404) + end + end + end + end + + shared_context 'kubernetes calls stubbed' do + before do + stub_kubeclient_discover(api_url) + end + end + + describe 'POST /groups/:id/clusters/user' do + include_context 'kubernetes calls stubbed' + + let(:api_url) { 'https://kubernetes.example.com' } + let(:authorization_type) { 'rbac' } + + let(:platform_kubernetes_attributes) do + { + api_url: api_url, + token: 'sample-token', + authorization_type: authorization_type + } + end + + let(:cluster_params) do + { + name: 'test-cluster', + domain: 'domain.example.com', + managed: false, + platform_kubernetes_attributes: platform_kubernetes_attributes + } + end + + context 'non-authorized user' do + it 'responds with 403' do + post api("/groups/#{group.id}/clusters/user", developer_user), params: cluster_params + + expect(response).to have_gitlab_http_status(403) + end + end + + context 'authorized user' do + before do + post api("/groups/#{group.id}/clusters/user", current_user), params: cluster_params + end + + context 'with valid params' do + it 'responds with 201' do + expect(response).to have_gitlab_http_status(201) + end + + it 'creates a new Cluster::Cluster' do + cluster_result = Clusters::Cluster.find(json_response["id"]) + platform_kubernetes = cluster_result.platform + + expect(cluster_result).to be_user + expect(cluster_result).to be_kubernetes + expect(cluster_result.group).to eq(group) + expect(cluster_result.name).to eq('test-cluster') + expect(cluster_result.domain).to eq('domain.example.com') + expect(cluster_result.managed).to be_falsy + expect(platform_kubernetes.rbac?).to be_truthy + expect(platform_kubernetes.api_url).to eq(api_url) + expect(platform_kubernetes.token).to eq('sample-token') + end + end + + context 'when user does not indicate authorization type' do + let(:platform_kubernetes_attributes) do + { + api_url: api_url, + token: 'sample-token' + } + end + + it 'defaults to RBAC' do + cluster_result = Clusters::Cluster.find(json_response['id']) + + expect(cluster_result.platform_kubernetes.rbac?).to be_truthy + end + end + + context 'when user sets authorization type as ABAC' do + let(:authorization_type) { 'abac' } + + it 'creates an ABAC cluster' do + cluster_result = Clusters::Cluster.find(json_response['id']) + + expect(cluster_result.platform.abac?).to be_truthy + end + end + + context 'with invalid params' do + let(:api_url) { 'invalid_api_url' } + + it 'responds with 400' do + expect(response).to have_gitlab_http_status(400) + end + + it 'does not create a new Clusters::Cluster' do + expect(group.reload.clusters).to be_empty + end + + it 'returns validation errors' do + expect(json_response['message']['platform_kubernetes.api_url'].first).to be_present + end + end + end + + context 'when user tries to add multiple clusters' do + before do + create(:cluster, :provided_by_gcp, :group, + groups: [group]) + + post api("/groups/#{group.id}/clusters/user", current_user), params: cluster_params + end + + it 'responds with 400' do + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']['base'].first).to include('Instance does not support multiple Kubernetes clusters') + end + end + + context 'non-authorized user' do + before do + post api("/groups/#{group.id}/clusters/user", developer_user), params: cluster_params + end + + it 'responds with 403' do + expect(response).to have_gitlab_http_status(403) + + expect(json_response['message']).to eq('403 Forbidden') + end + end + end + + describe 'PUT /groups/:id/clusters/:cluster_id' do + include_context 'kubernetes calls stubbed' + + let(:api_url) { 'https://kubernetes.example.com' } + + let(:update_params) do + { + domain: domain, + platform_kubernetes_attributes: platform_kubernetes_attributes + } + end + + let(:domain) { 'new-domain.com' } + let(:platform_kubernetes_attributes) { {} } + + let(:cluster) do + create(:cluster, :group, :provided_by_gcp, + groups: [group], domain: 'old-domain.com') + end + + context 'non-authorized user' do + it 'responds with 403' do + put api("/groups/#{group.id}/clusters/#{cluster.id}", developer_user), params: update_params + + expect(response).to have_gitlab_http_status(403) + end + end + + context 'authorized user' do + before do + put api("/groups/#{group.id}/clusters/#{cluster.id}", current_user), params: update_params + + cluster.reload + end + + context 'with valid params' do + it 'responds with 200' do + expect(response).to have_gitlab_http_status(200) + end + + it 'updates cluster attributes' do + expect(cluster.domain).to eq('new-domain.com') + end + end + + context 'with invalid params' do + let(:domain) { 'invalid domain' } + + it 'responds with 400' do + expect(response).to have_gitlab_http_status(400) + end + + it 'does not update cluster attributes' do + expect(cluster.domain).to eq('old-domain.com') + end + + it 'returns validation errors' do + expect(json_response['message']['domain'].first).to match('contains invalid characters (valid characters: [a-z0-9\\-])') + end + end + + context 'with a GCP cluster' do + context 'when user tries to change GCP specific fields' do + let(:platform_kubernetes_attributes) do + { + api_url: 'https://new-api-url.com', + token: 'new-sample-token' + } + end + + it 'responds with 400' do + expect(response).to have_gitlab_http_status(400) + end + + it 'returns validation error' do + expect(json_response['message']['platform_kubernetes.base'].first).to eq('Cannot modify managed Kubernetes cluster') + end + end + + context 'when user tries to change domain' do + let(:domain) { 'new-domain.com' } + + it 'responds with 200' do + expect(response).to have_gitlab_http_status(200) + end + end + end + + context 'with an user cluster' do + let(:api_url) { 'https://new-api-url.com' } + + let(:cluster) do + create(:cluster, :group, :provided_by_user, + groups: [group]) + end + + let(:platform_kubernetes_attributes) do + { + api_url: api_url, + token: 'new-sample-token' + } + end + + let(:update_params) do + { + name: 'new-name', + platform_kubernetes_attributes: platform_kubernetes_attributes + } + end + + it 'responds with 200' do + expect(response).to have_gitlab_http_status(200) + end + + it 'updates platform kubernetes attributes' do + platform_kubernetes = cluster.platform_kubernetes + + expect(cluster.name).to eq('new-name') + expect(platform_kubernetes.api_url).to eq('https://new-api-url.com') + expect(platform_kubernetes.token).to eq('new-sample-token') + end + end + + context 'with a cluster that does not belong to user' do + let(:cluster) { create(:cluster, :group, :provided_by_user) } + + it 'responds with 404' do + expect(response).to have_gitlab_http_status(404) + end + end + end + end + + describe 'DELETE /groups/:id/clusters/:cluster_id' do + let(:cluster_params) { { cluster_id: cluster.id } } + + let(:cluster) do + create(:cluster, :group, :provided_by_gcp, + groups: [group]) + end + + context 'non-authorized user' do + it 'responds with 403' do + delete api("/groups/#{group.id}/clusters/#{cluster.id}", developer_user), params: cluster_params + + expect(response).to have_gitlab_http_status(403) + end + end + + context 'authorized user' do + before do + delete api("/groups/#{group.id}/clusters/#{cluster.id}", current_user), params: cluster_params + end + + it 'responds with 204' do + expect(response).to have_gitlab_http_status(204) + end + + it 'deletes the cluster' do + expect(Clusters::Cluster.exists?(id: cluster.id)).to be_falsy + end + + context 'with a cluster that does not belong to user' do + let(:cluster) { create(:cluster, :group, :provided_by_user) } + + it 'responds with 404' do + expect(response).to have_gitlab_http_status(404) + end + end + end + end +end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index fcbff19bd61..3ab1818bebb 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -474,7 +474,7 @@ describe API::Internal do 'ssh', { authentication_abilities: [:read_project, :download_code, :push_code], - namespace_path: project.namespace.name, + namespace_path: project.namespace.path, project_path: project.path, redirected_path: nil } @@ -753,7 +753,7 @@ describe API::Internal do end describe 'GET /internal/merge_request_urls' do - let(:repo_name) { "#{project.namespace.name}/#{project.path}" } + let(:repo_name) { "#{project.full_path}" } let(:changes) { URI.escape("#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch") } before do @@ -765,7 +765,7 @@ describe API::Internal do expect(json_response).to match [{ "branch_name" => "new_branch", - "url" => "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch", + "url" => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch", "new_merge_request" => true }] end @@ -786,7 +786,7 @@ describe API::Internal do expect(json_response).to match [{ "branch_name" => "new_branch", - "url" => "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch", + "url" => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch", "new_merge_request" => true }] end @@ -927,7 +927,7 @@ describe API::Internal do expect(json_response['merge_request_urls']).to match [{ "branch_name" => branch_name, - "url" => "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}", + "url" => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}", "new_merge_request" => true }] end @@ -970,7 +970,7 @@ describe API::Internal do expect(json_response['merge_request_urls']).to match [{ 'branch_name' => branch_name, - 'url' => "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/1", + 'url' => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/1", 'new_merge_request' => false }] end diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb index a6e08ab3ab6..e8ed016db69 100644 --- a/spec/requests/api/project_clusters_spec.rb +++ b/spec/requests/api/project_clusters_spec.rb @@ -257,12 +257,22 @@ describe API::ProjectClusters do post api("/projects/#{project.id}/clusters/user", current_user), params: cluster_params end + it 'responds with 400' do + expect(response).to have_gitlab_http_status(400) + + expect(json_response['message']['base'].first).to eq('Instance does not support multiple Kubernetes clusters') + end + end + + context 'non-authorized user' do + before do + post api("/projects/#{project.id}/clusters/user", developer_user), params: cluster_params + end + it 'responds with 403' do expect(response).to have_gitlab_http_status(403) - end - it 'returns an appropriate message' do - expect(json_response['message']).to include('Instance does not support multiple Kubernetes clusters') + expect(json_response['message']).to eq('403 Forbidden') end end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index c67412a44c1..a2aae257352 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1102,6 +1102,12 @@ describe API::Projects do expect(json_response['wiki_enabled']).to be_present expect(json_response['jobs_enabled']).to be_present expect(json_response['snippets_enabled']).to be_present + expect(json_response['snippets_access_level']).to be_present + expect(json_response['repository_access_level']).to be_present + expect(json_response['issues_access_level']).to be_present + expect(json_response['merge_requests_access_level']).to be_present + expect(json_response['wiki_access_level']).to be_present + expect(json_response['builds_access_level']).to be_present expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions) expect(json_response['container_registry_enabled']).to be_present expect(json_response['created_at']).to be_present @@ -1913,6 +1919,34 @@ describe API::Projects do end end + it 'updates builds_access_level' do + project_param = { builds_access_level: 'private' } + + put api("/projects/#{project3.id}", user), params: project_param + + expect(response).to have_gitlab_http_status(200) + + expect(json_response['builds_access_level']).to eq('private') + end + + it 'updates build_git_strategy' do + project_param = { build_git_strategy: 'clone' } + + put api("/projects/#{project3.id}", user), params: project_param + + expect(response).to have_gitlab_http_status(200) + + expect(json_response['build_git_strategy']).to eq('clone') + end + + it 'rejects to update build_git_strategy when build_git_strategy is invalid' do + project_param = { build_git_strategy: 'invalid' } + + put api("/projects/#{project3.id}", user), params: project_param + + expect(response).to have_gitlab_http_status(400) + end + it 'updates merge_method' do project_param = { merge_method: 'ff' } @@ -1946,6 +1980,26 @@ describe API::Projects do '-/system/project/avatar/'\ "#{project3.id}/banana_sample.gif") end + + it 'updates auto_devops_deploy_strategy' do + project_param = { auto_devops_deploy_strategy: 'timed_incremental' } + + put api("/projects/#{project3.id}", user), params: project_param + + expect(response).to have_gitlab_http_status(200) + + expect(json_response['auto_devops_deploy_strategy']).to eq('timed_incremental') + end + + it 'updates auto_devops_enabled' do + project_param = { auto_devops_enabled: false } + + put api("/projects/#{project3.id}", user), params: project_param + + expect(response).to have_gitlab_http_status(200) + + expect(json_response['auto_devops_enabled']).to eq(false) + end end context 'when authenticated as project maintainer' do diff --git a/spec/requests/api/user_counts_spec.rb b/spec/requests/api/user_counts_spec.rb new file mode 100644 index 00000000000..c833bd047e2 --- /dev/null +++ b/spec/requests/api/user_counts_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe API::UserCounts do + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + + let!(:merge_request) { create(:merge_request, :simple, author: user, assignees: [user], source_project: project, title: "Test") } + + describe 'GET /user_counts' do + context 'when unauthenticated' do + it 'returns authentication error' do + get api('/user_counts') + + expect(response.status).to eq(401) + end + end + + context 'when authenticated' do + it 'returns open counts for current user' do + get api('/user_counts', user) + + expect(response.status).to eq(200) + expect(json_response).to be_a Hash + expect(json_response['merge_requests']).to eq(1) + end + + it 'updates the mr count when a new mr is assigned' do + create(:merge_request, source_project: project, author: user, assignees: [user]) + + get api('/user_counts', user) + + expect(response.status).to eq(200) + expect(json_response).to be_a Hash + expect(json_response['merge_requests']).to eq(2) + end + end + end +end diff --git a/spec/routing/api_routing_spec.rb b/spec/routing/api_routing_spec.rb deleted file mode 100644 index 3c48ead4ff2..00000000000 --- a/spec/routing/api_routing_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'spec_helper' - -describe 'api', 'routing' do - context 'when graphql is disabled' do - before do - stub_feature_flags(graphql: false) - end - - it 'does not route to the GraphqlController' do - expect(post('/api/graphql')).not_to route_to('graphql#execute') - end - end - - context 'when graphql is enabled' do - before do - stub_feature_flags(graphql: true) - end - - it 'routes to the GraphqlController' do - expect(post('/api/graphql')).to route_to('graphql#execute') - end - end -end diff --git a/spec/routing/environments_spec.rb b/spec/routing/environments_spec.rb index aacbe300966..28b3e79c1ff 100644 --- a/spec/routing/environments_spec.rb +++ b/spec/routing/environments_spec.rb @@ -9,7 +9,7 @@ describe 'environments routing' do end let(:environments_route) do - "#{project.namespace.name}/#{project.name}/environments/" + "#{project.full_path}/environments/" end describe 'routing environment folders' do @@ -36,13 +36,12 @@ describe 'environments routing' do end def get_folder(folder) - get("#{project.namespace.name}/#{project.name}/" \ - "environments/folders/#{folder}") + get("#{project.full_path}/environments/folders/#{folder}") end def folder_action(**opts) - options = { namespace_id: project.namespace.name, - project_id: project.name } + options = { namespace_id: project.namespace.path, + project_id: project.path } ['projects/environments#folder', options.merge(opts)] end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 6dde40d1cb6..8a3de2a52fc 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -713,4 +713,10 @@ describe 'project routing' do end end end + + describe Projects::DeployTokensController, 'routing' do + it 'routes to deploy_tokens#revoke' do + expect(put("/gitlab/gitlabhq/-/deploy_tokens/1/revoke")).to route_to("projects/deploy_tokens#revoke", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') + end + end end diff --git a/spec/rubocop/cop/gitlab/rails_logger_spec.rb b/spec/rubocop/cop/gitlab/rails_logger_spec.rb new file mode 100644 index 00000000000..f0158ddcc5c --- /dev/null +++ b/spec/rubocop/cop/gitlab/rails_logger_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'rubocop' +require 'rubocop/rspec/support' +require_relative '../../../../rubocop/cop/gitlab/rails_logger' + +describe RuboCop::Cop::Gitlab::RailsLogger do + include CopHelper + + subject(:cop) { described_class.new } + + it 'flags the use of Rails.logger.error with a constant receiver' do + inspect_source("Rails.logger.error('some error')") + + expect(cop.offenses.size).to eq(1) + end + + it 'flags the use of Rails.logger.info with a constant receiver' do + inspect_source("Rails.logger.info('some info')") + + expect(cop.offenses.size).to eq(1) + end + + it 'flags the use of Rails.logger.warn with a constant receiver' do + inspect_source("Rails.logger.warn('some warning')") + + expect(cop.offenses.size).to eq(1) + end + + it 'does not flag the use of Rails.logger with a constant that is not Rails' do + inspect_source("AppLogger.error('some error')") + + expect(cop.offenses.size).to eq(0) + end + + it 'does not flag the use of logger with a send receiver' do + inspect_source("file_logger.info('important info')") + + expect(cop.offenses.size).to eq(0) + end +end diff --git a/spec/rubocop/cop/qa/element_with_pattern_spec.rb b/spec/rubocop/cop/qa/element_with_pattern_spec.rb index ef20d9a1f26..fee390caa9f 100644 --- a/spec/rubocop/cop/qa/element_with_pattern_spec.rb +++ b/spec/rubocop/cop/qa/element_with_pattern_spec.rb @@ -23,9 +23,9 @@ describe RuboCop::Cop::QA::ElementWithPattern do expect_offense(<<-RUBY) view 'app/views/shared/groups/_search_form.html.haml' do element :groups_filter, 'search_field_tag :filter' - ^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't use a pattern for element, create a corresponding `qa-groups-filter` instead. + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't use a pattern for element, create a corresponding `data-qa-selector=groups_filter` instead. element :groups_filter_placeholder, /Search by name/ - ^^^^^^^^^^^^^^ Don't use a pattern for element, create a corresponding `qa-groups-filter-placeholder` instead. + ^^^^^^^^^^^^^^ Don't use a pattern for element, create a corresponding `data-qa-selector=groups_filter_placeholder` instead. end RUBY end diff --git a/spec/serializers/environment_status_entity_spec.rb b/spec/serializers/environment_status_entity_spec.rb index 8a6a38fe5f8..cb4749f019f 100644 --- a/spec/serializers/environment_status_entity_spec.rb +++ b/spec/serializers/environment_status_entity_spec.rb @@ -9,7 +9,7 @@ describe EnvironmentStatusEntity do let(:project) { deployment.project } let(:merge_request) { create(:merge_request, :deployed_review_app, deployment: deployment) } - let(:environment_status) { EnvironmentStatus.new(environment, merge_request, merge_request.diff_head_sha) } + let(:environment_status) { EnvironmentStatus.new(project, environment, merge_request, merge_request.diff_head_sha) } let(:entity) { described_class.new(environment_status, request: request) } subject { entity.as_json } @@ -55,8 +55,14 @@ describe EnvironmentStatusEntity do before do project.add_maintainer(user) allow(deployment).to receive(:prometheus_adapter).and_return(prometheus_adapter) - allow(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics) allow(entity).to receive(:deployment).and_return(deployment) + + expect_next_instance_of(DeploymentMetrics) do |deployment_metrics| + allow(deployment_metrics).to receive(:prometheus_adapter).and_return(prometheus_adapter) + + allow(prometheus_adapter).to receive(:query) + .with(:deployment, deployment).and_return(simple_metrics) + end end context 'when deployment succeeded' do @@ -64,7 +70,7 @@ describe EnvironmentStatusEntity do it 'returns metrics url' do expect(subject[:metrics_url]) - .to eq("/#{project.namespace.name}/#{project.name}/environments/#{environment.id}/deployments/#{deployment.iid}/metrics") + .to eq("/#{project.full_path}/environments/#{environment.id}/deployments/#{deployment.iid}/metrics") end end diff --git a/spec/services/audit_event_service_spec.rb b/spec/services/audit_event_service_spec.rb index 32fd98e6ef9..e42bff607b2 100644 --- a/spec/services/audit_event_service_spec.rb +++ b/spec/services/audit_event_service_spec.rb @@ -10,11 +10,8 @@ describe AuditEventService do let(:logger) { instance_double(Gitlab::AuditJsonLogger) } describe '#security_event' do - before do - expect(service).to receive(:file_logger).and_return(logger) - end - it 'creates an event and logs to a file' do + expect(service).to receive(:file_logger).and_return(logger) expect(logger).to receive(:info).with(author_id: user.id, entity_id: project.id, entity_type: "Project", @@ -22,5 +19,32 @@ describe AuditEventService do expect { service.security_event }.to change(SecurityEvent, :count).by(1) end + + it 'formats from and to fields' do + service = described_class.new( + user, project, + { + from: true, + to: false, + action: :create, + target_id: 1 + }) + expect(service).to receive(:file_logger).and_return(logger) + expect(logger).to receive(:info).with(author_id: user.id, + entity_type: 'Project', + entity_id: project.id, + from: 'true', + to: 'false', + action: :create, + target_id: 1) + + expect { service.security_event }.to change(SecurityEvent, :count).by(1) + + details = SecurityEvent.last.details + expect(details[:from]).to be true + expect(details[:to]).to be false + expect(details[:action]).to eq(:create) + expect(details[:target_id]).to eq(1) + end end end diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb index 16e2a2fba6b..1bfb5602df2 100644 --- a/spec/services/boards/issues/move_service_spec.rb +++ b/spec/services/boards/issues/move_service_spec.rb @@ -52,5 +52,91 @@ describe Boards::Issues::MoveService do it_behaves_like 'issues move service', true end + + describe '#execute_multiple' do + set(:group) { create(:group) } + set(:user) { create(:user) } + set(:project) { create(:project, namespace: group) } + set(:board1) { create(:board, group: group) } + set(:development) { create(:group_label, group: group, name: 'Development') } + set(:testing) { create(:group_label, group: group, name: 'Testing') } + set(:list1) { create(:list, board: board1, label: development, position: 0) } + set(:list2) { create(:list, board: board1, label: testing, position: 1) } + let(:params) { { board_id: board1.id, from_list_id: list1.id, to_list_id: list2.id } } + + before do + project.add_developer(user) + end + + it 'returns false if list of issues is empty' do + expect(described_class.new(group, user, params).execute_multiple([])).to eq(false) + end + + context 'moving multiple issues' do + let(:issue1) { create(:labeled_issue, project: project, labels: [development]) } + let(:issue2) { create(:labeled_issue, project: project, labels: [development]) } + + it 'moves multiple issues from one list to another' do + expect(described_class.new(group, user, params).execute_multiple([issue1, issue2])).to be_truthy + + expect(issue1.labels).to eq([testing]) + expect(issue2.labels).to eq([testing]) + end + end + + context 'moving a single issue' do + let(:issue1) { create(:labeled_issue, project: project, labels: [development]) } + + it 'moves one issue' do + expect(described_class.new(group, user, params).execute_multiple([issue1])).to be_truthy + + expect(issue1.labels).to eq([testing]) + end + end + + context 'moving issues visually after an existing issue' do + let(:existing_issue) { create(:labeled_issue, project: project, labels: [testing], relative_position: 10) } + let(:issue1) { create(:labeled_issue, project: project, labels: [development]) } + let(:issue2) { create(:labeled_issue, project: project, labels: [development]) } + + let(:move_params) do + params.dup.tap do |hash| + hash[:move_before_id] = existing_issue.id + end + end + + it 'moves one issue' do + expect(described_class.new(group, user, move_params).execute_multiple([issue1, issue2])).to be_truthy + + expect(issue1.labels).to eq([testing]) + expect(issue2.labels).to eq([testing]) + + expect(issue1.relative_position > existing_issue.relative_position).to eq(true) + expect(issue2.relative_position > issue1.relative_position).to eq(true) + end + end + + context 'moving issues visually before an existing issue' do + let(:existing_issue) { create(:labeled_issue, project: project, labels: [testing], relative_position: 10) } + let(:issue1) { create(:labeled_issue, project: project, labels: [development]) } + let(:issue2) { create(:labeled_issue, project: project, labels: [development]) } + + let(:move_params) do + params.dup.tap do |hash| + hash[:move_after_id] = existing_issue.id + end + end + + it 'moves one issue' do + expect(described_class.new(group, user, move_params).execute_multiple([issue1, issue2])).to be_truthy + + expect(issue1.labels).to eq([testing]) + expect(issue2.labels).to eq([testing]) + + expect(issue2.relative_position < existing_issue.relative_position).to eq(true) + expect(issue1.relative_position < issue2.relative_position).to eq(true) + end + end + end end end diff --git a/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb b/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb index be052a07da7..44407ae2793 100644 --- a/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb +++ b/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb @@ -34,6 +34,8 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService, '#execute' d stub_kubeclient_create_service_account(api_url, namespace: namespace) stub_kubeclient_create_secret(api_url, namespace: namespace) stub_kubeclient_put_secret(api_url, "#{namespace}-token", namespace: namespace) + stub_kubeclient_put_role(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME, namespace: namespace) + stub_kubeclient_put_role_binding(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace) stub_kubeclient_get_secret( api_url, diff --git a/spec/services/clusters/gcp/kubernetes/create_or_update_service_account_service_spec.rb b/spec/services/clusters/gcp/kubernetes/create_or_update_service_account_service_spec.rb index 382b9043566..8b874989758 100644 --- a/spec/services/clusters/gcp/kubernetes/create_or_update_service_account_service_spec.rb +++ b/spec/services/clusters/gcp/kubernetes/create_or_update_service_account_service_spec.rb @@ -143,6 +143,8 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do stub_kubeclient_get_role_binding_error(api_url, role_binding_name, namespace: namespace) stub_kubeclient_create_role_binding(api_url, namespace: namespace) + stub_kubeclient_put_role(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME, namespace: namespace) + stub_kubeclient_put_role_binding(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace) end it_behaves_like 'creates service account and token' @@ -169,6 +171,24 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do ) ) end + + it 'creates a role and role binding granting knative serving permissions to the service account' do + subject + + expect(WebMock).to have_requested(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/roles/#{Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME}").with( + body: hash_including( + metadata: { + name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME, + namespace: namespace + }, + rules: [{ + apiGroups: %w(serving.knative.dev), + resources: %w(configurations configurationgenerations routes revisions revisionuids autoscalers services), + verbs: %w(get list create update delete patch watch) + }] + ) + ) + end end end end diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb index aebc5ba2874..b0bcd7a36ba 100644 --- a/spec/services/issuable/bulk_update_service_spec.rb +++ b/spec/services/issuable/bulk_update_service_spec.rb @@ -11,343 +11,371 @@ describe Issuable::BulkUpdateService do .reverse_merge(issuable_ids: Array(issuables).map(&:id).join(',')) type = Array(issuables).first.model_name.param_key - Issuable::BulkUpdateService.new(project, user, bulk_update_params).execute(type) + Issuable::BulkUpdateService.new(user, bulk_update_params).execute(type) end - describe 'close issues' do - let(:issues) { create_list(:issue, 2, project: project) } - - it 'succeeds and returns the correct number of issues updated' do - result = bulk_update(issues, state_event: 'close') + shared_examples 'updates milestones' do + it 'succeeds' do + result = bulk_update(issues, milestone_id: milestone.id) expect(result[:success]).to be_truthy expect(result[:count]).to eq(issues.count) end - it 'closes all the issues passed' do - bulk_update(issues, state_event: 'close') + it 'updates the issues milestone' do + bulk_update(issues, milestone_id: milestone.id) - expect(project.issues.opened).to be_empty - expect(project.issues.closed).not_to be_empty + issues.each do |issue| + expect(issue.reload.milestone).to eq(milestone) + end end + end - context 'when issue for a different project is created' do - let(:private_project) { create(:project, :private) } - let(:issue) { create(:issue, project: private_project, author: user) } + context 'with project issues' do + describe 'close issues' do + let(:issues) { create_list(:issue, 2, project: project) } - context 'when user has access to the project' do - it 'closes all issues passed' do - private_project.add_maintainer(user) + it 'succeeds and returns the correct number of issues updated' do + result = bulk_update(issues, state_event: 'close') - bulk_update(issues + [issue], state_event: 'close') + expect(result[:success]).to be_truthy + expect(result[:count]).to eq(issues.count) + end - expect(project.issues.opened).to be_empty - expect(project.issues.closed).not_to be_empty - expect(private_project.issues.closed).not_to be_empty - end + it 'closes all the issues passed' do + bulk_update(issues, state_event: 'close') + + expect(project.issues.opened).to be_empty + expect(project.issues.closed).not_to be_empty end - context 'when user does not have access to project' do - it 'only closes all issues that the user has access to' do - bulk_update(issues + [issue], state_event: 'close') + context 'when issue for a different project is created' do + let(:private_project) { create(:project, :private) } + let(:issue) { create(:issue, project: private_project, author: user) } + + context 'when user has access to the project' do + it 'closes all issues passed' do + private_project.add_maintainer(user) + + bulk_update(issues + [issue], state_event: 'close') + + expect(project.issues.opened).to be_empty + expect(project.issues.closed).not_to be_empty + expect(private_project.issues.closed).not_to be_empty + end + end + + context 'when user does not have access to project' do + it 'only closes all issues that the user has access to' do + bulk_update(issues + [issue], state_event: 'close') - expect(project.issues.opened).to be_empty - expect(project.issues.closed).not_to be_empty - expect(private_project.issues.closed).to be_empty + expect(project.issues.opened).to be_empty + expect(project.issues.closed).not_to be_empty + expect(private_project.issues.closed).to be_empty + end end end end - end - describe 'reopen issues' do - let(:issues) { create_list(:closed_issue, 2, project: project) } + describe 'reopen issues' do + let(:issues) { create_list(:closed_issue, 2, project: project) } - it 'succeeds and returns the correct number of issues updated' do - result = bulk_update(issues, state_event: 'reopen') + it 'succeeds and returns the correct number of issues updated' do + result = bulk_update(issues, state_event: 'reopen') - expect(result[:success]).to be_truthy - expect(result[:count]).to eq(issues.count) - end + expect(result[:success]).to be_truthy + expect(result[:count]).to eq(issues.count) + end - it 'reopens all the issues passed' do - bulk_update(issues, state_event: 'reopen') + it 'reopens all the issues passed' do + bulk_update(issues, state_event: 'reopen') - expect(project.issues.closed).to be_empty - expect(project.issues.opened).not_to be_empty + expect(project.issues.closed).to be_empty + expect(project.issues.opened).not_to be_empty + end end - end - describe 'updating merge request assignee' do - let(:merge_request) { create(:merge_request, target_project: project, source_project: project, assignees: [user]) } + describe 'updating merge request assignee' do + let(:merge_request) { create(:merge_request, target_project: project, source_project: project, assignees: [user]) } - context 'when the new assignee ID is a valid user' do - it 'succeeds' do - new_assignee = create(:user) - project.add_developer(new_assignee) + context 'when the new assignee ID is a valid user' do + it 'succeeds' do + new_assignee = create(:user) + project.add_developer(new_assignee) - result = bulk_update(merge_request, assignee_ids: [user.id, new_assignee.id]) + result = bulk_update(merge_request, assignee_ids: [user.id, new_assignee.id]) - expect(result[:success]).to be_truthy - expect(result[:count]).to eq(1) - end + expect(result[:success]).to be_truthy + expect(result[:count]).to eq(1) + end - it 'updates the assignee to the user ID passed' do - assignee = create(:user) - project.add_developer(assignee) + it 'updates the assignee to the user ID passed' do + assignee = create(:user) + project.add_developer(assignee) - expect { bulk_update(merge_request, assignee_ids: [assignee.id]) } - .to change { merge_request.reload.assignee_ids }.from([user.id]).to([assignee.id]) + expect { bulk_update(merge_request, assignee_ids: [assignee.id]) } + .to change { merge_request.reload.assignee_ids }.from([user.id]).to([assignee.id]) + end end - end - context "when the new assignee ID is #{IssuableFinder::NONE}" do - it 'unassigns the issues' do - expect { bulk_update(merge_request, assignee_ids: [IssuableFinder::NONE]) } - .to change { merge_request.reload.assignee_ids }.to([]) + context "when the new assignee ID is #{IssuableFinder::NONE}" do + it 'unassigns the issues' do + expect { bulk_update(merge_request, assignee_ids: [IssuableFinder::NONE]) } + .to change { merge_request.reload.assignee_ids }.to([]) + end end - end - context 'when the new assignee ID is not present' do - it 'does not unassign' do - expect { bulk_update(merge_request, assignee_ids: []) } - .not_to change { merge_request.reload.assignee_ids } + context 'when the new assignee ID is not present' do + it 'does not unassign' do + expect { bulk_update(merge_request, assignee_ids: []) } + .not_to change { merge_request.reload.assignee_ids } + end end end - end - describe 'updating issue assignee' do - let(:issue) { create(:issue, project: project, assignees: [user]) } + describe 'updating issue assignee' do + let(:issue) { create(:issue, project: project, assignees: [user]) } - context 'when the new assignee ID is a valid user' do - it 'succeeds' do - new_assignee = create(:user) - project.add_developer(new_assignee) + context 'when the new assignee ID is a valid user' do + it 'succeeds' do + new_assignee = create(:user) + project.add_developer(new_assignee) - result = bulk_update(issue, assignee_ids: [new_assignee.id]) + result = bulk_update(issue, assignee_ids: [new_assignee.id]) - expect(result[:success]).to be_truthy - expect(result[:count]).to eq(1) - end + expect(result[:success]).to be_truthy + expect(result[:count]).to eq(1) + end - it 'updates the assignee to the user ID passed' do - assignee = create(:user) - project.add_developer(assignee) - expect { bulk_update(issue, assignee_ids: [assignee.id]) } - .to change { issue.reload.assignees.first }.from(user).to(assignee) + it 'updates the assignee to the user ID passed' do + assignee = create(:user) + project.add_developer(assignee) + expect { bulk_update(issue, assignee_ids: [assignee.id]) } + .to change { issue.reload.assignees.first }.from(user).to(assignee) + end end - end - context "when the new assignee ID is #{IssuableFinder::NONE}" do - it "unassigns the issues" do - expect { bulk_update(issue, assignee_ids: [IssuableFinder::NONE.to_s]) } - .to change { issue.reload.assignees.count }.from(1).to(0) + context "when the new assignee ID is #{IssuableFinder::NONE}" do + it "unassigns the issues" do + expect { bulk_update(issue, assignee_ids: [IssuableFinder::NONE.to_s]) } + .to change { issue.reload.assignees.count }.from(1).to(0) + end end - end - context 'when the new assignee ID is not present' do - it 'does not unassign' do - expect { bulk_update(issue, assignee_ids: []) } - .not_to change { issue.reload.assignees } + context 'when the new assignee ID is not present' do + it 'does not unassign' do + expect { bulk_update(issue, assignee_ids: []) } + .not_to change(issue.assignees, :count) + end end end - end - - describe 'updating milestones' do - let(:issue) { create(:issue, project: project) } - let(:milestone) { create(:milestone, project: project) } - it 'succeeds' do - result = bulk_update(issue, milestone_id: milestone.id) + describe 'updating milestones' do + let(:issues) { [create(:issue, project: project)] } + let(:milestone) { create(:milestone, project: project) } - expect(result[:success]).to be_truthy - expect(result[:count]).to eq(1) + it_behaves_like 'updates milestones' end - it 'updates the issue milestone' do - expect { bulk_update(issue, milestone_id: milestone.id) } - .to change { issue.reload.milestone }.from(nil).to(milestone) - end - end - - describe 'updating labels' do - def create_issue_with_labels(labels) - create(:labeled_issue, project: project, labels: labels) - end + describe 'updating labels' do + def create_issue_with_labels(labels) + create(:labeled_issue, project: project, labels: labels) + end - let(:bug) { create(:label, project: project) } - let(:regression) { create(:label, project: project) } - let(:merge_requests) { create(:label, project: project) } - - let(:issue_all_labels) { create_issue_with_labels([bug, regression, merge_requests]) } - let(:issue_bug_and_regression) { create_issue_with_labels([bug, regression]) } - let(:issue_bug_and_merge_requests) { create_issue_with_labels([bug, merge_requests]) } - let(:issue_no_labels) { create(:issue, project: project) } - let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests, issue_no_labels] } - - let(:labels) { [] } - let(:add_labels) { [] } - let(:remove_labels) { [] } - - let(:bulk_update_params) do - { - label_ids: labels.map(&:id), - add_label_ids: add_labels.map(&:id), - remove_label_ids: remove_labels.map(&:id) - } - end + let(:bug) { create(:label, project: project) } + let(:regression) { create(:label, project: project) } + let(:merge_requests) { create(:label, project: project) } - before do - bulk_update(issues, bulk_update_params) - end + let(:issue_all_labels) { create_issue_with_labels([bug, regression, merge_requests]) } + let(:issue_bug_and_regression) { create_issue_with_labels([bug, regression]) } + let(:issue_bug_and_merge_requests) { create_issue_with_labels([bug, merge_requests]) } + let(:issue_no_labels) { create(:issue, project: project) } + let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests, issue_no_labels] } - context 'when label_ids are passed' do - let(:issues) { [issue_all_labels, issue_no_labels] } - let(:labels) { [bug, regression] } + let(:labels) { [] } + let(:add_labels) { [] } + let(:remove_labels) { [] } - it 'updates the labels of all issues passed to the labels passed' do - expect(issues.map(&:reload).map(&:label_ids)).to all(match_array(labels.map(&:id))) + let(:bulk_update_params) do + { + label_ids: labels.map(&:id), + add_label_ids: add_labels.map(&:id), + remove_label_ids: remove_labels.map(&:id) + } end - it 'does not update issues not passed in' do - expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) + before do + bulk_update(issues, bulk_update_params) end - context 'when those label IDs are empty' do - let(:labels) { [] } + context 'when label_ids are passed' do + let(:issues) { [issue_all_labels, issue_no_labels] } + let(:labels) { [bug, regression] } - it 'updates the issues passed to have no labels' do - expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty) + it 'updates the labels of all issues passed to the labels passed' do + expect(issues.map(&:reload).map(&:label_ids)).to all(match_array(labels.map(&:id))) end - end - end - context 'when add_label_ids are passed' do - let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] } - let(:add_labels) { [bug, regression, merge_requests] } + it 'does not update issues not passed in' do + expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) + end - it 'adds those label IDs to all issues passed' do - expect(issues.map(&:reload).map(&:label_ids)).to all(include(*add_labels.map(&:id))) - end + context 'when those label IDs are empty' do + let(:labels) { [] } - it 'does not update issues not passed in' do - expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) + it 'updates the issues passed to have no labels' do + expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty) + end + end end - end - context 'when remove_label_ids are passed' do - let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] } - let(:remove_labels) { [bug, regression, merge_requests] } + context 'when add_label_ids are passed' do + let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] } + let(:add_labels) { [bug, regression, merge_requests] } - it 'removes those label IDs from all issues passed' do - expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty) - end + it 'adds those label IDs to all issues passed' do + expect(issues.map(&:reload).map(&:label_ids)).to all(include(*add_labels.map(&:id))) + end - it 'does not update issues not passed in' do - expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) + it 'does not update issues not passed in' do + expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) + end end - end - context 'when add_label_ids and remove_label_ids are passed' do - let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] } - let(:add_labels) { [bug] } - let(:remove_labels) { [merge_requests] } + context 'when remove_label_ids are passed' do + let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] } + let(:remove_labels) { [bug, regression, merge_requests] } - it 'adds the label IDs to all issues passed' do - expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id)) - end + it 'removes those label IDs from all issues passed' do + expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty) + end - it 'removes the label IDs from all issues passed' do - expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id) + it 'does not update issues not passed in' do + expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) + end end - it 'does not update issues not passed in' do - expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) - end - end + context 'when add_label_ids and remove_label_ids are passed' do + let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] } + let(:add_labels) { [bug] } + let(:remove_labels) { [merge_requests] } - context 'when add_label_ids and label_ids are passed' do - let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests] } - let(:labels) { [merge_requests] } - let(:add_labels) { [regression] } + it 'adds the label IDs to all issues passed' do + expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id)) + end - it 'adds the label IDs to all issues passed' do - expect(issues.map(&:reload).map(&:label_ids)).to all(include(regression.id)) - end + it 'removes the label IDs from all issues passed' do + expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id) + end - it 'ignores the label IDs parameter' do - expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id)) + it 'does not update issues not passed in' do + expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) + end end - it 'does not update issues not passed in' do - expect(issue_no_labels.label_ids).to be_empty - end - end + context 'when add_label_ids and label_ids are passed' do + let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests] } + let(:labels) { [merge_requests] } + let(:add_labels) { [regression] } - context 'when remove_label_ids and label_ids are passed' do - let(:issues) { [issue_no_labels, issue_bug_and_regression] } - let(:labels) { [merge_requests] } - let(:remove_labels) { [regression] } + it 'adds the label IDs to all issues passed' do + expect(issues.map(&:reload).map(&:label_ids)).to all(include(regression.id)) + end - it 'removes the label IDs from all issues passed' do - expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id) - end + it 'ignores the label IDs parameter' do + expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id)) + end - it 'ignores the label IDs parameter' do - expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id) + it 'does not update issues not passed in' do + expect(issue_no_labels.label_ids).to be_empty + end end - it 'does not update issues not passed in' do - expect(issue_all_labels.label_ids).to contain_exactly(bug.id, regression.id, merge_requests.id) - end - end + context 'when remove_label_ids and label_ids are passed' do + let(:issues) { [issue_no_labels, issue_bug_and_regression] } + let(:labels) { [merge_requests] } + let(:remove_labels) { [regression] } - context 'when add_label_ids, remove_label_ids, and label_ids are passed' do - let(:issues) { [issue_bug_and_merge_requests, issue_no_labels] } - let(:labels) { [regression] } - let(:add_labels) { [bug] } - let(:remove_labels) { [merge_requests] } + it 'removes the label IDs from all issues passed' do + expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id) + end - it 'adds the label IDs to all issues passed' do - expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id)) - end + it 'ignores the label IDs parameter' do + expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id) + end - it 'removes the label IDs from all issues passed' do - expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id) + it 'does not update issues not passed in' do + expect(issue_all_labels.label_ids).to contain_exactly(bug.id, regression.id, merge_requests.id) + end end - it 'ignores the label IDs parameter' do - expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id) - end + context 'when add_label_ids, remove_label_ids, and label_ids are passed' do + let(:issues) { [issue_bug_and_merge_requests, issue_no_labels] } + let(:labels) { [regression] } + let(:add_labels) { [bug] } + let(:remove_labels) { [merge_requests] } - it 'does not update issues not passed in' do - expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) + it 'adds the label IDs to all issues passed' do + expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id)) + end + + it 'removes the label IDs from all issues passed' do + expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id) + end + + it 'ignores the label IDs parameter' do + expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id) + end + + it 'does not update issues not passed in' do + expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id) + end end end - end - describe 'subscribe to issues' do - let(:issues) { create_list(:issue, 2, project: project) } + describe 'subscribe to issues' do + let(:issues) { create_list(:issue, 2, project: project) } - it 'subscribes the given user' do - bulk_update(issues, subscription_event: 'subscribe') + it 'subscribes the given user' do + bulk_update(issues, subscription_event: 'subscribe') - expect(issues).to all(be_subscribed(user, project)) + expect(issues).to all(be_subscribed(user, project)) + end end - end - describe 'unsubscribe from issues' do - let(:issues) do - create_list(:closed_issue, 2, project: project) do |issue| - issue.subscriptions.create(user: user, project: project, subscribed: true) + describe 'unsubscribe from issues' do + let(:issues) do + create_list(:closed_issue, 2, project: project) do |issue| + issue.subscriptions.create(user: user, project: project, subscribed: true) + end + end + + it 'unsubscribes the given user' do + bulk_update(issues, subscription_event: 'unsubscribe') + + issues.each do |issue| + expect(issue).not_to be_subscribed(user, project) + end end end + end - it 'unsubscribes the given user' do - bulk_update(issues, subscription_event: 'unsubscribe') + context 'with group issues' do + let(:group) { create(:group) } - issues.each do |issue| - expect(issue).not_to be_subscribed(user, project) + context 'updating milestone' do + let(:milestone) { create(:milestone, group: group) } + let(:project1) { create(:project, :repository, group: group) } + let(:project2) { create(:project, :repository, group: group) } + let(:issue1) { create(:issue, project: project1) } + let(:issue2) { create(:issue, project: project2) } + let(:issues) { [issue1, issue2] } + + before do + group.add_maintainer(user) end + + it_behaves_like 'updates milestones' end end end diff --git a/spec/services/issuable/clone/content_rewriter_spec.rb b/spec/services/issuable/clone/content_rewriter_spec.rb index 230e1123280..3479c20862a 100644 --- a/spec/services/issuable/clone/content_rewriter_spec.rb +++ b/spec/services/issuable/clone/content_rewriter_spec.rb @@ -165,5 +165,18 @@ describe Issuable::Clone::ContentRewriter do expect(note.note_html).not_to eq(new_note.note_html) end end + + context "discussion notes" do + let(:note) { create(:note, noteable: original_issue, note: "sample note", project: project1) } + let!(:discussion) { create(:discussion_note_on_issue, in_reply_to: note, note: "reply to sample note") } + + it 'rewrites discussion correctly' do + subject.execute + + expect(new_issue.notes.count).to eq(original_issue.notes.count) + expect(new_issue.notes.where(discussion_id: discussion.discussion_id).count).to eq(0) + expect(original_issue.notes.where(discussion_id: discussion.discussion_id).count).to eq(1) + end + end end end diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 28fa5d12d9c..468e7c286d5 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -480,6 +480,22 @@ describe Issues::UpdateService, :mailer do update_issue(description: "- [x] Task 1\n- [X] Task 2") end + it 'does not check for spam on task status change' do + params = { + update_task: { + index: 1, + checked: false, + line_source: '- [x] Task 1', + line_number: 1 + } + } + service = described_class.new(project, user, params) + + expect(service).not_to receive(:spam_check) + + service.execute(issue) + end + it 'creates system note about task status change' do note1 = find_note('marked the task **Task 1** as completed') note2 = find_note('marked the task **Task 2** as completed') diff --git a/spec/services/merge_requests/get_urls_service_spec.rb b/spec/services/merge_requests/get_urls_service_spec.rb index 0933c6d4336..9e7a5260ca4 100644 --- a/spec/services/merge_requests/get_urls_service_spec.rb +++ b/spec/services/merge_requests/get_urls_service_spec.rb @@ -8,8 +8,8 @@ describe MergeRequests::GetUrlsService do let(:project) { create(:project, :public, :repository) } let(:service) { described_class.new(project) } let(:source_branch) { "merge-test" } - let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" } - let(:show_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/#{merge_request.iid}" } + let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" } + let(:show_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/#{merge_request.iid}" } let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" } let(:deleted_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 #{Gitlab::Git::BLANK_SHA} refs/heads/#{source_branch}" } let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" } @@ -119,7 +119,7 @@ describe MergeRequests::GetUrlsService do let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch" } let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/markdown" } let(:changes) { "#{new_branch_changes}\n#{existing_branch_changes}" } - let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch" } + let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch" } it 'returns 2 urls for both creating new and showing merge request' do result = service.execute(changes) diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index aa759ac9edc..22578436c18 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -214,6 +214,19 @@ describe MergeRequests::MergeService do allow(Rails.logger).to receive(:error) end + context 'when source is missing' do + it 'logs and saves error' do + allow(merge_request).to receive(:diff_head_sha) { nil } + + error_message = 'No source for merge' + + service.execute(merge_request) + + expect(merge_request.merge_error).to eq(error_message) + expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message)) + end + end + it 'logs and saves error if there is an exception' do error_message = 'error message' diff --git a/spec/services/merge_requests/merge_to_ref_service_spec.rb b/spec/services/merge_requests/merge_to_ref_service_spec.rb index e2f201677fa..758679edc45 100644 --- a/spec/services/merge_requests/merge_to_ref_service_spec.rb +++ b/spec/services/merge_requests/merge_to_ref_service_spec.rb @@ -191,6 +191,28 @@ describe MergeRequests::MergeToRefService do it { expect(todo).not_to be_done } end + context 'when source is missing' do + it 'returns error' do + allow(merge_request).to receive(:diff_head_sha) { nil } + + error_message = 'No source for merge' + + result = service.execute(merge_request) + + expect(result[:status]).to eq(:error) + expect(result[:message]).to eq(error_message) + end + end + + context 'when target ref is passed as a parameter' do + let(:params) { { commit_message: 'merge train', target_ref: target_ref } } + + it_behaves_like 'successfully merges to ref with merge method' do + let(:first_parent_ref) { 'refs/heads/master' } + let(:target_ref) { 'refs/merge-requests/1/train' } + end + end + describe 'cascading merge refs' do set(:project) { create(:project, :repository) } let(:params) { { commit_message: 'Cascading merge', first_parent_ref: first_parent_ref, target_ref: target_ref } } diff --git a/spec/services/merge_requests/mergeability_check_service_spec.rb b/spec/services/merge_requests/mergeability_check_service_spec.rb index 6efece64092..6e827f2ea5b 100644 --- a/spec/services/merge_requests/mergeability_check_service_spec.rb +++ b/spec/services/merge_requests/mergeability_check_service_spec.rb @@ -11,7 +11,11 @@ describe MergeRequests::MergeabilityCheckService do end it 'does not change the merge ref HEAD' do - expect { subject }.not_to change(merge_request, :merge_ref_head) + merge_ref_head = merge_request.merge_ref_head + + subject + + expect(merge_request.reload.merge_ref_head).to eq merge_ref_head end it 'returns ServiceResponse.error' do @@ -73,7 +77,7 @@ describe MergeRequests::MergeabilityCheckService do it 'second call does not change the merge-ref' do expect { subject }.to change(merge_request, :merge_ref_head).from(nil) - expect { subject }.not_to change(merge_request, :merge_ref_head) + expect { subject }.not_to change(merge_request.merge_ref_head, :id) end end diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index f566d235787..7e5837a4798 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -598,7 +598,7 @@ describe MergeRequests::UpdateService, :mailer do feature_visibility_attr = :"#{merge_request.model_name.plural}_access_level" project.project_feature.update_attribute(feature_visibility_attr, ProjectFeature::PRIVATE) - expect { update_merge_request(assignee_ids: [assignee]) }.not_to change { merge_request.reload.assignees } + expect { update_merge_request(assignee_ids: [assignee]) }.not_to change(merge_request.assignees, :count) end end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index f25e2fe5e2b..3e3de051732 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -215,13 +215,14 @@ describe NotificationService, :mailer do let(:project) { create(:project, :private) } let(:issue) { create(:issue, project: project, assignees: [assignee]) } let(:mentioned_issue) { create(:issue, assignees: issue.assignees) } - let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced, @unsubscribed_mentioned and @outsider also') } + let(:author) { create(:user) } + let(:note) { create(:note_on_issue, author: author, noteable: issue, project_id: issue.project_id, note: '@mention referenced, @unsubscribed_mentioned and @outsider also') } before do - build_team(note.project) + build_team(project) project.add_maintainer(issue.author) project.add_maintainer(assignee) - project.add_maintainer(note.author) + project.add_maintainer(author) @u_custom_off = create_user_with_notification(:custom, 'custom_off') project.add_guest(@u_custom_off) @@ -240,7 +241,8 @@ describe NotificationService, :mailer do describe '#new_note' do it do - add_users_with_subscription(note.project, issue) + add_users(project) + add_user_subscriptions(issue) reset_delivered_emails! expect(SentNotification).to receive(:record).with(issue, any_args).exactly(10).times @@ -268,7 +270,8 @@ describe NotificationService, :mailer do end it "emails the note author if they've opted into notifications about their activity" do - add_users_with_subscription(note.project, issue) + add_users(project) + add_user_subscriptions(issue) reset_delivered_emails! note.author.notified_of_own_activity = true @@ -415,13 +418,15 @@ describe NotificationService, :mailer do let(:project) { create(:project, :public) } let(:issue) { create(:issue, project: project, assignees: [assignee]) } let(:mentioned_issue) { create(:issue, assignees: issue.assignees) } - let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@all mentioned') } + let(:author) { create(:user) } + let(:note) { create(:note_on_issue, author: author, noteable: issue, project_id: issue.project_id, note: '@all mentioned') } before do - build_team(note.project) - build_group(note.project) - note.project.add_maintainer(note.author) - add_users_with_subscription(note.project, issue) + build_team(project) + build_group(project) + add_users(project) + add_user_subscriptions(issue) + project.add_maintainer(author) reset_delivered_emails! end @@ -473,17 +478,18 @@ describe NotificationService, :mailer do context 'project snippet note' do let!(:project) { create(:project, :public) } let(:snippet) { create(:project_snippet, project: project, author: create(:user)) } - let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: project.id, note: '@all mentioned') } + let(:author) { create(:user) } + let(:note) { create(:note_on_project_snippet, author: author, noteable: snippet, project_id: project.id, note: '@all mentioned') } before do build_team(project) build_group(project) + project.add_maintainer(author) # make sure these users can read the project snippet! project.add_guest(@u_guest_watcher) project.add_guest(@u_guest_custom) add_member_for_parent_group(@pg_watcher, project) - note.project.add_maintainer(note.author) reset_delivered_emails! end @@ -708,10 +714,11 @@ describe NotificationService, :mailer do let(:issue) { create :issue, project: project, assignees: [assignee], description: 'cc @participant @unsubscribed_mentioned' } before do - build_team(issue.project) - build_group(issue.project) + build_team(project) + build_group(project) - add_users_with_subscription(issue.project, issue) + add_users(project) + add_user_subscriptions(issue) reset_delivered_emails! update_custom_notification(:new_issue, @u_guest_custom, resource: project) update_custom_notification(:new_issue, @u_custom_global) @@ -1281,13 +1288,16 @@ describe NotificationService, :mailer do let(:project) { create(:project, :public, :repository, namespace: group) } let(:another_project) { create(:project, :public, namespace: group) } let(:assignee) { create(:user) } - let(:merge_request) { create :merge_request, source_project: project, assignees: [assignee], description: 'cc @participant' } + let(:assignees) { Array.wrap(assignee) } + let(:author) { create(:user) } + let(:merge_request) { create :merge_request, author: author, source_project: project, assignees: assignees, description: 'cc @participant' } before do - project.add_maintainer(merge_request.author) - merge_request.assignees.each { |assignee| project.add_maintainer(assignee) } - build_team(merge_request.target_project) - add_users_with_subscription(merge_request.target_project, merge_request) + project.add_maintainer(author) + assignees.each { |assignee| project.add_maintainer(assignee) } + build_team(project) + add_users(project) + add_user_subscriptions(merge_request) update_custom_notification(:new_merge_request, @u_guest_custom, resource: project) update_custom_notification(:new_merge_request, @u_custom_global) reset_delivered_emails! @@ -1834,7 +1844,7 @@ describe NotificationService, :mailer do describe 'ProjectMember' do let(:project) { create(:project) } - set(:added_user) { create(:user) } + let(:added_user) { create(:user) } describe '#new_access_request' do context 'for a project in a user namespace' do @@ -2417,7 +2427,7 @@ describe NotificationService, :mailer do should_not_email(user, recipients: email_recipients) end - def add_users_with_subscription(project, issuable) + def add_users(project) @subscriber = create :user @unsubscriber = create :user @unsubscribed_mentioned = create :user, username: 'unsubscribed_mentioned' @@ -2429,7 +2439,9 @@ describe NotificationService, :mailer do project.add_maintainer(@unsubscriber) project.add_maintainer(@watcher_and_subscriber) project.add_maintainer(@unsubscribed_mentioned) + end + def add_user_subscriptions(issuable) issuable.subscriptions.create(user: @unsubscribed_mentioned, project: project, subscribed: false) issuable.subscriptions.create(user: @subscriber, project: project, subscribed: true) issuable.subscriptions.create(user: @subscribed_participant, project: project, subscribed: true) diff --git a/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb index 8d43ce4f662..af79a42b611 100644 --- a/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb +++ b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb @@ -12,6 +12,12 @@ describe PagesDomains::ObtainLetsEncryptCertificateService do stub_lets_encrypt_settings end + around do |example| + Sidekiq::Testing.fake! do + example.run + end + end + def expect_to_create_acme_challenge expect(::PagesDomains::CreateAcmeOrderService).to receive(:new).with(pages_domain) .and_wrap_original do |m, *args| diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 62fdc039b5e..95e0d8858b9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -47,7 +47,7 @@ Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } quality_level = Quality::TestLevel.new RSpec.configure do |config| - config.use_transactional_fixtures = false + config.use_transactional_fixtures = true config.use_instantiated_fixtures = false config.fixture_path = Rails.root @@ -103,6 +103,7 @@ RSpec.configure do |config| config.include RedisHelpers config.include Rails.application.routes.url_helpers, type: :routing config.include PolicyHelpers, type: :policy + config.include MemoryUsageHelper if ENV['CI'] # This includes the first try, i.e. tests will be run 4 times before failing. @@ -289,6 +290,16 @@ RSpec.configure do |config| config.before(:each, :https_pages_disabled) do |_| allow(Gitlab.config.pages).to receive(:external_https).and_return(false) end + + # We can't use an `around` hook here because the wrapping transaction + # is not yet opened at the time that is triggered + config.prepend_before do + Gitlab::Database.set_open_transactions_baseline + end + + config.append_after do + Gitlab::Database.reset_open_transactions_baseline + end end # add simpler way to match asset paths containing digest strings diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb index edd7de94203..f0dd6c52b74 100644 --- a/spec/support/database_cleaner.rb +++ b/spec/support/database_cleaner.rb @@ -26,31 +26,22 @@ RSpec.configure do |config| end config.append_after(:context) do - DatabaseCleaner.clean_with(:deletion, cache_tables: false) + delete_from_all_tables! end - config.before do - setup_database_cleaner - DatabaseCleaner.strategy = :transaction - end + config.around(:each, :delete) do |example| + self.class.use_transactional_tests = false - config.before(:each, :js) do - DatabaseCleaner.strategy = :deletion, { except: deletion_except_tables, cache_tables: false } - end + example.run - config.before(:each, :delete) do - DatabaseCleaner.strategy = :deletion, { except: deletion_except_tables, cache_tables: false } + delete_from_all_tables!(except: deletion_except_tables) end - config.before(:each, :migration) do - DatabaseCleaner.strategy = :deletion, { cache_tables: false } - end + config.around(:each, :migration) do |example| + self.class.use_transactional_tests = false - config.before do - DatabaseCleaner.start - end + example.run - config.append_after do - DatabaseCleaner.clean + delete_from_all_tables! end end diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index c69fa322073..08622dff6d9 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -1,4 +1,8 @@ module DbCleaner + def delete_from_all_tables!(except: nil) + DatabaseCleaner.clean_with(:deletion, cache_tables: false, except: except) + end + def deletion_except_tables [] end diff --git a/spec/support/features/rss_shared_examples.rb b/spec/support/features/rss_shared_examples.rb index 0de92aedba5..02d310a9afa 100644 --- a/spec/support/features/rss_shared_examples.rb +++ b/spec/support/features/rss_shared_examples.rb @@ -6,7 +6,7 @@ end shared_examples "it has an RSS button with current_user's feed token" do it "shows the RSS button with current_user's feed token" do - expect(page).to have_css("a:has(.fa-rss)[href*='feed_token=#{user.feed_token}']") + expect(page).to have_css("a:has(.fa-rss)[href*='feed_token=#{user.feed_token}'], .js-rss-button[href*='feed_token=#{user.feed_token}']") end end @@ -18,6 +18,6 @@ end shared_examples "it has an RSS button without a feed token" do it "shows the RSS button without a feed token" do - expect(page).to have_css("a:has(.fa-rss):not([href*='feed_token'])") + expect(page).to have_css("a:has(.fa-rss):not([href*='feed_token']), .js-rss-button:not([href*='feed_token'])") end end diff --git a/spec/support/helpers/git_http_helpers.rb b/spec/support/helpers/git_http_helpers.rb index cd49bb148f2..c83860d7b51 100644 --- a/spec/support/helpers/git_http_helpers.rb +++ b/spec/support/helpers/git_http_helpers.rb @@ -1,4 +1,8 @@ +require_relative 'workhorse_helpers' + module GitHttpHelpers + include WorkhorseHelpers + def clone_get(project, options = {}) get "/#{project}/info/refs", params: { service: 'git-upload-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token)) end diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index 1a09d48f4cd..ec3c460cd37 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -57,7 +57,8 @@ module GraphqlHelpers end def variables_for_mutation(name, input) - graphql_input = input.map { |name, value| [GraphqlHelpers.fieldnamerize(name), value] }.to_h + graphql_input = prepare_input_for_mutation(input) + result = { input_variable_name_for_mutation(name) => graphql_input } # Avoid trying to serialize multipart data into JSON @@ -68,6 +69,18 @@ module GraphqlHelpers end end + # Recursively convert a Hash with Ruby-style keys to GraphQL fieldname-style keys + # + # prepare_input_for_mutation({ 'my_key' => 1 }) + # => { 'myKey' => 1} + def prepare_input_for_mutation(input) + input.map do |name, value| + value = prepare_input_for_mutation(value) if value.is_a?(Hash) + + [GraphqlHelpers.fieldnamerize(name), value] + end.to_h + end + def input_variable_name_for_mutation(mutation_name) mutation_name = GraphqlHelpers.fieldnamerize(mutation_name) mutation_field = GitlabSchema.mutation.fields[mutation_name] diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index 3c7bcba2b42..278264f3df5 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -199,6 +199,11 @@ module KubernetesHelpers .to_return(kube_response({})) end + def stub_kubeclient_put_role(api_url, name, namespace: 'default') + WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/roles/#{name}") + .to_return(kube_response({})) + end + def kube_v1_secret_body(**options) { "kind" => "SecretList", diff --git a/spec/support/helpers/memory_usage_helper.rb b/spec/support/helpers/memory_usage_helper.rb new file mode 100644 index 00000000000..984ea8cc571 --- /dev/null +++ b/spec/support/helpers/memory_usage_helper.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module MemoryUsageHelper + extend ActiveSupport::Concern + + def gather_memory_data(csv_path) + write_csv_entry(csv_path, + { + example_group_path: TestEnv.topmost_example_group[:location], + example_group_description: TestEnv.topmost_example_group[:description], + time: Time.current, + job_name: ENV['CI_JOB_NAME'] + }.merge(get_memory_usage)) + end + + def write_csv_entry(path, entry) + CSV.open(path, "a", headers: entry.keys, write_headers: !File.exist?(path)) do |file| + file << entry.values + end + end + + def get_memory_usage + output, status = Gitlab::Popen.popen(%w(free -m)) + abort "`free -m` return code is #{status}: #{output}" unless status.zero? + + result = output.split("\n")[1].split(" ")[1..-1] + attrs = %i(m_total m_used m_free m_shared m_buffers_cache m_available).freeze + + attrs.zip(result).to_h + end + + included do |config| + config.after(:all) do + gather_memory_data(ENV['MEMORY_TEST_PATH']) if ENV['MEMORY_TEST_PATH'] + end + end +end diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb index cc1a28cb264..272b24f7541 100644 --- a/spec/support/helpers/migrations_helpers.rb +++ b/spec/support/helpers/migrations_helpers.rb @@ -18,8 +18,12 @@ module MigrationsHelpers ActiveRecord::Migrator.migrations_paths end + def migration_context + ActiveRecord::MigrationContext.new(migrations_paths) + end + def migrations - ActiveRecord::Migrator.migrations(migrations_paths) + migration_context.migrations end def clear_schema_cache! @@ -96,8 +100,7 @@ module MigrationsHelpers def schema_migrate_down! disable_migrations_output do - ActiveRecord::Migrator.migrate(migrations_paths, - migration_schema_version) + migration_context.down(migration_schema_version) end reset_column_in_all_models @@ -107,7 +110,7 @@ module MigrationsHelpers reset_column_in_all_models disable_migrations_output do - ActiveRecord::Migrator.migrate(migrations_paths) + migration_context.up end reset_column_in_all_models @@ -123,7 +126,7 @@ module MigrationsHelpers end def migrate! - ActiveRecord::Migrator.up(migrations_paths) do |migration| + migration_context.up do |migration| migration.name == described_class.name end end diff --git a/spec/support/helpers/reactive_caching_helpers.rb b/spec/support/helpers/reactive_caching_helpers.rb index b76b53db0b9..528da37e8cf 100644 --- a/spec/support/helpers/reactive_caching_helpers.rb +++ b/spec/support/helpers/reactive_caching_helpers.rb @@ -10,7 +10,7 @@ module ReactiveCachingHelpers def stub_reactive_cache(subject = nil, data = nil, *qualifiers) allow(ReactiveCachingWorker).to receive(:perform_async) allow(ReactiveCachingWorker).to receive(:perform_in) - write_reactive_cache(subject, data, *qualifiers) unless data.nil? + write_reactive_cache(subject, data, *qualifiers) unless subject.nil? end def synchronous_reactive_cache(subject) diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index e63099d89b7..893b10ea752 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -2,6 +2,7 @@ require 'rspec/mocks' require 'toml-rb' module TestEnv + extend ActiveSupport::Concern extend self ComponentFailedToInstallError = Class.new(StandardError) @@ -108,6 +109,12 @@ module TestEnv setup_forked_repo end + included do |config| + config.append_before do + set_current_example_group + end + end + def disable_mailer allow_any_instance_of(NotificationService).to receive(:mailer) .and_return(double.as_null_object) @@ -297,8 +304,23 @@ module TestEnv FileUtils.rm_rf(path) end + def current_example_group + Thread.current[:current_example_group] + end + + # looking for a top-level `describe` + def topmost_example_group + example_group = current_example_group + example_group = example_group[:parent_example_group] until example_group[:parent_example_group].nil? + example_group + end + private + def set_current_example_group + Thread.current[:current_example_group] = ::RSpec.current_example.metadata[:example_group] + end + # These are directories that should be preserved at cleanup time def test_dirs @test_dirs ||= %w[ diff --git a/spec/support/matchers/abort_matcher.rb b/spec/support/matchers/abort_matcher.rb new file mode 100644 index 00000000000..ce1dd140210 --- /dev/null +++ b/spec/support/matchers/abort_matcher.rb @@ -0,0 +1,46 @@ +RSpec::Matchers.define :abort_execution do + match do |code_block| + @captured_stderr = StringIO.new + original_stderr = $stderr + $stderr = @captured_stderr + + code_block.call + + false + rescue SystemExit => e + captured = @captured_stderr.string.chomp + @actual_exit_code = e.status + break false unless e.status == 1 + + if @message + if @message.is_a? String + @message == captured + elsif @message.is_a? Regexp + @message.match?(captured) + else + raise ArgumentError, 'with_message must be either a String or a Regular Expression' + end + end + + ensure + $stderr = original_stderr + end + + chain :with_message do |message| + @message = message + end + + failure_message do |block| + unless @actual_exit_code + break "expected #{block} to abort with '#{@message}' but didnt call abort." + end + + if @actual_exit_code != 1 + break "expected #{block} to abort with: '#{@message}' but exited with success instead." + end + + "expected #{block} to abort with: '#{@message}' \n but received: '#{@captured_stderr.string.chomp}' instead." + end + + supports_block_expectations +end diff --git a/spec/support/shared_contexts/email_shared_context.rb b/spec/support/shared_contexts/email_shared_context.rb index 9d806fc524d..4f5d53f9317 100644 --- a/spec/support/shared_contexts/email_shared_context.rb +++ b/spec/support/shared_contexts/email_shared_context.rb @@ -1,5 +1,3 @@ -require 'gitlab/email/receiver' - shared_context :email_shared_context do let(:mail_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" } let(:receiver) { Gitlab::Email::Receiver.new(email_raw) } diff --git a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb new file mode 100644 index 00000000000..f2e1a95345b --- /dev/null +++ b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'a Note mutation that does not create a Note' do + it do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.not_to change { Note.count } + end +end + +RSpec.shared_examples 'a Note mutation that creates a Note' do + it do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to change { Note.count }.by(1) + end +end + +RSpec.shared_examples 'a Note mutation when the user does not have permission' do + it_behaves_like 'a Note mutation that does not create a Note' + + it_behaves_like 'a mutation that returns top-level errors', + errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action'] +end + +RSpec.shared_examples 'a Note mutation when there are active record validation errors' do |model: Note| + before do + expect_next_instance_of(model) do |note| + expect(note).to receive(:valid?).at_least(:once).and_return(false) + expect(note).to receive_message_chain( + :errors, + :full_messages + ).and_return(['Error 1', 'Error 2']) + end + end + + it_behaves_like 'a Note mutation that does not create a Note' + + it_behaves_like 'a mutation that returns errors in the response', errors: ['Error 1', 'Error 2'] + + it 'returns an empty Note' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response).to have_key('note') + expect(mutation_response['note']).to be_nil + end +end + +RSpec.shared_examples 'a Note mutation when the given resource id is not for a Noteable' do + let(:noteable) { create(:label, project: project) } + + it_behaves_like 'a Note mutation that does not create a Note' + + it_behaves_like 'a mutation that returns top-level errors', errors: ['Cannot add notes to this resource'] +end + +RSpec.shared_examples 'a Note mutation when the given resource id is not for a Note' do + let(:note) { create(:issue) } + + it_behaves_like 'a mutation that returns top-level errors', errors: ['Resource is not a note'] +end diff --git a/spec/support/shared_examples/mentionable_shared_examples.rb b/spec/support/shared_examples/mentionable_shared_examples.rb index 1226841f24c..fea52c2eeb2 100644 --- a/spec/support/shared_examples/mentionable_shared_examples.rb +++ b/spec/support/shared_examples/mentionable_shared_examples.rb @@ -76,6 +76,30 @@ shared_examples 'a mentionable' do expect(refs).to include(ext_commit) end + context 'when there are cached markdown fields' do + before do + if subject.is_a?(CacheMarkdownField) + subject.refresh_markdown_cache + end + end + + it 'sends in cached markdown fields when appropriate' do + if subject.is_a?(CacheMarkdownField) + expect_next_instance_of(Gitlab::ReferenceExtractor) do |ext| + attrs = subject.class.mentionable_attrs.collect(&:first) & subject.cached_markdown_fields.markdown_fields + attrs.each do |field| + expect(ext).to receive(:analyze).with(subject.send(field), hash_including(rendered: anything)) + end + end + + expect(subject).not_to receive(:refresh_markdown_cache) + expect(subject).to receive(:cached_markdown_fields).at_least(:once).and_call_original + + subject.all_references(author) + end + end + end + it 'creates cross-reference notes' do mentioned_objects = [mentioned_issue, mentioned_mr, mentioned_commit, ext_issue, ext_mr, ext_commit] @@ -98,6 +122,33 @@ shared_examples 'an editable mentionable' do [create(:issue, project: project), create(:issue, project: ext_proj)] end + context 'when there are cached markdown fields' do + before do + if subject.is_a?(CacheMarkdownField) + subject.refresh_markdown_cache + end + end + + it 'refreshes markdown cache if necessary' do + subject.save! + + set_mentionable_text.call('This is a text') + + if subject.is_a?(CacheMarkdownField) + expect_next_instance_of(Gitlab::ReferenceExtractor) do |ext| + subject.cached_markdown_fields.markdown_fields.each do |field| + expect(ext).to receive(:analyze).with(subject.send(field), hash_including(rendered: anything)) + end + end + + expect(subject).to receive(:refresh_markdown_cache) + expect(subject).to receive(:cached_markdown_fields).at_least(:once).and_call_original + + subject.all_references(author) + end + end + end + it 'creates new cross-reference notes when the mentionable text is edited' do subject.save subject.create_cross_references! diff --git a/spec/support/shared_examples/policies/clusterable_shared_examples.rb b/spec/support/shared_examples/policies/clusterable_shared_examples.rb index d99f94c76c3..4f9873d53e4 100644 --- a/spec/support/shared_examples/policies/clusterable_shared_examples.rb +++ b/spec/support/shared_examples/policies/clusterable_shared_examples.rb @@ -24,14 +24,6 @@ shared_examples 'clusterable policies' do context 'with no clusters' do it { expect_allowed(:add_cluster) } end - - context 'with an existing cluster' do - before do - cluster - end - - it { expect_disallowed(:add_cluster) } - end end end end diff --git a/spec/tasks/gitlab/storage_rake_spec.rb b/spec/tasks/gitlab/storage_rake_spec.rb index 4b04d9cec39..0e47408fc72 100644 --- a/spec/tasks/gitlab/storage_rake_spec.rb +++ b/spec/tasks/gitlab/storage_rake_spec.rb @@ -50,7 +50,7 @@ describe 'rake gitlab:storage:*', :sidekiq do expect(Project).not_to receive(:with_unmigrated_storage) - expect { run_rake_task(task) }.to output(/This task requires database write access. Exiting./).to_stderr + expect { run_rake_task(task) }.to abort_execution.with_message(/This task requires database write access. Exiting./) end end end @@ -96,7 +96,7 @@ describe 'rake gitlab:storage:*', :sidekiq do expect(Project).not_to receive(:with_unmigrated_storage) - expect { run_rake_task(task) }.to output(/There is already a rollback operation in progress/).to_stderr + expect { run_rake_task(task) }.to abort_execution.with_message(/There is already a rollback operation in progress/) end end end @@ -105,14 +105,23 @@ describe 'rake gitlab:storage:*', :sidekiq do it 'does nothing' do expect(::HashedStorage::MigratorWorker).not_to receive(:perform_async) - run_rake_task(task) + expect { run_rake_task(task) }.to abort_execution.with_message('There are no projects requiring storage migration. Nothing to do!') end end context 'with 3 legacy projects' do let(:projects) { create_list(:project, 3, :legacy_storage) } - it_behaves_like "handles custom BATCH env var", ::HashedStorage::MigratorWorker + it 'enqueues migrations and count projects correctly' do + projects.map(&:id).sort.tap do |ids| + stub_env('ID_FROM', ids[0]) + stub_env('ID_TO', ids[1]) + end + + expect { run_rake_task(task) }.to output(/Enqueuing migration of 2 projects in batches/).to_stdout + end + + it_behaves_like 'handles custom BATCH env var', ::HashedStorage::MigratorWorker end context 'with same id in range' do @@ -120,7 +129,7 @@ describe 'rake gitlab:storage:*', :sidekiq do stub_env('ID_FROM', 99999) stub_env('ID_TO', 99999) - expect { run_rake_task(task) }.to output(/There are no projects requiring storage migration with ID=99999/).to_stderr + expect { run_rake_task(task) }.to abort_execution.with_message(/There are no projects requiring storage migration with ID=99999/) end it 'displays a message when project exists but its already migrated' do @@ -128,7 +137,7 @@ describe 'rake gitlab:storage:*', :sidekiq do stub_env('ID_FROM', project.id) stub_env('ID_TO', project.id) - expect { run_rake_task(task) }.to output(/There are no projects requiring storage migration with ID=#{project.id}/).to_stderr + expect { run_rake_task(task) }.to abort_execution.with_message(/There are no projects requiring storage migration with ID=#{project.id}/) end it 'enqueues migration when project can be found' do @@ -153,7 +162,7 @@ describe 'rake gitlab:storage:*', :sidekiq do expect(Project).not_to receive(:with_unmigrated_storage) - expect { run_rake_task(task) }.to output(/There is already a migration operation in progress/).to_stderr + expect { run_rake_task(task) }.to abort_execution.with_message(/There is already a migration operation in progress/) end end end @@ -162,13 +171,22 @@ describe 'rake gitlab:storage:*', :sidekiq do it 'does nothing' do expect(::HashedStorage::RollbackerWorker).not_to receive(:perform_async) - run_rake_task(task) + expect { run_rake_task(task) }.to abort_execution.with_message('There are no projects that can have storage rolledback. Nothing to do!') end end context 'with 3 hashed projects' do let(:projects) { create_list(:project, 3) } + it 'enqueues migrations and count projects correctly' do + projects.map(&:id).sort.tap do |ids| + stub_env('ID_FROM', ids[0]) + stub_env('ID_TO', ids[1]) + end + + expect { run_rake_task(task) }.to output(/Enqueuing rollback of 2 projects in batches/).to_stdout + end + it_behaves_like "handles custom BATCH env var", ::HashedStorage::RollbackerWorker end end diff --git a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb index 623237b111a..bf633a118ca 100644 --- a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb +++ b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb @@ -28,7 +28,7 @@ describe 'notify/pipeline_failed_email.html.haml' do expect(rendered).to have_content "Your pipeline has failed" expect(rendered).to have_content pipeline.project.name - expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' ') + expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ') expect(rendered).to have_content pipeline.commit.author_name expect(rendered).to have_content "##{pipeline.id}" expect(rendered).to have_content pipeline.user.name @@ -45,7 +45,7 @@ describe 'notify/pipeline_failed_email.html.haml' do expect(rendered).to have_content "Your pipeline has failed" expect(rendered).to have_content pipeline.project.name - expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' ') + expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ') expect(rendered).to have_content pipeline.commit.author_name expect(rendered).to have_content "##{pipeline.id}" expect(rendered).to have_content "by API" diff --git a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb index 81245239eba..060274eb56a 100644 --- a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb +++ b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb @@ -30,7 +30,7 @@ describe 'notify/pipeline_failed_email.text.erb' do expect(rendered).to have_content('Your pipeline has failed') expect(rendered).to have_content(pipeline.project.name) - expect(rendered).to have_content(pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' ')) + expect(rendered).to have_content(pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ')) expect(rendered).to have_content(pipeline.commit.author_name) expect(rendered).to have_content("##{pipeline.id}") expect(rendered).to have_content(pipeline.user.name) diff --git a/spec/views/notify/pipeline_success_email.html.haml_spec.rb b/spec/views/notify/pipeline_success_email.html.haml_spec.rb index a876bf13e11..46a6c908863 100644 --- a/spec/views/notify/pipeline_success_email.html.haml_spec.rb +++ b/spec/views/notify/pipeline_success_email.html.haml_spec.rb @@ -28,7 +28,7 @@ describe 'notify/pipeline_success_email.html.haml' do expect(rendered).to have_content "Your pipeline has passed" expect(rendered).to have_content pipeline.project.name - expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' ') + expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ') expect(rendered).to have_content pipeline.commit.author_name expect(rendered).to have_content "##{pipeline.id}" expect(rendered).to have_content pipeline.user.name @@ -45,7 +45,7 @@ describe 'notify/pipeline_success_email.html.haml' do expect(rendered).to have_content "Your pipeline has passed" expect(rendered).to have_content pipeline.project.name - expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' ') + expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ') expect(rendered).to have_content pipeline.commit.author_name expect(rendered).to have_content "##{pipeline.id}" expect(rendered).to have_content "by API" diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb index 51afb076da1..edc55920b8e 100644 --- a/spec/workers/project_cache_worker_spec.rb +++ b/spec/workers/project_cache_worker_spec.rb @@ -36,6 +36,11 @@ describe ProjectCacheWorker do end context 'with an existing project' do + before do + lease_key = "namespace:namespaces_root_statistics:#{project.namespace_id}" + stub_exclusive_lease_taken(lease_key, timeout: Namespace::AggregationSchedule::DEFAULT_LEASE_TIMEOUT) + end + it 'refreshes the method caches' do expect_any_instance_of(Repository).to receive(:refresh_method_caches) .with(%i(readme)) @@ -81,6 +86,10 @@ describe ProjectCacheWorker do expect(UpdateProjectStatisticsWorker).not_to receive(:perform_in) + expect(Namespaces::ScheduleAggregationWorker) + .not_to receive(:perform_async) + .with(project.namespace_id) + worker.update_statistics(project, statistics) end end @@ -98,6 +107,11 @@ describe ProjectCacheWorker do .with(lease_timeout, project.id, statistics) .and_call_original + expect(Namespaces::ScheduleAggregationWorker) + .to receive(:perform_async) + .with(project.namespace_id) + .twice + worker.update_statistics(project, statistics) end end diff --git a/yarn.lock b/yarn.lock index b76eba830b1..e8c11b1a033 100644 --- a/yarn.lock +++ b/yarn.lock @@ -700,15 +700,15 @@ dependencies: requireindex "~1.1.0" -"@gitlab/svgs@^1.66.0": - version "1.66.0" - resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.66.0.tgz#3c02da455421ea241f32e915671842435df027ff" - integrity sha512-nxOoQPnofMs3BjRr3SVzQcclM0G6QFrLM8L4nnUCN+8Gxq2u8ukfSU5FCrkivXz+FP9Qo/FYilWV7CY8kDkt6A== +"@gitlab/svgs@^1.67.0": + version "1.67.0" + resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.67.0.tgz#c7b94eca13b99fd3aaa737fb6dcc0abc41d3c579" + integrity sha512-hJOmWEs6RkjzyKkb1vc9wwKGZIBIP0coHkxu/KgOoxhBVudpGk4CH7xJ6UuB2TKpb0SEh5CC1CzRZfBYaFhsaA== -"@gitlab/ui@^5.3.2": - version "5.3.2" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.3.2.tgz#8ee906cf0586834de0077e165f25764c0bf8a9e9" - integrity sha512-VgxlDXqG2q+u72Km+/Ljdvjh0DzvljvsztiXTxnOO+Eb+/I26JBWfdboqFr3E02JzT8W4s4rRinhRttLWfcM/A== +"@gitlab/ui@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.6.0.tgz#6b5408050229e2135359f3fce5d0de7718326a0d" + integrity sha512-EohpACc5OCK8pOWgwB7/QZUcg3tA9k863ku6Ik9NxaRSKt/JIpQ8RI4wCr4UmqhejZLQMD9VZHLUmc9Sf3Mk9w== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.2.1" @@ -7476,9 +7476,9 @@ mississippi@^3.0.0: through2 "^2.0.0" mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" - integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" |