Skip to content

Commit 085fbdc

Browse files
createpjfclaude
andcommitted
feat: add release preflight check and CI workflow deduplication
- Add scripts/preflight.sh: local pre-flight check that mirrors CI (lockfile, tests, tsc, Docker builds, frontend bundle) - Add scripts/setup-secrets.sh: interactive guide for configuring Apple code signing GitHub Secrets - Refactor build.yml to reuse ci.yml via workflow_call instead of duplicating test jobs (single source of truth for test definitions) - Add workflow_call trigger to ci.yml Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 62a8662 commit 085fbdc

File tree

4 files changed

+286
-31
lines changed

4 files changed

+286
-31
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,12 @@ concurrency:
1111
cancel-in-progress: true
1212

1313
jobs:
14-
test-gateway:
15-
runs-on: ubuntu-latest
16-
steps:
17-
- uses: actions/checkout@v4
18-
- uses: oven-sh/setup-bun@v2
19-
- run: cd apps/gateway && bun install
20-
- run: cd apps/gateway && bun test
21-
22-
test-desktop:
23-
runs-on: ubuntu-latest
24-
steps:
25-
- uses: actions/checkout@v4
26-
- uses: actions/setup-node@v4
27-
with: { node-version: 20 }
28-
- uses: pnpm/action-setup@v4
29-
with: { version: 10 }
30-
- run: pnpm install
31-
- run: cd apps/desktop && npx tsc --noEmit
32-
- run: cd apps/desktop && npx vitest run
33-
34-
test-cloud-gateway:
35-
runs-on: ubuntu-latest
36-
steps:
37-
- uses: actions/checkout@v4
38-
- uses: oven-sh/setup-bun@v2
39-
- run: cd apps/cloud-gateway && bun install
40-
- run: cd apps/cloud-gateway && bun test
41-
- run: cd apps/cloud-gateway && bunx tsc --noEmit
14+
# Reuse CI workflow for all tests + Docker smoke tests
15+
ci:
16+
uses: ./.github/workflows/ci.yml
4217

4318
build-macos:
44-
needs: [test-gateway, test-desktop, test-cloud-gateway]
19+
needs: [ci]
4520
runs-on: macos-14
4621
strategy:
4722
matrix:
@@ -97,7 +72,7 @@ jobs:
9772
prerelease: false
9873

9974
docker-gateway:
100-
needs: [test-gateway]
75+
needs: [ci]
10176
runs-on: ubuntu-latest
10277
if: startsWith(github.ref, 'refs/tags/v')
10378
permissions:
@@ -132,7 +107,7 @@ jobs:
132107
labels: ${{ steps.meta.outputs.labels }}
133108

134109
docker-cloud-gateway:
135-
needs: [test-cloud-gateway]
110+
needs: [ci]
136111
runs-on: ubuntu-latest
137112
if: startsWith(github.ref, 'refs/tags/v')
138113
permissions:

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
branches: [main]
66
pull_request:
77
branches: [main]
8+
workflow_call: # Allow build.yml to reuse this workflow
89

910
concurrency:
1011
group: ci-${{ github.ref }}

