snapshot.go
1package engine
2
3import (
4 "archive/tar"
5 "compress/gzip"
6 "io"
7 "io/fs"
8 "os"
9 "path/filepath"
10)
11
12// Snapshot writes a tar.gz of what describes a run: state.json,
13// events.ndjson, manifest.yml, state/, results/ and artifacts/ (not its
14// checkouts). `ci run --report` uploads it; the runner imports it.
15func Snapshot(d Dir, w io.Writer) error {
16 zw := gzip.NewWriter(w)
17 tw := tar.NewWriter(zw)
18 for _, rel := range []string{"state.json", "events.ndjson", "manifest.yml", "state", "results", "artifacts"} {
19 root := filepath.Join(d.Path(), rel)
20 err := filepath.WalkDir(root, func(p string, e fs.DirEntry, err error) error {
21 if err != nil {
22 if os.IsNotExist(err) {
23 return nil
24 }
25 return err
26 }
27 if !e.Type().IsRegular() {
28 return nil
29 }
30 name, err := filepath.Rel(d.Path(), p)
31 if err != nil {
32 return err
33 }
34 fi, err := e.Info()
35 if err != nil {
36 return err
37 }
38 if err := tw.WriteHeader(&tar.Header{Name: filepath.ToSlash(name), Mode: 0o644, Size: fi.Size(), ModTime: fi.ModTime()}); err != nil {
39 return err
40 }
41 f, err := os.Open(p)
42 if err != nil {
43 return err
44 }
45 defer f.Close()
46 _, err = io.Copy(tw, f)
47 return err
48 })
49 if err != nil {
50 return err
51 }
52 }
53 if err := tw.Close(); err != nil {
54 return err
55 }
56 return zw.Close()
57}