Support full-tag prepared CI delivery
This commit is contained in:
@@ -14,9 +14,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/process"
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/versioncontract"
|
||||
)
|
||||
|
||||
const Usage = "usage: releasekit version\n releasekit check [--repo DIR] [--config FILE] VERSION\n releasekit publish [--repo DIR] [--config FILE] [--expected-module DIR] VERSION\n releasekit build [--repo DIR] [--config FILE] [--output DIR] VERSION\n releasekit deliver [--repo DIR] [--config FILE] [--assets DIR] VERSION\n releasekit ci-deliver [--repo DIR] [--config FILE] [--remote NAME] --commit OID VERSION"
|
||||
const Usage = "usage: releasekit version\n releasekit check [--repo DIR] [--config FILE] VERSION\n releasekit publish [--repo DIR] [--config FILE] [--expected-module DIR] VERSION\n releasekit build [--repo DIR] [--config FILE] [--output DIR] VERSION\n releasekit deliver [--repo DIR] [--config FILE] [--assets DIR] VERSION\n releasekit ci-deliver [--repo DIR] [--config FILE] [--remote NAME] [--prepared] [--expected-module DIR] --commit OID TAG"
|
||||
|
||||
type Command string
|
||||
|
||||
@@ -40,6 +41,8 @@ type Invocation struct {
|
||||
Commit string
|
||||
Version string
|
||||
ExpectedModule string
|
||||
ExpectedTag string
|
||||
Prepared bool
|
||||
}
|
||||
|
||||
// Parse accepts only commands that have been implemented by the executable.
|
||||
@@ -69,13 +72,15 @@ func Parse(args []string) (Invocation, error) {
|
||||
}
|
||||
var remote *string
|
||||
expectedModule := ""
|
||||
if action == PublishAction {
|
||||
if action == PublishAction || action == CIDeliveryAction {
|
||||
flags.StringVar(&expectedModule, "expected-module", "", "expected module directory from a launcher mapping")
|
||||
}
|
||||
expectedCommit := ""
|
||||
prepared := false
|
||||
if action == CIDeliveryAction {
|
||||
remote = flags.String("remote", "origin", "Git remote")
|
||||
flags.StringVar(&expectedCommit, "commit", "", "expected tagged commit")
|
||||
flags.BoolVar(&prepared, "prepared", false, "verify an already prepared tagged checkout without fetching or checkout")
|
||||
}
|
||||
if err := flags.Parse(args[1:]); err != nil {
|
||||
return Invocation{}, err
|
||||
@@ -89,11 +94,22 @@ func Parse(args []string) (Invocation, error) {
|
||||
if action == CIDeliveryAction && !validGitObjectID(expectedCommit) {
|
||||
return Invocation{}, errors.New("ci-deliver --commit must be a full Git object ID")
|
||||
}
|
||||
version := flags.Arg(0)
|
||||
expectedTag := ""
|
||||
if action == CIDeliveryAction {
|
||||
expectedTag = version
|
||||
if slash := strings.LastIndex(version, "/"); slash >= 0 {
|
||||
version = version[slash+1:]
|
||||
}
|
||||
if !versioncontract.ValidStableVersion(version) {
|
||||
return Invocation{}, errors.New("ci-deliver TAG must end in a stable vMAJOR.MINOR.PATCH version")
|
||||
}
|
||||
}
|
||||
parsedRemote := ""
|
||||
if remote != nil {
|
||||
parsedRemote = *remote
|
||||
}
|
||||
return Invocation{Command: action, Repository: *repository, ConfigPath: *configPath, OutputDir: outputDir, AssetsDir: assetsDir, Remote: parsedRemote, Commit: expectedCommit, Version: flags.Arg(0), ExpectedModule: expectedModule}, nil
|
||||
return Invocation{Command: action, Repository: *repository, ConfigPath: *configPath, OutputDir: outputDir, AssetsDir: assetsDir, Remote: parsedRemote, Commit: expectedCommit, Version: version, ExpectedModule: expectedModule, ExpectedTag: expectedTag, Prepared: prepared}, nil
|
||||
default:
|
||||
return Invocation{}, fmt.Errorf("unknown command %q", args[0])
|
||||
}
|
||||
|
||||
@@ -51,11 +51,11 @@ func TestParse(t *testing.T) {
|
||||
if deliver.Command != DeliveryAction || deliver.Repository != "candidate" || deliver.ConfigPath != "releasekit.json" || deliver.AssetsDir != "assets with spaces" || deliver.Version != "v1.2.6" {
|
||||
t.Fatalf("Parse(deliver) = %#v", deliver)
|
||||
}
|
||||
ciDelivery, err := Parse([]string{"ci-deliver", "--repo", "candidate", "--config", "profiles/release config.json", "--remote", "upstream", "--commit", "0123456789abcdef0123456789abcdef01234567", "v1.2.7"})
|
||||
ciDelivery, err := Parse([]string{"ci-deliver", "--repo", "candidate", "--config", "profiles/release config.json", "--remote", "upstream", "--prepared", "--expected-module", "integrations/ckconfig", "--commit", "0123456789abcdef0123456789abcdef01234567", "integrations/ckconfig/v1.2.7"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ciDelivery.Command != CIDeliveryAction || ciDelivery.Repository != "candidate" || ciDelivery.ConfigPath != "profiles/release config.json" || ciDelivery.Remote != "upstream" || ciDelivery.Commit != "0123456789abcdef0123456789abcdef01234567" || ciDelivery.Version != "v1.2.7" {
|
||||
if ciDelivery.Command != CIDeliveryAction || ciDelivery.Repository != "candidate" || ciDelivery.ConfigPath != "profiles/release config.json" || ciDelivery.Remote != "upstream" || ciDelivery.Commit != "0123456789abcdef0123456789abcdef01234567" || ciDelivery.Version != "v1.2.7" || ciDelivery.ExpectedTag != "integrations/ckconfig/v1.2.7" || ciDelivery.ExpectedModule != "integrations/ckconfig" || !ciDelivery.Prepared {
|
||||
t.Fatalf("Parse(ci-deliver) = %#v", ciDelivery)
|
||||
}
|
||||
ciDefault, err := Parse([]string{"ci-deliver", "--commit", "0123456789abcdef0123456789abcdef01234567", "v1.2.8"})
|
||||
@@ -68,6 +68,9 @@ func TestParse(t *testing.T) {
|
||||
if _, err := Parse([]string{"ci-deliver", "--commit", "abc123", "v1.2.3"}); err == nil || !strings.Contains(err.Error(), "full Git object ID") {
|
||||
t.Fatalf("Parse(ci-deliver with abbreviated commit) error = %v, want full object ID error", err)
|
||||
}
|
||||
if _, err := Parse([]string{"ci-deliver", "--commit", "0123456789abcdef0123456789abcdef01234567", "integrations/ckconfig/latest"}); err == nil || !strings.Contains(err.Error(), "stable vMAJOR.MINOR.PATCH") {
|
||||
t.Fatalf("Parse(ci-deliver unstable terminal tag) error = %v", err)
|
||||
}
|
||||
if _, err := Parse([]string{"check", "v1.2.3", "--repo", "repository"}); err == nil {
|
||||
t.Fatal("Parse accepted a flag after the version")
|
||||
}
|
||||
|
||||
@@ -255,9 +255,10 @@ func runDelivery(ctx context.Context, invocation cli.Invocation, stdout, stderr
|
||||
func runCIDelivery(ctx context.Context, invocation cli.Invocation, stdout, stderr io.Writer) int {
|
||||
err := releasekit.PrepareTaggedCheckout(ctx, releasekit.TaggedCheckoutRequest{
|
||||
RepositoryDir: invocation.Repository,
|
||||
Version: invocation.Version,
|
||||
Tag: invocation.ExpectedTag,
|
||||
Remote: invocation.Remote,
|
||||
ExpectedCommit: invocation.Commit,
|
||||
VerifyOnly: invocation.Prepared,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "prepare tagged checkout: %v\n", err)
|
||||
@@ -272,6 +273,10 @@ func runCIDelivery(ctx context.Context, invocation cli.Invocation, stdout, stder
|
||||
fmt.Fprintf(stderr, "load profile: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
if err := checkExpectedModule(invocation.Repository, invocation.ExpectedModule, settings.ModuleDir); err != nil {
|
||||
fmt.Fprintf(stderr, "profile module selection: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
destination, status := deliveryDestination(settings, stderr)
|
||||
if status != 0 {
|
||||
return status
|
||||
@@ -351,6 +356,7 @@ func deliverWithDestination(ctx context.Context, invocation cli.Invocation, sett
|
||||
ModuleDir: settings.ModuleDir,
|
||||
Product: settings.Product,
|
||||
Version: invocation.Version,
|
||||
ExpectedTag: invocation.ExpectedTag,
|
||||
Destination: destination,
|
||||
Build: buildSpec,
|
||||
Prebuilt: prebuilt,
|
||||
|
||||
@@ -719,12 +719,14 @@ func TestCIDeliverLoadsTaggedProfileAndSkipsUnavailablePageOnlyDependencies(t *t
|
||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||
state.commit = expectedCommit
|
||||
writeDeliveryProfileAt(t, repository, "profiles/ci-delivery.json", cli.Settings{Product: "Later Branch Profile"})
|
||||
cliGitRun(t, repository, "checkout", "--detach", expectedCommit)
|
||||
cliGitRun(t, repository, "remote", "remove", "origin")
|
||||
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
t.Setenv("GOPROXY", "off")
|
||||
t.Setenv("GOSUMDB", "off")
|
||||
var stdout, stderr bytes.Buffer
|
||||
args := []string{"ci-deliver", "--repo", repository, "--config", "profiles/ci-delivery.json", "--commit", expectedCommit, "v1.2.3"}
|
||||
args := []string{"ci-deliver", "--repo", repository, "--config", "profiles/ci-delivery.json", "--prepared", "--commit", expectedCommit, "v1.2.3"}
|
||||
if status := run(context.Background(), args, &stdout, &stderr); status != 0 {
|
||||
t.Fatalf("run() status=%d stdout=%q stderr=%q", status, stdout.String(), stderr.String())
|
||||
}
|
||||
@@ -778,6 +780,55 @@ func TestCIDeliverRejectsWrongCommitBeforeGoOrHostedRequests(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDeliverRejectsExpectedModuleMismatchBeforeDependencyPreparation(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
if err := os.MkdirAll(filepath.Join(repository, "integrations", "other"), 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(repository, "integrations", "expected"), 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeRepositoryFile(t, repository, "integrations/other/go.mod", "module example.com/other\n\ngo 1.25.0\n")
|
||||
writeRepositoryFile(t, repository, "integrations/expected/go.mod", "module example.com/expected\n\ngo 1.25.0\n")
|
||||
cliGitRun(t, repository, "add", "integrations")
|
||||
cliGitRun(t, repository, "commit", "-m", "add module profiles")
|
||||
|
||||
state := &deliveryServerState{}
|
||||
server := newDeliveryServer(t, state)
|
||||
defer server.Close()
|
||||
writeDeliveryProfile(t, repository, cli.Settings{
|
||||
Product: "Example Product",
|
||||
ModuleDir: "integrations/other",
|
||||
Build: &cli.BuildSettings{MainPackage: ".", BinaryName: "helper", Targets: []string{"linux/amd64"}},
|
||||
Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"},
|
||||
})
|
||||
expectedCommit := pushTaggedCLIRepository(t, repository)
|
||||
state.commit = expectedCommit
|
||||
shimDir := t.TempDir()
|
||||
markerPath := filepath.Join(shimDir, "go-invoked")
|
||||
goShim := "#!/bin/sh\n: > \"$RELEASEKIT_GO_MARKER\"\nexit 99\n"
|
||||
if err := os.WriteFile(filepath.Join(shimDir, "go"), []byte(goShim), 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("RELEASEKIT_GO_MARKER", markerPath)
|
||||
t.Setenv("PATH", shimDir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
||||
t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token")
|
||||
var stdout, stderr bytes.Buffer
|
||||
args := []string{"ci-deliver", "--repo", repository, "--expected-module", "integrations/expected", "--commit", expectedCommit, "v1.2.3"}
|
||||
if status := run(context.Background(), args, &stdout, &stderr); status != 2 {
|
||||
t.Fatalf("run() status=%d stdout=%q stderr=%q, want profile-selection error", status, stdout.String(), stderr.String())
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "profile module selection") {
|
||||
t.Fatalf("stderr=%q, want profile-selection diagnostic", stderr.String())
|
||||
}
|
||||
if _, err := os.Stat(markerPath); !os.IsNotExist(err) {
|
||||
t.Fatalf("Go was invoked before profile agreement: %v", err)
|
||||
}
|
||||
if state.apiRequests != 0 {
|
||||
t.Fatalf("hosted API requests = %d, want 0", state.apiRequests)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDeliverPreparesDependenciesAndBuildsConfiguredTargets(t *testing.T) {
|
||||
repository := newDeliveryBuildRepository(t)
|
||||
state := &deliveryServerState{}
|
||||
|
||||
Reference in New Issue
Block a user