Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd62f360a7 | |||
| 5a11b34aad | |||
| f9cf5c3861 | |||
| ea223fcf98 | |||
| 8d416558a3 | |||
| 561a84fcf8 | |||
| 3c405f0524 | |||
| 5ac704c4c4 | |||
| 5af6ba7d89 | |||
| 06dc273896 | |||
| 7e7940db9a | |||
| 721365e59d | |||
| 418da456d7 | |||
| 6de470b53d |
@@ -1,26 +1,21 @@
|
|||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- GO_IMAGE: golang:1.25.5
|
||||||
|
RELEASEKIT_VERSION: v0.1.0
|
||||||
|
|
||||||
when:
|
when:
|
||||||
- event: tag
|
- event: tag
|
||||||
|
ref: refs/tags/v*
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build-release-assets
|
- name: deliver
|
||||||
image: golang:1.25.5
|
image: ${GO_IMAGE}
|
||||||
commands:
|
environment:
|
||||||
- bash scripts/build-release.sh "$CI_COMMIT_TAG" dist
|
RELEASEKIT_GITEA_TOKEN:
|
||||||
|
|
||||||
- name: publish-release
|
|
||||||
image: woodpeckerci/plugin-release
|
|
||||||
depends_on:
|
|
||||||
- build-release-assets
|
|
||||||
settings:
|
|
||||||
api_key:
|
|
||||||
from_secret: GITEA_RELEASE_TOKEN
|
from_secret: GITEA_RELEASE_TOKEN
|
||||||
files:
|
commands:
|
||||||
- dist/audita-*-linux-amd64
|
- |
|
||||||
- dist/audita-*-linux-arm64
|
go run gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@${RELEASEKIT_VERSION} ci-deliver \
|
||||||
note: docs/releases/${CI_COMMIT_TAG}.md
|
--repo "$${CI_WORKSPACE}" \
|
||||||
checksum: sha256
|
--commit "$${CI_COMMIT_SHA}" \
|
||||||
checksum-file: SHA256SUMS
|
"$${CI_COMMIT_TAG}"
|
||||||
checksum-flatten: true
|
|
||||||
file-exists: skip
|
|
||||||
overwrite: false
|
|
||||||
prerelease: false
|
|
||||||
|
|||||||
45
docs/adr/0001-releasekit.md
Normal file
45
docs/adr/0001-releasekit.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# ADR 0001: Adopt ReleaseKit for release management
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted.
|
||||||
|
|
||||||
|
## Date
|
||||||
|
|
||||||
|
2026-10-07
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Audita maintains custom release scripts, build tooling, delivery configuration,
|
||||||
|
and tests for mechanics shared across applications in the same suite.
|
||||||
|
[ReleaseKit](https://gitea.maximumdirect.net/eric/ReleaseKit) provides a common implementation and standard workflow for
|
||||||
|
those responsibilities.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Adopt ReleaseKit for shared release validation, source publication, binary
|
||||||
|
builds, version information, and hosted delivery. Audita will follow its standard
|
||||||
|
workflow and supply product identity, delivery targets, release notes, and
|
||||||
|
necessary product-specific checks.
|
||||||
|
|
||||||
|
Better maintainability and less locally owned release-focused code outweigh any
|
||||||
|
small advantage from retaining Audita's custom release procedure. Changes to
|
||||||
|
that procedure are an accepted consequence of standardization.
|
||||||
|
|
||||||
|
## Alternatives considered
|
||||||
|
|
||||||
|
Retaining the custom procedure would preserve its exact behavior and local
|
||||||
|
control. It would also retain responsibility for duplicated mechanics, tests,
|
||||||
|
and maintenance across projects. The shared implementation offers greater
|
||||||
|
long-term value.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Audita's release integration becomes smaller, with common mechanics and their
|
||||||
|
tests maintained upstream. Audita benefits from shared fixes and takes on
|
||||||
|
ReleaseKit dependency maintenance.
|
||||||
|
|
||||||
|
Adoption requires updates to release tooling, CI wiring, documentation, and
|
||||||
|
runtime version reporting. The [release policy](../policy/release.md) continues
|
||||||
|
to govern compatibility and release completion; the
|
||||||
|
[release procedure](../release.md) will describe the adopted workflow.
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
# Architectural Decision Records
|
# Architectural Decision Records
|
||||||
|
|
||||||
No ADRs have been recorded here. Use the
|
## Accepted
|
||||||
|
|
||||||
|
- [ADR 0001: Adopt ReleaseKit for release management](0001-releasekit.md) —
|
||||||
|
accepted 2026-10-07.
|
||||||
|
|
||||||
|
Use the
|
||||||
[documentation policy's ADR lifecycle](../policy/documentation.md#architecture-decision-records)
|
[documentation policy's ADR lifecycle](../policy/documentation.md#architecture-decision-records)
|
||||||
for significant decisions. Current architecture belongs in the
|
for significant decisions. Current architecture belongs in the
|
||||||
[architecture policy](../policy/architecture.md); implementation status belongs in
|
[architecture policy](../policy/architecture.md); implementation status belongs in
|
||||||
|
|||||||
11
docs/cli.md
11
docs/cli.md
@@ -11,6 +11,7 @@ the [migration guide](migrations/v2.md) maps retired inputs.
|
|||||||
audita process <transcript.json> --glossary <glossary.yml> [flags]
|
audita process <transcript.json> --glossary <glossary.yml> [flags]
|
||||||
audita config validate --config <config.yml> [flags]
|
audita config validate --config <config.yml> [flags]
|
||||||
audita config print-effective [flags]
|
audita config print-effective [flags]
|
||||||
|
audita version
|
||||||
```
|
```
|
||||||
|
|
||||||
Root invocation, `--help`, `-h`, and command help succeed without loading a config
|
Root invocation, `--help`, `-h`, and command help succeed without loading a config
|
||||||
@@ -26,6 +27,16 @@ Missing system candidates are skipped; inaccessible selected files are errors.
|
|||||||
No file means a configuration error. `AUDITA_CONFIG` is ignored. The selected
|
No file means a configuration error. `AUDITA_CONFIG` is ignored. The selected
|
||||||
overlay is loaded beside the main file, not used to locate profile files.
|
overlay is loaded beside the main file, not used to locate profile files.
|
||||||
|
|
||||||
|
`audita version` prints `audita <version>` and a newline to stdout, with no
|
||||||
|
application configuration or provider setup. It returns `0` on success with
|
||||||
|
empty stderr. `help version`, `version -h`, and `version --help` print command
|
||||||
|
usage to stdout and return `0`. Help follows standard flag parsing and can
|
||||||
|
short-circuit trailing arguments. Other invalid invocations return `2` with a
|
||||||
|
usage error on stderr. The command uses ReleaseKit's public
|
||||||
|
`buildinfo.Version()` accessor. It reports an injected release identifier when
|
||||||
|
present, otherwise the executable's main-module version when available and not
|
||||||
|
`(devel)`, and `dev` when neither source provides a release identifier.
|
||||||
|
|
||||||
## Setting overrides
|
## Setting overrides
|
||||||
|
|
||||||
Only flags actually supplied override file or overlay settings. `--modules ''`
|
Only flags actually supplied override file or overlay settings. `--modules ''`
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ CLI wiring, and maintained examples. Definitions of behavior belong to the
|
|||||||
for the changed behavior.
|
for the changed behavior.
|
||||||
4. Update the CLI reference and affected integration guidance.
|
4. Update the CLI reference and affected integration guidance.
|
||||||
|
|
||||||
|
The `version` route uses ReleaseKit's public `buildinfo.Version()` accessor and
|
||||||
|
stays independent of application runtime assembly. Preserve its behavior from
|
||||||
|
the [CLI contract](../cli.md#commands-and-help) when changing the command; the
|
||||||
|
adoption decision is recorded in [ADR 0001](../adr/0001-releasekit.md).
|
||||||
|
|
||||||
## Validating Examples
|
## Validating Examples
|
||||||
|
|
||||||
Keep complete copyable files in [examples](../../examples/) and link to them from
|
Keep complete copyable files in [examples](../../examples/) and link to them from
|
||||||
|
|||||||
@@ -28,6 +28,20 @@ License and notices: [LGPL v3](licenses/LGPL-3.0.txt) |
|
|||||||
|
|
||||||
[Source](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/dc09959cca4e105f49ec316c6d4ba585bd2340bd).
|
[Source](https://gitea.maximumdirect.net/eric/ConfigKit/src/commit/dc09959cca4e105f49ec316c6d4ba585bd2340bd).
|
||||||
|
|
||||||
|
## ReleaseKit
|
||||||
|
|
||||||
|
Selected module: `gitea.maximumdirect.net/eric/ReleaseKit` at `v0.1.0`.
|
||||||
|
|
||||||
|
Copyright (C) 2026 Eric Rakestraw
|
||||||
|
|
||||||
|
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/5e60fe3113c69f95c42fbbe32a8ca4b51f5b83c1/COPYRIGHT).
|
||||||
|
|
||||||
|
[Source](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/5e60fe3113c69f95c42fbbe32a8ca4b51f5b83c1).
|
||||||
|
|
||||||
## Go runtime and standard library
|
## Go runtime and standard library
|
||||||
|
|
||||||
Selected toolchain: Go 1.25.5 for the distributed Linux binaries.
|
Selected toolchain: Go 1.25.5 for the distributed Linux binaries.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Base Policy Version:** 1.4
|
**Base Policy Version:** 1.4
|
||||||
|
|
||||||
**Last Updated**: 2026-09-28
|
**Last Updated**: 2026-10-07
|
||||||
**Upstream Source:** [Standard Repository Policies release](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/release.md)
|
**Upstream Source:** [Standard Repository Policies release](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/release.md)
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
@@ -49,12 +49,17 @@ failures and optional investigations under artifact-delivery status.
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Linux amd64 | Required production target |
|
| Linux amd64 | Required production target |
|
||||||
| Linux arm64 | Required production target |
|
| Linux arm64 | Required production target |
|
||||||
| macOS arm64 | Best effort for local development; failures do not block a release or Linux artifact publication |
|
| macOS arm64 | Best effort for local development; development failures do not block a release or Linux artifact publication |
|
||||||
|
|
||||||
Windows and macOS amd64 are unsupported. Neither their builds nor tests are
|
Windows and macOS amd64 are unsupported. Neither their builds nor tests are
|
||||||
release requirements. Native macOS testing and access to a Mac are not release
|
release requirements. Native macOS testing and access to a Mac are not release
|
||||||
prerequisites.
|
prerequisites.
|
||||||
|
|
||||||
|
Linux amd64 and arm64 remain the required production targets. Publication from
|
||||||
|
a host outside the selected target set must pass ReleaseKit's temporary native
|
||||||
|
version check. This check does not require access to a Mac or native tests on
|
||||||
|
every delivery target.
|
||||||
|
|
||||||
## Versioning And Compatibility
|
## Versioning And Compatibility
|
||||||
|
|
||||||
Stable releases use Semantic Versioning in the form `vMAJOR.MINOR.PATCH`, with
|
Stable releases use Semantic Versioning in the form `vMAJOR.MINOR.PATCH`, with
|
||||||
|
|||||||
186
docs/release.md
186
docs/release.md
@@ -1,144 +1,120 @@
|
|||||||
# Releasing Audita
|
# Releasing Audita
|
||||||
|
|
||||||
The [release policy](policy/release.md) defines versioning, supported platforms,
|
The [release policy](policy/release.md) owns release requirements. This runbook
|
||||||
immutability, and success. This runbook implements it with
|
uses the native commands in [scripts/release.sh](../scripts/release.sh), which
|
||||||
[scripts/release.sh](../scripts/release.sh). Publication ends after a successful
|
builds and invokes ReleaseKit. A confirmed source tag completes the release;
|
||||||
source/tag push; binary delivery runs asynchronously.
|
Woodpecker reports binary delivery separately.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Use Bash, Git, and Go 1.25.5 or newer (CI uses Go 1.25.5), with dependencies
|
Use Bash, Git, and Go 1.25.5 or newer. The authoritative remote is `origin`,
|
||||||
available locally or downloadable. Tests themselves are offline. Work from a
|
which must point to `https://gitea.maximumdirect.net/eric/audita.git`; the
|
||||||
checkout of `main` with permission to push that branch and tags to `origin`.
|
primary branch is `main`. Source publication needs no delivery credential.
|
||||||
For this repository, `origin` is `https://gitea.maximumdirect.net/eric/audita.git`.
|
Woodpecker uses the existing `GITEA_RELEASE_TOKEN` secret for delivery.
|
||||||
The script uses the checkout's configured `origin`; set it correctly before use.
|
|
||||||
The remote must support atomic pushes. No CI credentials or Mac are needed locally.
|
|
||||||
|
|
||||||
CI uses the existing `GITEA_RELEASE_TOKEN` secret for asynchronous asset delivery.
|
Prepare dependencies before offline candidate validation:
|
||||||
macOS arm64 is a best-effort development environment; no macOS binary is published.
|
|
||||||
|
|
||||||
## Prepare the Candidate
|
```sh
|
||||||
|
GOWORK=off GOTOOLCHAIN=local go mod download
|
||||||
|
```
|
||||||
|
|
||||||
Choose an unused version following the policy. Write
|
If dependency preparation changes `go.mod` or `go.sum`, include those changes in
|
||||||
`docs/releases/<version>.md`, beginning with `# Audita <version>`, followed by a
|
the committed candidate. The release wrapper separately bootstraps the pinned
|
||||||
short summary and any compatibility or upgrade instructions relevant to the
|
ReleaseKit command into ignored `bin/releasekit`; that build is tool preparation,
|
||||||
release. There are no mandatory boilerplate sections. Update affected canonical
|
not candidate validation.
|
||||||
documentation with behavior changes.
|
|
||||||
|
|
||||||
End each new note with these links, substituting the selected release version for
|
## Prepare and publish the candidate
|
||||||
`<version>`:
|
|
||||||
|
Choose an unused version under the [release policy](policy/release.md), write
|
||||||
|
`docs/releases/<version>.md`, and begin it with `# Audita <version>`. Include a
|
||||||
|
short summary, relevant compatibility or upgrade instructions, and the required
|
||||||
|
`COPYRIGHT | LICENSE | ATTRIBUTION` footer with links for the selected version.
|
||||||
|
Update affected canonical documentation with behavior changes.
|
||||||
|
|
||||||
|
End each note with this footer, substituting its complete tag for `<version>`:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
[COPYRIGHT](https://gitea.maximumdirect.net/eric/audita/src/tag/<version>/COPYRIGHT) | [LICENSE](https://gitea.maximumdirect.net/eric/audita/src/tag/<version>/LICENSE) | [ATTRIBUTION](https://gitea.maximumdirect.net/eric/audita/src/tag/<version>/docs/oss/attribution.md)
|
[COPYRIGHT](https://gitea.maximumdirect.net/eric/audita/src/tag/<version>/COPYRIGHT) | [LICENSE](https://gitea.maximumdirect.net/eric/audita/src/tag/<version>/LICENSE) | [ATTRIBUTION](https://gitea.maximumdirect.net/eric/audita/src/tag/<version>/docs/oss/attribution.md)
|
||||||
```
|
```
|
||||||
|
|
||||||
Check that all three paths exist in the candidate source. Retire completed
|
Commit the complete candidate on `main`, including the release note, then push
|
||||||
roadmaps when useful during preparation, preserving planned and deferred work
|
the branch first:
|
||||||
and repairing affected links. This is ordinary preparation, not a script gate.
|
|
||||||
|
|
||||||
Commit the intended changes, including the note, on `main`. The release script
|
|
||||||
requires a clean index and worktree and does not create commits. There is no
|
|
||||||
separate approval round. Published notes describe their tagged release; existing
|
|
||||||
release history does not need to be reconstructed.
|
|
||||||
|
|
||||||
## Validate and Publish
|
|
||||||
|
|
||||||
From the repository root, substitute the chosen version:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bash scripts/release.sh --publish v0.2.0
|
git push origin main
|
||||||
```
|
```
|
||||||
|
|
||||||
This single invocation:
|
Record the candidate commit. Before publication, remote `main` must identify
|
||||||
|
that exact commit. ReleaseKit checks this along with the clean candidate, note,
|
||||||
|
version, and remote tag state.
|
||||||
|
|
||||||
1. Checks the version, branch, clean candidate, matching note title, and tag absence.
|
Publish from the repository root, substituting the selected version:
|
||||||
2. Runs the default offline test suite once, then builds both required Linux targets
|
|
||||||
in a temporary directory using the same build script as CI.
|
|
||||||
3. Checks that the candidate and worktree are unchanged and that it includes the
|
|
||||||
current `origin/main` history.
|
|
||||||
4. Creates the version tag at that commit and atomically pushes the exact source
|
|
||||||
commit to `main` and the exact tag to `origin`.
|
|
||||||
5. Reports release success and exits without polling CI or checking asset availability.
|
|
||||||
|
|
||||||
To validate a prepared candidate without remote access, creating a tag, or pushing:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bash scripts/release.sh --check v0.2.0
|
bash scripts/release.sh publish VERSION
|
||||||
```
|
```
|
||||||
|
|
||||||
This is an optional diagnostic command, not a required step before `--publish`.
|
The command runs `go test -mod=readonly -count=1 ./...` once with module and
|
||||||
Publication validates within its own invocation; it does not use persistent cached
|
toolchain network access disabled, builds the profile's Linux amd64 and arm64
|
||||||
results. Both modes require the candidate note and a clean `main` checkout.
|
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.
|
||||||
|
|
||||||
## Artifact Delivery
|
`bash scripts/release.sh check VERSION` is an optional local diagnostic. It
|
||||||
|
does not inspect remote availability or publish a tag, and it is not a required
|
||||||
|
step before `publish`, which performs its own candidate validation.
|
||||||
|
|
||||||
[Woodpecker](../.woodpecker/release.yml) runs on tag events. It invokes
|
The wrapper preserves ReleaseKit's exit status: `0` means confirmed publication;
|
||||||
[scripts/build-release.sh](../scripts/build-release.sh), then publishes:
|
`1` means a known validation or publication failure/conflict; `2` means usage,
|
||||||
|
profile, or version input error; and `3` means the push outcome remains unknown.
|
||||||
|
|
||||||
|
## Binary delivery
|
||||||
|
|
||||||
|
Woodpecker runs the pinned ReleaseKit `ci-deliver` command for tags under
|
||||||
|
`refs/tags/v*`. It verifies the triggering commit against the tag, prepares the
|
||||||
|
tagged source, and uses `releasekit.json` to build and deliver:
|
||||||
|
|
||||||
- `audita-<version>-linux-amd64`
|
- `audita-<version>-linux-amd64`
|
||||||
- `audita-<version>-linux-arm64`
|
- `audita-<version>-linux-arm64`
|
||||||
- `SHA256SUMS`
|
- `SHA256SUMS`
|
||||||
|
|
||||||
Builds disable CGO and use the shared script's release flags. The version is in the
|
The workflow invokes:
|
||||||
asset name and source tag; Audita has no CLI version command. Cross-compilation
|
|
||||||
checks compilation, not native execution on each architecture.
|
|
||||||
|
|
||||||
The CI job status and logs show delivery progress or failure. Assets appear on
|
|
||||||
the matching Gitea release page when delivery succeeds. A successful tag push
|
|
||||||
must not be reported as confirmation that these files are available.
|
|
||||||
For a new hosted release, the release plugin uses the checked-in note at the
|
|
||||||
tag as its body, including the footer. On delivery retries it preserves
|
|
||||||
existing release information and skips files that already exist.
|
|
||||||
|
|
||||||
Consumers can download the binary for their Linux architecture and `SHA256SUMS`
|
|
||||||
from that page, check its listed SHA-256 digest with `sha256sum`, and make it
|
|
||||||
executable. Optional consumer checks are not maintainer release gates. Digests
|
|
||||||
detect file mismatches; they are not an independent signature of authenticity.
|
|
||||||
|
|
||||||
## Failure and Retry
|
|
||||||
|
|
||||||
**Local validation fails:** correct the issue, commit any changes, and rerun the
|
|
||||||
publication command. A failure before tag creation publishes nothing. Temporary
|
|
||||||
local build files are removed on exit.
|
|
||||||
|
|
||||||
**Push fails or the connection is interrupted:** the script retains the local tag
|
|
||||||
and reports failure rather than guessing whether publication succeeded. Inspect
|
|
||||||
remote state with the actual version:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git rev-parse 'v0.2.0^{commit}'
|
go run gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@${RELEASEKIT_VERSION} ci-deliver \
|
||||||
git ls-remote origin refs/heads/main refs/tags/v0.2.0
|
--repo "$${CI_WORKSPACE}" \
|
||||||
|
--commit "$${CI_COMMIT_SHA}" \
|
||||||
|
"$${CI_COMMIT_TAG}"
|
||||||
```
|
```
|
||||||
|
|
||||||
If the remote tag identifies the intended candidate and its source is on remote
|
Woodpecker sets `RELEASEKIT_VERSION` to `v0.1.0` for this workflow.
|
||||||
`main`, the release succeeded; no CI waiting is needed. An atomic push normally
|
|
||||||
updates both refs or neither. If `main` has advanced since publication, fetch it
|
|
||||||
and check candidate ancestry rather than requiring tip equality.
|
|
||||||
|
|
||||||
If the tag is absent remotely and was never published or used by a release
|
The complete checked-in release note, including its licensing footer, supplies
|
||||||
channel, remove only that unpublished local tag (`git tag -d v0.2.0`), resolve
|
the hosted release body. The workflow maps the existing `GITEA_RELEASE_TOKEN`
|
||||||
any rejected branch update, and rerun the command. If the remote tag has a
|
secret to `RELEASEKIT_GITEA_TOKEN`. Delivery runs independently after source
|
||||||
different target, do not overwrite it; choose a fresh version. Never force a
|
publication; its status and logs report delivery progress or failure.
|
||||||
branch or tag update to bypass the guards.
|
|
||||||
|
|
||||||
**CI delivery fails:** the release remains successful. Inspect the failed tag
|
ReleaseKit handles content-aware retries and retained-artifact recovery. See
|
||||||
job's logs and retry that same tagged job for a transient failure. Publication is
|
the pinned [publication recovery guide](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.1.0/docs/consumers/publish.md)
|
||||||
configured to skip existing filenames and not overwrite assets. Keep the same
|
and [delivery guide](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.1.0/docs/consumers/delivery.md)
|
||||||
source and build environment when retrying; inspect any existing assets/checksum
|
for those mechanics. A delivery failure does not undo a confirmed source
|
||||||
manifest before filling gaps. Do not regenerate and replace a published manifest
|
release. Retry the same immutable tag after resolving the reported state; do not
|
||||||
or binary. If an existing manifest cannot describe the missing rebuilt assets,
|
replace published files or a checksum manifest.
|
||||||
use a new version instead. A pipeline or source defect also requires a corrected
|
|
||||||
commit and new version. An outage does not require moving the tag.
|
|
||||||
|
|
||||||
**A released source defect is discovered:** fix it on `main` and publish a new
|
## Failure and recovery
|
||||||
appropriate version with a note explaining the correction. Leave the old tag and
|
|
||||||
published files intact.
|
|
||||||
|
|
||||||
## Maintaining the Release Workflow
|
Fix validation failures in a new committed candidate, then rerun `publish`.
|
||||||
|
After a failed push, ReleaseKit retains the local annotated tag and reports the
|
||||||
|
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.1.0/docs/consumers/publish.md)
|
||||||
|
for object-level recovery details.
|
||||||
|
|
||||||
The shared build script keeps local and CI build settings aligned. Tests in
|
A source-release success and artifact-delivery status are separate. For a
|
||||||
[scripts/release_test.go](../scripts/release_test.go) exercise publication against
|
partial or uncertain delivery, inspect the job output and follow the pinned
|
||||||
temporary local Git remotes, including guard failures and rejected pushes, with
|
[delivery recovery guidance](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.1.0/docs/consumers/delivery.md).
|
||||||
Go execution stubbed to avoid recursive builds/tests. They run in `go test ./...`.
|
A source or published-content correction requires a new appropriate version;
|
||||||
Changes to CI delivery should preserve explicit asset names, checksums, and
|
leave the original tag and its contents intact.
|
||||||
non-overwriting publication. CI delivery is separate from the local success path.
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
|
- [Audita v3.0.1](v3.0.1.md)
|
||||||
- [Audita v3.0.0](v3.0.0.md)
|
- [Audita v3.0.0](v3.0.0.md)
|
||||||
- [Audita v2.0.1](v2.0.1.md)
|
- [Audita v2.0.1](v2.0.1.md)
|
||||||
- [Audita v2.0.0](v2.0.0.md)
|
- [Audita v2.0.0](v2.0.0.md)
|
||||||
|
|||||||
14
docs/releases/v3.0.1.md
Normal file
14
docs/releases/v3.0.1.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Audita v3.0.1
|
||||||
|
|
||||||
|
This patch release adds a configuration-free version command and adopts
|
||||||
|
ReleaseKit for source publication and Linux binary delivery. Transcript
|
||||||
|
processing, configuration, and LLM behavior are unchanged.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- `audita version` prints the application version without loading application
|
||||||
|
configuration or contacting an LLM provider.
|
||||||
|
- Release publication and Linux artifact delivery now use ReleaseKit v0.1.0.
|
||||||
|
The release runbook describes the maintainer workflow and delivery recovery.
|
||||||
|
|
||||||
|
[COPYRIGHT](https://gitea.maximumdirect.net/eric/audita/src/tag/v3.0.1/COPYRIGHT) | [LICENSE](https://gitea.maximumdirect.net/eric/audita/src/tag/v3.0.1/LICENSE) | [ATTRIBUTION](https://gitea.maximumdirect.net/eric/audita/src/tag/v3.0.1/docs/oss/attribution.md)
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
**Status:** Complete. The implementation and tests are committed through
|
**Status:** Complete. The implementation and tests are committed through
|
||||||
`8175ed8`. Final documentation and example changes were included in the full
|
`8175ed8`. Final documentation and example changes were included in the full
|
||||||
build, test, example-validation, and local-link checks before the documentation
|
build, test, example-validation, and local-link checks before the documentation
|
||||||
completion commit. The [implementation plan](implementation.md) records the
|
completion commit. The [historical implementation plan](https://gitea.maximumdirect.net/eric/audita/src/commit/b31955fc97adec707b4e5f9a1b0c43c4cbc66750/docs/roadmap/implementation.md)
|
||||||
ordered work and verification.
|
records the ordered work and verification.
|
||||||
|
|
||||||
## Purpose and Baseline
|
## Purpose and Baseline
|
||||||
|
|
||||||
|
|||||||
@@ -1,523 +1,327 @@
|
|||||||
# Implementation Plan: PromptKit v0.12.0 Adoption
|
# Implementation Plan: ReleaseKit Adoption
|
||||||
|
|
||||||
**Last updated:** 2026-10-04
|
**Last updated:** 2026-10-07
|
||||||
**Status:** Complete; Stages 1–9 completed.
|
**Status:** Complete; Stages 1–5 complete.
|
||||||
**Audience:** Luna coding agent.
|
**Audience:** Luna coding agent.
|
||||||
|
|
||||||
## Scope and Execution
|
## Scope and Execution
|
||||||
|
|
||||||
Implement the accepted target in the [PromptKit roadmap](promptkit.md). That
|
Implement the target in the [ReleaseKit roadmap](releasekit.md), following
|
||||||
document owns feature intent and policy choices; this plan owns the ordered
|
[ADR 0001](../adr/0001-releasekit.md). The roadmap owns intent and policy
|
||||||
implementation work. The planning baseline is Audita commit
|
choices; this document owns execution. The planning baseline is Audita commit
|
||||||
`b31955fc97adec707b4e5f9a1b0c43c4cbc66750`: ConfigKit v0.5.0 is adopted,
|
`418da456d75a68d3bb5a43ab823c5b2b434b90cc`: v3.0.0 is released, the ADR is
|
||||||
PromptKit is v0.11.2, and the finalized roadmap includes the selected-profile,
|
accepted, and release tooling still uses the custom scripts and delivery plugin.
|
||||||
operator-override, example-layout, and major-release clarifications.
|
|
||||||
|
|
||||||
This replaces the completed ConfigKit plan at this path. Its
|
This replaces the completed PromptKit plan. Its
|
||||||
[completed implementation record](https://gitea.maximumdirect.net/eric/audita/src/commit/b31955fc97adec707b4e5f9a1b0c43c4cbc66750/docs/roadmap/implementation.md)
|
[completion record](https://gitea.maximumdirect.net/eric/audita/src/commit/418da456d75a68d3bb5a43ab823c5b2b434b90cc/docs/roadmap/implementation.md)
|
||||||
remains available for readers following the ConfigKit completion roadmap.
|
remains available for historical adoption references.
|
||||||
|
|
||||||
Execute Stage 1 through Stage 9 in order. Each is sized as one bounded Luna
|
Execute Stage 1 through Stage 5 in order. Each is a bounded Luna implementation
|
||||||
implementation prompt. Commit completed units with short, plain English messages
|
unit. Commit completed units with plain English messages under the
|
||||||
under the [agent policy](../policy/agents.md); no approval round is needed between
|
[agent policy](../policy/agents.md), recording the relevant checks. Intermediate
|
||||||
stages. Record actual checks and any remaining expected migration failures.
|
commits need not be release-ready; complete the migration directly without
|
||||||
Stages 1 and 2 change formats before Stage 3 changes their consumer. Intermediate
|
compatibility wrappers for the old release arguments or temporary release modes.
|
||||||
commits need not be runnable or release-ready. Do not add temporary readers,
|
|
||||||
compatibility shims, fallback engines for execution, or scaffolding to make them
|
|
||||||
so. Later stages complete the same migration rather than separate product modes.
|
|
||||||
|
|
||||||
Start with [development guidance](../development.md) and the applicable agent,
|
Start with [development guidance](../development.md), use graph-first discovery,
|
||||||
[architecture](../policy/architecture.md), [testing](../policy/testing.md),
|
and follow the applicable [architecture](../policy/architecture.md),
|
||||||
[documentation](../policy/documentation.md), and [OSS](../policy/oss.md)
|
[testing](../policy/testing.md), [documentation](../policy/documentation.md),
|
||||||
policies. Use graph-first discovery. Read the current
|
[release](../policy/release.md), and [OSS](../policy/oss.md) policies. Preserve
|
||||||
[configuration](../config.md), [LLM runtime](../internal/llm-runtime.md),
|
transcript processing and its current configuration, reporting, and LLM behavior.
|
||||||
[prompt registry](../internal/prompts.md), and
|
|
||||||
[LLM integration](../integrations/openai-compatible-llm.md) contracts for the
|
|
||||||
established behavior this migration preserves.
|
|
||||||
|
|
||||||
Target the released PromptKit v0.12.0 at commit
|
Use ReleaseKit v0.1.0, commit
|
||||||
`4b8169da286917fde7875425bb32b799b12c6af3`, ConfigKit v0.5.0, and both
|
`5e60fe3113c69f95c42fbbe32a8ca4b51f5b83c1`. The sibling checkout is reference
|
||||||
maintained `/v3` catalogs at `v3.20260930.0`. Read the pinned
|
material. Its pinned [configuration](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.1.0/docs/config.md),
|
||||||
[format reference](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.12.0/docs/formats.md)
|
[CLI](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.1.0/docs/cli.md),
|
||||||
and [migration guide](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.12.0/docs/consumers/pkg-promptkit.md#migrate-from-the-previous-configuration-api).
|
[build](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.1.0/docs/consumers/build.md),
|
||||||
The sibling checkout is reference material, not a dependency replacement.
|
and [delivery](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.1.0/docs/consumers/delivery.md)
|
||||||
|
contracts own generic mechanics. Keep Go 1.25.5 and unrelated dependency
|
||||||
|
selections. Use released modules without a production local replacement.
|
||||||
|
|
||||||
### Selected Implementation Decisions
|
### Selected Implementation Decisions
|
||||||
|
|
||||||
- Keep Go 1.25.5, application `version: 2`, current flags, role selection,
|
- Audita imports `ReleaseKit/buildinfo` for runtime version information. The
|
||||||
application overlays, and main-file-relative path rules. Retain the
|
upstream executable owns release orchestration; no application release engine
|
||||||
`profiles_dir` name and default; broaden its accepted documents as agreed.
|
or private linker variable is needed.
|
||||||
- Use PromptKit's catalog and owned defaults, then caller underlays for embedded
|
- `releasekit.json` owns product facts. Local tooling builds the executable from
|
||||||
Audita prompts and operator directory inputs, then `Sources.Main` for
|
the module graph; CI pins the same v0.1.0 release explicitly.
|
||||||
application backend settings. Directory inputs compose strictly within their
|
- Keep `scripts/release.sh` as a thin entry point with native ReleaseKit
|
||||||
group; discovery order supplies no field precedence. Keep imports disabled.
|
arguments, such as `publish VERSION` and `check VERSION`. Compile its tool into
|
||||||
- Use one ConfigKit mapping for the final application source. For each
|
ignored `bin/releasekit` and invoke it with `exec` to preserve native statuses.
|
||||||
application backend, supply its endpoint, `concurrency_limit`,
|
- Main-first preparation, annotated tags, the offline standard suite, Linux
|
||||||
`queue_capacity`, and explicit `api_key_env: null`. The null suppresses
|
builds, and the native version smoke check are accepted. On a nonselected
|
||||||
lower-tier credential lookup. Leave other supported provider fields available
|
host, the shared builder may compile a temporary native executable and require
|
||||||
to native layering. Do not put resolved credential values in this mapping.
|
its check to succeed. Keep only Linux amd64 and arm64 delivery targets.
|
||||||
- Keep selected-role registration, endpoint-match, competing-credential, and
|
- CI uses managed `ci-deliver` and the existing `GITEA_RELEASE_TOKEN` secret,
|
||||||
required-key checks. Add explicit nonblank effective model and endpoint checks
|
exposed to the command as `RELEASEKIT_GITEA_TOKEN`. Retained prebuilt artifacts
|
||||||
for both selected roles. Valid unselected partial profiles remain permitted.
|
remain an upstream recovery option rather than a second ordinary CI sequence.
|
||||||
- Preserve the adapter's complete per-call schemas, configured repairs/retries,
|
- Additional profile checks are omitted. Do not repeat the standard suite,
|
||||||
operation context, shared variables, observation, and prepare/check/run flow.
|
maintain a local Git/publication guard layer, or copy upstream delivery logic.
|
||||||
Operator messages can omit shared-variable references; do not append the
|
Focus tests on Audita wiring and reuse existing protection for common mechanics.
|
||||||
shared text unconditionally or require particular message counts or roles
|
|
||||||
beyond PromptKit's own format rules.
|
|
||||||
- Use captured source inspection for consumed host configuration and message
|
|
||||||
paths. Keep source-relative resource resolution with PromptKit and ConfigKit;
|
|
||||||
Audita projects paths for its existing output collision checks.
|
|
||||||
- Replace the old profile-reload test expectation with snapshot behavior.
|
|
||||||
Successful construction uses only final effective definitions. A failed
|
|
||||||
construction may make an embedded-only diagnostic construction to distinguish
|
|
||||||
asset defects from operator errors; it never executes or substitutes for the
|
|
||||||
requested configuration.
|
|
||||||
- Maintained example roots are `examples/profiles/local/` and
|
|
||||||
`examples/profiles/service/`. Service testing profiles stay in the service
|
|
||||||
root. Keep the literal `temperature: 0` in examples as an explicit outbound
|
|
||||||
setting, and use native presence semantics without a compatibility translator.
|
|
||||||
|
|
||||||
This unit is an intentional major-release migration. Exact version selection,
|
This implementation sprint does not choose an Audita release version, write a
|
||||||
release notes, tag/push, and publication timing remain release preparation under
|
new versioned release note, tag, push to the authoritative repository, contact
|
||||||
the [release policy](../policy/release.md). Guided recovery, new report formats,
|
Gitea with delivery credentials, or wait for CI. Deployment observations belong
|
||||||
hot reload, extra configuration commands, and scheduling changes remain outside
|
to the first adopting release and its separate delivery status. No extra release
|
||||||
this plan. Tests should protect changed integration behavior and established
|
gates or platform guarantees are introduced by these adoption checks.
|
||||||
contracts, not reproduce the libraries' entire validation matrices.
|
|
||||||
|
|
||||||
## Stage 1 — Convert Embedded Prompt Definitions
|
## Stage 1 — Add the Dependency and Version Command [x]
|
||||||
|
|
||||||
**Primary files:** The seven YAML files under
|
**Primary files:** `go.mod`, `go.sum`, `internal/cli/run.go`, and focused tests in
|
||||||
`internal/prompts/assets/prompts/`.
|
`internal/cli`.
|
||||||
|
|
||||||
1. Wrap each existing definition beneath `prompts`, its exact current ID, and
|
1. Add `gitea.maximumdirect.net/eric/ReleaseKit@v0.1.0` with `go get`. Import its
|
||||||
the string version `v1`. Remove the old inner `id` and `version` fields.
|
public `buildinfo` package in the CLI and run `go mod tidy` after the import
|
||||||
Keep filenames and Audita registry identifiers unchanged; filenames are
|
is present. Review the dependency diff for unrelated upgrades or replacements.
|
||||||
asset locations rather than PromptKit identities.
|
2. Add `version` dispatch to the existing CLI. With no arguments, print
|
||||||
2. Preserve messages, block-scalar text, variable references, task examples,
|
`audita ` followed by `buildinfo.Version()` and a newline to stdout, returning
|
||||||
output settings, and schema paths. Preserve the applicable references to
|
zero. Run this route independently of application configuration and runtime
|
||||||
`HardeningPolicy`, `ProposalContract`, and `ValidationContract`. Do not edit
|
assembly. Use the existing output streams and standard-library parsing style.
|
||||||
shared fragments, response schemas, or domain policies to accommodate the
|
3. Add the command to root help. Support `help version` and the usual
|
||||||
new container format.
|
`version -h`/`version --help` help behavior with stdout and status zero.
|
||||||
3. Review each before/after definition structurally, including YAML indentation
|
Unexpected positional arguments or unknown flags use the existing usage-error
|
||||||
inside block scalars. Do not introduce default profile selections or new
|
status two. No new version flags are needed at the root level.
|
||||||
repair counts. The adapter continues to own the runtime output contract.
|
4. Add compact CLI coverage for successful version output, help, and malformed
|
||||||
|
invocation. Derive the expected version from the public accessor rather than
|
||||||
**Verification and completion:** Check all seven migrated identities and
|
hardcoding `dev`. Exercise the version route from a temporary working directory
|
||||||
version keys against `RegisteredMetadata`, and run `git diff --check`. Registry
|
without application setup and verify stderr stays empty on success. Preserve
|
||||||
preparation tests become runnable with the new constructor in Stage 3. Commit
|
existing commands and help behavior.
|
||||||
the asset conversion; do not introduce an old-format reader to test it here.
|
|
||||||
|
|
||||||
**Completion marker:** Stage 1 complete. All seven registered prompt assets
|
|
||||||
use keyed prompt/version mappings; parsed definitions were structurally checked
|
|
||||||
against their prior content, and `git diff --check` passed.
|
|
||||||
|
|
||||||
## Stage 2 — Migrate Operator Profiles, Example Roots, and YAML Fixtures
|
|
||||||
|
|
||||||
**Primary files:** `examples/profiles/`, the three maintained configuration
|
|
||||||
examples, and profile-producing test fixtures in
|
|
||||||
`internal/framework/llm/{runtime_inspection,adapter,adapter_capacity}_test.go`,
|
|
||||||
`internal/framework/proposal_generation/adapter_integration_test.go`,
|
|
||||||
`internal/framework/validators/llm_integration_test.go`,
|
|
||||||
`internal/framework/runner/cancellation_test.go`, and
|
|
||||||
`cmd/audita/main_integration_test.go`.
|
|
||||||
|
|
||||||
1. Move local proposal/validation examples beneath `profiles/local/`; move the
|
|
||||||
four service proposal/validation and testing files beneath
|
|
||||||
`profiles/service/`. Convert each to `profiles: {<ID>: <fields>}` in readable
|
|
||||||
YAML. Keep IDs, models, positive timeouts, and controls, including explicit
|
|
||||||
zero temperature. No example needs a real credential or live model.
|
|
||||||
2. Set `profiles_dir: profiles/local` in `examples/minimal-config.yml` and
|
|
||||||
`examples/configkit-inheritance.yml`; set `profiles_dir: profiles/service`
|
|
||||||
in `examples/production-config.yml`. Preserve the testing overlay's role IDs
|
|
||||||
and the existing ConfigKit inheritance example. Select the leaf root, since
|
|
||||||
recursive discovery at their common parent would still load both groups.
|
|
||||||
3. Convert profile YAML emitted by the listed fixtures to keyed definitions.
|
|
||||||
Search for remaining old profile construction strings and removed
|
|
||||||
`base_profile` syntax, and migrate any genuine profile fixture found.
|
|
||||||
Do not rewrite transcript/proposal `id` fields or domain schema fixtures.
|
|
||||||
Keep test source names independent of profile IDs where already exercised.
|
|
||||||
4. Keep negative tests negative for their selected behavioral reason. Give
|
|
||||||
cases clean temporary layouts or replace their contribution completely
|
|
||||||
rather than leaving an unrelated invalid definition behind for later cases.
|
|
||||||
Eager validation now reads unselected final definitions too. Do not remove
|
|
||||||
such definitions merely to simulate the old lazy loader where the test is
|
|
||||||
intended to exercise eager validation.
|
|
||||||
|
|
||||||
**Verification and completion:** Review the six profiles, explicit root
|
|
||||||
selections, and changed fixture text; run `git diff --check` and format touched
|
|
||||||
Go test files. These formats become consumable in Stage 3. Commit this unit.
|
|
||||||
Current documentation links and recipes are updated together in Stage 8.
|
|
||||||
|
|
||||||
**Completion marker:** Stage 2 complete. Six maintained profile documents are
|
|
||||||
keyed and split into separately selected local/service roots; the listed Go
|
|
||||||
fixtures use keyed definitions. Touched Go files were formatted and
|
|
||||||
`git diff --check` passed. Runtime tests are deferred until Stage 3 replaces
|
|
||||||
the current consumer, as the plan allows intermediate commits not to run.
|
|
||||||
|
|
||||||
## Stage 3 — Adopt the Released Source API and Selected-Role Checks
|
|
||||||
|
|
||||||
**Primary files:** `go.mod`, `go.sum`,
|
|
||||||
`internal/framework/llm/runtime.go`, and `internal/prompts/registry_test.go`.
|
|
||||||
|
|
||||||
1. Upgrade with `go get gitea.maximumdirect.net/eric/promptkit@v0.12.0` and
|
|
||||||
reconcile the module files. Verify ConfigKit stays v0.5.0, the catalog
|
|
||||||
dependencies become `/v3` at `v3.20260930.0`, and obsolete `/v2` requirements
|
|
||||||
are removed when unused. Retain the Go minimum and unrelated selections;
|
|
||||||
introduce no local replacements.
|
|
||||||
2. Resolve `profiles_dir` exactly as the current runtime does. Obtain embedded
|
|
||||||
inputs with `pk.FilesystemInputs` and operator inputs with `pk.DirectoryInputs`.
|
|
||||||
An unavailable selected root is an operator error; an empty directory can
|
|
||||||
contribute no definitions. Do not require one profile per file, IDs matching
|
|
||||||
filenames, or profile-only top-level content.
|
|
||||||
3. Construct the agreed `pk.ConfigurationSources`: two ordered caller underlays
|
|
||||||
for embedded and operator groups, and one highest-priority application
|
|
||||||
`ck.Input` whose source is the mapping specified above. Retain
|
|
||||||
`pk.WithSchemaFS`, the supplied HTTP client, and configured transient retries.
|
|
||||||
Remove old prompt/profile/backend registration options.
|
|
||||||
4. Inspect registered prompt ID/version selections, then both selected roles.
|
|
||||||
Reject a selected role whose effective model or endpoint is empty or
|
|
||||||
whitespace-only. Retain backend registration, normalized endpoint equality,
|
|
||||||
nonblank effective `APIKeyEnv` rejection, and required-credential checks.
|
|
||||||
Preserve literal IDs; do not trim/re-key identities or require all profiles
|
|
||||||
to be complete. Populate the same role inspection map for both clients.
|
|
||||||
5. Migrate the registry test engine to `FilesystemInputs` and a programmatic
|
|
||||||
`ProfileSource` fixture in its own tier, retaining its schema filesystem.
|
|
||||||
A complete endpoint/model fixture without an application backend is valid
|
|
||||||
for this direct library test; Audita's selected-role guard belongs in its
|
|
||||||
runtime tests. Keep the embedded prompt preparation assertions scoped to
|
|
||||||
embedded prompts.
|
|
||||||
6. Remove the old `profileSourceFS` wrapper once unused. Source accounting is
|
|
||||||
completed in Stage 5. Preserve the adapter boundary; change other production
|
|
||||||
callers only to resolve actual API incompatibilities. Search for remaining
|
|
||||||
uses of removed source options and old `pk.Profile`/`pk.Backend` values.
|
|
||||||
Run `go mod tidy` after migrating construction callers and review its diff.
|
|
||||||
|
|
||||||
**Verification and completion:** Run `go test ./internal/prompts` and the
|
|
||||||
existing offline profile assembly, unsafe-selection, and absolute-directory
|
|
||||||
tests in `internal/framework/llm`. Run `git diff --check` and review the module
|
|
||||||
diff. The obsolete file-reload assertion is intentionally replaced in Stage 6;
|
|
||||||
do not restore reload behavior to make that assertion pass. Commit the runtime
|
|
||||||
API migration.
|
|
||||||
|
|
||||||
**Completion marker:** Stage 3 complete. Runtime construction now uses the
|
|
||||||
released PromptKit source API with the selected application mapping; module
|
|
||||||
selections match PromptKit v0.12.0 and both `/v3` catalogs at
|
|
||||||
`v3.20260930.0`. `go mod tidy`, prompt registry tests, offline profile
|
|
||||||
assembly, unsafe-selection, absolute-directory tests, and `git diff --check`
|
|
||||||
passed. Captured source accounting remains in Stage 5.
|
|
||||||
|
|
||||||
## Stage 4 — Preserve Startup Failure Classification and Validate Assembly
|
|
||||||
|
|
||||||
**Primary files:** `internal/framework/llm/runtime.go`,
|
|
||||||
`internal/framework/llm/runtime_inspection_test.go`, and configuration-command
|
|
||||||
tests in `internal/cli`. Update `internal/cli/run.go` only where the changed
|
|
||||||
startup path needs existing exit behavior preserved.
|
|
||||||
|
|
||||||
1. Treat embedded discovery failures as `AssetError`; treat operator-directory
|
|
||||||
discovery failures as configuration input errors. Preserve wrapped causes
|
|
||||||
for `errors.Is`/`errors.As` and retain configured-secret redaction at CLI
|
|
||||||
boundaries. Caller cancellation remains a runtime exit, not an operator
|
|
||||||
mistake, and should not trigger further diagnostic loading.
|
|
||||||
2. PromptKit wraps both definition-loading and template/content failures in
|
|
||||||
`ErrInvalidConfig`; some materialization errors have no public source/path
|
|
||||||
type. Do not classify by parsing error strings or by that sentinel alone.
|
|
||||||
On final engine-construction failure, perform an embedded-only diagnostic
|
|
||||||
construction with the same embedded prompt/schema sources and library
|
|
||||||
defaults, excluding operator inputs and application backend mappings. If
|
|
||||||
that also fails, surface an `AssetError` with the asset cause; otherwise
|
|
||||||
return the original final-construction error as an operator/configuration
|
|
||||||
error. The diagnostic engine does no provider work and is never retained
|
|
||||||
for execution. Keep a small unexported assembly seam accepting an embedded
|
|
||||||
filesystem so tests can exercise asset failures without modifying shipped
|
|
||||||
files or creating a global test hook.
|
|
||||||
3. Do not run this diagnostic construction as a successful-path preflight.
|
|
||||||
A valid final snapshot remains accepted even if an operator contribution
|
|
||||||
repairs an otherwise invalid lower-tier prompt. Verify that property with
|
|
||||||
the same small assembly test seam. This avoids strengthening upstream
|
|
||||||
final-value validation into mandatory validation of displaced definitions.
|
|
||||||
4. Cover incomplete selected profiles using omitted, empty, and null model
|
|
||||||
contributions and a cleared selected endpoint. Exercise proposal and
|
|
||||||
validation selection, including omission-derived validation selection, with
|
|
||||||
a rejecting HTTP transport. Confirm valid unselected partial profiles are
|
|
||||||
accepted; malformed unselected definitions, undefined backend references,
|
|
||||||
and conflicting directory contributions still fail under native rules.
|
|
||||||
5. Add focused boundary coverage that invalid operator YAML/template/resource
|
|
||||||
input produces configuration exit `2`, and test the asset classification
|
|
||||||
directly at the runtime seam. Reuse existing CLI handling of `AssetError`
|
|
||||||
for exit `1`; no damaged-binary subprocess fixture is needed. Cover the
|
|
||||||
shared `NewRuntime` validation route and preserve cancellation exit `1` in
|
|
||||||
configuration commands if the new error path would otherwise classify it
|
|
||||||
as `2`. Avoid exact complete diagnostic wording assertions.
|
|
||||||
|
|
||||||
**Verification and completion:** Run targeted startup/inspection tests and
|
|
||||||
`go test ./internal/cli`; check whitespace and commit. Preserve the original
|
|
||||||
failure cause where available rather than flattening it to a generic error.
|
|
||||||
|
|
||||||
**Completion marker:** Stage 4 complete. Embedded discovery and construction
|
|
||||||
failures are distinguished from operator inputs through an embedded-only
|
|
||||||
diagnostic construction; cancellation skips that diagnostic. Selected-profile
|
|
||||||
completeness and native directory validation are covered, and both config
|
|
||||||
commands preserve exit 1 for cancellation/assets and exit 2 for invalid
|
|
||||||
operator YAML, templates, and resources. Targeted runtime tests,
|
|
||||||
`go test ./internal/cli`, and `git diff --check` passed.
|
|
||||||
|
|
||||||
## Stage 5 — Protect Consumed Configuration and Message Files
|
|
||||||
|
|
||||||
**Primary files:** `internal/framework/llm/runtime.go` or a focused source-path
|
|
||||||
helper in that package, `internal/cli/process.go`, runtime source-accounting
|
|
||||||
tests, and `cmd/audita/main_integration_test.go`.
|
|
||||||
|
|
||||||
1. Rename the internal `Runtime.ProfilePaths` field to `SourcePaths` and update
|
|
||||||
its process caller. It now covers consumed host YAML and message resources,
|
|
||||||
rather than profiles alone. Populate it from the successfully captured final
|
|
||||||
engine; do not expose source metadata as a new public output.
|
|
||||||
2. Call `InspectConfiguration`. Index `Sources()` by source ID, and collect
|
|
||||||
paths with `Kind == ck.SourceKindFile`; embedded filesystem and synthetic
|
|
||||||
sources are not host paths. All loaded host YAML counts, including unused
|
|
||||||
definitions and files whose contributions were later overlaid. Deduplicate
|
|
||||||
paths and sort for a stable internal projection.
|
|
||||||
3. For message resources, enumerate `Metadata().HistoricalPaths()` and retain
|
|
||||||
effective, nonnull paths shaped as
|
|
||||||
`/prompts/<id>/<version>/messages/<index>/content_file`. Use `Lookup` and the
|
|
||||||
effective field's origin, not an inherited destination's guessed source.
|
|
||||||
PromptKit successfully captures these fields only with a unique usable
|
|
||||||
source context. Obtain the effective scalar from `Explain(path).Effective`
|
|
||||||
and decode that scalar YAML into a string with the existing YAML dependency.
|
|
||||||
This decodes captured inspection data; it is not a second configuration
|
|
||||||
loader. When the contributing source is a host file, join the untrimmed
|
|
||||||
resource name against that source file's directory and add it to the ledger.
|
|
||||||
Ignore displaced references and non-host resources. Do not reread operator
|
|
||||||
YAML, infer IDs from filenames, or reimplement resource-root validation.
|
|
||||||
4. Pass `SourcePaths` through existing `diagnostics.PrepareOutput` protection.
|
|
||||||
Leave canonicalization, symlink/hardlink checks, permitted in-place transcript
|
|
||||||
handling, and output initialization with their current owner. Do not protect
|
|
||||||
every unrelated file in the selected directory or add filesystem mutation
|
|
||||||
guarantees beyond the existing collision contract.
|
|
||||||
5. Extend `TestSubprocessProfileSources` and a focused runtime case to cover an
|
|
||||||
arbitrarily named YAML contribution, a consumed source-relative message
|
|
||||||
file, and conflicting transcript/report destinations. Use a non-YAML message
|
|
||||||
filename, such as `instruction.md`, so directory discovery does not mistake
|
|
||||||
plain prompt text for a configuration document. Include an inherited message
|
|
||||||
reference from a different directory and a prompt ID/version with a JSON
|
|
||||||
Pointer escape character so source provenance and literal keys determine
|
|
||||||
the path. Check rejection before provider dispatch and unchanged input bytes.
|
|
||||||
Preserve acceptance of an unused alternate output filename; do not turn the
|
|
||||||
whole directory into a forbidden output location.
|
|
||||||
|
|
||||||
**Verification and completion:** Run focused source-ledger tests and subprocess
|
|
||||||
collision cases, plus `git diff --check`, then commit. Check that resource
|
|
||||||
accounting uses the frozen snapshot and never changes the engine's source or
|
|
||||||
resource-resolution behavior to facilitate tracking.
|
|
||||||
|
|
||||||
**Completion marker:** Stage 5 complete. `Runtime.SourcePaths` now projects
|
|
||||||
host YAML and effective source-relative message files from PromptKit's captured
|
|
||||||
configuration inspection, using field provenance for inherited references.
|
|
||||||
Process output protection consumes that ledger. Focused source-accounting and
|
|
||||||
subprocess collision tests passed, including unchanged input bytes, no provider
|
|
||||||
dispatch, JSON Pointer-escaped keys, and acceptance of an unused alternate
|
|
||||||
output; `git diff --check` passed.
|
|
||||||
|
|
||||||
## Stage 6 — Verify Operator Prompt Overrides and Captured Execution
|
|
||||||
|
|
||||||
**Primary files:** `internal/framework/llm/adapter_test.go` and focused runtime
|
|
||||||
tests. Change `adapter.go` only for an actual integration defect.
|
|
||||||
|
|
||||||
1. Add a native keyed operator contribution overriding messages for an active
|
|
||||||
prompt/version. Use the real runtime and controlled HTTP responses to prove
|
|
||||||
the operator text reaches both the prepared snapshot and dispatched request.
|
|
||||||
One case deliberately omits shared-variable references and still succeeds;
|
|
||||||
do not require Audita to inject their text. A reference-bearing case renders
|
|
||||||
the adapter-supplied value and retains existing protection against caller
|
|
||||||
variables replacing the reserved shared variables.
|
|
||||||
2. Override the prompt's definition-level output contract with another valid
|
|
||||||
upstream contract. Prove that Audita's per-call JSON schema and configured
|
|
||||||
repair budget still govern execution: a structurally invalid response is
|
|
||||||
repaired or exhausted as the controlled budget dictates. Domain validators
|
|
||||||
remain downstream. Reuse existing validator/proposal integration coverage
|
|
||||||
for their rules rather than weakening them to fit an operator prompt.
|
|
||||||
3. Replace the reload portion of
|
|
||||||
`TestPreparedAdapterRejectsTargetDriftAndPreparationErrors`. After successful
|
|
||||||
construction, change a profile or message file and verify the existing
|
|
||||||
runtime still uses its captured settings/text. Construct a new runtime to
|
|
||||||
observe a valid edit or reject a newly mismatched endpoint. Keep the missing
|
|
||||||
prompt preparation failure case.
|
|
||||||
4. Retain a focused test of the prepared-target check independently of reload:
|
|
||||||
deliberately change the test runtime's application backend endpoint after
|
|
||||||
construction and verify the inconsistency is blocked before dispatch. This
|
|
||||||
tests the existing guard, not a supported live mutation/reload feature.
|
|
||||||
Continue executing the same prepared handle whose details were inspected;
|
|
||||||
do not add another preparation or an application-side retry loop.
|
|
||||||
|
|
||||||
**Verification and completion:** Run the prompt override, snapshot, and
|
|
||||||
prepared-adapter tests. Compare semantic messages and selected targets, not old
|
|
||||||
hash values or exact YAML formatting. Check whitespace and commit.
|
|
||||||
|
|
||||||
**Completion marker:** Stage 6 complete. Native operator messages reach both
|
|
||||||
the prepared snapshot and provider request, with and without shared-variable
|
|
||||||
references. The adapter's per-call schema and repair budget remain effective
|
|
||||||
under a valid operator output-contract override. Existing runtimes retain
|
|
||||||
captured prompt/profile values after source edits, and a new runtime observes
|
|
||||||
valid edits; the target guard and preparation failure remain covered. The
|
|
||||||
`internal/framework/llm` tests and `git diff --check` passed.
|
|
||||||
|
|
||||||
## Stage 7 — Verify Native Profiles, Backend Authority, and Resilience
|
|
||||||
|
|
||||||
**Primary files:** Runtime inspection and adapter tests,
|
|
||||||
`internal/framework/llm/adapter_capacity_test.go`, and existing framework/CLI
|
|
||||||
integration tests where their established behavior is affected.
|
|
||||||
|
|
||||||
1. Add a compact native profile inheritance fixture with literal keyed IDs
|
|
||||||
unrelated to filenames, multiple definitions in one file, a partial parent,
|
|
||||||
and a complete selected child. Verify inherited controls survive a child
|
|
||||||
model change and child overrides win. Reuse the metadata-facing literal-key
|
|
||||||
coverage from Stage 5 rather than repeating it in profile tests.
|
|
||||||
2. Use controlled HTTP request bodies to distinguish explicit zero temperature
|
|
||||||
from omission and inherited-control suppression by null. Check a
|
|
||||||
representative recursively overlaid provider parameter and null clearing;
|
|
||||||
do not promise that all providers accept every valid configured zero.
|
|
||||||
Reuse existing deadline/cancellation coverage for unchanged outer deadlines;
|
|
||||||
explicit zero profile timeout must not disable the operation context.
|
|
||||||
3. Verify an operator backend contribution can add supported provider fields,
|
|
||||||
while a conflicting endpoint/concurrency/queue/environment contribution
|
|
||||||
cannot displace the final application fields. Preserve an explicit profile
|
|
||||||
environment lookup as a selected-role error rather than hiding it with an
|
|
||||||
adapter override. Include an application backend using a catalog ID to show
|
|
||||||
it is allowed and its catalog credential lookup is suppressed. Use synthetic
|
|
||||||
environment values and local HTTP fixtures, not provider catalog endpoints.
|
|
||||||
4. Reuse `TestRolesShareBackendCapacityAndReleaseAfterCancellation` and
|
|
||||||
`TestDistinctBackendsHaveIndependentCapacity`; verify the final mapped
|
|
||||||
limits, including zero queue capacity, still coordinate both roles and
|
|
||||||
remain backend-local. Strengthen a fixture with conflicting lower-tier
|
|
||||||
capacity only where needed to prove the new mapping wins. Keep existing
|
|
||||||
synchronization; do not infer admission with sleeps or add another limiter.
|
|
||||||
5. Preserve offline selected-profile inspection and value-free application
|
|
||||||
explanations. Check configured secrets, endpoint strings, arbitrary provider
|
|
||||||
fields, and prompt text remain outside the current safe profile summary.
|
|
||||||
Do not replace it with a full upstream dump. Its numeric projection is not
|
|
||||||
proof that an omitted provider setting is sent; wire presence belongs to
|
|
||||||
the HTTP coverage above.
|
|
||||||
6. Run the existing transient retry, structural repair, attempt-count,
|
|
||||||
observation, partial-validation, and all-model-failure subprocess cases
|
|
||||||
after fixture migration. Preserve their error/recovery/debug classifications,
|
|
||||||
cancellation, and continued output. Fix proven integration regressions;
|
|
||||||
do not rewrite domain outcomes or reduce budgets to obtain a passing suite.
|
|
||||||
|
|
||||||
**Verification and completion:** Run
|
**Verification and completion:** Run
|
||||||
`go test ./internal/framework/llm ./internal/framework/proposal_generation ./internal/framework/validators ./internal/framework/runner ./internal/cli ./cmd/audita`
|
`go test ./internal/cli ./cmd/audita`, build the application to ignored
|
||||||
and `git diff --check`. Review whether each new test addresses a distinct changed
|
`bin/audita`, and run its `version` command. Run `git diff --check`, review the
|
||||||
boundary. Commit the native-profile and runtime verification unit.
|
module diff, and commit. Real release-version injection is verified in Stage 5;
|
||||||
|
no test should hardcode an upstream private linker symbol.
|
||||||
|
|
||||||
**Completion marker:** Stage 7 complete. Native profile inheritance, control
|
**Completed 2026-10-07:** Added ReleaseKit v0.1.0 and the configuration-free
|
||||||
presence, provider-map overlays, application backend authority, selected
|
`audita version` command with root/help routing and usage-error handling.
|
||||||
credential rejection, catalog-ID handling, shared backend capacity, and safe
|
Checks passed: `go test ./internal/cli ./cmd/audita`,
|
||||||
inspection behavior are covered. The six targeted packages passed, along with
|
`go build -o ./bin/audita ./cmd/audita`, `./bin/audita version`, and
|
||||||
focused wire, timeout, capacity, credential, and safe-view tests; `git diff
|
`git diff --check`.
|
||||||
--check` passed.
|
|
||||||
|
|
||||||
## Stage 8 — Document the Migration and Update Attribution
|
## Stage 2 — Introduce the Product Profile and Thin Local Wrapper [x]
|
||||||
|
|
||||||
**Primary files:** `docs/config.md`, `docs/internal/llm-runtime.md`,
|
**Primary files:** new `releasekit.json`, `scripts/release.sh`, and
|
||||||
`docs/internal/prompts.md`, `docs/integrations/openai-compatible-llm.md`,
|
`scripts/release_test.go`.
|
||||||
`docs/operations.md`, `docs/internal/cli-config.md`, `README.md`, and
|
|
||||||
`docs/oss/attribution.md`.
|
|
||||||
|
|
||||||
1. Update the configuration contract for unified `profiles_dir` content, exact
|
1. Write a JSON profile with these exact product settings:
|
||||||
keyed identities, directory composition, agreed tier precedence, profile
|
|
||||||
inheritance/presence, selected-role completeness, eager validation of all
|
|
||||||
final definitions, selected-root failures, and construction-time capture.
|
|
||||||
Replace the obsolete statement that PromptKit profiles do not gain ConfigKit
|
|
||||||
inheritance. Retain application overlays, defaults, and credential ownership.
|
|
||||||
2. Include concise migration instructions in the profile section: wrap old
|
|
||||||
profile fields beneath `profiles/<id>`, remove inner `id`, change
|
|
||||||
`base_profile` to `extends`, review explicit values/nulls, and select roots
|
|
||||||
whose final definitions have valid backend references. Show a small keyed
|
|
||||||
inheritance/override snippet and link complete maintained examples. Explain
|
|
||||||
explicit zero temperature versus omitted or null controls and link upstream
|
|
||||||
for the full field table. State that this ships as an Audita major migration;
|
|
||||||
do not invent an exact release version or automatic file conversion.
|
|
||||||
3. Document intentional prompt overrides and optional shared-variable references.
|
|
||||||
Supplying shared variables does not inject omitted text, while per-call
|
|
||||||
schemas and domain validators remain enforced. Internal prompt documentation
|
|
||||||
distinguishes shipped asset instructions from operator-effective messages;
|
|
||||||
no prompt-quality rewrite or new global prompt-policy requirement is added.
|
|
||||||
4. Describe layered runtime assembly, source accounting, snapshot use, and
|
|
||||||
startup classification in their internal owner. Update the pinned PromptKit
|
|
||||||
consumer link in the LLM integration contract. Expand operations' loaded
|
|
||||||
source description to include consumed operator message files; keep physical
|
|
||||||
output/collision rules with operations and diagnostics.
|
|
||||||
5. Repair example navigation for separate local/service roots, especially the
|
|
||||||
README's local-profile link. Update validation recipes to use the maintained
|
|
||||||
config files' explicit roots and keep the service testing overlay workflow.
|
|
||||||
Review other affected links without rewriting historical release notes.
|
|
||||||
6. Update the PromptKit and both catalog attribution entries to selected versions
|
|
||||||
and exact commit-pinned source/notice URLs. Resolve catalog tag commits from
|
|
||||||
the selected module origins or authoritative upstream refs, not annotated
|
|
||||||
tag object IDs. Check the releases' COPYRIGHT/LICENSE files and retain the
|
|
||||||
applicable unchanged local LGPL/GPL and MIT texts. Keep the existing House
|
|
||||||
Format; no new license selection, policy rewrite, or attribution inventory
|
|
||||||
is needed. Review `go.mod`/`go.sum` against those entries.
|
|
||||||
|
|
||||||
**Verification and completion:** Check affected local links/anchors, format
|
| Field | Value |
|
||||||
examples against the implemented loader, verify source revisions/notices, and
|
| --- | --- |
|
||||||
run `git diff --check`. Commit the documentation and attribution unit.
|
| `product` | `Audita` |
|
||||||
|
| `remote` | `origin` |
|
||||||
|
| `build.main_package` | `./cmd/audita` |
|
||||||
|
| `build.binary_name` | `audita` |
|
||||||
|
| `build.targets` | `linux/amd64`, `linux/arm64` |
|
||||||
|
| `delivery.base_url` | `https://gitea.maximumdirect.net` |
|
||||||
|
| `delivery.owner` | `eric` |
|
||||||
|
| `delivery.repository` | `audita` |
|
||||||
|
| `delivery.token_env` | `RELEASEKIT_GITEA_TOKEN` |
|
||||||
|
|
||||||
**Completion marker:** Stage 8 complete. Configuration, runtime, prompt,
|
Omit `checks`. Store no token value and use the upstream profile format
|
||||||
integration, operations, contributor, and README guidance now reflects native
|
directly; Audita does not need another profile parser in production.
|
||||||
PromptKit layering, migration, presence, snapshot, and separate example-root
|
2. Replace the wrapper body with straightforward Bash using `set -euo pipefail`:
|
||||||
behavior. Attribution matches PromptKit v0.12.0 and both v3 catalog module
|
select the repository directory relative to the script, create `bin/`, build
|
||||||
origins and notices. Local links/anchors and `git diff --check` passed.
|
the selected CLI, and execute it with the original argument vector. The
|
||||||
|
build command is:
|
||||||
## Stage 9 — Validate the Integrated End State and Close the Plan
|
|
||||||
|
|
||||||
**Primary files:** This plan and the feature roadmap; correct an implementation
|
|
||||||
file only if the final checks reveal an actual gap.
|
|
||||||
|
|
||||||
1. Verify the full roadmap acceptance criteria against the landed code and the
|
|
||||||
distinct tests from these stages. Review for unintended requirements:
|
|
||||||
profile-only filtering, mandatory shared text in operator messages,
|
|
||||||
filename-derived IDs, complete unselected profiles, catalog ID bans,
|
|
||||||
successful-path asset preflights, compatibility readers, and new output
|
|
||||||
guarantees are not part of the accepted target.
|
|
||||||
2. Run `go build -o ./bin/audita ./cmd/audita` and `go test ./...`. Keep the
|
|
||||||
generated binary out of commits. Reuse successful checks on unchanged
|
|
||||||
content; rerun checks affected by any corrective code changes.
|
|
||||||
3. Validate the maintained examples offline using the built binary:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./bin/audita config validate --config examples/minimal-config.yml
|
GOWORK=off GOTOOLCHAIN=local go build -mod=readonly \
|
||||||
./bin/audita config validate --config examples/configkit-inheritance.yml
|
-o ./bin/releasekit gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit
|
||||||
AUDITA_SERVICE_KEY=synthetic-example ./bin/audita config validate --config examples/production-config.yml
|
|
||||||
AUDITA_SERVICE_KEY=synthetic-example ./bin/audita config validate --config examples/production-config.yml --overlay testing
|
|
||||||
./bin/audita config print-effective --config examples/minimal-config.yml --view profiles
|
|
||||||
./bin/audita config print-effective --config examples/configkit-inheritance.yml --view explain
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Confirm local/service roots and testing selections work without provider
|
Finish with `exec ./bin/releasekit "$@"`. Preserve argument boundaries and
|
||||||
dispatch. Check safe views exclude resolved secrets and preserve the
|
build failures. Existing `bin/` ignore rules suffice. Let ReleaseKit parse
|
||||||
established explanation fields. Do not call a model to validate examples.
|
its commands and report errors; remove all local note, version, Git, test,
|
||||||
4. Check the selected dependency graph, attribution, affected links, and final
|
build-target, and publication mechanics from the wrapper.
|
||||||
diff. Run `git diff --check`. Confirm only the released modules are used and
|
3. Replace `TestReleaseWorkflow`'s old fourteen-scenario matrix. Use a temporary
|
||||||
no obsolete `/v2` catalog or removed construction API remains in active code.
|
layout with the real wrapper and a stub at the Go/tool boundary to verify
|
||||||
Historical release documents and historical roadmap links may retain old
|
repository selection, forwarded native arguments (including an argument with
|
||||||
versions as accurate records.
|
spaces), and returned tool status. Include uncertain publication status three
|
||||||
5. Mark this plan and the feature roadmap complete only after the full target
|
and a failed bootstrap that never executes the tool. The stub writes a small
|
||||||
passes. Record the tested implementation revision and checks accurately;
|
executable at the requested output path; assert effects and arguments without
|
||||||
distinguish a subsequent documentation-only completion commit. Commit the
|
tying tests to harmless build-flag ordering or a particular shell layout.
|
||||||
completion documentation and report remaining work, if any. Do not select
|
4. Add a focused check of the committed JSON profile's product/build/delivery
|
||||||
a release number, publish, tag, push, poll CI, or add permanent release gates.
|
facts and absence of additional checks or credential values. Standard JSON
|
||||||
|
decoding in test code is sufficient. Keep this coverage alongside wrapper
|
||||||
|
tests; compare target membership rather than incidental list order. Avoid
|
||||||
|
reproducing upstream profile validation or remote guard tests.
|
||||||
|
5. Keep `scripts/build-release.sh` until Stage 3 removes its CI caller. The new
|
||||||
|
wrapper already uses ReleaseKit's selected build specification.
|
||||||
|
|
||||||
**Completion marker:** Stage 9 complete. The tested implementation revision is
|
**Verification and completion:** Run `go test ./scripts`, then
|
||||||
`5525fe8`; the preceding documentation migration commit is `9bcf183`, and this
|
`bash scripts/release.sh version` to verify real tool bootstrap and execution.
|
||||||
roadmap closure is documentation-only. At `9bcf183`, the application built,
|
This prints the tool's version, not Audita's. Check ignored build output and
|
||||||
`go test ./...` passed, all six maintained example commands passed offline,
|
`git diff --check`, then commit. No publication or delivery invocation against
|
||||||
`go mod verify` passed, the selected module graph contained PromptKit v0.12.0
|
an external service is needed.
|
||||||
and both `/v3` catalogs at `v3.20260930.0` with no `/v2` catalogs, and
|
|
||||||
`git diff --check` passed. The active Go/module scan found no removed PromptKit
|
|
||||||
construction options.
|
|
||||||
|
|
||||||
**End state:** Native PromptKit configuration and catalogs are adopted,
|
**Completed 2026-10-07:** Added the Audita profile and replaced the local
|
||||||
operator overrides and captured resources work through the real runtime,
|
publication workflow with a wrapper that builds and execs ReleaseKit. Replaced
|
||||||
application backend/domain ownership and resilience remain effective, and
|
the publication test matrix with focused wrapper and profile checks. Checks
|
||||||
maintained examples and migration documentation match the implementation.
|
passed: `go test ./scripts`, `bash scripts/release.sh version`, ignored-output
|
||||||
No implementation-blocking decisions remain; exact major-release numbering
|
verification for `bin/releasekit`, and `git diff --check`.
|
||||||
and publication timing remain outside this implementation unit.
|
|
||||||
|
## Stage 3 — Migrate Woodpecker Delivery and Retire the Builder [x]
|
||||||
|
|
||||||
|
**Primary files:** `.woodpecker/release.yml` and deletion of
|
||||||
|
`scripts/build-release.sh`.
|
||||||
|
|
||||||
|
1. Adapt the maintained upstream
|
||||||
|
[tag-delivery template](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.1.0/examples/woodpecker/tag-delivery.yml).
|
||||||
|
Use its matrix and single `deliver` step with `GO_IMAGE: golang:1.25.5` and
|
||||||
|
`RELEASEKIT_VERSION: v0.1.0`. Select tag events under `refs/tags/v*`.
|
||||||
|
2. Map the command environment variable `RELEASEKIT_GITEA_TOKEN` from the
|
||||||
|
existing Woodpecker secret `GITEA_RELEASE_TOKEN`. Use the existing credential;
|
||||||
|
no new secret name or credential provisioning is selected by this migration.
|
||||||
|
3. Use the template's command, preserving Woodpecker interpolation escaping:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go run gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@${RELEASEKIT_VERSION} ci-deliver \
|
||||||
|
--repo "$${CI_WORKSPACE}" \
|
||||||
|
--commit "$${CI_COMMIT_SHA}" \
|
||||||
|
"$${CI_COMMIT_TAG}"
|
||||||
|
```
|
||||||
|
|
||||||
|
ReleaseKit owns fetch/checkout verification, dependency preparation, builds,
|
||||||
|
note-page creation, manifests, and uploads. Remove the separate build step,
|
||||||
|
release plugin, wildcard asset settings, and local checksum configuration.
|
||||||
|
Managed delivery gets its targets and destination from `releasekit.json`.
|
||||||
|
4. Delete `scripts/build-release.sh` after confirming its active caller is gone.
|
||||||
|
Search remaining script/CI references and remove only obsolete active
|
||||||
|
invocations. Historical release records retain their original descriptions.
|
||||||
|
5. Review the parsed workflow against the template: selected image/pin, tag
|
||||||
|
trigger, token mapping, and event arguments. Use the existing YAML dependency
|
||||||
|
or available tooling for local syntax inspection. Confirm the tool pin matches
|
||||||
|
the selected module. This is adoption validation; it does not require a new
|
||||||
|
permanent workflow-text snapshot test or a running Woodpecker instance.
|
||||||
|
|
||||||
|
**Verification and completion:** Run `go test ./scripts`, review the workflow
|
||||||
|
and profile together, and run `git diff --check`. Commit the delivery migration.
|
||||||
|
The existing Go image must provide compatible Go and Git; real runner execution
|
||||||
|
is observed during subsequent delivery, independently of source completion.
|
||||||
|
|
||||||
|
**Completed 2026-10-07:** Migrated Woodpecker to the pinned managed
|
||||||
|
`ci-deliver` step using the existing secret, then removed the obsolete binary
|
||||||
|
builder. Verified the parsed workflow settings and command against the pinned
|
||||||
|
template, confirmed the module/workflow pins match, ran `go test ./scripts`,
|
||||||
|
confirmed no active CI or script caller remained, and passed `git diff --check`.
|
||||||
|
|
||||||
|
## Stage 4 — Align Policy, Documentation, and Attribution [x]
|
||||||
|
|
||||||
|
**Primary files:** `docs/release.md`, `docs/policy/release.md`, `docs/cli.md`,
|
||||||
|
`docs/internal/cli-config.md`, `docs/adr/README.md`, and
|
||||||
|
`docs/oss/attribution.md`. Update other current references only where affected.
|
||||||
|
|
||||||
|
1. Rewrite the runbook around dependency preparation, a committed candidate,
|
||||||
|
`git push origin main`, and `bash scripts/release.sh publish VERSION`.
|
||||||
|
Explain that remote `main` must identify the captured candidate exactly.
|
||||||
|
`bash scripts/release.sh check VERSION` is an optional local diagnostic,
|
||||||
|
never a required pre-publication step. Document preparation with
|
||||||
|
`GOWORK=off GOTOOLCHAIN=local go mod download`; if it changes module files,
|
||||||
|
those changes belong in the candidate before publication.
|
||||||
|
2. Describe annotated notes-derived tags, native publication statuses, and
|
||||||
|
recovery against the captured authoritative endpoint. State that the tool
|
||||||
|
retains a local tag after push failure. An exact-object recovery can confirm
|
||||||
|
success; an unreadable result remains unknown. Link to pinned upstream
|
||||||
|
recovery guidance instead of implementing or repeating its full algorithm.
|
||||||
|
Remove the old atomic-push prerequisite and procedure.
|
||||||
|
3. Describe the shared tagged-source delivery command, the existing secret
|
||||||
|
mapping, Linux artifacts, note body/footer, and separate delivery status.
|
||||||
|
Explain content-aware retries and retained-artifact recovery by linking to
|
||||||
|
upstream. Remove references to the retired builder/plugin and the assertion
|
||||||
|
that Audita has no version command. Keep the release script's bootstrap
|
||||||
|
preparation distinct from its offline candidate validation.
|
||||||
|
4. In the release policy, retain upstream wording wherever it already permits
|
||||||
|
the selected approach. Add a concise project-specific native-host check
|
||||||
|
provision under supported platforms/candidate validation: Linux delivery
|
||||||
|
targets remain required, macOS development remains best effort, and running
|
||||||
|
publication on a nonselected host requires ReleaseKit's temporary native
|
||||||
|
version check to succeed. Qualify the existing statement that macOS failures
|
||||||
|
never block release accordingly. Mac access and native tests on every target
|
||||||
|
remain unnecessary. Date the local policy update and preserve its upstream
|
||||||
|
source attribution. Avoid unrelated policy changes or additional gates.
|
||||||
|
5. Document `audita version`, its stdout/help/usage behavior, and public version
|
||||||
|
fallback in `docs/cli.md`. Add a short implementation/change-recipe note in
|
||||||
|
`docs/internal/cli-config.md` linking to that contract and the accepted ADR.
|
||||||
|
Update the ADR index to list ADR 0001; its current assertion that no ADR exists
|
||||||
|
is obsolete. Preserve the accepted ADR's content.
|
||||||
|
6. Add a House Format ReleaseKit entry with selected module v0.1.0,
|
||||||
|
`Copyright (C) 2026 Eric Rakestraw`, and `LGPL-3.0-or-later`. Link existing
|
||||||
|
`licenses/LGPL-3.0.txt` and `licenses/GPL-3.0.txt`, and the upstream COPYRIGHT
|
||||||
|
and source at commit `5e60fe3113c69f95c42fbbe32a8ca4b51f5b83c1`. Check the
|
||||||
|
selected module's notices. Keep the application grant and unchanged license
|
||||||
|
texts intact; no policy-template material is copied in this feature.
|
||||||
|
7. Repair current navigation affected by replacing this implementation plan.
|
||||||
|
Completed PromptKit/ConfigKit roadmap references that intend to identify
|
||||||
|
their former plan should link to the corresponding historical completion
|
||||||
|
records, not this ReleaseKit plan. Preserve published release notes and tags.
|
||||||
|
The next release note is deferred to release preparation.
|
||||||
|
|
||||||
|
**Verification and completion:** Check commands against the new wrapper/CLI,
|
||||||
|
local links and anchors, pinned upstream references, source attribution, and
|
||||||
|
`git diff --check`. Use the prior successful code checks for unchanged code.
|
||||||
|
Commit the documentation unit.
|
||||||
|
|
||||||
|
**Completed 2026-10-07:** Updated the ReleaseKit runbook, release policy,
|
||||||
|
version contract and contributor guidance, ADR index, and component attribution.
|
||||||
|
Repaired PromptKit and ConfigKit links to their historical implementation
|
||||||
|
records. Verified local links and anchors, release CLI and version commands,
|
||||||
|
pinned module/notices, source attribution, and `git diff --check`.
|
||||||
|
|
||||||
|
## Stage 5 — Verify Adoption and Close the Plan [x]
|
||||||
|
|
||||||
|
**Primary files:** This plan and the ReleaseKit feature roadmap; fix an
|
||||||
|
implementation file only if the final checks expose a real integration defect.
|
||||||
|
|
||||||
|
1. Audit the roadmap acceptance criteria against the landed files. Confirm the
|
||||||
|
runtime uses the public version accessor, the profile selects only the two
|
||||||
|
Linux targets, pins agree, the wrapper owns no common release mechanics, and
|
||||||
|
the old builder and repeated publication tests have been retired.
|
||||||
|
2. Prepare dependencies if necessary, then run `go test ./...` and
|
||||||
|
`go build -o ./bin/audita ./cmd/audita`. Run the normal binary's `version`
|
||||||
|
command without application setup. These tests remain offline; preparation
|
||||||
|
may use the module network separately.
|
||||||
|
3. On the committed implementation, verify the real shared builder once using
|
||||||
|
temporary output outside the repository:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
releasekit_check_dir=$(mktemp -d)
|
||||||
|
bash scripts/release.sh build --output "$releasekit_check_dir" v0.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
`v0.0.0` is solely a build-test identifier, not an Audita release selection.
|
||||||
|
`build` requires no release note or remote publication. Confirm both
|
||||||
|
`audita-v0.0.0-linux-amd64` and `audita-v0.0.0-linux-arm64` are produced;
|
||||||
|
ReleaseKit's own mandatory native smoke check must report success. It checks
|
||||||
|
`audita v0.0.0` with a newline on a host-runnable executable, building a
|
||||||
|
temporary native executable itself when needed. Do not execute a foreign
|
||||||
|
architecture binary. Remove the temporary output afterward. The CLI's private
|
||||||
|
build receipt is an upstream artifact and should not enter Git.
|
||||||
|
4. Review the dependency/attribution diff, wrapper/profile wiring evidence,
|
||||||
|
workflow syntax and pin, and current documentation. Run `git diff --check`
|
||||||
|
and confirm no generated output, credentials, or unrelated work is staged.
|
||||||
|
Fix proven defects and rerun only affected checks. No real tag publication,
|
||||||
|
hosted upload, paid inference, extra platform matrix, or CI polling is needed
|
||||||
|
to establish this implementation's completion.
|
||||||
|
5. Mark the plan and feature roadmap complete after these requirements pass.
|
||||||
|
Record the tested implementation revision and checks; distinguish a later
|
||||||
|
documentation-only closure commit. Summarize the local infrastructure removed
|
||||||
|
and any remaining deployment observations accurately. Commit the closure.
|
||||||
|
The first adopting release and live delivery remain separate authorized work.
|
||||||
|
|
||||||
|
**End state:** Audita supplies a product profile, native version command, thin
|
||||||
|
local entry point, and shared CI invocation. ReleaseKit owns the common release
|
||||||
|
mechanics and their tests. Documentation and attribution match the integration.
|
||||||
|
No implementation-blocking open questions remain.
|
||||||
|
|
||||||
|
**Completed 2026-10-07:** Audited the adoption criteria against implementation
|
||||||
|
revision `5ac704c4c42fd23ed94b1f7351491ad580726db1`. Checks passed:
|
||||||
|
`go test ./...`; `go build -o ./bin/audita ./cmd/audita` followed by the binary's
|
||||||
|
`version` and `--help` commands; ReleaseKit's real `build` command produced
|
||||||
|
`audita-v0.0.0-linux-amd64` and `audita-v0.0.0-linux-arm64` and passed its
|
||||||
|
native version smoke check; profile, module/tool pins, wrapper, and parsed
|
||||||
|
Woodpecker workflow matched the selected integration; and `git diff --check`.
|
||||||
|
The closure record is a documentation-only commit after the tested
|
||||||
|
implementation revision. Audita's local build and publication mechanics and
|
||||||
|
their repeated-mechanics test matrix have been removed in favor of ReleaseKit.
|
||||||
|
The first adopting release and live Woodpecker/Gitea delivery observation
|
||||||
|
remain separate work.
|
||||||
|
|||||||
@@ -262,8 +262,8 @@ No live provider calls, new release gates, or CI-success dependency are needed
|
|||||||
to establish adoption. Guided domain recovery and the other work recorded in
|
to establish adoption. Guided domain recovery and the other work recorded in
|
||||||
[future.md](future.md) remain separate features. Do not expand this upgrade into
|
[future.md](future.md) remain separate features. Do not expand this upgrade into
|
||||||
new report formats, pipeline scheduling, or general configuration-inspection
|
new report formats, pipeline scheduling, or general configuration-inspection
|
||||||
commands. The [implementation plan](implementation.md) owns execution steps
|
commands. The [historical implementation plan](https://gitea.maximumdirect.net/eric/audita/src/commit/418da456d75a68d3bb5a43ab823c5b2b434b90cc/docs/roadmap/implementation.md)
|
||||||
and their verification.
|
records that work's execution steps and verification.
|
||||||
|
|
||||||
## Completion Record
|
## Completion Record
|
||||||
|
|
||||||
|
|||||||
216
docs/roadmap/releasekit.md
Normal file
216
docs/roadmap/releasekit.md
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
# ReleaseKit Adoption Roadmap
|
||||||
|
|
||||||
|
**Last updated:** 2026-10-07
|
||||||
|
**Status:** Implementation complete; first adopting release and live delivery
|
||||||
|
observation pending.
|
||||||
|
|
||||||
|
## Purpose and Original Baseline
|
||||||
|
|
||||||
|
Implement the decision in [ADR 0001](../adr/0001-releasekit.md): standardize
|
||||||
|
Audita's release process on ReleaseKit to improve maintainability and reduce
|
||||||
|
locally owned release-focused code. Those benefits outweigh the small advantages
|
||||||
|
of retaining Audita's custom procedure. The workflow changes required by the
|
||||||
|
shared standard are accepted.
|
||||||
|
|
||||||
|
At Audita revision `6de470b53d185b10327ec4deba23f5a84e606e1f`, v3.0.0 is
|
||||||
|
released. Audita owns `scripts/release.sh`, `scripts/build-release.sh`, and
|
||||||
|
`scripts/release_test.go`, totaling 207 lines, plus the 26-line Woodpecker delivery
|
||||||
|
workflow. Publication validates a clean candidate, cross-builds Linux binaries,
|
||||||
|
and atomically pushes `main` and a lightweight tag. Woodpecker builds artifacts
|
||||||
|
and uses a release plugin to publish the note, binaries, and checksums. Audita
|
||||||
|
has no runtime version command.
|
||||||
|
|
||||||
|
The adoption baseline is ReleaseKit v0.1.0, commit
|
||||||
|
`5e60fe3113c69f95c42fbbe32a8ca4b51f5b83c1`. Its public library, executable,
|
||||||
|
`buildinfo` package, and maintained Woodpecker integration cover the required
|
||||||
|
mechanics. The upstream test suite passes; its documentation records live
|
||||||
|
Woodpecker/Gitea verification as remaining deployment work.
|
||||||
|
|
||||||
|
## Current Baseline
|
||||||
|
|
||||||
|
The adoption implementation is now present: `go.mod` selects ReleaseKit
|
||||||
|
v0.1.0, `releasekit.json` supplies Audita's product and delivery facts,
|
||||||
|
`scripts/release.sh` builds and forwards arguments to its executable, and
|
||||||
|
`.woodpecker/release.yml` invokes the same pinned version for tag delivery.
|
||||||
|
The application exposes `audita version` through `buildinfo`; the custom
|
||||||
|
release builder has been removed. The sections below retain the intended
|
||||||
|
adoption scope rather than describing work that still needs implementation.
|
||||||
|
|
||||||
|
The [adoption review](remediation.md) records focused CLI and wiring-test
|
||||||
|
findings and their resolution status. Remaining implementation corrections
|
||||||
|
belong there. First-release delivery observation remains deployment work,
|
||||||
|
separate from source-release completion.
|
||||||
|
|
||||||
|
## Target End State
|
||||||
|
|
||||||
|
- ReleaseKit owns candidate validation, source publication, version injection,
|
||||||
|
selected-target builds, checksum generation, and hosted delivery. Audita owns
|
||||||
|
its product facts, compatibility decisions, release notes, and necessary
|
||||||
|
product-specific checks.
|
||||||
|
- A small `releasekit.json` declares product `Audita`, remote `origin`, main
|
||||||
|
package `./cmd/audita`, binary name `audita`, and delivery targets
|
||||||
|
`linux/amd64` and `linux/arm64`. Its Gitea destination identifies
|
||||||
|
`https://gitea.maximumdirect.net`, owner `eric`, and repository `audita`.
|
||||||
|
- Maintainers commit the candidate and publish it to authoritative `main`
|
||||||
|
before invoking ReleaseKit publication. ReleaseKit validates that candidate,
|
||||||
|
creates an annotated tag from the committed note, and pushes its exact tag
|
||||||
|
object. Confirmed source publication completes the release independently of
|
||||||
|
CI and artifact delivery.
|
||||||
|
- One captured candidate receives ReleaseKit's standard offline test suite and
|
||||||
|
selected build verification. Audita's profile introduces no additional checks
|
||||||
|
without a concrete product need. The optional local check remains a diagnostic
|
||||||
|
operation rather than a prerequisite that duplicates publication validation.
|
||||||
|
- `audita version` reports `audita <version>` through ReleaseKit's public
|
||||||
|
`buildinfo.Version()` accessor. Ordinary development builds use its native
|
||||||
|
fallback behavior, including `dev` when release information is unavailable.
|
||||||
|
- Woodpecker invokes the pinned shared `ci-deliver` command. Hosted notes,
|
||||||
|
Linux assets, and `SHA256SUMS` retain their current identities; delivery
|
||||||
|
compares existing content, adds missing assets, and reports conflicts and
|
||||||
|
uncertainty separately from source publication.
|
||||||
|
- Audita's release code consists of thin tool invocation and product wiring,
|
||||||
|
with focused integration coverage. Common mechanics and their repeated tests
|
||||||
|
reside upstream.
|
||||||
|
|
||||||
|
## Accepted Standardization Choices
|
||||||
|
|
||||||
|
Adopt ReleaseKit's main-first publication and annotated tags, replacing the
|
||||||
|
atomic branch/tag push and lightweight tags for new releases. Existing released
|
||||||
|
tags, notes, pages, and assets retain their historical contents.
|
||||||
|
|
||||||
|
Adopt its standard build settings and mandatory native version smoke check.
|
||||||
|
The check verifies that version injection reaches the executable. On a host
|
||||||
|
outside the selected Linux targets, ReleaseKit can build a temporary native
|
||||||
|
binary; publication from that host depends on that check succeeding. This is an
|
||||||
|
accepted workflow change, including for macOS invocations. Linux remains the
|
||||||
|
production delivery target, and macOS remains a best-effort development platform.
|
||||||
|
Mac access and macOS artifact delivery are not release prerequisites.
|
||||||
|
|
||||||
|
Use the shared managed build-and-deliver operation for ordinary CI delivery.
|
||||||
|
ReleaseKit's retained-artifact workflow remains available when a delivery retry
|
||||||
|
needs the original bytes. Follow its content-aware recovery behavior rather
|
||||||
|
than reproducing that machinery in Audita.
|
||||||
|
|
||||||
|
## Adoption Scope
|
||||||
|
|
||||||
|
### Dependency, Profile, and Tool Entry Point
|
||||||
|
|
||||||
|
Select the released v0.1.0 module in `go.mod` and `go.sum`. The application uses
|
||||||
|
the lightweight `buildinfo` facade; release orchestration is exercised through
|
||||||
|
the upstream executable. The sibling checkout is review material, not a
|
||||||
|
production dependency replacement.
|
||||||
|
|
||||||
|
Add the product profile described above. Leave additional checks empty and keep
|
||||||
|
delivery tokens out of the profile. Use `RELEASEKIT_GITEA_TOKEN` as the token
|
||||||
|
environment variable, supplied from the existing Woodpecker
|
||||||
|
`GITEA_RELEASE_TOKEN` secret.
|
||||||
|
|
||||||
|
Reduce `scripts/release.sh` to repository selection, building the CLI selected
|
||||||
|
by Audita's module graph into ignored `bin/` output, and invoking it directly.
|
||||||
|
Forward ReleaseKit's native commands and arguments and preserve its exit status,
|
||||||
|
including uncertain publication status. Use the same released tool version in
|
||||||
|
the module graph and CI pin. Dependency/tool preparation belongs before offline
|
||||||
|
candidate validation.
|
||||||
|
|
||||||
|
### Runtime Version Reporting
|
||||||
|
|
||||||
|
Add `version` to the existing CLI dispatch and help, with output
|
||||||
|
`audita <version>` followed by a newline and exit status zero. This operation
|
||||||
|
works without loading application configuration, resolving LLM credentials, or
|
||||||
|
contacting a provider. Use the public accessor and let ReleaseKit own injection
|
||||||
|
mechanics. Keep the release orchestration dependency outside transcript
|
||||||
|
processing.
|
||||||
|
|
||||||
|
### Publication and Binary Delivery
|
||||||
|
|
||||||
|
Replace the local publication and cross-build mechanics with ReleaseKit's
|
||||||
|
commands and profile. Retire `scripts/build-release.sh` when its callers use
|
||||||
|
the shared builder.
|
||||||
|
|
||||||
|
Replace the current Woodpecker build/plugin sequence with the maintained
|
||||||
|
tag-delivery integration, pinned to v0.1.0. Retain a compatible Go 1.25.5 image
|
||||||
|
with Git, the existing secret, and only the selected Linux targets. Pass the
|
||||||
|
workspace, event commit, and version to `ci-deliver`; the shared command owns
|
||||||
|
tagged-checkout verification, dependency preparation, build, and delivery.
|
||||||
|
|
||||||
|
The declared artifacts remain `audita-<version>-linux-amd64`,
|
||||||
|
`audita-<version>-linux-arm64`, and `SHA256SUMS`. The committed release note
|
||||||
|
supplies the hosted body and required licensing footer. Delivery status and
|
||||||
|
recovery follow the upstream operation; source publication does not poll CI or
|
||||||
|
wait for assets.
|
||||||
|
|
||||||
|
### Tests and Removal of Duplicated Mechanics
|
||||||
|
|
||||||
|
Replace the repeated release-mechanics matrix in `scripts/release_test.go`
|
||||||
|
with proportionate coverage of Audita's wiring. Protect the new version command
|
||||||
|
and wrapper/profile integration where these introduce distinct consumer risks.
|
||||||
|
Verify actual version injection and the selected Linux builds through the real
|
||||||
|
builder during adoption validation. Use local Git/HTTP fixtures and controlled
|
||||||
|
command boundaries where needed; keep the default suite offline.
|
||||||
|
|
||||||
|
Reuse ReleaseKit's coverage for common Git guards, push reconciliation,
|
||||||
|
checksums, and delivery retries. Evaluate the reduction in local machinery from
|
||||||
|
the resulting implementation rather than imposing a numerical deletion target.
|
||||||
|
|
||||||
|
### Documentation and Attribution
|
||||||
|
|
||||||
|
Update `docs/release.md` for main-first preparation, native ReleaseKit commands,
|
||||||
|
annotated tags, dependency preparation, delivery status, and failure recovery.
|
||||||
|
Align `docs/policy/release.md` where necessary to describe the deliberately
|
||||||
|
accepted native-host check while retaining the existing production targets and
|
||||||
|
independent source-completion rule.
|
||||||
|
|
||||||
|
Update `docs/cli.md`, relevant internal CLI guidance, and the ADR index for the
|
||||||
|
version command and accepted decision. Keep the ADR's decision content intact.
|
||||||
|
Record the adoption and maintainer workflow change in the next release note
|
||||||
|
when that release is prepared.
|
||||||
|
|
||||||
|
Add ReleaseKit's selected source and LGPL-3.0-or-later notices to
|
||||||
|
`docs/oss/attribution.md` under the existing OSS policy. Reuse applicable license
|
||||||
|
texts already present. Incorporating `buildinfo` does not change Audita's
|
||||||
|
application license.
|
||||||
|
|
||||||
|
## Verification and Completion
|
||||||
|
|
||||||
|
Adoption is complete when:
|
||||||
|
|
||||||
|
- The released dependency and pinned tool are selected consistently, and the
|
||||||
|
application builds with its default offline test suite passing.
|
||||||
|
- Version output works without LLM setup and reports the injected version from
|
||||||
|
a ReleaseKit-built executable.
|
||||||
|
- The native profile and thin wrapper drive candidate checks and publication
|
||||||
|
without Audita retaining common publication or build mechanics.
|
||||||
|
- Both required Linux targets build through ReleaseKit, and the CI workflow
|
||||||
|
declares the intended destination, artifacts, note body, and secret mapping.
|
||||||
|
- Focused consumer coverage exercises the changed boundaries, and redundant
|
||||||
|
mechanics tests and the old builder have been retired.
|
||||||
|
- Current documentation and attribution describe the adopted behavior.
|
||||||
|
|
||||||
|
## Remaining Release and Deployment Work
|
||||||
|
|
||||||
|
Prepare the first adopting release under the [release procedure](../release.md),
|
||||||
|
including a release note explaining the maintainer workflow change. Resolve
|
||||||
|
outstanding adoption-review findings through the linked remediation record.
|
||||||
|
Version selection and publication timing remain release-preparation decisions.
|
||||||
|
|
||||||
|
Offline integration evidence and live delivery observations are distinct.
|
||||||
|
Observe the shared workflow during the first adopting release and address any
|
||||||
|
deployment failures through ReleaseKit's delivery recovery. Check that the
|
||||||
|
tagged commit, hosted note with its licensing footer, selected Linux assets,
|
||||||
|
and checksums reach the intended destination. This observation is asynchronous;
|
||||||
|
CI success or asset availability does not become a source-release completion
|
||||||
|
gate or require the publication command to wait.
|
||||||
|
|
||||||
|
The accepted ADR settles the architectural choice and standard workflow
|
||||||
|
trade-offs. No significant feature decisions remain open. The next Audita
|
||||||
|
version and publication timing belong to release preparation. The
|
||||||
|
[implementation plan](implementation.md) owns the ordered execution steps and
|
||||||
|
their verification; this roadmap owns the desired end state and adoption intent.
|
||||||
|
|
||||||
|
Implementation completed 2026-10-07 at revision
|
||||||
|
`5ac704c4c42fd23ed94b1f7351491ad580726db1`. The full Go suite and executable
|
||||||
|
version path passed, and ReleaseKit built both selected Linux artifacts with
|
||||||
|
its native version smoke check passing. The profile, dependency and CI pins,
|
||||||
|
wrapper, and parsed workflow were verified together. Audita's local release
|
||||||
|
builder and publication mechanics are retired. This completion record is a
|
||||||
|
later documentation-only commit; live delivery remains to be observed during
|
||||||
|
the first adopting release and is separate from source implementation.
|
||||||
151
docs/roadmap/remediation.md
Normal file
151
docs/roadmap/remediation.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# ReleaseKit Adoption Review: Findings and Recommended Changes
|
||||||
|
|
||||||
|
**Reviewed:** 2026-10-07, source commit
|
||||||
|
`3c405f0524ec6c098d00d4e371abbace7648d446`.
|
||||||
|
**Scope:** ReleaseKit adoption commits after `721365e`, including CLI version
|
||||||
|
reporting, local release wiring, CI delivery, tests, and documentation.
|
||||||
|
**Status:** R1–R3 addressed. The findings below describe the reviewed
|
||||||
|
baseline; completed work is recorded under Resolution Record.
|
||||||
|
|
||||||
|
## Architecture Assessment
|
||||||
|
|
||||||
|
The core adoption matches [ADR 0001](../adr/0001-releasekit.md) and the
|
||||||
|
[feature roadmap](releasekit.md). Audita supplies product facts in
|
||||||
|
`releasekit.json`, a thin executable wrapper, a configuration-free version
|
||||||
|
command using the public `buildinfo` accessor, and a pinned Woodpecker
|
||||||
|
`ci-deliver` invocation. ReleaseKit owns candidate validation, annotated-tag
|
||||||
|
publication, version injection, Linux builds, checksums, and hosted delivery.
|
||||||
|
The custom builder and local publication mechanics have been retired.
|
||||||
|
|
||||||
|
No core ownership or delivery-target gap was found. The findings concern one
|
||||||
|
CLI behavior defect, an ineffective target-selection assertion, and unnecessary
|
||||||
|
test restrictions. Main-first publication and the native-host version smoke
|
||||||
|
check are deliberately accepted workflow changes, not accidental strengthening.
|
||||||
|
|
||||||
|
## R1 — P2: Simplify Version Help Handling and Correct Its Streams
|
||||||
|
|
||||||
|
**Category:** CLI behavior defect and unnecessary argument restriction.
|
||||||
|
|
||||||
|
**Evidence:** [runVersion](../../internal/cli/run.go) handles standalone `-h`
|
||||||
|
and `--help` before parsing, then rejects either spelling when combined with
|
||||||
|
other arguments. This adds a special prohibition beyond the requested usual
|
||||||
|
help behavior. For example, `audita version -h --help` returns status `2`.
|
||||||
|
|
||||||
|
The parser's help path also writes usage twice: `FlagSet.Usage` writes to stderr,
|
||||||
|
and the `flag.ErrHelp` branch writes it again to stdout. Reproduced with
|
||||||
|
`audita version --h` and `audita version -help`: both return status `0` and emit
|
||||||
|
the same usage on both streams. The standard library recognizes these help
|
||||||
|
spellings, but the preliminary special cases do not handle them.
|
||||||
|
|
||||||
|
**Recommended change:** Use one standard parsing path. Let help short-circuit
|
||||||
|
according to the parser's ordinary behavior, and emit successful help once to
|
||||||
|
stdout with empty stderr. Keep non-help unknown flags and unexpected positional
|
||||||
|
arguments as usage errors. Remove the bespoke mixed-help prohibition and the
|
||||||
|
test that promotes it into a requirement. Update the CLI wording if needed so
|
||||||
|
it describes ordinary help handling without promising validation of trailing
|
||||||
|
arguments after a help request.
|
||||||
|
|
||||||
|
**Acceptance:** Normal version output remains configuration-free and unchanged.
|
||||||
|
Representative parser help paths produce status `0`, stdout usage, and empty
|
||||||
|
stderr. Non-help invalid invocations still return status `2`. Tests protect
|
||||||
|
streams and meaningful behavior without establishing a new argument-combination
|
||||||
|
policy or a comprehensive help-alias contract.
|
||||||
|
|
||||||
|
## R2 — P2: Make the Delivery-Target Check Compare Actual Membership
|
||||||
|
|
||||||
|
**Category:** Test correctness gap.
|
||||||
|
|
||||||
|
**Evidence:** [sameMembers](../../scripts/release_test.go) compares slice
|
||||||
|
lengths and checks only that each observed item belongs to the expected slice.
|
||||||
|
It never checks that every required target is present. Calling the actual helper
|
||||||
|
with `got = [linux/amd64, linux/amd64]` and
|
||||||
|
`want = [linux/amd64, linux/arm64]` returns true. A profile missing arm64 can
|
||||||
|
therefore pass the selected-target assertion. ReleaseKit deduplicates repeated
|
||||||
|
targets, so that configuration would build only amd64.
|
||||||
|
|
||||||
|
The reverse problem also occurs: a list containing both required targets plus
|
||||||
|
a repeated amd64 entry returns false despite having the same effective target
|
||||||
|
membership. The current committed profile is correct; the defect is in its
|
||||||
|
regression protection.
|
||||||
|
|
||||||
|
**Recommended change:** Compare the sets of configured and required targets,
|
||||||
|
including coverage of every required member and rejection of an unintended
|
||||||
|
member. Reuse straightforward standard-library data structures. Duplicate
|
||||||
|
normalization already belongs to ReleaseKit; Audita needs no duplicate ban or
|
||||||
|
order requirement.
|
||||||
|
|
||||||
|
**Acceptance:** A repeated single target cannot conceal an omitted production
|
||||||
|
target. Either omitted required target and an additional unintended target fail
|
||||||
|
the profile check. Reordered or repeated entries with the same effective target
|
||||||
|
set remain equivalent. Focused coverage catches the demonstrated false positive.
|
||||||
|
|
||||||
|
## R3 — P3: Remove Incidental Wiring-Test Requirements
|
||||||
|
|
||||||
|
**Category:** Tests strengthen representation choices into contracts.
|
||||||
|
|
||||||
|
**Evidence:** [release wrapper/profile tests](../../scripts/release_test.go)
|
||||||
|
require the exact output argument spelling `-o ./bin/releasekit` and reject the
|
||||||
|
presence of a `checks` key regardless of its value. In disposable copies of the
|
||||||
|
tests, changing the output path to the equivalent `bin/releasekit` fails the
|
||||||
|
wrapper assertion even though execution still reaches the intended tool.
|
||||||
|
Adding `"checks": []` fails the profile assertion despite configuring no
|
||||||
|
additional commands.
|
||||||
|
|
||||||
|
The accepted requirements concern the executable destination, native argument
|
||||||
|
and status forwarding, and avoiding redundant additional checks. They do not
|
||||||
|
require one path spelling or one serialization of an empty list. The plan's
|
||||||
|
instruction to omit `checks` selects an initial file representation rather than
|
||||||
|
an enduring prohibition on equivalent configuration.
|
||||||
|
|
||||||
|
**Recommended change:** Check the resolved output destination and observed
|
||||||
|
wrapper behavior instead of the literal relative-path spelling. Check that the
|
||||||
|
effective additional-check list is empty, accepting omission or an empty list.
|
||||||
|
Keep the existing product, target, destination, argument-boundary, failure, and
|
||||||
|
exit-status protection. Simplify fixture helpers where that reduces maintenance
|
||||||
|
without adding another parser or test framework.
|
||||||
|
|
||||||
|
**Acceptance:** The two demonstrated equivalent edits preserve passing wiring
|
||||||
|
tests, while a wrong executable destination, changed product facts, nonempty
|
||||||
|
additional checks, lost argument boundaries, or lost tool status still fail.
|
||||||
|
The tests remain offline and do not duplicate ReleaseKit's release-mechanics
|
||||||
|
matrix.
|
||||||
|
|
||||||
|
## Review Verification and Limits
|
||||||
|
|
||||||
|
- `go test ./...` passed on the reviewed source.
|
||||||
|
- A normal application build succeeded. Direct executable invocations reproduced
|
||||||
|
the version-help stream defect and mixed-help rejection.
|
||||||
|
- The actual membership helper reproduced the missing-target false positive.
|
||||||
|
Disposable test copies reproduced both equivalent-configuration failures;
|
||||||
|
the repository's code and tests were left unchanged.
|
||||||
|
- The real ReleaseKit builder produced both configured Linux artifacts with
|
||||||
|
build-test identifier `v0.0.0` and passed its injected-version smoke check.
|
||||||
|
This used the available Go 1.26.8 Linux amd64 toolchain; CI remains configured
|
||||||
|
for Go 1.25.5. Temporary build artifacts were removed.
|
||||||
|
- Module selection, source attribution, product profile, secret mapping, and
|
||||||
|
CI command match the intended integration. Live Woodpecker/Gitea delivery
|
||||||
|
remains unobserved, as already recorded in the feature roadmap; it is separate
|
||||||
|
deployment work rather than an implementation finding or source-release gate.
|
||||||
|
- No tags, pushes, hosted uploads, or LLM calls were made. Remediation should
|
||||||
|
remain bounded to the findings above and add no new release gates.
|
||||||
|
|
||||||
|
## Resolution Record
|
||||||
|
|
||||||
|
- **R1:** The version command now uses one standard flag-parsing path. Successful
|
||||||
|
help is written once to stdout, with no stderr usage, and help can short-circuit
|
||||||
|
trailing arguments. Non-help usage errors retain status `2`. Regression tests
|
||||||
|
reproduced both defects before the fix; `go test ./internal/cli ./cmd/audita`
|
||||||
|
passed afterward. The CLI contract reflects ordinary help behavior.
|
||||||
|
- **R2:** Delivery targets are compared as sets using the standard library.
|
||||||
|
Regression cases reject either missing Linux target concealed by duplicates
|
||||||
|
and an unintended target, while accepting equivalent reordered/repeated lists.
|
||||||
|
The cases reproduced the faulty membership behavior before the fix;
|
||||||
|
`go test ./scripts` passed afterward.
|
||||||
|
- **R3:** Wrapper coverage now checks the executable's actual destination, and
|
||||||
|
profile coverage checks the effective additional-check list. Equivalent output
|
||||||
|
paths and an explicit empty list are accepted. Fixture helpers use standard
|
||||||
|
library operations where appropriate. Disposable mutation checks accepted
|
||||||
|
`-o bin/releasekit`, `-o=bin/releasekit`, and `"checks": []`, while detecting a
|
||||||
|
wrong executable destination, changed product, nonempty checks, lost argument
|
||||||
|
boundaries, and lost tool status. The complete `go test ./...` suite passed
|
||||||
|
with all three remediations in place. No new release gates were added.
|
||||||
1
go.mod
1
go.mod
@@ -4,6 +4,7 @@ go 1.25.5
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.maximumdirect.net/eric/ConfigKit v0.5.0
|
gitea.maximumdirect.net/eric/ConfigKit v0.5.0
|
||||||
|
gitea.maximumdirect.net/eric/ReleaseKit v0.1.0
|
||||||
gitea.maximumdirect.net/eric/promptkit v0.12.0
|
gitea.maximumdirect.net/eric/promptkit v0.12.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1,5 +1,7 @@
|
|||||||
gitea.maximumdirect.net/eric/ConfigKit v0.5.0 h1:2KwG10+kApWM3avOoYA+p768xcbHUp7tQ2rqizBNpyg=
|
gitea.maximumdirect.net/eric/ConfigKit v0.5.0 h1:2KwG10+kApWM3avOoYA+p768xcbHUp7tQ2rqizBNpyg=
|
||||||
gitea.maximumdirect.net/eric/ConfigKit v0.5.0/go.mod h1:tYWpsrbLyu05r8+1MapWp5fN6ZrX9I8hU2xEPedYmYk=
|
gitea.maximumdirect.net/eric/ConfigKit v0.5.0/go.mod h1:tYWpsrbLyu05r8+1MapWp5fN6ZrX9I8hU2xEPedYmYk=
|
||||||
|
gitea.maximumdirect.net/eric/ReleaseKit v0.1.0 h1:Ejk85+9Of+A8M7Mod91iB3KxrrQYgnJcz9hPA6fL1a4=
|
||||||
|
gitea.maximumdirect.net/eric/ReleaseKit v0.1.0/go.mod h1:KmH607Vc/RK/zKrCB96cWh0QZYvkbxBFvtqV+BkKaxs=
|
||||||
gitea.maximumdirect.net/eric/promptkit v0.12.0 h1:oIFWEg6GPxtmX3Plqv0euP+FnK1LCk/t4xDy6oKCpP4=
|
gitea.maximumdirect.net/eric/promptkit v0.12.0 h1:oIFWEg6GPxtmX3Plqv0euP+FnK1LCk/t4xDy6oKCpP4=
|
||||||
gitea.maximumdirect.net/eric/promptkit v0.12.0/go.mod h1:pFOOy5v1zH17stEi9rmHYmfbuLjnfVMcUhfmk+4VtJY=
|
gitea.maximumdirect.net/eric/promptkit v0.12.0/go.mod h1:pFOOy5v1zH17stEi9rmHYmfbuLjnfVMcUhfmk+4VtJY=
|
||||||
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20260930.0 h1:lzfCDbzJQRGzIlRPRprnAV9hVVL5zUQy80VMQ0kbYa0=
|
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20260930.0 h1:lzfCDbzJQRGzIlRPRprnAV9hVVL5zUQy80VMQ0kbYa0=
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/ReleaseKit/buildinfo"
|
||||||
"gitea.maximumdirect.net/eric/audita/internal/core/schema"
|
"gitea.maximumdirect.net/eric/audita/internal/core/schema"
|
||||||
"gitea.maximumdirect.net/eric/audita/internal/framework/llm"
|
"gitea.maximumdirect.net/eric/audita/internal/framework/llm"
|
||||||
)
|
)
|
||||||
@@ -42,6 +44,9 @@ func Run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
|||||||
case "config":
|
case "config":
|
||||||
writeConfigUsage(stdout)
|
writeConfigUsage(stdout)
|
||||||
return 0
|
return 0
|
||||||
|
case "version":
|
||||||
|
writeVersionUsage(stdout)
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +61,36 @@ func Run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
|||||||
if args[0] == "config" {
|
if args[0] == "config" {
|
||||||
return runConfig(ctx, args[1:], stdout, stderr)
|
return runConfig(ctx, args[1:], stdout, stderr)
|
||||||
}
|
}
|
||||||
|
if args[0] == "version" {
|
||||||
|
return runVersion(args[1:], stdout, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Fprintf(stderr, "audita: unknown command %q\n\n", args[0])
|
fmt.Fprintf(stderr, "audita: unknown command %q\n\n", args[0])
|
||||||
writeRootUsage(stderr)
|
writeRootUsage(stderr)
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runVersion(args []string, stdout, stderr io.Writer) int {
|
||||||
|
flags := flag.NewFlagSet("version", flag.ContinueOnError)
|
||||||
|
flags.SetOutput(stderr)
|
||||||
|
flags.Usage = func() {} // Route usage after parsing so help stays on stdout.
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
if err == flag.ErrHelp {
|
||||||
|
writeVersionUsage(stdout)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
writeVersionUsage(stderr)
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if positional := flags.Args(); len(positional) > 0 {
|
||||||
|
fmt.Fprintf(stderr, "audita version: unexpected argument %q\n\n", positional[0])
|
||||||
|
writeVersionUsage(stderr)
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
fmt.Fprintf(stdout, "audita %s\n", buildinfo.Version())
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func runConfig(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
func runConfig(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||||
if len(args) == 0 || isHelpCommand(args) || args[0] == "--help" || args[0] == "-h" {
|
if len(args) == 0 || isHelpCommand(args) || args[0] == "--help" || args[0] == "-h" {
|
||||||
writeConfigUsage(stdout)
|
writeConfigUsage(stdout)
|
||||||
@@ -177,11 +206,19 @@ func writeRootUsage(w io.Writer) {
|
|||||||
fmt.Fprintln(w, "Commands:")
|
fmt.Fprintln(w, "Commands:")
|
||||||
fmt.Fprintln(w, " process Process a transcript JSON file")
|
fmt.Fprintln(w, " process Process a transcript JSON file")
|
||||||
fmt.Fprintln(w, " config Validate and inspect config")
|
fmt.Fprintln(w, " config Validate and inspect config")
|
||||||
|
fmt.Fprintln(w, " version Print the Audita version")
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
fmt.Fprintln(w, "Example:")
|
fmt.Fprintln(w, "Example:")
|
||||||
fmt.Fprintln(w, " audita process transcript.json --glossary glossary.yaml --output corrected.json")
|
fmt.Fprintln(w, " audita process transcript.json --glossary glossary.yaml --output corrected.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeVersionUsage(w io.Writer) {
|
||||||
|
fmt.Fprintln(w, "Print the Audita version.")
|
||||||
|
fmt.Fprintln(w)
|
||||||
|
fmt.Fprintln(w, "Usage:")
|
||||||
|
fmt.Fprintln(w, " audita version")
|
||||||
|
}
|
||||||
|
|
||||||
func writeConfigUsage(w io.Writer) {
|
func writeConfigUsage(w io.Writer) {
|
||||||
fmt.Fprintln(w, "Validate and inspect Audita config.")
|
fmt.Fprintln(w, "Validate and inspect Audita config.")
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
|
|||||||
73
internal/cli/version_test.go
Normal file
73
internal/cli/version_test.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/ReleaseKit/buildinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVersionCommandRunsWithoutApplicationSetup(t *testing.T) {
|
||||||
|
t.Chdir(t.TempDir())
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
if code := Run(context.Background(), []string{"version"}, &stdout, &stderr); code != 0 {
|
||||||
|
t.Fatalf("exit=%d, stdout=%q, stderr=%q", code, stdout.String(), stderr.String())
|
||||||
|
}
|
||||||
|
if got, want := stdout.String(), fmt.Sprintf("audita %s\n", buildinfo.Version()); got != want {
|
||||||
|
t.Fatalf("stdout=%q, want %q", got, want)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got != "" {
|
||||||
|
t.Fatalf("stderr=%q, want empty", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionCommandHelp(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{name: "root help", args: []string{"--help"}, want: "version Print the Audita version"},
|
||||||
|
{name: "help version", args: []string{"help", "version"}, want: "audita version"},
|
||||||
|
{name: "short flag", args: []string{"version", "-h"}, want: "audita version"},
|
||||||
|
{name: "long flag", args: []string{"version", "--help"}, want: "audita version"},
|
||||||
|
{name: "parser help", args: []string{"version", "--h"}, want: "audita version"},
|
||||||
|
{name: "help short circuits", args: []string{"version", "-h", "--help", "extra"}, want: "audita version"},
|
||||||
|
} {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
if code := Run(context.Background(), test.args, &stdout, &stderr); code != 0 {
|
||||||
|
t.Fatalf("exit=%d, stdout=%q, stderr=%q", code, stdout.String(), stderr.String())
|
||||||
|
}
|
||||||
|
if !strings.Contains(stdout.String(), test.want) {
|
||||||
|
t.Fatalf("stdout=%q, want %q", stdout.String(), test.want)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got != "" {
|
||||||
|
t.Fatalf("stderr=%q, want empty", got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionCommandRejectsUnexpectedArguments(t *testing.T) {
|
||||||
|
for _, args := range [][]string{
|
||||||
|
{"version", "extra"},
|
||||||
|
{"version", "--unknown"},
|
||||||
|
} {
|
||||||
|
t.Run(strings.Join(args, " "), func(t *testing.T) {
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
if code := Run(context.Background(), args, &stdout, &stderr); code != 2 {
|
||||||
|
t.Fatalf("exit=%d, stdout=%q, stderr=%q", code, stdout.String(), stderr.String())
|
||||||
|
}
|
||||||
|
if got := stdout.String(); got != "" {
|
||||||
|
t.Fatalf("stdout=%q, want empty", got)
|
||||||
|
}
|
||||||
|
if got := stderr.String(); got == "" {
|
||||||
|
t.Fatal("expected usage error on stderr")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
15
releasekit.json
Normal file
15
releasekit.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"product": "Audita",
|
||||||
|
"remote": "origin",
|
||||||
|
"build": {
|
||||||
|
"main_package": "./cmd/audita",
|
||||||
|
"binary_name": "audita",
|
||||||
|
"targets": ["linux/amd64", "linux/arm64"]
|
||||||
|
},
|
||||||
|
"delivery": {
|
||||||
|
"base_url": "https://gitea.maximumdirect.net",
|
||||||
|
"owner": "eric",
|
||||||
|
"repository": "audita",
|
||||||
|
"token_env": "RELEASEKIT_GITEA_TOKEN"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
if [[ $# != 2 || ! $1 =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
|
|
||||||
echo 'Usage: scripts/build-release.sh vMAJOR.MINOR.PATCH OUTPUT_DIRECTORY' >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
version=$1
|
|
||||||
output=$2
|
|
||||||
mkdir -p "$output"
|
|
||||||
output=$(cd "$output" && pwd)
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
for arch in amd64 arm64; do
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=$arch GOWORK=off \
|
|
||||||
go build -mod=readonly -trimpath -ldflags '-s -w' \
|
|
||||||
-o "$output/audita-$version-linux-$arch" ./cmd/audita
|
|
||||||
done
|
|
||||||
@@ -1,48 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
fail() { echo "Release stopped: $*" >&2; exit 1; }
|
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||||
if [[ $# != 2 || ( $1 != --check && $1 != --publish ) || ! $2 =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
|
cd "$script_dir/.."
|
||||||
echo 'Usage: scripts/release.sh --check|--publish vMAJOR.MINOR.PATCH' >&2
|
mkdir -p bin
|
||||||
exit 2
|
GOWORK=off GOTOOLCHAIN=local go build -mod=readonly \
|
||||||
fi
|
-o ./bin/releasekit gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit
|
||||||
mode=$1
|
exec ./bin/releasekit "$@"
|
||||||
version=$2
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
[[ $(git symbolic-ref --quiet --short HEAD) == main ]] || fail 'switch to main'
|
|
||||||
[[ -z $(git status --porcelain) ]] || fail 'commit or remove outstanding changes first'
|
|
||||||
candidate=$(git rev-parse HEAD)
|
|
||||||
note="docs/releases/$version.md"
|
|
||||||
[[ -s $note ]] || fail "missing release note: $note"
|
|
||||||
IFS= read -r title < "$note" || true
|
|
||||||
[[ $title == "# Audita $version" ]] || fail "note must begin with # Audita $version"
|
|
||||||
git show-ref --verify --quiet "refs/tags/$version" && fail 'version already exists locally'
|
|
||||||
|
|
||||||
# Check mode is local and never contacts a remote or creates a tag.
|
|
||||||
if [[ $mode == --publish ]]; then
|
|
||||||
existing=$(git ls-remote --tags origin "refs/tags/$version")
|
|
||||||
[[ -z $existing ]] || fail 'version already exists on origin'
|
|
||||||
fi
|
|
||||||
build_dir=$(mktemp -d)
|
|
||||||
trap 'rm -rf "$build_dir"' EXIT
|
|
||||||
GOWORK=off go test -mod=readonly ./...
|
|
||||||
bash scripts/build-release.sh "$version" "$build_dir"
|
|
||||||
[[ $(git rev-parse HEAD) == "$candidate" ]] || fail 'HEAD changed during validation'
|
|
||||||
[[ -z $(git status --porcelain) ]] || fail 'worktree changed during validation'
|
|
||||||
[[ $(git symbolic-ref --quiet --short HEAD) == main ]] || fail 'branch changed during validation'
|
|
||||||
if [[ $mode == --check ]]; then
|
|
||||||
echo "Validated $version at $candidate; nothing published."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
# Fetch the authoritative branch before checking that this is a forward update.
|
|
||||||
git fetch --no-tags origin refs/heads/main
|
|
||||||
git merge-base --is-ancestor FETCH_HEAD "$candidate" || fail 'candidate does not include origin/main'
|
|
||||||
existing=$(git ls-remote --tags origin "refs/tags/$version")
|
|
||||||
[[ -z $existing ]] || fail 'version already exists on origin'
|
|
||||||
git tag "$version" "$candidate"
|
|
||||||
[[ $(git rev-parse "refs/tags/$version") == "$candidate" ]] || fail 'tag target changed'
|
|
||||||
# Atomic publication prevents a rejected tag/branch update from publishing only half.
|
|
||||||
if ! git push --atomic origin "$candidate:refs/heads/main" "refs/tags/$version:refs/tags/$version"; then
|
|
||||||
fail "push failed or its outcome is uncertain; inspect origin/main and refs/tags/$version before retrying (local tag retained)"
|
|
||||||
fi
|
|
||||||
echo "Released $version at $candidate. Binary publication runs asynchronously; CI is not awaited."
|
|
||||||
|
|||||||
@@ -1,142 +1,272 @@
|
|||||||
package scripts
|
package scripts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Real local Git repositories exercise publication; Go is stubbed at the slow
|
const goStub = `#!/usr/bin/env bash
|
||||||
// build/test boundary so these tests remain offline and cannot recurse.
|
set -euo pipefail
|
||||||
func TestReleaseWorkflow(t *testing.T) {
|
printf '%s\0' "$PWD" "GOWORK=$GOWORK" "GOTOOLCHAIN=$GOTOOLCHAIN" "$@" > "$RELEASEKIT_BUILD_LOG"
|
||||||
for _, scenario := range []string{"check", "publish", "dirty", "tests-fail", "build-fail", "existing-remote-tag", "remote-ahead", "rejected-push", "source-changed", "missing-note", "invalid-version", "existing-local-tag", "wrong-branch", "head-changed"} {
|
if [[ ${RELEASEKIT_BOOTSTRAP_STATUS:-0} != 0 ]]; then
|
||||||
t.Run(scenario, func(t *testing.T) {
|
exit "$RELEASEKIT_BOOTSTRAP_STATUS"
|
||||||
root := t.TempDir()
|
fi
|
||||||
repo, remote := filepath.Join(root, "repo"), filepath.Join(root, "remote.git")
|
output=
|
||||||
if err := os.MkdirAll(repo, 0755); err != nil {
|
while (($#)); do
|
||||||
t.Fatal(err)
|
case $1 in
|
||||||
|
-o)
|
||||||
|
shift
|
||||||
|
output=${1:?missing output path}
|
||||||
|
;;
|
||||||
|
-o=*) output=${1#-o=} ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
[[ -n $output ]]
|
||||||
|
cat > "$output" <<'RELEASEKIT_TOOL'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
printf '%s\0' "$PWD" "$@" > "$RELEASEKIT_ARGS_LOG"
|
||||||
|
exit "${RELEASEKIT_TOOL_STATUS:-0}"
|
||||||
|
RELEASEKIT_TOOL
|
||||||
|
chmod +x "$output"
|
||||||
|
`
|
||||||
|
|
||||||
|
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_BUILD_LOG": layout.buildLog,
|
||||||
|
"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)
|
||||||
}
|
}
|
||||||
env := append(os.Environ(), "GIT_CONFIG_NOSYSTEM=1", "GIT_CONFIG_GLOBAL="+os.DevNull, "GIT_AUTHOR_NAME=Release Test", "GIT_AUTHOR_EMAIL=test@example.invalid", "GIT_COMMITTER_NAME=Release Test", "GIT_COMMITTER_EMAIL=test@example.invalid", "GIT_TERMINAL_PROMPT=0")
|
if len(output) != 0 {
|
||||||
run := func(dir string, args ...string) string {
|
t.Fatalf("unexpected output: %q", output)
|
||||||
t.Helper()
|
|
||||||
c := exec.Command("git", args...)
|
|
||||||
c.Dir = dir
|
|
||||||
c.Env = env
|
|
||||||
b, e := c.CombinedOutput()
|
|
||||||
if e != nil {
|
|
||||||
t.Fatalf("git %v: %v\n%s", args, e, b)
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(b))
|
|
||||||
}
|
}
|
||||||
write := func(path, data string, mode os.FileMode) {
|
|
||||||
t.Helper()
|
buildInvocation := readNULArguments(t, layout.buildLog)
|
||||||
if e := os.MkdirAll(filepath.Dir(path), 0755); e != nil {
|
if len(buildInvocation) < 4 || buildInvocation[0] != layout.repo {
|
||||||
t.Fatal(e)
|
t.Fatalf("build invocation=%q, want repository %q", buildInvocation, layout.repo)
|
||||||
}
|
|
||||||
if e := os.WriteFile(path, []byte(data), mode); e != nil {
|
|
||||||
t.Fatal(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
run(root, "init", "--bare", "--initial-branch=main", remote)
|
if buildInvocation[1] != "GOWORK=off" || buildInvocation[2] != "GOTOOLCHAIN=local" {
|
||||||
run(repo, "init", "--initial-branch=main")
|
t.Fatalf("build environment=%q", buildInvocation[1:3])
|
||||||
for _, name := range []string{"release.sh", "build-release.sh"} {
|
|
||||||
b, e := os.ReadFile(name)
|
|
||||||
if e != nil {
|
|
||||||
t.Fatal(e)
|
|
||||||
}
|
|
||||||
write(filepath.Join(repo, "scripts", name), string(b), 0755)
|
|
||||||
}
|
}
|
||||||
write(filepath.Join(repo, "docs/releases/v1.2.3.md"), "# Audita v1.2.3\n\nA test release.\n", 0644)
|
buildArgs := buildInvocation[3:]
|
||||||
run(repo, "add", ".")
|
if buildArgs[0] != "build" || !slices.Contains(buildArgs, "-mod=readonly") || !slices.Contains(buildArgs, "gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit") {
|
||||||
run(repo, "commit", "-m", "Initial fixture")
|
t.Fatalf("unexpected build arguments: %q", buildArgs)
|
||||||
run(repo, "remote", "add", "origin", remote)
|
|
||||||
run(repo, "push", "origin", "main")
|
|
||||||
write(filepath.Join(repo, "change.txt"), "candidate\n", 0644)
|
|
||||||
run(repo, "add", ".")
|
|
||||||
run(repo, "commit", "-m", "Candidate fixture")
|
|
||||||
candidate := run(repo, "rev-parse", "HEAD")
|
|
||||||
log := filepath.Join(root, "go.log")
|
|
||||||
stub := "#!/bin/sh\necho \"$* GOOS=$GOOS GOARCH=$GOARCH CGO_ENABLED=$CGO_ENABLED GOWORK=$GOWORK\" >> \"$GO_LOG\"\n"
|
|
||||||
switch scenario {
|
|
||||||
case "existing-local-tag":
|
|
||||||
run(repo, "tag", "v1.2.3")
|
|
||||||
case "wrong-branch":
|
|
||||||
run(repo, "checkout", "-b", "feature")
|
|
||||||
case "head-changed":
|
|
||||||
stub += "if [ \"$1\" = test ]; then echo changed > change.txt; git add change.txt; git commit -m Changed; fi\n"
|
|
||||||
case "dirty":
|
|
||||||
write(filepath.Join(repo, "untracked"), "dirty", 0644)
|
|
||||||
case "tests-fail":
|
|
||||||
stub += "[ \"$1\" != test ] || exit 1\n"
|
|
||||||
case "build-fail":
|
|
||||||
stub += "[ \"$GOARCH\" != arm64 ] || exit 1\n"
|
|
||||||
case "existing-remote-tag":
|
|
||||||
run(remote, "tag", "v1.2.3", "main")
|
|
||||||
case "remote-ahead":
|
|
||||||
other := filepath.Join(root, "other")
|
|
||||||
run(root, "clone", remote, other)
|
|
||||||
write(filepath.Join(other, "other.txt"), "new remote commit", 0644)
|
|
||||||
run(other, "add", ".")
|
|
||||||
run(other, "commit", "-m", "Remote fixture")
|
|
||||||
run(other, "push", "origin", "main")
|
|
||||||
case "rejected-push":
|
|
||||||
write(filepath.Join(remote, "hooks/pre-receive"), "#!/bin/sh\nexit 1\n", 0755)
|
|
||||||
case "source-changed":
|
|
||||||
stub += "[ \"$1\" != test ] || echo changed > change.txt\n"
|
|
||||||
case "missing-note":
|
|
||||||
run(repo, "rm", "docs/releases/v1.2.3.md")
|
|
||||||
run(repo, "commit", "-m", "Remove fixture note")
|
|
||||||
}
|
}
|
||||||
stub += "exit 0\n"
|
if info, err := os.Stat(filepath.Join(layout.repo, "bin", "releasekit")); err != nil || !info.Mode().IsRegular() {
|
||||||
write(filepath.Join(root, "bin/go"), stub, 0755)
|
t.Fatalf("ReleaseKit executable was not built in bin: %v", err)
|
||||||
mode, version := "--publish", "v1.2.3"
|
|
||||||
if scenario == "check" {
|
|
||||||
mode = "--check"
|
|
||||||
run(repo, "remote", "remove", "origin")
|
|
||||||
}
|
}
|
||||||
if scenario == "invalid-version" {
|
|
||||||
version = "v01.2.3"
|
toolInvocation := readNULArguments(t, layout.argsLog)
|
||||||
}
|
wantToolInvocation := append([]string{layout.repo}, args...)
|
||||||
c := exec.Command("bash", "scripts/release.sh", mode, version)
|
if !reflect.DeepEqual(toolInvocation, wantToolInvocation) {
|
||||||
c.Dir = repo
|
t.Fatalf("tool invocation=%q, want %q", toolInvocation, wantToolInvocation)
|
||||||
c.Env = append(env, "PATH="+filepath.Join(root, "bin")+":"+os.Getenv("PATH"), "GO_LOG="+log)
|
|
||||||
b, e := c.CombinedOutput()
|
|
||||||
success := scenario == "check" || scenario == "publish"
|
|
||||||
if (e == nil) != success {
|
|
||||||
t.Fatalf("success=%v: %v\n%s", success, e, b)
|
|
||||||
}
|
|
||||||
tags := run(remote, "tag", "--list")
|
|
||||||
if scenario == "publish" {
|
|
||||||
if run(remote, "rev-parse", "refs/tags/v1.2.3") != candidate || run(remote, "rev-parse", "main") != candidate {
|
|
||||||
t.Fatal("wrong source published")
|
|
||||||
}
|
|
||||||
if !strings.Contains(string(b), "CI is not awaited") {
|
|
||||||
t.Fatal("missing asynchronous completion message")
|
|
||||||
}
|
|
||||||
} else if scenario != "existing-remote-tag" && tags != "" {
|
|
||||||
t.Fatalf("unexpected remote tags: %s", tags)
|
|
||||||
}
|
|
||||||
if scenario == "rejected-push" && run(remote, "rev-parse", "main") == candidate {
|
|
||||||
t.Fatal("rejected push published candidate")
|
|
||||||
}
|
|
||||||
if scenario == "check" && run(repo, "tag", "--list") != "" {
|
|
||||||
t.Fatal("check created a tag")
|
|
||||||
}
|
|
||||||
if success {
|
|
||||||
calls, e := os.ReadFile(log)
|
|
||||||
if e != nil {
|
|
||||||
t.Fatal(e)
|
|
||||||
}
|
|
||||||
lines := strings.Split(strings.TrimSpace(string(calls)), "\n")
|
|
||||||
if len(lines) != 3 {
|
|
||||||
t.Fatalf("expected one test and two builds: %s", calls)
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(lines[0], "test -mod=readonly ./...") || !strings.Contains(lines[1], "GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GOWORK=off") || !strings.Contains(lines[2], "GOOS=linux GOARCH=arm64 CGO_ENABLED=0 GOWORK=off") {
|
|
||||||
t.Fatalf("unexpected validation: %s", calls)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_BUILD_LOG": layout.buildLog,
|
||||||
|
"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 TestReleaseKitProfileDeclaresProductAndDeliverySettings(t *testing.T) {
|
||||||
|
data, err := os.ReadFile("../releasekit.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var deliveryFields map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(raw["delivery"], &deliveryFields); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, exists := deliveryFields["token"]; exists {
|
||||||
|
t.Fatal("profile must not contain a credential value")
|
||||||
|
}
|
||||||
|
var profile struct {
|
||||||
|
Product string `json:"product"`
|
||||||
|
Remote string `json:"remote"`
|
||||||
|
Checks []json.RawMessage `json:"checks"`
|
||||||
|
Build struct {
|
||||||
|
MainPackage string `json:"main_package"`
|
||||||
|
BinaryName string `json:"binary_name"`
|
||||||
|
Targets []string `json:"targets"`
|
||||||
|
} `json:"build"`
|
||||||
|
Delivery struct {
|
||||||
|
BaseURL string `json:"base_url"`
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
Repository string `json:"repository"`
|
||||||
|
TokenEnv string `json:"token_env"`
|
||||||
|
} `json:"delivery"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &profile); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(profile.Checks) != 0 {
|
||||||
|
t.Fatal("profile must not define additional checks")
|
||||||
|
}
|
||||||
|
if profile.Product != "Audita" || profile.Remote != "origin" {
|
||||||
|
t.Fatalf("product=%q remote=%q", profile.Product, profile.Remote)
|
||||||
|
}
|
||||||
|
if profile.Build.MainPackage != "./cmd/audita" || profile.Build.BinaryName != "audita" {
|
||||||
|
t.Fatalf("build=%+v", profile.Build)
|
||||||
|
}
|
||||||
|
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" {
|
||||||
|
t.Fatalf("delivery=%+v", profile.Delivery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReleaseKitTargetMembership(t *testing.T) {
|
||||||
|
want := []string{"linux/amd64", "linux/arm64"}
|
||||||
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
|
targets []string
|
||||||
|
match bool
|
||||||
|
}{
|
||||||
|
{"missing amd64", []string{"linux/arm64", "linux/arm64"}, false},
|
||||||
|
{"missing arm64", []string{"linux/amd64", "linux/amd64"}, false},
|
||||||
|
{"additional target", []string{"linux/amd64", "linux/arm64", "darwin/arm64"}, false},
|
||||||
|
{"reordered", []string{"linux/arm64", "linux/amd64"}, true},
|
||||||
|
{"repeated", []string{"linux/amd64", "linux/arm64", "linux/amd64"}, true},
|
||||||
|
} {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if got := sameMembers(test.targets, want); got != test.match {
|
||||||
|
t.Fatalf("target membership for %q = %t, want %t", test.targets, got, test.match)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type releaseTestLayout struct {
|
||||||
|
repo string
|
||||||
|
caller string
|
||||||
|
wrapper string
|
||||||
|
argsLog string
|
||||||
|
buildLog 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"),
|
||||||
|
buildLog: filepath.Join(root, "build-arguments"),
|
||||||
|
path: filepath.Join(root, "bin"),
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Join(layout.repo, "scripts"), 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(layout.caller, 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(layout.path, 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 (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 {
|
||||||
|
gotSet[value] = struct{}{}
|
||||||
|
}
|
||||||
|
wantSet := make(map[string]struct{}, len(want))
|
||||||
|
for _, value := range want {
|
||||||
|
wantSet[value] = struct{}{}
|
||||||
|
}
|
||||||
|
return maps.Equal(gotSet, wantSet)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user