2020-06-10 05:08:35 -04:00
# frozen_string_literal: true
module Gitlab
module Golang
2021-03-24 08:09:32 -04:00
PseudoVersion = Struct . new ( :semver , :timestamp , :commit_id )
2020-06-10 05:08:35 -04:00
extend self
def local_module_prefix
2021-03-25 23:09:21 -04:00
@gitlab_prefix || = " #{ Settings . build_gitlab_go_url } / "
2020-06-10 05:08:35 -04:00
end
def semver_tag? ( tag )
return false if tag . dereferenced_target . nil?
Packages :: SemVer . match? ( tag . name , prefixed : true )
end
def pseudo_version? ( version )
return false unless version
if version . is_a? String
version = parse_semver version
return false unless version
end
pre = version . prerelease
# Valid pseudo-versions are:
# vX.0.0-yyyymmddhhmmss-sha1337beef0, when no earlier tagged commit exists for X
# vX.Y.Z-pre.0.yyyymmddhhmmss-sha1337beef0, when most recent prior tag is vX.Y.Z-pre
# vX.Y.(Z+1)-0.yyyymmddhhmmss-sha1337beef0, when most recent prior tag is vX.Y.Z
if version . minor != 0 || version . patch != 0
m = / \ A(.* \ .)?0 \ . / . freeze . match pre
return false unless m
pre = pre [ m [ 0 ] . length .. ]
end
# This pattern is intentionally more forgiving than the patterns
2021-03-24 08:09:32 -04:00
# above. Correctness is verified by #validate_pseudo_version.
2020-06-10 05:08:35 -04:00
/ \ A \ d{14}- \ h+ \ z / . freeze . match? pre
end
2021-03-24 08:09:32 -04:00
def parse_pseudo_version ( semver )
2020-06-10 05:08:35 -04:00
# Per Go's implementation of pseudo-versions, a tag should be
# considered a pseudo-version if it matches one of the patterns
# listed in #pseudo_version?, regardless of the content of the
# timestamp or the length of the SHA fragment. However, an error
# should be returned if the timestamp is not correct or if the SHA
# fragment is not exactly 12 characters long. See also Go's
# implementation of:
#
# - [*codeRepo.validatePseudoVersion](https://github.com/golang/go/blob/daf70d6c1688a1ba1699c933b3c3f04d6f2f73d9/src/cmd/go/internal/modfetch/coderepo.go#L530)
# - [Pseudo-version parsing](https://github.com/golang/go/blob/master/src/cmd/go/internal/modfetch/pseudo.go)
# - [Pseudo-version request processing](https://github.com/golang/go/blob/master/src/cmd/go/internal/modfetch/coderepo.go)
# Go ignores anything before '.' or after the second '-', so we will do the same
2021-03-24 08:09:32 -04:00
timestamp , commit_id = semver . prerelease . split ( '-' ) . last 2
2020-06-10 05:08:35 -04:00
timestamp = timestamp . split ( '.' ) . last
2021-03-24 08:09:32 -04:00
PseudoVersion . new ( semver , timestamp , commit_id )
end
def validate_pseudo_version ( project , version , commit = nil )
commit || = project . repository . commit_by ( oid : version . commit_id )
2020-06-10 05:08:35 -04:00
# Error messages are based on the responses of proxy.golang.org
# Verify that the SHA fragment references a commit
2021-05-04 11:10:36 -04:00
raise ArgumentError , 'invalid pseudo-version: unknown commit' unless commit
2020-06-10 05:08:35 -04:00
# Require the SHA fragment to be 12 characters long
2021-05-04 11:10:36 -04:00
raise ArgumentError , 'invalid pseudo-version: revision is shorter than canonical' unless version . commit_id . length == 12
2020-06-10 05:08:35 -04:00
# Require the timestamp to match that of the commit
2021-05-04 11:10:36 -04:00
raise ArgumentError , 'invalid pseudo-version: does not match version-control timestamp' unless commit . committed_date . strftime ( '%Y%m%d%H%M%S' ) == version . timestamp
2020-06-10 05:08:35 -04:00
commit
end
def parse_semver ( str )
Packages :: SemVer . parse ( str , prefixed : true )
end
2021-03-24 08:09:32 -04:00
def go_path ( project , path = nil )
if path . blank?
" #{ local_module_prefix } / #{ project . full_path } "
else
" #{ local_module_prefix } / #{ project . full_path } / #{ path } "
end
end
2020-06-10 05:08:35 -04:00
def pkg_go_dev_url ( name , version = nil )
if version
" https://pkg.go.dev/ #{ name } @ #{ version } "
else
" https://pkg.go.dev/ #{ name } "
end
end
def package_url ( name , version = nil )
return unless UrlSanitizer . valid? ( " https:// #{ name } " )
return pkg_go_dev_url ( name , version ) unless name . starts_with? ( local_module_prefix )
# This will not work if `name` refers to a subdirectory of a project. This
# could be expanded with logic similar to Gitlab::Middleware::Go to locate
# the project, check for permissions, and return a smarter result.
" #{ Gitlab . config . gitlab . protocol } :// #{ name } / "
end
end
end