Prepare v0.4.1 release notes

This commit is contained in:
2026-10-11 00:57:29 +00:00
parent d42727b038
commit 30a84fa97b
4 changed files with 29 additions and 406 deletions

29
docs/releases/v0.4.1.md Normal file
View File

@@ -0,0 +1,29 @@
# ReleaseKit v0.4.1
## Summary
This patch synchronizes shared state in the delivery and Gitea HTTP test
fixtures. It makes the test suite reliable under concurrent request handling
and the Go race detector. Production code and public behavior are unchanged.
## Changes
- Protect delivery fixture state with mutexes and independent snapshots; use
atomic observations for independent Gitea fixture counters and flags.
- Keep fatal test assertions in the test goroutine and ensure release test
servers close with their owning tests.
- Preserve existing delivery, conflict, retry, and source-tag assertions while
exercising the synchronized fixtures.
## Compatibility
There are no API, configuration, or runtime behavior changes, and no consumer
migration is required. The Go minimum remains 1.26.0. The published v0.4.0 tag
and its contents are unchanged.
Consumers may upgrade to v0.4.1 with their existing API, profile, and workflow
configuration.
[COPYRIGHT](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/COPYRIGHT) |
[LICENSE](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/LICENSE) |
[ATTRIBUTION](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/docs/oss/attribution.md)

View File

