Skip to content
quarry Artifact [69378cfb8f]
Login

Artifact [69378cfb8f]

Artifact 69378cfb8f0946fa7011cf203145a82ede84292ede642c4bbc8486f00ff61f3c:


package zitcli

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"strings"

	"craftdesign.group/quarry/internal/lease"
)

// fossilPushArgs is the whole command line of a Fossil mirror's push.
// The password is not in it: argv is readable by every process on the
// machine, so it travels in ZIT_PASSWORD (see FossilPush).
//
// --once authenticates this push and saves nothing: without it zit
// keeps the derived login in the store and a later pull or push to the
// same remote reuses it, a credential nobody here can see or revoke.
//
// resolve pins the address zit connects to, one "HOST:PORT:ADDR" per
// entry (zit's --resolve): with any given zit never looks a name up, so
// the address the caller vetted is the one dialled, and a redirect to
// another host or port is refused by zit itself.
func fossilPushArgs(remote, user string, resolve []string) []string {
	args := []string{"push", remote, "--user", user, "--once"}
	for _, r := range resolve {
		args = append(args, "--resolve", r)
	}
	return args
}

// FossilPush pushes everything the store holds (check-ins, wiki,
// tickets, forum, every branch) to the Fossil server at remote over
// Fossil's sync protocol, logging in as user, connecting to the addresses resolve pins, with env added to the
// child's environment: the caller puts ZIT_PASSWORD there. It answers
// zit's output either way, because on a refused push that output is the
// only account of why. Fossil sync only adds, so nothing on the remote
// is ever discarded.
//
// Runs in the store directory: zit push, like pull, refuses -R. Under
// the store's lease like the other unattended writes, so a node that
// loses the lease stops pushing; ctx bounds it.
func (c *Client) FossilPush(tok lease.Token, ctx context.Context, remote, user string, resolve, env []string) (string, error) {
	if err := tok.Err(); err != nil {
		return "", err
	}
	ctx, stop := underLease(ctx, tok)
	defer stop()
	args := fossilPushArgs(remote, user, resolve)
	if err := checkArgs(args); err != nil {
		return "", err
	}
	cmd, done := spawnUnattended(ctx, repoName(c.repo), "fossil-mirror", args...)
	defer done()
	if cmd.Err != nil {
		return "", cmd.Err
	}
	cmd.Dir = c.repo
	cmd.Env = append(os.Environ(), env...)
	OwnGroup(cmd)
	cmd.WaitDelay = waitDelay
	var out bytes.Buffer
	cmd.Stdout, cmd.Stderr = &out, &out
	if err := cmd.Run(); err != nil {
		return strings.TrimSpace(out.String()), fmt.Errorf("zit push: %w", err)
	}
	return strings.TrimSpace(out.String()), nil
}