Commit 575b8495 authored by Ethan Reesor's avatar Ethan Reesor

Fix Go module proxy issues

- Fix zip entry paths. The Go proxy spec requires zip entries to conform
  to `module@version/file`, where `file` is the path within the module.
- Fix /v2+ handling. For major versions 2+, the module name must include
  the major version as a suffix, e.g. /v2.
- Handle case encoding. Requests to the Go proxy encode uppercase
  characters in URLs as '!' followed by the character in lowercase.
- Per Zoom discussion with @trizzi, @sabrams, and team, modules with an
  invalid module name in go.mod will be ignored, initially.
parent 2a9f40a6
# frozen_string_literal: true
class Packages::GoModule
SEMVER_TAG_REGEX = Regexp.new("^#{::Packages::GoModuleVersion::SEMVER_REGEX.source}$").freeze
SEMVER_TAG_REGEX = /^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([-.a-z0-9]+))?(?:\+([-.a-z0-9]+))?$/i.freeze
# belongs_to :project
......@@ -22,7 +22,7 @@ class Packages::GoModule
end
def versions
@versions ||= project.repository.tags
@versions ||= @project.repository.tags
.filter { |tag| SEMVER_TAG_REGEX.match?(tag.name) && !tag.dereferenced_target.nil? }
.map { |tag| ::Packages::GoModuleVersion.new self, tag }
.filter { |ver| ver.valid? }
......
# frozen_string_literal: true
class Packages::GoModuleVersion
SEMVER_REGEX = /v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([-.A-Z0-9]+))?(?:\+([-.A-Z0-9]+))?/i.freeze
SEMVER_REGEX = /v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([-.a-z0-9]+))?(?:\+([-.a-z0-9]+))?/i.freeze
VERSION_SUFFIX_REGEX = /\/v([1-9]\d*)$/i.freeze
# belongs_to :mod
......@@ -16,22 +16,30 @@ class Packages::GoModuleVersion
end
def gomod
return @gomod unless @gomod.nil?
blob = @mod.project.repository.blob_at(@tag.dereferenced_target.sha, @mod.path + '/go.mod')
@gomod = blob ? blob.data : ''
@gomod ||= @mod.project.repository.blob_at(@tag.dereferenced_target.sha, @mod.path + '/go.mod')&.data
end
def valid?
m = gomod.split("\n", 2).first
valid_path? && valid_module?
end
def valid_path?
m = VERSION_SUFFIX_REGEX.match(@mod.name)
case major
when 0, 1
m == "module #{@mod.name}"
m.nil?
else
m == "module #{@mod.name}/v#{major}"
!m.nil? && m[1].to_i == major
end
end
def valid_module?
return false unless gomod
gomod.split("\n", 2).first == "module #{@mod.name}"
end
def major
SEMVER_REGEX.match(@tag.name)[1].to_i
end
......@@ -56,8 +64,8 @@ class Packages::GoModuleVersion
return @files unless @files.nil?
sha = @tag.dereferenced_target.sha
tree = @mod.project.repository.tree(sha, mod.path, recursive: true).entries.filter { |e| e.file? }
nested = tree.filter { |e| e.name == 'go.mod' && !(mod.path == '' && e.path == 'go.mod' || e.path == mod.path + '/go.mod') }.map { |e| e.path[0..-7] }
tree = @mod.project.repository.tree(sha, @mod.path, recursive: true).entries.filter { |e| e.file? }
nested = tree.filter { |e| e.name == 'go.mod' && !(@mod.path == '' && e.path == 'go.mod' || e.path == @mod.path + '/go.mod') }.map { |e| e.path[0..-7] }
@files = tree.filter { |e| !nested.any? { |n| e.path.start_with? n } }
end
......
......@@ -3,13 +3,18 @@ module API
class GoProxy < Grape::API
helpers ::API::Helpers::PackagesHelpers
MODULE_VERSION_REQUIREMENTS = { module_version: ::Packages::GoModuleVersion::SEMVER_REGEX }.freeze
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
before { require_packages_enabled! }
helpers do
def case_decode(str)
str.gsub(/![[:alpha:]]/) { |s| s[1..].upcase }
end
def find_module
module_name = params[:module_name].gsub(/![[:alpha:]]/) { |s| s[1..].upcase }
module_name = case_decode params[:module_name]
bad_request!('Module Name') if module_name.blank?
......@@ -18,6 +23,15 @@ module API
mod
end
def find_version
mod = find_module
ver = mod.find_version case_decode params[:module_version]
not_found! unless ver
ver
end
end
params do
......@@ -49,10 +63,7 @@ module API
requires :module_version, type: String, desc: 'Module version'
end
get ':module_version.info', requirements: MODULE_VERSION_REQUIREMENTS do
mod = find_module
ver = mod.find_version params[:module_version]
not_found! unless ver
ver = find_version
present ::Packages::Go::ModuleVersionPresenter.new(ver), with: EE::API::Entities::GoModuleVersion
end
......@@ -64,10 +75,7 @@ module API
requires :module_version, type: String, desc: 'Module version'
end
get ':module_version.mod', requirements: MODULE_VERSION_REQUIREMENTS do
mod = find_module
ver = mod.find_version params[:module_version]
not_found! unless ver
ver = find_version
content_type 'text/plain'
ver.gomod
......@@ -80,14 +88,13 @@ module API
requires :module_version, type: String, desc: 'Module version'
end
get ':module_version.zip', requirements: MODULE_VERSION_REQUIREMENTS do
mod = find_module
ver = find_version
ver = mod.find_version params[:module_version]
not_found! unless ver
suffix_len = ver.mod.path == '' ? 0 : ver.mod.path.length + 1
s = Zip::OutputStream.write_buffer do |zip|
ver.files.each do |file|
zip.put_next_entry file.path
zip.put_next_entry "#{ver.mod.name}@#{ver.name}/#{file.path[suffix_len...]}"
zip.write ver.blob_at(file.path)
end
end
......
This diff is collapsed.
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