From b500b855c6442b39b1b8823c9ab29abfb0aeb420 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 10 Nov 2026 16:02:17 +0000 Subject: [PATCH] Update toolchain and release integration --- .woodpecker/release.yml | 21 +- docs/oss/attribution.md | 46 ++--- docs/release.md | 39 ++-- docs/roadmap/implementation.md | 5 + go.mod | 11 +- go.sum | 25 +++ internal/cli/run.go | 2 +- internal/cli/version_test.go | 2 +- releasekit.json | 20 +- scripts/release.sh | 6 +- scripts/release_test.go | 349 +-------------------------------- 11 files changed, 119 insertions(+), 407 deletions(-) diff --git a/.woodpecker/release.yml b/.woodpecker/release.yml index b4d2ff7..0887346 100644 --- a/.woodpecker/release.yml +++ b/.woodpecker/release.yml @@ -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}" \ diff --git a/docs/oss/attribution.md b/docs/oss/attribution.md index ddb92e4..c6da857 100644 --- a/docs/oss/attribution.md +++ b/docs/oss/attribution.md @@ -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 diff --git a/docs/release.md b/docs/release.md index 99a013c..1e5bdf9 100644 --- a/docs/release.md +++ b/docs/release.md @@ -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. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 018edb7..95a0882 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -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`, diff --git a/go.mod b/go.mod index 56a3d30..ab45c7b 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 5116123..ae4f797 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/cli/run.go b/internal/cli/run.go index a8af88f..047f7bf 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -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" ) diff --git a/internal/cli/version_test.go b/internal/cli/version_test.go index 6637afc..d7b8ec9 100644 --- a/internal/cli/version_test.go +++ b/internal/cli/version_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "gitea.maximumdirect.net/eric/ReleaseKit/buildinfo" + "go.maximumdirect.net/releasekit/buildinfo" ) func TestVersionCommandRunsWithoutApplicationSetup(t *testing.T) { diff --git a/releasekit.json b/releasekit.json index 3c6569f..fe185a5 100644 --- a/releasekit.json +++ b/releasekit.json @@ -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 + } } } diff --git a/scripts/release.sh b/scripts/release.sh index 86c4913..b00350b 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -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 "$@" diff --git a/scripts/release_test.go b/scripts/release_test.go index 95d8cf7..0a33723 100644 --- a/scripts/release_test.go +++ b/scripts/release_test.go @@ -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 {