feat: support packing multiple packages #13
+17
-4
@@ -36,6 +36,18 @@ func shortCommitHash(hash string) string {
|
||||
return hash
|
||||
}
|
||||
|
||||
func preferredRevisionCandidates(preferredRev string) []string {
|
||||
preferredRev = strings.TrimSpace(preferredRev)
|
||||
if preferredRev == "" {
|
||||
return nil
|
||||
}
|
||||
candidates := []string{preferredRev}
|
||||
if !strings.HasPrefix(preferredRev, "v") && semverRegexp.MatchString("v"+preferredRev) {
|
||||
candidates = append(candidates, "v"+preferredRev)
|
||||
}
|
||||
return candidates
|
||||
}
|
||||
|
||||
// pkgVersionFromGit determines the actual version to be packaged
|
||||
// from the git repository status and user preference.
|
||||
// Besides returning the upstream version, the "upstream" struct
|
||||
@@ -50,11 +62,12 @@ func pkgVersionFromGit(gitdir string, u *upstream, preferredRev string, forcePre
|
||||
var cmd *exec.Cmd // the temporary shell commands we execute
|
||||
|
||||
// If the user specifies a valid tag as the preferred revision, that tag should be used without additional heuristics.
|
||||
if preferredRev != "" {
|
||||
cmd = exec.Command("git", "tag", "--list", preferredRev)
|
||||
for _, candidate := range preferredRevisionCandidates(preferredRev) {
|
||||
cmd = exec.Command("git", "tag", "--list", candidate)
|
||||
cmd.Dir = gitdir
|
||||
if out, err := cmd.Output(); err == nil && slices.Contains(strings.Fields(string(out)), preferredRev) {
|
||||
latestTag = preferredRev
|
||||
if out, err := cmd.Output(); err == nil && slices.Contains(strings.Fields(string(out)), candidate) {
|
||||
latestTag = candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -959,10 +959,10 @@ func mainPack(args []string, usage func()) {
|
||||
flagSet.Usage = usage
|
||||
} else {
|
||||
flagSet.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [pack] [FLAG]... <go-package-importpath>\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "Example: %s pack golang.org/x/oauth2\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [pack] [FLAG]... <go-package-importpath>[@revision]...\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "Example: %s pack golang.org/x/oauth2@v0.30.0 github.com/pquerna/cachecontrol@v0.2.0\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
fmt.Fprintf(os.Stderr, "\"%s pack\" downloads the specified Go package from the Internet,\nand creates new files and directories in the current working directory.\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "\"%s pack\" downloads the specified Go packages from the Internet,\nand creates new files and directories in the current working directory.\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
fmt.Fprintf(os.Stderr, "Flags:\n")
|
||||
flagSet.PrintDefaults()
|
||||
@@ -973,7 +973,7 @@ func mainPack(args []string, usage func()) {
|
||||
flagSet.StringVar(&gitRevision,
|
||||
"git_revision",
|
||||
"",
|
||||
"git revision (see gitrevisions(7)) of the specified Go package\n"+
|
||||
"legacy default git revision (see gitrevisions(7)) for package arguments without @revision\n"+
|
||||
"to check out, defaulting to the default behavior of git clone.\n"+
|
||||
"Useful in case you do not want to package e.g. current HEAD.")
|
||||
|
||||
@@ -1026,28 +1026,6 @@ func mainPack(args []string, usage func()) {
|
||||
}
|
||||
|
||||
gitRevision = strings.TrimSpace(gitRevision)
|
||||
gopkg := flagSet.Arg(0)
|
||||
|
||||
// Remove URL scheme if present (https://, http://, git://, etc.)
|
||||
gopkg = strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(gopkg, "https://"), "http://"), "git://")
|
||||
|
||||
// Verify the provided argument using the official pkg.go.dev API. Keep the
|
||||
// requested path for naming and go_import_path; use the module path only for
|
||||
// source checkout and tarball generation.
|
||||
info, err := getPkgsiteInfo(context.TODO(), gopkg)
|
||||
if err != nil {
|
||||
log.Fatalf("Verifying arguments: %v — did you specify a Go package import path?", err)
|
||||
}
|
||||
sourceGopkg := sourceImportPathForPackage(gopkg, info)
|
||||
if sourceGopkg != gopkg {
|
||||
log.Printf("Using module root %q as source checkout for specified import path %q", sourceGopkg, gopkg)
|
||||
}
|
||||
|
||||
// Set default source and binary package names.
|
||||
openRuyiSrc := nameFromGopkg(gopkg, typeLibrary, customProgPkgName, allowUnknownHoster)
|
||||
openRuyiLib := openRuyiSrc
|
||||
openRuyiProgram := nameFromGopkg(gopkg, typeProgram, customProgPkgName, allowUnknownHoster)
|
||||
|
||||
var pkgType packageType
|
||||
|
||||
switch strings.TrimSpace(pkgTypeString) {
|
||||
@@ -1068,6 +1046,56 @@ func mainPack(args []string, usage func()) {
|
||||
log.Fatalf("-type=%q not recognized, aborting\n", pkgTypeString)
|
||||
}
|
||||
|
||||
for _, arg := range flagSet.Args() {
|
||||
gopkg, revision, err := parsePackArg(arg, gitRevision)
|
||||
if err != nil {
|
||||
log.Fatalf("parse package argument %q: %v", arg, err)
|
||||
}
|
||||
packPackage(gopkg, revision, forcePrerelease, pkgType, customProgPkgName, allowUnknownHoster)
|
||||
}
|
||||
}
|
||||
|
||||
func parsePackArg(arg, defaultRevision string) (string, string, error) {
|
||||
arg = strings.TrimSpace(arg)
|
||||
defaultRevision = strings.TrimSpace(defaultRevision)
|
||||
if arg == "" {
|
||||
return "", "", fmt.Errorf("package import path is empty")
|
||||
}
|
||||
if at := strings.LastIndex(arg, "@"); at >= 0 {
|
||||
revision := strings.TrimSpace(arg[at+1:])
|
||||
if revision == "" {
|
||||
return "", "", fmt.Errorf("package revision is empty")
|
||||
}
|
||||
path := strings.TrimSpace(arg[:at])
|
||||
if path == "" {
|
||||
return "", "", fmt.Errorf("package import path is empty")
|
||||
}
|
||||
return path, revision, nil
|
||||
}
|
||||
return arg, defaultRevision, nil
|
||||
}
|
||||
|
||||
func packPackage(gopkg, gitRevision string, forcePrerelease bool, pkgType packageType, customProgPkgName string, allowUnknownHoster bool) {
|
||||
// Remove URL scheme if present (https://, http://, git://, etc.)
|
||||
gopkg = strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(gopkg, "https://"), "http://"), "git://")
|
||||
|
||||
// Verify the provided argument using the official pkg.go.dev API. Keep the
|
||||
// requested path for naming and go_import_path; use the module path only for
|
||||
// source checkout and tarball generation.
|
||||
info, err := getPkgsiteInfo(context.TODO(), gopkg)
|
||||
if err != nil {
|
||||
log.Fatalf("Verifying arguments: %v — did you specify a Go package import path?", err)
|
||||
}
|
||||
sourceGopkg := sourceImportPathForPackage(gopkg, info)
|
||||
if sourceGopkg != gopkg {
|
||||
log.Printf("Using module root %q as source checkout for specified import path %q", sourceGopkg, gopkg)
|
||||
}
|
||||
|
||||
// Set default source and binary package names.
|
||||
openRuyiSrc := nameFromGopkg(gopkg, typeLibrary, customProgPkgName, allowUnknownHoster)
|
||||
openRuyiLib := openRuyiSrc
|
||||
openRuyiProgram := nameFromGopkg(gopkg, typeProgram, customProgPkgName, allowUnknownHoster)
|
||||
|
||||
if pkgType != typeGuess {
|
||||
openRuyiSrc = nameFromGopkg(gopkg, pkgType, customProgPkgName, allowUnknownHoster)
|
||||
}
|
||||
|
||||
@@ -547,6 +547,104 @@ func TestPkgVersionFromGitUsesPackagingDateAndSevenCharHash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPkgVersionFromGitAcceptsUnprefixedSemverPreferredRevision(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
runGit(t, dir, nil, "init")
|
||||
if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module example.com/project\n"), 0644); err != nil {
|
||||
t.Fatalf("write go.mod: %v", err)
|
||||
}
|
||||
runGit(t, dir, nil, "add", "go.mod")
|
||||
runGit(t, dir, []string{
|
||||
"GIT_AUTHOR_NAME=Test",
|
||||
"GIT_AUTHOR_EMAIL=test@example.invalid",
|
||||
"GIT_COMMITTER_NAME=Test",
|
||||
"GIT_COMMITTER_EMAIL=test@example.invalid",
|
||||
}, "commit", "-m", "initial")
|
||||
runGit(t, dir, nil, "tag", "v1.2.0")
|
||||
|
||||
u := upstream{}
|
||||
got, err := pkgVersionFromGit(dir, &u, "1.2.0", false)
|
||||
if err != nil {
|
||||
t.Fatalf("pkgVersionFromGit() returned error: %v", err)
|
||||
}
|
||||
if got != "1.2.0" {
|
||||
t.Fatalf("pkgVersionFromGit() = %q, want %q", got, "1.2.0")
|
||||
}
|
||||
if u.tag != "v1.2.0" || !u.isRelease {
|
||||
t.Fatalf("tag = %q, isRelease = %v, want v1.2.0 release", u.tag, u.isRelease)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePackArg(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
arg string
|
||||
defaultRevision string
|
||||
wantPath string
|
||||
wantRevision string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "explicit revision",
|
||||
arg: "github.com/a/pkg@v1.2.3",
|
||||
wantPath: "github.com/a/pkg",
|
||||
wantRevision: "v1.2.3",
|
||||
},
|
||||
{
|
||||
name: "explicit unprefixed semver revision",
|
||||
arg: "github.com/a/pkg@1.2.3",
|
||||
wantPath: "github.com/a/pkg",
|
||||
wantRevision: "1.2.3",
|
||||
},
|
||||
{
|
||||
name: "uses default revision",
|
||||
arg: "github.com/a/pkg",
|
||||
defaultRevision: "v0.9.0",
|
||||
wantPath: "github.com/a/pkg",
|
||||
wantRevision: "v0.9.0",
|
||||
},
|
||||
{
|
||||
name: "keeps empty default revision",
|
||||
arg: "github.com/a/pkg",
|
||||
wantPath: "github.com/a/pkg",
|
||||
},
|
||||
{
|
||||
name: "semantic import version with explicit revision",
|
||||
arg: "github.com/a/pkg/v2@v2.1.0",
|
||||
wantPath: "github.com/a/pkg/v2",
|
||||
wantRevision: "v2.1.0",
|
||||
},
|
||||
{
|
||||
name: "rejects empty explicit revision",
|
||||
arg: "github.com/a/pkg@",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "rejects empty import path",
|
||||
arg: "@v1.2.3",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotPath, gotRevision, err := parsePackArg(tt.arg, tt.defaultRevision)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("parsePackArg() returned error: %v", err)
|
||||
}
|
||||
if gotPath != tt.wantPath || gotRevision != tt.wantRevision {
|
||||
t.Fatalf("parsePackArg() = (%q, %q), want (%q, %q)", gotPath, gotRevision, tt.wantPath, tt.wantRevision)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func runGit(t *testing.T, dir string, env []string, args ...string) string {
|
||||
t.Helper()
|
||||
cmd := exec.Command("git", args...)
|
||||
|
||||
Reference in New Issue
Block a user