gitea: composite cargo-cache action + runner-integration doc #31
@@ -0,0 +1,108 @@
|
||||
name: 'Clawstor cargo cache'
|
||||
description: >
|
||||
Wraps `cargo build` with a peer-cache lookup: HIT restores the
|
||||
target dir from the runner-local clawstor daemon (skipping dep
|
||||
compile); MISS runs cargo build then captures + uploads the target
|
||||
dir to the daemon. Config comes from
|
||||
`~/.config/claw-cargo/config.toml` on the runner host — provisioned
|
||||
by the fleet-admin, not from the workflow.
|
||||
|
||||
inputs:
|
||||
workspace:
|
||||
description: 'Path to the cargo workspace root'
|
||||
required: false
|
||||
default: '.'
|
||||
profile:
|
||||
description: 'Cargo profile (dev, release, custom)'
|
||||
required: false
|
||||
default: 'dev'
|
||||
no-upload:
|
||||
description: >
|
||||
When "true", skip capture + upload on a miss. Useful for
|
||||
read-only CI jobs (PR builds, forks). Defaults to false so
|
||||
main-branch jobs populate the cache.
|
||||
required: false
|
||||
default: 'false'
|
||||
parallel-restore:
|
||||
description: >
|
||||
Concurrent chunk fetches on cache HIT. Default 1 (sequential
|
||||
BlobGetStream) — measured faster than parallel on loopback. Set
|
||||
to 8+ on cross-node deployments where per-stream throughput
|
||||
caps bite.
|
||||
required: false
|
||||
default: '1'
|
||||
|
||||
outputs:
|
||||
cache-outcome:
|
||||
description: 'One of: HIT, MISS, POPULATED, SKIPPED'
|
||||
value: ${{ steps.build.outputs.cache-outcome }}
|
||||
fingerprint:
|
||||
description: 'The fingerprint claw-cargo computed for this build'
|
||||
value: ${{ steps.build.outputs.fingerprint }}
|
||||
elapsed-seconds:
|
||||
description: 'Wall-clock seconds elapsed in the build step'
|
||||
value: ${{ steps.build.outputs.elapsed-seconds }}
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Preflight check (claw-cargo + config)
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if ! command -v claw-cargo >/dev/null 2>&1; then
|
||||
echo "::error::claw-cargo not on PATH — fleet admin needs to install /usr/local/bin/claw-cargo on this runner"
|
||||
exit 1
|
||||
fi
|
||||
claw-cargo --version
|
||||
# The daemon config is per-runner; the action expects it to
|
||||
# exist under $HOME/.config/claw-cargo/config.toml. We do not
|
||||
# ship secrets from the workflow.
|
||||
cfg="${HOME}/.config/claw-cargo/config.toml"
|
||||
if [ ! -s "$cfg" ]; then
|
||||
echo "::error::missing $cfg — fleet admin needs to provision peer + tls_dir for this runner"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build with clawstor cache
|
||||
id: build
|
||||
shell: bash
|
||||
env:
|
||||
WS: ${{ inputs.workspace }}
|
||||
PROFILE: ${{ inputs.profile }}
|
||||
NO_UPLOAD: ${{ inputs.no-upload }}
|
||||
PAR_RESTORE: ${{ inputs.parallel-restore }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
args=(build --workspace "$WS" --profile "$PROFILE" --parallel-restore "$PAR_RESTORE")
|
||||
if [ "$NO_UPLOAD" = "true" ]; then
|
||||
args+=(--no-upload)
|
||||
fi
|
||||
started=$SECONDS
|
||||
# Tee the summary so we can grep the outcome out for the output.
|
||||
set +e
|
||||
claw-cargo "${args[@]}" 2>&1 | tee /tmp/clawstor-build.out
|
||||
rc=${PIPESTATUS[0]}
|
||||
set -e
|
||||
elapsed=$((SECONDS - started))
|
||||
|
||||
# Parse `cache:` line. Shapes we handle:
|
||||
# cache: HIT (... bytes downloaded)
|
||||
# cache: MISS
|
||||
# cache: MISS -> populated (... bytes uploaded)
|
||||
# cache: MISS (no upload performed)
|
||||
outcome="SKIPPED"
|
||||
if grep -q 'cache: *HIT' /tmp/clawstor-build.out; then
|
||||
outcome="HIT"
|
||||
elif grep -q 'cache: *MISS -> populated' /tmp/clawstor-build.out \
|
||||
|| grep -q 'cache: *MISS → populated' /tmp/clawstor-build.out; then
|
||||
outcome="POPULATED"
|
||||
elif grep -q 'cache: *MISS' /tmp/clawstor-build.out; then
|
||||
outcome="MISS"
|
||||
fi
|
||||
fingerprint="$(grep -oE 'fingerprint: [0-9a-f]+' /tmp/clawstor-build.out | head -1 | awk '{print $2}')"
|
||||
echo "cache-outcome=$outcome" >> "$GITHUB_OUTPUT"
|
||||
echo "fingerprint=${fingerprint:-unknown}" >> "$GITHUB_OUTPUT"
|
||||
echo "elapsed-seconds=$elapsed" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::clawstor $outcome fingerprint=${fingerprint:-unknown} elapsed=${elapsed}s"
|
||||
exit "$rc"
|
||||
@@ -0,0 +1,46 @@
|
||||
name: Build with clawstor cache
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Cargo build (clawstor-cached)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install system build deps
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
cmake build-essential pkg-config
|
||||
|
||||
- name: Ensure rustup toolchain matches rust-toolchain.toml
|
||||
run: |
|
||||
if ! command -v rustup >/dev/null 2>&1; then
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
||||
| sh -s -- -y --profile minimal
|
||||
. "$HOME/.cargo/env"
|
||||
fi
|
||||
# rustup will honour rust-toolchain.toml on next invocation
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Build with clawstor cache
|
||||
id: cache
|
||||
uses: ./.gitea/actions/cargo-cache
|
||||
with:
|
||||
workspace: '.'
|
||||
profile: 'dev'
|
||||
# On main-branch pushes we populate the cache; on PRs we
|
||||
# read-only to avoid a fork poisoning the cache.
|
||||
no-upload: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
- name: Announce outcome
|
||||
run: |
|
||||
echo "cache: ${{ steps.cache.outputs.cache-outcome }}"
|
||||
echo "fp: ${{ steps.cache.outputs.fingerprint }}"
|
||||
echo "time: ${{ steps.cache.outputs.elapsed-seconds }}s"
|
||||
@@ -0,0 +1,112 @@
|
||||
# Wiring a Gitea runner into clawstor
|
||||
|
||||
One-time per-runner setup, then workflows opt in with
|
||||
|
||||
```yaml
|
||||
- uses: ./.gitea/actions/cargo-cache
|
||||
```
|
||||
|
||||
## Prerequisites on the runner host
|
||||
|
||||
1. A running clawstor cluster daemon on the host. See
|
||||
`systemd/clawstor-cluster.service`.
|
||||
2. The runner uses `runs-on: ubuntu-latest:host` (or equivalent host
|
||||
runner label). Container-only runners need to bind-mount the CLI
|
||||
+ tls dir into the job container; that's a future extension.
|
||||
|
||||
## Provisioning claw-cargo for the runner user
|
||||
|
||||
Run these on the runner host as an operator with sudo. The runner
|
||||
user (act_runner on stock Gitea Actions installs) needs the CLI on
|
||||
`PATH`, a leaf cert signed by the fleet CA, and a
|
||||
`~/.config/claw-cargo/config.toml` pointing at the local daemon.
|
||||
|
||||
```sh
|
||||
# 1. Sign a leaf cert for this runner. `--node` becomes the SAN.
|
||||
claw-store fleet-ca-sign \
|
||||
--ca-dir /path/to/fleet-ca \
|
||||
--node act-runner-$(hostname -s) \
|
||||
--out-dir /tmp/act-runner-tls
|
||||
|
||||
# 2. Install the CLI + TLS material where act_runner can read them.
|
||||
sudo install -m 755 /path/to/claw-cargo /usr/local/bin/claw-cargo
|
||||
|
||||
sudo install -d -o act_runner -g act_runner -m 700 \
|
||||
/var/lib/act_runner/.config/claw-cargo \
|
||||
/var/lib/act_runner/clawstor-tls
|
||||
|
||||
sudo install -o act_runner -g act_runner -m 644 \
|
||||
/tmp/act-runner-tls/ca.crt /var/lib/act_runner/clawstor-tls/ca.crt
|
||||
sudo install -o act_runner -g act_runner -m 644 \
|
||||
/tmp/act-runner-tls/node.crt /var/lib/act_runner/clawstor-tls/node.crt
|
||||
sudo install -o act_runner -g act_runner -m 600 \
|
||||
/tmp/act-runner-tls/node.key /var/lib/act_runner/clawstor-tls/node.key
|
||||
|
||||
# 3. Write the runner's claw-cargo config. `peer.name` MUST match the
|
||||
# daemon's cert SAN. `peer.addr` is where act_runner reaches the
|
||||
# daemon — 127.0.0.1:7702 for host-mode runners.
|
||||
sudo tee /var/lib/act_runner/.config/claw-cargo/config.toml >/dev/null <<'EOF'
|
||||
[peer]
|
||||
name = "tank" # <-- match this to your host
|
||||
addr = "127.0.0.1:7702"
|
||||
tls_dir = "/var/lib/act_runner/clawstor-tls"
|
||||
|
||||
[build]
|
||||
profile = "dev"
|
||||
EOF
|
||||
sudo chown act_runner:act_runner /var/lib/act_runner/.config/claw-cargo/config.toml
|
||||
sudo chmod 600 /var/lib/act_runner/.config/claw-cargo/config.toml
|
||||
|
||||
# 4. Smoke test as the runner user.
|
||||
sudo -u act_runner claw-cargo peer-metrics \
|
||||
--peer tank --peer-addr 127.0.0.1:7702 \
|
||||
--tls-dir /var/lib/act_runner/clawstor-tls
|
||||
```
|
||||
|
||||
## Using the action in a workflow
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest # host runner
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Rust toolchain
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
||||
| sh -s -- -y --profile minimal
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
- uses: ./.gitea/actions/cargo-cache
|
||||
with:
|
||||
workspace: .
|
||||
# Read-only from forks so a malicious PR can't poison the cache.
|
||||
no-upload: ${{ github.event_name == 'pull_request' }}
|
||||
```
|
||||
|
||||
Outputs available on the step: `cache-outcome` (HIT | MISS |
|
||||
POPULATED | SKIPPED), `fingerprint`, `elapsed-seconds`.
|
||||
|
||||
## What to expect
|
||||
|
||||
- **First push on main** → cache MISS → the runner rebuilds cold,
|
||||
captures + uploads the target dir. Wall clock ≈ cold-build time.
|
||||
- **Second push touching just workspace crates** → cache HIT → deps
|
||||
restored from the daemon, cargo only rebuilds changed workspace
|
||||
crates. Ratio measured on real workloads:
|
||||
|
||||
| Runner | Cold | Warm HIT | Speedup |
|
||||
|---|---|---|---|
|
||||
| Pi 5 (4 core, 8 GB, SSD) | 7m 54s | 2m 50s | **2.79× wall, 5m 4s saved** |
|
||||
| Tank (48 core Threadripper) | 59s | 27s | 2.18× wall |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- `claw-cargo not on PATH` → step 2 missed.
|
||||
- `missing $HOME/.config/claw-cargo/config.toml` → step 3 missed.
|
||||
- `Error: dialing 127.0.0.1:7702 for name tank` → daemon isn't running
|
||||
or `bind_lan` still points at the fabric-only address. Change to
|
||||
`0.0.0.0:7701` and restart.
|
||||
- `rustc release drift: local_rustc=X peer_rustc=Y` → your
|
||||
workspace's `rust-toolchain.toml` doesn't match the runner's
|
||||
system rustc. The cache will still be valid but siloed per rustc.
|
||||
Fix by pinning `rust-toolchain.toml`.
|
||||
Reference in New Issue
Block a user