Gate migrated-ref push on the checkpoint policy · Entire

Gate migrated-ref push on the checkpoint policy

36563bc→main· pfleidi·1w ago·2 files·+86 added/-1 removed

PushMigratedCheckpointRefs flushed the queue without the policy check both pre-push paths enforce, so a policy that blocks checkpoint pushes (diverged or requiring a newer CLI) could be bypassed via the doctor's opt-in push. Apply the same sync-and-gate, returning an error with the refs left queued.

Cover the push entry point: success returns the pushed count and clears the queue; a blocked policy and a failed push both leave refs queued.

Sessions

0c43e61ea113View transcript

Changes

2

// checkpoint remote, surfacing errors (unlike the fail-soft pre-push path). It is
// the opt-in "push now" invoked by the checkpoint migration command; the caller
// owns the repo. Returns the number of refs pushed — a no-op (0, nil) when
// pushing is disabled or the queue is empty.
// pushing is disabled or the queue is empty. Like the pre-push paths, a
// checkpoint policy that blocks pushing errors with the refs left queued.
func PushMigratedCheckpointRefs(ctx context.Context, repo *git.Repository, remote string) (int, error) {
    ps := resolvePushSettings(ctx, remote)
    if ps.pushDisabled {
        return 0, nil
    }
    syncCheckpointPolicyForPrePush(ctx, repo, ps)
    if !checkpointPolicyAllowsGitHook(ctx, repo) {
        return 0, errors.New("checkpoint policy does not allow pushing checkpoint refs; refs stay queued")
    }
    return flushCheckpointRefsQueue(ctx, repo, ps.pushTarget())
}
import (
    "context"
    "os/exec"
    "path/filepath"
    "strings"
    "testing"

"github.com/entireio/cli/cmd/entire/cli/checkpoint"
    "github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
    "github.com/entireio/cli/cmd/entire/cli/paths"
    "github.com/entireio/cli/cmd/entire/cli/testutil"
)

// enqueueRefs seeds the repo's push queue with the given refs.
func enqueueRefs(t *testing.T, repo *git.Repository, refs []plumbing.ReferenceName) *checkpoint.PushQueue {
    t.Helper()
    queue, err := checkpoint.PushQueueForRepo(context.Background(), repo)
    require.NoError(t, err)
    for _, ref := range refs {
        require.NoError(t, queue.Enqueue(ref))
    }
    return queue
}

func TestPushMigratedCheckpointRefs(t *testing.T) {
    workDir, bareDir, refs := setupRepoWithCheckpointRefs(t)
    t.Chdir(workDir)
    paths.ClearWorktreeRootCache()

repo, err := git.PlainOpen(workDir)
    require.NoError(t, err)
    queue := enqueueRefs(t, repo, refs)

pushed, err := PushMigratedCheckpointRefs(context.Background(), repo, bareDir)
    require.NoError(t, err)
    assert.Equal(t, len(refs), pushed)

for _, ref := range refs {
        assert.NotEmpty(t, remoteRefHash(t, bareDir, ref), "ref should be on the remote")
    }
    remaining, err := queue.Drain()
    require.NoError(t, err)
    assert.Empty(t, remaining, "pushed refs are removed from the queue")
}

func TestPushMigratedCheckpointRefs_PolicyBlocked(t *testing.T) {
    workDir, bareDir, refs := setupRepoWithCheckpointRefs(t)
    t.Chdir(workDir)
    paths.ClearWorktreeRootCache()

repo, err := git.PlainOpen(workDir)
    require.NoError(t, err)
    writeUnsupportedCheckpointPolicy(t, repo)
    queue := enqueueRefs(t, repo, refs)

pushed, err := PushMigratedCheckpointRefs(context.Background(), repo, bareDir)
    require.ErrorContains(t, err, "checkpoint policy")
    assert.Equal(t, 0, pushed)

remaining, err := queue.Drain()
    require.NoError(t, err)
    assert.ElementsMatch(t, refs, remaining, "blocked push leaves refs queued")

lsCmd := exec.CommandContext(context.Background(), "git", "ls-remote", bareDir)
    lsCmd.Env = testutil.GitIsolatedEnv()
    out, err := lsCmd.CombinedOutput()
    require.NoError(t, err, "ls-remote failed: %s", out)
    for _, ref := range refs {
        assert.NotContains(t, string(out), ref.String(), "blocked push must not reach the remote")
    }
}

func TestPushMigratedCheckpointRefs_FailureLeavesRefsQueued(t *testing.T) {
    workDir, _, refs := setupRepoWithCheckpointRefs(t)
    t.Chdir(workDir)
    paths.ClearWorktreeRootCache()

repo, err := git.PlainOpen(workDir)
    require.NoError(t, err)
    queue := enqueueRefs(t, repo, refs)

badTarget := filepath.Join(t.TempDir(), "missing.git")
    pushed, err := PushMigratedCheckpointRefs(context.Background(), repo, badTarget)
    require.ErrorContains(t, err, "failed to push")
    assert.Equal(t, 0, pushed)

remaining, err := queue.Drain()
    require.NoError(t, err)
    assert.ElementsMatch(t, refs, remaining, "failed push leaves refs queued")
}

// remoteRefFiles lists the files in the tree a ref points at on the bare remote.
func remoteRefFiles(t *testing.T, bareDir string, ref plumbing.ReferenceName) string {
    t.Helper()
}`