139 lines
4.6 KiB
Ruby
Executable file
139 lines
4.6 KiB
Ruby
Executable file
# frozen_string_literal: true
|
|
module API
|
|
class GoProxy < ::API::Base
|
|
helpers Gitlab::Golang
|
|
helpers ::API::Helpers::PackagesHelpers
|
|
|
|
feature_category :package_registry
|
|
|
|
# basic semver, except case encoded (A => !a)
|
|
MODULE_VERSION_REGEX = /v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([-.!a-z0-9]+))?(?:\+([-.!a-z0-9]+))?/.freeze
|
|
|
|
MODULE_VERSION_REQUIREMENTS = { module_version: MODULE_VERSION_REGEX }.freeze
|
|
|
|
content_type :txt, 'text/plain'
|
|
|
|
before { require_packages_enabled! }
|
|
|
|
helpers do
|
|
def case_decode(str)
|
|
# Converts "github.com/!azure" to "github.com/Azure"
|
|
#
|
|
# From `go help goproxy`:
|
|
#
|
|
# > To avoid problems when serving from case-sensitive file systems,
|
|
# > the <module> and <version> elements are case-encoded, replacing
|
|
# > every uppercase letter with an exclamation mark followed by the
|
|
# > corresponding lower-case letter: github.com/Azure encodes as
|
|
# > github.com/!azure.
|
|
|
|
str.gsub(/![[:alpha:]]/) { |s| s[1..].upcase }
|
|
end
|
|
|
|
def find_project!(id)
|
|
# based on API::Helpers::Packages::BasicAuthHelpers#authorized_project_find!
|
|
|
|
project = find_project(id)
|
|
|
|
return project if project && can?(current_user, :read_project, project)
|
|
|
|
if current_user
|
|
not_found!('Project')
|
|
else
|
|
unauthorized!
|
|
end
|
|
end
|
|
|
|
def find_module
|
|
not_found! unless Feature.enabled?(:go_proxy, user_project)
|
|
|
|
module_name = case_decode params[:module_name]
|
|
bad_request!('Module Name') if module_name.blank?
|
|
|
|
mod = ::Packages::Go::ModuleFinder.new(user_project, module_name).execute
|
|
|
|
not_found! unless mod
|
|
|
|
mod
|
|
end
|
|
|
|
def find_version
|
|
module_version = case_decode params[:module_version]
|
|
ver = ::Packages::Go::VersionFinder.new(find_module).find(module_version)
|
|
|
|
not_found! unless ver&.valid?
|
|
|
|
ver
|
|
|
|
rescue ArgumentError
|
|
not_found!
|
|
end
|
|
end
|
|
|
|
params do
|
|
requires :id, type: String, desc: 'The ID of a project'
|
|
requires :module_name, type: String, desc: 'Module name', coerce_with: ->(val) { CGI.unescape(val) }
|
|
end
|
|
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
|
|
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
|
before do
|
|
authorize_read_package!
|
|
end
|
|
|
|
namespace ':id/packages/go/*module_name/@v' do
|
|
desc 'Get all tagged versions for a given Go module' do
|
|
detail 'See `go help goproxy`, GET $GOPROXY/<module>/@v/list. This feature was introduced in GitLab 13.1.'
|
|
end
|
|
get 'list' do
|
|
mod = find_module
|
|
|
|
content_type 'text/plain'
|
|
mod.versions.map { |t| t.name }.join("\n")
|
|
end
|
|
|
|
desc 'Get information about the given module version' do
|
|
detail 'See `go help goproxy`, GET $GOPROXY/<module>/@v/<version>.info. This feature was introduced in GitLab 13.1.'
|
|
success ::API::Entities::GoModuleVersion
|
|
end
|
|
params do
|
|
requires :module_version, type: String, desc: 'Module version'
|
|
end
|
|
get ':module_version.info', requirements: MODULE_VERSION_REQUIREMENTS do
|
|
ver = find_version
|
|
|
|
present ::Packages::Go::ModuleVersionPresenter.new(ver), with: ::API::Entities::GoModuleVersion
|
|
end
|
|
|
|
desc 'Get the module file of the given module version' do
|
|
detail 'See `go help goproxy`, GET $GOPROXY/<module>/@v/<version>.mod. This feature was introduced in GitLab 13.1.'
|
|
end
|
|
params do
|
|
requires :module_version, type: String, desc: 'Module version'
|
|
end
|
|
get ':module_version.mod', requirements: MODULE_VERSION_REQUIREMENTS do
|
|
ver = find_version
|
|
|
|
content_type 'text/plain'
|
|
ver.gomod
|
|
end
|
|
|
|
desc 'Get a zip of the source of the given module version' do
|
|
detail 'See `go help goproxy`, GET $GOPROXY/<module>/@v/<version>.zip. This feature was introduced in GitLab 13.1.'
|
|
end
|
|
params do
|
|
requires :module_version, type: String, desc: 'Module version'
|
|
end
|
|
get ':module_version.zip', requirements: MODULE_VERSION_REQUIREMENTS do
|
|
ver = find_version
|
|
|
|
content_type 'application/zip'
|
|
env['api.format'] = :binary
|
|
header['Content-Disposition'] = ActionDispatch::Http::ContentDisposition.format(disposition: 'attachment', filename: ver.name + '.zip')
|
|
header['Content-Transfer-Encoding'] = 'binary'
|
|
status :ok
|
|
body ver.archive.string
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|