flake-like toml nix pins, lazily fetched and transformed
maintains pins.toml (what you want), pins.lock.json (what's fetched),
and a vendored default.nix resolver to consume locked inputs without
nix's flake machinery — all tucked into ./.tack/ so your repo root
stays clean.
tack init creates ./.tack/ (override with $TACK_DIR) containing:
pins.tomlinputs and shorturl schemes, hand-editablepins.lock.jsonresolved inputs, written bytack update, read by nixdefault.nixthe resolver;import ./.tackgives a name -> input attrset
let inputs = import ./.tack;
in inputs.nixpkgs.legacyPackages.x86_64-linux.helloor from a flake:
outputs = { self, ... }@args:
let inputs = (import ./.tack) {
overrides = args.tackOverrides or { };
}; in {
packages.x86_64-linux.default =
inputs.nixpkgs.legacyPackages.x86_64-linux.hello;
};tack warns when default.nix has drifted from the running binary, as long as
the tack-managed comment at its top is present. run tack init --resolver to
update it, or delete that comment to fork the resolver and silence the warning.
the @args form lets a parent tack project override this project's
pins through the follows machinery. omit it for a closed
project that doesn't want to be re-composed.
legacy ./inputs.nix at repo root is detected and preserved as-is.
tack init [--force] [--resolver] [--flake]
scaffold .tack/ (--resolver writes only default.nix,
--flake also a wired flake.nix)
tack update [names...] [--accept] fetch latest, rewrite lock
tack look [names...] [--verbose|-v] report pins with newer upstream revs
tack add <name> <url> [--fetch|--fixed [--unpack tarball|file]]
[--dir <d>] [--submodules] [--follows c=p]...
tack rm <name>
tack alias <name> <template> define a shorturl scheme
tack alias --rm <name> remove one
tack dedup report inputs reachable from multiple pins
tack dedup reports inputs reachable from more than one of your pins, whether
direct or transitive, and recurses through the pins of your pins indefinitely.
its output is two sub-blocks of ready-to-paste [all_follow] rules. the first
block refers to existing top-level pins, the subsequent one refers to inputs tack
will synthesise on the next tack update. targets with multiple aliases collapse
into a single array entry.
flake(default) — evaluate the input'sflake.nix, expose its outputsfetch— source tree only, no flake eval. legacyflake = falsefixed— hash-locked download; won't drift,tack updaterefuses to silently relock (use--acceptif you want to)
[inputs.release]
url = "https://example.com/release-1.2.3.tar.gz"
type = "fixed"
# unpack = "tarball" | "file" # auto-detected from the URLgithub:owner/repo[/ref]tarball via codeloadgit+https://.../git+ssh://...any git remote;?ref=<branch>/?rev=<sha>to pin,submodules = trueto recursehttps://.../http://...raw tarball, where the format is inferred from the extension (e.g..tar,.tar.gz/.tgz,.tar.xz/.txz).
scheme:rest expands by substituting rest into the template {path}
[shorturls]
gh = "github:{path}"
[inputs.coolproject]
url = "gh:owner/coolproject"point a pin's input at one of your top-level pins instead of its own lock
[inputs.foo]
url = "gh:owner/foo"
follows = { nixpkgs = "nixpkgs" } # foo's nixpkgs -> your nixpkgs pinall_follow applies a rule to every pin that has a matching input. two value
shapes are accepted:
[all_follow]
# alias -> target. every input named fenix follows your top-level fenix pin
fenix = "fenix"
# target -> [aliases]. the key is the canonical target, and the key plus every
# array member alias to it. one row covers many aliases of the same target
nixpkgs = ["nixpkgs-stable", "nixpkgs-unstable"]
[inputs.bar]
url = "gh:owner/bar"
exclude_follow = ["nixpkgs"] # ...except bar'swhen a target named in [all_follow] isn't itself a top-level [inputs] pin,
tack update synthesises a lock entry for it by walking every top-level
flake.lock, collecting the observed revs of the aliased name, and preferring
the branch-ahead rev when GitHub can compare the commits. when comparison is
unavailable or histories have diverged, it falls back to lastModified. the
resolver then treats the synthetic entry as a default flake, or as a bare
source tree when its repo has no flake.nix. this lets you dedup transitive
inputs (e.g. crane) without declaring them as top-level pins you don't
actually consume.
follows reach an upstream's tack pins too, provided the upstream wired
its flake for it (see publishing). when an upstream has
both a flake input and a tack pin under the same name, a follow on that
name reaches both. scope it with a flake: or tack: prefix to hit
just one side:
[inputs.bar]
follows = { "flake:systems" = "systems", "tack:nixpkgs" = "nixpkgs" }if third parties consume your project as a tack pin, wire your flake so
their follows can reach your pins — thread tackOverrides
through outputs as in layout. tack init --flake writes a
wired flake for you; on an existing flake tack init prints the
snippet. without the wiring, downstream overrides don't reach your
pins.
nix develop # rust toolchain + openssl/libgit2
nix build # the binary
EUPL-1.2. see LICENSE