Skip to content

Commit 4d137d6

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 4d137d6

File tree

2 files changed

+277
-3
lines changed

2 files changed

+277
-3
lines changed

.github/workflows/ci.yml

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