Documentation
¶
Index ¶
- Constants
- type Observation
- type Provider
- func (p *Provider) Checkout(repo *Resource, ref string) (*Resource, error)
- func (p *Provider) Clone(activationRecord *op.ActivationRecord, repository string, directory string, ...) (*Resource, *Receipt, error)
- func (p *Provider) CompensateClone(activationRecord *op.ActivationRecord, receipt *Receipt) error
- func (p *Provider) Observe(repo *Resource) (*Observation, error)
- func (p *Provider) Pull(repo *Resource) (*Resource, error)
- type Receipt
- type Remote
- type Resource
- func (r *Resource) Addressing() op.AddressingMode
- func (*Resource) CanConvertFrom(source reflect.Type) bool
- func (*Resource) ConvertFrom(value any) (any, error)
- func (r *Resource) Digest() (op.Digest, error)
- func (r *Resource) Equal(other any) bool
- func (r *Resource) Etag() (string, error)
- func (r *Resource) String() string
- func (r *Resource) UnmarshalJSON(data []byte) error
- func (r *Resource) UnmarshalText(text []byte) error
- func (r *Resource) UnmarshalYAML(unmarshal func(any) error) error
Constants ¶
const ( Checkout op.ActionName = "git.checkout" Clone op.ActionName = "git.clone" Observe op.ActionName = "git.observe" Pull op.ActionName = "git.pull" )
Action-name constants for the git provider's plan-mode actions.
Each constant is the short dotted action label its method dispatches under. Pass these to plan.Plan, op.ReceiverRegistry().BuildAction, RuntimeEnvironment.ActionByName, or WithActionNamed in place of a string literal so a typo is a compile error and rename / find-references work through the constant.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Observation ¶
type Observation struct {
op.ObservationBase
// ObservedHEAD is the commit SHA the on-disk clone currently points at. May differ from the
// observed [Resource]'s `HEAD` (which is plan-time intent).
ObservedHEAD string
// ObservedRef is the branch / tag / ref name the on-disk clone is positioned at. May differ
// from the observed [Resource]'s `Ref` (which is plan-time intent).
ObservedRef string
// Bare reports whether the on-disk repository is bare (no working tree).
Bare bool
// Dirty reports whether the working tree had uncommitted changes at observation time. Always
// false for bare repositories.
Dirty bool
// Remotes maps remote name (e.g., `origin`) to the fetch / push URL pair recorded in
// `.git/config` at observation time.
Remotes map[string]Remote
}
Observation captures the runtime-observed state of a *Resource's on-disk clone at the moment it was observed.
Distinct from Resource, which carries identity (URI, fsroot.Path, and the identity-extension intent fields `HEAD` and `Ref` from the plan). An observation is a point-in-time metadata snapshot record — not a Resource, never cataloged — whose identity comes from the resource it references (op.ObservationBase.OfResource, by pointer value). It embeds op.ObservationBase (the back-link + op.ObservationBase.Exists) and adds the git-specific measurement fields: `ObservedHEAD`, `ObservedRef`, `Bare`, `Dirty`, `Remotes`.
func NewObservation ¶
func NewObservation( ofResource *Resource, exists bool, observedHEAD string, observedRef string, bare bool, dirty bool, remotes map[string]Remote, ) *Observation
NewObservation constructs a *Observation anchored to the resource it observes.
Parameters:
- `ofResource`: the *Resource this observation is of. Must be non-nil (asserted by op.NewObservationBase).
- `exists`: true when the path was a git repository at observation time.
- `observedHEAD`: the disk's current HEAD SHA.
- `observedRef`: the disk's current ref name.
- `bare`: true when the on-disk repository is bare.
- `dirty`: true when the working tree had uncommitted changes.
- `remotes`: the on-disk remote configuration at observation time.
Returns:
- `*Observation`: the constructed observation.
func (*Observation) String ¶
func (o *Observation) String() string
String returns a debug-oriented single-line representation of the observation.
Returns:
- `string`: `git.Observation{of=<OfResource.URI()>, exists=<bool>, head=<sha>, ref=<name>, bare=<bool>, dirty=<bool>, remotes=<count>}`.
type Provider ¶
type Provider struct {
op.ProviderBase
// contains filtered or unexported fields
}
Provider provides git actions.
+devlore:access=planned
func NewProvider ¶
func NewProvider(runtimeEnvironment *op.RuntimeEnvironment) *Provider
NewProvider constructs a Provider bound to `runtimeEnvironment`.
Parameters:
- `runtimeEnvironment`: execution context.
Returns:
- `*Provider`: the initialized provider.
func (*Provider) Checkout ¶
Checkout checks out a ref in the given repository directory.
`repo.Ref` and `repo.HEAD` are plan-time intent and are not mutated here. Callers that need the post-checkout disk state call Provider.Observe to obtain a *Observation carrying the disk's current `ObservedHEAD` / `ObservedRef`.
Parameters:
- `repo`: git resource identifying the local repository.
- `ref`: branch, tag, or commit to check out.
Returns:
- `*Resource`: the repository resource (identity unchanged).
- `error`: any error from `git checkout`.
func (*Provider) Clone ¶
func (p *Provider) Clone( activationRecord *op.ActivationRecord, repository string, directory string, bare bool, branch string, depth int, filter string, noCheckout bool, noTags bool, origin string, recurseSubmodules bool, singleBranch bool, kwargs map[string]any, ) (*Resource, *Receipt, error)
Clone clones a repository into a newly created directory.
Identity for the cloned repository is constructed by NewResource from `directory`; operational metadata (Remotes, Bare, Dirty, HEAD) is populated by [Resource.Resolve] after the clone completes. When `directory` is empty, the directory name is derived from repository via [guessDirName] — the same algorithm git itself uses for `git clone <repository>` with no explicit directory.
The nine named options correspond one-to-one with `git clone` flags under the kwarg-to-flag rule (strip leading `--`, convert `-` to `_`, always expect a value — `--no-tags` becomes `no_tags=<bool>`). Any additional options a caller needs pass through kwargs and are translated using the same rule in reverse; see [buildCloneArgs].
Parameters:
- `repository`: remote git URL (HTTPS, SSH, git protocol, or local path) to clone from.
- `directory`: local filesystem path where the repository will be cloned; empty defers to git's own naming algorithm via [guessDirName].
- `bare`: when true, emits `--bare` — bare repository (no working tree).
- `branch`: when non-empty, emits `--branch <branch>` — branch, tag, or ref to check out.
- `depth`: when > 0, emits `--depth <depth>` — shallow clone with truncated history.
- `filter`: when non-empty, emits `--filter=<filter>` — partial-clone filter specification.
- `noCheckout`: when true, emits `--no-checkout` — populate `.git/` but leave the working tree empty.
- `noTags`: when true, emits `--no-tags` — do not fetch tags.
- `origin`: when non-empty, emits `--origin <origin>` — name to use for the upstream remote in place of "origin".
- `recurseSubmodules`: when true, emits `--recurse-submodules` — initialize and clone submodules recursively.
- `singleBranch`: when true, emits `--single-branch` — fetch only the specified branch's history.
- `kwargs`: catch-all for any `git clone` option not in the named set; each entry becomes an additional flag per the kwarg-to-flag rule.
Returns:
- `*Resource`: the cloned git.Resource with populated metadata.
- `*Resource`: the compensation handle — the same *Resource as the first return, passed to Provider.CompensateClone to reverse the clone. Git's Clone creates a directory rather than displacing one, so per the Tombstone rule (a tombstone exists for any object moved to a RecoverySite) there is no git tombstone; the compensation handle is the created Resource itself. Nil on error from `git clone` or resource construction; non-nil when the directory exists on disk even if [Resource.Resolve] failed afterward.
- `error`: any error from directory derivation, resource construction, or the underlying `git clone`.
+devlore:defaults directory="",bare=false,branch="",depth=0,filter="",noCheckout=false,noTags=false,origin="",recurseSubmodules=false,singleBranch=false
func (*Provider) CompensateClone ¶
func (p *Provider) CompensateClone(activationRecord *op.ActivationRecord, receipt *Receipt) error
CompensateClone removes the cloned directory.
Clone is a Bucket-B action: the cloned tree is creation, not displacement, so there is nothing to restore from op.RecoverySite — compensation just removes the directory. A nil receipt is a no-op (Clone never produced a resource to reverse).
Parameters:
- `activationRecord`: the dispatch activation (the required floor for compensating actions — step 27).
- `receipt`: the *Receipt returned by Provider.Clone; may be nil.
Returns:
- `error`: any error from os.RemoveAll on the cloned directory; nil when receipt or its resource is nil.
func (*Provider) Observe ¶
func (p *Provider) Observe(repo *Resource) (*Observation, error)
Observe captures the runtime-observed state of a git repository on disk as a *Observation.
Reads `.git/` via the unexported [isGitRepo], [readHEADSha], [readBranchName], [readRemotes], and [isDirtyRepo] helpers. When the path is not a git repository, the Observation carries `Exists=false` and zero values for everything else — non-existence is a valid observation, not an error. Stat / read failures surface as errors.
Provider methods that previously called `r.Resolve()` and read fields off the Resource use `obs := p.Observe(r)` and read fields off the observation. `repo.HEAD` and `repo.Ref` on the Resource are plan-time intent; the disk's current state lives on the returned observation's `ObservedHEAD` and `ObservedRef`.
Parameters:
- `repo`: the *Resource whose current git state to observe.
Returns:
- `*Observation`: the constructed observation; never nil.
- `error`: always nil — the `.git` reads are best-effort and non-existence is a valid observation; the error return keeps the announced fallible-action shape.
func (*Provider) Pull ¶
Pull pulls the latest changes in the given repository directory.
`repo.Ref` and `repo.HEAD` are plan-time intent and are not mutated here. Callers that need the post-pull disk state call Provider.Observe.
Parameters:
- `repo`: git resource identifying the local repository.
Returns:
- `*Resource`: the repository resource (identity unchanged).
- `error`: any error from `git pull`.
type Receipt ¶
type Receipt struct {
op.ReceiptBase
}
Receipt holds git-specific compensation state for a Provider.Clone call.
The embedded op.ReceiptBase carries the affected Resource (the cloned local repository) and the opaque op.ReceiptBase.TransactionID minted at op.ReceiptBase.Commit time. Clone is a Bucket-B (creation, not displacement) action — there is no prior content to archive, so the recovery key is the receipt's own transactionID; compensation simply removes the cloned directory tree.
Receipt has no provider-specific fields, so it inherits op.ReceiptBase.MarshalJSON and op.ReceiptBase.MarshalYAML unchanged. Only Receipt.RestoreEncoded is overridden, since rehydration requires the concrete Resource type that op.ReceiptBase cannot construct generically.
func NewReceipt ¶
NewReceipt constructs a Receipt anchored to the cloned Resource.
The transactionID and action name remain zero-valued until op.ReceiptBase.Commit is invoked when the receipt lands on a op.RecoveryStack via op.RecoveryStack.PushCompensator.
Parameters:
- `resource`: the cloned Resource returned by Provider.Clone.
Returns:
- `*Receipt`: the constructed receipt with only its resource populated.
func (*Receipt) RestoreEncoded ¶
func (r *Receipt) RestoreEncoded( runtimeEnvironment *op.RuntimeEnvironment, base op.ReceiptData, _ map[string]any, ) error
RestoreEncoded reconstructs the receipt from its codec-decoded envelope, resolving its Resource against the rehydrated catalog.
It is the op.Receipt.RestoreEncoded override the recovery stack drives at re-arm (via [op.reconstructReceipt]) — the env is threaded in explicitly as a parameter, not read off the receiver, so the stack path (which loads a bare receipt before the catalog is rehydrated) can reconstruct it. The cloned Resource is resolved from `base.ResourceURI` via DiscoverResource; the base is re-seated via op.NewReceiptBase so op.ReceiptBase.Restore's URI-match check has a live resource, then Restore writes the full base. Receipt has no provider-specific fields, so `fields` is unused.
Parameters:
- `runtimeEnvironment`: the resume environment; its catalog must hold (or be able to construct) the resource.
- `base`: the codec-decoded base execution state.
- `_`: the receipt's id-reference sub-field, unused (no provider-specific fields).
Returns:
- `error`: non-nil only when the runtime environment or its catalog is missing; resolution and restore failures are verified-side defects and assert.
type Remote ¶
Remote carries the fetch and push URLs for a named git remote.
PushURL is empty when the push direction uses FetchURL (git's default) — the distinction matters for workflows that split read and write endpoints (e.g., HTTPS fetch / SSH push, mirror fetch / authoritative push).
type Resource ¶
type Resource struct {
op.ResourceBase
// SourcePath is the local clone's canonical absolute path; identity derives from this via the file:// URI. Not
// persisted — reconstructed from the URI on deserialization.
SourcePath fsroot.Path `json:"-" yaml:"-"`
// Ref is the branch, tag, or commit reference the clone is positioned at as plan-time intent.
// Set at construction by [Provider.Clone] (from the just-cloned tree's `.git/HEAD`) or by
// serialized-form deserialization (from a saved plan). Not mutated by [Resource.Resolve]; the runtime
// view of the disk's current ref lives on [Observation.ObservedRef].
Ref string `json:"ref,omitempty" yaml:"ref,omitempty"`
// HEAD is the commit SHA (40-char hex) the clone was positioned at as plan-time intent. Set at
// construction by [Provider.Clone] (from the just-cloned tree's `.git/HEAD`) or by serialized-form
// deserialization. Pins the clone to an exact version across serialization. Empty for resources
// constructed via [NewResource] without an associated clone. Not mutated by [Resource.Resolve];
// the runtime view of the disk's current HEAD lives on [Observation.ObservedHEAD].
HEAD string `json:"head,omitempty" yaml:"head,omitempty"`
}
Resource represents a cloned git repository.
Identity is the local clone's filesystem location, stored as a file:// URI in op.ResourceBase. Every domain field — Ref, HEAD, Remotes, Bare, Dirty — is populated by [Resource.Resolve] from the on-disk `.git/` contents. Ref and HEAD are additionally persisted through JSON/YAML so a serialized Resource can carry its version snapshot to contexts where Resolve cannot run (e.g., cross-host comparison, offline inspection); Remotes, Bare, and Dirty are operational and not persisted — they're always rebuilt by Resolve.
func DiscoverResource ¶
func DiscoverResource(runtimeEnvironment *op.RuntimeEnvironment, value any) (*Resource, error)
DiscoverResource registers a git.Resource via op.ResourceCatalog.Discover without claiming production.
Use DiscoverResource from non-production callsites: receipt rehydration (UnmarshalJSON/Text/YAML), reference handles in CLI tools, and scanner-style discovery walks. The returned catalog entry has no producer stamp (or carries whatever stamp a previous NewResource call already applied). Use NewResource instead when the caller is a producer claiming this Resource as its output.
Discover does not stamp a producer, so unlike NewResource it takes only `runtimeEnvironment` — no unit reference is needed.
Nil-Catalog tolerance mirrors the receipt-rehydration paths: when `runtimeEnvironment.Catalog` is nil, the candidate is returned unlinked.
Parameters:
- `runtimeEnvironment`: the session runtime environment.
- `value`: a string file path or file URI.
Returns:
- `*Resource`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
- `error`: if `value` is not a string, or the input violates RFC 8089 when in file URI form.
func NewResource ¶
func NewResource(runtimeEnvironment *op.RuntimeEnvironment, producerID string, value any) (*Resource, error)
NewResource constructs a git.Resource and claims production via op.ResourceCatalog.GetOrCreate.
Use NewResource from a producer dispatch context — typically a provider method that has received an op.ActivationRecord from the framework. The returned Resource is the canonical catalog entry, stamped with `producerID = unit.ID()` (or empty when `unit` is nil for non-graph dispatch). Use DiscoverResource instead when the caller is not claiming production (rehydration, reference handles, scanner-style discovery).
The input is a bare filesystem path ("/opt/repo"), or — on the catalog-rehydration round-trip — this provider's own emitted identity specific ("file://" + path), stripped back to the path. Identity is the canonical file:// specific computed from the resolved absolute path; remotes, ref, HEAD, and other metadata are populated post-construction by Clone, Resolve, or explicit setters.
Nil-Catalog tolerance mirrors DiscoverResource: when `runtimeEnvironment.Catalog` is nil (test fixtures, library callers without a runtime), the candidate is returned unlinked.
Parameters:
- `runtimeEnvironment`: the session runtime environment.
- `producerID`: the producing caller's id (`activationRecord.CallerID`), or "" for caller-less dispatch. for non-graph dispatch.
- `value`: a string file path or file URI.
Returns:
- `*Resource`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
- `error`: if `value` is not a string, or the input violates RFC 8089 when in file URI form, or op.ResourceCatalog.GetOrCreate's strict assertions fail.
func (*Resource) Addressing ¶
func (r *Resource) Addressing() op.AddressingMode
Addressing reports that git.Resource is location-keyed.
The identity is the local clone's filesystem location, and the bytes under that location (commit SHAs, working-tree contents) are mutable.
The catalog uses op.AddressingLocation semantics — content drift triggers shadow chains, not new URIs.
Returns:
- `op.AddressingMode`: always op.AddressingLocation.
func (*Resource) CanConvertFrom ¶
CanConvertFrom reports whether `source` can be projected into a *Resource via Resource.ConvertFrom.
Opts the git Resource into the framework's op.TargetConverter contract — accepted source shape is `string` (interpreted as a local clone's filesystem path or a git URL). The framework consults this probe both at plan-time via [op.typesAreInterconvertible] (the bubble-up parameter-consistency check honors the convertibility relation without running an actual conversion) and at dispatch-time via op.Convert step 7 (env-less fallback). The canonical dispatch-time path remains the registered constructor at op.Convert step 6, which receives the full op.RuntimeEnvironment and produces a fully-canonicalized Resource via [buildCandidate].
Cheap-probe contract: this method is called against a nil-or-zero `*Resource` receiver by [op.typesAreInterconvertible] during plan-time bubble-up checks. MUST NOT dereference receiver fields.
Parameters:
- `source`: the candidate source type to test.
Returns:
- `bool`: true when `source` is `string`.
func (*Resource) ConvertFrom ¶
ConvertFrom projects `value` into an env-less unlinked *Resource.
Used by op.Convert step 7 when the env-aware registered constructor (step 6) is unavailable — env-less library callers, tests, or op.RuntimeEnvironment.Registry-missing contexts. The returned Resource carries only the SourcePath set from `value`; URI / Ref / HEAD / catalog interning are not populated here. Provider methods consuming the projected Resource are responsible for re-canonicalization via their own NewResource/DiscoverResource path when full identity is required.
Parameters:
- `value`: the source value; must be `string`.
Returns:
- `any`: the constructed unlinked *Resource.
- `error`: non-nil when `value` is not a `string`.
func (*Resource) Digest ¶
Digest returns the honest content hash for the local clone:
- Clean repository (bare or working-tree): sha256 of HEAD's hex string.
- Dirty working tree: sha256 of HEAD + "\n" + tree SHA over the index + working tree.
The HEAD SHA-1 itself already content-addresses git's commit graph; wrapping it in a sha256 layer keeps the algorithm consistent with the rest of the system (the catalog stores `op.Digest` values uniformly and round trips them through op.ParseDigest, which only accepts the sha256 allowlist). For dirty working trees, the tree SHA (derived from stash-create followed by rev-parse to the tree, not the commit SHA which would carry timestamps) captures the index + working-tree state deterministically — same state same digest.
Always fresh — recomputes at call time. Errors when the path is not a git repository or HEAD cannot be read.
Returns:
- `op.Digest`: sha256 of the HEAD SHA (plus stash-create tree SHA when the working tree is dirty).
- `error`: when the path is not a git repository or HEAD cannot be read.
func (*Resource) Equal ¶
Equal reports whether r and other identify the same git resource.
Strict equality: other must be a *git.Resource (not merely an op.Resource with the same URI). Once the type check passes, URI comparison is delegated to op.ResourceBase.Equal.
Parameters:
- `other`: the value to compare against; may be any, including nil or a non-Resource.
Returns:
- `bool`: true if `other` is a *git.Resource with the same URI as r.
func (*Resource) Etag ¶
Etag returns a cheap stat-derived change-detection token for the local clone:
- Bare repository: the 7-character HEAD short-id (e.g., "a1b2c3d").
- Working tree, clean: the 7-character HEAD short-id.
- Working tree, dirty: HEAD short-id + "-" + 7-character prefix of the tree SHA covering the current index + working tree.
The dirty fingerprint is derived from `git stash create` followed by `git rev-parse <stash>^{tree}`. The stash commit's own SHA cannot be used directly: commit objects include author/committer timestamps, so two calls on the same unchanged tree state would produce different commit SHAs (catalog would falsely detect drift on every Resolve). The tree SHA is content-addressed and timestamp-free — same tree state same SHA, different tree state different SHA. This lets the catalog detect drift within the dirty state without false-positive drift on identical state.
Always fresh — re-reads HEAD and (when dirty) re-runs the stash-create + rev-parse pair at call time. Errors when the path is not a git repository or HEAD cannot be read.
Returns:
- `string`: the etag (HEAD short-id, optionally suffixed with `-<tree-short>` for a dirty working tree).
- `error`: when the path is not a git repository or HEAD cannot be read.
func (*Resource) String ¶
String returns a debug-oriented single-line representation of the resource.
Suitable for log lines and debug windows. Identity-only — runtime-observed state (bare, dirty, remotes, disk's current HEAD/ref) lives on *Observation, minted by Provider.Observe.
Returns:
- `string`: `git.Resource{uri=<URI>, ref=<ref>, head=<head>}`.
func (*Resource) UnmarshalJSON ¶
UnmarshalJSON populates the receiver from its JSON document.
The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method. Identity is reconstructed via NewResource from the URI; Ref and HEAD are assigned from the decoded snapshot. Operational state (Remotes, Bare, Dirty) stays at zero values until [Resource.Resolve] reads the on-disk clone.
Parameters:
- `data`: JSON-encoded document.
Returns:
- `error`: non-nil if the RuntimeEnvironment is missing, the JSON does not decode, or resource construction fails.
func (*Resource) UnmarshalText ¶
UnmarshalText populates the receiver from raw UTF-8 bytes containing a local path or file URI.
Scalar form: only identity (URI) round-trips. Ref, HEAD, and Remotes remain at zero values; richer round trip uses Resource.UnmarshalJSON or Resource.UnmarshalYAML.
Parameters:
- `text`: UTF-8 bytes containing the resource's URI or path.
Returns:
- `error`: non-nil if the RuntimeEnvironment is missing or resource construction fails.
func (*Resource) UnmarshalYAML ¶
UnmarshalYAML populates the receiver from its YAML document.
The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method. Identity is reconstructed via NewResource from the URI; Ref and HEAD are assigned from the decoded snapshot. Operational state (Remotes, Bare, Dirty) stays at zero values until [Resource.Resolve] reads the on-disk clone.
Parameters:
- `unmarshal`: callback supplied by the YAML decoder that projects the current node into the given target.
Returns:
- `error`: non-nil if the RuntimeEnvironment is missing, the YAML does not decode, or resource construction fails.