No description
  • JavaScript 96.4%
  • Nix 3.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Matt b13466a16f
Some checks failed
Smoke / smoke (push) Failing after 38s
push flake inputs to the cache by default
nix substitutes locked inputs from binary caches before refetching
upstream (fetchers.cc: ensurePath on the narHash-derived store path),
so pushing nix flake archive paths lets runners pull inputs from attic
instead of downloading tarballs from github in every ephemeral job.
Opt out with push-flake-inputs: false.
2026-07-03 04:02:47 -04:00
.forgejo/workflows fix: use attic use for authenticated substituter config 2026-04-17 20:01:07 -04:00
docs/superpowers docs: add implementation plan for setup-attic Forgejo Action 2026-04-17 19:05:56 -04:00
fixtures/test-flake test: add smoke fixture flake with time-stamped derivation 2026-04-17 19:25:20 -04:00
lib push flake inputs to the cache by default 2026-07-03 04:02:47 -04:00
test push flake inputs to the cache by default 2026-07-03 04:02:47 -04:00
.envrc chore: bootstrap repo with flake scaffold and action design spec 2026-04-17 19:02:25 -04:00
.gitignore chore: scaffold Node action repo (package.json, node in devShell) 2026-04-17 19:14:25 -04:00
action.yml push flake inputs to the cache by default 2026-07-03 04:02:47 -04:00
flake.lock chore: bootstrap repo with flake scaffold and action design spec 2026-04-17 19:02:25 -04:00
flake.nix chore: scaffold Node action repo (package.json, node in devShell) 2026-04-17 19:14:25 -04:00
main.js push flake inputs to the cache by default 2026-07-03 04:02:47 -04:00
package.json chore: scaffold Node action repo (package.json, node in devShell) 2026-04-17 19:14:25 -04:00
post.js push flake inputs to the cache by default 2026-07-03 04:02:47 -04:00
README.md push flake inputs to the cache by default 2026-07-03 04:02:47 -04:00

setup-attic

A Forgejo Action that configures a CI job to pull from and push to a self-hosted attic Nix binary cache. One step in your workflow; both phases handled automatically via Forgejo's main/post action hooks.

Usage

- uses: actions/checkout@v6

- uses: matt/setup-attic@v1
  with:
    endpoint: https://cache.example.com
    cache: my-cache
    token: ${{ secrets.ATTIC_TOKEN }}
    flake-refs: .#my-package

- run: nix build .#my-package --no-link

The main phase installs the attic client, wires the cache up as a Nix substituter (so your builds pull from it), and authenticates. The post phase resolves paths:/flake-refs: to realised store paths and pushes them — skipping anything that wasn't actually built, with no error.

Inputs

Name Required Description
endpoint yes Attic server base URL, e.g. https://cache.example.com.
cache yes Cache name on the server, e.g. my-cache.
token yes Attic JWT, scoped to at least push on the target cache. Always source from a secret.
paths no Newline-separated list of store paths or symlinks (like ./result) to push.
flake-refs no Newline-separated list of flake references (like .#my-package) to resolve and push at job end.
push-flake-inputs no Push the flake's source and all locked inputs at job end. Default true; set false to disable. Nix substitutes locked inputs from the cache instead of refetching them upstream, so later jobs skip the GitHub downloads.
fail-on-push-error no true to fail the job on push failure. Default false — cache is an optimization, main work already succeeded.
push-jobs no Maximum parallel upload processes for attic push. Default 5 (the attic client default). Lower this to reduce load on the attic server.

At least one of paths, flake-refs, or push-flake-inputs must be active.

Multiple entries, newline-separated:

with:
  flake-refs: |
    .#foo
    .#bar
    .#baz

Obtaining a token

On the attic server:

attic make-token --sub ci --validity 90d --push my-cache

Copy the output into your repo's secrets as ATTIC_TOKEN (or whatever name you reference in the workflow). Refer to attic's docs for finer-grained token options.

How it works

  • Node action with main: and post: hooks, following the same pattern as actions/cache. One step in your workflow; the post phase fires automatically after subsequent steps finish.
  • Zero npm dependencies. Pure Node stdlib + child_process. No dist/ bundle.
  • Attic client is realised on-demand via nix run nixpkgs#attic-client. First invocation pays the build cost; subsequent runs on the same host are a cache lookup.
  • Substituter config is append-only. The cache is added via extra-substituters / extra-trusted-public-keys in the user-level nix.conf (plus a netrc for private caches), which merge with the system config. attic use is deliberately not used: it writes a bare substituters line that silently overrides any host- or image-level substituter policy (e.g. an internal pull-through cache).
  • Server name in the attic config is deterministically derived from <endpoint-hostname-slug>-<cache>, so you can use this action multiple times in one job pointing at different caches (or different servers) without collisions.
  • Missing paths (a declared flake-ref that wasn't built, a ./result symlink that doesn't exist) are skipped with a warning rather than failing the job. Mirrors actions/cache's behavior.

Security

  • Token is a secret. Always source token: from ${{ secrets.* }}. Never hardcode.
  • No cross-job leakage. Forgejo's runner creates a fresh container per job (ReuseContainers: false). Credentials written to ~/.config/attic/config.toml die with the container.
  • Within-job exposure. Other actions running in the same job can read the attic credentials file while the job is in progress. If you compose this action with untrusted third-party actions in the same workflow, be aware.

License

MIT.