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}