feat: monorepo modernization — pnpm 11 + Node 24, Docusaurus docs, React <Apollon> API, VS Code rewrite
#16
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Health Checks | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| detect-changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| code: ${{ steps.filter.outputs.code }} | |
| library: ${{ steps.filter.outputs.library }} | |
| should-skip: ${{ steps.skip-check.outputs.should_skip }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - id: skip-check | |
| uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5 | |
| with: | |
| do_not_skip: '["workflow_dispatch"]' | |
| - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 | |
| id: filter | |
| if: steps.skip-check.outputs.should_skip != 'true' | |
| with: | |
| filters: | | |
| code: | |
| - 'library/**' | |
| - 'standalone/**' | |
| - 'vscode-extension/**' | |
| - 'package.json' | |
| - 'pnpm-lock.yaml' | |
| - 'pnpm-workspace.yaml' | |
| - '.npmrc' | |
| - '.nvmrc' | |
| - 'tsconfig*.json' | |
| - '.github/workflows/pr-health-checks.yml' | |
| library: | |
| - 'library/**' | |
| - 'pnpm-lock.yaml' | |
| - 'pnpm-workspace.yaml' | |
| - '.github/workflows/pr-health-checks.yml' | |
| lint-and-format-check: | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.should-skip != 'true' && needs.detect-changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@ac6db6d3c1f721f886538a378a2d73e85697340a # v6.0.8 | |
| - name: Set up Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 | |
| with: | |
| node-version-file: ".nvmrc" | |
| cache: "pnpm" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run Lint | |
| run: pnpm run lint | |
| - name: Check format | |
| run: pnpm run format:check | |
| - name: Check build | |
| run: pnpm run build | |
| - name: Run unit tests | |
| run: pnpm run test | |
| e2e-tests: | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.should-skip != 'true' && needs.detect-changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@ac6db6d3c1f721f886538a378a2d73e85697340a # v6.0.8 | |
| - name: Set up Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 | |
| with: | |
| node-version-file: ".nvmrc" | |
| cache: "pnpm" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm run build:lib | |
| - name: Cache Playwright browsers | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| id: playwright-cache | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: playwright-${{ runner.os }}-v1.59.1-${{ hashFiles('standalone/webapp/package.json') }} | |
| - name: Install Playwright browsers | |
| if: steps.playwright-cache.outputs.cache-hit != 'true' | |
| run: pnpm --filter @tumaet/webapp exec playwright install chromium | |
| - name: Install Playwright system deps | |
| run: pnpm --filter @tumaet/webapp exec playwright install-deps chromium | |
| - name: Run E2E tests | |
| run: pnpm --filter @tumaet/webapp exec playwright test tests/e2e/ | |
| - name: Upload Playwright report | |
| if: ${{ failure() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: playwright-report-e2e | |
| path: standalone/webapp/playwright-report/ | |
| retention-days: 14 | |
| - name: Upload Playwright test results | |
| if: ${{ failure() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: playwright-test-results-e2e | |
| path: standalone/webapp/test-results/ | |
| retention-days: 14 | |
| # Library has `engines.node: ">=22"` for its published consumers — back the | |
| # promise with a build+test on Node 22 LTS so a Node-24-only feature can't | |
| # silently land in the npm tarball. | |
| library-node22-compat: | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.should-skip != 'true' && needs.detect-changes.outputs.library == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@ac6db6d3c1f721f886538a378a2d73e85697340a # v6.0.8 | |
| - name: Set up Node 22 LTS | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 | |
| with: | |
| node-version: "22" | |
| cache: "pnpm" | |
| - name: Install dependencies | |
| # `engine-strict=true` rejects Node 22 against the root `>=24.15.0` | |
| # gate; library workspace is `>=22`, so install with the flag off. | |
| run: pnpm install --frozen-lockfile --config.engine-strict=false | |
| - name: Build library | |
| run: pnpm --filter @tumaet/apollon run build | |
| - name: Run library unit tests | |
| run: pnpm --filter @tumaet/apollon run test | |
| - name: Verify ESM exports-map resolution | |
| # The library is ESM-only. `import.meta.resolve` (sync, no side | |
| # effects) validates every subpath in the exports map — actually | |
| # evaluating the module would require jsdom. | |
| working-directory: standalone/server | |
| run: | | |
| node --input-type=module -e ' | |
| const root = import.meta.resolve("@tumaet/apollon"); | |
| const react = import.meta.resolve("@tumaet/apollon/react"); | |
| const internals = import.meta.resolve("@tumaet/apollon/internals"); | |
| const css = import.meta.resolve("@tumaet/apollon/style.css"); | |
| console.log({ root, react, internals, css }); | |
| ' | |
| - name: Lint published package shape (publint) | |
| working-directory: library | |
| run: pnpm dlx publint | |
| - name: Validate type resolution (arethetypeswrong) | |
| working-directory: library | |
| # ESM-only library, all exports flagged as such; CJS profile | |
| # would force-flag dual-package issues that don't apply. The | |
| # CLI binary `attw` ships from `@arethetypeswrong/cli`; pnpm | |
| # dlx resolves the binary from the package name implicitly. | |
| run: pnpm dlx @arethetypeswrong/cli@0.18.1 attw --pack . --profile esm-only | |
| - name: Enforce bundle-size budget | |
| working-directory: library | |
| # Budgets in library/package.json#size-limit. Fails on regression | |
| # of >10% (size-limit default) per artifact. | |
| run: pnpm run size | |
| # The library declares `react: ^18.3 || ^19` as a peer. Back the upper | |
| # bound with a build + test against React 19 so the range claim is | |
| # actually verified. | |
| library-react19-compat: | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.should-skip != 'true' && needs.detect-changes.outputs.library == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@ac6db6d3c1f721f886538a378a2d73e85697340a # v6.0.8 | |
| - name: Set up Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 | |
| with: | |
| node-version-file: ".nvmrc" | |
| cache: "pnpm" | |
| - name: Override library devDeps to React 19 via pnpm.overrides | |
| # Run only inside the library workspace; the rest of the | |
| # monorepo stays on React 18.3 (matches Artemis / standalone | |
| # webapp consumers). MUI v6 peer-supports React 19 since 6.0. | |
| run: | | |
| pnpm --filter @tumaet/apollon add -D \ | |
| react@19 \ | |
| react-dom@19 \ | |
| @types/react@19 \ | |
| @types/react-dom@19 \ | |
| @testing-library/react@16 \ | |
| --save-exact --ignore-scripts | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile=false --config.engine-strict=false | |
| - name: Build library against React 19 | |
| run: pnpm --filter @tumaet/apollon run build | |
| - name: Run library unit tests against React 19 | |
| run: pnpm --filter @tumaet/apollon run test | |
| # Baseline regeneration: see docs/development/visual-tests.md. | |
| visual-regression-tests: | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.should-skip != 'true' && needs.detect-changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| container: | |
| image: mcr.microsoft.com/playwright:v1.59.1-noble | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install Node 24 | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| curl -fsSL https://deb.nodesource.com/setup_24.x | bash - | |
| apt-get install -y nodejs | |
| node --version | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@ac6db6d3c1f721f886538a378a2d73e85697340a # v6.0.8 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm run build:lib | |
| - name: Run visual regression tests | |
| run: pnpm --filter @tumaet/webapp exec playwright test tests/visual/ | |
| - name: Upload Playwright report | |
| if: ${{ failure() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: playwright-report-visual | |
| path: standalone/webapp/playwright-report/ | |
| retention-days: 14 | |
| - name: Upload Playwright test results | |
| if: ${{ failure() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: playwright-test-results-visual | |
| path: standalone/webapp/test-results/ | |
| retention-days: 14 | |
| # Gate job — set this as the required status check in branch protection. | |
| # Correctly handles skipped jobs (docs-only, duplicate runs). | |
| pr-health-gate: | |
| name: PR Health Gate | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| needs: | |
| [ | |
| detect-changes, | |
| lint-and-format-check, | |
| library-node22-compat, | |
| library-react19-compat, | |
| e2e-tests, | |
| visual-regression-tests, | |
| ] | |
| if: always() | |
| steps: | |
| - name: Evaluate results | |
| run: | | |
| if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then | |
| echo "One or more checks failed" | |
| exit 1 | |
| fi | |
| if [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then | |
| echo "One or more checks were cancelled" | |
| exit 1 | |
| fi | |
| echo "All checks passed or were skipped" |