Magic Nix Cache

repository·main·Indexed 18 days ago

https://github.com/determinatesystems/magic-nix-cache

A free, zero-configuration binary cache for Nix on GitHub Actions that leverages the built-in GitHub Actions cache to share builds between runs. It includes the gha-cache crate (v0.1.0), which provides an asynchronous API to the GitHub Actions Cache service for uploading binary blobs and retrieving download URLs using credentials from $ACTIONS_CACHE_URL and $ACTIONS_RUNTIME_TOKEN.

Tokens
9.3K
Snippets
35
Records
46
Agent score
62%

What's inside magic-nix-cache

  1. Overview of gha-cache

    main

    The gha-cache crate provides an asynchronous API to the GitHub Actions Cache (GHAC) service. It allows you to upload binary blobs using AsyncRead streams and obtain presigned URLs to download them.

    GHAC identifies blobs using a 3-tuple:

    • Cache Key: A developer-specified name.
    • Cache Version: A string acting as a namespace (e.g., a SHA256 hash of paths/compression). This crate allows users to provide arbitrary bytes to mutate this hash.
    • Cache Scope: The branch containing the workflow run that uploaded the blob.
  2. Understand the GitHub Actions Cache APIs used by gha-cache

    main

    The gha-cache crate specifically supports the GitHub Actions Cache API (the private API used by GHAC), rather than the public GitHub REST API.

    To use this crate, you must provide credentials for the following endpoints:

    • Endpoint: $ACTIONS_CACHE_URL
    • Token: $ACTIONS_RUNTIME_TOKEN

    Note: This crate does not currently support the GitHub REST API (used for listing and deleting blobs via $GITHUB_API_URL).

  3. Set up credentials for gha-cache using the keygen workflow

    main

    Because GHAC uses private APIs requiring special tokens, you must retrieve them from a workflow run. The recommended method is using the keygen workflow provided in this repository:

    1. Generate an age encryption key:
      nix shell nixpkgs#age --command age-keygen -o key.txt
    2. Add the Public Key from that file as a repository secret named AGE_PUBLIC_KEY.
    3. Trigger the keygen workflow in your repository.
    4. The workflow will output a command that allows you to decrypt the necessary credentials.
    nix shell nixpkgs#age --command age-keygen -o key.txt
  4. Install Magic Nix Cache in GitHub Actions

    main

    To use Magic Nix Cache, add the DeterminateSystems/magic-nix-cache-action@main to your GitHub Actions workflow after installing Nix. This provides a zero-configuration binary cache that uses GitHub Actions' built-in cache to share builds between workflow runs.

    Important: You must ensure your job has the necessary permissions for the GitHub Actions cache to work, specifically id-token: write.

    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - uses: DeterminateSystems/nix-installer-action@main
      - uses: DeterminateSystems/magic-nix-cache-action@main
      - run: nix flake check
  5. Understand the Nix manifest format

    main

    Nix binary cache manifests (typically found in .narinfo files or /nix-cache-info) use a simple, flat key-value format where a colon (:) acts as the delimiter. The format is not strictly defined by a formal specification, and official Nix implementations often handle serialization manually.

    Example of a /nix-cache-info file:

    StoreDir: /nix/store
    WantMassQuery: 1
    Priority: 40
  6. Configure FlakeHub authentication

    main

    FlakeHub authentication can be sourced in two ways:

    1. Determinate Nixd: If determinate-nixd is available (detected via the presence of the determinate-nixd.socket at /nix/var/determinate/determinate-nixd.socket), the cache will automatically use the netrc file located at /nix/var/determinate/netrc.
    2. Manual Netrc: If determinate-nixd is missing, you must provide a path to a netrc file using the --flakehub-api-server-netrc <PATH> flag.
  7. The .narinfo file format

    main

    A .narinfo file contains metadata for a Nix binary cache object. It is used by Nix to locate, verify, and download NAR (Nix Archive) files. The format is a key-value text format where keys are capitalized (e.g., StorePath, URL, NarHash).

    Key fields include:

    • StorePath: The full path in the Nix store.
    • URL: The location of the object (can be relative to the cache base or an absolute URL).
    • Compression: The compression algorithm used (e.g., xz, zstd).
    • FileHash: The hash of the compressed file.
    • NarHash: The hash of the uncompressed NAR archive.
    • References: A space-delimited list of other store paths referenced by this object.
    StorePath: /nix/store/p4pclmv1gyja5kzc26npqpia1qqxrf0l-ruby-2.7.3
    URL: nar/1w1fff338fvdw53sqgamddn1b2xgds473pv6y13gizdbqjv4i5p3.nar.xz
    Compression: xz
    FileHash: sha256:1w1fff338fvdw53sqgamddn1b2xgds473pv6y13gizdbqjv4i5p3
    FileSize: 4029176
    NarHash: sha256:1impfw8zdgisxkghq9a3q7cn7jb9zyzgxdydiamp8z2nlyyl0h5h
    NarSize: 18735072
    References: 0d71ygfwbmy1xjlbj1v027dfmy9cqavy-libffi-3.3 0dbbrvlw2rahvzi69bmpqy1z9mvzg62s-gdbm-1.19
    Deriver: bidkcs01mww363s4s7akdhbl6ws66b0z-ruby-2.7.3.drv
    Sig: cache.nixos.org-1:GrGV/Ls10TzoOaCnrcAqmPbKXFLLSBDeGNh5EQGKyuGA4K1wv1LcRVb6/sU+NAPK8lDiam8XcdJzUngmdhfTBQ==
  8. Handle circuit breaker status

    main

    The client includes a built-in circuit breaker to protect against GitHub Actions rate limiting (HTTP 429) or service overloads (502 Bad Gateway / Resource Exhausted).

    When the client detects these errors, it trips the circuit breaker and executes the provided CircuitBreakerTrippedCallback. Once tripped, subsequent calls to upload_file or get_file_url will immediately return Error::CircuitBreakerTripped without making network requests.

    You can check the current status using circuit_breaker_tripped().

  9. Nix Manifest Fingerprint format

    main

    The fingerprint is a specific string format used to identify a NAR info object. It is used for authentication and identification in Nix manifests. The format is a semicolon-delimited string containing the store path, NAR hash, NAR size, and a comma-delimited list of references.

    Format: 1;{storePath};{narHash};{narSize};{commaDelimitedReferences}

    1;{storePath};{narHash};{narSize};{commaDelimitedReferences}
  10. Control cache usage with CacheTrinary

    main

    The --use-gha-cache and --use-flakehub flags use a ternary logic system to determine cache preference:

    • No value provided: NoPreference. The application will attempt to enable the cache if appropriate (e.g., if determinate-nixd is available for FlakeHub).
    • --use-gha-cache (or --use-flakehub): Disabled. The cache is explicitly turned off.
    • --use-gha-cache=enabled (or --use-flakehub=enabled): Enabled. The cache is explicitly turned on.
  11. Handling GitHub Actions Cache Rate Limits

    main

    The GitHub Actions Cache has rate limits on reads and writes. If your project is very large, you may see HTTP error 418 or API error (429 Too Many Requests) in your logs.

    Magic Nix Cache and Nix handle these limits gracefully:

    • Pulling dependencies: If the rate limit is hit while pulling, the workflow will simply perform more local builds instead of failing.
    • Uploading to cache: If the rate limit is hit while uploading, the remaining store paths will be uploaded during the next workflow run.
    • Job failures: If a job fails or is canceled, any successfully built paths are still stored in the cache via the runs.post step.
    error: unable to download 'http://127.0.0.1:37515/<...>': HTTP error 418
           response body:
           GitHub API error: API error (429 Too Many Requests): StructuredApiError { message: "Request was blocked due to exceeding usage of resource 'Count' in namespace ''." }
  12. Full GitHub Actions Workflow Example

    main

    This is a complete example of a Linux or macOS workflow configured to use Magic Nix Cache. It includes the required permissions and the sequence of steps to check out code, install Nix, initialize the cache, and run a Nix command.

    name: CI
    
    on:
      push:
      pull_request:
    
    jobs:
      check:
        runs-on: ubuntu-22.04
        permissions:
          contents: read
          id-token: write
        steps:
          - uses: actions/checkout@v4
          - uses: DeterminateSystems/nix-installer-action@main
          - uses: DeterminateSystems/magic-nix-cache-action@main
          - run: nix flake check