Move shared checks into validation package
This commit is contained in:
35
check.go
35
check.go
@@ -23,13 +23,13 @@ type Candidate struct {
|
||||
// local candidate. Remote is retained for publication operations; Check makes
|
||||
// no remote availability claim.
|
||||
type ReleaseRequest struct {
|
||||
RepositoryDir string
|
||||
Product string
|
||||
Version string
|
||||
Remote string
|
||||
RepositoryDir string
|
||||
Product string
|
||||
Version string
|
||||
Remote string
|
||||
// AdditionalChecks optionally runs product checks after the standard suite.
|
||||
AdditionalChecks func(context.Context, Candidate) error
|
||||
Build *BuildSpec // optional selected-target build after validation.
|
||||
Build *BuildSpec // optional selected-target build after validation.
|
||||
Output io.Writer
|
||||
}
|
||||
|
||||
@@ -42,21 +42,11 @@ type ReleaseRequest struct {
|
||||
// branch, HEAD, worktree, index, and local tag state. Success establishes local
|
||||
// candidate evidence only; it does not establish remote availability.
|
||||
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{
|
||||
RepositoryDir: request.RepositoryDir,
|
||||
Product: request.Product,
|
||||
Version: request.Version,
|
||||
AdditionalChecks: validate,
|
||||
AdditionalChecks: candidateChecks(request.AdditionalChecks),
|
||||
AfterValidate: internalbuild.CandidateCallback(internalBuildSpec(request.Build), request.Output),
|
||||
Output: request.Output,
|
||||
})
|
||||
@@ -69,3 +59,16 @@ func Check(ctx context.Context, request ReleaseRequest) (Candidate, error) {
|
||||
Version: checked.Version,
|
||||
}, 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/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/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/runtimeinfo` | Private version lookup used by the runtime facade. |
|
||||
| `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
|
||||
[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
|
||||
|
||||
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 |
|
||||
| --- | --- | --- |
|
||||
| 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` |
|
||||
| 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` |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 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
|
||||
[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
|
||||
|
||||
**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
|
||||
candidate and publication behavior preserved.
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/gotool"
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/process"
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/validation"
|
||||
"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
|
||||
// when supplied, for the captured candidate.
|
||||
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)
|
||||
}
|
||||
if request.AdditionalChecks != nil {
|
||||
@@ -214,16 +218,6 @@ func hasLocalTag(ctx context.Context, repositoryDir, version string) (bool, erro
|
||||
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 {
|
||||
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"
|
||||
|
||||
internalbuild "gitea.maximumdirect.net/eric/ReleaseKit/internal/build"
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/candidate"
|
||||
"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
|
||||
// by the successful push or exact remote object match.
|
||||
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{
|
||||
RepositoryDir: request.RepositoryDir,
|
||||
Product: request.Product,
|
||||
Version: request.Version,
|
||||
Remote: request.Remote,
|
||||
AdditionalChecks: validate,
|
||||
AdditionalChecks: candidateChecks(request.AdditionalChecks),
|
||||
AfterValidate: internalbuild.CandidateCallback(internalBuildSpec(request.Build), request.Output),
|
||||
Output: request.Output,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user