# Store Checkpoint Policy in a Git Ref

`24fc4b2`·

pfleidi·3w ago·2 files·+210 added/-0 removed

Read and write checkpoint policy commits at refs/entire/policies/checkpoint using the existing checkpoint commit signing path.

## Sessions

6d613e0a7f30 View transcript

## Changes

2

- cmd/entire/cli/checkpointpolicy

- Astore.go+116

- Astore_test.go+94

```go
package checkpointpolicy

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"

"github.com/entireio/cli/cmd/entire/cli/checkpoint"
	"github.com/entireio/cli/cmd/entire/cli/jsonutil"
	"github.com/go-git/go-git/v6"
	"github.com/go-git/go-git/v6/plumbing"
	"github.com/go-git/go-git/v6/plumbing/filemode"
	"github.com/go-git/go-git/v6/plumbing/object"
)

const PolicyFileName = "policy.json"

const RefName = plumbing.ReferenceName("refs/entire/policies/checkpoint")

type Source string

const (
	SourceDefaults      Source = "defaults"
	SourceLocal         Source = "local"
	SourceRemote        Source = "remote"
	SourceLocalDiverged Source = "local-diverged"
)

type State struct {
	Policy     Policy
	Source     Source
	Hash       plumbing.Hash
	RemoteHash plumbing.Hash
	Warning    string
}

func ReadLocal(ctx context.Context, repo *git.Repository) (State, error) {
	ref, err := repo.Reference(RefName, true)
	if err != nil {
		if errors.Is(err, plumbing.ErrReferenceNotFound) {
			return State{Policy: DefaultPolicy(), Source: SourceDefaults}, nil
		}
		return State{}, fmt.Errorf("read checkpoint policy ref: %w", err)
	}
	return readFromHash(ctx, repo, ref.Hash(), SourceLocal)
}

func ReadFromRef(ctx context.Context, repo *git.Repository, refName plumbing.ReferenceName, source Source) (State, error) {
	ref, err := repo.Reference(refName, true)
	if err != nil {
		return State{}, fmt.Errorf("read checkpoint policy ref %s: %w", refName, err)
	}
	return readFromHash(ctx, repo, ref.Hash(), source)
}

func WriteLocal(ctx context.Context, repo *git.Repository, parent plumbing.Hash, policy Policy) (plumbing.Hash, error) {
	policy = Normalize(policy)
	data, err := jsonutil.MarshalIndentWithNewline(policy, "", "  ")
	if err != nil {
		return plumbing.ZeroHash, fmt.Errorf("marshal checkpoint policy: %w", err)
	}
	blobHash, err := checkpoint.CreateBlobFromContent(repo, data)
	if err != nil {
		return plumbing.ZeroHash, err
	}
	treeHash, err := checkpoint.BuildTreeFromEntries(ctx, repo, map[string]object.TreeEntry{
		PolicyFileName: {Name: PolicyFileName, Mode: filemode.Regular, Hash: blobHash},
	})
	if err != nil {
		return plumbing.ZeroHash, fmt.Errorf("build checkpoint policy tree: %w", err)
	}
	authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo)
	commitHash, err := checkpoint.CreateCommit(ctx, repo, treeHash, parent, "Update checkpoint policy", authorName, authorEmail)
	if err != nil {
		return plumbing.ZeroHash, err
	}
	if err := SetRef(repo, RefName, commitHash); err != nil {
		return plumbing.ZeroHash, err
	}
	return commitHash, nil
}

func SetRef(repo *git.Repository, ref plumbing.ReferenceName, hash plumbing.Hash) error {
	if err := repo.Storer.SetReference(plumbing.NewHashReference(ref, hash)); err != nil {
		return fmt.Errorf("set checkpoint policy ref %s: %w", ref, err)
	}
	return nil
}

func readFromHash(ctx context.Context, repo *git.Repository, hash plumbing.Hash, source Source) (State, error) {
	if err := ctx.Err(); err != nil {
		return State{}, err
	}
	commit, err := repo.CommitObject(hash)
	if err != nil {
		return State{}, fmt.Errorf("read checkpoint policy commit: %w", err)
	}
	tree, err := commit.Tree()
	if err != nil {
		return State{}, fmt.Errorf("read checkpoint policy tree: %w", err)
	}
	file, err := tree.File(PolicyFileName)
	if err != nil {
		return State{}, fmt.Errorf("read %s: %w", PolicyFileName, err)
	}
	content, err := file.Contents()
	if err != nil {
		return State{}, fmt.Errorf("read %s contents: %w", PolicyFileName, err)
	}
	var policy Policy
	if err := json.Unmarshal([]byte(content), &policy); err != nil {
		return State{}, fmt.Errorf("parse %s: %w", PolicyFileName, err)
	}
	return State{Policy: Normalize(policy), Source: source, Hash: hash}, nil
}
```

