Skip to content

Commit 381a930

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 381a930

File tree

2 files changed

+275
-3
lines changed

2 files changed

+275
-3
lines changed

.github/workflows/ci.yml

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