Skip to content

ci: run the release pack-and-smoke gate on every PR (#297) #753

ci: run the release pack-and-smoke gate on every PR (#297)

ci: run the release pack-and-smoke gate on every PR (#297) #753

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
# Reads `packageManager` from root package.json (corepack-style pin).
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm check
- run: pnpm test
- name: Coverage (measure-only, per-directory)
run: pnpm test:coverage
- name: Upload coverage report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-report
path: coverage/
if-no-files-found: error
- name: Changeset presence (warn-only)
if: github.event_name == 'pull_request'
run: |
base="${{ github.event.pull_request.base.sha }}"
subject="$(git log -1 --format=%s)"
if printf '%s' "$subject" | grep -qE '^(chore|ci)(\(|:)'; then
echo "::notice::chore/ci commit — changeset not expected, skipping check."
exit 0
fi
if git diff --name-only "$base"...HEAD -- '.changeset/*.md' | grep -q .; then
echo "::notice::Changeset present."
else
echo "::warning::No changeset found for this PR. Behavioral/user-facing changes should ship a .changeset/*.md (see .changeset/README.md). chore:/ci: PRs are exempt."
fi
smoke:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm -r build
- name: Pack and smoke cycling-coach
# Mirrors release.yml's smoke gate: install the packed tarball into a
# fresh consumer and run the bundled binary. Runs on every PR (including
# the Version PR) and on main, so a broken published bundle — e.g. an ESM
# dynamic-require of a Node builtin that only surfaces in the packed
# artifact, not in `pnpm test` — is caught before merge, not at release.
# cycling-coach is the only publishable binary (ADR-0009); extend when
# running-coach / duathlon-coach graduate.
run: |
set -euo pipefail
PACKAGE=cycling-coach
cd "packages/$PACKAGE"
pnpm pack --pack-destination "/tmp/smoke-pack-$PACKAGE"
mkdir -p "/tmp/smoke-consumer-$PACKAGE"
cd "/tmp/smoke-consumer-$PACKAGE"
npm init -y > /dev/null
npm install /tmp/smoke-pack-$PACKAGE/*.tgz
timeout 30 node "node_modules/$PACKAGE/dist/index.js" --help
secrets-macos:
runs-on: macos-latest
env:
CI: macos
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
# Reads `packageManager` from root package.json (corepack-style pin).
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @enduragent/core exec vitest run tests/secrets