scripts/preflight.sh

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/bin/bash
2+
# ─────────────────────────────────────────────────────────────────────────────
3+
# preflight.sh — Local pre-flight check before pushing a release tag
4+
#
5+
# Simulates everything CI does: lockfile check, tests, tsc, Docker builds,
6+
# and frontend bundling. Run this BEFORE `git tag` + `git push`.
7+
#
8+
# Usage:
9+
# ./scripts/preflight.sh # Full check (default)
10+
# ./scripts/preflight.sh --quick # Skip Docker builds
11+
# ─────────────────────────────────────────────────────────────────────────────
12+
set -e
13+
14+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
15+
cd "$ROOT"
16+
17+
GREEN='\033[0;32m'
18+
RED='\033[0;31m'
19+
YELLOW='\033[1;33m'
20+
NC='\033[0m'
21+
BOLD='\033[1m'
22+
23+
pass() { echo -e "${GREEN}${NC} $1"; }
24+
fail() { echo -e "${RED}${NC} $1"; exit 1; }
25+
info() { echo -e "${YELLOW}${NC} $1"; }
26+
step() { echo -e "\n${BOLD}[$1/$TOTAL] $2${NC}"; }
27+
28+
QUICK=false
29+
[ "$1" = "--quick" ] && QUICK=true
30+
31+
if $QUICK; then TOTAL=5; else TOTAL=7; fi
32+
ERRORS=0
33+
34+
# ─────────────────────────────────────────────────────────────────────────────
35+
echo ""
36+
echo "═══════════════════════════════════════════════════════════"
37+
echo " RouteBox Release Pre-flight Check"
38+
echo "═══════════════════════════════════════════════════════════"
39+
echo ""
40+
41+
# ── 1. Lockfile consistency ──────────────────────────────────────────────────
42+
step 1 "Checking pnpm lockfile consistency..."
43+
if pnpm install --frozen-lockfile > /dev/null 2>&1; then
44+
pass "pnpm-lock.yaml is up to date"
45+
else
46+
fail "pnpm-lock.yaml is out of date — run 'pnpm install' first"
47+
fi
48+
49+
# ── 2. Gateway tests ────────────────────────────────────────────────────────
50+
step 2 "Running gateway tests..."
51+
# Clean macOS resource forks that break bun
52+
find apps/gateway -name '._*' -delete 2>/dev/null || true
53+
if (cd apps/gateway && bun test 2>&1 | tail -5); then
54+
pass "Gateway tests passed"
55+
else
56+
fail "Gateway tests failed"
57+
fi
58+
59+
# ── 3. Cloud-gateway tests + tsc ────────────────────────────────────────────
60+
step 3 "Running cloud-gateway tests + TypeScript check..."
61+
find apps/cloud-gateway -name '._*' -delete 2>/dev/null || true
62+
if (cd apps/cloud-gateway && bun test 2>&1 | tail -5) && \
63+
(cd apps/cloud-gateway && bunx tsc --noEmit 2>&1); then
64+
pass "Cloud-gateway tests + tsc passed"
65+
else
66+
fail "Cloud-gateway tests or tsc failed"
67+
fi
68+
69+
# ── 4. Desktop tests + tsc ──────────────────────────────────────────────────
70+
step 4 "Running desktop TypeScript check + tests..."
71+
find apps/desktop -name '._*' -delete 2>/dev/null || true
72+
if (cd apps/desktop && npx tsc --noEmit 2>&1) && \
73+
(cd apps/desktop && npx vitest run 2>&1 | tail -10); then
74+
pass "Desktop tsc + tests passed"
75+
else
76+
fail "Desktop tsc or tests failed"
77+
fi
78+
79+
# ── 5. Frontend bundle (Vite + gateway bundle) ──────────────────────────────
80+
step 5 "Verifying gateway bundle + Vite build..."
81+
if bun build apps/gateway/src/index.ts --target=bun --outfile apps/desktop/src-tauri/gateway-bundle.js > /dev/null 2>&1 && \
82+
(cd apps/desktop && npx vite build > /dev/null 2>&1); then
83+
pass "Frontend bundle OK"
84+
else
85+
fail "Frontend bundle failed"
86+
fi
87+
88+
if ! $QUICK; then
89+
# ── 6. Docker build: gateway ─────────────────────────────────────────────
90+
step 6 "Building gateway Docker image..."
91+
if docker build -t routebox-gw-preflight apps/gateway > /dev/null 2>&1; then
92+
pass "Gateway Docker image built"
93+
docker rmi routebox-gw-preflight > /dev/null 2>&1 || true
94+
else
95+
fail "Gateway Docker build failed"
96+
fi
97+
98+
# ── 7. Docker build: cloud-gateway ───────────────────────────────────────
99+
step 7 "Building cloud-gateway Docker image..."
100+
if docker build -t routebox-cgw-preflight apps/cloud-gateway > /dev/null 2>&1; then
101+
pass "Cloud-gateway Docker image built"
102+
docker rmi routebox-cgw-preflight > /dev/null 2>&1 || true
103+
else
104+
fail "Cloud-gateway Docker build failed"
105+
fi
106+
fi
107+
108+
# ── Summary ──────────────────────────────────────────────────────────────────
109+
echo ""
110+
echo "═══════════════════════════════════════════════════════════"
111+
echo -e " ${GREEN}${BOLD}All pre-flight checks passed!${NC}"
112+
echo " Safe to tag and push:"
113+
echo ""
114+
echo " git tag -a v\$VERSION -m \"V\$VERSION — description\""
115+
echo " git push origin main --tags"
116+
echo "═══════════════════════════════════════════════════════════"
117+
echo ""

