diff --git a/.gitea/actions/rust-cache/action.yml b/.gitea/actions/rust-cache/action.yml index 80b4bc7..73771e4 100644 --- a/.gitea/actions/rust-cache/action.yml +++ b/.gitea/actions/rust-cache/action.yml @@ -1,5 +1,5 @@ -name: Rust Setup + Cache -description: Install Rust toolchain and cache cargo/rustup dependencies for Gitea Actions +name: Rust Setup + Sccache +description: Install Rust and configure sccache for Rust builds in Gitea Actions author: eric inputs: @@ -7,73 +7,120 @@ inputs: description: Rust toolchain to install (e.g., stable, 1.75.0, nightly) default: stable required: false - cache-prefix: - description: Prefix for cache keys. - default: rust + sccache: + description: Enable sccache and set RUSTC_WRAPPER. + default: "true" required: false - cache-version: - description: Cache format version. Bump this to avoid reusing stale or partial cache entries. - default: v3 + sccache-version: + description: Mozilla sccache release tag to install. + default: v0.15.0 + required: false + sccache-bucket: + description: S3 bucket for sccache. Leave empty to use local sccache storage. + default: "" + required: false + sccache-region: + description: S3 region. Use auto for S3-compatible services when an endpoint is provided. + default: auto + required: false + sccache-endpoint: + description: Optional S3-compatible endpoint URL, for example https://s3.example.com. + default: "" + required: false + sccache-s3-use-ssl: + description: Whether the S3 endpoint uses TLS. + default: "true" + required: false + sccache-s3-enable-virtual-host-style: + description: Enable virtual-host-style S3 addressing. + default: "false" + required: false + sccache-key-prefix: + description: Optional S3 key prefix. Defaults to repository/runner/toolchain scope. + default: "" required: false outputs: - cache-hit: - description: "Whether a cache entry was found" - value: ${{ steps.cache-restore.outputs.cache-hit }} + sccache-enabled: + description: Whether sccache was enabled. + value: ${{ steps.configure-sccache.outputs.enabled }} runs: using: composite steps: - - name: Compute cache key - id: cache-key - shell: bash - run: | - OS=$(printf '%s' '${{ runner.os }}' | tr '[:upper:]' '[:lower:]') - ARCH=$(printf '%s' '${{ runner.arch }}' | tr '[:upper:]' '[:lower:]') - { - printf 'toolchain=%s\n' '${{ inputs.toolchain }}' - find . \ - \( -path './.git' -o -path './target' \) -prune -o \ - \( -name Cargo.toml -o -name Cargo.lock -o -name rust-toolchain -o -name rust-toolchain.toml \) \ - -type f -print0 \ - | sort -z \ - | xargs -0 sha256sum 2>/dev/null || true - } > /tmp/rust-cache-fingerprint - HASH=$(sha256sum /tmp/rust-cache-fingerprint | cut -d' ' -f1) - echo "hash=$HASH" >> $GITHUB_OUTPUT - echo "key=${{ inputs.cache-prefix }}-${{ inputs.cache-version }}-$OS-$ARCH-${{ inputs.toolchain }}-$HASH" >> $GITHUB_OUTPUT - echo "restore-prefix=${{ inputs.cache-prefix }}-${{ inputs.cache-version }}-$OS-$ARCH-${{ inputs.toolchain }}-" >> $GITHUB_OUTPUT - - - name: Configure cache upload - shell: bash - run: | - echo "CACHE_UPLOAD_CONCURRENCY=1" >> "$GITHUB_ENV" - - - name: Restore Rust cache - id: cache-restore - env: - ACTIONS_CACHE_SERVICE_V2: "0" - uses: https://gitea.com/actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - ~/.rustup/toolchains - ~/.rustup/settings.toml - target - key: ${{ steps.cache-key.outputs.key }} - restore-keys: | - ${{ steps.cache-key.outputs.restore-prefix }} - - - name: Show Rust cache status - shell: bash - run: | - echo "Rust cache key: ${{ steps.cache-key.outputs.key }}" - echo "Rust cache hit: ${{ steps.cache-restore.outputs.cache-hit }}" - - name: Setup Rust shell: bash run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --profile minimal --default-toolchain ${{ inputs.toolchain }} - echo "$HOME/.cargo/bin" >> $GITHUB_PATH + | sh -s -- -y --profile minimal --default-toolchain '${{ inputs.toolchain }}' + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + + - name: Install sccache + if: ${{ inputs.sccache == 'true' }} + shell: bash + run: | + set -euo pipefail + version='${{ inputs.sccache-version }}' + case "$(uname -s)-$(uname -m)" in + Linux-x86_64) target=x86_64-unknown-linux-musl ;; + Linux-aarch64|Linux-arm64) target=aarch64-unknown-linux-musl ;; + Darwin-x86_64) target=x86_64-apple-darwin ;; + Darwin-arm64|Darwin-aarch64) target=aarch64-apple-darwin ;; + *) + echo "unsupported sccache platform: $(uname -s)-$(uname -m)" >&2 + exit 1 + ;; + esac + asset="sccache-${version}-${target}.tar.gz" + base_url="https://github.com/mozilla/sccache/releases/download/${version}" + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' EXIT + curl -fsSLo "$tmp/$asset" "$base_url/$asset" + curl -fsSLo "$tmp/$asset.sha256" "$base_url/$asset.sha256" + expected="$(tr -d '[:space:]' < "$tmp/$asset.sha256")" + if command -v sha256sum >/dev/null 2>&1; then + actual="$(sha256sum "$tmp/$asset" | cut -d ' ' -f 1)" + else + actual="$(shasum -a 256 "$tmp/$asset" | cut -d ' ' -f 1)" + fi + test "$expected" = "$actual" + tar -xzf "$tmp/$asset" -C "$tmp" + install -m 755 "$(find "$tmp" -type f -name sccache | head -n 1)" "$HOME/.cargo/bin/sccache" + sccache --version + + - name: Configure sccache + id: configure-sccache + if: ${{ inputs.sccache == 'true' }} + shell: bash + run: | + set -euo pipefail + echo "enabled=true" >> "$GITHUB_OUTPUT" + echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV" + echo "SCCACHE_IGNORE_SERVER_IO_ERROR=1" >> "$GITHUB_ENV" + echo "SCCACHE_LOG=${SCCACHE_LOG:-warn}" >> "$GITHUB_ENV" + if [ -n '${{ inputs.sccache-bucket }}' ]; then + repo="${GITHUB_REPOSITORY:-unknown/repository}" + runner_os="${RUNNER_OS:-$(uname -s)}" + runner_arch="${RUNNER_ARCH:-$(uname -m)}" + prefix='${{ inputs.sccache-key-prefix }}' + if [ -z "$prefix" ]; then + prefix="$repo/$runner_os-$runner_arch/${{ inputs.toolchain }}" + fi + echo "SCCACHE_BUCKET=${{ inputs.sccache-bucket }}" >> "$GITHUB_ENV" + echo "SCCACHE_REGION=${{ inputs.sccache-region }}" >> "$GITHUB_ENV" + echo "SCCACHE_S3_USE_SSL=${{ inputs.sccache-s3-use-ssl }}" >> "$GITHUB_ENV" + echo "SCCACHE_S3_KEY_PREFIX=$prefix" >> "$GITHUB_ENV" + if [ -n '${{ inputs.sccache-endpoint }}' ]; then + echo "SCCACHE_ENDPOINT=${{ inputs.sccache-endpoint }}" >> "$GITHUB_ENV" + fi + if [ '${{ inputs.sccache-s3-enable-virtual-host-style }}' = 'true' ]; then + echo "SCCACHE_S3_ENABLE_VIRTUAL_HOST_STYLE=true" >> "$GITHUB_ENV" + fi + fi + + - name: Start sccache + if: ${{ inputs.sccache == 'true' }} + shell: bash + run: | + sccache --start-server + sccache --show-stats diff --git a/README.md b/README.md index e13da9e..31cd9ad 100644 --- a/README.md +++ b/README.md @@ -4,43 +4,68 @@ Reusable Gitea Actions for Rust projects. ## actions/rust-cache -Composite action that restores Rust build caches, installs a Rust toolchain, and saves updated cargo/rustup/target caches after the job. +Composite action that installs Rust, installs a prebuilt Mozilla `sccache` +binary, and configures `RUSTC_WRAPPER=sccache` for later Cargo steps. -### Usage +This action intentionally does not use Gitea `actions/cache` and does not cache +`target/`. Rust build artifacts are cached through `sccache`; callers can use +local sccache storage or an S3-compatible backend. + +### S3 usage + +Create one shared bucket for Rust compiler artifacts, then pass non-secret S3 +settings as inputs and credentials as normal workflow secrets. ```yaml jobs: build: runs-on: ubuntu-latest steps: - - uses: https://gitea.com/actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Setup Rust + Cache + - name: Setup Rust + sccache uses: https://git.ericxliu.me/eric/actions-rust/.gitea/actions/rust-cache@main with: - toolchain: stable # optional, defaults to stable - cache-prefix: rust # optional, defaults to rust - cache-version: v3 # optional, defaults to v3 + toolchain: stable + sccache-bucket: sccache + sccache-region: auto + sccache-endpoint: https://s3.example.com + sccache-key-prefix: my-org/my-repo/linux-x64/stable + env: + AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_AWS_SECRET_ACCESS_KEY }} - - run: cargo build --release - run: cargo test + - run: sccache --show-stats ``` -### What it does +### Local usage -1. **Cache key** — Hashes root/workspace `Cargo.toml`, `Cargo.lock`, and `rust-toolchain*` files without relying on `hashFiles()`. -2. **Cache restore/save** — Restores `~/.cargo/registry`, `~/.cargo/git`, `~/.rustup/toolchains`, and `target/` using Gitea's native `actions/cache`. The cache action saves updated paths in its post step after later build/test steps have populated them. -3. **Rust install** — Runs `rustup` with the minimal profile to install or verify the requested toolchain. -4. **PATH setup** — Appends `$HOME/.cargo/bin` to `$GITHUB_PATH` so cargo/rustc are available in subsequent steps. +Omit `sccache-bucket` to use the runner-local sccache cache: -The cache upload is serialized with `CACHE_UPLOAD_CONCURRENCY=1` because Gitea -act_runner's cache server stores chunk metadata in BoltDB and can return 500s -under large parallel uploads. - -### Cache key format - -``` -{cache-prefix}-{cache-version}-{runner.os}-{runner.arch}-{toolchain}-{hash(manifests/toolchain files)} +```yaml +- name: Setup Rust + local sccache + uses: https://git.ericxliu.me/eric/actions-rust/.gitea/actions/rust-cache@main + with: + toolchain: stable ``` -Warm builds run in ~40–60s vs ~3min for a cold build. +### Inputs + +- `toolchain`: Rust toolchain to install. Defaults to `stable`. +- `sccache`: Set to `"false"` to install Rust without configuring sccache. +- `sccache-version`: Mozilla sccache release tag. Defaults to `v0.15.0`. +- `sccache-bucket`: S3 bucket name. Empty means local sccache storage. +- `sccache-region`: S3 region. For S3-compatible endpoints, `auto` is usually right. +- `sccache-endpoint`: Optional S3-compatible endpoint URL. +- `sccache-s3-use-ssl`: Whether the endpoint uses TLS. Defaults to `"true"`. +- `sccache-s3-enable-virtual-host-style`: Set to `"true"` only for endpoints that require virtual-host-style addressing. +- `sccache-key-prefix`: Optional cache key prefix. Defaults to repository, runner, and toolchain scope. + +### Notes + +- Do not pass secret values as action inputs. Pass standard AWS environment + variables from workflow secrets. +- `SCCACHE_IGNORE_SERVER_IO_ERROR=1` is set so a cache outage falls back to + local compilation instead of failing the build. +- Add `sccache --show-stats` after build/test steps to inspect cache hit rates.