From 78d8830cec030ff12afed3c8ae1dddec454d0a24 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 19 Mar 2020 15:09:41 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .gitlab/ci/frontend.gitlab-ci.yml | 23 ++- .gitlab/ci/qa.gitlab-ci.yml | 6 +- GITLAB_WORKHORSE_VERSION | 2 +- .../registry/explorer/pages/details.vue | 1 + .../import/gitlab_projects_controller.rb | 13 +- app/models/ci/build.rb | 18 +-- app/views/layouts/_head.html.haml | 2 +- .../197926-add-branch-to-package-title.yml | 5 + .../208674-use-wh-accel-only-for-ui-imports.yml | 5 + .../refactor-admin-mode-single-session.yml | 5 + .../remove-asset-host-prefetch-feature-flag.yml | 5 + lib/gitlab/auth/current_user_mode.rb | 15 +- lib/tasks/gitlab/assets.rake | 75 +++++++++- package.json | 4 +- .../import/gitlab_projects_controller_spec.rb | 100 ------------- spec/features/groups/navbar_spec.rb | 85 +---------- spec/features/projects/navbar_spec.rb | 135 ++--------------- .../snippets/user_comments_on_snippet_spec.rb | 6 +- .../registry/explorer/pages/details_spec.js | 4 + spec/lib/gitlab/auth/current_user_mode_spec.rb | 4 +- .../project_services/bugzilla_service_spec.rb | 2 - .../import/gitlab_projects_controller_spec.rb | 139 ++++++++++++++++++ spec/support/helpers/navbar_structure_helper.rb | 21 +++ .../shared_contexts/navbar_structure_context.rb | 163 +++++++++++++++++++++ spec/views/layouts/_head.html.haml_spec.rb | 25 +--- yarn.lock | 18 +-- 26 files changed, 501 insertions(+), 380 deletions(-) create mode 100644 changelogs/unreleased/197926-add-branch-to-package-title.yml create mode 100644 changelogs/unreleased/208674-use-wh-accel-only-for-ui-imports.yml create mode 100644 changelogs/unreleased/refactor-admin-mode-single-session.yml create mode 100644 changelogs/unreleased/remove-asset-host-prefetch-feature-flag.yml delete mode 100644 spec/controllers/import/gitlab_projects_controller_spec.rb create mode 100644 spec/requests/import/gitlab_projects_controller_spec.rb create mode 100644 spec/support/helpers/navbar_structure_helper.rb create mode 100644 spec/support/shared_contexts/navbar_structure_context.rb diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index 8685ccc5432..630e9dfd06a 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -2,6 +2,8 @@ cache: paths: - vendor/ruby/ + - public/assets/webpack/ + - assets-hash.txt - .yarn-cache/ - tmp/cache/assets/sprockets - tmp/cache/babel-loader @@ -28,18 +30,24 @@ DOCKER_DRIVER: overlay2 DOCKER_HOST: tcp://docker:2375 cache: - key: "assets-compile:production:vendor_ruby:.yarn-cache:tmp_cache_assets_sprockets:tmp_cache_webpack:v9" + key: "assets-compile:production:v1" artifacts: name: webpack-report expire_in: 31d paths: - webpack-report/ - - public/assets/ + - assets-compile.log + - public/assets/application-*.css + - public/assets/application-*.css.gz + when: always script: - node --version - retry yarn install --frozen-lockfile --production --cache-folder .yarn-cache --prefer-offline - free -m - - retry bundle exec rake gitlab:assets:compile + - time bin/rake gitlab:assets:compile > assets-compile.log 2>&1 + # TODO: Change the image tag to be the MD5 of assets files and skip image building if the image exists + # We'll also need to pass GITLAB_ASSETS_TAG to the trigerred omnibus-gitlab pipeline similarly to how we do it for trigerred CNG pipelines + # https://gitlab.com/gitlab-org/gitlab/issues/208389 - time scripts/build_assets_image - scripts/clean-old-cached-assets - rm -f /etc/apt/sources.list.d/google*.list # We don't need to update Chrome here @@ -71,7 +79,7 @@ gitlab:assets:compile pull-cache: - node --version - retry yarn install --frozen-lockfile --cache-folder .yarn-cache --prefer-offline - free -m - - retry bundle exec rake gitlab:assets:compile + - time bin/rake gitlab:assets:compile > assets-compile.log 2>&1 - scripts/clean-old-cached-assets variables: SETUP_DB: "false" @@ -79,12 +87,13 @@ gitlab:assets:compile pull-cache: NODE_OPTIONS: --max_old_space_size=3584 WEBPACK_VENDOR_DLL: "true" cache: - key: "assets-compile:v9" + key: "assets-compile:test:v1" artifacts: expire_in: 7d paths: - node_modules - public/assets + - assets-compile.log compile-assets pull-push-cache: extends: @@ -100,7 +109,7 @@ compile-assets pull-push-cache as-if-foss: - .as-if-foss cache: policy: pull-push - key: "assets-compile:v9:foss" + key: "assets-compile:test:as-if-foss:v1" compile-assets pull-cache: extends: @@ -116,7 +125,7 @@ compile-assets pull-cache as-if-foss: - .as-if-foss cache: policy: pull - key: "assets-compile:v9:foss" + key: "assets-compile:test:as-if-foss:v1" .frontend-fixtures-base: extends: diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml index b0713c0944a..8a8f66a4643 100644 --- a/.gitlab/ci/qa.gitlab-ci.yml +++ b/.gitlab/ci/qa.gitlab-ci.yml @@ -55,5 +55,9 @@ package-and-qa: extends: - .package-and-qa-base - .qa:rules:package-and-qa - needs: ["build-qa-image", "gitlab:assets:compile pull-cache"] + needs: + - job: build-qa-image + artifacts: false + - job: gitlab:assets:compile pull-cache + artifacts: false allow_failure: true diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index f062572ef7b..c4ca5ee3475 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -8.25.0 +8.26.0 diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue index 88e437b16d9..be9a27e2c42 100644 --- a/app/assets/javascripts/registry/explorer/pages/details.vue +++ b/app/assets/javascripts/registry/explorer/pages/details.vue @@ -327,6 +327,7 @@ export default { MAX_ADMIN_MODE_TIME.ago.to_i - all_sessions.any? do |session| - session[ADMIN_MODE_START_TIME_KEY].to_i > MAX_ADMIN_MODE_TIME.ago.to_i - end - end - - def all_sessions - @all_sessions ||= ActiveSession.list_sessions(user).lazy.map do |session| - Gitlab::NamespacedSessionStore.new(SESSION_STORE_KEY, session.with_indifferent_access ) - end + current_session_data.initiated? && current_session_data[ADMIN_MODE_START_TIME_KEY].to_i > MAX_ADMIN_MODE_TIME.ago.to_i end def admin_mode_requested_in_grace_period? diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake index b398bbe403f..69a3526b872 100644 --- a/lib/tasks/gitlab/assets.rake +++ b/lib/tasks/gitlab/assets.rake @@ -1,3 +1,48 @@ +# frozen_string_literal: true + +require 'fileutils' + +module Tasks + module Gitlab + module Assets + FOSS_ASSET_FOLDERS = %w[app/assets app/views fixtures/emojis vendor/assets/javascripts].freeze + EE_ASSET_FOLDERS = %w[ee/app/assets ee/app/views].freeze + JS_ASSET_PATTERNS = %w[*.js config/**/*.js].freeze + JS_ASSET_FILES = %w[package.json yarn.lock].freeze + MASTER_MD5_HASH_FILE = 'master-assets-hash.txt' + HEAD_MD5_HASH_FILE = 'assets-hash.txt' + PUBLIC_ASSETS_WEBPACK_DIR = 'public/assets/webpack' + + def self.md5_of_assets_impacting_webpack_compilation + start_time = Time.now + asset_files = assets_impacting_webpack_compilation + puts "Generating the MD5 hash for #{assets_impacting_webpack_compilation.size} Webpack-related assets..." + + asset_file_md5s = asset_files.map do |asset_file| + Digest::MD5.file(asset_file).hexdigest + end + + Digest::MD5.hexdigest(asset_file_md5s.join).tap { |md5| puts "=> MD5 generated in #{Time.now - start_time}: #{md5}" } + end + + def self.assets_impacting_webpack_compilation + assets_folders = FOSS_ASSET_FOLDERS + assets_folders += EE_ASSET_FOLDERS if ::Gitlab.ee? + + asset_files = Dir.glob(JS_ASSET_PATTERNS) + asset_files += JS_ASSET_FILES + + assets_folders.each do |folder| + asset_files.concat(Dir.glob(["#{folder}/**/*.*"])) + end + + asset_files + end + private_class_method :assets_impacting_webpack_compilation + end + end +end + namespace :gitlab do namespace :assets do desc 'GitLab | Assets | Compile all frontend assets' @@ -8,9 +53,35 @@ namespace :gitlab do yarn:check gettext:po_to_json rake:assets:precompile - webpack:compile + gitlab:assets:compile_webpack_if_needed gitlab:assets:fix_urls - ].each(&Gitlab::TaskHelpers.method(:invoke_and_time_task)) + ].each(&::Gitlab::TaskHelpers.method(:invoke_and_time_task)) + end + + desc 'GitLab | Assets | Compile all Webpack assets' + task :compile_webpack_if_needed do + FileUtils.mv(Tasks::Gitlab::Assets::HEAD_MD5_HASH_FILE, Tasks::Gitlab::Assets::MASTER_MD5_HASH_FILE, force: true) + + master_assets_md5 = + if File.exist?(Tasks::Gitlab::Assets::MASTER_MD5_HASH_FILE) + File.read(Tasks::Gitlab::Assets::MASTER_MD5_HASH_FILE) + else + 'missing!' + end + + head_assets_md5 = Tasks::Gitlab::Assets.md5_of_assets_impacting_webpack_compilation.tap do |md5| + File.write(Tasks::Gitlab::Assets::HEAD_MD5_HASH_FILE, md5) + end + + puts "Webpack assets MD5 for `master`: #{master_assets_md5}" + puts "Webpack assets MD5 for `HEAD`: #{head_assets_md5}" + + public_assets_webpack_dir_exists = Dir.exist?(Tasks::Gitlab::Assets::PUBLIC_ASSETS_WEBPACK_DIR) + + if head_assets_md5 != master_assets_md5 || !public_assets_webpack_dir_exists + FileUtils.rm_r(Tasks::Gitlab::Assets::PUBLIC_ASSETS_WEBPACK_DIR) if public_assets_webpack_dir_exists + Rake::Task['webpack:compile'].invoke + end end desc 'GitLab | Assets | Clean up old compiled frontend assets' diff --git a/package.json b/package.json index 11e5da73c23..f4f5d3a7070 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,8 @@ "@babel/plugin-syntax-import-meta": "^7.8.3", "@babel/preset-env": "^7.8.4", "@gitlab/at.js": "^1.5.5", - "@gitlab/svgs": "^1.113.0", - "@gitlab/ui": "^9.29.0", + "@gitlab/svgs": "^1.114.0", + "@gitlab/ui": "^9.31.1", "@gitlab/visual-review-tools": "1.5.1", "@sentry/browser": "^5.10.2", "@sourcegraph/code-host-integration": "0.0.31", diff --git a/spec/controllers/import/gitlab_projects_controller_spec.rb b/spec/controllers/import/gitlab_projects_controller_spec.rb deleted file mode 100644 index 0b74e2bbcbf..00000000000 --- a/spec/controllers/import/gitlab_projects_controller_spec.rb +++ /dev/null @@ -1,100 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Import::GitlabProjectsController do - let_it_be(:namespace) { create(:namespace) } - let_it_be(:user) { namespace.owner } - let(:file) { fixture_file_upload('spec/fixtures/project_export.tar.gz', 'text/plain') } - - before do - sign_in(user) - end - - describe 'POST create' do - context 'with an invalid path' do - it 'redirects with an error' do - post :create, params: { namespace_id: namespace.id, path: '/test', file: file } - - expect(flash[:alert]).to start_with('Project could not be imported') - expect(response).to have_gitlab_http_status(:found) - end - - it 'redirects with an error when a relative path is used' do - post :create, params: { namespace_id: namespace.id, path: '../test', file: file } - - expect(flash[:alert]).to start_with('Project could not be imported') - expect(response).to have_gitlab_http_status(:found) - end - end - - context 'with a valid path' do - it 'redirects to the new project path' do - post :create, params: { namespace_id: namespace.id, path: 'test', file: file } - - expect(flash[:notice]).to include('is being imported') - expect(response).to have_gitlab_http_status(:found) - end - end - - it_behaves_like 'project import rate limiter' - end - - describe 'POST authorize' do - let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') } - - before do - request.headers['GitLab-Workhorse'] = '1.0' - request.headers[Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER] = workhorse_token - end - - it 'authorizes importing project with workhorse header' do - post :authorize, format: :json - - expect(response).to have_gitlab_http_status(:ok) - expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) - end - - it 'rejects requests that bypassed gitlab-workhorse or have invalid header' do - request.headers[Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER] = 'INVALID_HEADER' - - expect { post :authorize, format: :json }.to raise_error(JWT::DecodeError) - end - - context 'when using remote storage' do - context 'when direct upload is enabled' do - before do - stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: true) - end - - it 'responds with status 200, location of file remote store and object details' do - post :authorize, format: :json - - expect(response).to have_gitlab_http_status(:ok) - expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) - expect(json_response).not_to have_key('TempPath') - expect(json_response['RemoteObject']).to have_key('ID') - expect(json_response['RemoteObject']).to have_key('GetURL') - expect(json_response['RemoteObject']).to have_key('StoreURL') - expect(json_response['RemoteObject']).to have_key('DeleteURL') - expect(json_response['RemoteObject']).to have_key('MultipartUpload') - end - end - - context 'when direct upload is disabled' do - before do - stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: false) - end - - it 'handles as a local file' do - post :authorize, format: :json - - expect(response).to have_gitlab_http_status(:ok) - expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) - expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path) - expect(json_response['RemoteObject']).to be_nil - end - end - end - end -end diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb index 0c457c11fce..0cdc2aa88f4 100644 --- a/spec/features/groups/navbar_spec.rb +++ b/spec/features/groups/navbar_spec.rb @@ -3,90 +3,21 @@ require 'spec_helper' describe 'Group navbar' do - let(:user) { create(:user) } - let(:group) { create(:group) } + include NavbarStructureHelper - let(:analytics_nav_item) do - { - nav_item: _('Analytics'), - nav_sub_items: [ - _('Contribution') - ] - } - end + include_context 'group navbar structure' + + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } - let(:structure) do - [ - { - nav_item: _('Group overview'), - nav_sub_items: [ - _('Details'), - _('Activity') - ] - }, - { - nav_item: _('Issues'), - nav_sub_items: [ - _('List'), - _('Board'), - _('Labels'), - _('Milestones') - ] - }, - { - nav_item: _('Merge Requests'), - nav_sub_items: [] - }, - { - nav_item: _('Kubernetes'), - nav_sub_items: [] - }, - (analytics_nav_item if Gitlab.ee?), - { - nav_item: _('Members'), - nav_sub_items: [] - } - ] + before do + group.add_maintainer(user) + sign_in(user) end it_behaves_like 'verified navigation bar' do before do - group.add_maintainer(user) - sign_in(user) - visit group_path(group) end end - - if Gitlab.ee? - context 'when productivity analytics is available' do - before do - stub_licensed_features(productivity_analytics: true) - - analytics_nav_item[:nav_sub_items] << _('Productivity') - - group.add_maintainer(user) - sign_in(user) - - visit group_path(group) - end - - it_behaves_like 'verified navigation bar' - end - - context 'when value stream analytics is available' do - before do - stub_licensed_features(cycle_analytics_for_groups: true) - - analytics_nav_item[:nav_sub_items] << _('Value Stream') - - group.add_maintainer(user) - sign_in(user) - - visit group_path(group) - end - - it_behaves_like 'verified navigation bar' - end - end end diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb index 2b8dfc4a5fa..6dbcace5401 100644 --- a/spec/features/projects/navbar_spec.rb +++ b/spec/features/projects/navbar_spec.rb @@ -3,112 +3,14 @@ require 'spec_helper' describe 'Project navbar' do - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } + include NavbarStructureHelper - let(:analytics_nav_item) do - { - nav_item: _('Analytics'), - nav_sub_items: [ - _('CI / CD'), - (_('Code Review') if Gitlab.ee?), - _('Repository'), - _('Value Stream') - ] - } - end + include_context 'project navbar structure' - let(:requirements_nav_item) do - { - nav_item: _('Requirements'), - nav_sub_items: [_('List')] - } - end - - let(:structure) do - [ - { - nav_item: _('Project overview'), - nav_sub_items: [ - _('Details'), - _('Activity'), - _('Releases') - ] - }, - { - nav_item: _('Repository'), - nav_sub_items: [ - _('Files'), - _('Commits'), - _('Branches'), - _('Tags'), - _('Contributors'), - _('Graph'), - _('Compare'), - (_('Locked Files') if Gitlab.ee?) - ] - }, - { - nav_item: _('Issues'), - nav_sub_items: [ - _('List'), - _('Boards'), - _('Labels'), - _('Milestones') - ] - }, - { - nav_item: _('Merge Requests'), - nav_sub_items: [] - }, - (requirements_nav_item if Gitlab.ee?), - { - nav_item: _('CI / CD'), - nav_sub_items: [ - _('Pipelines'), - _('Jobs'), - _('Artifacts'), - _('Schedules') - ] - }, - { - nav_item: _('Operations'), - nav_sub_items: [ - _('Metrics'), - _('Environments'), - _('Error Tracking'), - _('Serverless'), - _('Logs'), - _('Kubernetes') - ] - }, - analytics_nav_item, - { - nav_item: _('Wiki'), - nav_sub_items: [] - }, - { - nav_item: _('Snippets'), - nav_sub_items: [] - }, - { - nav_item: _('Settings'), - nav_sub_items: [ - _('General'), - _('Members'), - _('Integrations'), - _('Webhooks'), - _('Repository'), - _('CI / CD'), - _('Operations'), - (_('Audit Events') if Gitlab.ee?) - ].compact - } - ] - end + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository) } before do - stub_licensed_features(requirements: false) project.add_maintainer(user) sign_in(user) end @@ -119,28 +21,19 @@ describe 'Project navbar' do end end - if Gitlab.ee? - context 'when issues analytics is available' do - before do - stub_licensed_features(issues_analytics: true) - - analytics_nav_item[:nav_sub_items] << _('Issues') - analytics_nav_item[:nav_sub_items].sort! + context 'when pages are available' do + before do + allow(Gitlab.config.pages).to receive(:enabled).and_return(true) - visit project_path(project) - end + insert_after_sub_nav_item( + _('Operations'), + within: _('Settings'), + new_sub_nav_item_name: _('Pages') + ) - it_behaves_like 'verified navigation bar' + visit project_path(project) end - context 'when requirements is available' do - before do - stub_licensed_features(requirements: true) - - visit project_path(project) - end - - it_behaves_like 'verified navigation bar' - end + it_behaves_like 'verified navigation bar' end end diff --git a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb index 9d11b55228b..a7a220b926d 100644 --- a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb +++ b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb @@ -13,6 +13,9 @@ describe 'Projects > Snippets > User comments on a snippet', :js do sign_in(user) visit(project_snippet_path(project, snippet)) + + # Snippet's content is loaded async, we wait for it before we try to click anything + wait_for_requests end it 'leaves a comment on a snippet' do @@ -34,7 +37,8 @@ describe 'Projects > Snippets > User comments on a snippet', :js do end it 'has zen mode' do - find('.js-zen-enter').click + click_button 'Go full screen' + expect(page).to have_selector('.fullscreen') end end diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js index 15c6b36af03..660004e5eea 100644 --- a/spec/frontend/registry/explorer/pages/details_spec.js +++ b/spec/frontend/registry/explorer/pages/details_spec.js @@ -66,6 +66,10 @@ describe('Details Page', () => { it('does not have list items', () => { expect(findFirstRowItem('rowCheckbox').exists()).toBe(false); }); + + it('does not show pagination', () => { + expect(findPagination().exists()).toBe(false); + }); }); describe('table', () => { diff --git a/spec/lib/gitlab/auth/current_user_mode_spec.rb b/spec/lib/gitlab/auth/current_user_mode_spec.rb index 2b910fac155..8863f26fbf2 100644 --- a/spec/lib/gitlab/auth/current_user_mode_spec.rb +++ b/spec/lib/gitlab/auth/current_user_mode_spec.rb @@ -151,13 +151,13 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode, :request_store allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session]) end - it 'can be enabled in one and seen in the other' do + it 'cannot be enabled in one and seen in the other' do Gitlab::Session.with_session(another_session) do another_subject.request_admin_mode! another_subject.enable_admin_mode!(password: user.password) end - expect(subject.admin_mode?).to be(true) + expect(subject.admin_mode?).to be(false) end end end diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb index d0ab5afc765..ab939e0d2f8 100644 --- a/spec/models/project_services/bugzilla_service_spec.rb +++ b/spec/models/project_services/bugzilla_service_spec.rb @@ -34,8 +34,6 @@ describe BugzillaService do end context 'overriding properties' do - let(:default_title) { 'JIRA' } - let(:default_description) { 'JiraService|Jira issue tracker' } let(:url) { 'http://bugzilla.example.com' } let(:access_params) do { project_url: url, issues_url: url, new_issue_url: url } diff --git a/spec/requests/import/gitlab_projects_controller_spec.rb b/spec/requests/import/gitlab_projects_controller_spec.rb new file mode 100644 index 00000000000..f16755e9766 --- /dev/null +++ b/spec/requests/import/gitlab_projects_controller_spec.rb @@ -0,0 +1,139 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Import::GitlabProjectsController do + include WorkhorseHelpers + + let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') } + let(:workhorse_headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } } + + let_it_be(:namespace) { create(:namespace) } + let_it_be(:user) { namespace.owner } + + before do + login_as(user) + end + + describe 'POST create' do + subject { upload_archive(file_upload, workhorse_headers, params) } + + let(:file) { File.join('spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } + let(:file_upload) { fixture_file_upload(file) } + let(:params) { { namespace_id: namespace.id, path: 'test' } } + + before do + allow(ImportExportUploader).to receive(:workhorse_upload_path).and_return('/') + end + + context 'with a valid path' do + it 'schedules an import and redirects to the new project path' do + stub_import(namespace) + + subject + + expect(flash[:notice]).to include('is being imported') + expect(response).to have_gitlab_http_status(:found) + end + end + + context 'with an invalid path' do + ['/test', '../test'].each do |invalid_path| + it "redirects with an error when path is `#{invalid_path}`" do + params[:path] = invalid_path + + subject + + expect(flash[:alert]).to start_with('Project could not be imported') + expect(response).to have_gitlab_http_status(:found) + end + end + end + + context 'when request exceeds the rate limit' do + before do + allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true) + end + + it 'prevents users from importing projects' do + subject + + expect(flash[:alert]).to eq('This endpoint has been requested too many times. Try again later.') + expect(response).to have_gitlab_http_status(:found) + end + end + + def upload_archive(file, headers = {}, params = {}) + workhorse_finalize( + import_gitlab_project_path, + method: :post, + file_key: :file, + params: params.merge(file: file), + headers: headers, + send_rewritten_field: true + ) + end + + def stub_import(namespace) + expect_any_instance_of(ProjectImportState).to receive(:schedule) + expect(::Projects::CreateService) + .to receive(:new) + .with(user, instance_of(ActionController::Parameters)) + .and_call_original + end + end + + describe 'POST authorize' do + subject { post authorize_import_gitlab_project_path, headers: workhorse_headers } + + it 'authorizes importing project with workhorse header' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) + expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path) + end + + it 'rejects requests that bypassed gitlab-workhorse' do + workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER) + + expect { subject }.to raise_error(JWT::DecodeError) + end + + context 'when using remote storage' do + context 'when direct upload is enabled' do + before do + stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: true) + end + + it 'responds with status 200, location of file remote store and object details' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) + expect(json_response).not_to have_key('TempPath') + expect(json_response['RemoteObject']).to have_key('ID') + expect(json_response['RemoteObject']).to have_key('GetURL') + expect(json_response['RemoteObject']).to have_key('StoreURL') + expect(json_response['RemoteObject']).to have_key('DeleteURL') + expect(json_response['RemoteObject']).to have_key('MultipartUpload') + end + end + + context 'when direct upload is disabled' do + before do + stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: false) + end + + it 'handles as a local file' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) + expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path) + expect(json_response['RemoteObject']).to be_nil + end + end + end + end +end diff --git a/spec/support/helpers/navbar_structure_helper.rb b/spec/support/helpers/navbar_structure_helper.rb new file mode 100644 index 00000000000..cfb1b185560 --- /dev/null +++ b/spec/support/helpers/navbar_structure_helper.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module NavbarStructureHelper + def insert_after_nav_item(before_nav_item_name, new_nav_item:) + expect(structure).to include(a_hash_including(nav_item: before_nav_item_name)) + + index = structure.find_index { |h| h[:nav_item] == before_nav_item_name } + structure.insert(index + 1, new_nav_item) + end + + def insert_after_sub_nav_item(before_sub_nav_item_name, within:, new_sub_nav_item_name:) + expect(structure).to include(a_hash_including(nav_item: within)) + hash = structure.find { |h| h[:nav_item] == within } + + expect(hash).to have_key(:nav_sub_items) + expect(hash[:nav_sub_items]).to include(before_sub_nav_item_name) + + index = hash[:nav_sub_items].find_index(before_sub_nav_item_name) + hash[:nav_sub_items].insert(index + 1, new_sub_nav_item_name) + end +end diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb new file mode 100644 index 00000000000..c7df54f9501 --- /dev/null +++ b/spec/support/shared_contexts/navbar_structure_context.rb @@ -0,0 +1,163 @@ +# frozen_string_literal: true + +RSpec.shared_context 'project navbar structure' do + let(:requirements_nav_item) do + { + nav_item: _('Requirements'), + nav_sub_items: [_('List')] + } + end + + let(:analytics_nav_item) do + { + nav_item: _('Analytics'), + nav_sub_items: [ + _('CI / CD'), + (_('Code Review') if Gitlab.ee?), + _('Repository'), + _('Value Stream') + ] + } + end + + let(:structure) do + [ + { + nav_item: _('Project overview'), + nav_sub_items: [ + _('Details'), + _('Activity'), + _('Releases') + ] + }, + { + nav_item: _('Repository'), + nav_sub_items: [ + _('Files'), + _('Commits'), + _('Branches'), + _('Tags'), + _('Contributors'), + _('Graph'), + _('Compare'), + (_('Locked Files') if Gitlab.ee?) + ] + }, + { + nav_item: _('Issues'), + nav_sub_items: [ + _('List'), + _('Boards'), + _('Labels'), + _('Milestones') + ] + }, + { + nav_item: _('Merge Requests'), + nav_sub_items: [] + }, + (requirements_nav_item if Gitlab.ee?), + { + nav_item: _('CI / CD'), + nav_sub_items: [ + _('Pipelines'), + _('Jobs'), + _('Artifacts'), + _('Schedules') + ] + }, + { + nav_item: _('Operations'), + nav_sub_items: [ + _('Metrics'), + _('Environments'), + _('Error Tracking'), + _('Serverless'), + _('Logs'), + _('Kubernetes') + ] + }, + analytics_nav_item, + { + nav_item: _('Wiki'), + nav_sub_items: [] + }, + { + nav_item: _('Snippets'), + nav_sub_items: [] + }, + { + nav_item: _('Settings'), + nav_sub_items: [ + _('General'), + _('Members'), + _('Integrations'), + _('Webhooks'), + _('Repository'), + _('CI / CD'), + _('Operations'), + (_('Audit Events') if Gitlab.ee?) + ].compact + } + ].compact + end +end + +RSpec.shared_context 'group navbar structure' do + let(:analytics_nav_item) do + { + nav_item: _('Analytics'), + nav_sub_items: [ + _('Contribution') + ] + } + end + + let(:settings_nav_item) do + { + nav_item: _('Settings'), + nav_sub_items: [ + _('General'), + _('Projects'), + _('CI / CD'), + _('Webhooks'), + _('Audit Events'), + _('Usage Quotas') + ] + } + end + + let(:structure) do + [ + { + nav_item: _('Group overview'), + nav_sub_items: [ + _('Details'), + _('Activity') + ] + }, + { + nav_item: _('Issues'), + nav_sub_items: [ + _('List'), + _('Board'), + _('Labels'), + _('Milestones') + ] + }, + { + nav_item: _('Merge Requests'), + nav_sub_items: [] + }, + { + nav_item: _('Kubernetes'), + nav_sub_items: [] + }, + (analytics_nav_item if Gitlab.ee?), + { + nav_item: _('Members'), + nav_sub_items: [] + } + ] + end +end diff --git a/spec/views/layouts/_head.html.haml_spec.rb b/spec/views/layouts/_head.html.haml_spec.rb index f181e18e53d..8d7dcfc2416 100644 --- a/spec/views/layouts/_head.html.haml_spec.rb +++ b/spec/views/layouts/_head.html.haml_spec.rb @@ -34,36 +34,23 @@ describe 'layouts/_head' do expect(rendered).to match(%{content="foo" http-equiv="refresh"}) end - context 'when an asset_host is set and feature is activated in the config it will' do + context 'when an asset_host is set' do let(:asset_host) { 'http://assets' } before do - stub_feature_flags(asset_host_prefetch: true) allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) end - it 'add a link dns-prefetch tag' do + it 'adds a link dns-prefetch tag' do render - expect(rendered).to match('') - end - - it 'add a link preconnect tag' do - render - expect(rendered).to match('') - end - end - context 'when an asset_host is set and feature is not activated in the config it will' do - let(:asset_host) { 'http://assets' } - - before do - stub_feature_flags(asset_host_prefetch: false) - allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) + expect(rendered).to match(%Q()) end - it 'not add a link dns-prefetch tag' do + it 'adds a link preconnect tag' do render - expect(rendered).not_to match('') + + expect(rendered).to match(%Q()) end end diff --git a/yarn.lock b/yarn.lock index 7342879af10..00c6486764b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -781,15 +781,15 @@ eslint-plugin-vue "^6.2.1" vue-eslint-parser "^7.0.0" -"@gitlab/svgs@^1.113.0": - version "1.113.0" - resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.113.0.tgz#0ea9cb3122a479f3ed4bb22943d68a9a38f69948" - integrity sha512-upT+sKEnnwZDU7vzI5VSyg2l6IOd/0Icm/MLae6po/nOGbf2vftkUVfbalzISAmk9eYTuJQ1sGmRWdKXPGy1cw== - -"@gitlab/ui@^9.29.0": - version "9.29.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.29.0.tgz#2308aec1d3d837392eaaf9d441aecec4b695ed73" - integrity sha512-zIY/aChWaU4UBlAjZ95PpBxgUd9VrGiXs9ujVU6Gbi+ZsHbpDvPlBHsHEG9isnUVBE2AD4GPqMLO8K9i+B9O4A== +"@gitlab/svgs@^1.114.0": + version "1.114.0" + resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.114.0.tgz#ffee243fa540016c8198596686a46e3c459adb32" + integrity sha512-r2tizWDx1fX2QbeXzU0Z5Fi9YS7TLIxt7K+vAnZxOeddgd5KdipCvhT/H7n9Oa9jLU4bMwGSrHZ1usCmCoWOnw== + +"@gitlab/ui@^9.31.1": + version "9.31.1" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.31.1.tgz#53206aac643d79f8eddbc5715131bad5ffc1e412" + integrity sha512-MHBVIpVzUYPkr70ti21dTZbzzZttOidWZdN3TeZOdqYbjYg2ONujFBxOGSCNYcaMYr+1/wFtuVRL/JmMVGjJrg== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" -- cgit v1.2.3