1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
# frozen_string_literal: true
module API
class GoProxy < ::API::Base
helpers Gitlab::Golang
helpers ::API::Helpers::PackagesHelpers
GO_PROXY_TAGS = %w[go_proxy].freeze
feature_category :package_registry
urgency :low
# 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]+))?/
MODULE_VERSION_REQUIREMENTS = { module_version: MODULE_VERSION_REGEX }.freeze
content_type :txt, 'text/plain'
before { require_packages_enabled! }
helpers do
def project
user_project(action: :read_package)
end
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_module
not_found! unless Feature.enabled?(:go_proxy, project)
module_name = case_decode params[:module_name]
bad_request_missing_attribute!('Module Name') if module_name.blank?
mod = ::Packages::Go::ModuleFinder.new(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, types: [String, Integer], desc: 'The project ID or full path of a project'
requires :module_name, type: String, desc: 'The name of the Go module', coerce_with: ->(val) { CGI.unescape(val) }
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true,
authenticate_non_public: true
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before do
authorize_read_package!(project)
end
namespace ':id/packages/go/*module_name/@v' do
desc 'List' do
detail 'Get all tagged versions for a given Go module.'\
'See `go help goproxy`, GET $GOPROXY/<module>/@v/list. This feature was introduced in GitLab 13.1.'
tags GO_PROXY_TAGS
end
get 'list' do
mod = find_module
content_type 'text/plain'
mod.versions.map { |t| t.name }.join("\n")
end
desc 'Version metadata' do
detail 'Get all tagged versions for a given Go module.'\
'See `go help goproxy`, GET $GOPROXY/<module>/@v/<version>.info. This feature was introduced in GitLab 13.1'
success ::API::Entities::GoModuleVersion
tags GO_PROXY_TAGS
end
params do
requires :module_version, type: String, desc: 'The version of the Go module'
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 'Download module file' do
detail 'Get the module file of a given module version.'\
'See `go help goproxy`, GET $GOPROXY/<module>/@v/<version>.mod. This feature was introduced in GitLab 13.1.'
tags GO_PROXY_TAGS
end
params do
requires :module_version, type: String, desc: 'The version of the Go module'
end
get ':module_version.mod', requirements: MODULE_VERSION_REQUIREMENTS do
ver = find_version
content_type 'text/plain'
ver.gomod
end
desc 'Download module source' do
detail 'Get a zip of the source of the given module version.'\
'See `go help goproxy`, GET $GOPROXY/<module>/@v/<version>.zip. This feature was introduced in GitLab 13.1.'
tags GO_PROXY_TAGS
end
params do
requires :module_version, type: String, desc: 'The version of the Go module'
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
|