@@ -1,152 +0,0 @@
# Targeted concurrency audit
Status: complete; all five finding groups corrected and verified.
## Scope and baseline
Reviewed ReleaseKit at `6176971`, including the
[PromptKit report](promptkit-bug-report.md) and
[fixture synchronization roadmap](race.md). The audit covered shared-state
ownership in HTTP fixtures, background goroutines, cancellation callbacks,
subprocess-driven tests, output capture, and production operation callbacks.
Code discovery used the codebase graph, with source inspection and filesystem
searches where the graph omitted recent files. Reviewed current source and
fixture consumers, including assertion and diagnostic reads. The full offline
race-enabled baseline used Go `go1.26.8 linux/amd64`:
```sh
GOTOOLCHAIN=local GOWORK=off GOPROXY=off GOSUMDB=off \
go test -race -mod=readonly -count=1 ./...
```
The recorded baseline failed in
`TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout`. It reported four race
diagnostics involving three fields: `commit`, `remoteCommit`, and
`releaseCreates`. Other packages passed. An earlier redirected invocation
returned unsuccessfully without diagnostics and supplied no usable baseline;
the complete results above came from a direct invocation.
The additional findings below were established by source inspection. Their
absence from this baseline's diagnostics reflects its exercised schedules.
## Findings and corrections
### 1. CLI delivery fixtures share mutable state without synchronization
Status: corrected; original reported defect and related fixture accesses.
The HTTP handlers in [main_test.go](../../cmd/releasekit/main_test.go) read
configuration and update page state, counters, note content, and assets while
tests configure and inspect that state. The
[mapped workflow fixture](../../cmd/releasekit/multi_module_woodpecker_test.go)
has the same ownership pattern. Subprocess completion supplies no shared-state
synchronization with the parent process's HTTP goroutines.
**Correction:** Each fixture owns a mutex and a separate plain state value.
Handlers serialize state access; test configuration uses locked updates, and
assertions and diagnostics use locked snapshots. Snapshots copy asset maps and
byte slices so inspection retains independent values. All consumers use that
boundary, including source-only, binary, sparse-checkout, alias, failure,
ordinary Woodpecker, and mapped-module tests. Response helpers run under the
handler's lock. The CLI fixture derives response URLs from the request; mapped
fixture configuration is initialized before its server starts.
### 2. The public delivery fixture has the same unsynchronized ownership
Status: corrected; additional finding.
The fixture in [delivery_test.go](../../delivery_test.go) shares request counts,
tags, notes, release-page maps, and asset maps between handlers and test code.
The module-page test also changes the selected tag and note between deliveries.
Its consumers include [repository resolution](../../repository_test.go) and
[partial-clone inspection](../../source_inspection_test.go) tests.
**Correction:** Added a fixture-local mutex, locked updates, and independent
snapshots, including copied page and asset maps. Tag and note changes occur in
one locked update. The server URL is initialized before serving requests.
Applied the same startup ordering to the existing synchronized benchmark server
and its [artifact-read regression test](../../delivery_reads_linux_test.go).
Binary assertion groups reuse their captured snapshot to avoid repeated asset
copies.
### 3. Gitea adapter fixtures expose unsynchronized counters and flags
Status: corrected; additional finding.
[Release fixtures](../../internal/gitea/release_test.go) share creation and
lookup counts, creation flags, and cancellation observations between handlers
and assertions. Lost-acknowledgment scenarios also carry state across separate
HTTP connections. The
[external attachment fixture](../../internal/gitea/assets_test.go) shares its
download counter with the test goroutine.
**Correction:** Used standard-library atomic counters and flags for these
independent scalar observations. Handler updates and assertion/diagnostic reads
use their atomic methods. Existing exact counts, conflict handling, recovery,
and external-origin credential assertions remain in place.
### 4. HTTP assertions invoke fatal methods from request goroutines
Status: corrected; additional finding.
Gitea release and asset handlers, including their assertion helpers, used
`testing.T.Fatal` and `Fatalf`. Those methods terminate the calling goroutine
and belong to the test goroutine. In a handler, they can abandon request
processing and produce misleading transport failures.
**Correction:** Handler assertions report through `Error` or `Errorf` and
return from the affected handler or helper. Fatal assertions remain in the test
goroutine. The fixtures still mark every violated assertion as a test failure.
### 5. A release fixture omits server lifetime cleanup
Status: corrected; additional finding.
`newReleaseServer` returned an active HTTP server without registering cleanup,
and several callers omitted closure. This left listeners and serving goroutines
alive after their owning test finished.
**Correction:** The helper registers `server.Close` with `t.Cleanup`, giving
server shutdown one owner for all its consumers.
## Production and other reviewed paths
The audit found no confirmed production concurrency defect in these paths:
| Boundary | Ownership and synchronization evidence |
| --- | --- |
| [Git blob reader](../../internal/git/blob_reader.go) | A mutex serializes reads, close, and terminal state. A cancellation callback closes process pipes, and the read operation joins a started callback before continuing. Process diagnostics are inspected after child wait. |
| [Process adapter](../../internal/process/process.go) | Process handles are initialized before publication; cancellation uses a context. `sync.Once` protects child wait and its result. Ordinary command completion joins output-copy workers before returning captures. |
| [Go output capture](../../internal/gotool/gotool.go) | One mutex protects combined output, forwarding, and writer errors. Capture ownership transfers after the process finishes writing. |
| [Gitea upload](../../internal/gitea/upload.go) | The multipart worker owns the writer and file reader. An error channel transfers completion; request failure closes the pipe and joins the worker. |
| [Runtime metadata](../../internal/runtimeinfo/version.go) | The injected value is set at link time; fallback initialization uses `sync.OnceValue`. Existing concurrent accessor coverage uses atomics, channels, and a wait group. |
| [Go settings owner](../../internal/gotool/owner.go), candidate validation, and publication callbacks | Mutable owners and validation caches belong to one operation. Validation, product callbacks, and subsequent builds execute sequentially. Internal mutable owners remain behind the facade. |
| Background test workers | Reviewed build/process/dependency cancellation, Git-reader lifecycle, publication watchers, and destination-resolution workers. Results transfer through channels; the publication watcher has an explicit completion join. |
| Other HTTP fixtures | Immutable asset payloads remain read-only. The upload fixture already locks files and observations and returns copied POST names. The module-proxy fixture uses an atomic request counter. |
| Process environment | Tests changing environment settings run sequentially within their package; subprocess overlays belong to individual invocations. |
These conclusions apply to the reviewed ownership paths. Race-enabled execution
and inspection supply evidence for this audit, rather than a guarantee about
every possible caller or schedule. Production files and public API contracts
were unchanged by the corrections.
## Verification and completion
The affected source-delivery, ordinary Woodpecker, and mapped-module workflows
passed with the race detector after fixture synchronization. The Gitea release
and asset checks also passed during focused verification. Existing assertions
continue to cover tag mismatch rejection, source preservation, complete notes,
idempotent delivery, asset counts, conflicts, and recovery.
**Completed:** The final full offline race-enabled suite passed across all
packages with Go 1.26.8, including the original failing workflow and every
fixture consumer. The compiled public API/dependency check passed. The corrected
handlers, configuration updates, snapshots, diagnostics, counters, and server
startup/cleanup were reviewed against their ownership boundaries. Local
documentation links and fragments, Go formatting, and diff whitespace passed.
The linked roadmap and bug report record the completed remediation.
The audit and corrections are local development work. Future publication follows
the existing [release procedure](../release.md), with the published v0.4.0
contents preserved.

