Commit 1c94e4e

Eric Bower  ·  2026-08-09 11:16:59 -0400 EDT
parent 4fdd561
chore: checksum for local runs
3 files changed,  +58, -8
+52, -7
 1@@ -424,10 +424,11 @@ type Workspace interface {
 2 }
 3 
 4 type WorkspaceRsync struct {
 5-	Cfg    *Cfg
 6-	Logger *slog.Logger
 7-	Source string
 8-	Dest   string
 9+	Cfg      *Cfg
10+	Logger   *slog.Logger
11+	Source   string
12+	Dest     string
13+	checksum string
14 }
15 
16 func (w *WorkspaceRsync) Setup() error {
17@@ -451,7 +452,18 @@ func (w *WorkspaceRsync) Setup() error {
18 	} else {
19 		cmd = exec.Command("rsync", "-rv", "--exclude=/.git", "--exclude=/.jj", w.Source+"/", w.Dest+"/")
20 	}
21-	return runCmd(cmd, log)
22+	if err := runCmd(cmd, log); err != nil {
23+		return err
24+	}
25+
26+	cs, err := computeDirChecksum(w.Dest)
27+	if err != nil {
28+		log.Error("compute workspace checksum", "err", err)
29+	} else {
30+		w.checksum = cs
31+		log.Debug("workspace checksum", "checksum", w.checksum)
32+	}
33+	return nil
34 }
35 
36 func (w *WorkspaceRsync) Cleanup() error {
37@@ -466,7 +478,40 @@ func (w *WorkspaceRsync) GetDir() string {
38 }
39 
40 func (w *WorkspaceRsync) Checksum() string {
41-	return ""
42+	return w.checksum
43+}
44+
45+func computeDirChecksum(dir string) (string, error) {
46+	hasher := sha256.New()
47+	err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
48+		if err != nil {
49+			return err
50+		}
51+		if d.IsDir() {
52+			return nil
53+		}
54+		rel, err := filepath.Rel(dir, path)
55+		if err != nil {
56+			return err
57+		}
58+		hasher.Write([]byte(rel))
59+		hasher.Write([]byte{0})
60+
61+		f, err := os.Open(path)
62+		if err != nil {
63+			return err
64+		}
65+		_, copyErr := io.Copy(hasher, f)
66+		_ = f.Close()
67+		if copyErr != nil {
68+			return copyErr
69+		}
70+		return nil
71+	})
72+	if err != nil {
73+		return "", err
74+	}
75+	return fmt.Sprintf("sha256:%x", hasher.Sum(nil)), nil
76 }
77 
78 type WorkspaceTar struct {
79@@ -700,7 +745,7 @@ func eventHandler(cfg *Cfg, eventData *Event) error {
80 	if eventData.Type != "" {
81 		domain = eventData.Type
82 	}
83-	fmt.Fprintf(os.Stdout, "🚀 starting job %s.%s.%s\n", domain, eventData.Name, jobID) //nolint:errcheck
84+	fmt.Fprintf(os.Stdout, "🚀 starting job %s.%s.%s\n", domain, eventData.Name, jobID)                                      //nolint:errcheck
85 	fmt.Fprintf(os.Stdout, "   event: type=%s name=%s workspace=%s\n", eventData.Type, eventData.Name, eventData.Workspace) //nolint:errcheck
86 	fmt.Fprintf(os.Stdout, "   %s\n", string(eventBytes))                                                                   //nolint:errcheck
87 
+5, -1
 1@@ -851,5 +851,9 @@ func TestWorkspaceRsync_ExcludesGitAndJJ(t *testing.T) {
 2 	if _, err := os.Stat(filepath.Join(destDir, ".jj")); !os.IsNotExist(err) {
 3 		t.Error("expected .jj to be excluded from dest workspace")
 4 	}
 5-}
 6 
 7+	checksum := wk.Checksum()
 8+	if !strings.HasPrefix(checksum, "sha256:") {
 9+		t.Errorf("expected checksum to start with 'sha256:', got %q", checksum)
10+	}
11+}
+1, -0
1@@ -8,6 +8,7 @@ EVENT_TYPE="${PICI_EVENT:-manual}"
2 
3 printf "\x1b[33m[%s:%s] running ci (event=%s)\x1b[0m\n" "$REPO" "$JOB_ID" "$EVENT_TYPE"
4 
5+zmx run fmt -d docker run -t --rm -v "$(pwd):/app" -w /app golang:1.26 bash -c 'files=$(gofmt -l .); if [ -n "$files" ]; then echo "unformatted files:"; echo "$files"; exit 1; fi'
6 zmx run lint -d docker run -t --rm -v "$(pwd):/app" -w /app golangci/golangci-lint:v2.11.4 golangci-lint run
7 zmx run test -d docker run -t --rm -v "$(pwd):/app" -w /app golang:1.26 go test ./...
8 zmx wait "*"