Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
3cb09d05
Commit
3cb09d05
authored
Jun 18, 2021
by
Steve Abrams
Committed by
David Fernandez
Jun 18, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
NuGet symbol package support
parent
09ad4f3b
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
386 additions
and
134 deletions
+386
-134
app/models/packages/nuget.rb
app/models/packages/nuget.rb
+1
-0
app/presenters/packages/nuget/service_index_presenter.rb
app/presenters/packages/nuget/service_index_presenter.rb
+9
-1
app/services/packages/nuget/metadata_extraction_service.rb
app/services/packages/nuget/metadata_extraction_service.rb
+6
-0
app/services/packages/nuget/update_package_from_metadata_service.rb
...es/packages/nuget/update_package_from_metadata_service.rb
+19
-2
doc/user/packages/nuget_repository/index.md
doc/user/packages/nuget_repository/index.md
+19
-0
lib/api/nuget_project_packages.rb
lib/api/nuget_project_packages.rb
+87
-26
spec/factories/packages.rb
spec/factories/packages.rb
+6
-0
spec/factories/packages/package_file.rb
spec/factories/packages/package_file.rb
+8
-0
spec/fixtures/packages/nuget/package.snupkg
spec/fixtures/packages/nuget/package.snupkg
+0
-0
spec/fixtures/packages/nuget/with_package_types.nuspec
spec/fixtures/packages/nuget/with_package_types.nuspec
+14
-0
spec/presenters/packages/nuget/service_index_presenter_spec.rb
...presenters/packages/nuget/service_index_presenter_spec.rb
+9
-4
spec/requests/api/nuget_project_packages_spec.rb
spec/requests/api/nuget_project_packages_spec.rb
+32
-95
spec/services/packages/nuget/metadata_extraction_service_spec.rb
...rvices/packages/nuget/metadata_extraction_service_spec.rb
+12
-1
spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
...ckages/nuget/update_package_from_metadata_service_spec.rb
+36
-1
spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
...d_examples/requests/api/nuget_packages_shared_examples.rb
+128
-4
No files found.
app/models/packages/nuget.rb
View file @
3cb09d05
...
...
@@ -2,6 +2,7 @@
module
Packages
module
Nuget
TEMPORARY_PACKAGE_NAME
=
'NuGet.Temporary.Package'
TEMPORARY_SYMBOL_PACKAGE_NAME
=
'NuGet.Temporary.SymbolPackage'
def
self
.
table_name_prefix
'packages_nuget_'
...
...
app/presenters/packages/nuget/service_index_presenter.rb
View file @
3cb09d05
...
...
@@ -8,6 +8,7 @@ module Packages
SERVICE_VERSIONS
=
{
download:
%w[PackageBaseAddress/3.0.0]
,
search:
%w[SearchQueryService SearchQueryService/3.0.0-beta SearchQueryService/3.0.0-rc]
,
symbol:
%w[SymbolPackagePublish/4.9.0]
,
publish:
%w[PackagePublish/2.0.0]
,
metadata:
%w[RegistrationsBaseUrl RegistrationsBaseUrl/3.0.0-beta RegistrationsBaseUrl/3.0.0-rc]
}.
freeze
...
...
@@ -15,13 +16,14 @@ module Packages
SERVICE_COMMENTS
=
{
download:
'Get package content (.nupkg).'
,
search:
'Filter and search for packages by keyword.'
,
symbol:
'Push symbol packages.'
,
publish:
'Push and delete (or unlist) packages.'
,
metadata:
'Get package metadata.'
}.
freeze
VERSION
=
'3.0.0'
PROJECT_LEVEL_SERVICES
=
%i[download publish]
.
freeze
PROJECT_LEVEL_SERVICES
=
%i[download publish
symbol
]
.
freeze
GROUP_LEVEL_SERVICES
=
%i[search metadata]
.
freeze
def
initialize
(
project_or_group
)
...
...
@@ -63,6 +65,8 @@ module Packages
download_service_url
when
:search
search_service_url
when
:symbol
symbol_service_url
when
:metadata
metadata_service_url
when
:publish
...
...
@@ -124,6 +128,10 @@ module Packages
def
publish_service_url
api_v4_projects_packages_nuget_path
(
id:
@project_or_group
.
id
)
end
def
symbol_service_url
api_v4_projects_packages_nuget_symbolpackage_path
(
id:
@project_or_group
.
id
)
end
end
end
end
app/services/packages/nuget/metadata_extraction_service.rb
View file @
3cb09d05
...
...
@@ -18,6 +18,7 @@ module Packages
XPATH_DEPENDENCIES
=
'//xmlns:package/xmlns:metadata/xmlns:dependencies/xmlns:dependency'
XPATH_DEPENDENCY_GROUPS
=
'//xmlns:package/xmlns:metadata/xmlns:dependencies/xmlns:group'
XPATH_TAGS
=
'//xmlns:package/xmlns:metadata/xmlns:tags'
XPATH_PACKAGE_TYPES
=
'//xmlns:package/xmlns:metadata/xmlns:packageTypes/xmlns:packageType'
MAX_FILE_SIZE
=
4
.
megabytes
.
freeze
...
...
@@ -57,6 +58,7 @@ module Packages
.
tap
do
|
metadata
|
metadata
[
:package_dependencies
]
=
extract_dependencies
(
doc
)
metadata
[
:package_tags
]
=
extract_tags
(
doc
)
metadata
[
:package_types
]
=
extract_package_types
(
doc
)
end
end
...
...
@@ -85,6 +87,10 @@ module Packages
}.
compact
end
def
extract_package_types
(
doc
)
doc
.
xpath
(
XPATH_PACKAGE_TYPES
).
map
{
|
node
|
node
.
attr
(
'name'
)
}.
uniq
end
def
extract_tags
(
doc
)
tags
=
doc
.
xpath
(
XPATH_TAGS
).
text
...
...
app/services/packages/nuget/update_package_from_metadata_service.rb
View file @
3cb09d05
...
...
@@ -8,6 +8,7 @@ module Packages
# used by ExclusiveLeaseGuard
DEFAULT_LEASE_TIMEOUT
=
1
.
hour
.
to_i
.
freeze
SYMBOL_PACKAGE_IDENTIFIER
=
'SymbolsPackage'
InvalidMetadataError
=
Class
.
new
(
StandardError
)
...
...
@@ -20,7 +21,13 @@ module Packages
try_obtain_lease
do
@package_file
.
transaction
do
package
=
existing_package
?
link_to_existing_package
:
update_linked_package
if
existing_package
package
=
link_to_existing_package
elsif
symbol_package?
raise
InvalidMetadataError
,
'symbol package is invalid, matching package does not exist'
else
package
=
update_linked_package
end
update_package
(
package
)
...
...
@@ -39,6 +46,8 @@ module Packages
private
def
update_package
(
package
)
return
if
symbol_package?
::
Packages
::
Nuget
::
SyncMetadatumService
.
new
(
package
,
metadata
.
slice
(
:project_url
,
:license_url
,
:icon_url
))
.
execute
...
...
@@ -103,6 +112,14 @@ module Packages
metadata
.
fetch
(
:package_tags
,
[])
end
def
package_types
metadata
.
fetch
(
:package_types
,
[])
end
def
symbol_package?
package_types
.
include?
(
SYMBOL_PACKAGE_IDENTIFIER
)
end
def
metadata
strong_memoize
(
:metadata
)
do
::
Packages
::
Nuget
::
MetadataExtractionService
.
new
(
@package_file
.
id
).
execute
...
...
@@ -110,7 +127,7 @@ module Packages
end
def
package_filename
"
#{
package_name
.
downcase
}
.
#{
package_version
.
downcase
}
.
nupkg
"
"
#{
package_name
.
downcase
}
.
#{
package_version
.
downcase
}
.
#{
symbol_package?
?
'snupkg'
:
'nupkg'
}
"
end
# used by ExclusiveLeaseGuard
...
...
doc/user/packages/nuget_repository/index.md
View file @
3cb09d05
...
...
@@ -8,6 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20050) in GitLab Premium 12.8.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) to GitLab Free in 13.3.
> - Symbol package support [added](https://gitlab.com/gitlab-org/gitlab/-/issues/262081) in GitLab 14.1.
Publish NuGet packages in your project's Package Registry. Then, install the
packages whenever you need to use them as a dependency.
...
...
@@ -394,6 +395,24 @@ dotnet add package <package_id> \
-
`<package_id>`
is the package ID.
-
`<package_version>`
is the package version. Optional.
## Symbol packages
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/262081) in GitLab 14.1.
If you push a
`.nupkg`
, symbol package files in the
`.snupkg`
format are uploaded automatically. You
can also push them manually:
```
shell
nuget push My.Package.snupkg
-Source
<source_name>
```
Consuming symbol packages is not yet guaranteed using clients such as Visual Studio or
dotnet-symbol. The
`.snupkg`
files are available for download through the UI or the
[
API
](
../../../api/packages/nuget.md#download-a-package-file
)
.
Follow the
[
NuGet symbol package issue
](
https://gitlab.com/gitlab-org/gitlab/-/issues/262081
)
for further updates.
## Supported CLI commands
The GitLab NuGet repository supports the following commands for the NuGet CLI (
`nuget`
) and the .NET
...
...
lib/api/nuget_project_packages.rb
View file @
3cb09d05
...
...
@@ -16,6 +16,7 @@ module API
feature_category
:package_registry
PACKAGE_FILENAME
=
'package.nupkg'
SYMBOL_PACKAGE_FILENAME
=
'package.snupkg'
default_format
:json
...
...
@@ -33,6 +34,10 @@ module API
end
helpers
do
params
:file_params
do
requires
:package
,
type:
::
API
::
Validations
::
Types
::
WorkhorseFile
,
desc:
'The package file to be published (generated by Multipart middleware)'
end
def
project_or_group
authorized_user_project
end
...
...
@@ -40,6 +45,49 @@ module API
def
snowplow_gitlab_standard_context
{
project:
authorized_user_project
,
namespace:
authorized_user_project
.
namespace
}
end
def
authorize_nuget_upload
authorize_workhorse!
(
subject:
project_or_group
,
has_length:
false
,
maximum_size:
project_or_group
.
actual_limits
.
nuget_max_file_size
)
end
def
temp_file_name
(
symbol_package
)
return
::
Packages
::
Nuget
::
TEMPORARY_SYMBOL_PACKAGE_NAME
if
symbol_package
::
Packages
::
Nuget
::
TEMPORARY_PACKAGE_NAME
end
def
file_name
(
symbol_package
)
return
SYMBOL_PACKAGE_FILENAME
if
symbol_package
PACKAGE_FILENAME
end
def
upload_nuget_package_file
(
symbol_package:
false
)
authorize_upload!
(
project_or_group
)
bad_request!
(
'File is too large'
)
if
project_or_group
.
actual_limits
.
exceeded?
(
:nuget_max_file_size
,
params
[
:package
].
size
)
file_params
=
params
.
merge
(
file:
params
[
:package
],
file_name:
file_name
(
symbol_package
)
)
package
=
::
Packages
::
CreateTemporaryPackageService
.
new
(
project_or_group
,
current_user
,
declared_params
.
merge
(
build:
current_authenticated_job
)
).
execute
(
:nuget
,
name:
temp_file_name
(
symbol_package
))
package_file
=
::
Packages
::
CreatePackageFileService
.
new
(
package
,
file_params
.
merge
(
build:
current_authenticated_job
))
.
execute
yield
(
package
)
if
block_given?
::
Packages
::
Nuget
::
ExtractionWorker
.
perform_async
(
package_file
.
id
)
# rubocop:disable CodeReuse/Worker
created!
end
end
params
do
...
...
@@ -55,40 +103,45 @@ module API
end
params
do
requires
:package
,
type:
::
API
::
Validations
::
Types
::
WorkhorseFile
,
desc:
'The package file to be published (generated by Multipart middleware)'
use
:file_params
end
put
do
authorize_upload!
(
project_or_group
)
bad_request!
(
'File is too large'
)
if
project_or_group
.
actual_limits
.
exceeded?
(
:nuget_max_file_size
,
params
[
:package
].
size
)
file_params
=
params
.
merge
(
file:
params
[
:package
],
file_name:
PACKAGE_FILENAME
)
package
=
::
Packages
::
CreateTemporaryPackageService
.
new
(
project_or_group
,
current_user
,
declared_params
.
merge
(
build:
current_authenticated_job
)
).
execute
(
:nuget
,
name:
::
Packages
::
Nuget
::
TEMPORARY_PACKAGE_NAME
)
package_file
=
::
Packages
::
CreatePackageFileService
.
new
(
package
,
file_params
.
merge
(
build:
current_authenticated_job
))
.
execute
upload_nuget_package_file
do
|
package
|
track_package_event
(
'push_package'
,
:nuget
,
category:
'API::NugetPackages'
,
user:
current_user
,
project:
package
.
project
,
namespace:
package
.
project
.
namespace
)
end
rescue
ObjectStorage
::
RemoteStoreError
=>
e
Gitlab
::
ErrorTracking
.
track_exception
(
e
,
extra:
{
file_name:
params
[
:file_name
],
project_id:
project_or_group
.
id
})
track_package_event
(
'push_package'
,
:nuget
,
category:
'API::NugetPackages'
,
user:
current_user
,
project:
package
.
project
,
namespace:
package
.
project
.
namespace
)
forbidden!
end
put
'authorize'
do
authorize_nuget_upload
end
::
Packages
::
Nuget
::
ExtractionWorker
.
perform_async
(
package_file
.
id
)
# rubocop:disable CodeReuse/Worker
# https://docs.microsoft.com/en-us/nuget/api/symbol-package-publish-resource
desc
'The NuGet Symbol Package Publish endpoint'
do
detail
'This feature was introduced in GitLab 14.1'
end
created!
params
do
use
:file_params
end
put
'symbolpackage'
do
upload_nuget_package_file
(
symbol_package:
true
)
rescue
ObjectStorage
::
RemoteStoreError
=>
e
Gitlab
::
ErrorTracking
.
track_exception
(
e
,
extra:
{
file_name:
params
[
:file_name
],
project_id:
project_or_group
.
id
})
forbidden!
end
put
'authorize'
do
authorize_workhorse!
(
subject:
project_or_group
,
has_length:
false
,
maximum_size:
project_or_group
.
actual_limits
.
nuget_max_file_size
)
put
'symbolpackage/authorize'
do
authorize_nuget_upload
end
# https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
...
...
@@ -115,14 +168,22 @@ module API
requires
:package_version
,
type:
String
,
desc:
'The NuGet package version'
,
regexp:
API
::
NO_SLASH_URL_PART_REGEX
requires
:package_filename
,
type:
String
,
desc:
'The NuGet package filename'
,
regexp:
API
::
NO_SLASH_URL_PART_REGEX
end
get
'*package_version/*package_filename'
,
format:
:nupkg
do
get
'*package_version/*package_filename'
,
format:
[
:nupkg
,
:snupkg
]
do
filename
=
"
#{
params
[
:package_filename
]
}
.
#{
params
[
:format
]
}
"
package_file
=
::
Packages
::
PackageFileFinder
.
new
(
find_package
(
params
[
:package_name
],
params
[
:package_version
]),
filename
,
with_file_name_like:
true
)
.
execute
not_found!
(
'Package'
)
unless
package_file
track_package_event
(
'pull_package'
,
:nuget
,
category:
'API::NugetPackages'
,
project:
package_file
.
project
,
namespace:
package_file
.
project
.
namespace
)
if
params
[
:format
]
==
'nupkg'
track_package_event
(
'pull_package'
,
:nuget
,
category:
'API::NugetPackages'
,
project:
package_file
.
project
,
namespace:
package_file
.
project
.
namespace
)
end
# nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false
present_carrierwave_file!
(
package_file
.
file
,
supports_direct_download:
false
)
...
...
spec/factories/packages.rb
View file @
3cb09d05
...
...
@@ -162,6 +162,12 @@ FactoryBot.define do
pkg
.
nuget_metadatum
=
build
(
:nuget_metadatum
)
end
end
trait
(
:with_symbol_package
)
do
after
:create
do
|
package
|
create
:package_file
,
:snupkg
,
package:
package
,
file_name:
"
#{
package
.
name
}
.
#{
package
.
version
}
.snupkg"
end
end
end
factory
:pypi_package
do
...
...
spec/factories/packages/package_file.rb
View file @
3cb09d05
...
...
@@ -271,6 +271,14 @@ FactoryBot.define do
size
{
300
.
kilobytes
}
end
trait
(
:snupkg
)
do
package
file_fixture
{
'spec/fixtures/packages/nuget/package.snupkg'
}
file_name
{
'package.snupkg'
}
file_sha1
{
'5fe852b2a6abd96c22c11fa1ff2fb19d9ce58b57'
}
size
{
300
.
kilobytes
}
end
trait
(
:gem
)
do
package
file_fixture
{
'spec/fixtures/packages/rubygems/package-0.0.1.gem'
}
...
...
spec/fixtures/packages/nuget/package.snupkg
0 → 100644
View file @
3cb09d05
File added
spec/fixtures/packages/nuget/with_package_types.nuspec
0 → 100644
View file @
3cb09d05
<?xml version="1.0" encoding="utf-8"?>
<package
xmlns=
"http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"
>
<metadata>
<id>
Test.Package
</id>
<version>
3.5.2
</version>
<authors>
Test Author
</authors>
<owners>
Test Owner
</owners>
<requireLicenseAcceptance>
false
</requireLicenseAcceptance>
<description>
Package Description
</description>
<packageTypes>
<packageType
name=
"SymbolsPackage"
/>
</packageTypes>
</metadata>
</package>
spec/presenters/packages/nuget/service_index_presenter_spec.rb
View file @
3cb09d05
...
...
@@ -27,7 +27,7 @@ RSpec.describe ::Packages::Nuget::ServiceIndexPresenter do
describe
'#resources'
do
subject
{
presenter
.
resources
}
shared_examples
'returning valid resources'
do
|
resources_count:
8
,
include_publish_service:
true
|
shared_examples
'returning valid resources'
do
|
resources_count:
9
,
include_publish_service:
true
|
it
'has valid resources'
do
expect
(
subject
.
size
).
to
eq
resources_count
subject
.
each
do
|
resource
|
...
...
@@ -38,10 +38,15 @@ RSpec.describe ::Packages::Nuget::ServiceIndexPresenter do
end
end
it
"does
#{
'not '
unless
include_publish_service
}
return the publish resource"
do
it
"does
#{
'not '
unless
include_publish_service
}
return the publish resource"
,
:aggregate_failures
do
services_types
=
subject
.
map
{
|
res
|
res
[
:@type
]
}
described_class
::
SERVICE_VERSIONS
[
:publish
].
each
do
|
publish_service_version
|
publish_service_versions
=
[
described_class
::
SERVICE_VERSIONS
[
:publish
],
described_class
::
SERVICE_VERSIONS
[
:symbol
]
].
flatten
publish_service_versions
.
each
do
|
publish_service_version
|
if
include_publish_service
expect
(
services_types
).
to
include
(
publish_service_version
)
else
...
...
@@ -54,7 +59,7 @@ RSpec.describe ::Packages::Nuget::ServiceIndexPresenter do
context
'for a group'
do
let
(
:target
)
{
group
}
# at the group level we don't have the publish and download service
# at the group level we don't have the publish
, symbol,
and download service
it_behaves_like
'returning valid resources'
,
resources_count:
6
,
include_publish_service:
false
end
...
...
spec/requests/api/nuget_project_packages_spec.rb
View file @
3cb09d05
...
...
@@ -92,9 +92,10 @@ RSpec.describe API::NugetProjectPackages do
describe
'GET /api/v4/projects/:id/packages/nuget/download/*package_name/*package_version/*package_filename'
do
let_it_be
(
:package_name
)
{
'Dummy.Package'
}
let_it_be
(
:package
)
{
create
(
:nuget_package
,
project:
project
,
name:
package_name
)
}
let_it_be
(
:package
)
{
create
(
:nuget_package
,
:with_symbol_package
,
project:
project
,
name:
package_name
)
}
let
(
:url
)
{
"/projects/
#{
target
.
id
}
/packages/nuget/download/
#{
package
.
name
}
/
#{
package
.
version
}
/
#{
package
.
name
}
.
#{
package
.
version
}
.nupkg"
}
let
(
:format
)
{
'nupkg'
}
let
(
:url
)
{
"/projects/
#{
target
.
id
}
/packages/nuget/download/
#{
package
.
name
}
/
#{
package
.
version
}
/
#{
package
.
name
}
.
#{
package
.
version
}
.
#{
format
}
"
}
subject
{
get
api
(
url
)
}
...
...
@@ -154,50 +155,7 @@ RSpec.describe API::NugetProjectPackages do
subject
{
put
api
(
url
),
headers:
headers
}
context
'with valid project'
do
where
(
:visibility_level
,
:user_role
,
:member
,
:user_token
,
:shared_examples_name
,
:expected_status
)
do
'PUBLIC'
|
:developer
|
true
|
true
|
'process nuget workhorse authorization'
|
:success
'PUBLIC'
|
:guest
|
true
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:developer
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:guest
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:developer
|
false
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:guest
|
false
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:developer
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:guest
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:anonymous
|
false
|
true
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:developer
|
true
|
true
|
'process nuget workhorse authorization'
|
:success
'PRIVATE'
|
:guest
|
true
|
true
|
'rejects nuget packages access'
|
:forbidden
'PRIVATE'
|
:developer
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:guest
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:developer
|
false
|
true
|
'rejects nuget packages access'
|
:not_found
'PRIVATE'
|
:guest
|
false
|
true
|
'rejects nuget packages access'
|
:not_found
'PRIVATE'
|
:developer
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:guest
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:anonymous
|
false
|
true
|
'rejects nuget packages access'
|
:unauthorized
end
with_them
do
let
(
:token
)
{
user_token
?
personal_access_token
.
token
:
'wrong'
}
let
(
:user_headers
)
{
user_role
==
:anonymous
?
{}
:
basic_auth_header
(
user
.
username
,
token
)
}
let
(
:headers
)
{
user_headers
.
merge
(
workhorse_headers
)
}
before
do
update_visibility_to
(
Gitlab
::
VisibilityLevel
.
const_get
(
visibility_level
,
false
))
end
it_behaves_like
params
[
:shared_examples_name
],
params
[
:user_role
],
params
[
:expected_status
],
params
[
:member
]
end
end
it_behaves_like
'deploy token for package uploads'
it_behaves_like
'job token for package uploads'
,
authorize_endpoint:
true
do
let_it_be
(
:job
)
{
create
(
:ci_build
,
:running
,
user:
user
,
project:
project
)
}
end
it_behaves_like
'rejects nuget access with unknown target id'
it_behaves_like
'rejects nuget access with invalid target id'
it_behaves_like
'nuget authorize upload endpoint'
end
describe
'PUT /api/v4/projects/:id/packages/nuget'
do
...
...
@@ -221,63 +179,42 @@ RSpec.describe API::NugetProjectPackages do
)
end
context
'with valid project'
do
where
(
:visibility_level
,
:user_role
,
:member
,
:user_token
,
:shared_examples_name
,
:expected_status
)
do
'PUBLIC'
|
:developer
|
true
|
true
|
'process nuget upload'
|
:created
'PUBLIC'
|
:guest
|
true
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:developer
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:guest
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:developer
|
false
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:guest
|
false
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:developer
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:guest
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:anonymous
|
false
|
true
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:developer
|
true
|
true
|
'process nuget upload'
|
:created
'PRIVATE'
|
:guest
|
true
|
true
|
'rejects nuget packages access'
|
:forbidden
'PRIVATE'
|
:developer
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:guest
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:developer
|
false
|
true
|
'rejects nuget packages access'
|
:not_found
'PRIVATE'
|
:guest
|
false
|
true
|
'rejects nuget packages access'
|
:not_found
'PRIVATE'
|
:developer
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:guest
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:anonymous
|
false
|
true
|
'rejects nuget packages access'
|
:unauthorized
end
with_them
do
let
(
:token
)
{
user_token
?
personal_access_token
.
token
:
'wrong'
}
let
(
:user_headers
)
{
user_role
==
:anonymous
?
{}
:
basic_auth_header
(
user
.
username
,
token
)
}
let
(
:headers
)
{
user_headers
.
merge
(
workhorse_headers
)
}
let
(
:snowplow_gitlab_standard_context
)
{
{
project:
project
,
user:
user
,
namespace:
project
.
namespace
}
}
before
do
update_visibility_to
(
Gitlab
::
VisibilityLevel
.
const_get
(
visibility_level
,
false
))
end
it_behaves_like
params
[
:shared_examples_name
],
params
[
:user_role
],
params
[
:expected_status
],
params
[
:member
]
end
end
it_behaves_like
'nuget upload endpoint'
end
it_behaves_like
'deploy token for package uploads'
describe
'PUT /api/v4/projects/:id/packages/nuget/symbolpackage/authorize'
do
include_context
'workhorse headers'
it_behaves_like
'job token for package uploads'
do
let_it_be
(
:job
)
{
create
(
:ci_build
,
:running
,
user:
user
,
project:
project
)
}
end
let
(
:url
)
{
"/projects/
#{
target
.
id
}
/packages/nuget/symbolpackage/authorize"
}
let
(
:headers
)
{
{}
}
it_behaves_like
'rejects nuget access with unknown target id'
subject
{
put
api
(
url
),
headers:
headers
}
it_behaves_like
'rejects nuget access with invalid target id'
it_behaves_like
'nuget authorize upload endpoint'
end
context
'file size above maximum limit
'
do
let
(
:headers
)
{
basic_auth_header
(
deploy_token
.
username
,
deploy_token
.
token
).
merge
(
workhorse_headers
)
}
describe
'PUT /api/v4/projects/:id/packages/nuget/symbolpackage
'
do
include_context
'workhorse headers'
before
do
allow_next_instance_of
(
UploadedFile
)
do
|
uploaded_file
|
allow
(
uploaded_file
).
to
receive
(
:size
).
and_return
(
project
.
actual_limits
.
nuget_max_file_size
+
1
)
end
end
let_it_be
(
:file_name
)
{
'package.snupkg'
}
let
(
:url
)
{
"/projects/
#{
target
.
id
}
/packages/nuget/symbolpackage"
}
let
(
:headers
)
{
{}
}
let
(
:params
)
{
{
package:
temp_file
(
file_name
)
}
}
let
(
:file_key
)
{
:package
}
let
(
:send_rewritten_field
)
{
true
}
it_behaves_like
'returning response status'
,
:bad_request
subject
do
workhorse_finalize
(
api
(
url
),
method: :put
,
file_key:
file_key
,
params:
params
,
headers:
headers
,
send_rewritten_field:
send_rewritten_field
)
end
it_behaves_like
'nuget upload endpoint'
,
symbol_package:
true
end
def
update_visibility_to
(
visibility
)
...
...
spec/services/packages/nuget/metadata_extraction_service_spec.rb
View file @
3cb09d05
...
...
@@ -21,7 +21,8 @@ RSpec.describe Packages::Nuget::MetadataExtractionService do
version:
'12.0.3'
}
],
package_tags:
[]
package_tags:
[],
package_types:
[]
}
it
{
is_expected
.
to
eq
(
expected_metadata
)
}
...
...
@@ -47,6 +48,16 @@ RSpec.describe Packages::Nuget::MetadataExtractionService do
end
end
context
'with package types'
do
let
(
:nuspec_filepath
)
{
'packages/nuget/with_package_types.nuspec'
}
it
{
is_expected
.
to
have_key
(
:package_types
)
}
it
'extracts package types'
do
expect
(
subject
[
:package_types
]).
to
include
(
'SymbolsPackage'
)
end
end
context
'with a nuspec file with metadata'
do
let
(
:nuspec_filepath
)
{
'packages/nuget/with_metadata.nuspec'
}
...
...
spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
View file @
3cb09d05
...
...
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec
.
describe
Packages
::
Nuget
::
UpdatePackageFromMetadataService
,
:clean_gitlab_redis_shared_state
do
include
ExclusiveLeaseHelpers
let
(
:package
)
{
create
(
:nuget_package
,
:processing
)
}
let
(
:package
)
{
create
(
:nuget_package
,
:processing
,
:with_symbol_package
)
}
let
(
:package_file
)
{
package
.
package_files
.
first
}
let
(
:service
)
{
described_class
.
new
(
package_file
)
}
let
(
:package_name
)
{
'DummyProject.DummyPackage'
}
...
...
@@ -201,6 +201,41 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_
it_behaves_like
'raising an'
,
::
Packages
::
Nuget
::
MetadataExtractionService
::
ExtractionError
end
context
'with a symbol package'
do
let
(
:package_file
)
{
package
.
package_files
.
last
}
let
(
:package_file_name
)
{
'dummyproject.dummypackage.1.0.0.snupkg'
}
context
'with no existing package'
do
let
(
:package_id
)
{
package
.
id
}
it_behaves_like
'raising an'
,
::
Packages
::
Nuget
::
UpdatePackageFromMetadataService
::
InvalidMetadataError
end
context
'with existing package'
do
let!
(
:existing_package
)
{
create
(
:nuget_package
,
project:
package
.
project
,
name:
package_name
,
version:
package_version
)
}
let
(
:package_id
)
{
existing_package
.
id
}
it
'link existing package and updates package file'
,
:aggregate_failures
do
expect
(
service
).
to
receive
(
:try_obtain_lease
).
and_call_original
expect
(
::
Packages
::
Nuget
::
SyncMetadatumService
).
not_to
receive
(
:new
)
expect
(
::
Packages
::
UpdateTagsService
).
not_to
receive
(
:new
)
expect
{
subject
}
.
to
change
{
::
Packages
::
Package
.
count
}.
by
(
-
1
)
.
and
change
{
Packages
::
Dependency
.
count
}.
by
(
0
)
.
and
change
{
Packages
::
DependencyLink
.
count
}.
by
(
0
)
.
and
change
{
Packages
::
Nuget
::
DependencyLinkMetadatum
.
count
}.
by
(
0
)
.
and
change
{
::
Packages
::
Nuget
::
Metadatum
.
count
}.
by
(
0
)
expect
(
package_file
.
reload
.
file_name
).
to
eq
(
package_file_name
)
expect
(
package_file
.
package
).
to
eq
(
existing_package
)
end
it_behaves_like
'taking the lease'
it_behaves_like
'not updating the package if the lease is taken'
end
end
context
'with an invalid package name'
do
invalid_names
=
[
''
,
...
...
spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
View file @
3cb09d05
...
...
@@ -136,8 +136,8 @@ RSpec.shared_examples 'process nuget workhorse authorization' do |user_type, sta
end
end
RSpec
.
shared_examples
'process nuget upload'
do
|
user_type
,
status
,
add_member
=
true
|
RSpec
.
shared_examples
'creates nuget package files'
do
RSpec
.
shared_examples
'process nuget upload'
do
|
user_type
,
status
,
add_member
=
true
,
symbol_package
=
false
|
shared_examples
'creates nuget package files'
do
it
'creates package files'
do
expect
(
::
Packages
::
Nuget
::
ExtractionWorker
).
to
receive
(
:perform_async
).
once
expect
{
subject
}
...
...
@@ -146,7 +146,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
expect
(
response
).
to
have_gitlab_http_status
(
status
)
package_file
=
target
.
packages
.
last
.
package_files
.
reload
.
last
expect
(
package_file
.
file_name
).
to
eq
(
'package.nupkg'
)
expect
(
package_file
.
file_name
).
to
eq
(
file_name
)
end
end
...
...
@@ -169,7 +169,10 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
context
'with correct params'
do
it_behaves_like
'package workhorse uploads'
it_behaves_like
'creates nuget package files'
it_behaves_like
'a package tracking event'
,
'API::NugetPackages'
,
'push_package'
unless
symbol_package
it_behaves_like
'a package tracking event'
,
'API::NugetPackages'
,
'push_package'
end
end
end
...
...
@@ -300,6 +303,16 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st
it_behaves_like
'rejects nuget packages access'
,
:anonymous
,
:not_found
end
context
'with symbol package'
do
let
(
:format
)
{
'snupkg'
}
it
'returns a valid package archive'
do
subject
expect
(
response
.
media_type
).
to
eq
(
'application/octet-stream'
)
end
end
context
'with lower case package name'
do
let_it_be
(
:package_name
)
{
'dummy.package'
}
...
...
@@ -407,3 +420,114 @@ RSpec.shared_examples 'rejects nuget access with unknown target id' do
end
end
end
RSpec
.
shared_examples
'nuget authorize upload endpoint'
do
using
RSpec
::
Parameterized
::
TableSyntax
context
'with valid project'
do
where
(
:visibility_level
,
:user_role
,
:member
,
:user_token
,
:shared_examples_name
,
:expected_status
)
do
'PUBLIC'
|
:developer
|
true
|
true
|
'process nuget workhorse authorization'
|
:success
'PUBLIC'
|
:guest
|
true
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:developer
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:guest
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:developer
|
false
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:guest
|
false
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:developer
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:guest
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:anonymous
|
false
|
true
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:developer
|
true
|
true
|
'process nuget workhorse authorization'
|
:success
'PRIVATE'
|
:guest
|
true
|
true
|
'rejects nuget packages access'
|
:forbidden
'PRIVATE'
|
:developer
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:guest
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:developer
|
false
|
true
|
'rejects nuget packages access'
|
:not_found
'PRIVATE'
|
:guest
|
false
|
true
|
'rejects nuget packages access'
|
:not_found
'PRIVATE'
|
:developer
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:guest
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:anonymous
|
false
|
true
|
'rejects nuget packages access'
|
:unauthorized
end
with_them
do
let
(
:token
)
{
user_token
?
personal_access_token
.
token
:
'wrong'
}
let
(
:user_headers
)
{
user_role
==
:anonymous
?
{}
:
basic_auth_header
(
user
.
username
,
token
)
}
let
(
:headers
)
{
user_headers
.
merge
(
workhorse_headers
)
}
before
do
update_visibility_to
(
Gitlab
::
VisibilityLevel
.
const_get
(
visibility_level
,
false
))
end
it_behaves_like
params
[
:shared_examples_name
],
params
[
:user_role
],
params
[
:expected_status
],
params
[
:member
]
end
end
it_behaves_like
'deploy token for package uploads'
it_behaves_like
'job token for package uploads'
,
authorize_endpoint:
true
do
let_it_be
(
:job
)
{
create
(
:ci_build
,
:running
,
user:
user
,
project:
project
)
}
end
it_behaves_like
'rejects nuget access with unknown target id'
it_behaves_like
'rejects nuget access with invalid target id'
end
RSpec
.
shared_examples
'nuget upload endpoint'
do
|
symbol_package:
false
|
using
RSpec
::
Parameterized
::
TableSyntax
context
'with valid project'
do
where
(
:visibility_level
,
:user_role
,
:member
,
:user_token
,
:shared_examples_name
,
:expected_status
)
do
'PUBLIC'
|
:developer
|
true
|
true
|
'process nuget upload'
|
:created
'PUBLIC'
|
:guest
|
true
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:developer
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:guest
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:developer
|
false
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:guest
|
false
|
true
|
'rejects nuget packages access'
|
:forbidden
'PUBLIC'
|
:developer
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:guest
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PUBLIC'
|
:anonymous
|
false
|
true
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:developer
|
true
|
true
|
'process nuget upload'
|
:created
'PRIVATE'
|
:guest
|
true
|
true
|
'rejects nuget packages access'
|
:forbidden
'PRIVATE'
|
:developer
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:guest
|
true
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:developer
|
false
|
true
|
'rejects nuget packages access'
|
:not_found
'PRIVATE'
|
:guest
|
false
|
true
|
'rejects nuget packages access'
|
:not_found
'PRIVATE'
|
:developer
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:guest
|
false
|
false
|
'rejects nuget packages access'
|
:unauthorized
'PRIVATE'
|
:anonymous
|
false
|
true
|
'rejects nuget packages access'
|
:unauthorized
end
with_them
do
let
(
:token
)
{
user_token
?
personal_access_token
.
token
:
'wrong'
}
let
(
:user_headers
)
{
user_role
==
:anonymous
?
{}
:
basic_auth_header
(
user
.
username
,
token
)
}
let
(
:headers
)
{
user_headers
.
merge
(
workhorse_headers
)
}
let
(
:snowplow_gitlab_standard_context
)
{
{
project:
project
,
user:
user
,
namespace:
project
.
namespace
}
}
before
do
update_visibility_to
(
Gitlab
::
VisibilityLevel
.
const_get
(
visibility_level
,
false
))
end
it_behaves_like
params
[
:shared_examples_name
],
params
[
:user_role
],
params
[
:expected_status
],
params
[
:member
],
symbol_package
end
end
it_behaves_like
'deploy token for package uploads'
it_behaves_like
'job token for package uploads'
do
let_it_be
(
:job
)
{
create
(
:ci_build
,
:running
,
user:
user
,
project:
project
)
}
end
it_behaves_like
'rejects nuget access with unknown target id'
it_behaves_like
'rejects nuget access with invalid target id'
context
'file size above maximum limit'
do
let
(
:headers
)
{
basic_auth_header
(
deploy_token
.
username
,
deploy_token
.
token
).
merge
(
workhorse_headers
)
}
before
do
allow_next_instance_of
(
UploadedFile
)
do
|
uploaded_file
|
allow
(
uploaded_file
).
to
receive
(
:size
).
and_return
(
project
.
actual_limits
.
nuget_max_file_size
+
1
)
end
end
it_behaves_like
'returning response status'
,
:bad_request
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment