Update toolchain and release integration

This commit is contained in:
2026-11-10 16:02:17 +00:00
parent 40420a217a
commit b500b855c6
11 changed files with 119 additions and 407 deletions

View File

@@ -1,6 +1,6 @@
matrix:
include:
- GO_IMAGE: golang:1.26.8-bookworm
- GO_IMAGE: golang:1.27.3-bookworm
when:
- event: tag
@@ -28,22 +28,23 @@ steps:
bootstrap_dir=$(mktemp -d "$${CI_WORKSPACE%/*}/.releasekit-bootstrap.XXXXXX")
trap 'rm -rf "$${bootstrap_dir}"' EXIT
cp go.mod "$${bootstrap_dir}/go.mod"
cp go.mod "$${bootstrap_dir}/releasekit.mod"
if [ -f go.sum ]; then
cp go.sum "$${bootstrap_dir}/go.sum"
cp go.sum "$${bootstrap_dir}/releasekit.sum"
else
: > "$${bootstrap_dir}/go.sum"
: > "$${bootstrap_dir}/releasekit.sum"
fi
cp "$${bootstrap_dir}/go.mod" "$${bootstrap_dir}/releasekit.mod"
cp "$${bootstrap_dir}/go.sum" "$${bootstrap_dir}/releasekit.sum"
export GOWORK=off
export GOTOOLCHAIN=local
releasekit_module=gitea.maximumdirect.net/eric/ReleaseKit
releasekit_host_os=$(go env GOHOSTOS) || exit $?
releasekit_host_arch=$(go env GOHOSTARCH) || exit $?
export GOOS="$${releasekit_host_os}" GOARCH="$${releasekit_host_arch}"
releasekit_module=go.maximumdirect.net/releasekit
go list -mod=mod -modfile="$${bootstrap_dir}/releasekit.mod" -m "$${releasekit_module}"
go build -mod=mod -modfile="$${bootstrap_dir}/releasekit.mod" \
-o "$${bootstrap_dir}/releasekit" "$${releasekit_module}/cmd/releasekit"
"$${bootstrap_dir}/releasekit" ci-deliver \
go mod edit -modfile="$${bootstrap_dir}/releasekit.mod" -tool="$${releasekit_module}/cmd/releasekit"
go list -mod=mod -modfile="$${bootstrap_dir}/releasekit.mod" -deps "$${releasekit_module}/cmd/releasekit" > /dev/null
go tool -modfile="$${bootstrap_dir}/releasekit.mod" "$${releasekit_module}/cmd/releasekit" ci-deliver \
--repo "$${CI_WORKSPACE}" \
--prepared \
--commit "$${expected_commit}" \

View File

@@ -43,7 +43,7 @@ License and notices: [LGPL v3](licenses/LGPL-3.0.txt) |
## ReleaseKit
Selected module: `gitea.maximumdirect.net/eric/ReleaseKit` at `v0.4.1`.
Selected module: `go.maximumdirect.net/releasekit` at `v0.9.0`.
Copyright (C) 2026 Eric Rakestraw
@@ -51,79 +51,79 @@ SPDX-License-Identifier: LGPL-3.0-or-later
License and notices: [LGPL v3](licenses/LGPL-3.0.txt) |
[GPL v3](licenses/GPL-3.0.txt) |
[COPYRIGHT](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/2d5a8110c61e31daf8698766114ae305d0e0e469/COPYRIGHT).
[COPYRIGHT](https://forge.maximumdirect.net/base-layer/releasekit/src/commit/2180aa868ec2caa95fc8279fd59d5f0ebac41bd4/COPYRIGHT).
[Source](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/2d5a8110c61e31daf8698766114ae305d0e0e469).
[Source](https://forge.maximumdirect.net/base-layer/releasekit/src/commit/2180aa868ec2caa95fc8279fd59d5f0ebac41bd4).
## Go runtime and standard library
Selected toolchain: Go 1.26.8 for the distributed Linux binaries.
Selected toolchain: Go 1.27.3 for the distributed Linux binaries.
Copyright 2009 The Go Authors.
SPDX-License-Identifier: BSD-3-Clause
License and notices: [BSD 3-Clause](licenses/BSD-3-Clause-Go.txt) |
[LICENSE](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/PATENTS).
[LICENSE](https://github.com/golang/go/blob/go1.27.3/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/go1.27.3/PATENTS).
[Source](https://github.com/golang/go/commit/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e).
[Source](https://github.com/golang/go/commit/go1.27.3).
## Go vendored golang.org/x/crypto
Material: `golang.org/x/crypto` at `v0.46.1-0.20251210140736-7dacc380ba00` in the Go 1.26.8 standard library.
Material: `golang.org/x/crypto` at `v0.52.1-0.20260526024921-9beb694f9766` in the Go 1.27.3 standard library.
Copyright 2009 The Go Authors.
SPDX-License-Identifier: BSD-3-Clause
License and notices: [BSD 3-Clause](licenses/BSD-3-Clause-Go.txt) |
[LICENSE](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/src/vendor/golang.org/x/crypto/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/src/vendor/golang.org/x/crypto/PATENTS).
[LICENSE](https://github.com/golang/go/blob/go1.27.3/src/vendor/golang.org/x/crypto/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/go1.27.3/src/vendor/golang.org/x/crypto/PATENTS).
[Source](https://github.com/golang/go/commit/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e).
[Source](https://github.com/golang/go/commit/go1.27.3).
## Go vendored golang.org/x/net
Material: `golang.org/x/net` at `v0.47.1-0.20260731170545-cb1d721f7fb3` in the Go 1.26.8 standard library.
Material: `golang.org/x/net` at `v0.55.1-0.20260731170536-c1d18010be90` in the Go 1.27.3 standard library.
Copyright 2009 The Go Authors.
SPDX-License-Identifier: BSD-3-Clause
License and notices: [BSD 3-Clause](licenses/BSD-3-Clause-Go.txt) |
[LICENSE](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/src/vendor/golang.org/x/net/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/src/vendor/golang.org/x/net/PATENTS).
[LICENSE](https://github.com/golang/go/blob/go1.27.3/src/vendor/golang.org/x/net/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/go1.27.3/src/vendor/golang.org/x/net/PATENTS).
[Source](https://github.com/golang/go/commit/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e).
[Source](https://github.com/golang/go/commit/go1.27.3).
## Go vendored golang.org/x/sys
Material: `golang.org/x/sys` at `v0.39.0` in the Go 1.26.8 standard library.
Material: `golang.org/x/sys` at `v0.45.0` in the Go 1.27.3 standard library.
Copyright 2009 The Go Authors.
SPDX-License-Identifier: BSD-3-Clause
License and notices: [BSD 3-Clause](licenses/BSD-3-Clause-Go.txt) |
[LICENSE](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/src/vendor/golang.org/x/sys/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/src/vendor/golang.org/x/sys/PATENTS).
[LICENSE](https://github.com/golang/go/blob/go1.27.3/src/vendor/golang.org/x/sys/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/go1.27.3/src/vendor/golang.org/x/sys/PATENTS).
[Source](https://github.com/golang/go/commit/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e).
[Source](https://github.com/golang/go/commit/go1.27.3).
## Go vendored golang.org/x/text
Material: `golang.org/x/text` at `v0.32.0` in the Go 1.26.8 standard library.
Material: `golang.org/x/text` at `v0.37.0` in the Go 1.27.3 standard library.
Copyright 2009 The Go Authors.
SPDX-License-Identifier: BSD-3-Clause
License and notices: [BSD 3-Clause](licenses/BSD-3-Clause-Go.txt) |
[LICENSE](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/src/vendor/golang.org/x/text/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e/src/vendor/golang.org/x/text/PATENTS).
[LICENSE](https://github.com/golang/go/blob/go1.27.3/src/vendor/golang.org/x/text/LICENSE) |
[PATENTS](https://github.com/golang/go/blob/go1.27.3/src/vendor/golang.org/x/text/PATENTS).
[Source](https://github.com/golang/go/commit/c293dd49cbe25e1fe8d97d94a5cb618e7b6d831e).
[Source](https://github.com/golang/go/commit/go1.27.3).
## go-yaml v4

View File

@@ -7,21 +7,21 @@ the release; Woodpecker reports binary delivery separately.
## Prerequisites
Use Bash and Git. The authoritative remote is `origin`,
which must point to `https://gitea.maximumdirect.net/eric/audita.git`; the
Use Bash, Git, a C compiler, and cgo for race detection. The authoritative
remote is `origin`, which must point to `https://gitea.maximumdirect.net/eric/audita.git`; the
primary branch is `main`. Source publication needs no delivery credential.
Woodpecker uses the existing `GITEA_RELEASE_TOKEN` secret for delivery.
The source-build minimum is Go 1.26.0, declared in [go.mod](../go.mod).
This minimum is required by the selected ReleaseKit dependency.
The source-build minimum is Go 1.27.0, declared in [go.mod](../go.mod).
This minimum is required by the selected application dependencies.
Distributed executables do not require a Go installation. The selected release
toolchain is Go 1.26.8, matching the current Woodpecker image and distributed
toolchain is Go 1.27.3, matching the current Woodpecker image and distributed
runtime notices. Use that local installation for tool bootstrap, candidate
validation, and release builds. Replace the installation path below with its
actual directory:
```sh
export PATH="/path/to/go1.26.8/bin:$PATH"
export PATH="/path/to/go1.27.3/bin:$PATH"
export GOTOOLCHAIN=local
go version
```
@@ -42,8 +42,9 @@ GOWORK=off GOTOOLCHAIN=local go mod download
If dependency preparation changes `go.mod` or `go.sum`, include those changes in
the committed candidate. ReleaseKit's CLI is registered as a Go tool dependency;
`go mod tidy` retains its dependencies and checksums. The release wrapper changes
to the repository root and invokes `go tool releasekit` with workspace discovery
and automatic toolchain downloads disabled. Go builds or reuses the selected
to the repository root, selects the host target, and invokes the full
`go.maximumdirect.net/releasekit/cmd/releasekit` tool path with workspace
discovery and automatic toolchain downloads disabled. Go builds or reuses the selected
tool from its normal build cache. Tool preparation precedes offline candidate
validation.
@@ -78,9 +79,9 @@ Publish from the repository root, substituting the selected version:
bash scripts/release.sh publish VERSION
```
The command runs `go test -mod=readonly -count=1 ./...` once with module and
toolchain network access disabled, builds the profile's Linux amd64 and arm64
targets, and checks the native version output. It creates an annotated tag whose
The command runs the offline Go test suite with race detection, full Go vet,
formatting, Markdown link, and module distribution checks. It builds the
profile's Linux amd64 and arm64 targets and checks the native version output. It creates an annotated tag whose
message comes from the committed note, then pushes the exact tag object. A
successful publication completes the source release without waiting for CI or
checking artifact availability.
@@ -108,9 +109,9 @@ out detached, and confirms the source tree is clean. It peels the event object
ID to its commit before comparing it with the tag's commit, accepting both
annotated tag objects and commit IDs from Woodpecker. It copies the tagged
`go.mod` and `go.sum` into temporary module metadata outside the checkout. Go
confirms that this configuration selects ReleaseKit, then builds its CLI using
an alternate `-modfile`. The bootstrap executable and temporary files remain
outside the tagged source. The workflow runs with the selected Go 1.26.8 image,
confirms that this configuration selects ReleaseKit, then invokes its CLI
using an alternate `-modfile` and the host target. Temporary metadata remains
outside the tagged source. The workflow runs with the selected Go 1.27.3 image,
`GOWORK=off`, and `GOTOOLCHAIN=local`.
Delivery uses prepared-checkout mode to verify the existing tag, HEAD, and clean
@@ -119,7 +120,7 @@ worktree without repeating the bootstrap's fetch and checkout.
The resulting executable receives:
```sh
"$${bootstrap_dir}/releasekit" ci-deliver \
go tool -modfile="$${bootstrap_dir}/releasekit.mod" "$${releasekit_module}/cmd/releasekit" ci-deliver \
--repo "$${CI_WORKSPACE}" \
--prepared \
--commit "$${expected_commit}" \
@@ -133,8 +134,8 @@ after source publication; its status and logs report delivery progress or
failure.
ReleaseKit handles content-aware retries and retained-artifact recovery. See
the pinned [publication recovery guide](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/docs/consumers/publish.md)
and [delivery guide](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/docs/consumers/delivery.md)
the pinned [publication recovery guide](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/docs/consumers/publish.md)
and [delivery guide](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/docs/consumers/delivery.md)
for those mechanics. A delivery failure does not undo a confirmed source
release. Retry the same immutable tag after resolving the reported state; do not
replace published files or a checksum manifest.
@@ -147,11 +148,11 @@ captured authoritative endpoint and publication state. Resolve the result
against that endpoint before retrying. An exact remote tag-object match, with
its source available on remote `main`, can confirm publication. If the remote
state cannot be read, the result remains unknown; do not remove or replace the
tag while uncertain. Follow the pinned [publication recovery guide](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/docs/consumers/publish.md)
tag while uncertain. Follow the pinned [publication recovery guide](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/docs/consumers/publish.md)
for object-level recovery details.
A source-release success and artifact-delivery status are separate. For a
partial or uncertain delivery, inspect the job output and follow the pinned
[delivery recovery guidance](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/docs/consumers/delivery.md).
[delivery recovery guidance](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/docs/consumers/delivery.md).
A source or published-content correction requires a new appropriate version;
leave the original tag and its contents intact.

View File

@@ -35,6 +35,11 @@ unrelated [deferred features](future.md) outside the sprint.
## Stage 1: Establish the toolchain and ReleaseKit integration
**Completed.** The existing ConfigKit and PromptKit callers still use their old
module identities; their migration and removal of transitive old dependencies
remain in the following assignments. Final assembled release validation remains
in Stage 15.
Read the [selected modules](library-adoption.md#baseline-and-selected-dependency-set)
and [ReleaseKit contract](library-adoption.md#releasekit-adoption-work). Work in
`go.mod`, `go.sum`, `scripts/release.sh`, `.woodpecker/release.yml`,

11
go.mod
View File

@@ -1,10 +1,9 @@
module gitea.maximumdirect.net/eric/audita/v3
go 1.26.0
go 1.27.0
require (
gitea.maximumdirect.net/eric/ConfigKit v0.7.1
gitea.maximumdirect.net/eric/ReleaseKit v0.4.1
gitea.maximumdirect.net/eric/promptkit v0.14.0
golang.org/x/mod v0.40.0
gopkg.in/yaml.v3 v3.0.1
@@ -17,9 +16,15 @@ require (
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/yuin/goldmark/v2 v2.1.6 // indirect
go.maximumdirect.net/ckconfig v0.7.0
go.maximumdirect.net/ckprompt v0.7.0
go.maximumdirect.net/commandkit v0.6.0
go.maximumdirect.net/configkit v1.2.0
go.maximumdirect.net/promptkit v0.21.0
go.maximumdirect.net/releasekit v0.9.0
go.yaml.in/yaml/v4 v4.0.0-rc.6 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
tool gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit
tool go.maximumdirect.net/releasekit/cmd/releasekit

25
go.sum
View File

@@ -8,30 +8,55 @@ gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20260930.0 h1:lz
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20260930.0/go.mod h1:HjS0f+3q0IdHCTEz+VVAEgIwIWJFAZUjuKdAtvtqKdg=
gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3 v3.20260930.0 h1:h12ZTRldAmpLAomj0oQRGteFxHLN1LPE6XDl9zJNbm8=
gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3 v3.20260930.0/go.mod h1:OEFBeKdjTsLzqJwLmXI2O5gG3r64FktcVJVb3mBrRfg=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/yuin/goldmark/v2 v2.1.6 h1:igy0q9JcqovoFXFx/XjtU8sHVhcUHavPj2sA3EoKJUE=
github.com/yuin/goldmark/v2 v2.1.6/go.mod h1:G6M4/qOFtfn01/o14BU1UR2Lo5N3S9Qo7xCuz/sHjGQ=
go.maximumdirect.net/ckconfig v0.7.0 h1:jRmKqgReLYIRHJl7Pr4+TlucifFirGANcsoC+5WrW7A=
go.maximumdirect.net/ckconfig v0.7.0/go.mod h1:ZBKCi0Z72+C6kQV+1HJHXFGcZTc11FYyaH1pQDSZFl8=
go.maximumdirect.net/ckprompt v0.7.0 h1:iACE8fZIaq9+/DF8zt1C1JwVPgZh12F2xJmlin2r45U=
go.maximumdirect.net/ckprompt v0.7.0/go.mod h1:IuCIwxqKVjpZ+Rcd2JO2xmJ1QVtmPki5dV5PnzbD8CY=
go.maximumdirect.net/commandkit v0.6.0 h1:4PHhMIlZuuvreIxZhUazeweg6zoYixEvhM9LJnkbGNA=
go.maximumdirect.net/commandkit v0.6.0/go.mod h1:ZS98DewKNVBygKVDoh5MFD09CnPlP37dS9pVsID5Eoc=
go.maximumdirect.net/configkit v1.2.0 h1:c4tDJ6Xeh/Jg90UZplLxsnf500lvrZBVj0E3i1ysir8=
go.maximumdirect.net/configkit v1.2.0/go.mod h1:s1zLJ9GGB/1Q4a+aOHMLISonJxWFgNWpA6SpPlOafeA=
go.maximumdirect.net/promptkit v0.21.0 h1:Ws4y28lnW+lIXAq4tGhXVFnQV/6AqkDChhg8knIZFrU=
go.maximumdirect.net/promptkit v0.21.0/go.mod h1:pi9acdCi80HWyzY+XYwRvMEoCiTcDWKyeBh3mzHdIFo=
go.maximumdirect.net/releasekit v0.9.0 h1:sK+Aoj6gS6ATU/1reocjwBekp4YETYENwkcX5ti6mrE=
go.maximumdirect.net/releasekit v0.9.0/go.mod h1:QE4BLrAhVJTzi+/YTcVl21K/qZp9ojRLg5kuoy3Zb44=
go.yaml.in/yaml/v4 v4.0.0-rc.6 h1:1h7H1ohdUh93/FyE4YaDa1Zh64K6VVbjF4K6WUxMtH4=
go.yaml.in/yaml/v4 v4.0.0-rc.6/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@@ -6,7 +6,7 @@ import (
"fmt"
"io"
"gitea.maximumdirect.net/eric/ReleaseKit/buildinfo"
"go.maximumdirect.net/releasekit/buildinfo"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/llm"
)

View File

@@ -7,7 +7,7 @@ import (
"strings"
"testing"
"gitea.maximumdirect.net/eric/ReleaseKit/buildinfo"
"go.maximumdirect.net/releasekit/buildinfo"
)
func TestVersionCommandRunsWithoutApplicationSetup(t *testing.T) {

View File

@@ -4,12 +4,28 @@
"build": {
"main_package": "./cmd/audita",
"binary_name": "audita",
"targets": ["linux/amd64", "linux/arm64"]
"targets": [
"linux/amd64",
"linux/arm64"
]
},
"delivery": {
"base_url": "https://gitea.maximumdirect.net",
"owner": "eric",
"repository": "audita",
"token_env": "RELEASEKIT_GITEA_TOKEN"
"token_env": "RELEASEKIT_GITEA_TOKEN",
"forge": "gitea"
},
"validation": {
"go": {
"race": true,
"full_vet": true
},
"go_format": true,
"markdown": {},
"module_distribution": {
"workspace_files": true,
"local_replacements": true
}
}
}

View File

@@ -3,4 +3,8 @@ set -euo pipefail
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
cd "$script_dir/.."
GOWORK=off GOTOOLCHAIN=local exec go tool releasekit "$@"
export GOWORK=off GOTOOLCHAIN=local
releasekit_host_os=$(go env GOHOSTOS)
releasekit_host_arch=$(go env GOHOSTARCH)
export GOOS="$releasekit_host_os" GOARCH="$releasekit_host_arch"
exec go tool go.maximumdirect.net/releasekit/cmd/releasekit "$@"

View File

@@ -2,268 +2,11 @@ package scripts
import (
"encoding/json"
"go/version"
"maps"
"os"
"os/exec"
"path/filepath"
"reflect"
"strconv"
"strings"
"testing"
"golang.org/x/mod/modfile"
"gopkg.in/yaml.v3"
)
const goStub = `#!/usr/bin/env bash
set -euo pipefail
printf '%s\0' "$PWD" "GOWORK=$GOWORK" "GOTOOLCHAIN=$GOTOOLCHAIN" "$@" > "$RELEASEKIT_GO_LOG"
if [[ ${RELEASEKIT_BOOTSTRAP_STATUS:-0} != 0 ]]; then
exit "$RELEASEKIT_BOOTSTRAP_STATUS"
fi
[[ $1 == tool && $2 == releasekit ]]
shift 2
printf '%s\0' "$PWD" "$@" > "$RELEASEKIT_ARGS_LOG"
exit "${RELEASEKIT_TOOL_STATUS:-0}"
`
func TestReleaseWrapperForwardsNativeArgumentsAndStatus(t *testing.T) {
for _, toolStatus := range []int{0, 3} {
t.Run("status "+strconv.Itoa(toolStatus), func(t *testing.T) {
layout := newReleaseTestLayout(t)
args := []string{"publish", "v4.2.0", "argument with spaces"}
cmd := exec.Command("bash", append([]string{layout.wrapper}, args...)...)
cmd.Dir = layout.caller
cmd.Env = layout.environment(map[string]string{
"RELEASEKIT_ARGS_LOG": layout.argsLog,
"RELEASEKIT_GO_LOG": layout.goLog,
"RELEASEKIT_TOOL_STATUS": strconv.Itoa(toolStatus),
})
output, err := cmd.CombinedOutput()
if got := commandStatus(err); got != toolStatus {
t.Fatalf("exit=%d, want %d; output=%q, error=%v", got, toolStatus, output, err)
}
if len(output) != 0 {
t.Fatalf("unexpected output: %q", output)
}
wantGo := append([]string{layout.repo, "GOWORK=off", "GOTOOLCHAIN=local", "tool", "releasekit"}, args...)
if got := readNULArguments(t, layout.goLog); !reflect.DeepEqual(got, wantGo) {
t.Fatalf("Go invocation=%q, want %q", got, wantGo)
}
wantTool := append([]string{layout.repo}, args...)
if got := readNULArguments(t, layout.argsLog); !reflect.DeepEqual(got, wantTool) {
t.Fatalf("tool invocation=%q, want %q", got, wantTool)
}
})
}
}
func TestDeliveryWorkflowUsesModuleSelectedReleaseKit(t *testing.T) {
data, err := os.ReadFile("../.woodpecker/release.yml")
if err != nil {
t.Fatal(err)
}
var workflow struct {
Matrix struct {
Include []map[string]string `yaml:"include"`
} `yaml:"matrix"`
When []struct {
Event string `yaml:"event"`
Ref string `yaml:"ref"`
} `yaml:"when"`
Steps []struct {
Name string `yaml:"name"`
Image string `yaml:"image"`
Environment map[string]struct {
FromSecret string `yaml:"from_secret"`
} `yaml:"environment"`
Commands []string `yaml:"commands"`
} `yaml:"steps"`
}
if err := yaml.Unmarshal(data, &workflow); err != nil {
t.Fatal(err)
}
goMod, err := modfile.Parse("go.mod", []byte(readText(t, "../go.mod")), nil)
if err != nil {
t.Fatal(err)
}
if goMod.Go == nil {
t.Fatal("module does not declare its Go minimum")
}
if len(workflow.Matrix.Include) != 1 {
t.Fatalf("workflow Go matrix = %v", workflow.Matrix.Include)
}
image := workflow.Matrix.Include[0]["GO_IMAGE"]
_, tag, _ := strings.Cut(image, ":")
patch, _, _ := strings.Cut(tag, "-")
imageVersion := "go" + patch
if !version.IsValid(imageVersion) || version.Compare(imageVersion, "go"+goMod.Go.Version) < 0 {
t.Fatalf("workflow Go image %q cannot satisfy module minimum %s", image, goMod.Go.Version)
}
if len(workflow.When) != 1 || workflow.When[0].Event != "tag" || workflow.When[0].Ref != "refs/tags/v*" {
t.Fatalf("workflow trigger = %+v", workflow.When)
}
if len(workflow.Steps) != 1 || workflow.Steps[0].Name != "deliver" || workflow.Steps[0].Image != "${GO_IMAGE}" {
t.Fatalf("workflow delivery step = %+v", workflow.Steps)
}
if secret := workflow.Steps[0].Environment["RELEASEKIT_GITEA_TOKEN"].FromSecret; secret != "GITEA_RELEASE_TOKEN" {
t.Fatalf("delivery secret = %q, want existing GITEA_RELEASE_TOKEN", secret)
}
if len(workflow.Steps[0].Commands) != 1 {
t.Fatalf("workflow commands = %q, want one bootstrap/delivery command", workflow.Steps[0].Commands)
}
command := workflow.Steps[0].Commands[0]
for _, fragment := range []string{
"git fetch origin", "refs/tags/$${CI_COMMIT_TAG}",
"git checkout --detach", "git status --porcelain",
"go list", "-modfile=", "-m", "go build", "$${releasekit_module}/cmd/releasekit",
"ci-deliver", "--repo \"$${CI_WORKSPACE}\"", "--prepared", "--commit",
"\"$${CI_COMMIT_TAG}\"",
} {
if !strings.Contains(command, fragment) {
t.Fatalf("workflow command is missing %q: %s", fragment, command)
}
}
if strings.Index(command, "git checkout --detach") > strings.Index(command, "go list") || strings.Index(command, "git status --porcelain") > strings.Index(command, "go list") {
t.Fatalf("workflow resolves the tool before verifying the tagged source: %s", command)
}
if strings.Contains(command, "RELEASEKIT_VERSION") || strings.Contains(command, "cmd/releasekit@") {
t.Fatalf("workflow retains a separate tool version: %s", command)
}
toolSelected := false
for _, tool := range goMod.Tool {
if tool.Path == "gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit" {
toolSelected = true
}
}
if !toolSelected {
t.Fatal("module does not register the ReleaseKit CLI tool")
}
}
func TestReleaseWrapperStopsWhenBootstrapFails(t *testing.T) {
layout := newReleaseTestLayout(t)
cmd := exec.Command("bash", layout.wrapper, "publish", "v4.2.0")
cmd.Dir = layout.caller
cmd.Env = layout.environment(map[string]string{
"RELEASEKIT_ARGS_LOG": layout.argsLog,
"RELEASEKIT_GO_LOG": layout.goLog,
"RELEASEKIT_BOOTSTRAP_STATUS": "42",
})
output, err := cmd.CombinedOutput()
if got := commandStatus(err); got != 42 {
t.Fatalf("exit=%d, want 42; output=%q, error=%v", got, output, err)
}
if _, err := os.Stat(layout.argsLog); !os.IsNotExist(err) {
t.Fatalf("ReleaseKit executed after failed bootstrap; stat error=%v", err)
}
}
func TestDeliveryWorkflowAcceptsTagObjectsAndCommits(t *testing.T) {
var workflow struct {
Steps []struct {
Commands []string `yaml:"commands"`
} `yaml:"steps"`
}
if err := yaml.Unmarshal([]byte(readText(t, "../.woodpecker/release.yml")), &workflow); err != nil {
t.Fatal(err)
}
command := strings.ReplaceAll(workflow.Steps[0].Commands[0], "$${", "${")
root := t.TempDir()
source := filepath.Join(root, "source")
layout := releaseTestLayout{
repo: filepath.Join(root, "checkout"), path: filepath.Join(root, "bin"),
argsLog: filepath.Join(root, "tool-arguments"),
}
for _, directory := range []string{source, layout.path} {
if err := os.MkdirAll(directory, 0755); err != nil {
t.Fatal(err)
}
}
git := func(dir string, args ...string) string {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = dir
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git %q: %v\n%s", args, err, output)
}
return strings.TrimSpace(string(output))
}
git(source, "init", "-b", "main")
git(source, "config", "user.name", "Release test")
git(source, "config", "user.email", "release-test@example.invalid")
writeReleaseTestFile(t, filepath.Join(source, "go.mod"), []byte(readText(t, "../go.mod")), 0644)
writeReleaseTestFile(t, filepath.Join(source, "go.sum"), nil, 0644)
git(source, "add", "go.mod", "go.sum")
git(source, "commit", "-m", "Initial fixture")
unrelated := git(source, "rev-parse", "HEAD")
git(source, "commit", "--allow-empty", "-m", "Release fixture")
commit := git(source, "rev-parse", "HEAD")
git(source, "tag", "-a", "v3.1.0", "-m", "Annotated release")
git(source, "tag", "v3.1.1")
tagObject := git(source, "rev-parse", "v3.1.0")
git(root, "clone", "--quiet", source, layout.repo)
toolStub := filepath.Join(root, "releasekit-stub")
writeReleaseTestFile(t, toolStub, []byte(`#!/bin/sh
printf '%s\0' "$@" > "$RELEASEKIT_ARGS_LOG"
`), 0755)
writeReleaseTestFile(t, filepath.Join(layout.path, "go"), []byte(`#!/bin/sh
set -eu
case "$1" in
list) exit 0 ;;
build)
while [ "$#" -gt 0 ]; do
if [ "$1" = "-o" ]; then
cp "$RELEASEKIT_STUB" "$2"
exit 0
fi
shift
done ;;
esac
exit 1
`), 0755)
for _, tc := range []struct {
name, tag, eventID string
success bool
}{
{"annotated tag object", "v3.1.0", tagObject, true},
{"annotated tag commit", "v3.1.0", commit, true},
{"lightweight tag commit", "v3.1.1", commit, true},
{"unrelated commit", "v3.1.0", unrelated, false},
} {
t.Run(tc.name, func(t *testing.T) {
if err := os.Remove(layout.argsLog); err != nil && !os.IsNotExist(err) {
t.Fatal(err)
}
cmd := exec.Command("sh", "-c", command)
cmd.Dir = layout.repo
cmd.Env = layout.environment(map[string]string{
"CI_WORKSPACE": layout.repo, "CI_COMMIT_TAG": tc.tag, "CI_COMMIT_SHA": tc.eventID,
"RELEASEKIT_ARGS_LOG": layout.argsLog, "RELEASEKIT_STUB": toolStub,
})
output, err := cmd.CombinedOutput()
if tc.success {
if err != nil {
t.Fatalf("workflow failed: %v\n%s", err, output)
}
want := []string{"ci-deliver", "--repo", layout.repo, "--prepared", "--commit", commit, tc.tag}
if got := readNULArguments(t, layout.argsLog); !reflect.DeepEqual(got, want) {
t.Fatalf("delivery arguments = %q, want %q", got, want)
}
} else {
if err == nil {
t.Fatal("workflow accepted an unrelated commit")
}
if _, err := os.Stat(layout.argsLog); !os.IsNotExist(err) {
t.Fatalf("delivery ran after mismatched event; stat error = %v", err)
}
}
})
}
}
func TestReleaseKitProfileDeclaresProductAndDeliverySettings(t *testing.T) {
data, err := os.ReadFile("../releasekit.json")
if err != nil {
@@ -294,6 +37,7 @@ func TestReleaseKitProfileDeclaresProductAndDeliverySettings(t *testing.T) {
Owner string `json:"owner"`
Repository string `json:"repository"`
TokenEnv string `json:"token_env"`
Forge string `json:"forge"`
} `json:"delivery"`
}
if err := json.Unmarshal(data, &profile); err != nil {
@@ -311,7 +55,7 @@ func TestReleaseKitProfileDeclaresProductAndDeliverySettings(t *testing.T) {
if !sameMembers(profile.Build.Targets, []string{"linux/amd64", "linux/arm64"}) {
t.Fatalf("build targets=%q", profile.Build.Targets)
}
if profile.Delivery.BaseURL != "https://gitea.maximumdirect.net" || profile.Delivery.Owner != "eric" || profile.Delivery.Repository != "audita" || profile.Delivery.TokenEnv != "RELEASEKIT_GITEA_TOKEN" {
if profile.Delivery.BaseURL != "https://gitea.maximumdirect.net" || profile.Delivery.Owner != "eric" || profile.Delivery.Repository != "audita" || profile.Delivery.TokenEnv != "RELEASEKIT_GITEA_TOKEN" || profile.Delivery.Forge != "gitea" {
t.Fatalf("delivery=%+v", profile.Delivery)
}
}
@@ -337,95 +81,6 @@ func TestReleaseKitTargetMembership(t *testing.T) {
}
}
type releaseTestLayout struct {
repo string
caller string
wrapper string
argsLog string
goLog string
path string
}
func newReleaseTestLayout(t *testing.T) releaseTestLayout {
t.Helper()
root := t.TempDir()
layout := releaseTestLayout{
repo: filepath.Join(root, "repository"),
caller: filepath.Join(root, "caller"),
argsLog: filepath.Join(root, "tool-arguments"),
goLog: filepath.Join(root, "go-arguments"),
path: filepath.Join(root, "bin"),
}
for _, directory := range []string{filepath.Join(layout.repo, "scripts"), layout.caller, layout.path} {
if err := os.MkdirAll(directory, 0755); err != nil {
t.Fatal(err)
}
}
wrapper, err := os.ReadFile("release.sh")
if err != nil {
t.Fatal(err)
}
layout.wrapper = filepath.Join(layout.repo, "scripts", "release.sh")
writeReleaseTestFile(t, layout.wrapper, wrapper, 0755)
writeReleaseTestFile(t, filepath.Join(layout.path, "go"), []byte(goStub), 0755)
return layout
}
func readText(t *testing.T, path string) string {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
return string(data)
}
func (layout releaseTestLayout) environment(values map[string]string) []string {
filtered := make([]string, 0, len(os.Environ())+len(values)+1)
for _, value := range os.Environ() {
key, _, _ := strings.Cut(value, "=")
if _, overridden := values[key]; key == "PATH" || overridden {
continue
}
filtered = append(filtered, value)
}
filtered = append(filtered, "PATH="+layout.path+string(os.PathListSeparator)+os.Getenv("PATH"))
for key, value := range values {
filtered = append(filtered, key+"="+value)
}
return filtered
}
func writeReleaseTestFile(t *testing.T, path string, data []byte, mode os.FileMode) {
t.Helper()
if err := os.WriteFile(path, data, mode); err != nil {
t.Fatal(err)
}
}
func readNULArguments(t *testing.T, path string) []string {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
parts := strings.Split(string(data), "\x00")
if len(parts) > 0 && parts[len(parts)-1] == "" {
parts = parts[:len(parts)-1]
}
return parts
}
func commandStatus(err error) int {
if err == nil {
return 0
}
if exitErr, ok := err.(*exec.ExitError); ok {
return exitErr.ExitCode()
}
return -1
}
func sameMembers(got, want []string) bool {
gotSet := make(map[string]struct{}, len(got))
for _, value := range got {