Skip to content

Commit 5711200

Browse files
committed
ci: add comprehensive GitHub Actions workflow with lint, test, and e2e jobs
Add a complete CI/CD pipeline with the following features: - **lint-and-unit job**: Runs format checks, clippy linting, and unit tests - **e2e job**: Builds WASM components and runs end-to-end smoke tests with Spin - Optimized caching for Rust toolchain, sccache, and build artifacts - Uses latest GitHub Actions (checkout@v4, setup-node@v4, cache@v4) - Configurable Spin CLI version (pinned to v2.7.0) - Comprehensive error handling and debugging aids Enhanced e2e test script with: - Improved build logging and error reporting - Environment variable configuration for RUST_LOG and listen address - Better failure diagnostics with socket status checks - Artifact upload on failure for debugging (logs, SQLite DB, WASM files) The workflow runs on push/PR with concurrency controls and provides detailed feedback for troubleshooting build and runtime issues.
1 parent 738e21e commit 5711200

File tree

2 files changed

+282
-3
lines changed

2 files changed

+282
-3
lines changed

.github/workflows/ci.yml

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.ref }}
9+
cancel-in-progress: true
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
lint-and-unit:
16+
name: Lint + Unit Tests
17+
runs-on: ubuntu-latest
18+
env:
19+
RUSTC_WRAPPER: sccache
20+
SCCACHE_DIR: ~/.cache/sccache
21+
SCCACHE_CACHE_SIZE: 2G
22+
CARGO_INCREMENTAL: '0'
23+
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v4
27+
28+
- name: Install zsh (needed by justfile shell)
29+
run: |
30+
sudo apt-get update
31+
sudo apt-get install -y zsh
32+
33+
- name: Setup Node.js (for tooling)
34+
uses: actions/setup-node@v4
35+
with:
36+
node-version: '20'
37+
cache: 'npm'
38+
cache-dependency-path: |
39+
package-lock.json
40+
41+
# We intentionally avoid pnpm here; just init will use npm if pnpm is absent
42+
43+
- name: Install Rust (stable)
44+
uses: dtolnay/rust-toolchain@stable
45+
with:
46+
targets: wasm32-wasip2
47+
48+
- name: Cache sccache
49+
uses: actions/cache@v4
50+
with:
51+
path: ${{ env.SCCACHE_DIR }}
52+
key: ${{ runner.os }}-sccache-${{ hashFiles('**/Cargo.lock') }}
53+
54+
- name: Cache cargo registry + target
55+
uses: actions/cache@v4
56+
with:
57+
path: |
58+
~/.cargo/registry
59+
~/.cargo/git
60+
target
61+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
62+
63+
- name: Install sccache (prebuilt)
64+
uses: taiki-e/install-action@v2
65+
with:
66+
tool: sccache@0.7.5
67+
68+
- name: Install just (prebuilt)
69+
uses: taiki-e/install-action@v2
70+
with:
71+
tool: just@1.28.0
72+
73+
- name: Verify tools
74+
run: |
75+
rustc --version
76+
cargo --version
77+
just --version
78+
sccache --version
79+
80+
- name: Format check
81+
run: |
82+
just fmt-check
83+
84+
- name: Clippy (all targets)
85+
run: |
86+
just clippy
87+
88+
- name: Unit tests
89+
run: |
90+
just test
91+
92+
- name: sccache stats
93+
if: always()
94+
run: sccache --show-stats || true
95+
96+
e2e:
97+
name: E2E Spin Smoke
98+
runs-on: ubuntu-latest
99+
env:
100+
RUSTC_WRAPPER: sccache
101+
SCCACHE_DIR: ~/.cache/sccache
102+
SCCACHE_CACHE_SIZE: 2G
103+
SPIN_VERSION: '2.7.0'
104+
RUST_LOG: debug
105+
RUST_BACKTRACE: 1
106+
CARGO_INCREMENTAL: '0'
107+
108+
steps:
109+
- name: Checkout
110+
uses: actions/checkout@v4
111+
112+
- name: Install zsh (needed by justfile shell)
113+
run: |
114+
sudo apt-get update
115+
sudo apt-get install -y zsh
116+
117+
- name: Setup Node.js
118+
uses: actions/setup-node@v4
119+
with:
120+
node-version: '20'
121+
cache: 'npm'
122+
cache-dependency-path: |
123+
package-lock.json
124+
125+
# We intentionally avoid pnpm here; just init will use npm if pnpm is absent
126+
127+
- name: Install Rust (stable)
128+
uses: dtolnay/rust-toolchain@stable
129+
with:
130+
targets: wasm32-wasip2
131+
132+
- name: Cache sccache
133+
uses: actions/cache@v4
134+
with:
135+
path: ${{ env.SCCACHE_DIR }}
136+
key: ${{ runner.os }}-sccache-${{ hashFiles('**/Cargo.lock') }}
137+
138+
- name: Cache cargo registry + target
139+
uses: actions/cache@v4
140+
with:
141+
path: |
142+
~/.cargo/registry
143+
~/.cargo/git
144+
target
145+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
146+
147+
- name: Install sccache (prebuilt)
148+
uses: taiki-e/install-action@v2
149+
with:
150+
tool: sccache@0.7.5
151+
152+
- name: Install just (prebuilt)
153+
uses: taiki-e/install-action@v2
154+
with:
155+
tool: just@1.28.0
156+
157+
- name: Install Spin CLI
158+
run: |
159+
# Pin Spin CLI version via env var
160+
curl -fsSL https://developer.fermyon.com/downloads/spin/install.sh | bash
161+
echo "$HOME/.spin/bin" >> "$GITHUB_PATH"
162+
163+
- name: Verify Spin version
164+
run: |
165+
spin --version
166+
if ! spin --version | grep -q "${SPIN_VERSION}"; then
167+
echo "Expected Spin ${SPIN_VERSION}, but got: $(spin --version)" >&2
168+
exit 1
169+
fi
170+
171+
- name: Verify tools
172+
run: |
173+
rustc --version
174+
cargo --version
175+
just --version
176+
spin --version
177+
sccache --version
178+
179+
- name: Prepare environment via just tasks
180+
run: |
181+
# Ensure wasm target + JS deps installed via our recipes
182+
just init
183+
# Verify Spin availability using our helper
184+
just spin-check
185+
186+
- name: Build WASM (sanity)
187+
run: |
188+
just build-wasm
189+
190+
- name: Restore dist cache
191+
uses: actions/cache/restore@v4
192+
with:
193+
path: dist
194+
key: ${{ runner.os }}-dist-${{ hashFiles('package.json', 'pnpm-lock.yaml', 'package-lock.json', 'justfile', '**/*.wit', 'Cargo.lock') }}
195+
restore-keys: |
196+
${{ runner.os }}-dist-
197+
198+
- name: Transpile JS bindings (cached)
199+
run: |
200+
just transpile
201+
202+
- name: Cache dist outputs
203+
uses: actions/cache/save@v4
204+
if: success()
205+
with:
206+
path: dist
207+
key: ${{ runner.os }}-dist-${{ hashFiles('package.json', 'pnpm-lock.yaml', 'package-lock.json', 'justfile', '**/*.wit', 'Cargo.lock') }}
208+
209+
- name: sccache stats
210+
if: always()
211+
run: sccache --show-stats || true
212+
213+
- name: Run E2E smoke
214+
run: |
215+
just e2e-run
216+
217+
- name: Upload Spin logs on failure
218+
if: failure()
219+
uses: actions/upload-artifact@v4
220+
with:
221+
name: spin-logs
222+
path: |
223+
apps/e2e-keel/.spin/build.log
224+
apps/e2e-keel/.spin/e2e-run.log
225+
if-no-files-found: warn
226+
227+
- name: Upload SQLite db (debug aid)
228+
if: failure()
229+
uses: actions/upload-artifact@v4
230+
with:
231+
name: sqlite-db
232+
path: apps/e2e-keel/dev.db
233+
if-no-files-found: ignore
234+
235+
- name: Upload Wasm artifacts (debug aid)
236+
if: failure()
237+
uses: actions/upload-artifact@v4
238+
with:
239+
name: wasm-artifacts
240+
path: |
241+
target/wasm32-wasip2/release/**/*.wasm
242+
target/wasm32-wasip2/debug/**/*.wasm
243+
if-no-files-found: warn

