diff options
22 files changed, 235 insertions, 66 deletions
@@ -103,7 +103,7 @@ gem 'hashie-forbidden_attributes' gem 'kaminari', '~> 1.0' # HAML -gem 'hamlit', '~> 2.11.0' +gem 'hamlit', '~> 2.14.2' # Files attachments gem 'carrierwave', '~> 1.3' diff --git a/Gemfile.lock b/Gemfile.lock index 974bc4181f1..ee5cbc7e2a0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -557,7 +557,7 @@ GEM rainbow rubocop (>= 0.50.0) sysexits (~> 1.1) - hamlit (2.11.0) + hamlit (2.14.2) temple (>= 0.8.2) thor tilt @@ -1388,7 +1388,7 @@ DEPENDENCIES gssapi guard-rspec haml_lint (~> 0.36.0) - hamlit (~> 2.11.0) + hamlit (~> 2.14.2) hangouts-chat (~> 0.0.5) hashie hashie-forbidden_attributes diff --git a/app/assets/javascripts/pipelines/stores/test_reports/actions.js b/app/assets/javascripts/pipelines/stores/test_reports/actions.js index 3c664457756..c462dbfcf2d 100644 --- a/app/assets/javascripts/pipelines/stores/test_reports/actions.js +++ b/app/assets/javascripts/pipelines/stores/test_reports/actions.js @@ -28,16 +28,12 @@ export const fetchTestSuite = ({ state, commit, dispatch }, index) => { dispatch('toggleLoading'); - const { name = '', build_ids = [] } = state.testReports?.test_suites?.[index] || {}; + const { build_ids = [] } = state.testReports?.test_suites?.[index] || {}; // Replacing `/:suite_name.json` with the name of the suite. Including the extra characters // to ensure that we replace exactly the template part of the URL string - const endpoint = state.suiteEndpoint?.replace( - '/:suite_name.json', - `/${encodeURIComponent(name)}.json`, - ); return axios - .get(endpoint, { params: { build_ids } }) + .get(state.suiteEndpoint, { params: { build_ids } }) .then(({ data }) => commit(types.SET_SUITE, { suite: data, index })) .catch(() => { createFlash(s__('TestReports|There was an error fetching the test suite.')); diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 352050f7b01..33bd40a488f 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -49,8 +49,6 @@ position: relative; .dropdown-menu { - min-width: 100%; - width: 100%; left: inherit; right: 0; } diff --git a/app/services/packages/maven/find_or_create_package_service.rb b/app/services/packages/maven/find_or_create_package_service.rb index 8ee449cbfdc..6e0346058e8 100644 --- a/app/services/packages/maven/find_or_create_package_service.rb +++ b/app/services/packages/maven/find_or_create_package_service.rb @@ -11,12 +11,7 @@ module Packages .execute unless Namespace::PackageSetting.duplicates_allowed?(package) - files = package&.package_files || [] - current_maven_files = files.map { |file| extname(file.file_name) } - - if current_maven_files.compact.include?(extname(params[:file_name])) - return ServiceResponse.error(message: 'Duplicate package is not allowed') - end + return ServiceResponse.error(message: 'Duplicate package is not allowed') if target_package_is_duplicate?(package) end unless package @@ -67,6 +62,17 @@ module Packages File.extname(filename) end + + def target_package_is_duplicate?(package) + # duplicate metadata files can be uploaded multiple times + return false if package.version.nil? + + package + .package_files + .map { |file| extname(file.file_name) } + .compact + .include?(extname(params[:file_name])) + end end end end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 881a139437a..5273dedb56f 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class SystemHooksService - BUILDER_DRIVEN_EVENT_DATA_AVAILABLE_FOR_CLASSES = [GroupMember].freeze + BUILDER_DRIVEN_EVENT_DATA_AVAILABLE_FOR_CLASSES = [GroupMember, Group].freeze def execute_hooks_for(model, event) data = build_event_data(model, event) @@ -58,15 +58,6 @@ class SystemHooksService end when ProjectMember data.merge!(project_member_data(model)) - when Group - data.merge!(group_data(model)) - - if event == :rename - data.merge!( - old_path: model.path_before_last_save, - old_full_path: model.full_path_before_last_save - ) - end end data @@ -114,19 +105,6 @@ class SystemHooksService } end - def group_data(model) - owner = model.owner - - { - name: model.name, - path: model.path, - full_path: model.full_path, - group_id: model.id, - owner_name: owner.try(:name), - owner_email: owner.try(:email) - } - end - def user_data(model) { name: model.name, @@ -141,10 +119,14 @@ class SystemHooksService end def builder_driven_event_data(model, event) - case model - when GroupMember - Gitlab::HookData::GroupMemberBuilder.new(model).build(event) - end + builder_class = case model + when GroupMember + Gitlab::HookData::GroupMemberBuilder + when Group + Gitlab::HookData::GroupBuilder + end + + builder_class.new(model).build(event) end end diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index b41c3f4fc27..a551d4ab459 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -85,5 +85,5 @@ #js-tab-tests.tab-pane #js-pipeline-tests-detail{ data: { summary_endpoint: summary_project_pipeline_tests_path(@project, @pipeline, format: :json), - suite_endpoint: project_pipeline_test_path(@project, @pipeline, suite_name: ':suite_name', format: :json) } } + suite_endpoint: project_pipeline_test_path(@project, @pipeline, suite_name: 'suite', format: :json) } } = render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project diff --git a/changelogs/unreleased/254643-fix-broken-testsuite-link-when-containing-dot.yml b/changelogs/unreleased/254643-fix-broken-testsuite-link-when-containing-dot.yml new file mode 100644 index 00000000000..accfee61a16 --- /dev/null +++ b/changelogs/unreleased/254643-fix-broken-testsuite-link-when-containing-dot.yml @@ -0,0 +1,5 @@ +--- +title: Fix broken testsuite link if the suite contains a dot +merge_request: 51828 +author: Michael Aigner @tonka3000 +type: fixed diff --git a/changelogs/unreleased/276882-Allow-metadata-duplicates.yml b/changelogs/unreleased/276882-Allow-metadata-duplicates.yml new file mode 100644 index 00000000000..9aadeaf3f4c --- /dev/null +++ b/changelogs/unreleased/276882-Allow-metadata-duplicates.yml @@ -0,0 +1,6 @@ +--- +title: Allow versionless maven-metadata.xml file duplicates even when maven duplicates + are disabled +merge_request: 51758 +author: +type: fixed diff --git a/changelogs/unreleased/Set-min-width-of-breadcrumb-dropdown-to-200px.yml b/changelogs/unreleased/Set-min-width-of-breadcrumb-dropdown-to-200px.yml new file mode 100644 index 00000000000..ac3254afabd --- /dev/null +++ b/changelogs/unreleased/Set-min-width-of-breadcrumb-dropdown-to-200px.yml @@ -0,0 +1,5 @@ +--- +title: Fix breadcrumb dropdown on mobile being too narrow +merge_request: 51092 +author: Kev @KevSlashNull +type: fixed diff --git a/changelogs/unreleased/sh-update-hamlit-for-gc.yml b/changelogs/unreleased/sh-update-hamlit-for-gc.yml new file mode 100644 index 00000000000..abfb61e0ba7 --- /dev/null +++ b/changelogs/unreleased/sh-update-hamlit-for-gc.yml @@ -0,0 +1,5 @@ +--- +title: Update hamlit to v2.14.2 +merge_request: 52177 +author: +type: fixed diff --git a/config/feature_flags/development/whats_new_drawer.yml b/config/feature_flags/development/whats_new_drawer.yml deleted file mode 100644 index c06dfbe1fe6..00000000000 --- a/config/feature_flags/development/whats_new_drawer.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: whats_new_drawer -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38975 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/254186 -milestone: '13.3' -type: development -group: group::retention -default_enabled: false diff --git a/doc/development/snowplow.md b/doc/development/snowplow.md index 6b37936cd93..82e2947db34 100644 --- a/doc/development/snowplow.md +++ b/doc/development/snowplow.md @@ -303,11 +303,14 @@ GitLab provides `Gitlab::Tracking`, an interface that wraps the [Snowplow Ruby T Custom event tracking and instrumentation can be added by directly calling the `GitLab::Tracking.event` class method, which accepts the following arguments: -| argument | type | default value | description | -|:-----------|:-------|:--------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `category` | string | 'application' | Area or aspect of the application. This could be `HealthCheckController` or `Lfs::FileTransformer` for instance. | -| `action` | string | 'generic' | The action being taken, which can be anything from a controller action like `create` to something like an Active Record callback. | -| `data` | object | {} | Additional data such as `label`, `property`, `value`, and `context` as described in [Structured event taxonomy](#structured-event-taxonomy). These are set as empty strings if you don't provide them. | +| argument | type | default value | description | +|------------|---------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------| +| `category` | String | | Area or aspect of the application. This could be `HealthCheckController` or `Lfs::FileTransformer` for instance. | +| `action` | String | | The action being taken, which can be anything from a controller action like `create` to something like an Active Record callback. | +| `label` | String | nil | As described in [Structured event taxonomy](#structured-event-taxonomy). | +| `property` | String | nil | As described in [Structured event taxonomy](#structured-event-taxonomy). | +| `value` | Numeric | nil | As described in [Structured event taxonomy](#structured-event-taxonomy). | +| `context` | Array[SelfDescribingJSON] | nil | An array of custom contexts to send with this event. Most events should not have any custom contexts. | Tracking can be viewed as either tracking user behavior, or can be used for instrumentation to monitor and visualize performance over time in an area or aspect of code. diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md index 1c90add1390..87087850038 100644 --- a/doc/user/packages/nuget_repository/index.md +++ b/doc/user/packages/nuget_repository/index.md @@ -106,6 +106,9 @@ You can now add a new source to NuGet with: #### Project-level endpoint +A project-level endpoint is required to publish NuGet packages to the Package Registry. +A project-level endpoint is also required to install NuGet packages from a project. + To use the [project-level](#use-the-gitlab-endpoint-for-nuget-packages) NuGet endpoint, add the Package Registry as a source with `nuget`: ```shell @@ -122,6 +125,8 @@ nuget source Add -Name "GitLab" -Source "https://gitlab.example.com/api/v4/proje #### Group-level endpoint +To install a NuGet package from a group, use a group-level endpoint. + To use the [group-level](#use-the-gitlab-endpoint-for-nuget-packages) NuGet endpoint, add the Package Registry as a source with `nuget`: ```shell @@ -140,6 +145,9 @@ nuget source Add -Name "GitLab" -Source "https://gitlab.example.com/api/v4/group #### Project-level endpoint +A project-level endpoint is required to publish NuGet packages to the Package Registry. +A project-level endpoint is also required to install NuGet packages from a project. + To use the [project-level](#use-the-gitlab-endpoint-for-nuget-packages) NuGet endpoint, add the Package Registry as a source with Visual Studio: 1. Open [Visual Studio](https://visualstudio.microsoft.com/vs/). @@ -167,6 +175,8 @@ If you get a warning, ensure that the **Location**, **Username**, and #### Group-level endpoint +To install a package from a group, use a group-level endpoint. + To use the [group-level](#use-the-gitlab-endpoint-for-nuget-packages) NuGet endpoint, add the Package Registry as a source with Visual Studio: 1. Open [Visual Studio](https://visualstudio.microsoft.com/vs/). @@ -196,6 +206,9 @@ If you get a warning, ensure that the **Location**, **Username**, and #### Project-level endpoint +A project-level endpoint is required to publish NuGet packages to the Package Registry. +A project-level endpoint is also required to install NuGet packages from a project. + To use the [project-level](#use-the-gitlab-endpoint-for-nuget-packages) Package Registry as a source for .NET: 1. In the root of your project, create a file named `nuget.config`. @@ -219,6 +232,8 @@ To use the [project-level](#use-the-gitlab-endpoint-for-nuget-packages) Package #### Group-level endpoint +To install a package from a group, use a group-level endpoint. + To use the [group-level](#use-the-gitlab-endpoint-for-nuget-packages) Package Registry as a source for .NET: 1. In the root of your project, create a file named `nuget.config`. @@ -326,8 +341,19 @@ updated: 1. Commit the changes and push it to your GitLab repository to trigger a new CI/CD build. +### Publishing a package with the same name or version + +When you publish a package with the same name or version as an existing package, +the existing package is overwritten. + ## Install packages +To install a NuGet package from the Package Registry, you must first +[add a project-level or group-level endpoint](#add-the-package-registry-as-a-source-for-nuget-packages). + +If multiple packages have the same name and version, when you install +a package, the most recently-published package is retrieved. + ### Install a package with the NuGet CLI WARNING: diff --git a/lib/gitlab/hook_data/base_builder.rb b/lib/gitlab/hook_data/base_builder.rb index 434d30d9717..e5bae61ae4e 100644 --- a/lib/gitlab/hook_data/base_builder.rb +++ b/lib/gitlab/hook_data/base_builder.rb @@ -21,6 +21,12 @@ module Gitlab private + def event_data(event) + event_name = "#{object.class.name.downcase}_#{event}" + + { event_name: event_name } + end + def timestamps_data { created_at: object.created_at&.xmlschema, diff --git a/lib/gitlab/hook_data/group_builder.rb b/lib/gitlab/hook_data/group_builder.rb new file mode 100644 index 00000000000..25c34a4c4a7 --- /dev/null +++ b/lib/gitlab/hook_data/group_builder.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module Gitlab + module HookData + class GroupBuilder < BaseBuilder + alias_method :group, :object + + # Sample data + # { + # :created_at=>"2021-01-20T09:40:12Z", + # :updated_at=>"2021-01-20T09:40:12Z", + # :event_name=>"group_rename", + # :name=>"group1", + # :path=>"group1", + # :full_path=>"group1", + # :group_id=>1, + # :owner_name=>nil, + # :owner_email=>nil, + # :old_path=>"old-path", + # :old_full_path=>"old-path" + # } + + def build(event) + [ + timestamps_data, + event_data(event), + group_data, + event_specific_group_data(event) + ].reduce(:merge) + end + + private + + def group_data + owner = group.owner + + { + name: group.name, + path: group.path, + full_path: group.full_path, + group_id: group.id, + owner_name: owner.try(:name), + owner_email: owner.try(:email) + } + end + + def event_specific_group_data(event) + return {} unless event == :rename + + { + old_path: group.path_before_last_save, + old_full_path: group.full_path_before_last_save + } + end + end + end +end diff --git a/spec/frontend/pipelines/test_reports/stores/actions_spec.js b/spec/frontend/pipelines/test_reports/stores/actions_spec.js index f7ff36c0a46..970430b3adf 100644 --- a/spec/frontend/pipelines/test_reports/stores/actions_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/actions_spec.js @@ -16,7 +16,7 @@ describe('Actions TestReports Store', () => { const testReports = getJSONFixture('pipelines/test_report.json'); const summary = { total_count: 1 }; - const suiteEndpoint = `${TEST_HOST}/tests/:suite_name.json`; + const suiteEndpoint = `${TEST_HOST}/tests/suite.json`; const summaryEndpoint = `${TEST_HOST}/test_reports/summary.json`; const defaultState = { suiteEndpoint, @@ -69,9 +69,8 @@ describe('Actions TestReports Store', () => { beforeEach(() => { const buildIds = [1]; testReports.test_suites[0].build_ids = buildIds; - const endpoint = suiteEndpoint.replace(':suite_name', testReports.test_suites[0].name); mock - .onGet(endpoint, { params: { build_ids: buildIds } }) + .onGet(suiteEndpoint, { params: { build_ids: buildIds } }) .replyOnce(200, testReports.test_suites[0], {}); }); diff --git a/spec/lib/gitlab/hook_data/group_builder_spec.rb b/spec/lib/gitlab/hook_data/group_builder_spec.rb new file mode 100644 index 00000000000..d95669d502f --- /dev/null +++ b/spec/lib/gitlab/hook_data/group_builder_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::HookData::GroupBuilder do + let_it_be(:group) { create(:group) } + + describe '#build' do + let(:data) { described_class.new(group).build(event) } + let(:event_name) { data[:event_name] } + let(:attributes) do + [ + :event_name, :created_at, :updated_at, :name, :path, :full_path, :group_id, + :owner_name, :owner_email + ] + end + + context 'data' do + shared_examples_for 'includes the required attributes' do + it 'includes the required attributes' do + expect(data).to include(*attributes) + + expect(data[:name]).to eq(group.name) + expect(data[:path]).to eq(group.path) + expect(data[:full_path]).to eq(group.full_path) + expect(data[:group_id]).to eq(group.id) + expect(data[:owner_name]).to eq(nil) + expect(data[:owner_email]).to eq(nil) + expect(data[:created_at]).to eq(group.created_at.xmlschema) + expect(data[:updated_at]).to eq(group.updated_at.xmlschema) + end + end + + shared_examples_for 'does not include old path attributes' do + it 'does not include old path attributes' do + expect(data).not_to include(:old_path, :old_full_path) + end + end + + context 'on create' do + let(:event) { :create } + + it { expect(event_name).to eq('group_create') } + it_behaves_like 'includes the required attributes' + it_behaves_like 'does not include old path attributes' + end + + context 'on destroy' do + let(:event) { :destroy } + + it { expect(event_name).to eq('group_destroy') } + it_behaves_like 'includes the required attributes' + it_behaves_like 'does not include old path attributes' + end + + context 'on rename' do + let(:event) { :rename } + + it { expect(event_name).to eq('group_rename') } + it_behaves_like 'includes the required attributes' + + it 'includes old path details' do + allow(group).to receive(:path_before_last_save).and_return('old-path') + + expect(data[:old_path]).to eq(group.path_before_last_save) + expect(data[:old_full_path]).to eq(group.path_before_last_save) + end + end + end + end +end diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb index e5d11fb1218..5c85909a851 100644 --- a/spec/requests/api/maven_packages_spec.rb +++ b/spec/requests/api/maven_packages_spec.rb @@ -32,6 +32,7 @@ RSpec.describe API::MavenPackages do end let(:version) { '1.0-SNAPSHOT' } + let(:param_path) { "#{package_name}/#{version}"} before do project.add_developer(user) @@ -695,6 +696,14 @@ RSpec.describe API::MavenPackages do expect(json_response['message']).to include('Duplicate package is not allowed') end + context 'when uploading to the versionless package which contains metadata about all versions' do + let(:version) { nil } + let(:param_path) { package_name } + let!(:package) { create(:maven_package, project: project, version: version, name: project.full_path) } + + it_behaves_like 'storing the package file' + end + context 'when uploading different non-duplicate files to the same package' do let!(:package) { create(:maven_package, project: project, name: project.full_path) } @@ -744,7 +753,7 @@ RSpec.describe API::MavenPackages do end def upload_file(params: {}, request_headers: headers, file_extension: 'jar') - url = "/projects/#{project.id}/packages/maven/#{package_name}/#{version}/my-app-1.0-20180724.124855-1.#{file_extension}" + url = "/projects/#{project.id}/packages/maven/#{param_path}/my-app-1.0-20180724.124855-1.#{file_extension}" workhorse_finalize( api(url), method: :put, diff --git a/spec/services/packages/maven/find_or_create_package_service_spec.rb b/spec/services/packages/maven/find_or_create_package_service_spec.rb index 82dffeefcde..191a443a837 100644 --- a/spec/services/packages/maven/find_or_create_package_service_spec.rb +++ b/spec/services/packages/maven/find_or_create_package_service_spec.rb @@ -111,6 +111,13 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do expect(subject.errors).to include('Duplicate package is not allowed') end + context 'when uploading to the versionless package which contains metadata about all versions' do + let(:version) { nil } + let(:param_path) { path } + + it_behaves_like 'reuse existing package' + end + context 'when uploading different non-duplicate files to the same package' do before do package_file = existing_package.package_files.find_by(file_name: 'my-app-1.0-20180724.124855-1.jar') diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 2020c67f465..63ed19bd556 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -156,9 +156,6 @@ RSpec.describe SystemHooksService do it { expect(event_name(project_member, :update)).to eq "user_update_for_team" } it { expect(event_name(key, :create)).to eq 'key_create' } it { expect(event_name(key, :destroy)).to eq 'key_destroy' } - it { expect(event_name(group, :create)).to eq 'group_create' } - it { expect(event_name(group, :destroy)).to eq 'group_destroy' } - it { expect(event_name(group, :rename)).to eq 'group_rename' } end def event_data(*args) diff --git a/tooling/danger/helper.rb b/tooling/danger/helper.rb index e50d5af3b6f..70df0dd82c7 100644 --- a/tooling/danger/helper.rb +++ b/tooling/danger/helper.rb @@ -198,7 +198,6 @@ module Tooling # Files that don't fit into any category are marked with :none %r{\A(ee/)?changelogs/} => :none, %r{\Alocale/gitlab\.pot\z} => :none, - %r{\Adata/whats_new/} => :none, # GraphQL auto generated doc files and schema %r{\Adoc/api/graphql/reference/} => :backend, |