Observe original Git objects consistently
This commit is contained in:
@@ -7,7 +7,7 @@ The eleven [audit reports](audit/) supply evidence and reproduction details;
|
|||||||
the target document owns the accepted outcomes and policy choices. This plan
|
the target document owns the accepted outcomes and policy choices. This plan
|
||||||
owns execution order, concrete implementation choices, and completion evidence.
|
owns execution order, concrete implementation choices, and completion evidence.
|
||||||
|
|
||||||
All stages are pending. Follow Stage 1 through Stage 19 in order as one
|
Follow Stage 1 through Stage 19 in order as one
|
||||||
end-to-end sprint. Stages separate work into units suitable for a Luna coding
|
end-to-end sprint. Stages separate work into units suitable for a Luna coding
|
||||||
agent. Intermediate stages may leave related code or tests uncompilable;
|
agent. Intermediate stages may leave related code or tests uncompilable;
|
||||||
complete the dependencies before evaluating the final implementation. Run
|
complete the dependencies before evaluating the final implementation. Run
|
||||||
@@ -37,7 +37,7 @@ the agent policy; an intermediate compilation dependency is recorded accurately.
|
|||||||
| Stage | Work items | Status |
|
| Stage | Work items | Status |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 1 | A01; publication assertion portion of A19 | Complete — regression failed before the fix and passed afterward; focused publication and reconciliation tests passed. |
|
| 1 | A01; publication assertion portion of A19 | Complete — regression failed before the fix and passed afterward; focused publication and reconciliation tests passed. |
|
||||||
| 2 | A02, A12: common Git mechanics | Pending |
|
| 2 | A02, A12: common Git mechanics | Complete — focused Git, candidate, identity, and public build/publication/delivery tests passed; A02 regression evidence follows in Stage 3. |
|
||||||
| 3 | A02: source identity regression evidence | Pending |
|
| 3 | A02: source identity regression evidence | Pending |
|
||||||
| 4 | A03: buffered command teardown | Pending |
|
| 4 | A03: buffered command teardown | Pending |
|
||||||
| 5 | A03, A18: piped lifetime and queued cancellation | Pending |
|
| 5 | A03, A18: piped lifetime and queued cancellation | Pending |
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"go.maximumdirect.net/releasekit/internal/artifact"
|
"go.maximumdirect.net/releasekit/internal/artifact"
|
||||||
"go.maximumdirect.net/releasekit/internal/candidate"
|
"go.maximumdirect.net/releasekit/internal/candidate"
|
||||||
|
"go.maximumdirect.net/releasekit/internal/git"
|
||||||
"go.maximumdirect.net/releasekit/internal/gotool"
|
"go.maximumdirect.net/releasekit/internal/gotool"
|
||||||
"go.maximumdirect.net/releasekit/internal/releaseidentity"
|
"go.maximumdirect.net/releasekit/internal/releaseidentity"
|
||||||
"go.maximumdirect.net/releasekit/internal/tempdir"
|
"go.maximumdirect.net/releasekit/internal/tempdir"
|
||||||
@@ -102,14 +103,14 @@ func BuildResolved(ctx context.Context, request Request) (Result, error) {
|
|||||||
return result, fmt.Errorf("resolve module directory: %w", err)
|
return result, fmt.Errorf("resolve module directory: %w", err)
|
||||||
}
|
}
|
||||||
result.ModuleDir = moduleDir
|
result.ModuleDir = moduleDir
|
||||||
commit, err := gitCommit(ctx, repositoryDir)
|
commit, err := git.CaptureHead(ctx, repositoryDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, fmt.Errorf("read source commit: %w", err)
|
||||||
}
|
}
|
||||||
result.CandidateCommit = commit
|
result.CandidateCommit = commit
|
||||||
cleanBefore, err := cleanSource(ctx, repositoryDir)
|
statusBefore, err := git.WorktreeStatus(ctx, repositoryDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, fmt.Errorf("inspect build source: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result.Targets) == 0 {
|
if len(result.Targets) == 0 {
|
||||||
@@ -192,30 +193,18 @@ func BuildResolved(ctx context.Context, request Request) (Result, error) {
|
|||||||
if err := checkVersion(ctx, smokePath, settings.BinaryName, identity.Version); err != nil {
|
if err := checkVersion(ctx, smokePath, settings.BinaryName, identity.Version); err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
commitAfter, err := gitCommit(ctx, repositoryDir)
|
commitAfter, err := git.CaptureHead(ctx, repositoryDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, fmt.Errorf("read source commit: %w", err)
|
||||||
}
|
}
|
||||||
cleanAfter, err := cleanSource(ctx, repositoryDir)
|
statusAfter, err := git.WorktreeStatus(ctx, repositoryDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, fmt.Errorf("inspect build source: %w", err)
|
||||||
}
|
}
|
||||||
result.SourceVerified = cleanBefore && cleanAfter && commitAfter == commit
|
result.SourceVerified = statusBefore == "" && statusAfter == "" && commitAfter == commit
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gitCommit(ctx context.Context, repositoryDir string) (string, error) {
|
|
||||||
command := exec.CommandContext(ctx, "git", "-C", repositoryDir, "rev-parse", "--verify", "HEAD^{commit}")
|
|
||||||
output, err := command.Output()
|
|
||||||
if err != nil {
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
return "", fmt.Errorf("read source commit: %w", ctx.Err())
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("read source commit: %w", err)
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(output)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildTarget(ctx context.Context, goTool *gotool.Tool, moduleDir, mainPackage, outputPath, version string, target Target) error {
|
func buildTarget(ctx context.Context, goTool *gotool.Tool, moduleDir, mainPackage, outputPath, version string, target Target) error {
|
||||||
ldflags := "-s -w -X " + versioncontract.InjectedVersionLinkerSymbol + "=" + version
|
ldflags := "-s -w -X " + versioncontract.InjectedVersionLinkerSymbol + "=" + version
|
||||||
err := goTool.Run(ctx, moduleDir, gotool.Static, gotool.Target{OS: target.OS, Arch: target.Arch}, nil,
|
err := goTool.Run(ctx, moduleDir, gotool.Static, gotool.Target{OS: target.OS, Arch: target.Arch}, nil,
|
||||||
@@ -252,15 +241,3 @@ func checkVersion(ctx context.Context, binaryPath, binaryName, version string) e
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanSource(ctx context.Context, repositoryDir string) (bool, error) {
|
|
||||||
command := exec.CommandContext(ctx, "git", "-C", repositoryDir, "status", "--porcelain", "--untracked-files=all")
|
|
||||||
output, err := command.Output()
|
|
||||||
if err != nil {
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
return false, fmt.Errorf("inspect build source: %w", ctx.Err())
|
|
||||||
}
|
|
||||||
return false, fmt.Errorf("inspect build source: %w", err)
|
|
||||||
}
|
|
||||||
return len(output) == 0, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -194,18 +194,18 @@ func inspectLocalState(ctx context.Context, repositoryDir string) (localState, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return localState{}, fmt.Errorf("inspect current branch: %w", err)
|
return localState{}, fmt.Errorf("inspect current branch: %w", err)
|
||||||
}
|
}
|
||||||
commitOutput, err := gitOutput(ctx, repositoryDir, "rev-parse", "--verify", "HEAD^{commit}")
|
commit, err := git.CaptureHead(ctx, repositoryDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return localState{}, fmt.Errorf("capture HEAD commit: %w", err)
|
return localState{}, fmt.Errorf("capture HEAD commit: %w", err)
|
||||||
}
|
}
|
||||||
statusOutput, err := gitOutput(ctx, repositoryDir, "status", "--porcelain=v1", "--untracked-files=all")
|
status, err := git.WorktreeStatus(ctx, repositoryDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return localState{}, fmt.Errorf("inspect worktree and index: %w", err)
|
return localState{}, fmt.Errorf("inspect worktree and index: %w", err)
|
||||||
}
|
}
|
||||||
return localState{
|
return localState{
|
||||||
branch: strings.TrimSpace(string(branchOutput)),
|
branch: strings.TrimSpace(string(branchOutput)),
|
||||||
commit: strings.TrimSpace(string(commitOutput)),
|
commit: commit,
|
||||||
status: strings.TrimSpace(string(statusOutput)),
|
status: status,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +215,7 @@ func gitOutput(ctx context.Context, repositoryDir string, args ...string) ([]byt
|
|||||||
err := process.Run(ctx, process.Invocation{
|
err := process.Run(ctx, process.Invocation{
|
||||||
Args: commandArgs,
|
Args: commandArgs,
|
||||||
Dir: repositoryDir,
|
Dir: repositoryDir,
|
||||||
|
Env: map[string]string{"GIT_NO_REPLACE_OBJECTS": "1"},
|
||||||
Stdout: &stdout,
|
Stdout: &stdout,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
})
|
})
|
||||||
@@ -233,6 +234,7 @@ func hasLocalTag(ctx context.Context, repositoryDir, tag string) (bool, error) {
|
|||||||
err := process.Run(ctx, process.Invocation{
|
err := process.Run(ctx, process.Invocation{
|
||||||
Args: []string{"git", "show-ref", "--verify", "--quiet", "refs/tags/" + tag},
|
Args: []string{"git", "show-ref", "--verify", "--quiet", "refs/tags/" + tag},
|
||||||
Dir: repositoryDir,
|
Dir: repositoryDir,
|
||||||
|
Env: map[string]string{"GIT_NO_REPLACE_OBJECTS": "1"},
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -128,8 +128,9 @@ func (reader *BlobReader) startLocked() error {
|
|||||||
Args: []string{"git", "cat-file", "--batch"},
|
Args: []string{"git", "cat-file", "--batch"},
|
||||||
Dir: reader.repositoryDir,
|
Dir: reader.repositoryDir,
|
||||||
Env: map[string]string{
|
Env: map[string]string{
|
||||||
"GIT_NO_LAZY_FETCH": "1",
|
"GIT_NO_LAZY_FETCH": "1",
|
||||||
"GIT_ALLOW_PROTOCOL": "",
|
"GIT_ALLOW_PROTOCOL": "",
|
||||||
|
"GIT_NO_REPLACE_OBJECTS": "1",
|
||||||
},
|
},
|
||||||
Stderr: &reader.stderr,
|
Stderr: &reader.stderr,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -234,6 +234,7 @@ func run(ctx context.Context, dir string, config []string, args ...string) (stri
|
|||||||
err := process.Run(ctx, process.Invocation{
|
err := process.Run(ctx, process.Invocation{
|
||||||
Args: append([]string{"git"}, commandArgs...),
|
Args: append([]string{"git"}, commandArgs...),
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
|
Env: map[string]string{"GIT_NO_REPLACE_OBJECTS": "1"},
|
||||||
Stdout: &stdout,
|
Stdout: &stdout,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func DiscoverRepositoryRoot(ctx context.Context, directory string) (string, bool
|
|||||||
var stdout, stderr strings.Builder
|
var stdout, stderr strings.Builder
|
||||||
err := process.Run(ctx, process.Invocation{
|
err := process.Run(ctx, process.Invocation{
|
||||||
Args: []string{"git", "rev-parse", "--show-toplevel"}, Dir: directory,
|
Args: []string{"git", "rev-parse", "--show-toplevel"}, Dir: directory,
|
||||||
Env: map[string]string{"LC_ALL": "C"}, Stdout: &stdout, Stderr: &stderr,
|
Env: map[string]string{"LC_ALL": "C", "GIT_NO_REPLACE_OBJECTS": "1"}, Stdout: &stdout, Stderr: &stderr,
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
root, err := normalizeRepositoryRoot(stdout.String())
|
root, err := normalizeRepositoryRoot(stdout.String())
|
||||||
@@ -199,6 +199,15 @@ func CaptureHead(ctx context.Context, repositoryDir string) (string, error) {
|
|||||||
return head, nil
|
return head, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WorktreeStatus observes repository-wide worktree and index changes.
|
||||||
|
func WorktreeStatus(ctx context.Context, repositoryDir string) (string, error) {
|
||||||
|
status, err := run(ctx, repositoryDir, nil, "status", "--porcelain=v1", "--untracked-files=all")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(status), nil
|
||||||
|
}
|
||||||
|
|
||||||
// InspectTaggedSource reads the local HEAD, worktree state, and selected tag.
|
// InspectTaggedSource reads the local HEAD, worktree state, and selected tag.
|
||||||
// Both lightweight historical tags and annotated tags are supported.
|
// Both lightweight historical tags and annotated tags are supported.
|
||||||
func InspectTaggedSource(ctx context.Context, repositoryDir, tag string) (TaggedSource, error) {
|
func InspectTaggedSource(ctx context.Context, repositoryDir, tag string) (TaggedSource, error) {
|
||||||
@@ -206,15 +215,12 @@ func InspectTaggedSource(ctx context.Context, repositoryDir, tag string) (Tagged
|
|||||||
return TaggedSource{}, err
|
return TaggedSource{}, err
|
||||||
}
|
}
|
||||||
var source TaggedSource
|
var source TaggedSource
|
||||||
head, err := run(ctx, repositoryDir, nil, "rev-parse", "--verify", "HEAD^{commit}")
|
head, err := CaptureHead(ctx, repositoryDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TaggedSource{}, fmt.Errorf("read checkout HEAD: %w", err)
|
return TaggedSource{}, err
|
||||||
}
|
}
|
||||||
source.Head = strings.TrimSpace(head)
|
source.Head = head
|
||||||
if !validObjectID(source.Head) {
|
status, err := WorktreeStatus(ctx, repositoryDir)
|
||||||
return TaggedSource{}, fmt.Errorf("Git returned an invalid checkout commit %q", source.Head)
|
|
||||||
}
|
|
||||||
status, err := run(ctx, repositoryDir, nil, "status", "--porcelain", "--untracked-files=all")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TaggedSource{}, fmt.Errorf("inspect tagged checkout state: %w", err)
|
return TaggedSource{}, fmt.Errorf("inspect tagged checkout state: %w", err)
|
||||||
}
|
}
|
||||||
@@ -411,7 +417,7 @@ func readLocalObjects(ctx context.Context, repositoryDir string, input io.Reader
|
|||||||
Args: append([]string{"git"}, args...),
|
Args: append([]string{"git"}, args...),
|
||||||
Dir: repositoryDir,
|
Dir: repositoryDir,
|
||||||
// Disable transports as well for Git versions predating lazy-fetch control.
|
// Disable transports as well for Git versions predating lazy-fetch control.
|
||||||
Env: map[string]string{"GIT_NO_LAZY_FETCH": "1", "GIT_ALLOW_PROTOCOL": ""},
|
Env: map[string]string{"GIT_NO_LAZY_FETCH": "1", "GIT_ALLOW_PROTOCOL": "", "GIT_NO_REPLACE_OBJECTS": "1"},
|
||||||
Stdin: input,
|
Stdin: input,
|
||||||
Stdout: &stdout,
|
Stdout: &stdout,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
|
|||||||
Reference in New Issue
Block a user