scripts/e2e-run.sh

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,46 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
3+
export RUST_BACKTRACE="${RUST_BACKTRACE:-1}"
34

45
# Base URL for the app; default to Spin's default if not provided
56
E2E_URL="${E2E_URL:-http://127.0.0.1:3000}"
7+
RUST_LOG="${RUST_LOG:-info}"
8+
SPIN_HTTP_LISTEN_ADDR="${SPIN_HTTP_LISTEN_ADDR:-127.0.0.1:3000}"
69

710
APP_DIR="apps/e2e-keel"
811
LOG_DIR="$APP_DIR/.spin"
912
PID_FILE="$LOG_DIR/e2e-run.pid"
1013
LOG_FILE="$LOG_DIR/e2e-run.log"
1114

12-
echo "[e2e-run] Building app..."
13-
just spin-build "$APP_DIR"
15+
BUILD_LOG_FILE="$LOG_DIR/build.log"
16+
echo "[e2e-run] Building app (logging to $BUILD_LOG_FILE)..."
17+
mkdir -p "$LOG_DIR"
18+
# Capture build output; if it fails, print the full log then exit
19+
if ! (cd "$APP_DIR" && spin build 2>&1 | tee "$BUILD_LOG_FILE"); then
20+
echo "[e2e-run] Build failed." >&2
21+
# If the log is huge, avoid flooding CI: show last 400 lines
22+
lines=$(wc -l < "$BUILD_LOG_FILE" || echo 0)
23+
if [ "$lines" -gt 400 ]; then
24+
echo "[e2e-run] Build log is $lines lines; showing last 400 lines:" >&2
25+
tail -n 400 "$BUILD_LOG_FILE" || true
26+
else
27+
echo "[e2e-run] Full build log:" >&2
28+
cat "$BUILD_LOG_FILE" || true
29+
fi
30+
# Run a quiet build to surface concise errors, but don't fail if this also errors
31+
echo "[e2e-run] Retrying with 'spin build --quiet' for concise errors..." >&2
32+
(cd "$APP_DIR" && spin build --quiet) || true
33+
exit 1
34+
fi
1435

