package runner
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
)
// ZitCheckout clones the repository once per workspace and moves it to
// each check-in after that, the same refresh the forge's own worker
// does: pull, discard anything the last task left, update.
type ZitCheckout struct {
Zit string // the zit binary
URL string // the repository's URL on the forge
User string // for a private repository: the account whose sync token is in ZIT_PASSWORD
// Password is that token, for the one thing fetched without zit: a
// premerge pair, which is a tree the forge merges, not a check-in a
// clone could update to.
Password string
}
func (z ZitCheckout) run(ctx context.Context, dir string, args ...string) error {
cmd := exec.CommandContext(ctx, z.Zit, args...)
cmd.Dir = dir
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("zit %s: %v: %s", args[0], err, out)
}
return nil
}
// Checkout puts checkin's tree in dir. A premerge pair has no check-in
// to update to, so its merged tree comes from the forge's archive; the
// workspace is then a tree, and the next check-in reclones.
func (z ZitCheckout) Checkout(ctx context.Context, dir, checkin string) error {
if IsPair(checkin) {
return ArchiveCheckout{URL: z.URL, User: z.User, Password: z.Password}.Checkout(ctx, dir, checkin)
}
auth := []string{}
if z.User != "" {
auth = []string{"--user", z.User}
}
if _, err := os.Stat(filepath.Join(dir, ".zit")); err == nil {
if err := z.run(ctx, dir, append([]string{"pull", z.URL}, auth...)...); err == nil {
_ = z.run(ctx, dir, "revert")
_ = z.run(ctx, dir, "clean")
if err := z.run(ctx, dir, "update", checkin); err == nil {
return nil
}
}
os.RemoveAll(dir) // a workspace that will not refresh is recloned
}
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
if err := z.run(ctx, dir, append([]string{"clone", z.URL, "."}, auth...)...); err != nil {
return err
}
if err := z.run(ctx, dir, "update", checkin); err != nil {
return z.run(ctx, dir, "open", checkin)
}
return nil
}