scripts/setup-secrets.sh

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#!/bin/bash
2+
# ─────────────────────────────────────────────────────────────────────────────
3+
# setup-secrets.sh — Interactive guide to configure GitHub Secrets
4+
# for Apple code signing and Tauri updater
5+
#
6+
# Prerequisites:
7+
# 1. gh CLI authenticated (gh auth login)
8+
# 2. Developer ID Application certificate in Keychain
9+
# 3. Apple ID with app-specific password
10+
# 4. Tauri signer key pair (tauri signer generate)
11+
#
12+
# Usage:
13+
# ./scripts/setup-secrets.sh
14+
# ─────────────────────────────────────────────────────────────────────────────
15+
set -e
16+
17+
GREEN='\033[0;32m'
18+
YELLOW='\033[1;33m'
19+
RED='\033[0;31m'
20+
CYAN='\033[0;36m'
21+
NC='\033[0m'
22+
BOLD='\033[1m'
23+
24+
info() { echo -e "${GREEN}${NC} $1"; }
25+
warn() { echo -e "${YELLOW}!${NC} $1"; }
26+
ask() { echo -e "${CYAN}?${NC} $1"; }
27+
step() { echo -e "\n${BOLD}── Step $1 ──${NC}"; }
28+
29+
REPO="createpjf/RouteBox"
30+
31+
echo ""
32+
echo "═══════════════════════════════════════════════════════════"
33+
echo " RouteBox — GitHub Secrets Setup for Code Signing"
34+
echo "═══════════════════════════════════════════════════════════"
35+
echo ""
36+
37+
# Check gh is authenticated
38+
if ! gh auth status > /dev/null 2>&1; then
39+
echo -e "${RED}${NC} gh CLI not authenticated. Run: gh auth login"
40+
exit 1
41+
fi
42+
info "gh CLI authenticated"
43+
44+
# Show current secrets
45+
echo ""
46+
info "Current secrets:"
47+
EXISTING=$(gh secret list --repo "$REPO" 2>&1)
48+
if [ -z "$EXISTING" ]; then
49+
warn "No secrets configured yet"
50+
else
51+
echo "$EXISTING"
52+
fi
53+
54+
# ── Step 1: Apple Certificate ───────────────────────────────────────────────
55+
step "1/6: APPLE_CERTIFICATE"
56+
echo ""
57+
echo " Export your Developer ID Application certificate as .p12:"
58+
echo ""
59+
echo " 1. Open Keychain Access"
60+
echo " 2. Find: Developer ID Application: Jinfeng Peng (M2XH53X5DB)"
61+
echo " 3. Right-click → Export Items → Save as .p12"
62+
echo " 4. Set an export password (you'll need it in Step 2)"
63+
echo ""
64+
ask "Path to exported .p12 file (or 'skip'):"
65+
read -r P12_PATH
66+
67+
if [ "$P12_PATH" != "skip" ] && [ -f "$P12_PATH" ]; then
68+
base64 -i "$P12_PATH" | gh secret set APPLE_CERTIFICATE --repo "$REPO"
69+
info "APPLE_CERTIFICATE set!"
70+
else
71+
warn "Skipped APPLE_CERTIFICATE"
72+
fi
73+
74+
# ── Step 2: Certificate Password ────────────────────────────────────────────
75+
step "2/6: APPLE_CERTIFICATE_PASSWORD"
76+
ask "Enter the .p12 export password (or 'skip'):"
77+
read -rs CERT_PASS
78+
echo ""
79+
80+
if [ "$CERT_PASS" != "skip" ] && [ -n "$CERT_PASS" ]; then
81+
echo "$CERT_PASS" | gh secret set APPLE_CERTIFICATE_PASSWORD --repo "$REPO"
82+
info "APPLE_CERTIFICATE_PASSWORD set!"
83+
else
84+
warn "Skipped APPLE_CERTIFICATE_PASSWORD"
85+
fi
86+
87+
# ── Step 3: Apple ID + Team ─────────────────────────────────────────────────
88+
step "3/6: APPLE_SIGNING_IDENTITY + APPLE_TEAM_ID"
89+
90+
echo "Developer ID Application: Jinfeng Peng (M2XH53X5DB)" | \
91+
gh secret set APPLE_SIGNING_IDENTITY --repo "$REPO"
92+
info "APPLE_SIGNING_IDENTITY set!"
93+
94+
echo "M2XH53X5DB" | gh secret set APPLE_TEAM_ID --repo "$REPO"
95+
info "APPLE_TEAM_ID set!"
96+
97+
# ── Step 4: Apple ID for Notarization ───────────────────────────────────────
98+
step "4/6: APPLE_ID + APPLE_PASSWORD (for notarization)"
99+
echo ""
100+
echo " APPLE_ID: Your Apple ID email address"
101+
echo " APPLE_PASSWORD: App-specific password from https://appleid.apple.com"
102+
echo " → Sign In → App-Specific Passwords → Generate"
103+
echo ""
104+
105+
ask "Apple ID email (or 'skip'):"
106+
read -r APPLE_ID_VAL
107+
108+
if [ "$APPLE_ID_VAL" != "skip" ] && [ -n "$APPLE_ID_VAL" ]; then
109+
echo "$APPLE_ID_VAL" | gh secret set APPLE_ID --repo "$REPO"
110+
info "APPLE_ID set!"
111+
112+
ask "App-specific password:"
113+
read -rs APP_PASS
114+
echo ""
115+
if [ -n "$APP_PASS" ]; then
116+
echo "$APP_PASS" | gh secret set APPLE_PASSWORD --repo "$REPO"
117+
info "APPLE_PASSWORD set!"
118+
fi
119+
else
120+
warn "Skipped Apple ID + Password"
121+
fi
122+
123+
# ── Step 5: Tauri Signing Key ───────────────────────────────────────────────
124+
step "5/6: TAURI_SIGNING_PRIVATE_KEY"
125+
echo ""
126+
echo " This is the Tauri updater signing key (minisign format)."
127+
echo " If you don't have one, generate with: npx tauri signer generate"
128+
echo ""
129+
ask "Path to Tauri private key file (or 'skip'):"
130+
read -r TAURI_KEY_PATH
131+
132+
if [ "$TAURI_KEY_PATH" != "skip" ] && [ -f "$TAURI_KEY_PATH" ]; then
133+
gh secret set TAURI_SIGNING_PRIVATE_KEY --repo "$REPO" < "$TAURI_KEY_PATH"
134+
info "TAURI_SIGNING_PRIVATE_KEY set!"
135+
else
136+
warn "Skipped TAURI_SIGNING_PRIVATE_KEY"
137+
fi
138+
139+
# ── Step 6: Tauri Key Password ──────────────────────────────────────────────
140+
step "6/6: TAURI_SIGNING_PRIVATE_KEY_PASSWORD"
141+
ask "Tauri signing key password (or 'skip'):"
142+
read -rs TAURI_PASS
143+
echo ""
144+
145+
if [ "$TAURI_PASS" != "skip" ] && [ -n "$TAURI_PASS" ]; then
146+
echo "$TAURI_PASS" | gh secret set TAURI_SIGNING_PRIVATE_KEY_PASSWORD --repo "$REPO"
147+
info "TAURI_SIGNING_PRIVATE_KEY_PASSWORD set!"
148+
else
149+
warn "Skipped TAURI_SIGNING_PRIVATE_KEY_PASSWORD"
150+
fi
151+
152+
# ── Summary ─────────────────────────────────────────────────────────────────
153+
echo ""
154+
echo "═══════════════════════════════════════════════════════════"
155+
echo " Setup Complete!"
156+
echo "═══════════════════════════════════════════════════════════"
157+
echo ""
158+
info "Configured secrets:"
159+
gh secret list --repo "$REPO"
160+
echo ""
161+
echo " Next: push a v* tag to trigger Build & Release workflow"
162+
echo ""

0 commit comments

Comments
 (0)