Skip to content

Commit f5db7e2

Browse files
committed
feat: build the CI image locally instead of pulling from GHCR
Add a build-image action that builds image/Dockerfile for a Rust version and loads it into the daemon, so the Docker actions run against a local rust-ci tag with no registry. With cache:true it caches only the added layer (clippy/rustfmt/jq) via the GitHub Actions cache (buildx type=gha,mode=min) while the rust base is pulled from Docker Hub; caching is opt-in. Parameterize image/Dockerfile with ARG RUST_VERSION, point the six Docker actions' image default at the local rust-ci:latest, and remove publish-image.yml. selftest builds via build-image with the cache on. Sweep the repo slug in the docs from cicd-rust to rust-ci.
1 parent 2db1f3a commit f5db7e2

18 files changed

Lines changed: 164 additions & 68 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# build-image
2+
3+
Build the rust-ci toolchain image locally from [`image/Dockerfile`](../../../image/Dockerfile) and load it into the Docker daemon, so the Docker actions run against a local tag with no registry pull.
4+
5+
## How it works
6+
7+
The image is `rust:<rust-version>` plus clippy, rustfmt, and jq.
8+
`build-image` builds it for the requested Rust version and `--load`s it into the daemon under `tag`; the Docker actions (`cargo-fetch`, `lint-and-test-docker`, `cargo-docker`, `cargo-use`, `cargo-install`, `publish-dry-run`) then `docker run` that tag — point their `image:` input at it, or use the matching default.
9+
10+
With `cache: "true"` the build runs under buildx with the GitHub Actions cache (`type=gha,mode=min`): only the added layer (clippy/rustfmt/jq) is cached, scoped per Rust version, while the `rust:<version>` base is pulled from Docker Hub.
11+
Caching is off by default, so a consumer opts in before anything writes to their Actions cache.
12+
13+
## Usage
14+
15+
```yaml
16+
- uses: gronke/rust-ci/.github/actions/build-image@main
17+
with:
18+
rust-version: "1" # any rust:<tag>; default "latest"
19+
tag: rust-ci:latest # the Docker actions' default image
20+
cache: "true" # opt in to caching the added layer
21+
- uses: gronke/rust-ci/.github/actions/lint-and-test-docker@main
22+
with:
23+
working-directory: . # image defaults to rust-ci:latest
24+
```
25+
26+
## Inputs
27+
28+
| Input | Default | Description |
29+
| --- | --- | --- |
30+
| `rust-version` | `latest` | The `rust:<version>` base tag to build on. |
31+
| `tag` | `rust-ci:latest` | Tag for the built image. |
32+
| `cache` | `"false"` | Cache the added layer across runs (GitHub Actions cache via buildx `type=gha`). |
33+
34+
## Outputs
35+
36+
| Output | Description |
37+
| --- | --- |
38+
| `image` | The built image tag. |
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Build image (local)
2+
description: >-
3+
Build the rust-ci toolchain image from image/Dockerfile for a Rust version and
4+
load it into the Docker daemon, so the Docker actions run against a local tag
5+
with no registry. With cache:true the added layer (clippy/rustfmt/jq) is cached
6+
via the GitHub Actions cache; the rust base is pulled from Docker Hub.
7+
8+
inputs:
9+
rust-version:
10+
description: Rust base version — the rust:<version> tag to build on.
11+
required: false
12+
default: latest
13+
tag:
14+
description: Tag for the built image.
15+
required: false
16+
default: rust-ci:latest
17+
cache:
18+
description: Cache the added layer across runs (GitHub Actions cache via buildx type=gha).
19+
required: false
20+
default: "false"
21+
22+
outputs:
23+
image:
24+
description: The built image tag.
25+
value: ${{ inputs.tag }}
26+
27+
runs:
28+
using: composite
29+
steps:
30+
# Re-export the runtime token + v2 results URL; the runner withholds them from
31+
# step processes, and buildx needs them for the type=gha cache.
32+
- name: Expose Actions cache tokens
33+
if: ${{ inputs.cache == 'true' }}
34+
uses: actions/github-script@v7
35+
with:
36+
script: |
37+
for (const k of ['ACTIONS_RUNTIME_TOKEN', 'ACTIONS_RESULTS_URL']) {
38+
if (process.env[k]) core.exportVariable(k, process.env[k]);
39+
}
40+
41+
- name: Build image
42+
shell: bash
43+
env:
44+
RUST_VERSION: ${{ inputs.rust-version }}
45+
TAG: ${{ inputs.tag }}
46+
CACHE: ${{ inputs.cache }}
47+
SCOPE: rust-ci-${{ inputs.rust-version }}
48+
ACTIONS_CACHE_SERVICE_V2: "true"
49+
run: bash "$GITHUB_ACTION_PATH/build-image.sh"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
# Build image/Dockerfile and load the result into the Docker daemon. Inputs arrive
3+
# as env vars from action.yml:
4+
# RUST_VERSION the rust:<version> base tag
5+
# TAG tag for the built image
6+
# CACHE "true" → buildx with the GitHub Actions cache (type=gha,mode=min)
7+
# SCOPE gha cache scope (per Rust version)
8+
set -euo pipefail
9+
10+
context="$GITHUB_ACTION_PATH/../../../image"
11+
12+
if [ "${CACHE:-false}" = "true" ]; then
13+
echo "::group::buildx build (gha cache)"
14+
# type=gha caches only the added layer (mode=min); the rust base is pulled from
15+
# Docker Hub. A docker-container builder is required; pin a buildkit that speaks
16+
# the v2 Actions cache service. ignore-error keeps a cache hiccup from failing
17+
# the build (the image still loads).
18+
docker buildx inspect rust-ci-builder >/dev/null 2>&1 \
19+
|| docker buildx create --name rust-ci-builder --driver docker-container \
20+
--driver-opt image=moby/buildkit:latest >/dev/null
21+
docker buildx build --builder rust-ci-builder \
22+
--build-arg "RUST_VERSION=$RUST_VERSION" \
23+
--cache-from "type=gha,scope=$SCOPE" \
24+
--cache-to "type=gha,mode=min,scope=$SCOPE,ignore-error=true" \
25+
--load -t "$TAG" "$context"
26+
echo "::endgroup::"
27+
else
28+
echo "::group::docker build"
29+
docker build --build-arg "RUST_VERSION=$RUST_VERSION" -t "$TAG" "$context"
30+
echo "::endgroup::"
31+
fi