View File

@@ -1,98 +0,0 @@
# PromptKit Bug Report: Race In Source-Delivery Test Fixture
Status: remediated in current source; focused and full offline race-enabled
verification passed. See the [concurrency audit](audit.md) for corrections and
additional findings. The original report below records the reviewed release.
## Reviewed Release And Environment
- ReleaseKit v0.4.0, commit
`b96262eb0ce9c8f2eb3e948a5841db2b799bc91b`, matching the published tag.
- Go `go1.26.8 linux/amd64`, with `GOTOOLCHAIN=local` and `GOWORK=off`.
- Discovered while PromptKit evaluated upgrading its release-tool dependency
from v0.3.0 to v0.4.0.
## Observed Failure
The upstream race-enabled suite failed in
`TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout`. The race detector
reported shared fixture state accessed by the test goroutine and HTTP-handler
goroutines without synchronization.
Observed command, run from the ReleaseKit repository root at the commit above:
```sh
GOTOOLCHAIN=local GOWORK=off go test -race -mod=readonly ./...
```
The root package and the other reported packages passed; `cmd/releasekit` failed
with `race detected during execution of test`. `go vet -mod=readonly ./...`
passed under the same toolchain/environment settings.
For focused investigation, the affected test can be selected with:
```sh
GOTOOLCHAIN=local GOWORK=off go test -race -mod=readonly -count=1 \
./cmd/releasekit \
-run '^TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout$'
```
The full-suite command above is the observed reproduction. The focused command
is a suggested diagnostic; repeatability under that selection has not been
established. Race detection may depend on scheduling.
## Race Locations
Locations below refer to the reviewed v0.4.0 commit:
| Shared field | HTTP handler access | Test goroutine access |
| --- | --- | --- |
| `remoteCommit` | Read in `newDeliveryServer`, `cmd/releasekit/main_test.go:1178` | Written in `cmd/releasekit/releasekit_delivery_workflow_test.go:90` and `:97` |
| `releaseCreates` | Updated during release creation in `cmd/releasekit/main_test.go:1207` | Read in `cmd/releasekit/releasekit_delivery_workflow_test.go:94` |
See the [workflow test](../../cmd/releasekit/releasekit_delivery_workflow_test.go)
and [HTTP fixture](../../cmd/releasekit/main_test.go). Representative diagnostic:
```text
WARNING: DATA RACE
Read ... newDeliveryServer.func1 ... main_test.go:1178
Previous write ... TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout
... releasekit_delivery_workflow_test.go:90
WARNING: DATA RACE
Write ... newDeliveryServer.func1 ... main_test.go:1207
Previous read ... TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout
... releasekit_delivery_workflow_test.go:94
```
## Assessment And Impact
The workflow test mutates `deliveryServerState.remoteCommit` to simulate a
hosted-tag mismatch and later restores it. The HTTP fixture reads that field
while handling requests and updates counters that the test inspects. These
accesses have no explicit Go synchronization. Subprocess completion does not
provide the Go synchronization needed for accesses shared with HTTP goroutines.
Source comparison shows the same unsynchronized access pattern in v0.3.0.
This report does not establish that the failure was introduced by v0.4.0, and
the v0.3.0 race suite was not rerun for this assessment.
The demonstrated defect is in the upstream test fixture, not production
publication or delivery code. PromptKit's existing disposable publication
success/failure fixtures passed using the published v0.4.0 tool. PromptKit uses
source-tag publication and does not currently use hosted delivery. Accordingly,
this finding is not a demonstrated blocker to that adoption, but it prevents a
clean upstream race-suite result and can cause validation noise.
## Requested Outcome
Please synchronize the fixture's cross-goroutine state accesses, including test
reads and writes, and check the other users of the shared fixture for the same
pattern. Preserve the mismatched-tag rejection and idempotent source-only
delivery assertions. A fixture-local synchronization approach is sufficient;
the evidence does not call for production API changes or additional release
gates.
Verify the affected workflow under the race detector and rerun the upstream
race-enabled suite after remediation. Upstream should select the specific
implementation and any warranted follow-up coverage.

