gitea: composite cargo-cache action + runner-integration doc #31

Merged
osobh merged 1 commits from gitea-action-cargo-cache into main 2026-07-13 08:30:30 +00:00
3 changed files with 266 additions and 0 deletions
+108
View File
@@ -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"
+46
View File
@@ -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"
+112
View File
@@ -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`.