diff --git a/.claude/skills/run-statemate/SKILL.md b/.claude/skills/run-statemate/SKILL.md index 31909a3..439f864 100644 --- a/.claude/skills/run-statemate/SKILL.md +++ b/.claude/skills/run-statemate/SKILL.md @@ -80,7 +80,7 @@ export STATEMATE_SCRATCH=$(mktemp -d)/sm python3 .claude/skills/run-statemate/driver.py scaffold ``` -The scaffold covers a plain file, a `#template` file, a `#perm-r:755` directory, +The scaffold covers a plain file, a `#tmpl` file, a `#perm-r:755` directory, a per-source `.mate.yaml` with packages, and two scripts (`#once#before` with a `# Description:`, `#always#after`). @@ -110,9 +110,9 @@ Verified prompt output — note `once` offers `[s]kip` and `always` does not: ``` Run setup.sh (before, once)? Create a marker proving the script ran -[y]es / [n]o / [s]kip (mark as done) / [a]ll / [q]uit: n +[Y]es / [n]o / [s]kip (mark as done) / [a]ll / [q]uit: n Run after.sh (after, always)? -[y]es / [n]o / [a]ll / [q]uit: +[Y]es / [n]o / [a]ll / [q]uit: ``` ## Direct invocation (for PRs touching internals) @@ -164,7 +164,7 @@ Use the driver. - **A source's packages ignore its `profile:` key.** `internal/packages/sync.go` collects `dirCfg.Packages` unconditionally; `profile:` in `.mate.yaml` gates *files* only. You cannot hide a package behind a non-matching profile. -- **`mate` resolves the repo from config, not cwd.** `~/.config/statemate/mate.yaml` +- **`mate` resolves the repo from config, not cwd.** `~/.config/statemate/config.yaml` sets `source_dir`, which beats the current directory. `cd`-ing into a scratch repo is **not** enough — you must set `STATEMATE_DIR` (the driver does). - **Resolve symlinks in scratch paths.** On macOS `mktemp -d` returns @@ -172,8 +172,8 @@ Use the driver. `mate managed ` compare resolved paths, so an unresolved path yields `... is not managed by mate`. The driver calls `.resolve()`. - **`#` in filenames.** Safe as a mid-word shell argument in bash and zsh - (`file.conf#template` needs no quoting). But it *is* nvim's alternate-file - token, so `:edit path#encrypted` fails with `E194` — use `:edit path\#encrypted` + (`file.conf#tmpl` needs no quoting). But it *is* nvim's alternate-file + token, so `:edit path#enc` fails with `E194` — use `:edit path\#enc` or `vim.fn.fnameescape`. - **`make build-all` skips linux/arm64.** Only darwin-arm64 and linux-amd64. diff --git a/.claude/skills/run-statemate/driver.py b/.claude/skills/run-statemate/driver.py index 998ae8e..7231952 100755 --- a/.claude/skills/run-statemate/driver.py +++ b/.claude/skills/run-statemate/driver.py @@ -109,7 +109,7 @@ def scaffold() -> Path: # A plain source, a templated file, and a per-source .mate.yaml. (repo / "app" / ".config" / "app").mkdir(parents=True) (repo / "app" / ".config" / "app" / "settings.conf").write_text("mode=plain\n") - (repo / "app" / ".config" / "app" / "rendered.conf#template").write_text( + (repo / "app" / ".config" / "app" / "rendered.conf#tmpl").write_text( "greeting={{ .Vars.greeting }}\n" ) # A deliberately non-existent package, so `status` always has something to @@ -120,7 +120,7 @@ def scaffold() -> Path: # "missing", and `mate apply --force` would try to install it and abort the # apply when that fails. Never pass bare --force here; see apply below. (repo / "app" / ".mate.yaml").write_text( - "packages:\n common:\n - statemate-no-such-package\n" + "packages:\n - statemate-no-such-package\n" ) # A source whose files need executable permissions via a recursive attr. @@ -307,7 +307,7 @@ def smoke() -> None: # --- interactive apply under a PTY -------------------------------------- # 'y' runs the before-script, 'y' again for the after-script. code, out = run_pty(["apply"], keys="y\ny\n", root=root) - expect(out, "[y]es", "apply prompts for script confirmation") + expect(out, "[Y]es", "apply prompts for script confirmation") if not (root / "ran-setup").exists(): die(f"answering 'y' should have run the script:\n{out}") log("ok: PTY prompt ran the script") diff --git a/.githooks/post-checkout b/.githooks/post-checkout new file mode 120000 index 0000000..ace4560 --- /dev/null +++ b/.githooks/post-checkout @@ -0,0 +1 @@ +post-commit \ No newline at end of file diff --git a/.githooks/post-commit b/.githooks/post-commit new file mode 100755 index 0000000..f4aad0b --- /dev/null +++ b/.githooks/post-commit @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# +# Rebuilds dist/ after a commit, merge or checkout (post-merge and +# post-checkout link here), so a `mate` symlinked to dist/mate-- +# always runs the checked-out code. Git ignores the exit status of these hooks, +# so a failed build only warns. + +# post-checkout passes 0 as $3 for a file checkout, which moves no commit. +[ "${3:-1}" = 0 ] && exit 0 + +make -s build-all >/dev/null || echo "⚠ make build-all failed: dist/ is stale" >&2 diff --git a/.githooks/post-merge b/.githooks/post-merge new file mode 120000 index 0000000..ace4560 --- /dev/null +++ b/.githooks/post-merge @@ -0,0 +1 @@ +post-commit \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b86d71c..9b3fad2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -41,7 +41,8 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v7 with: - version: v2.6 + # Must be built with at least go.mod's Go version, or it refuses to load. + version: v2.15 # docs/commands/ is generated from the cobra command tree and committed, so a # help-text change must be regenerated in the same commit. Without this, the diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index beb1a6b..0790696 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -20,7 +20,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version-file: go.mod - name: Set up AUR SSH key run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index f69a405..19f247e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.7.0] - 2026-10-25 + +### Added +- `mate add -s ` offers to create the source when its directory does not exist yet, then adds the file to it. The config is not changed: mate warns that the new source must still be listed under `sources:` before it is deployed +- `mate unmanaged [dir]` lists the files under the current directory, or the given one, that no source deploys for any profile. Only files are listed, not directories, and paths are relative to the current directory so they can be passed to `mate add` +- Colored output: change markers (`+` `~` `!` `<`), `mate doctor`'s `[OK]`/`[WARN]`/`[ERROR]`, warnings, errors, script and hook progress, and section headers. Color is off when stdout is not a terminal, `NO_COLOR` is set, or `--no-color` is passed +- `--log-file ` appends an audit log of what each run changed: target files written, moved or removed, source-tree changes (add, edit, rename, encrypt, decrypt, import, delete), forgotten files, secrets fetched, scripts and hooks run, packages installed or uninstalled, and the error a run ended with. A run that changed nothing, including any `--dry-run`, leaves no line. `--log-level debug` adds every external command mate runs with its exit code, the config and profile resolved, each file's status, conflict answers, and which scripts and hooks were due. Both can also be set with `STATEMATE_LOG_FILE`/`STATEMATE_LOG_LEVEL` or `log_file`/`log_level` in `mate.yaml` or the local config. Command output and environment are never logged +- `-v`/`--verbose` on every command prints the same log records to stderr as they happen: `-v` for what changed, `-vv` for the debug detail too. It does not need a log file and does not change the file's level. `mate -v` no longer prints the version; use `mate --version` +- `#noconfirm` script attribute and `confirm: false` hook field: the script or hook runs during `mate apply` without asking, even when there is no terminal. `mate scripts list`, `mate hooks list` and `mate status` mark them `noconfirm`. Adding `#noconfirm` to an existing script renames it, so a `#once` script runs again +- A `targets` mapping in a source's `.mate.yaml` can list several paths, deploying the subdirectory to each of them: `targets: {.claude.shared: [.claude.personal, .claude.work]}` +- An `#onchange` script can name what triggers it in a `# Onchange:` comment near its top: `self` runs it when the script itself has changed since its last run (or has never run), and target patterns such as `/etc/keyd/*.conf` run it when a matching file in its source has pending changes. With the comment, other changes in the source no longer trigger the script; without it, nothing changes. An empty comment, a pattern that does not parse, or the comment on a script that is not `#onchange` stops `mate apply` with an error naming the script + +### Changed +- **Breaking:** `variable_commands` is removed. A variable's value may now be a template instead, so `variable_commands: {key: "gpg ..."}` becomes `variables: {key: '{{ cmd "gpg ..." }}'}`. Template values are rendered in `variables`, `include`, `var_files` and profile variables, including list items and nested keys, so a list such as `gpg_keys` can come from commands too. A leftover `variable_commands` key is ignored, leaving those variables undefined +- **Breaking:** `{{ cmd "..." }}` now fails rendering, showing the command's stderr, when the command exits non-zero. Before, it returned an empty string, so a broken command deployed a file with the value missing. Append `|| true` to the command to accept a failure +- **Breaking:** the local config is renamed from `~/.config/statemate/mate.yaml` to `~/.config/statemate/config.yaml` (`config.yml` works too). The old file is not read anymore: until you rename it with `mv ~/.config/statemate/mate.yaml ~/.config/statemate/config.yaml`, its `source_dir`, profile and other overrides are ignored and `mate` looks for `mate.yaml` in the current directory +- **Breaking:** the `#encrypted`, `#template` and `#symlink` attributes are renamed to `#enc`, `#tmpl` and `#link`, and the long forms are no longer recognised. Scripts use `#tmpl` too. Rename existing sources, for example `ssh/.ssh/config#encrypted` to `ssh/.ssh/config#enc`; mate stops with "#encrypted was renamed to #enc" until they are. `mate encrypt` and `mate add --encrypt`/`--template` write the short forms, and a `var_files` or `include` entry named without a suffix finds the `#enc` file. Recorded script runs follow the rename, so `#once` scripts do not run again +- **Breaking:** `-v` now means log output on every command, so the per-command verbose flags are renamed or removed: `mate apply -V`/`--verbose` is now `-l`/`--list`, `mate packages status -v`/`--verbose` is now `-d`/`--descriptions`, and `mate scripts run -v` and `mate hooks run -v` are removed; with `-v` they log each script and hook they run, and `-vv` shows each hook command +- An unrecognised attribute in a file, directory or script name is now an error that names the file, and so is a `#perm:` mode that is not octal. Before, it was dropped silently, so a typo such as `config#templte` deployed an unrendered `config` +- When the age identity fails to load (a missing key file, or an `identity_command` that fails), `mate status`, `diff`, `check` and `eval` now report why, naming the `#enc` file that needed it: ` is #enc, but the age identity failed to load: `. Before, status and check said no identity was configured, and diff and eval failed even when nothing had to be decrypted +- `mate apply`, `status`, `diff`, `check`, `scripts list` and shell completion run `age.identity_command` fewer times, so an identity that prompts (a password manager, a hardware key) asks less often +- **Breaking:** `mate rename`, `mate encrypt`, `mate decrypt`, `mate delete` and `mate edit` take their file as a path, the way `mate edit` already did: absolute, under `~`, or relative to the current directory, and a relative path that is not there is also looked up in the source directory. It names the file by its source, with or without the attributes (`nvim/.config/nvim/init.lua` finds `init.lua#tmpl`), or by its target. rename, encrypt and decrypt no longer search for a bare name or a path suffix: `mate encrypt secrets.yaml` must become `mate encrypt nvim/secrets.yaml`. That search took the first file that matched, so `mate rename config ...` could rename a different file than the one meant +- `mate delete` also accepts the source file, and finds files from every source, not only the current profile's. When a target has a variant per profile, the commands above act on the current profile's and otherwise stop, listing the variants +- `mate rename` and `mate encrypt` now build the full template context to render `.mate.yaml`, which loads the age identity: an `identity_command` runs once more than before +- `mate status`, `diff` and `check` stop with an error naming the file when a `#tmpl` file cannot be rendered, such as a template error or a secret that is not cached. Before, they compared the unrendered template with the target and reported a change that `mate apply` then failed on. `mate diff` no longer notes "(secrets not cached)" per file and carries on; it fails with the same message as the other commands +- `mate diff` labels each side with the target and the source instead of a temporary file, shows a file that is not deployed yet as a normal diff against `/dev/null`, and reports a binary file under its `===` header. A `diff_tool` gets temporary files named after the target, so it can tell the file type from the extension +- Answering `[d]iff` to a conflict during `mate apply` uses `diff_tool` when it is set + +- `mate hooks list` drops the STEPS column, which only counted a hook's `do:` entries. `mate hooks run --dry-run` shows the steps themselves +- **Breaking:** `mate packages status` is renamed to `mate packages list`, matching `mate scripts list` and `mate hooks list`. Its flags are unchanged +- `mate packages list` sorts each manager's packages by name, with `--all` extras after the declared ones. Before, they were grouped by the source that declared them, and `mate apply` and `mate packages apply` listed missing packages in that order too +- **Breaking:** `packages:` is a list with one package per item, replacing the `brew:`, `pacman:`, `aur:` and `common:` lists. A bare name (`- ripgrep`) goes to the system manager, brew or else pacman, as `common` did. A name followed by managers is installed with exactly those, each optionally under another name: `- gnu-sed: brew`, `- gh: [brew, pacman: github-cli]`, `- tpack: [brew, aur: tpack-bin]`. The old lists stop with an error that shows the new form, and so do an unknown manager, a manager listed twice, or `pacman` and `aur` on one item. In TOML it is an array, `packages = ["ripgrep", {gnu-sed = "brew"}]`, written before the file's first table +### Fixed +- `mate diff`, and `[d]iff` at the conflict prompt, work for targets only readable with sudo, such as a root-owned file under `/etc`. Before, they printed `Permission denied` +- `[d]iff` at the conflict prompt shows what apply would write for a file generated by `.mate.yaml`, and the link text for a `#link`. Before, it diffed the source directory, or what the link points at +- A relative `targets` path such as `.config` now deploys under the global target base (normally `~`). Before, it produced a relative target path +- `mate check` marks an `#import` change with `<`, as `mate status` does, instead of leaving the marker blank +- On macOS, targets that can only be read with sudo are compared again. mate ran GNU `stat` options that macOS's `stat` rejects, so `mate status`, `diff`, `check` and `apply` reported every such target as skipped, even with `--sudo`. A symlink in such a target's place is now also recognised as one, on Linux too +- `mate rename`, `mate encrypt`, `mate decrypt` and `mate edit` read a templated `.mate.yaml` rendered, as every other command does. Unrendered, a `.mate.yaml` with template syntax was skipped, so its `targets` and `target_base` were ignored and a file given by its target path was not found +- `mate scripts list` no longer shows a source's `#onchange` scripts as pending when its only change is a `#tmpl` file that is already up to date. It compared the unrendered template with the deployed file, so it disagreed with `mate status` and `mate apply` + ## [0.6.0] - 2026-10-17 ### Added @@ -16,7 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A source's `.mate.yaml` accepts `include`, like `mate.yaml`. Included packages count as that source's packages, and included variables are available to every template while the source is active, including the `.mate.yaml` itself. Paths are relative to the source directory. Previously the key was silently ignored ### Changed -- The Homebrew package is now a cask instead of a formula. `brew install subbeh/tap/statemate` works as before, and existing formula installs are moved to the cask on `brew upgrade` +- The Homebrew package is now a cask instead of a formula. `brew install subbeh/tap/statemate` works as before. `brew update` migrates an existing formula install, but Homebrew 7 only installs a trusted cask, so if it reports that the migration target is not trusted, run `brew uninstall --formula statemate`, `brew trust --cask subbeh/tap/statemate` and `brew install --cask subbeh/tap/statemate` - `mate apply` now lists the files it is about to change and asks for confirmation before doing anything. Pass `--noconfirm` to skip the prompt (`--force` and `--dry-run` skip it too). Without a terminal the apply stops, so unattended runs must pass `--noconfirm` or `--force` - `mate clean`, `mate delete` and `mate packages apply` accept `--noconfirm` as well, as an alias for their `--force` / `--yes` flag, so the same flag skips prompts on every command that asks - Pressing Enter at a script or hook prompt (`[Y]es / [n]o / [a]ll / [q]uit`) now runs it, as `y` does. Before, Enter was ignored and the prompt waited for a letter diff --git a/CLAUDE.md b/CLAUDE.md index 3561e49..39373f5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,6 +70,10 @@ non-test files under `internal/` or `cmd/` without staging `CHANGELOG.md` are re The same hook regenerates `docs/commands/` when `internal/cli/` changes, and rejects the commit if the result is not staged. +`.githooks/post-commit` (linked as `post-merge` and `post-checkout`) runs +`make build-all` so `dist/`, which the installed `mate` symlinks to, tracks the +checked-out code. It only warns when the build fails. + One-time setup after cloning (`core.hooksPath` is local git config, not version-controlled): ```bash @@ -105,6 +109,17 @@ Active development happens on the `develop` branch. Commit directly there — no 6. **Push**: `git push` directly to develop When ready for a release, merge `develop` into `main` (via PR or direct merge). +Tag the merge commit on main (`git tag -a vX.Y.Z -m vX.Y.Z`) to run the release +workflow. Then merge `main` back into `develop` and push it: + +```bash +git checkout develop && git merge --no-ff origin/main && git push +``` + +The tag sits on main's merge commit, so without this step develop never contains +it and `git describe` -- the version `make build-all` stamps into `mate version` +-- keeps reporting the previous release. The repo deletes the head branch when a +PR merges, so `develop` may have to be pushed back first anyway. **No direct pushes to main** — main is the stable release branch. diff --git a/GLOSSARY.md b/GLOSSARY.md new file mode 100644 index 0000000..58ed91e --- /dev/null +++ b/GLOSSARY.md @@ -0,0 +1,16 @@ +# Statemate + +Statemate deploys files from source directories in a repository to their targets +on a machine, as a profile selects them. + +## Language + +**Env**: +Everything one run of mate resolves before it looks at any source: the config, +the profile, the age identity and the template context. +_Avoid_: setup, context, environment (environment variables are a separate thing) + +**Plan**: +What the sources say should be on disk for one Env, and how that differs from +the targets and the state database: entries, conflicts, changes and orphans. +_Avoid_: desired state, tree, diff diff --git a/README.md b/README.md index e962f08..56abea2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Statemate -Declarative system configuration management. Manage dotfiles, configs, and packages -across machines. +Declarative system configuration management. Manage dotfiles, configs, packages, and +maintenance tasks across machines. ## Features @@ -58,10 +58,10 @@ A source directory is deployed relative to your home directory: Behavior is controlled by `#` suffixes on filenames, stripped from the target: ``` -.ssh/config#encrypted#perm:600 encrypted in the repo, mode 0600 deployed -.gitconfig#template rendered as a Go template -.gitconfig#profile:work only on machines matching the work profile -.claude/settings.json#import app owns it; changes flow back to the repo +.ssh/config#enc#perm:600 encrypted in the repo, mode 0600 deployed +.gitconfig#tmpl rendered as a Go template +.gitconfig#profile:work only on machines matching the work profile +.claude/settings.json#import app owns it; changes flow back to the repo ``` ## Documentation @@ -73,6 +73,7 @@ built from [`docs/`](docs/README.md): |-------|----------| | [Getting Started](docs/getting-started.md) | Install, create a repository, add your first file | | [Concepts](docs/concepts.md) | Sources, targets, state, and how a change is detected | +| [Comparison](docs/comparison.md) | statemate next to chezmoi, yadm, GNU Stow, and dotbot | | [How-to guides](docs/README.md#how-to-guides) | Repository layout, multiple machines, encryption, system files, new machines, tips | | [Configuration](docs/configuration.md) | `mate.yaml`, `.mate.yaml`, profiles, local overrides | | [File Attributes](docs/attributes.md) | Every `#` suffix and what it does | @@ -106,14 +107,15 @@ profiles: variables: email: "me@company.com" packages: - brew: [slack] + - slack age: identity: "~/.config/statemate/key.txt" recipients: ["age1..."] packages: - brew: [ripgrep, fd] + - fd + - ripgrep ``` See [Configuration](docs/configuration.md) for every key. diff --git a/docs/README.md b/docs/README.md index 761d175..e1abe43 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,10 +8,10 @@ makes it so. ~/dotfiles/ mate.yaml sources, profiles, packages, hooks zsh/.zshrc → ~/.zshrc - git/.config/git/config#template → ~/.config/git/config, rendered per machine - ssh/.ssh/config#encrypted → ~/.ssh/config, decrypted + git/.config/git/config#tmpl → ~/.config/git/config, rendered per machine + ssh/.ssh/config#enc → ~/.ssh/config, decrypted arch/etc#owner-r:root/keyd/… → /etc/keyd/…, owned by root - nvim/.mate.yaml packages: { common: [neovim] } + nvim/.mate.yaml packages: [neovim] nvim/.matescripts/ 70-plugins.sh#monthly#after run after applying, once a month ``` @@ -37,6 +37,7 @@ makes it so. |-------|----------| | [Getting Started](getting-started.md) | Install, create a repository, add your first file, set up a second machine | | [Concepts](concepts.md) | Sources, targets, state, and how a change is detected | +| [Comparison](comparison.md) | statemate next to chezmoi, yadm, GNU Stow, and dotbot | ## How-to guides @@ -55,7 +56,7 @@ makes it so. | Reference | Contents | |-----------|----------| | [Configuration](configuration.md) | `mate.yaml`, `.mate.yaml`, profiles, local overrides | -| [File Attributes](attributes.md) | `#template`, `#encrypted`, `#import`, `#perm:`, and the rest | +| [File Attributes](attributes.md) | `#tmpl`, `#enc`, `#import`, `#perm:`, and the rest | | [Templates](templates.md) | Variables and functions available when rendering | | [Secrets](secrets.md) | Bitwarden references and the encrypted cache | | [Scripts](scripts.md) | Lifecycle scripts, frequency, timing | diff --git a/docs/adr/0001-strict-file-arguments.md b/docs/adr/0001-strict-file-arguments.md new file mode 100644 index 0000000..89fa33d --- /dev/null +++ b/docs/adr/0001-strict-file-arguments.md @@ -0,0 +1,19 @@ +# File arguments are paths, not search terms + +Commands that act on one managed file (`edit`, `rename`, `encrypt`, `decrypt`, +`delete`) treat their argument like any command-line path: absolute, under `~`, +or relative to the current directory. A relative path that is not there is also +tried against the source directory. The path names an entry by its source file, +with or without the attributes, or by its target. A bare name such as +`init.lua` is never searched for. + +We chose this over suffix matching because suffix matching picked an arbitrary +file when several matched, and `rename` and `delete` change files. It also lets +tab completion be plain filesystem completion: what the shell offers is exactly +what the command accepts. `mate edit` made this choice first (ef6e629), and the +other commands follow it. + +**Considered options:** suffix matching that fails when it is ambiguous. We +rejected it because a name that is unique today becomes ambiguous once another +source adds a file of the same name, so the same command would stop working +later. diff --git a/docs/attributes.md b/docs/attributes.md index 42a6c4f..670561b 100644 --- a/docs/attributes.md +++ b/docs/attributes.md @@ -1,24 +1,24 @@ # File Attributes Attributes are `#`-suffixes on a filename that control how a file is deployed. -They are stripped from the target name, so `config#encrypted#perm:600` deploys as +They are stripped from the target name, so `config#enc#perm:600` deploys as `config`. ``` -.ssh/config#encrypted#perm:600 → ~/.ssh/config, decrypted, mode 0600 +.ssh/config#enc#perm:600 → ~/.ssh/config, decrypted, mode 0600 ``` Order does not matter, and any number can be combined. An unrecognised attribute -is ignored silently but still stripped, so a typo such as `config#templte` -deploys an unrendered `config`. Everything after the first `#` in a name is +is an error: every command that reads the sources stops and names the file, so a +typo such as `config#templte` cannot deploy an unrendered `config`. The same goes +for a `#perm:` mode that is not octal. Everything after the first `#` in a name is attributes, so a file whose real name contains `#` cannot be managed. | Attribute | Effect | |-----------|--------| -| [`#template`](#template) | Render as a Go template | -| [`#tmpl`](#template) | Alias for `#template` | -| [`#encrypted`](#encrypted) | Decrypt with age before deploying | -| [`#symlink`](#symlink) | Recreate a symlink (source must be one) | +| [`#tmpl`](#tmpl) | Render as a Go template | +| [`#enc`](#enc) | Decrypt with age before deploying | +| [`#link`](#link) | Recreate a symlink (source must be one) | | [`#import`](#import) | Target is authoritative; changes flow back to the source | | [`#profile:name`](#profilename) | Only deploy under this profile | | [`#perm:600`](#perm600) | Set file mode | @@ -40,13 +40,13 @@ Attributes are written on directories too. There, only some take effect: The source directory's own name counts as well: a source named `etc#owner-r:root` makes every file in it root-owned. -## `#template` +## `#tmpl` Renders the file with Go's `text/template` before writing it. See [Templates](templates.md) for the available variables and functions. ``` -gitconfig#template +gitconfig#tmpl ``` ```gotemplate @@ -54,19 +54,17 @@ gitconfig#template email = {{ .Vars.email }} ``` -`#tmpl` is an equivalent short form. - Because the deployed content differs from the source, statemate compares the *rendered* output against the target. A template whose variables change is detected as modified even though the source file itself did not change. -## `#encrypted` +## `#enc` Marks the file as age-encrypted in the repository. It is decrypted on apply, so the target holds plaintext while the repository holds ciphertext. ``` -.ssh/id_ed25519#encrypted#perm:600 +.ssh/id_ed25519#enc#perm:600 ``` Deploying needs an age identity (`identity` or `identity_command` in @@ -80,20 +78,20 @@ remove the suffix for you. `mate edit` decrypts to a temporary file, opens your editor, and re-encrypts on save, preserving the original mode. `mate cat` decrypts to stdout. -Combines with `#template`: the file is decrypted first, then rendered. +Combines with `#tmpl`: the file is decrypted first, then rendered. -## `#symlink` +## `#link` Reproduces a symlink at the target. **The source file must itself be a symlink**; statemate reads where it points and recreates a link to that same destination. ```bash cd ~/dotfiles/mysource -ln -s /opt/homebrew/bin/nvim 'bin/vim#symlink' +ln -s /opt/homebrew/bin/nvim 'bin/vim#link' ``` ``` -~/dotfiles/mysource/bin/vim#symlink → /opt/homebrew/bin/nvim +~/dotfiles/mysource/bin/vim#link → /opt/homebrew/bin/nvim ⇓ apply ~/bin/vim → /opt/homebrew/bin/nvim ``` @@ -107,19 +105,19 @@ The link text is copied verbatim, so a relative link resolves relative to the retargeting the source link is a change, and a link that points nowhere on this machine is deployed as-is. -A `#symlink` attribute on a regular file is an error, reported by `mate status` +A `#link` attribute on a regular file is an error, reported by `mate status` and `mate apply` alike: ``` -Error: computing changes: readlink .../f.txt#symlink: invalid argument +Error: computing changes: readlink .../f.txt#link: invalid argument ``` -Because the link destination is copied verbatim, `#template`, `#encrypted`, -`#perm:`, `#owner:` and `#group:` have no effect alongside `#symlink`: there is +Because the link destination is copied verbatim, `#tmpl`, `#enc`, +`#perm:`, `#owner:` and `#group:` have no effect alongside `#link`: there is no content to render, decrypt, chmod, or chown. Links are created without sudo, -so a `#symlink` cannot target a directory you cannot write to. +so a `#link` cannot target a directory you cannot write to. -If a target is a symlink but the source is not marked `#symlink`, statemate treats +If a target is a symlink but the source is not marked `#link`, statemate treats it as a conflict the first time it sees it, rather than silently replacing it. This is what happens on the first apply after migrating from GNU Stow; answer `[o]verwrite` to replace the links with real files. @@ -132,7 +130,7 @@ changes whenever you toggle a setting, so without `#import` every `mate apply` stops to ask about a drifted target, and the answer is always "import". ``` -claude/.claude/settings.json#encrypted#import +claude/.claude/settings.json#enc#import ``` | Source | Target | Result | @@ -154,10 +152,10 @@ still prompts once, because no recorded state exists to say which side is newer. shows the diff in the import direction — target as the new side. `mate apply` prints `← path (imported)`. -Composes with `#encrypted`: imported content is encrypted before being written to +Composes with `#enc`: imported content is encrypted before being written to the source, so the repository never holds plaintext. -**Cannot be combined with `#template`.** Importing would write the rendered output +**Cannot be combined with `#tmpl`.** Importing would write the rendered output over the template, destroying the source. Statemate rejects the combination with an error rather than doing it. @@ -264,7 +262,7 @@ real example, deploying to `/etc` as root: restic/ .mate.yaml # targets: { etc: /etc } etc#owner-r:root/ - ssh/ssh_config.d/50-storagebox.conf#template + ssh/ssh_config.d/50-storagebox.conf#tmpl ``` Note that directories which already exist and carry no perm/owner/group attribute @@ -280,7 +278,7 @@ socket directory, a cache directory some program refuses to create itself: ssh/ .cache/ssh/controlmasters#perm:700/ .gitkeep ← so git stores the directory at all - .ssh/config#template + .ssh/config#tmpl ``` Git cannot store an empty directory, so the keep-file is unavoidable. Hide it diff --git a/docs/commands/mate.md b/docs/commands/mate.md index c7ac270..8ca6682 100644 --- a/docs/commands/mate.md +++ b/docs/commands/mate.md @@ -20,9 +20,13 @@ Use "mate [command] --help" for more information about a command. ## Options ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -h, --help help for mate - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + -h, --help help for mate + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO @@ -50,5 +54,6 @@ Use "mate [command] --help" for more information about a command. * [mate scripts](mate_scripts.md) - Manage scripts * [mate secrets](mate_secrets.md) - Manage secrets * [mate status](mate_status.md) - Show what apply would change +* [mate unmanaged](mate_unmanaged.md) - List files that are not managed * [mate version](mate_version.md) - Print version information diff --git a/docs/commands/mate_add.md b/docs/commands/mate_add.md index 22cf19e..214ef8f 100644 --- a/docs/commands/mate_add.md +++ b/docs/commands/mate_add.md @@ -16,7 +16,9 @@ Unreadable files, broken symlinks, and special files stop the command. The source is the one named with --source, else 'default_source' from the config, else one you pick from a list of the configured sources. The source must -already be listed under 'sources:' and exist as a directory. +already be listed under 'sources:' and exist as a directory. A named source +whose directory does not exist yet is offered for creation: mate creates the +directory and adds the file, but you still list it under 'sources:' yourself. A file outside your home directory, such as one under /etc, needs a source that maps that location (see 'targets:' in a source's .mate.yaml); for a source @@ -53,8 +55,12 @@ mate add [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_apply.md b/docs/commands/mate_apply.md index 60859af..cbf5f5b 100644 --- a/docs/commands/mate_apply.md +++ b/docs/commands/mate_apply.md @@ -71,17 +71,21 @@ mate apply [path] [flags] --dry-run show what would be done without making changes --force overwrite conflicting targets and auto-confirm scripts, hooks and package installs -h, --help help for apply + -l, --list list every file written, and with --dry-run the files and steps of each hook --no-scripts skip all scripts --noconfirm apply without listing the changes and asking first -s, --source string limit to a single source - -V, --verbose count list every file written, and with --dry-run the files and steps of each hook ``` ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_cat.md b/docs/commands/mate_cat.md index 5ff8b17..a12dc29 100644 --- a/docs/commands/mate_cat.md +++ b/docs/commands/mate_cat.md @@ -17,7 +17,7 @@ mate cat [flags] ## Examples ``` - mate cat ssh/.ssh/config#encrypted + mate cat ssh/.ssh/config#enc mate cat ~/.config/app/config.yaml ``` @@ -30,8 +30,12 @@ mate cat [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_check.md b/docs/commands/mate_check.md index 16f4ae8..7d840a1 100644 --- a/docs/commands/mate_check.md +++ b/docs/commands/mate_check.md @@ -32,8 +32,12 @@ mate check [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_clean.md b/docs/commands/mate_clean.md index 1295f39..6fcc4ef 100644 --- a/docs/commands/mate_clean.md +++ b/docs/commands/mate_clean.md @@ -42,8 +42,12 @@ mate clean [path...] [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_config.md b/docs/commands/mate_config.md index 7f51feb..0582291 100644 --- a/docs/commands/mate_config.md +++ b/docs/commands/mate_config.md @@ -15,8 +15,12 @@ Show resolved configuration values, for use in scripts and editor integrations ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_config_source-dir.md b/docs/commands/mate_config_source-dir.md index 6b50bfc..9e65572 100644 --- a/docs/commands/mate_config_source-dir.md +++ b/docs/commands/mate_config_source-dir.md @@ -14,7 +14,7 @@ cd "$(mate config source-dir)" Resolution order matches the rest of mate: the --config flag, then the STATEMATE_DIR environment variable, then source_dir in the local config -(~/.config/statemate/mate.yaml), then the current directory. +(~/.config/statemate/config.yaml), then the current directory. ``` mate config source-dir [flags] @@ -29,8 +29,12 @@ mate config source-dir [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_decrypt.md b/docs/commands/mate_decrypt.md index 1f5de01..3634f7d 100644 --- a/docs/commands/mate_decrypt.md +++ b/docs/commands/mate_decrypt.md @@ -7,13 +7,14 @@ Decrypt a managed file Decrypt a file in place. This reads the encrypted file, decrypts it using the configured age identity, -writes it back, and removes the #encrypted attribute from the filename -wherever it appears among the attributes (config#encrypted#template becomes -config#template). +writes it back, and removes the #enc attribute from the filename +wherever it appears among the attributes (config#enc#tmpl becomes +config#tmpl). -The file can be a managed source file or any file path (e.g. a var_file -in .matedata/). Paths are resolved relative to the current directory, -falling back to the source directory. The #encrypted suffix is optional. +The file can be a managed file, named by its source or its target, or any +other file (e.g. a var_file in .matedata/). Paths are resolved relative to the +current directory, falling back to the source directory. The #enc suffix is +optional. A bare file name is not searched for. The age identity must be configured in mate.yaml: @@ -29,7 +30,7 @@ mate decrypt [flags] ## Examples ``` - mate decrypt nvim/secrets.yaml#encrypted + mate decrypt nvim/secrets.yaml#enc mate decrypt .matedata/secrets.yaml ``` @@ -42,8 +43,12 @@ mate decrypt [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_delete.md b/docs/commands/mate_delete.md index 16e9a55..b27f532 100644 --- a/docs/commands/mate_delete.md +++ b/docs/commands/mate_delete.md @@ -6,8 +6,12 @@ Delete file from source and target Delete a managed file from the source and the target, and stop tracking it. -Give the target path, meaning the deployed file. The target is deleted as well -unless --keep-target is given, and the deletion is confirmed unless --force (or --noconfirm) is. +Give the target path, meaning the deployed file, or the source file. It is a +path like any other: absolute, under ~, or relative to the current directory, +and a relative path that is not there is also looked up in the source +directory. The file may come from any source, not only the current profile's. +The target is deleted as well unless --keep-target is given, and the deletion +is confirmed unless --force (or --noconfirm) is. Hooks matching the removed target run afterwards (see 'mate hooks'); --force also confirms them. Nothing runs with --keep-target. @@ -35,8 +39,12 @@ mate delete [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_diff.md b/docs/commands/mate_diff.md index 0f0c0fd..e7044c5 100644 --- a/docs/commands/mate_diff.md +++ b/docs/commands/mate_diff.md @@ -29,8 +29,12 @@ mate diff [path] [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_doctor.md b/docs/commands/mate_doctor.md index 4c7ce1d..fbf780e 100644 --- a/docs/commands/mate_doctor.md +++ b/docs/commands/mate_doctor.md @@ -5,7 +5,7 @@ Check configuration and dependencies ## Synopsis Check that the configuration loads and validates, that every source directory -exists, that every active hook matches some managed file, that every #encrypted +exists, that every active hook matches some managed file, that every #enc file in any source really is age-encrypted, that the age identity and recipients work, and which package managers are available. @@ -25,8 +25,12 @@ mate doctor [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_edit.md b/docs/commands/mate_edit.md index 5faeaa9..f62e9fc 100644 --- a/docs/commands/mate_edit.md +++ b/docs/commands/mate_edit.md @@ -7,13 +7,15 @@ Edit a managed file Edit a managed file in your editor. Paths are resolved like any other command-line tool: absolute paths are used -as-is, relative paths resolve against the current directory. +as-is, relative paths resolve against the current directory. A relative path +that is not there is also looked up in the source directory. A bare file name +is not searched for. Files under the source directory are opened directly. If you pass a target path (a deployed file), the corresponding source file is opened instead -- mate never edits deployed files in place. -For encrypted files (with the '#encrypted' attribute), the file is decrypted to a +For encrypted files (with the '#enc' attribute), the file is decrypted to a temporary location, opened in the editor, and re-encrypted after saving. The original file permissions are preserved. @@ -47,8 +49,12 @@ mate edit [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_encrypt.md b/docs/commands/mate_encrypt.md index 97e4c5a..374c629 100644 --- a/docs/commands/mate_encrypt.md +++ b/docs/commands/mate_encrypt.md @@ -7,11 +7,12 @@ Encrypt a managed file Encrypt a file in place. This reads the file, encrypts it using the configured age recipients, -writes it back, and adds the #encrypted suffix to the filename. +writes it back, and adds the #enc suffix to the filename. -The file can be a managed source file or any file path (e.g. a var_file -in .matedata/). Paths are resolved relative to the current directory, -falling back to the source directory. +The file can be a managed file, named by its source or its target, or any +other file (e.g. a var_file in .matedata/). Paths are resolved relative to the +current directory, falling back to the source directory. A bare file name is +not searched for. The age recipients must be configured in mate.yaml: @@ -41,8 +42,12 @@ mate encrypt [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_eval.md b/docs/commands/mate_eval.md index 1778115..9871be6 100644 --- a/docs/commands/mate_eval.md +++ b/docs/commands/mate_eval.md @@ -7,7 +7,7 @@ Render a template file Render a file as a template and print the result. Useful for debugging a template, or previewing what apply would deploy. Any -file is rendered, whether or not it is marked #template. An encrypted file is +file is rendered, whether or not it is marked #tmpl. An encrypted file is decrypted first, which needs the age identity. The path is used as given: absolute, starting with ~, or relative to the @@ -20,8 +20,8 @@ mate eval [flags] ## Examples ``` - mate eval git/.config/git/config#template - mate eval --profile work git/.config/git/config#template + mate eval git/.config/git/config#tmpl + mate eval --profile work git/.config/git/config#tmpl ``` ## Options @@ -33,8 +33,12 @@ mate eval [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_forget.md b/docs/commands/mate_forget.md index f5e8c2d..d8021d1 100644 --- a/docs/commands/mate_forget.md +++ b/docs/commands/mate_forget.md @@ -38,8 +38,12 @@ mate forget ... [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_hooks.md b/docs/commands/mate_hooks.md index 13c39ed..8b5daed 100644 --- a/docs/commands/mate_hooks.md +++ b/docs/commands/mate_hooks.md @@ -23,7 +23,7 @@ auto-confirms them and --no-scripts skips them. Hooks declared in a source's .mate.yaml are named <source>/<name> and match only that source's files. Patterns match target paths; source attributes such -as #template and #perm:600 are ignored when included in a pattern. +as #tmpl and #perm:600 are ignored when included in a pattern. ## Options @@ -34,8 +34,12 @@ as #template and #perm:600 are ignored when included in a pattern. ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_hooks_list.md b/docs/commands/mate_hooks_list.md index 2e46911..d414de9 100644 --- a/docs/commands/mate_hooks_list.md +++ b/docs/commands/mate_hooks_list.md @@ -23,8 +23,12 @@ mate hooks list [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_hooks_run.md b/docs/commands/mate_hooks_run.md index 72eec23..f89f60d 100644 --- a/docs/commands/mate_hooks_run.md +++ b/docs/commands/mate_hooks_run.md @@ -19,14 +19,17 @@ mate hooks run [flags] ``` --dry-run show what would be done without running -h, --help help for run - -v, --verbose verbose output ``` ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_init.md b/docs/commands/mate_init.md index d99eb47..b90b1d9 100644 --- a/docs/commands/mate_init.md +++ b/docs/commands/mate_init.md @@ -15,7 +15,7 @@ In a directory that already has a mate.yaml, mate.yml or mate.toml, such as a fresh clone on a new machine, it only registers the directory. Registering writes source_dir to the local config -(~/.config/statemate/mate.yaml), so mate works from any directory. Other +(~/.config/statemate/config.yaml), so mate works from any directory. Other settings in that file are kept. ``` @@ -32,8 +32,12 @@ mate init [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_managed.md b/docs/commands/mate_managed.md index 0debafb..c0acfb4 100644 --- a/docs/commands/mate_managed.md +++ b/docs/commands/mate_managed.md @@ -38,8 +38,12 @@ mate managed [path] [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_packages.md b/docs/commands/mate_packages.md index 7481640..7bdd8cb 100644 --- a/docs/commands/mate_packages.md +++ b/docs/commands/mate_packages.md @@ -15,13 +15,17 @@ Declarative package management across package managers ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO * [mate](mate.md) - Statemate - system configuration management * [mate packages apply](mate_packages_apply.md) - Install missing packages -* [mate packages status](mate_packages_status.md) - Show package sync status +* [mate packages list](mate_packages_list.md) - List packages and whether they are installed diff --git a/docs/commands/mate_packages_apply.md b/docs/commands/mate_packages_apply.md index df24c55..96da19a 100644 --- a/docs/commands/mate_packages_apply.md +++ b/docs/commands/mate_packages_apply.md @@ -28,8 +28,12 @@ mate packages apply [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_packages_list.md b/docs/commands/mate_packages_list.md new file mode 100644 index 0000000..104b32f --- /dev/null +++ b/docs/commands/mate_packages_list.md @@ -0,0 +1,56 @@ +# mate packages list + +List packages and whether they are installed + +## Synopsis + +List declared packages across configured package managers, and whether each +is installed. Each manager's packages are sorted by name. + +Packages can be defined in: + +- mate.yaml packages (global packages) +- mate.yaml profiles.<name>.packages, for the active profile and every + profile it extends +- <source>/.mate.yaml packages, for each active source +- Files listed under 'include', either top-level (global) or in a + profile's 'include' (part of that profile) + +The SOURCE column shows where each package is declared: config, profile:<name>, +or the source directory. + +Use --all to show extra packages not in config. Detecting extras means listing +every installed package, which is noticeably slower, so it is only done when +--all is given. +Use --descriptions to show package descriptions. A description of <unknown> means the +package manager does not recognise the name at all, usually a typo or a package +that only exists on another platform; an empty description means the package +exists but publishes none. + +``` +mate packages list [flags] +``` + +## Options + +``` + --all also show extra packages not in config + -d, --descriptions show package descriptions + -h, --help help for list +``` + +## Options inherited from parent commands + +``` + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug +``` + +## SEE ALSO + +* [mate packages](mate_packages.md) - Manage packages + diff --git a/docs/commands/mate_packages_status.md b/docs/commands/mate_packages_status.md deleted file mode 100644 index 63d8afe..0000000 --- a/docs/commands/mate_packages_status.md +++ /dev/null @@ -1,51 +0,0 @@ -# mate packages status - -Show package sync status - -## Synopsis - -Show package sync status across configured package managers. - -Packages can be defined in: - -- mate.yaml packages (global packages) -- mate.yaml profiles.<name>.packages, for the active profile and every - profile it extends -- <source>/.mate.yaml packages, for each active source -- Files listed under 'include', either top-level (global) or in a - profile's 'include' (part of that profile) - -The SOURCE column shows where each package is declared: config, profile:<name>, -or the source directory. - -Use --all to show extra packages not in config. Detecting extras means listing -every installed package, which is noticeably slower, so it is only done when ---all is given. -Use --verbose to show package descriptions. A description of <unknown> means the -package manager does not recognise the name at all, usually a typo or a package -that only exists on another platform; an empty description means the package -exists but publishes none. - -``` -mate packages status [flags] -``` - -## Options - -``` - --all also show extra packages not in config - -h, --help help for status - -v, --verbose show package descriptions -``` - -## Options inherited from parent commands - -``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile -``` - -## SEE ALSO - -* [mate packages](mate_packages.md) - Manage packages - diff --git a/docs/commands/mate_profile.md b/docs/commands/mate_profile.md index c7787c9..e6bd270 100644 --- a/docs/commands/mate_profile.md +++ b/docs/commands/mate_profile.md @@ -22,8 +22,12 @@ mate profile [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_rename.md b/docs/commands/mate_rename.md index c81f78a..a977947 100644 --- a/docs/commands/mate_rename.md +++ b/docs/commands/mate_rename.md @@ -8,10 +8,15 @@ Rename a managed file in both source and target. This renames the source file, the target file, and updates tracking. +<source> names the file by its source, with or without the attributes, or by +its target. It is a path like any other: absolute, under ~, or relative to the +current directory, and a relative path that is not there is also looked up in +the source directory. A bare file name is not searched for. + Attributes belong to the source file only; the target is always named without them. If <new-name> has no attributes, the source keeps the ones it already has. If it has any, they replace the source's attributes, so give the -full set. #encrypted cannot be added or dropped this way, since that would +full set. #enc cannot be added or dropped this way, since that would not change the content: use 'mate encrypt' or 'mate decrypt'. Hooks matching the old or the new target path run afterwards, with a @@ -24,10 +29,10 @@ mate rename [flags] ## Examples ``` - mate rename nvim/init.lua init.vim - mate rename zsh/.zshrc .zshrc.bak - mate rename git/.gitconfig#template .gitconfig.local - (source becomes .gitconfig.local#template, target .gitconfig.local) + mate rename nvim/.config/nvim/init.lua init.vim + mate rename ~/.zshrc .zshrc.bak + mate rename git/.gitconfig#tmpl .gitconfig.local + (source becomes .gitconfig.local#tmpl, target .gitconfig.local) mate rename ssh/.ssh/config config#perm:600 (changes only the attributes; the target keeps its name) ``` @@ -41,8 +46,12 @@ mate rename [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_scripts.md b/docs/commands/mate_scripts.md index 4943b18..8a2564b 100644 --- a/docs/commands/mate_scripts.md +++ b/docs/commands/mate_scripts.md @@ -31,8 +31,12 @@ triggers it. Editing the script itself does not trigger it; use ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_scripts_list.md b/docs/commands/mate_scripts_list.md index 7c86a49..6a83c98 100644 --- a/docs/commands/mate_scripts_list.md +++ b/docs/commands/mate_scripts_list.md @@ -19,8 +19,12 @@ mate scripts list [flags] ## Options inherited from parent commands ``` - -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) - -p, --profile string override auto-detected profile + -c, --config string config file (default: mate.yaml in $STATEMATE_DIR if set, else in the local config's source_dir, else in the current directory) + --log-file string append a log of what mate changes to this file, and at debug what it decides and runs (also STATEMATE_LOG_FILE, or log_file in the config) + --log-level string log level: debug, info, warn or error (default info; also STATEMATE_LOG_LEVEL, or log_level in the config) + --no-color disable colored output (also off when stdout is not a terminal or NO_COLOR is set) + -p, --profile string override auto-detected profile + -v, --verbose count print log records to stderr: -v for info (what changed), -vv for debug ``` ## SEE ALSO diff --git a/docs/commands/mate_scripts_run.md b/docs/commands/mate_scripts_run.md index e3b2a03..94cd862 100644 --- a/docs/commands/mate_scripts_run.md +++ b/docs/commands/mate_scripts_run.md @@ -19,14 +19,17 @@ mate scripts run