View File

@@ -1,156 +0,0 @@
# Roadmap: synchronize delivery test fixtures
Status: complete; all audited corrections implemented and verified in `059a795`.
The [concurrency audit](audit.md) records the wider review, additional findings,
corrections, and final verification evidence.
## Objective and evidence
Make the delivery HTTP fixtures safe for concurrent access by test goroutines
and request handlers while preserving their existing behavioral assertions.
The [PromptKit bug report](promptkit-bug-report.md) supplies the original
diagnostics and consumer context.
Upstream investigation reproduced
`TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout` under the race detector
with Go 1.26.8. The diagnostics identified `commit`, `remoteCommit`, and
`releaseCreates` accesses shared between the workflow test and HTTP handlers.
The workflow test and relevant unsynchronized accesses predate v0.4.0.
Subprocess completion supplies no synchronization for this shared Go state.
The ordinary Woodpecker and multi-module workflow tests passed their focused
race-enabled runs during investigation. Source inspection nevertheless found
the same unsynchronized state pattern in their fixtures. A passing run alone
does not establish safe access under other scheduling.
## Scope
The correction belongs in test infrastructure:
- The `deliveryServerState`, `newDeliveryServer`, and `writeDeliveryRelease`
helpers in [main_test.go](../../cmd/releasekit/main_test.go), including all
tests using that fixture.
- The [source-delivery workflow test](../../cmd/releasekit/releasekit_delivery_workflow_test.go)
and [ordinary Woodpecker workflow tests](../../cmd/releasekit/woodpecker_test.go).
- The `mappedDeliveryFixture`, `newMappedDeliveryFixture`, and their consumers
in [multi_module_woodpecker_test.go](../../cmd/releasekit/multi_module_woodpecker_test.go).
- The public delivery fixture in [delivery_test.go](../../delivery_test.go),
including repository-resolution and partial-clone test consumers, plus server
startup in the delivery benchmark and artifact-read regression test.
- Shared observations, HTTP assertion helpers, and server cleanup in the Gitea
[release tests](../../internal/gitea/release_test.go) and
[asset tests](../../internal/gitea/assets_test.go).
Production behavior, public API contracts, and release requirements retain their
current definitions. Use the standard library and small fixture-local helpers,
following the [agent](../policy/agents.md) and
[testing](../policy/testing.md) policies.
## Implementation
1. **Synchronize the shared delivery fixture.** Give mutable fixture state a
`sync.Mutex`. Protect handler reads and writes, test-side configuration
changes, assertion reads, counters, page state, note content, and asset-map
operations with the same mutex. Initialize immutable configuration before
serving requests where practical; synchronize later initialization such as
the source commit assigned after profile creation. Review captured handler
values and response helpers as part of this boundary.
2. **Update every shared-fixture consumer.** Use simple locked setters and
snapshot accessors for test configuration and assertions. Snapshot plain
values separately from the mutex, copying maps and mutable byte slices when
they will be inspected outside the lock. Include failure diagnostics that
format fixture state. Keep lock ownership clear in handler helpers and
release locks before executing CLI workflows or closing the server.
3. **Apply the same discipline to the mapped fixture.** Protect its commit,
request and creation counters, page fields, uploads, and asset map, together
with all corresponding test accesses. Keep each fixture's synchronization
local and proportionate to its existing responsibilities.
4. **Synchronize the public delivery fixture.** Protect request counts,
selected tags and notes, release-page maps, and asset maps with a fixture-local
mutex. Use locked updates and independent snapshots in every consumer,
including repository-resolution and partial-clone tests. Update related tag
and note fields together. Initialize server URLs before serving requests in
the public fixture, benchmark, and artifact-read regression fixture. Reuse
captured snapshots within binary assertion groups.
5. **Synchronize Gitea scalar observations.** Use atomic counters and flags for
independent creation counts, lookup counts, creation state, cancellation
observations, and external attachment download counts. Update handler
accesses, test assertions, and diagnostic reads together. Retain exact
request-count, reconciliation, and credential assertions.
6. **Keep fatal assertions in the test goroutine.** Change HTTP handlers and
their assertion helpers to report failures through `Error` or `Errorf` and
return from the affected handler or helper. Preserve failure reporting for
unexpected requests, malformed payloads, and incorrect credentials.
7. **Give release-server cleanup a single owner.** Register `server.Close` with
`t.Cleanup` inside `newReleaseServer` so all consumers shut down the listener
and serving goroutines when their test finishes.
8. **Preserve behavioral coverage.** Retain rejection of mismatched hosted tags
before release creation, exact source-tag preservation, complete note bodies,
idempotent page and asset delivery, expected asset counts, mapped module
identity, retries using tagged configuration, and existing failure outcomes.
Use these workflows as the regression coverage for the reported race. Add
further coverage only for a concrete gap discovered during the correction.
## Audit finding coverage
All five finding groups are corrected in `059a795`. The audit owns their
diagnostics and review evidence; this roadmap records the corresponding work.
| Audit finding | Roadmap work | Status |
| --- | --- | --- |
| [1. CLI and mapped fixture state](audit.md#1-cli-delivery-fixtures-share-mutable-state-without-synchronization) | Steps 1–3: synchronized handlers, configuration, snapshots, and consumers. | Complete. |
| [2. Public delivery fixture state](audit.md#2-the-public-delivery-fixture-has-the-same-unsynchronized-ownership) | Step 4: synchronized public fixture and safe server startup. | Complete. |
| [3. Gitea counters and flags](audit.md#3-gitea-adapter-fixtures-expose-unsynchronized-counters-and-flags) | Step 5: atomic observations and diagnostic reads. | Complete. |
| [4. Fatal HTTP assertions](audit.md#4-http-assertions-invoke-fatal-methods-from-request-goroutines) | Step 6: failure reporting and returns from handlers. | Complete. |
| [5. Server lifetime cleanup](audit.md#5-a-release-fixture-omits-server-lifetime-cleanup) | Step 7: cleanup owned by the release-server helper. | Complete. |
## Verification and completion
With dependencies prepared, run the affected workflows from the repository root
using the local toolchain and offline module settings:
```sh
GOTOOLCHAIN=local GOWORK=off GOPROXY=off GOSUMDB=off \
go test -race -mod=readonly -count=1 ./cmd/releasekit \
-run '^(TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout|TestWoodpeckerTagDeliveryTemplate|TestMultiModuleWoodpeckerDelivery)$'
```
Then run the full race-enabled suite once to verify the other shared-fixture
users and catch remaining cross-goroutine access:
```sh
GOTOOLCHAIN=local GOWORK=off GOPROXY=off GOSUMDB=off \
go test -race -mod=readonly -count=1 ./...
```
Review the fixture accesses as well as the test results: successful execution
complements the synchronization review. Keep local Git repositories, HTTP
servers, and synthetic module caches isolated as described in
[development](../development.md#testing-synthetic-modules).
Completion means every audited fixture and its consumers use synchronized state,
HTTP assertions and server cleanup follow their goroutine ownership, the existing
assertions remain effective, and the focused and full race-enabled suites pass.
Record completion and verification here and update the bug report's status with
the remediation outcome. Commit the completed correction under the repository's
ordinary change workflow.
This verification addresses the reported defect. Future release validation
continues to follow the [release procedure](../release.md). The correction is
suitable for a subsequent patch release; preparation and publication are separate
work under that procedure, preserving the published v0.4.0 contents.
**Completed:** CLI, mapped, and public delivery fixtures and their consumers use
mutex-protected updates and independent snapshots. Existing mismatch, note,
asset, retry, and source-preservation assertions remain in place. Focused workflow checks and the
full offline race-enabled suite passed with Go 1.26.8; the compiled API/dependency
check passed. The audit also corrected public delivery fixture ownership,
Gitea scalar observations, HTTP assertion handling, and server cleanup.