|
| 1 | +# `os create` scaffold smoke — the emitted project installs and builds OUTSIDE |
| 2 | +# this monorepo (#14824). |
| 3 | +# |
| 4 | +# ## What this gate holds |
| 5 | +# |
| 6 | +# `os create` is presented on four public documentation pages as a user-facing |
| 7 | +# scaffolder, and every project it emitted was monorepo-shaped: `workspace:*` |
| 8 | +# dependency specs, a `tsconfig.json` extending `'../../tsconfig.json'`, and a |
| 9 | +# default output directory inside this repository. A reader who followed the |
| 10 | +# docs got a project `pnpm install` refuses. The maintainer ruled that a |
| 11 | +# documented developer-facing command must work for the developer who follows |
| 12 | +# the docs, and attached an executable criterion: scaffold each template into a |
| 13 | +# temporary directory outside the repository, install it with the registry, and |
| 14 | +# boot / typecheck it — green outside the monorepo, ON CI, not on a developer |
| 15 | +# box. `scripts/create-scaffold-smoke.sh` is that criterion; this workflow is |
| 16 | +# the "on CI" half. |
| 17 | +# |
| 18 | +# ## Why paths-filtered rather than label-gated opt-in |
| 19 | +# |
| 20 | +# `pack-smoke-optin.yml` covers a different defect class — an author widening |
| 21 | +# the unauthenticated surface — where the only reliable trigger is the author |
| 22 | +# recognising their own change, so a label is the honest shape and its header |
| 23 | +# forbids growing a `paths:` filter. This gate's defect class is the opposite: |
| 24 | +# it can only be introduced by editing a bounded, nameable set of files, and |
| 25 | +# those files are the `paths:` below. A label would mean a scaffolder change |
| 26 | +# could be merged by anyone who did not think to apply it, which is precisely |
| 27 | +# how the emitted contract drifted into being uninstallable in the first place. |
| 28 | +# |
| 29 | +# `init.ts` is in the set even though this gate does not test `os init`. The |
| 30 | +# standalone emission CALLS that module — `getCliVersion`, `SCAFFOLD_PNPM_RANGE` |
| 31 | +# and `renderPnpmWorkspaceYaml` are its exports — so its build approvals and its |
| 32 | +# version resolution decide whether an `os create` scaffold installs. Naming the |
| 33 | +# consumer and not the producer is the shape of coupling that lets a gate sit |
| 34 | +# green through the change that breaks it. |
| 35 | +# |
| 36 | +# The nightly run is the backstop for everything the `paths:` set cannot name: a |
| 37 | +# scaffolded project resolves the whole `@objectstack/*` graph, so a change in |
| 38 | +# any of those packages can break its install or its boot without touching a |
| 39 | +# single file listed here. |
| 40 | +# |
| 41 | +# ⛔ Not a required context — `scripts/check-required-contexts.mjs` owns that |
| 42 | +# registry, and a paths-filtered job cannot be required: on a PR that does not |
| 43 | +# trip the filter it never reports, and branch protection would block forever. |
| 44 | +# It is advisory in the same way `scaffold-e2e.yml` is. |
| 45 | +# |
| 46 | +# Every step below is part of a build / scaffold / install / build pipeline, not |
| 47 | +# a named local verification a dev pre-runs with `pnpm check:x`: |
| 48 | +# dispatch-gates: no-check-families -- scaffold + install + build pipeline, no named local check family exists for it |
| 49 | + |
| 50 | +name: OS Create Smoke |
| 51 | + |
| 52 | +on: |
| 53 | + pull_request: |
| 54 | + branches: |
| 55 | + - main |
| 56 | + paths: |
| 57 | + - 'packages/cli/src/commands/create.ts' |
| 58 | + - 'packages/cli/src/commands/init.ts' |
| 59 | + - 'scripts/create-scaffold-smoke.sh' |
| 60 | + - 'scripts/publish-smoke-pack.mjs' |
| 61 | + - '.github/workflows/os-create-smoke.yml' |
| 62 | + schedule: |
| 63 | + - cron: '41 4 * * *' |
| 64 | + workflow_dispatch: |
| 65 | + |
| 66 | +permissions: |
| 67 | + contents: read |
| 68 | + |
| 69 | +jobs: |
| 70 | + create-scaffold-smoke: |
| 71 | + name: Scaffold outside the monorepo, install, build |
| 72 | + runs-on: ubuntu-latest |
| 73 | + timeout-minutes: 60 |
| 74 | + concurrency: |
| 75 | + group: os-create-smoke-${{ github.event.pull_request.number || github.ref }} |
| 76 | + cancel-in-progress: true |
| 77 | + steps: |
| 78 | + # No `ref:` — on `pull_request` the default checkout is `refs/pull/N/merge`, |
| 79 | + # the merge preview, which is what `main` will actually contain. |
| 80 | + - name: Checkout |
| 81 | + uses: actions/checkout@v7 |
| 82 | + |
| 83 | + - name: Setup Node.js |
| 84 | + uses: actions/setup-node@v7 |
| 85 | + with: |
| 86 | + node-version: '22' |
| 87 | + |
| 88 | + - name: Setup pnpm |
| 89 | + uses: ./.github/actions/setup-pnpm |
| 90 | + |
| 91 | + - name: Get pnpm store directory |
| 92 | + shell: bash |
| 93 | + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV |
| 94 | + |
| 95 | + - name: Setup pnpm cache |
| 96 | + uses: actions/cache@v6 |
| 97 | + with: |
| 98 | + path: ${{ env.STORE_PATH }} |
| 99 | + key: ${{ runner.os }}-pnpm-store-v3-${{ hashFiles('**/pnpm-lock.yaml') }} |
| 100 | + restore-keys: | |
| 101 | + ${{ runner.os }}-pnpm-store-v3- |
| 102 | +
|
| 103 | + - name: Setup turbo cache |
| 104 | + uses: actions/cache@v6 |
| 105 | + with: |
| 106 | + path: .turbo/cache |
| 107 | + key: ${{ runner.os }}-turbo-${{ github.job }}-${{ github.sha }} |
| 108 | + restore-keys: | |
| 109 | + ${{ runner.os }}-turbo-${{ github.job }}- |
| 110 | + ${{ runner.os }}-turbo- |
| 111 | +
|
| 112 | + - name: Install dependencies |
| 113 | + run: pnpm install --frozen-lockfile |
| 114 | + |
| 115 | + # The smoke's own prerequisite, asserted by the script itself: it refuses |
| 116 | + # to run without packages/cli/dist and the `os` bin. |
| 117 | + - name: Build |
| 118 | + run: pnpm run build |
| 119 | + |
| 120 | + # ⛔ Do not add flags or env here to make a red go away. A refusal this |
| 121 | + # script reports is a refusal a developer following the docs would get. |
| 122 | + - name: Scaffold smoke (packed tarballs, outside the repo) |
| 123 | + run: bash scripts/create-scaffold-smoke.sh |
0 commit comments