Move shared checks into validation package
This commit is contained in:
25
check.go
25
check.go
@@ -42,21 +42,11 @@ type ReleaseRequest struct {
|
|||||||
// branch, HEAD, worktree, index, and local tag state. Success establishes local
|
// branch, HEAD, worktree, index, and local tag state. Success establishes local
|
||||||
// candidate evidence only; it does not establish remote availability.
|
// candidate evidence only; it does not establish remote availability.
|
||||||
func Check(ctx context.Context, request ReleaseRequest) (Candidate, error) {
|
func Check(ctx context.Context, request ReleaseRequest) (Candidate, error) {
|
||||||
var validate func(context.Context, candidate.Candidate) error
|
|
||||||
if request.AdditionalChecks != nil {
|
|
||||||
validate = func(ctx context.Context, captured candidate.Candidate) error {
|
|
||||||
return request.AdditionalChecks(ctx, Candidate{
|
|
||||||
RepositoryDir: captured.RepositoryDir,
|
|
||||||
Commit: captured.Commit,
|
|
||||||
Version: captured.Version,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checked, err := candidate.Check(ctx, candidate.Request{
|
checked, err := candidate.Check(ctx, candidate.Request{
|
||||||
RepositoryDir: request.RepositoryDir,
|
RepositoryDir: request.RepositoryDir,
|
||||||
Product: request.Product,
|
Product: request.Product,
|
||||||
Version: request.Version,
|
Version: request.Version,
|
||||||
AdditionalChecks: validate,
|
AdditionalChecks: candidateChecks(request.AdditionalChecks),
|
||||||
AfterValidate: internalbuild.CandidateCallback(internalBuildSpec(request.Build), request.Output),
|
AfterValidate: internalbuild.CandidateCallback(internalBuildSpec(request.Build), request.Output),
|
||||||
Output: request.Output,
|
Output: request.Output,
|
||||||
})
|
})
|
||||||
@@ -69,3 +59,16 @@ func Check(ctx context.Context, request ReleaseRequest) (Candidate, error) {
|
|||||||
Version: checked.Version,
|
Version: checked.Version,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func candidateChecks(checks func(context.Context, Candidate) error) func(context.Context, candidate.Candidate) error {
|
||||||
|
if checks == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return func(ctx context.Context, captured candidate.Candidate) error {
|
||||||
|
return checks(ctx, Candidate{
|
||||||
|
RepositoryDir: captured.RepositoryDir,
|
||||||
|
Commit: captured.Commit,
|
||||||
|
Version: captured.Version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ these implemented packages:
|
|||||||
| `internal/tempdir` | Disposable system temporary storage outside source worktrees. |
|
| `internal/tempdir` | Disposable system temporary storage outside source worktrees. |
|
||||||
| `internal/process` | Cancellable child-process execution with per-command environment overlays and removals. |
|
| `internal/process` | Cancellable child-process execution with per-command environment overlays and removals. |
|
||||||
| `internal/preparation` | Coordination for expected-commit tag checkout and Go module dependency preparation. |
|
| `internal/preparation` | Coordination for expected-commit tag checkout and Go module dependency preparation. |
|
||||||
|
| `internal/validation` | Shared default Go validation for captured candidates; `internal/candidate` retains capture, callback sequencing, and local-state rechecks. |
|
||||||
| `internal/publish` | Publication guards, one validation run, annotated tag creation, push, and recovery state. |
|
| `internal/publish` | Publication guards, one validation run, annotated tag creation, push, and recovery state. |
|
||||||
| `internal/runtimeinfo` | Private version lookup used by the runtime facade. |
|
| `internal/runtimeinfo` | Private version lookup used by the runtime facade. |
|
||||||
| `internal/versioncontract` | Shared stable-version rules and linker-symbol identity. |
|
| `internal/versioncontract` | Shared stable-version rules and linker-symbol identity. |
|
||||||
@@ -58,6 +59,13 @@ dependency direction with `go run ./internal/apicheck`. These constraints are
|
|||||||
also recorded in [ADR 0001](../adr/0001-public-facade.md) and the
|
also recorded in [ADR 0001](../adr/0001-public-facade.md) and the
|
||||||
[architecture policy](../policy/architecture.md).
|
[architecture policy](../policy/architecture.md).
|
||||||
|
|
||||||
|
Candidate checking captures and validates one clean committed source revision
|
||||||
|
in `internal/candidate`. Its validation bridge calls `internal/validation` for
|
||||||
|
the default offline Go suite, then runs product-specific checks once. The
|
||||||
|
candidate package owns post-validation builds and local-state rechecks;
|
||||||
|
publication owns endpoint guards, tag creation, push, and recovery around that
|
||||||
|
same candidate validation path.
|
||||||
|
|
||||||
## Capability owners and behavioral evidence
|
## Capability owners and behavioral evidence
|
||||||
|
|
||||||
The following tests are representative evidence from the current offline suite,
|
The following tests are representative evidence from the current offline suite,
|
||||||
@@ -66,7 +74,7 @@ not a separate release matrix:
|
|||||||
| Capability | Implementation owner | Existing evidence |
|
| Capability | Implementation owner | Existing evidence |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Runtime version lookup and injected application version | `buildinfo`, `internal/runtimeinfo`, `internal/versioncontract`, `internal/build` | `TestVersionInConsumerExecutable`, `TestVersion` |
|
| Runtime version lookup and injected application version | `buildinfo`, `internal/runtimeinfo`, `internal/versioncontract`, `internal/build` | `TestVersionInConsumerExecutable`, `TestVersion` |
|
||||||
| Candidate capture, standard suite, additional checks, and rechecks | `releasekit.Check`, `releasekit.Publish`, `internal/candidate`, `cmd/releasekit/internal/cli` | `TestCheckCapturesCandidateAndCallsAdditionalChecksOnce`, `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestPublishFailedStandardSuiteSkipsAdditionalChecksAndTagCreation`, `TestPublishAdditionalCheckFailurePreservesCauseAndPreventsTagCreation`, `TestCheckCommandBindsConfiguredChecksToCapturedCandidate`, `TestPublishCommandPublishesConfiguredCandidate` |
|
| Candidate capture, shared validation, additional checks, and rechecks | `releasekit.Check`, `releasekit.Publish`, `internal/candidate`, `internal/validation`, `cmd/releasekit/internal/cli` | `TestCheckCapturesCandidateAndCallsAdditionalChecksOnce`, `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestPublishFailedStandardSuiteSkipsAdditionalChecksAndTagCreation`, `TestPublishAdditionalCheckFailurePreservesCauseAndPreventsTagCreation`, `TestCheckCommandBindsConfiguredChecksToCapturedCandidate`, `TestPublishCommandPublishesConfiguredCandidate` |
|
||||||
| Shared Go settings for validation and builds | `internal/gotool`, `internal/process`, `internal/candidate`, `internal/build` | `TestDefaultValidationDoesNotInheritGOFLAGSRunFilter`, `TestNativeAndStaticCommandsUseOwnedBaseline`, `TestPersistentSettingsPreserveCachesAndNativeCTools`, `TestBuildIgnoresAmbientAndPersistentArchitectureTuning` |
|
| Shared Go settings for validation and builds | `internal/gotool`, `internal/process`, `internal/candidate`, `internal/build` | `TestDefaultValidationDoesNotInheritGOFLAGSRunFilter`, `TestNativeAndStaticCommandsUseOwnedBaseline`, `TestPersistentSettingsPreserveCachesAndNativeCTools`, `TestBuildIgnoresAmbientAndPersistentArchitectureTuning` |
|
||||||
| Authoritative endpoint and exact annotated tag publication | `releasekit.Publish`, `internal/publish`, `internal/git` | `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestResolveUsesPushURLAndQueriesWithoutMutation` |
|
| Authoritative endpoint and exact annotated tag publication | `releasekit.Publish`, `internal/publish`, `internal/git` | `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestResolveUsesPushURLAndQueriesWithoutMutation` |
|
||||||
| Publication partial and uncertain recovery | `internal/publish`, `internal/git` | `TestPublishRejectedPushKeepsLocalTagAndReportsNotPublished`, `TestPublishLostAcknowledgmentConfirmsExactRemoteObject`, `TestPublishLostAcknowledgmentWithChangedEndpointRemainsUnknown`, `TestPublishUnavailableReconciliationRemainsUnknownAndPreservesPushError` |
|
| Publication partial and uncertain recovery | `internal/publish`, `internal/git` | `TestPublishRejectedPushKeepsLocalTagAndReportsNotPublished`, `TestPublishLostAcknowledgmentConfirmsExactRemoteObject`, `TestPublishLostAcknowledgmentWithChangedEndpointRemainsUnknown`, `TestPublishUnavailableReconciliationRemainsUnknownAndPreservesPushError` |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Validation model implementation plan
|
# Validation model implementation plan
|
||||||
|
|
||||||
Status: Ready for implementation using incremental API rollout.
|
Status: In progress; the shared validation executor is complete.
|
||||||
|
|
||||||
This plan implements the enabling work in [model.md](model.md), guided by
|
This plan implements the enabling work in [model.md](model.md), guided by
|
||||||
[ADR 0003](../adr/0003-declarative-validation.md). It is written for a Luna
|
[ADR 0003](../adr/0003-declarative-validation.md). It is written for a Luna
|
||||||
@@ -178,6 +178,11 @@ filesystem. Their tests use synthetic trees and local Git repositories.
|
|||||||
|
|
||||||
## Stage 1: Extract the shared validation executor
|
## Stage 1: Extract the shared validation executor
|
||||||
|
|
||||||
|
**Completion:** Complete. The default Go suite now runs through
|
||||||
|
`internal/validation`, candidate capture and callbacks remain in
|
||||||
|
`internal/candidate`, and `Check` and `Publish` share the public callback
|
||||||
|
adapter.
|
||||||
|
|
||||||
**Outcome:** One internal owner runs current default validation, with current
|
**Outcome:** One internal owner runs current default validation, with current
|
||||||
candidate and publication behavior preserved.
|
candidate and publication behavior preserved.
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/gotool"
|
|
||||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/process"
|
"gitea.maximumdirect.net/eric/ReleaseKit/internal/process"
|
||||||
|
"gitea.maximumdirect.net/eric/ReleaseKit/internal/validation"
|
||||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/versioncontract"
|
"gitea.maximumdirect.net/eric/ReleaseKit/internal/versioncontract"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -98,7 +98,11 @@ func Capture(ctx context.Context, request Request) (Candidate, error) {
|
|||||||
// Validate runs the standard offline Go suite, followed by AdditionalChecks
|
// Validate runs the standard offline Go suite, followed by AdditionalChecks
|
||||||
// when supplied, for the captured candidate.
|
// when supplied, for the captured candidate.
|
||||||
func Validate(ctx context.Context, request Request, captured Candidate) error {
|
func Validate(ctx context.Context, request Request, captured Candidate) error {
|
||||||
if err := runDefaultValidation(ctx, captured.RepositoryDir, request.Output); err != nil {
|
if err := validation.Run(ctx, validation.Request{
|
||||||
|
RepositoryDir: captured.RepositoryDir,
|
||||||
|
Commit: captured.Commit,
|
||||||
|
Output: request.Output,
|
||||||
|
}); err != nil {
|
||||||
return fmt.Errorf("validate candidate %s: %w", captured.Commit, err)
|
return fmt.Errorf("validate candidate %s: %w", captured.Commit, err)
|
||||||
}
|
}
|
||||||
if request.AdditionalChecks != nil {
|
if request.AdditionalChecks != nil {
|
||||||
@@ -214,16 +218,6 @@ func hasLocalTag(ctx context.Context, repositoryDir, version string) (bool, erro
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDefaultValidation(ctx context.Context, repositoryDir string, output io.Writer) error {
|
|
||||||
tool, err := gotool.New(ctx, repositoryDir)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("prepare Go toolchain: %w", err)
|
|
||||||
}
|
|
||||||
_, err = tool.Run(ctx, repositoryDir, gotool.Native, gotool.Target{}, output,
|
|
||||||
"test", "-mod=readonly", "-count=1", "./...")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func validStableVersion(version string) bool {
|
func validStableVersion(version string) bool {
|
||||||
return versioncontract.ValidStableVersion(version)
|
return versioncontract.ValidStableVersion(version)
|
||||||
}
|
}
|
||||||
|
|||||||
33
internal/validation/validation.go
Normal file
33
internal/validation/validation.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Package validation owns the shared checks run against a captured candidate.
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/ReleaseKit/internal/gotool"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Settings selects shared validation behavior. Its zero value runs the current
|
||||||
|
// default Go suite.
|
||||||
|
type Settings struct{}
|
||||||
|
|
||||||
|
// Request identifies the captured source and output used by shared validation.
|
||||||
|
type Request struct {
|
||||||
|
RepositoryDir string
|
||||||
|
Commit string
|
||||||
|
Output io.Writer
|
||||||
|
Settings Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run executes shared validation for the captured candidate.
|
||||||
|
func Run(ctx context.Context, request Request) error {
|
||||||
|
tool, err := gotool.New(ctx, request.RepositoryDir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("prepare Go toolchain: %w", err)
|
||||||
|
}
|
||||||
|
_, err = tool.Run(ctx, request.RepositoryDir, gotool.Native, gotool.Target{}, request.Output,
|
||||||
|
"test", "-mod=readonly", "-count=1", "./...")
|
||||||
|
return err
|
||||||
|
}
|
||||||
13
publish.go
13
publish.go
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
internalbuild "gitea.maximumdirect.net/eric/ReleaseKit/internal/build"
|
internalbuild "gitea.maximumdirect.net/eric/ReleaseKit/internal/build"
|
||||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/candidate"
|
|
||||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/publish"
|
"gitea.maximumdirect.net/eric/ReleaseKit/internal/publish"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -61,22 +60,12 @@ type PublicationResult struct {
|
|||||||
// an unreadable result is unknown. A nil error means publication is confirmed
|
// an unreadable result is unknown. A nil error means publication is confirmed
|
||||||
// by the successful push or exact remote object match.
|
// by the successful push or exact remote object match.
|
||||||
func Publish(ctx context.Context, request ReleaseRequest) (PublicationResult, error) {
|
func Publish(ctx context.Context, request ReleaseRequest) (PublicationResult, error) {
|
||||||
var validate func(context.Context, candidate.Candidate) error
|
|
||||||
if request.AdditionalChecks != nil {
|
|
||||||
validate = func(ctx context.Context, captured candidate.Candidate) error {
|
|
||||||
return request.AdditionalChecks(ctx, Candidate{
|
|
||||||
RepositoryDir: captured.RepositoryDir,
|
|
||||||
Commit: captured.Commit,
|
|
||||||
Version: captured.Version,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result, err := publish.Publish(ctx, publish.Request{
|
result, err := publish.Publish(ctx, publish.Request{
|
||||||
RepositoryDir: request.RepositoryDir,
|
RepositoryDir: request.RepositoryDir,
|
||||||
Product: request.Product,
|
Product: request.Product,
|
||||||
Version: request.Version,
|
Version: request.Version,
|
||||||
Remote: request.Remote,
|
Remote: request.Remote,
|
||||||
AdditionalChecks: validate,
|
AdditionalChecks: candidateChecks(request.AdditionalChecks),
|
||||||
AfterValidate: internalbuild.CandidateCallback(internalBuildSpec(request.Build), request.Output),
|
AfterValidate: internalbuild.CandidateCallback(internalBuildSpec(request.Build), request.Output),
|
||||||
Output: request.Output,
|
Output: request.Output,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user