Resolve annotated tag events in Woodpecker delivery workflows
This commit is contained in:
@@ -51,6 +51,7 @@ func TestMultiModuleWoodpeckerDelivery(t *testing.T) {
|
||||
commit := tagAndPushWorkflowRepository(t, repository, tag)
|
||||
delivery.update(func(delivery *mappedDeliveryData) { delivery.commit = commit })
|
||||
env := multiModuleWorkflowEnvironment(t, repository, commit, tag, toolBinary, proxy)
|
||||
env["CI_COMMIT_SHA"] = cliGitOutput(t, repository, "rev-parse", "refs/tags/"+tag)
|
||||
if output := runWoodpeckerBlock(t, command, repository, env); !strings.Contains(output, "state complete") || !strings.Contains(output, "tool-version=v0.2.197") {
|
||||
t.Fatalf("tagged page delivery output = %q", output)
|
||||
}
|
||||
@@ -382,7 +383,7 @@ func tagAndPushWorkflowRepository(t *testing.T, repository, tag string) string {
|
||||
cliGitRun(t, repository, "add", "--all")
|
||||
cliGitRun(t, repository, "commit", "-m", "prepare mapped delivery")
|
||||
commit := cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
cliGitRun(t, repository, "tag", tag)
|
||||
cliGitRun(t, repository, "tag", "-a", tag, "-m", "mapped release")
|
||||
remote := filepath.Join(filepath.Dir(repository), "origin.git")
|
||||
cliGitRun(t, repository, "init", "--bare", remote)
|
||||
cliGitRun(t, repository, "remote", "add", "origin", remote)
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout(t *testing.T) {
|
||||
}
|
||||
env := map[string]string{
|
||||
"CI_WORKSPACE": repository,
|
||||
"CI_COMMIT_SHA": state.snapshot().commit,
|
||||
"CI_COMMIT_SHA": cliGitOutput(t, repository, "rev-parse", "refs/tags/"+version),
|
||||
"CI_COMMIT_TAG": version,
|
||||
"RELEASEKIT_GITEA_TOKEN": "fixture-token",
|
||||
"GOTOOLCHAIN": "local",
|
||||
|
||||
@@ -104,10 +104,14 @@ func TestWoodpeckerTagDeliveryTemplate(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
selectReleaseKitModule(t, repository, root, false)
|
||||
commit := pushTaggedCLIRepository(t, repository)
|
||||
cliGitRun(t, repository, "commit", "--allow-empty", "-m", "unrelated event commit")
|
||||
unrelated := cliGitOutput(t, repository, "rev-parse", "HEAD")
|
||||
goDir, goLog := installControlledGoBootstrapShim(t)
|
||||
env := templateEnvironment(repository, strings.Repeat("f", len(commit)), goDir, goLog)
|
||||
env := templateEnvironment(repository, unrelated, goDir, goLog)
|
||||
if output, err := executeWoodpeckerBlock(command, repository, env); err == nil {
|
||||
t.Fatalf("wrong tag succeeded: %s", output)
|
||||
} else if !strings.Contains(output, commit) || !strings.Contains(output, unrelated) {
|
||||
t.Fatalf("tag mismatch omitted commit identities: %s", output)
|
||||
}
|
||||
assertNoCommands(t, goLog)
|
||||
})
|
||||
@@ -126,26 +130,49 @@ func TestWoodpeckerTagDeliveryTemplate(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("selected executable receives event arguments and its status", func(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
selectReleaseKitModule(t, repository, root, false)
|
||||
commit := pushTaggedCLIRepository(t, repository)
|
||||
goDir, goLog := installControlledGoBootstrapShim(t)
|
||||
toolArgs := filepath.Join(t.TempDir(), "tool-arguments")
|
||||
env := templateEnvironment(repository, strings.ToUpper(commit), goDir, goLog)
|
||||
env["RELEASEKIT_TOOL_ARGS_LOG"] = toolArgs
|
||||
env["RELEASEKIT_TOOL_EXIT"] = "37"
|
||||
output, err := executeWoodpeckerBlock(command, repository, env)
|
||||
var exitError *exec.ExitError
|
||||
if !errors.As(err, &exitError) || exitError.ExitCode() != 37 {
|
||||
t.Fatalf("template error = %v, want propagated exit 37; output=%q", err, output)
|
||||
for _, event := range []struct {
|
||||
name, tag string
|
||||
tagObject bool
|
||||
}{
|
||||
{"lightweight commit", "v1.2.3", false},
|
||||
{"annotated commit", "v1.2.4", false},
|
||||
{"annotated tag object", "v1.2.4", true},
|
||||
} {
|
||||
t.Run(event.name, func(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
selectReleaseKitModule(t, repository, root, false)
|
||||
commit := pushTaggedCLIRepository(t, repository)
|
||||
if event.tag == "v1.2.4" {
|
||||
cliGitRun(t, repository, "tag", "-a", event.tag, "-m", "annotated release")
|
||||
cliGitRun(t, repository, "push", "origin", "refs/tags/"+event.tag)
|
||||
}
|
||||
eventID := commit
|
||||
if event.tagObject {
|
||||
eventID = cliGitOutput(t, repository, "rev-parse", "refs/tags/"+event.tag)
|
||||
}
|
||||
goDir, goLog := installControlledGoBootstrapShim(t)
|
||||
toolArgs := filepath.Join(t.TempDir(), "tool-arguments")
|
||||
env := templateEnvironment(repository, strings.ToUpper(eventID), goDir, goLog)
|
||||
env["CI_COMMIT_TAG"] = event.tag
|
||||
env["RELEASEKIT_TOOL_ARGS_LOG"] = toolArgs
|
||||
env["RELEASEKIT_TOOL_EXIT"] = "37"
|
||||
output, err := executeWoodpeckerBlock(command, repository, env)
|
||||
var exitError *exec.ExitError
|
||||
if !errors.As(err, &exitError) || exitError.ExitCode() != 37 {
|
||||
t.Fatalf("template error = %v, want propagated exit 37; output=%q", err, output)
|
||||
}
|
||||
args := readWoodpeckerFixtureFile(t, toolArgs)
|
||||
for _, expected := range []string{"<ci-deliver>", "<--repo>", "<" + repository + ">", "<--prepared>", "<" + event.tag + ">"} {
|
||||
if !strings.Contains(args, expected) {
|
||||
t.Errorf("selected executable arguments %q omit %q", args, expected)
|
||||
}
|
||||
}
|
||||
if !strings.Contains(strings.ToLower(args), "<--commit>\n<"+commit+">") {
|
||||
t.Errorf("selected executable did not receive source commit %s: %q", commit, args)
|
||||
}
|
||||
assertGoSelection(t, repository, goLog)
|
||||
})
|
||||
}
|
||||
args := readWoodpeckerFixtureFile(t, toolArgs)
|
||||
for _, expected := range []string{"<ci-deliver>", "<--repo>", "<" + repository + ">", "<--prepared>", "<--commit>", "<" + strings.ToUpper(commit) + ">", "<v1.2.3>"} {
|
||||
if !strings.Contains(args, expected) {
|
||||
t.Errorf("selected executable arguments %q omit %q", args, expected)
|
||||
}
|
||||
}
|
||||
assertGoSelection(t, repository, goLog)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -241,8 +241,11 @@ changes the selected checkout; `--prepared` reuses a verified checkout.
|
||||
Ordinary `deliver` continues to operate on already prepared source without
|
||||
fetching, checking out, or downloading dependencies.
|
||||
|
||||
In Woodpecker, pass `CI_WORKSPACE`, `CI_COMMIT_SHA`, and `CI_COMMIT_TAG` as the
|
||||
repository, expected commit, and full tag arguments. The CLI does not read
|
||||
In Woodpecker, pass `CI_WORKSPACE`, the source commit resolved from
|
||||
`CI_COMMIT_SHA`, and `CI_COMMIT_TAG` as the repository, expected commit, and full
|
||||
tag arguments. The event ID may identify an annotated tag object; see the
|
||||
[delivery guide](consumers/delivery.md#woodpecker-tag-workflows) for resolution
|
||||
and workflow preparation. The CLI does not read
|
||||
provider-specific environment variable names. Checkout and dependency
|
||||
preparation failures return status 1; usage, profile, credential, and invalid
|
||||
version errors return status 2. Final delivery uses the same statuses as
|
||||
|
||||
@@ -196,14 +196,16 @@ a `delivery` profile. Page-only and binary behavior both use this template; the
|
||||
profile's build target list selects whether application binaries are built and
|
||||
delivered.
|
||||
|
||||
Before running Go, the template fetches the explicit event tag, compares its
|
||||
peeled commit with the provider's full commit ID without regard to hex case,
|
||||
checks out that commit detached, and confirms the source tree is clean. It then
|
||||
copies the tagged `go.mod` and any `go.sum` to a temporary directory outside
|
||||
the checkout. `go list -m` confirms that the module selects ReleaseKit; Go uses
|
||||
Before running Go, the template fetches the explicit event tag and independently
|
||||
resolves both the tag and `CI_COMMIT_SHA` to source commits. The event ID may
|
||||
identify an annotated tag object or a commit. The template requires the resolved
|
||||
commits to match, checks out that commit detached, and confirms the source tree
|
||||
is clean. It then copies the tagged `go.mod` and any `go.sum` to a temporary
|
||||
directory outside the checkout. `go list -m` confirms that the module selects
|
||||
ReleaseKit; Go uses
|
||||
an alternate `-modfile` to prepare dependencies and build the selected
|
||||
`cmd/releasekit` package without changing the tagged module files. The template
|
||||
invokes that executable with `CI_WORKSPACE`, `CI_COMMIT_SHA`, and
|
||||
invokes that executable with `CI_WORKSPACE`, the resolved event commit, and
|
||||
`CI_COMMIT_TAG`. A cleanup trap removes the temporary metadata and executable.
|
||||
|
||||
Tool bootstrap and application artifact preparation are separate. The bootstrap
|
||||
@@ -243,10 +245,12 @@ The normal Go module and build caches are sufficient. Cache mounts may be added
|
||||
as deployment configuration when they help the runner; they are not needed by
|
||||
the template. A separately installed executable can be installed with
|
||||
`go install gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@PIN`, then run
|
||||
with the same arguments:
|
||||
with the same arguments. With the event object available in the CI checkout,
|
||||
resolve it to its source commit before invocation:
|
||||
|
||||
```sh
|
||||
releasekit ci-deliver --repo "$CI_WORKSPACE" --commit "$CI_COMMIT_SHA" "$CI_COMMIT_TAG"
|
||||
expected_commit=$(git -C "$CI_WORKSPACE" rev-parse --verify "${CI_COMMIT_SHA}^{commit}")
|
||||
releasekit ci-deliver --repo "$CI_WORKSPACE" --commit "$expected_commit" "$CI_COMMIT_TAG"
|
||||
```
|
||||
|
||||
The module-based workflow follows the ReleaseKit version selected by the tagged
|
||||
@@ -255,6 +259,12 @@ consumer module configuration when adopting a newer ReleaseKit version and
|
||||
verify it with a tag fixture. The command and profile behavior are documented
|
||||
in the [CLI contract](../cli.md) and [configuration reference](../config.md).
|
||||
|
||||
Update copied workflow YAML when adopting corrected examples. A dependency
|
||||
upgrade alone leaves that copy unchanged. An older tagged workflow that passes
|
||||
a raw annotated tag object ID still fails on retry. Recover delivery from the
|
||||
original tagged source with the resolved event commit through the command above,
|
||||
preserving the published tag and existing content.
|
||||
|
||||
Woodpecker runs delivery after the tag event, independently from the source
|
||||
publication result. Read its workflow status as delivery status: a delivery
|
||||
failure does not undo source publication. A failed, partial, or uncertain run
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Audita Bug Report: Annotated Tag IDs in Woodpecker Delivery Examples
|
||||
|
||||
**Status:** Reported; upstream correction requested.
|
||||
**Status:** Remediated in current source; offline race-enabled workflow
|
||||
verification passed.
|
||||
**Reviewed release:** ReleaseKit v0.4.1, commit
|
||||
`2d5a8110c61e31daf8698766114ae305d0e0e469`.
|
||||
**Affected consumer:** Audita v3.1.0.
|
||||
@@ -107,3 +108,16 @@ receive corrected examples; retrying an existing tag's unchanged faulty workflow
|
||||
will still fail. Existing artifacts can be delivered from the original tagged
|
||||
source using the resolved commit ID through the documented delivery path,
|
||||
without moving the source tag or replacing published contents.
|
||||
|
||||
## ReleaseKit Resolution
|
||||
|
||||
All three maintained workflows independently resolve the event ID to its source
|
||||
commit, compare it with the selected tag's commit, and pass that resolved commit
|
||||
to delivery. Mismatch diagnostics show both identities. The CLI and delivery
|
||||
guides describe event resolution and adoption of corrected workflow copies.
|
||||
|
||||
Regression coverage reproduced the annotated-tag-object failure before the fix.
|
||||
The affected workflow tests passed afterward with offline module settings and
|
||||
the race detector. Coverage includes annotated tag-object events, annotated and
|
||||
lightweight commit events, case-insensitive object IDs, resolved delivery
|
||||
arguments, unrelated-commit rejection, tagged retries, and source bootstrap.
|
||||
|
||||
@@ -20,8 +20,12 @@ steps:
|
||||
git check-ref-format "refs/tags/$${CI_COMMIT_TAG}"
|
||||
git fetch origin "refs/tags/$${CI_COMMIT_TAG}:refs/tags/$${CI_COMMIT_TAG}"
|
||||
tag_commit=$(git rev-parse --verify "refs/tags/$${CI_COMMIT_TAG}^{commit}")
|
||||
expected_commit=$(printf '%s' "$${CI_COMMIT_SHA}" | tr '[:upper:]' '[:lower:]')
|
||||
test "$(printf '%s' "$${tag_commit}" | tr '[:upper:]' '[:lower:]')" = "$${expected_commit}"
|
||||
expected_commit=$(git rev-parse --verify "$${CI_COMMIT_SHA}^{commit}")
|
||||
if [ "$${tag_commit}" != "$${expected_commit}" ]; then
|
||||
printf 'Tag %s resolves to commit %s; event %s resolves to commit %s\n' \
|
||||
"$${CI_COMMIT_TAG}" "$${tag_commit}" "$${CI_COMMIT_SHA}" "$${expected_commit}" >&2
|
||||
exit 1
|
||||
fi
|
||||
git checkout --detach "$${tag_commit}"
|
||||
test "$(git rev-parse HEAD | tr '[:upper:]' '[:lower:]')" = "$${expected_commit}"
|
||||
test -z "$(git status --porcelain --untracked-files=all)"
|
||||
@@ -59,5 +63,5 @@ steps:
|
||||
--config "$${profile_path}" \
|
||||
--expected-module "$${module_dir}" \
|
||||
--prepared \
|
||||
--commit "$${CI_COMMIT_SHA}" \
|
||||
--commit "$${expected_commit}" \
|
||||
"$${CI_COMMIT_TAG}"
|
||||
|
||||
@@ -15,9 +15,13 @@ steps:
|
||||
test -n "$${CI_COMMIT_SHA}"
|
||||
git check-ref-format "refs/tags/$${CI_COMMIT_TAG}"
|
||||
git fetch origin "refs/tags/$${CI_COMMIT_TAG}:refs/tags/$${CI_COMMIT_TAG}"
|
||||
tag_commit=$(git rev-parse "refs/tags/$${CI_COMMIT_TAG}^{commit}")
|
||||
expected_commit=$(printf '%s' "$${CI_COMMIT_SHA}" | tr '[:upper:]' '[:lower:]')
|
||||
test "$(printf '%s' "$${tag_commit}" | tr '[:upper:]' '[:lower:]')" = "$${expected_commit}"
|
||||
tag_commit=$(git rev-parse --verify "refs/tags/$${CI_COMMIT_TAG}^{commit}")
|
||||
expected_commit=$(git rev-parse --verify "$${CI_COMMIT_SHA}^{commit}")
|
||||
if [ "$${tag_commit}" != "$${expected_commit}" ]; then
|
||||
printf 'Tag %s resolves to commit %s; event %s resolves to commit %s\n' \
|
||||
"$${CI_COMMIT_TAG}" "$${tag_commit}" "$${CI_COMMIT_SHA}" "$${expected_commit}" >&2
|
||||
exit 1
|
||||
fi
|
||||
git checkout --detach "$${tag_commit}"
|
||||
test "$(git rev-parse HEAD | tr '[:upper:]' '[:lower:]')" = "$${expected_commit}"
|
||||
test -z "$(git status --porcelain --untracked-files=all)"
|
||||
@@ -30,5 +34,5 @@ steps:
|
||||
"$${bootstrap_dir}/releasekit" ci-deliver \
|
||||
--repo "$${CI_WORKSPACE}" \
|
||||
--prepared \
|
||||
--commit "$${CI_COMMIT_SHA}" \
|
||||
--commit "$${expected_commit}" \
|
||||
"$${CI_COMMIT_TAG}"
|
||||
|
||||
@@ -19,9 +19,13 @@ steps:
|
||||
test -n "$${CI_COMMIT_SHA}"
|
||||
git check-ref-format "refs/tags/$${CI_COMMIT_TAG}"
|
||||
git fetch origin "refs/tags/$${CI_COMMIT_TAG}:refs/tags/$${CI_COMMIT_TAG}"
|
||||
tag_commit=$(git rev-parse "refs/tags/$${CI_COMMIT_TAG}^{commit}")
|
||||
expected_commit=$(printf '%s' "$${CI_COMMIT_SHA}" | tr '[:upper:]' '[:lower:]')
|
||||
test "$(printf '%s' "$${tag_commit}" | tr '[:upper:]' '[:lower:]')" = "$${expected_commit}"
|
||||
tag_commit=$(git rev-parse --verify "refs/tags/$${CI_COMMIT_TAG}^{commit}")
|
||||
expected_commit=$(git rev-parse --verify "$${CI_COMMIT_SHA}^{commit}")
|
||||
if [ "$${tag_commit}" != "$${expected_commit}" ]; then
|
||||
printf 'Tag %s resolves to commit %s; event %s resolves to commit %s\n' \
|
||||
"$${CI_COMMIT_TAG}" "$${tag_commit}" "$${CI_COMMIT_SHA}" "$${expected_commit}" >&2
|
||||
exit 1
|
||||
fi
|
||||
git checkout --detach "$${tag_commit}"
|
||||
test "$(git rev-parse HEAD | tr '[:upper:]' '[:lower:]')" = "$${expected_commit}"
|
||||
test -z "$(git status --porcelain --untracked-files=all)"
|
||||
@@ -46,5 +50,5 @@ steps:
|
||||
"$${bootstrap_dir}/releasekit" ci-deliver \
|
||||
--repo "$${CI_WORKSPACE}" \
|
||||
--prepared \
|
||||
--commit "$${CI_COMMIT_SHA}" \
|
||||
--commit "$${expected_commit}" \
|
||||
"$${CI_COMMIT_TAG}"
|
||||
|
||||
Reference in New Issue
Block a user