36+
echo "[e2e-run] Using RUST_LOG=$RUST_LOG, SPIN_HTTP_LISTEN_ADDR=$SPIN_HTTP_LISTEN_ADDR"
37+
echo "[e2e-run] spin version: $(spin --version || echo 'spin not found')"
1538
echo "[e2e-run] Starting app in background (logging to $LOG_FILE)..."
1639
mkdir -p "$LOG_DIR"
1740
(
1841
cd "$APP_DIR"
19-
spin up > ".spin/e2e-run.log" 2>&1 & echo $! > ".spin/e2e-run.pid"
42+
RUST_LOG="$RUST_LOG" SPIN_HTTP_LISTEN_ADDR="$SPIN_HTTP_LISTEN_ADDR" \
43+
spin up > ".spin/e2e-run.log" 2>&1 & echo $! > ".spin/e2e-run.pid"
2044
)
2145
PID="$(cat "$APP_DIR/.spin/e2e-run.pid")"
2246
cleanup() {
@@ -39,6 +63,11 @@ done
3963
if [ "$ready" -ne 1 ]; then
4064
echo "[e2e-run] App did not become ready. Recent log:"
4165
tail -n 100 "$APP_DIR/.spin/e2e-run.log" || true
66+
if command -v ss >/dev/null 2>&1; then
67+
echo "[e2e-run] Listening sockets (ss -ltn):"; ss -ltn || true
68+
elif command -v netstat >/dev/null 2>&1; then
69+
echo "[e2e-run] Listening sockets (netstat -tln):"; netstat -tln || true
70+
fi
4271
exit 1
4372
fi
4473

@@ -47,6 +76,13 @@ if just e2e-smoke; then
4776
rc=0
4877
else
4978
rc=$?
79+
echo "[e2e-run] Smoke tests failed (exit $rc). Recent app log:"
80+
tail -n 200 "$APP_DIR/.spin/e2e-run.log" || true
81+
if command -v ss >/dev/null 2>&1; then
82+
echo "[e2e-run] Listening sockets (ss -ltn):"; ss -ltn || true
83+
elif command -v netstat >/dev/null 2>&1; then
84+
echo "[e2e-run] Listening sockets (netstat -tln):"; netstat -tln || true
85+
fi
5086
fi
5187

5288
exit "$rc"

0 commit comments

Comments
 (0)