Skip to content
Merged
68 changes: 32 additions & 36 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
# Checkout the PR head (works for forks) instead of epicshop add
# repo#branch, which only clones epicweb-dev and cannot see fork branches.
- name: ⬇️ Checkout repo
uses: actions/checkout@v4
with:
path: workshop

- name: ⎔ Setup node
uses: actions/setup-node@v4
with:
Expand All @@ -30,8 +37,9 @@ jobs:
# intentionally fail solution tests/types, which would create false CI red.
# Use bash explicitly: Windows runners default to PowerShell, which cannot
# parse this retry loop.
- name: ▶️ Add repo
- name: ▶️ Setup workshop
shell: bash
working-directory: ./workshop
env:
# Kept getting npm ECOMPROMISED errors on windows. This fixed it.
npm_config_cache: ${{ runner.temp }}/npm-cache
Expand All @@ -43,20 +51,20 @@ jobs:
max_attempts=3
delay_seconds=20
while true; do
echo "::group::epicshop add attempt ${attempt}/${max_attempts}"
echo "::group::epicshop setup attempt ${attempt}/${max_attempts}"
set +e
npx --yes epicshop@latest add ${{ github.event.repository.name }}#${{ github.head_ref || github.ref_name }} ./workshop
npx --yes epicshop@latest setup
status=$?
set -e
echo "::endgroup::"
if [ "$status" -eq 0 ]; then
exit 0
fi
if [ "$attempt" -ge "$max_attempts" ]; then
echo "epicshop add failed after ${max_attempts} attempts" >&2
echo "epicshop setup failed after ${max_attempts} attempts" >&2
exit "$status"
fi
echo "epicshop add failed (exit ${status}); retrying in ${delay_seconds}s..."
echo "epicshop setup failed (exit ${status}); retrying in ${delay_seconds}s..."
sleep "$delay_seconds"
delay_seconds=$((delay_seconds * 2))
attempt=$((attempt + 1))
Expand Down Expand Up @@ -85,53 +93,41 @@ jobs:
name: 🧪 Test
# Browser installation plus solution-app tests can exceed the setup budget.
timeout-minutes: 30
runs-on: ubuntu-latest
# Pin 24.04: ubuntu-latest moved to 26.04, and Playwright 1.56.1 in this
# lockfile does not support chromium on ubuntu26.04 yet.
runs-on: ubuntu-24.04
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: ⎔ Setup node
uses: actions/setup-node@v4
with:
node-version: 26
# The workshop's pinned MSW/Undici stack has an AbortSignal realm
# incompatibility on Node 26. Node 22 is supported by package.json.
# Restores the Test job config from #45 that epicshop updates overwrote.
node-version: 22

- name: 📦 Install dependencies
run: npm ci

- name: 🎭 Install Playwright browser
if: ${{ hashFiles('epicshop/test.js') != '' }}
run: npx playwright install --with-deps chromium

- name: ◭ Generate Prisma clients
if: ${{ hashFiles('epicshop/test.js') != '' }}
run: |
set -euo pipefail
shopt -s globstar nullglob
schemas=()
for schema in ./exercises/**/prisma/schema.prisma; do
app_dir="${schema%/prisma/schema.prisma}"
app_name="${app_dir##*/}"
if [[ "$app_name" =~ ^[0-9]+\.solution(\..+)?$ ]]; then
schemas+=("$schema")
fi
done
if [ "${#schemas[@]}" -eq 0 ]; then
echo "No solution-app Prisma schemas found; skipping Prisma client generation."
exit 0
fi
for schema in "${schemas[@]}"; do
app_dir="${schema%/prisma/schema.prisma}"
echo "::group::prisma generate in ${app_dir}"
(cd "$app_dir" && npx prisma generate)
echo "::endgroup::"
done
# Solution CI runs vitest via epicshop/test.js, not Playwright e2e.
# Skip browser install: with-deps has been hanging after Chromium download.
- name: 🏗 Prepare solution tests
env:
SKIP_PLAYWRIGHT: true
SKIP_PLAYGROUND: true
run: npm run setup:custom

# Workshops ship epicshop/test.js (not .ts). `..s` runs solution apps only.
# Exercises 1-3 contain Playwright suites, which cannot be collected by
# their `npm test` Vitest command. Run every test in the completed Vitest
# solution for exercises 4-11; intermediate steps can intentionally
# demonstrate failing tests that later steps fix.
- name: 🧪 Run solution tests
run: |
set -euo pipefail
if [ -f ./epicshop/test.js ]; then
node ./epicshop/test.js ..s
node ./epicshop/test.js '4.4.s,5.2.s,6.2.s,7.2.s,8.3.s,9.3.s,10.1.s,11.3.s'
else
echo "No epicshop/test.js in this workshop; skipping solution-app test runner."
fi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ expect.extend({
? ` to ${this.utils.printExpected(redirectTo)}`
: ''
} but got ${
location ? 'no redirect' : this.utils.printReceived(location)
// Note: the videos show these swapped; this is the corrected order so the failure message prints the received location when there is one.
location ? this.utils.printReceived(location) : 'no redirect'
}`,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ expect.extend({
? ` to ${this.utils.printExpected(redirectTo)}`
: ''
} but got ${
location ? 'no redirect' : this.utils.printReceived(location)
// Note: the videos show these swapped; this is the corrected order so the failure message prints the received location when there is one.
location ? this.utils.printReceived(location) : 'no redirect'
}`,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ expect.extend({
? ` to ${this.utils.printExpected(redirectTo)}`
: ''
} but got ${
location ? 'no redirect' : this.utils.printReceived(location)
// Note: the videos show these swapped; this is the corrected order so the failure message prints the received location when there is one.
location ? this.utils.printReceived(location) : 'no redirect'
}`,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ expect.extend({
? ` to ${this.utils.printExpected(redirectTo)}`
: ''
} but got ${
location ? 'no redirect' : this.utils.printReceived(location)
// Note: the videos show these swapped; this is the corrected order so the failure message prints the received location when there is one.
location ? this.utils.printReceived(location) : 'no redirect'
}`,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ expect.extend({
? ` to ${this.utils.printExpected(redirectTo)}`
: ''
} but got ${
location ? 'no redirect' : this.utils.printReceived(location)
// Note: the videos show these swapped; this is the corrected order so the failure message prints the received location when there is one.
location ? this.utils.printReceived(location) : 'no redirect'
}`,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ expect.extend({
? ` to ${this.utils.printExpected(redirectTo)}`
: ''
} but got ${
location ? 'no redirect' : this.utils.printReceived(location)
// Note: the videos show these swapped; this is the corrected order so the failure message prints the received location when there is one.
location ? this.utils.printReceived(location) : 'no redirect'
}`,
}
}
Expand Down
Loading