Commit 50409745 authored by David Fernandez's avatar David Fernandez Committed by Etienne Baqué

Support SemVer versions on generic packages

Version strings must now be in the [SemVer](https://semver.org/) format
parent 94c5bb3f
---
title: Relax version validation on generic packages
merge_request: 56755
author:
type: changed
......@@ -47,7 +47,7 @@ PUT /projects/:id/packages/generic/:package_name/:package_version/:file_name?sta
| -------------------| --------------- | ---------| -------------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../../../api/README.md#namespaced-path-encoding). |
| `package_name` | string | yes | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`).
| `package_version` | string | yes | The package version. It can contain only numbers (`0-9`), and dots (`.`). Must be in the format of `X.Y.Z`, i.e. should match `/\A\d+\.\d+\.\d+\z/` regular expression.
| `package_version` | string | yes | The package version. The following regex validates this: `\A(\.?[\w\+-]+\.?)+\z`. You can test your version strings on [Rubular](https://rubular.com/r/aNCV0wG5K14uq8).
| `file_name` | string | yes | The filename. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`).
| `status` | string | no | The package status. It can be `default` (default) or `hidden`. Hidden packages do not appear in the UI or [package API list endpoints](../../../api/packages.md).
......
......@@ -181,7 +181,7 @@ module Gitlab
end
def generic_package_version_regex
/\A\d+\.\d+\.\d+\z/
maven_version_regex
end
def generic_package_name_regex
......
......@@ -667,7 +667,14 @@ RSpec.describe Gitlab::Regex do
it { is_expected.to match('1.2.3') }
it { is_expected.to match('1.3.350') }
it { is_expected.not_to match('1.3.350-20201230123456') }
it { is_expected.to match('1.3.350-20201230123456') }
it { is_expected.to match('1.2.3-rc1') }
it { is_expected.to match('1.2.3g') }
it { is_expected.to match('1.2') }
it { is_expected.to match('1.2.bananas') }
it { is_expected.to match('v1.2.4-build') }
it { is_expected.to match('d50d836eb3de6177ce6c7a5482f27f9c2c84b672') }
it { is_expected.to match('this_is_a_string_only') }
it { is_expected.not_to match('..1.2.3') }
it { is_expected.not_to match(' 1.2.3') }
it { is_expected.not_to match("1.2.3 \r\t") }
......
......@@ -367,7 +367,14 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to validate_presence_of(:version) }
it { is_expected.to allow_value('1.2.3').for(:version) }
it { is_expected.to allow_value('1.3.350').for(:version) }
it { is_expected.not_to allow_value('1.3.350-20201230123456').for(:version) }
it { is_expected.to allow_value('1.3.350-20201230123456').for(:version) }
it { is_expected.to allow_value('1.2.3-rc1').for(:version) }
it { is_expected.to allow_value('1.2.3g').for(:version) }
it { is_expected.to allow_value('1.2').for(:version) }
it { is_expected.to allow_value('1.2.bananas').for(:version) }
it { is_expected.to allow_value('v1.2.4-build').for(:version) }
it { is_expected.to allow_value('d50d836eb3de6177ce6c7a5482f27f9c2c84b672').for(:version) }
it { is_expected.to allow_value('this_is_a_string_only').for(:version) }
it { is_expected.not_to allow_value('..1.2.3').for(:version) }
it { is_expected.not_to allow_value(' 1.2.3').for(:version) }
it { is_expected.not_to allow_value("1.2.3 \r\t").for(:version) }
......
......@@ -16,6 +16,7 @@ RSpec.describe API::GenericPackages do
let_it_be(:project_deploy_token_ro) { create(:project_deploy_token, deploy_token: deploy_token_ro, project: project) }
let_it_be(:deploy_token_wo) { create(:deploy_token, read_package_registry: false, write_package_registry: true) }
let_it_be(:project_deploy_token_wo) { create(:project_deploy_token, deploy_token: deploy_token_wo, project: project) }
let(:user) { personal_access_token.user }
let(:ci_build) { create(:ci_build, :running, user: user) }
......@@ -326,6 +327,34 @@ RSpec.describe API::GenericPackages do
end
end
end
context 'different versions' do
where(:version, :expected_status) do
'1.3.350-20201230123456' | :created
'1.2.3' | :created
'1.2.3g' | :created
'1.2' | :created
'1.2.bananas' | :created
'v1.2.4-build' | :created
'd50d836eb3de6177ce6c7a5482f27f9c2c84b672' | :created
'..1.2.3' | :bad_request
'1.2.3-4/../../' | :bad_request
'%2e%2e%2f1.2.3' | :bad_request
end
with_them do
let(:expected_package_diff_count) { expected_status == :created ? 1 : 0 }
let(:headers) { workhorse_headers.merge(auth_header) }
subject { upload_file(params, headers, package_version: version) }
it "returns the #{params[:expected_status]}", :aggregate_failures do
expect { subject }.to change { project.packages.generic.count }.by(expected_package_diff_count)
expect(response).to have_gitlab_http_status(expected_status)
end
end
end
end
end
......@@ -418,8 +447,8 @@ RSpec.describe API::GenericPackages do
end
end
def upload_file(params, request_headers, send_rewritten_field: true, package_name: 'mypackage', file_name: 'myfile.tar.gz')
url = "/projects/#{project.id}/packages/generic/#{package_name}/0.0.1/#{file_name}"
def upload_file(params, request_headers, send_rewritten_field: true, package_name: 'mypackage', package_version: '0.0.1', file_name: 'myfile.tar.gz')
url = "/projects/#{project.id}/packages/generic/#{package_name}/#{package_version}/#{file_name}"
workhorse_finalize(
api(url),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment