Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

136 linesCodeBlameRaw
1#!/usr/bin/env bash
2# sccache for a workflow's Rust builds. Every rustc call that makes a
3# library is looked up by what it compiles (sources, flags, toolchain,
4# dependencies) in the repository's Actions cache, the one `actions/cache`
5# uses, through sccache's GitHub Actions backend. A checkout gives every
6# source file a new mtime, so Cargo calls rustc again for the workspace's
7# own crates however much of `target/` was restored; with sccache, the
8# crates whose inputs did not change come back from the cache instead of
9# being compiled. Binaries, cdylibs (a Worker's own crate), proc macros and
10# test harnesses are linked, and sccache compiles those as before.
11#
12# bash scripts/sccache.sh install # a step before the first cargo
13# bash scripts/sccache.sh stats # the last step, with if: always()
14#
15# install uses the pinned release when it is already installed (g1t's
16# runner image has it), or downloads it and checks its sha256; starts the
17# server (which reads and writes a check entry in the cache), and only then
18# sets RUSTC_WRAPPER for the steps after it. If anything there fails, the
19# job builds as it did without sccache, and the step says why. A release
20# tag's run only reads: a run restores from its own ref, then its base, then
21# main, so what a tag saved no later run would read
22# (docs.g1t.sh/guides/actions/#the-cache); it reads what main's runs saved,
23# the Caches workflow's among them.
24# stats prints the server's hits and misses, and adds them to the job's
25# summary.
26#
27# See the Actions guide's "Caching Rust builds".
28set -euo pipefail
29
30VERSION=0.18.0
31TARGET=x86_64-unknown-linux-musl
32# sccache-v0.18.0-x86_64-unknown-linux-musl.tar.gz, from the release's
33# .sha256 file.
34SHA256=45f1447fbe231e3037bde351ef70677dd212216c8d62ae7ca409fecc4d6acc89
35
36BIN="$HOME/.cargo/bin"
37SCCACHE="$BIN/sccache"
38
39# What sccache and Cargo read, here and in the steps after this one.
40export SCCACHE_GHA_ENABLED=true
41# One server for the whole job: its statistics are the job's.
42export SCCACHE_IDLE_TIMEOUT=0
43# If the server goes away mid-build, rustc runs without it.
44export SCCACHE_IGNORE_SERVER_IO_ERROR=1
45# sccache cannot cache incremental builds; a fresh checkout gains nothing
46# from them anyway.
47export CARGO_INCREMENTAL=0
48# Read only, on a tag: sccache's GHA backend then makes no CreateCacheEntry
49# call, and still looks every compile up.
50if [ "${GITHUB_REF_TYPE:-}" = tag ]; then
51 export SCCACHE_GHA_RW_MODE=READ_ONLY
52fi
53
54warn() { echo "::warning title=sccache::$1, so this job builds without it"; }
55
56version_of() { "$1" --version 2>/dev/null | awk '{print $2}'; }
57
58# Finds this version of sccache, or downloads it. g1t's runner image has it
59# in ~/.cargo/bin already (services/runner/base/Dockerfile), so a job there
60# downloads nothing; one on PATH anywhere else is used too.
61fetch() {
62 local name="sccache-v${VERSION}-${TARGET}"
63 local dir="${RUNNER_TEMP:-/tmp}/sccache-${VERSION}"
64 local found
65 if [ -x "$SCCACHE" ] && [ "$(version_of "$SCCACHE")" = "$VERSION" ]; then
66 return 0
67 fi
68 found="$(command -v sccache || true)"
69 if [ -n "$found" ] && [ "$(version_of "$found")" = "$VERSION" ]; then
70 SCCACHE="$found"
71 return 0
72 fi
73 mkdir -p "$dir" "$BIN"
74 curl -fsSL --retry 3 -o "$dir/$name.tar.gz" "https://github.com/mozilla/sccache/releases/download/v${VERSION}/${name}.tar.gz" || return 1
75 echo "${SHA256} $dir/$name.tar.gz" | sha256sum -c --quiet - || return 1
76 tar -xzf "$dir/$name.tar.gz" -C "$dir" || return 1
77 install -m 0755 "$dir/$name/sccache" "$SCCACHE"
78}
79
80install_sccache() {
81 if [ -z "${ACTIONS_RUNTIME_TOKEN:-}" ] || [ -z "${ACTIONS_CACHE_URL:-}${ACTIONS_RESULTS_URL:-}" ]; then
82 warn "The job has no Actions cache to use (ACTIONS_RUNTIME_TOKEN, ACTIONS_CACHE_URL)"
83 return 0
84 fi
85 if ! fetch; then
86 warn "sccache ${VERSION} could not be downloaded or did not match its checksum"
87 return 0
88 fi
89 if ! "$SCCACHE" --start-server; then
90 warn "sccache's server did not start (it could not read the cache)"
91 return 0
92 fi
93 {
94 echo "RUSTC_WRAPPER=$SCCACHE"
95 echo "SCCACHE_GHA_ENABLED=$SCCACHE_GHA_ENABLED"
96 echo "SCCACHE_IDLE_TIMEOUT=$SCCACHE_IDLE_TIMEOUT"
97 echo "SCCACHE_IGNORE_SERVER_IO_ERROR=$SCCACHE_IGNORE_SERVER_IO_ERROR"
98 echo "CARGO_INCREMENTAL=$CARGO_INCREMENTAL"
99 if [ -n "${SCCACHE_GHA_RW_MODE:-}" ]; then echo "SCCACHE_GHA_RW_MODE=$SCCACHE_GHA_RW_MODE"; fi
100 } >> "$GITHUB_ENV"
101 if [ -n "${SCCACHE_GHA_RW_MODE:-}" ]; then
102 echo "sccache ${VERSION}: rustc goes through it from here on, reading this repository's Actions cache; a tag's run saves nothing to it."
103 else
104 echo "sccache ${VERSION}: rustc goes through it from here on, cached in this repository's Actions cache."
105 fi
106}
107
108stats() {
109 # The sccache that install found or downloaded, which RUSTC_WRAPPER names.
110 SCCACHE="${RUSTC_WRAPPER:-$SCCACHE}"
111 if [ -z "${RUSTC_WRAPPER:-}" ] || [ ! -x "$SCCACHE" ]; then
112 echo "sccache was not used in this job."
113 return 0
114 fi
115 local out
116 out="$("$SCCACHE" --show-stats 2>&1)" || true
117 echo "$out"
118 if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
119 {
120 echo "### sccache"
121 echo
122 echo '```'
123 echo "$out"
124 echo '```'
125 } >> "$GITHUB_STEP_SUMMARY"
126 fi
127}
128
129case "${1:-}" in
130 install) install_sccache ;;
131 stats) stats ;;
132 *)
133 echo "usage: bash scripts/sccache.sh install|stats" >&2
134 exit 2
135 ;;
136esac