Commit 9772fc9
Eric Bower
·
2026-05-08 15:14:26 -0400 EDT
parent ab5ea95
feat: attestation
1 files changed,
+53,
-4
M
main.go
M
main.go
+53,
-4
1@@ -17,6 +17,7 @@ import (
2 "os/exec"
3 "os/signal"
4 "path/filepath"
5+ "runtime"
6 "strconv"
7 "strings"
8 "syscall"
9@@ -313,6 +314,9 @@ type Workspace interface {
10 Setup() error
11 Cleanup() error
12 GetDir() string
13+ // Checksum returns a content hash of the workspace (e.g. sha256 of the tarball).
14+ // Returns empty string if not available.
15+ Checksum() string
16 }
17
18 type WorkspaceRsync struct {
19@@ -355,11 +359,16 @@ func (w *WorkspaceRsync) GetDir() string {
20 return w.Dest
21 }
22
23+func (w *WorkspaceRsync) Checksum() string {
24+ return ""
25+}
26+
27 type WorkspaceTar struct {
28- Cfg *Cfg
29- Logger *slog.Logger
30- Source string // e.g. "pgs.sh:/private-ci/workspaces/repo_abc123.tar"
31- Dest string
32+ Cfg *Cfg
33+ Logger *slog.Logger
34+ Source string // e.g. "pgs.sh:/private-ci/workspaces/repo_abc123.tar"
35+ Dest string
36+ checksum string
37 }
38
39 func (w *WorkspaceTar) Setup() error {
40@@ -383,6 +392,14 @@ func (w *WorkspaceTar) Setup() error {
41 return fmt.Errorf("rsync download: %w", err)
42 }
43
44+ // Compute sha256 checksum of the tarball
45+ tarData, err := os.ReadFile(tarPath)
46+ if err != nil {
47+ return fmt.Errorf("read tar for checksum: %w", err)
48+ }
49+ w.checksum = fmt.Sprintf("sha256:%x", sha256.Sum256(tarData))
50+ log.Debug("workspace checksum", "checksum", w.checksum)
51+
52 // Open the tar file for extraction
53 f, err := os.Open(tarPath)
54 if err != nil {
55@@ -436,6 +453,10 @@ func (w *WorkspaceTar) Setup() error {
56 return nil
57 }
58
59+func (w *WorkspaceTar) Checksum() string {
60+ return w.checksum
61+}
62+
63 func (w *WorkspaceTar) Cleanup() error {
64 return nil
65 }
66@@ -586,6 +607,26 @@ func eventHandler(cfg *Cfg, eventData *Event) error {
67 }
68 }
69
70+ // Write attestation.json with runner/workspace provenance
71+ hostname, _ := os.Hostname()
72+ attestation := map[string]interface{}{
73+ "runner": map[string]string{
74+ "hostname": hostname,
75+ "os": runtimeOS(),
76+ "arch": runtimeArch(),
77+ },
78+ "provenance": map[string]string{
79+ "repo": eventData.Name,
80+ "branch": eventData.Branch,
81+ "commit": eventData.Commit,
82+ },
83+ "workspace_checksum": eng.Wk.Checksum(),
84+ }
85+ attestationBytes, _ := json.Marshal(attestation)
86+ if err := os.WriteFile(filepath.Join(eventDir, "attestation.json"), attestationBytes, 0644); err != nil {
87+ log.Error("write attestation", "err", err)
88+ }
89+
90 manifest, err := eng.FindManifest()
91 if err != nil {
92 fmt.Fprintf(os.Stdout, "❌ %s\n\n", err) //nolint:errcheck
93@@ -1293,6 +1334,14 @@ func generateJobID(name, workspace string) string {
94 return jobIDFor(name, workspace, time.Now().UnixNano())
95 }
96
97+func runtimeOS() string {
98+ return runtime.GOOS
99+}
100+
101+func runtimeArch() string {
102+ return runtime.GOARCH
103+}
104+
105 func jobIDFor(name, workspace string, ts int64) string {
106 h := sha256.Sum256([]byte(name + workspace + fmt.Sprintf("%d", ts)))
107 return fmt.Sprintf("%x", h[:4])