gittuf/pkg/gitstore/gitstore.go
Paulo Gomes 9e269f46fb
Introduce gitstore.Storer and make gittuf's core dependency-light
Restructure storage so gittuf verification can run over backends other
than the git binary (e.g. go-git):

- pkg/githash: concrete Git object hash, stdlib-only.
  gitinterface.Hash aliases it.
- pkg/gitstore: the single Storer interface (24 methods) that all
  storage consumers program against, plus the shared
  ErrReferenceNotFound sentinel. *gitinterface.Repository satisfies it
  structurally (compile-time asserted). Also defines ConfigKey, the
  canonical type for the Git config settings gittuf reads.
- pkg/rsl (from internal/rsl): entry model, codec, and readers over
  gitstore.Storer; zero gitinterface/sigstore dependencies. rsl.Hash
  aliases githash.Hash; nil is the unset-Hash sentinel and IsZero
  matches nil and empty as well as both format zeros (no
  object-format-unaware ZeroHash). Entry commits (empty tree on the
  RSL ref) are owned by the package; no storer adapter.
- internal/signerverifier/gitobject: verifies commit/tag signatures
  over (payload, signature) bytes, Rekor URL as an option. The storage
  half is Repository.GetObjectSignature. Removes sigstore, cosign, and
  gitsign from gitinterface's dependency tree.
- internal/propagation: propagation workflow, moved off pkg/rsl's
  public API (its tuf directive types are internal).
- internal/{attestations,cache,policy}: storage via gitstore.Storer;
  tree writing via WriteTree(blobs, subtrees).

Breaking changes to pkg/gitinterface: Repository.VerifySignature and
the verification sentinels are removed (use gitobject.Verify);
ErrReferenceNotFound now aliases gitstore's. Repository.GetGitConfig
(which returned the whole config map) is replaced by
LookupConfig(gitstore.ConfigKey), returning a single setting's value.
Policy resolves the Rekor override from git config once per
verification and extracts signed payloads once per object instead of
per key attempt.

Assisted-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Paulo Gomes <paulo@entire.io>
2026-08-03 21:27:49 +01:00

116 lines
5.1 KiB
Go

// Copyright The gittuf Authors
// SPDX-License-Identifier: Apache-2.0
// Package gitstore defines the Git storage interface gittuf's packages
// consume. It depends only on the standard library and pkg/githash, so
// dependency-light packages (rsl, and eventually policy) can name their
// storage without importing gitinterface's signing/attestation stack.
// *gitinterface.Repository satisfies Storer structurally. Other backends
// (e.g. go-git) can implement it directly.
package gitstore
import (
"errors"
"github.com/gittuf/gittuf/pkg/githash"
)
// ErrReferenceNotFound must be returned (via errors.Is) by a Storer's
// GetReference when the requested reference does not exist.
// gitinterface.ErrReferenceNotFound is an alias of this sentinel.
var ErrReferenceNotFound = errors.New("requested Git reference not found")
// Storer is the union of Git storage operations gittuf's consumers need.
type Storer interface {
// GetReference returns the tip of the specified reference, or an error
// matching ErrReferenceNotFound if the reference does not exist.
GetReference(refName string) (githash.Hash, error)
// SetReference sets the specified reference to the provided Git ID.
SetReference(refName string, gitID githash.Hash) error
// DeleteReference deletes the specified reference.
DeleteReference(refName string) error
// ReadBlob returns the contents of the specified blob.
ReadBlob(blobID githash.Hash) ([]byte, error)
// WriteBlob writes the contents as a blob and returns its ID.
WriteBlob(contents []byte) (githash.Hash, error)
// EmptyTree returns the ID of the empty tree.
EmptyTree() (githash.Hash, error)
// WriteTree writes a tree from the given entries and returns its ID.
// Intermediate trees implied by "/" in an entry's path are created
// automatically. Order is irrelevant to the result. It returns
// ErrDuplicateTreePath if two entries share a path.
WriteTree(entries []TreeEntry) (githash.Hash, error)
// GetAllFilesInTree returns the recursively flattened path → blobID
// mapping of the specified tree.
GetAllFilesInTree(treeID githash.Hash) (map[string]githash.Hash, error)
// GetEntriesInTree returns the immediate entries of the specified tree
// (non-recursive), each carrying its name, ID, and kind.
GetEntriesInTree(treeID githash.Hash) ([]TreeEntry, error)
// GetPathIDInTree returns the ID of the object at the specified
// slash-separated path within the specified tree.
GetPathIDInTree(treeID githash.Hash, treePath string) (githash.Hash, error)
// GetCommitTreeID returns the ID of the specified commit's tree.
GetCommitTreeID(commitID githash.Hash) (githash.Hash, error)
// GetCommitMessage returns the specified commit's message.
GetCommitMessage(commitID githash.Hash) (string, error)
// GetCommitParentIDs returns the IDs of the specified commit's parents.
GetCommitParentIDs(commitID githash.Hash) ([]githash.Hash, error)
// GetCommitsBetweenRange returns the commits reachable from commitNewID
// but not from commitOldID. A zero commitOldID (nil, empty, or the
// all-zeroes hash of either object format) means no lower bound, and
// all commits reachable from commitNewID are returned.
GetCommitsBetweenRange(commitNewID, commitOldID githash.Hash) ([]githash.Hash, error)
// GetFilePathsChangedByCommit returns the paths changed by the specified
// commit relative to its parents.
GetFilePathsChangedByCommit(commitID githash.Hash) ([]string, error)
// KnowsCommit reports whether ancestorID is an ancestor of commitID.
KnowsCommit(commitID, ancestorID githash.Hash) (bool, error)
// GetMergeTree returns the tree resulting from merging the two commits.
// This is the hardest method for backends not built on the git binary.
GetMergeTree(commitAID, commitBID githash.Hash) (githash.Hash, error)
// GetTagTarget returns the ID of the object the specified tag points to.
GetTagTarget(tagID githash.Hash) (githash.Hash, error)
// GetObjectSignature returns the signed payload and detached signature
// of the specified commit or tag. The signature is empty when the object
// is unsigned.
GetObjectSignature(objectID githash.Hash) ([]byte, []byte, error)
// Commit commits the specified tree to targetRef, signing it per the
// store's configuration when sign is true, and returns the commit ID.
Commit(treeID githash.Hash, targetRef, message string, sign bool) (githash.Hash, error)
// CommitUsingSpecificKey is Commit signing with the provided PEM encoded
// key. Intended for gittuf's developer mode and tests.
CommitUsingSpecificKey(treeID githash.Hash, targetRef, message string, signingKeyPEMBytes []byte) (githash.Hash, error)
// ZeroHash returns the all-zeroes hash matching the store's object
// format.
ZeroHash() githash.Hash
// LookupConfig returns the value of a single Git config setting. ok is
// false when the key is not set (distinct from set-but-empty, which
// returns "" with ok true).
LookupConfig(key ConfigKey) (value string, ok bool, err error)
// ResetDueToError force-resets the specified reference to commitID and
// returns cause, wrapped if the reset itself fails.
ResetDueToError(cause error, refName string, commitID githash.Hash) error
}