go.bigb.es/auxilia
Documentation
Index
- func Commit(ctx context.Context, conn legatus.Conn, remoteRoot, message string) error
- func CommitOrRollback(ctx context.Context, conn legatus.Conn, root, okMsg, failMsg string, apply func() error) error
- func EnsureGitignore(ctx context.Context, conn legatus.Conn, remoteRoot string, entries []string) (bool, error)
- func EnsureRepo(ctx context.Context, conn legatus.Conn, remoteRoot string) error
- func IsDirty(ctx context.Context, conn legatus.Conn, remoteRoot string) (bool, string, error)
- func Status(ctx context.Context, conn legatus.Conn, remoteRoot string) (string, error)
- type Diff
- type EnvSync
- type GitGuard
- type Gitignore
- type Pull
- type PullMode
- type Push
Variables
var ErrEnvAborted = culpa.WithCode(culpa.New("sync: .env sync aborted by operator"), "SYNC_ENV_ABORTED")
ErrEnvAborted is raised when the operator chooses "abort" on a .env diff, so the deploy stops before any later step (e.g. post_push).
var ErrRemoteDirty = culpa.WithCode( culpa.New("sync: remote git repo has uncommitted changes"), "SYNC_REMOTE_DIRTY", )
ErrRemoteDirty is the sentinel raised by GitGuard when the remote repo has uncommitted changes that would be clobbered by a push.
Functions
func Commit(ctx context.Context, conn legatus.Conn, remoteRoot, message string) error
Commit stages all changes and commits them with message. It is a no-op when there is nothing to commit.
func CommitOrRollback(ctx context.Context, conn legatus.Conn, root, okMsg, failMsg string, apply func() error) error
CommitOrRollback runs apply(); on success it commits okMsg, on failure it commits failMsg and returns the original apply error — so the remote repo is left committed (never dirty) regardless of how apply fails, and the next push is not blocked by uncommitted drift. A failure of the rollback commit itself is wrapped onto the apply error, never swallowed (GPC6).
func EnsureGitignore(ctx context.Context, conn legatus.Conn, remoteRoot string, entries []string) (bool, error)
EnsureGitignore writes (idempotently) a .gitignore at remoteRoot containing the given entries, deduped and sorted, inside the managed marker block. Manual lines outside the block survive. It performs no write when the managed block already matches, reporting changed=false.
func EnsureRepo(ctx context.Context, conn legatus.Conn, remoteRoot string) error
EnsureRepo initializes a git repository at remoteRoot if one does not already exist (idempotent). A missing directory or a git failure raises.
func IsDirty(ctx context.Context, conn legatus.Conn, remoteRoot string) (bool, string, error)
IsDirty reports whether the remote repo has uncommitted changes, returning the porcelain status. A non-zero git exit (e.g. not a repository) raises rather than being swallowed as "clean".
func Status(ctx context.Context, conn legatus.Conn, remoteRoot string) (string, error)
Status returns a short human-readable summary of the remote repo state: "no repo", "clean", or "dirty (<n> changes)".
Types
type Diff struct { // Files are individual paths relative to LocalRoot/RemoteRoot. Exclude does // not apply to these: naming a file explicitly is the intent. Files []string // Dirs are directory paths (relative) compared recursively. Dirs []string // Exclude holds gitignore-style patterns (see matchIgnore) scoping the Dirs // walk, so local build artifacts and ignored state are not reported as drift. Exclude []string // LocalRoot is the base of the local tree. LocalRoot string // RemoteRoot is the base of the remote tree. RemoteRoot string // Normalize, when set, is applied to both local and remote bytes before the // equality test, so a JSON-canonicalizer or whitespace-normalizer makes the // comparison semantic. The rendered diff is of the normalized forms. Normalize func([]byte) ([]byte, error) // Summary, when set, makes Apply report a content-free per-file verdict // ("rel: same" / "rel: differs") instead of a unified diff — so secret-bearing // files can be diffed for change-detection without printing their content. Summary bool }
Diff is a read-only step: it downloads each remote file, compares it to its local counterpart, and reports a unified diff. It never writes anywhere — Outcome.Changed is always false (PC: idempotent, observation-only). The rendered diff is in Outcome.Data (string); empty string means no differences.
func (d *Diff) Apply(fc *legatus.FlowCtx) (legatus.Outcome, error)
Apply renders the comparison and reports it. It is read-only: Changed is false. In Summary mode the Data is a content-free per-file verdict; otherwise it is the concatenated unified diffs of the differing files.
func (d *Diff) Check(fc *legatus.FlowCtx) (bool, error)
Check reports whether any file differs.
type EnvSync struct { // File is the file name relative to the roots; defaults to ".env". File string // LocalRoot is the base of the local tree. LocalRoot string // RemoteRoot is the base of the remote tree. RemoteRoot string // Sudo stages the upload with sudo (for a root-owned remote .env). Sudo bool // Owner, if set, chowns the uploaded file (user or user:group). Owner string }
EnvSync syncs one sensitive, untracked file (default ".env") that the normal Push skips. On a diff it shows the change and asks skip|overwrite|abort; abort halts the flow. When only the local file exists it asks to upload. The file's content reaches only the operator-facing sink (fc.Out()) and the prompter, never the structured logger (it is a secret).
func (e *EnvSync) Apply(fc *legatus.FlowCtx) (legatus.Outcome, error)
Apply syncs the file through the prompter. abort returns ErrEnvAborted.
func (e *EnvSync) Check(fc *legatus.FlowCtx) (bool, error)
Check reports whether the local file exists and the remote is absent or differs.
type GitGuard struct { // RemoteRoot is the remote repository directory. RemoteRoot string }
GitGuard is a gate step: it blocks the flow when the remote git repo is dirty, preventing a push from clobbering uncommitted remote drift. Check reports need=true when dirty; Apply raises ErrRemoteDirty.
func (g *GitGuard) Apply(fc *legatus.FlowCtx) (legatus.Outcome, error)
Apply raises if the remote is dirty; otherwise reports a clean, no-change outcome.
func (g *GitGuard) Check(fc *legatus.FlowCtx) (bool, error)
Check reports whether the remote is dirty (needs attention).
type Gitignore struct { // RemoteRoot is the remote repository directory holding the .gitignore. RemoteRoot string // Entries are the patterns written inside the managed block. Entries []string }
Gitignore is the step form of EnsureGitignore, so a deploy Flow can ensure the managed .gitignore declaratively alongside GitGuard.
func (g *Gitignore) Apply(fc *legatus.FlowCtx) (legatus.Outcome, error)
Apply writes the managed block when it differs, delegating to EnsureGitignore.
func (g *Gitignore) Check(fc *legatus.FlowCtx) (bool, error)
Check reports whether the managed block would change.
type Pull struct { // Files are individual paths relative to LocalRoot/RemoteRoot. Exclude does // not apply to these: naming a file explicitly is the intent. Files []string // Dirs are directory paths (relative) pulled recursively. Dirs []string // LocalRoot is the base of the local tree. LocalRoot string // RemoteRoot is the base of the remote tree. RemoteRoot string // Mode selects conflict resolution; the zero value is PullConfirm. Mode PullMode // Exclude holds gitignore-style patterns (see matchIgnore) scoping the Dirs // walk. Remote runtime state inside a synced dir — a database, a log — has no // local counterpart, so without this it reads as a *new* file and is written // straight into the local tree with no prompt. Exclude []string }
Pull downloads remote files into the local tree. A new file (no local counterpart) is written directly. A conflict (local exists and differs) is resolved through the prompter (PC2): the non-interactive default declines, so the conflict is skipped and the local file is left untouched — never silently overwritten.
func (p *Pull) Apply(fc *legatus.FlowCtx) (legatus.Outcome, error)
Apply downloads each remote file and applies it. New files are written; conflicts go through the prompter. Outcome.Data is the sorted list of applied relative paths.
func (p *Pull) Check(fc *legatus.FlowCtx) (bool, error)
Check reports whether any remote file is new or differs locally.
type PullMode int
PullMode selects how Pull resolves a conflict (local exists and differs).
const ( // PullConfirm asks a yes/no Confirm to overwrite the whole file (default). PullConfirm PullMode = iota // PullInteractive offers per-file apply|skip|patch|quit and, in patch mode, a // per-hunk picker. PullInteractive )
type Push struct { // Files are individual paths relative to LocalRoot/RemoteRoot. Exclude does // not apply to these: listing a file explicitly is the intent. Files []string // Dirs are directory paths (relative) synced recursively. Dirs []string // LocalRoot is the base of the local tree. LocalRoot string // RemoteRoot is the base of the remote tree. RemoteRoot string // Delete removes remote-only files under Dirs (gated by the prompter). Delete bool // Exclude holds gitignore-style patterns (see matchIgnore) scoping the Dirs // walk. A matching path is neither uploaded nor considered remote-only, so // remote runtime state living inside a synced dir — a database, a log — is // never shipped over and never offered for deletion. Exclude []string }
Push uploads local files to the remote, comparing by content hash so unchanged files are skipped (PC4: Check reports need=false when every file already matches). When Delete is set, remote-only files under the synced Dirs are removed — but only after the prompter confirms (PC2); the non-interactive default declines, so a stale file is kept, never force-deleted.
func (p *Push) Apply(fc *legatus.FlowCtx) (legatus.Outcome, error)
Apply uploads changed files and, when Delete is set, removes confirmed remote-only files. Outcome.Data is the sorted list of uploaded relative paths. Dry-run is handled by the engine (IV4): Apply only runs for real.
func (p *Push) Check(fc *legatus.FlowCtx) (bool, error)
Check reports whether any file needs uploading. need=false when all match.
Package sync provides legatus steps for keeping a local file tree and a remote host in agreement: Push (upload changed files), Pull (download with per-file conflict resolution), Diff (read-only unified diff), and a git-drift battery (EnsureRepo, IsDirty, Commit, Status, plus a GitGuard gate step).
All remote work goes through legatus.Conn. Every interactive decision goes through the FlowCtx prompter (PC2); the non-interactive default declines, so destructive paths (delete, conflict overwrite) skip rather than force.