ci: add a windows-latest leg and document Windows support
None of this was caught because nothing ever ran on Windows: every job in every workflow was ubuntu-latest, and checks.yml did not even run on the PR that reported it — only Vercel, which failed on fork authorization. The check job now runs on both, fail-fast off so a Windows-only break still reports the Linux result. `shell: bash` on the multi-line steps, since the default shell there is pwsh, which shares none of that syntax; Git Bash ships on the runner, so nothing needs rewriting. The two drift checks stay Linux-only — they verify that a committed generated file matches its generator, which is a property of the repo, not of the platform. The new build step is unconditional and unscoped on purpose. `--affected` is exactly what let these through: they lived in build scripts, so a PR touching no affected package never ran them. A `pnpm clean` step guards the lane that was broken in all eleven packages. pnpm-workspace.yaml's release-age exclusions had drifted almost across the board — turbo pinned at 2.10.1 against 2.10.9 in the lockfile, the Claude SDK at 0.3.196 against 0.3.226, both codex packages a minor behind. A stale pin does not fail loudly; it simply stops excluding anything, and the package falls back under the gate. That is the silent optional-dep drop the file's own esbuild comment documents, and every one of these ships per-platform packages including win32.
This commit is contained in:
@@ -21,7 +21,16 @@ concurrency:
|
||||
jobs:
|
||||
check:
|
||||
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
# Windows is a first-class target — the CLI installs from npm onto it — but
|
||||
# it went untested until a contributor reported that a fresh clone could not
|
||||
# build there at all. Three separate path/line-ending bugs had shipped
|
||||
# invisibly because every lane ran on Linux only. fail-fast is off so a
|
||||
# Windows-only break still reports the Linux result, and vice versa.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
@@ -33,7 +42,12 @@ jobs:
|
||||
- name: Lint (Biome via Ultracite)
|
||||
run: pnpm lint
|
||||
|
||||
# `shell: bash` on every multi-line block below: the default shell on
|
||||
# windows-latest is pwsh, which shares none of this syntax — no `[ -n … ]`,
|
||||
# no `if ! cmd`, and `${VAR}` is not env expansion. Git Bash ships on the
|
||||
# Windows runner, so pinning the shell is enough; nothing needs rewriting.
|
||||
- name: Typecheck + test (affected)
|
||||
shell: bash
|
||||
env:
|
||||
TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha }}
|
||||
run: |
|
||||
@@ -43,10 +57,25 @@ jobs:
|
||||
pnpm turbo run typecheck test
|
||||
fi
|
||||
|
||||
# Unconditional and unscoped, unlike the step above. `--affected` is what
|
||||
# let the Windows build bugs through: they lived in build scripts
|
||||
# (check-css.mjs, vendor-assets.mjs, the gen.mjs front-matter readers), and
|
||||
# a PR that touched none of the affected packages never ran them. This is
|
||||
# the step that actually proves a clean checkout builds on both platforms.
|
||||
- name: Build (every package)
|
||||
run: pnpm build
|
||||
|
||||
# apps/cli/README.md is generated from the root README.md, and it is what
|
||||
# npmjs.com renders for @airshiplabs/cli. Committed rather than built on
|
||||
# demand so a clean checkout can publish without running the generator.
|
||||
#
|
||||
# Linux only, here and below: both of these check that a COMMITTED
|
||||
# generated file matches what the generator emits. That is a property of
|
||||
# the repo, not of the platform, so running it twice doubles the runtime
|
||||
# and the flake surface for no extra signal.
|
||||
- name: README is not stale
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
if ! node scripts/sync-readme.mjs --check; then
|
||||
echo "::error file=apps/cli/README.md::The CLI README is stale. Run 'make readme' and commit the result."
|
||||
@@ -57,16 +86,24 @@ jobs:
|
||||
# Regenerated by BUILDING, not by `tsr generate`: two things write this
|
||||
# file and they disagree — the router CLI emits the tree alone, while the
|
||||
# Start Vite plugin appends the `declare module` block that registers the
|
||||
# router type for SSR. The build's output is the committed one.
|
||||
# router type for SSR. The build's output is the committed one. The build
|
||||
# step above already wrote it; this only reads the result.
|
||||
- name: Route tree is not stale
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
pnpm turbo run build --filter=@airship/web
|
||||
if ! git diff --quiet -- apps/web/src/routeTree.gen.ts; then
|
||||
git diff --stat -- apps/web/src/routeTree.gen.ts
|
||||
echo "::error file=apps/web/src/routeTree.gen.ts::The route tree is stale. Run 'make web:build' and commit the result."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Last, because it deletes what everything above produced. Every workspace
|
||||
# `clean` was `rm -rf` until this lane existed to catch it — a command that
|
||||
# does not exist on Windows, so `pnpm clean` failed in all eleven packages.
|
||||
- name: Clean (removes what the build produced)
|
||||
run: pnpm clean
|
||||
|
||||
commitlint:
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -30,6 +30,43 @@ Open <http://localhost:5174> and you are looking at Airship, with Airship's own
|
||||
inside it. Pick the hero's button, ask for a change, and the diff lands in `apps/web/src/`.
|
||||
`make run:solo` does both in one terminal via `--exec`.
|
||||
|
||||
### On Windows
|
||||
|
||||
Everything builds, tests and runs on Windows — `checks.yml` gates every PR on a
|
||||
`windows-latest` leg alongside Linux, so a break there fails the PR.
|
||||
|
||||
`make` is the one thing that does not carry over: the Makefile declares
|
||||
`SHELL := /bin/bash` and a handful of targets genuinely need it (`help` is an `awk`
|
||||
program, `preflight` a shell conditional, `release` a bash script). It is a thin
|
||||
wrapper either way — every recipe is one `pnpm` or `node` call — so use those directly:
|
||||
|
||||
| Instead of | Run |
|
||||
| --------------- | ---------------------------------------------------------- |
|
||||
| `make demo` | `pnpm install && pnpm build` |
|
||||
| `make web:dev` | `pnpm dev:web` |
|
||||
| `make run` | `node apps/cli/dist/index.js --target 5173 --cwd apps/web` |
|
||||
| `make run:solo` | `node apps/cli/dist/index.js --cwd apps/web --exec "pnpm dev:web"` |
|
||||
| `make doctor` | `node apps/cli/dist/index.js doctor --cwd apps/web` |
|
||||
| `make check` | `pnpm lint && pnpm typecheck && pnpm test` |
|
||||
| `make readme` | `node scripts/sync-readme.mjs` |
|
||||
| `make storybook`| `pnpm turbo run storybook --filter=@airship/overlay` |
|
||||
|
||||
`pnpm dev:web` rather than a bare `vite dev`: the site cannot start until
|
||||
`@airship/site-tokens` has emitted `dist/tokens.css`, and only turbo knows that.
|
||||
|
||||
Two things worth setting up once:
|
||||
|
||||
- **Git Bash**, which ships with Git for Windows, runs the Husky hooks and the release
|
||||
scripts. Without a POSIX `sh` on PATH the pre-commit formatter silently does not run.
|
||||
- **Developer Mode** (Settings → System → For developers), so pnpm can create the
|
||||
symlinks its `node_modules` layout depends on without elevation.
|
||||
|
||||
Line endings are pinned to LF by [`.gitattributes`](.gitattributes) — do not override it
|
||||
with `core.autocrlf`. Several generators parse their input with anchored regexes, and a
|
||||
CRLF checkout makes them report a missing front-matter block rather than a wrong one.
|
||||
If you cloned before that file existed, renormalize once with
|
||||
`git rm --cached -r . && git reset --hard`.
|
||||
|
||||
## Repo layout
|
||||
|
||||
| Package | Role |
|
||||
|
||||
@@ -428,6 +428,8 @@ and provider it would use from your terminal.
|
||||
|
||||
Node 22.13 or later, and one of Claude Code, OpenAI Codex or OpenCode.
|
||||
|
||||
macOS, Linux and Windows. Every PR is built and tested on Linux and Windows.
|
||||
|
||||
## Links
|
||||
|
||||
- [airship.design](https://airship.design)
|
||||
|
||||
@@ -430,6 +430,8 @@ and provider it would use from your terminal.
|
||||
|
||||
Node 22.13 or later, and one of Claude Code, OpenAI Codex or OpenCode.
|
||||
|
||||
macOS, Linux and Windows. Every PR is built and tested on Linux and Windows.
|
||||
|
||||
## Links
|
||||
|
||||
- [airship.design](https://airship.design)
|
||||
|
||||
+30
-21
@@ -24,28 +24,37 @@ minimumReleaseAgeExclude:
|
||||
# a name pattern carrying a version union and enumerating all 25 for every
|
||||
# bump is not maintainable. They only ever publish in lockstep with the
|
||||
# version above, so the unpinned glob follows whatever it is pinned to.
|
||||
#
|
||||
# EVERY VERSION BELOW MUST MATCH THE LOCKFILE. A pin left behind at an older
|
||||
# version does not fail loudly — it simply stops excluding anything, and the
|
||||
# package silently falls back under the release-age gate, which is the
|
||||
# esbuild failure above all over again. All of these but esbuild had drifted
|
||||
# at once (turbo 2.10.1 vs 2.10.9, the Claude SDK 0.3.196 vs 0.3.226, both
|
||||
# codex packages a minor behind), and every one of them ships per-platform
|
||||
# optional deps including win32 — so the package most likely to go missing is
|
||||
# the one for whichever platform is least represented in the team's machines.
|
||||
# `pnpm why <pkg>` after a bump, and update the pin in the same commit.
|
||||
- 'esbuild@0.28.2'
|
||||
- '@esbuild/*'
|
||||
- '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.196'
|
||||
- '@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.196'
|
||||
- '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.196'
|
||||
- '@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.196'
|
||||
- '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.196'
|
||||
- '@anthropic-ai/claude-agent-sdk-linux-x64@0.3.196'
|
||||
- '@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.196'
|
||||
- '@anthropic-ai/claude-agent-sdk-win32-x64@0.3.196'
|
||||
- '@anthropic-ai/claude-agent-sdk@0.3.196'
|
||||
- '@openai/codex-sdk@0.146.0'
|
||||
- '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.226'
|
||||
- '@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.226'
|
||||
- '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.226'
|
||||
- '@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.226'
|
||||
- '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.226'
|
||||
- '@anthropic-ai/claude-agent-sdk-linux-x64@0.3.226'
|
||||
- '@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.226'
|
||||
- '@anthropic-ai/claude-agent-sdk-win32-x64@0.3.226'
|
||||
- '@anthropic-ai/claude-agent-sdk@0.3.226'
|
||||
- '@openai/codex-sdk@0.147.0'
|
||||
# Unlike the other two SDKs this bundles no binary — the `opencode` CLI is a
|
||||
# separate install, detected on PATH at runtime. See providers/opencode.ts.
|
||||
- '@opencode-ai/sdk@1.18.13'
|
||||
- '@openai/codex@0.146.0-darwin-arm64 || 0.146.0-darwin-x64 || 0.146.0-linux-arm64 || 0.146.0-linux-x64 || 0.146.0-win32-arm64 || 0.146.0-win32-x64 || 0.146.0'
|
||||
- '@turbo/darwin-64@2.10.1'
|
||||
- '@turbo/darwin-arm64@2.10.1'
|
||||
- '@turbo/linux-64@2.10.1'
|
||||
- '@turbo/linux-arm64@2.10.1'
|
||||
- '@turbo/windows-64@2.10.1'
|
||||
- '@turbo/windows-arm64@2.10.1'
|
||||
- turbo@2.10.1
|
||||
- ultracite@7.10.0
|
||||
- '@opencode-ai/sdk@1.18.13'
|
||||
- '@opencode-ai/sdk@1.18.15'
|
||||
- '@openai/codex@0.147.0-darwin-arm64 || 0.147.0-darwin-x64 || 0.147.0-linux-arm64 || 0.147.0-linux-x64 || 0.147.0-win32-arm64 || 0.147.0-win32-x64 || 0.147.0'
|
||||
- '@turbo/darwin-64@2.10.9'
|
||||
- '@turbo/darwin-arm64@2.10.9'
|
||||
- '@turbo/linux-64@2.10.9'
|
||||
- '@turbo/linux-arm64@2.10.9'
|
||||
- '@turbo/windows-64@2.10.9'
|
||||
- '@turbo/windows-arm64@2.10.9'
|
||||
- turbo@2.10.9
|
||||
- ultracite@7.10.2
|
||||
|
||||
Reference in New Issue
Block a user