Forgejo-native composite action: install sccache for Rust builds, no GitHub PAT required
  • Shell 96.8%
  • Makefile 3.2%
Find a file
Stephen Way 6e5e2ceb86
All checks were successful
test / unit (push) Successful in 7s
test / integration (push) Successful in 5s
test / install (v0.15.0) (push) Successful in 6s
test / install (v0.14.0) (push) Successful in 7s
test / install-without-v-prefix (push) Successful in 5s
test / reinstall-on-version-change (push) Successful in 7s
Release prep for v1.1.2: stamp CHANGELOG
Move the Unreleased entries (SCCACHE_PATH export, cache-stats and Mozilla
differences docs) under v1.1.2 (2026-05-28).
2026-05-28 06:36:20 -07:00
.forgejo Split CI unit job into offline unit and networked integration 2026-05-27 21:52:30 -07:00
.githooks Add git hooks and a Makefile 2026-05-27 21:29:48 -07:00
scripts Export SCCACHE_PATH to $GITHUB_ENV 2026-05-28 06:30:15 -07:00
tests Export SCCACHE_PATH to $GITHUB_ENV 2026-05-28 06:30:15 -07:00
.editorconfig Add editorconfig, gitignore, and Forgejo issue/PR templates 2026-05-27 21:29:55 -07:00
.gitignore Ignore local .claude/ agent state 2026-05-28 06:36:20 -07:00
action.yml Move install logic into scripts/install.sh 2026-05-27 21:29:35 -07:00
CHANGELOG.md Release prep for v1.1.2: stamp CHANGELOG 2026-05-28 06:36:20 -07:00
CONTRIBUTING.md Docs: reflect the standalone script, tests, hooks, and make targets 2026-05-27 21:30:02 -07:00
LICENSE Initial sccache-action: composite action, no GitHub PAT, Linux x86_64+aarch64 2026-05-27 14:20:23 -07:00
Makefile Add git hooks and a Makefile 2026-05-27 21:29:48 -07:00
README.md Docs: cache-stats step and differences from the Mozilla action 2026-05-28 06:30:23 -07:00
SECURITY.md Docs: SECURITY, CONTRIBUTING, CHANGELOG; README for new behavior 2026-05-27 14:37:32 -07:00

sccache-action

test

Install sccache on Forgejo Actions runners. Composite action, no JavaScript, no GitHub PAT required, with SHA-256 verification of the downloaded binary against the upstream sidecar.

Why not Mozilla-Actions/sccache-action?

Mozilla's action requires a GitHub token via with: token: to call api.github.com for release lookup. On GitHub Actions the workflow's auto-provided secrets.GITHUB_TOKEN is a GitHub token and that works. On Forgejo Actions, secrets.GITHUB_TOKEN is a Forgejo token: it can't authenticate to GitHub's API, and the action fails before installing anything:

::error::Error: Input required and not supplied: token

You could provision a real GitHub PAT just to satisfy the action, but it's a new long-lived secret to rotate for what should be a single curl. This action skips the API call entirely, downloads the public release tarball directly, and verifies it against the SHA-256 sidecar Mozilla publishes alongside.

Usage

- uses: https://rasterhub.com/rasterstate/sccache-action@v1

That's it. Defaults to sccache v0.15.0 with checksum verification on.

With explicit version pin (recommended for reproducible CI):

- uses: https://rasterhub.com/rasterstate/sccache-action@v1
  with:
    version: v0.15.0

Full workflow pattern for a Rust job backed by S3:

jobs:
  test:
    runs-on: [self-hosted, Linux]
    env:
      RUSTC_WRAPPER: sccache
      SCCACHE_BUCKET: my-bucket
      SCCACHE_ENDPOINT: https://fsn1.your-objectstorage.com
      SCCACHE_REGION: fsn1
      SCCACHE_S3_USE_SSL: "true"
      SCCACHE_S3_KEY_PREFIX: ${{ github.repository }}
      AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_AWS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_AWS_SECRET_KEY }}
    steps:
      - uses: actions/checkout@v6
      - uses: https://github.com/dtolnay/rust-toolchain@stable
      - uses: https://rasterhub.com/rasterstate/sccache-action@v1

      - run: cargo build --release

      - name: sccache stats
        if: always()
        run: sccache --show-stats

Inputs

Name Default Description
version v0.15.0 sccache release tag. Pinned by default so a release that breaks the cache layout doesn't silently land. Accepts v0.15.0 or 0.15.0.
install-dir /usr/local/bin Where to drop the binary. Uses sudo install if the directory isn't writable as the runner user.
verify-checksum true Verify the downloaded tarball against the upstream .sha256 sidecar. Fails closed on mismatch. Only disable if your mirror doesn't serve sidecar files.

Outputs

Name Description
version Resolved sccache tag actually installed, with leading v (e.g. v0.15.0).
install-path Absolute path to the installed sccache binary.

Example:

- id: sccache
  uses: https://rasterhub.com/rasterstate/sccache-action@v1
- run: |
    echo "installed ${{ steps.sccache.outputs.version }} at ${{ steps.sccache.outputs.install-path }}"

Environment

The action exports SCCACHE_PATH (absolute path to the installed binary) to $GITHUB_ENV, so later steps can reference $SCCACHE_PATH directly. This mirrors the upstream Mozilla action. It is the same value as the install-path output; use whichever fits your step.

What it does

  1. If sccache is already on PATH at the requested version, skip install (idempotent across multiple invocations on a long-lived runner).
  2. Detect OS + arch (Linux x86_64, Linux aarch64, macOS x86_64, macOS arm64).
  3. Download the matching sccache-${VERSION}-${TRIPLE}.tar.gz from https://github.com/mozilla/sccache/releases/download/..., public asset, no auth, with retries.
  4. Download the matching .sha256 sidecar and verify the tarball's digest. Fail closed on mismatch.
  5. Extract and install the binary into install-dir (sudo if needed).
  6. Append install-dir to $GITHUB_PATH and reset bash's command cache so subsequent steps and the in-step --version smoke test see the new binary.
  7. Export SCCACHE_PATH to $GITHUB_ENV and run sccache --version as a smoke test.

Cache stats

The upstream Mozilla action ships a JavaScript post step that prints sccache --show-stats and renders a job-summary table after the build. A composite action runs all its steps at the point it's invoked, before your build, so it can't do post work without bundling JavaScript, which this action deliberately avoids.

To get the same job-summary table, add your own step at the end of the job. It's self-contained, needs nothing from this action, and works on Forgejo and GitHub runners alike:

- name: sccache stats
  if: always()
  shell: bash
  run: |
    {
      echo '### sccache stats'
      echo '```'
      sccache --show-stats
      echo '```'
    } >>"$GITHUB_STEP_SUMMARY"

sccache --show-stats --stats-format=json is available if you want to parse the numbers (e.g. with jq) into a custom table. The one thing this can't reproduce is the upstream live notice annotation on the run; that path is JavaScript-only.

Supported runners

  • Linux x86_64 (x86_64-unknown-linux-musl)
  • Linux ARM64 (aarch64-unknown-linux-musl)
  • macOS x86_64 (x86_64-apple-darwin)
  • macOS ARM64 (aarch64-apple-darwin)

Windows runners aren't supported; the upstream sccache release exists but the install path and sudo assumptions are Unix-shaped. The action errors out cleanly on non-Linux/Darwin platforms rather than failing later.

Versioning

Tags follow vMAJOR rolling-pointer style so consumers can @v1 and get patch/minor fixes without re-pinning. Concrete release tags (v1.0.0, v1.0.1, ...) exist alongside for users who want full pinning.

A default-version bump (e.g. defaulting sccache from v0.15.0 to a later release) is not a major bump of this action; see CHANGELOG.md for what shipped when. Pin version: explicitly if you don't want default bumps to land.

Why a composite action and not JS

Composite actions are pure YAML, no dist/ bundle to keep in sync with source, no node_modules, no build step. The job we do here (curl + verify + install) is a few shell lines; the JS overhead would be larger than the work. The whole thing is two small files: action.yml (the composite wrapper) and scripts/install.sh (the install logic). Keeping the logic in a standalone script is what lets it be shellchecked, formatted, and unit-tested directly. Readable, auditable, and forkable without a toolchain.

Differences from Mozilla-Actions/sccache-action

This action is a pure-shell reimplementation of the install half of the upstream Mozilla action, built for Forgejo runners where the auto-provided secrets.GITHUB_TOKEN is a Forgejo token and can't authenticate to GitHub's API. The behavior is intentionally trimmed; here's the full parity picture.

What we do that upstream doesn't:

  • No GitHub token or api.github.com call. Upstream requires token: to resolve "latest" and fails on Forgejo without a real GitHub PAT.
  • verify-checksum is a real input (upstream hard-codes verification on), and our verify strips all whitespace and length-checks the 64 hex chars, so it won't trip on a <hash> <file> style sidecar.
  • install-dir input and version/install-path outputs. Upstream has none of these.
  • Explicit --retry/--retry-connrefused, a temp-dir cleanup trap, and v-prefix normalization (0.15.0 and v0.15.0 are equivalent).

What upstream does that we deliberately don't:

  • Resolve the latest release automatically. We require a pinned version (default v0.15.0); auto-latest is exactly what needs the token.
  • A JavaScript post step for stats (annotation + job-summary table). See Cache stats for the self-contained step that gets you the summary table; the live annotation stays JS-only.
  • Windows (pc-windows-msvc) and 32-bit ARM Linux (armv7/musleabi). We error out cleanly on unsupported platforms instead.
  • Export the GitHub Actions cache backend variables (ACTIONS_CACHE_SERVICE_V2, ACTIONS_RESULTS_URL, ACTIONS_RUNTIME_TOKEN) used by sccache's gha cache backend. Those are GitHub-runtime specific and don't apply to the Forgejo + S3 setup this action targets.

What differs in approach:

  • Idempotence. Upstream reads/writes the GitHub tool-cache (RUNNER_TOOL_CACHE) to skip re-download across jobs. We skip when the requested version is already on PATH. On ephemeral autoscaler VMs both re-download; on a persistent runner upstream avoids more re-downloads.
  • SCCACHE_PATH is exported the same way (to $GITHUB_ENV).

Security

See SECURITY.md for the threat model and disclosure path.

Contributing

See CONTRIBUTING.md for the local test loop and release procedure.

License

MIT. See LICENSE.