‎.github/actions/cargo-docker/action.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ inputs:
1010
image:
1111
description: CI image (Rust toolchain + clippy/rustfmt + jq).
1212
required: false
13-
default: ghcr.io/gronke/rust-ci:latest
13+
default: rust-ci:latest
1414
working-directory:
1515
description: Crate/workspace directory (mounted read-only).
1616
required: false

‎.github/actions/cargo-fetch/action.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ inputs:
88
image:
99
description: CI image (Rust toolchain + clippy/rustfmt + jq).
1010
required: false
11-
default: ghcr.io/gronke/rust-ci:latest
11+
default: rust-ci:latest
1212
working-directory:
1313
description: Crate/workspace directory (mounted read-only).
1414
required: false

‎.github/actions/cargo-install/README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ That mode runs `args` on the host, so use it only with trusted input; Docker mod
2222
## Usage
2323

2424
```yaml
25-
- uses: gronke/cicd-rust/.github/actions/cargo-install@main
25+
- uses: gronke/rust-ci/.github/actions/cargo-install@main
2626
with:
2727
tool: cargo-audit
2828
version: "0.21" # optional; pins the install (and a consumer cache key)
@@ -42,7 +42,7 @@ a binary is only guaranteed to run in the image it was built in.
4242
| `args` | `""` | Extra flags appended to `cargo install` (for example `--features cli`). Word-split inside the container. |
4343
| `locked` | `"true"` | Append `--locked`. |
4444
| `docker` | `"true"` | Install sealed in the container; `"false"` installs on the host. |
45-
| `image` | `ghcr.io/gronke/rust-ci:latest` | CI image. Match it in `cargo-use`. |
45+
| `image` | `rust-ci:latest` | CI image. Match it in `cargo-use`. |
4646
| `cargo-cache` | `.cargo-cache` | Host dir mounted read-write as `CARGO_HOME`; the tool lands in its `bin/`. |
4747
| `working-directory` | `.` | Directory the action runs from. |
4848
| `env-include` / `env-exclude` / `env` | (as `cargo-docker`) | Control which runner env vars are forwarded into the container. |

‎.github/actions/cargo-install/action.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ inputs:
3434
image:
3535
description: CI image (Rust toolchain). Use the same image in cargo-use (ABI).
3636
required: false
37-
default: ghcr.io/gronke/rust-ci:latest
37+
default: rust-ci:latest
3838
cargo-cache:
3939
description: >-
4040
Host dir mounted RW as CARGO_HOME (/cache/cargo); the tool installs to its

‎.github/actions/cargo-use/README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ That mode runs `args` on the host, so use it only with trusted input; Docker mod
1717
## Usage
1818

1919
```yaml
20-
- uses: gronke/cicd-rust/.github/actions/cargo-use@main # fetch the advisory DB (networked, runs no project build)
20+
- uses: gronke/rust-ci/.github/actions/cargo-use@main # fetch the advisory DB (networked, runs no project build)
2121
with:
2222
args: "cargo-audit fetch"
2323
cargo-cache: .cargo-tools
2424
offline: "false"
25-
- uses: gronke/cicd-rust/.github/actions/cargo-use@main # audit sealed, zero egress
25+
- uses: gronke/rust-ci/.github/actions/cargo-use@main # audit sealed, zero egress
2626
with:
2727
args: "cargo audit --no-fetch"
2828
cargo-cache: .cargo-tools
@@ -37,7 +37,7 @@ a binary is only guaranteed to run in the image it was built in.
3737
| --- | --- | --- |
3838
| `args` | — (required) | The command line to run, for example `cargo audit --no-fetch`. Word-split inside the container. |
3939
| `docker` | `"true"` | Run sealed in the container; `"false"` runs on the host. |
40-
| `image` | `ghcr.io/gronke/rust-ci:latest` | CI image. Match the one `cargo-install` used (binary ABI). |
40+
| `image` | `rust-ci:latest` | CI image. Match the one `cargo-install` used (binary ABI). |
4141
| `cargo-cache` | `.cargo-cache` | Host dir mounted read-write as `CARGO_HOME`; its `bin/` is prepended to `PATH`. |
4242
| `offline` | `"true"` | Run with `--network=none`; set `"false"` for a tool that needs the network. |
4343
| `target-dir` | `""` | Optional host dir mounted read-write at `/work/target` for a tool that builds. Empty mounts no target. |

‎.github/actions/cargo-use/action.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ inputs:
1414
image:
1515
description: CI image. Use the SAME image cargo-install used (binary ABI must match).
1616
required: false
17-
default: ghcr.io/gronke/rust-ci:latest
17+
default: rust-ci:latest
1818
cargo-cache:
1919
description: >-
2020
Host dir mounted RW as CARGO_HOME (/cache/cargo); must match the cargo-cache

‎.github/actions/check-release-readiness/check-release-readiness.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ else
5252
fi
5353
# Not-already-published guard (crates.io API; non-fatal on a network blip).
5454
CODE=$(curl -s -o /dev/null -w '%{http_code}' \
55-
-H "User-Agent: cicd-rust release-readiness" \
55+
-H "User-Agent: rust-ci release-readiness" \
5656
"https://crates.io/api/v1/crates/${NAME}/${VERSION}" || echo "000")
5757
if [ "$CODE" = "200" ]; then
5858
echo "::error::${NAME} ${VERSION} is already published on crates.io"

0 commit comments

Comments
 (0)