```go
package checkpointpolicy_test

import (
	"context"
	"encoding/json"
	"testing"

"github.com/entireio/cli/cmd/entire/cli/checkpoint"
	"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
	"github.com/entireio/cli/cmd/entire/cli/testutil"
	"github.com/go-git/go-git/v6"
	"github.com/go-git/go-git/v6/plumbing"
	"github.com/go-git/go-git/v6/plumbing/filemode"
	"github.com/go-git/go-git/v6/plumbing/object"
	"github.com/stretchr/testify/require"
)

func TestReadLocalPolicyDefaultsWhenRefMissing(t *testing.T) {
	t.Parallel()
	repo := initPolicyRepo(t)
	got, err := checkpointpolicy.ReadLocal(t.Context(), repo)
	require.NoError(t, err)
	require.Equal(t, checkpointpolicy.SourceDefaults, got.Source)
	require.Equal(t, checkpointpolicy.DefaultPolicy(), got.Policy)
	require.True(t, got.Hash.IsZero())
}

func TestWriteAndReadLocalPolicy(t *testing.T) {
	t.Parallel()
	repo := initPolicyRepo(t)
	policy := checkpointpolicy.Policy{
		CheckpointVersion:    checkpoint.CheckpointVersionBranchV1,
		CheckpointMinVersion: checkpoint.CheckpointVersionBranchV1,
	}
	hash, err := checkpointpolicy.WriteLocal(t.Context(), repo, plumbing.ZeroHash, policy)
	require.NoError(t, err)
	require.False(t, hash.IsZero())

got, err := checkpointpolicy.ReadLocal(t.Context(), repo)
	require.NoError(t, err)
	require.Equal(t, checkpointpolicy.SourceLocal, got.Source)
	require.Equal(t, hash, got.Hash)
	require.Equal(t, policy, got.Policy)
}

func TestReadLocalPolicyRejectsMalformedJSON(t *testing.T) {
	t.Parallel()
	repo := initPolicyRepo(t)
	writeRawPolicyCommit(t, repo, []byte(`{"checkpoint_version":`), plumbing.ZeroHash)

_, err := checkpointpolicy.ReadLocal(t.Context(), repo)
	require.ErrorContains(t, err, "parse policy.json")
}

func TestReadLocalPolicyAllowsUnsupportedPolicy(t *testing.T) {
	t.Parallel()
	repo := initPolicyRepo(t)
	policy := checkpointpolicy.Policy{
		CheckpointVersion:    "refs-v1",
		CheckpointMinVersion: "refs-v1",
	}
	data, err := json.Marshal(policy)
	require.NoError(t, err)
	hash := writeRawPolicyCommit(t, repo, data, plumbing.ZeroHash)

func initPolicyRepo(t *testing.T) *git.Repository {
	t.Helper()
	dir := t.TempDir()
	testutil.InitRepo(t, dir)
	repo, err := git.PlainOpen(dir)
	require.NoError(t, err)
	return repo
}

func writeRawPolicyCommit(t *testing.T, repo *git.Repository, data []byte, parent plumbing.Hash) plumbing.Hash {
	t.Helper()
	blobHash, err := checkpoint.CreateBlobFromContent(repo, data)
	require.NoError(t, err)
	treeHash, err := checkpoint.BuildTreeFromEntries(context.Background(), repo, map[string]object.TreeEntry{
		checkpointpolicy.PolicyFileName: {Name: checkpointpolicy.PolicyFileName, Mode: filemode.Regular, Hash: blobHash},
	})
	require.NoError(t, err)
	commitHash, err := checkpoint.CreateCommit(context.Background(), repo, treeHash, parent, "raw policy", "Test", "test@example.com")
	require.NoError(t, err)
	require.NoError(t, checkpointpolicy.SetRef(repo, checkpointpolicy.RefName, commitHash))
	return commitHash
}
```
