-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommit_prep_io.go
More file actions
99 lines (90 loc) · 3.6 KB
/
commit_prep_io.go
File metadata and controls
99 lines (90 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package repomap
// commit_prep_io.go — I/O-side helpers for the commit prep pipeline:
// shell-outs (release gate, git reset) and dc-plugin script resolution.
import (
"context"
"encoding/json"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
// Commit-prep subprocess timeouts. Var (not const) so tests may shorten them.
var (
stashGitTimeout = 30 * time.Second //nolint:gochecknoglobals // test-overridable timeout
releaseGateTimeout = 5 * time.Minute //nolint:gochecknoglobals // test-overridable timeout
)
// dcScriptPath returns the path to a dc-plugin script, honoring CLAUDE_PLUGIN_ROOT
// when set, then falling back to the dc plugin cache. The returned path is not
// stat'd — callers decide how to handle absence.
func dcScriptPath(name string) string {
if root := os.Getenv("CLAUDE_PLUGIN_ROOT"); root != "" {
p := filepath.Join(root, "scripts", name)
if _, err := os.Stat(p); err == nil {
return p
}
}
home, _ := os.UserHomeDir()
return filepath.Join(home, ".claude", "plugins", "cache", "dotcommander", "dc", "current", "scripts", name)
}
// StashArtifacts adds artifact paths to .gitignore and unstages them.
// Best-effort: I/O failures are swallowed (artifacts are advisory).
func StashArtifacts(repoRoot string, artifacts []string) {
StashArtifactsContext(context.Background(), repoRoot, artifacts)
}
// StashArtifactsContext is StashArtifacts with caller cancellation.
func StashArtifactsContext(ctx context.Context, repoRoot string, artifacts []string) {
if len(artifacts) == 0 {
return
}
giPath := filepath.Join(repoRoot, ".gitignore")
existing, _ := os.ReadFile(giPath)
existingStr := string(existing)
var additions []string
for _, a := range artifacts {
if !strings.Contains(existingStr, a) {
additions = append(additions, a)
}
}
if len(additions) > 0 {
newContent := strings.TrimRight(existingStr, "\n") + "\n" + strings.Join(additions, "\n") + "\n"
_ = atomicWriteFile(giPath, []byte(newContent), 0o644)
}
// Batch all artifacts into a single `git reset HEAD -- a b c` call.
// Bounded by stashGitTimeout — see var block.
ctx, cancel := context.WithTimeout(ctx, stashGitTimeout)
defer cancel()
args := append([]string{"-C", repoRoot, "reset", "HEAD", "--"}, artifacts...)
cmd := exec.CommandContext(ctx, "git", args...) //nolint:gosec // args are repo-internal paths, not user input
cmd.WaitDelay = 2 * time.Second
_ = cmd.Run()
}
// RunReleaseGate shells out to release-gate.sh and returns a summary.
// build_ok=true when the script exits 0 or is absent.
func RunReleaseGate(repoRoot string) *PrepReleaseGate {
return RunReleaseGateContext(context.Background(), repoRoot)
}
// RunReleaseGateContext is RunReleaseGate with caller cancellation.
func RunReleaseGateContext(ctx context.Context, repoRoot string) *PrepReleaseGate {
script := dcScriptPath("release-gate.sh")
if _, err := os.Stat(script); err != nil {
return &PrepReleaseGate{Applied: []any{}, BuildOK: true}
}
// Bounded by releaseGateTimeout — see var block. A wedged release-gate
// script (e.g. hung build) surfaces as build_ok=false rather than hanging.
ctx, cancel := context.WithTimeout(ctx, releaseGateTimeout)
defer cancel()
cmd := exec.CommandContext(ctx, "bash", script, "--path", repoRoot, "--no-toolchain") //nolint:gosec // script path resolved via dcScriptPath, not user input
cmd.Dir = repoRoot
cmd.WaitDelay = 5 * time.Second
out, err := cmd.Output()
gate := &PrepReleaseGate{Applied: []any{}, BuildOK: err == nil}
var result struct {
Applied []any `json:"applied"`
}
if json.Unmarshal(out, &result) == nil && result.Applied != nil {
gate.Applied = result.Applied
}
return gate
}