chore: version packages #4026
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: CI | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| # Cancel in-progress runs on the same PR ref when a new commit lands. Pushes | |
| # to main never cancel — every merge should produce a green run. | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| env: | |
| BUN_VERSION: "1.3.14" | |
| jobs: | |
| # Cheap path-filter job so downstream jobs can skip on docs-only PRs and | |
| # so the expensive codegen-drift steps only run when GraphQL inputs | |
| # actually moved. Jobs that depend on this and evaluate `if:` to false | |
| # are reported as "Skipped" — required status checks treat that as | |
| # passing, so the workflow stays compatible with branch protection. | |
| # | |
| # Two paths-filter steps: exclusion-based `code` needs | |
| # predicate-quantifier: every (with the default `some`, `**` matches | |
| # every file and `!**/*.md` never excludes — so CHANGELOG-only PRs | |
| # still ran full lint/test). OR-style filters keep the default `some`. | |
| changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| code: ${{ steps.code.outputs.code }} | |
| schema_inputs: ${{ steps.rest.outputs.schema_inputs }} | |
| codegen_inputs: ${{ steps.rest.outputs.codegen_inputs }} | |
| publishable: ${{ steps.rest.outputs.publishable }} | |
| web_build: ${{ steps.rest.outputs.web_build }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 2 | |
| persist-credentials: false | |
| # Exclusions only work with predicate-quantifier: every (dorny/paths-filter). | |
| # Workflow files intentionally stay in scope — changing CI on a | |
| # docs-only commit should still run CI. | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: code | |
| with: | |
| predicate-quantifier: every | |
| filters: | | |
| code: | |
| - '**' | |
| - '!**/*.md' | |
| - '!docs/**' | |
| - '!.context/**' | |
| - '!LICENSE' | |
| # OR-style filters — default predicate-quantifier: some. | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: rest | |
| with: | |
| filters: | | |
| # GraphQL schema snapshot is generated from workers/api source. | |
| # Skip the verify step when nothing here moved. | |
| schema_inputs: | |
| - 'workers/api/**' | |
| - 'packages/core/**' | |
| - 'packages/api-types/graphql/schema.graphql' | |
| # web/codegen consumes the committed schema + web operations. | |
| codegen_inputs: | |
| - 'web/**' | |
| - 'packages/api-types/graphql/schema.graphql' | |
| # The two packages published to npm. A PR touching them should | |
| # carry a changeset — the changeset-check job below only warns. | |
| publishable: | |
| - 'packages/core/**' | |
| - 'packages/api-types/**' | |
| # Mirror web/vercel.json ignoreCommand: Next production build when | |
| # anything Vercel would rebuild has moved. Root oxlint ignores | |
| # web/**, so missing named exports only surface on `next build`. | |
| web_build: | |
| - 'web/**' | |
| - 'packages/**' | |
| - 'scripts/**' | |
| - 'bun.lock' | |
| - 'package.json' | |
| - '.github/workflows/ci.yml' | |
| lint: | |
| name: Lint & Format | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - run: bun install --frozen-lockfile | |
| # oxlint lints + type-checks the workspace from the root .oxlintrc.json | |
| # (typescript-go via oxlint-tsgolint; curated semantic rules + typeCheck). | |
| - name: Oxlint | |
| run: bun run lint | |
| - name: Oxfmt | |
| run: bun run format:check | |
| test: | |
| name: Test | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - run: bun install --frozen-lockfile | |
| # workers/discovery, workers/mcp, and workers/webhooks are excluded | |
| # from the root workspace and carry their own bun.lock — the root | |
| # install above doesn't populate their node_modules, so per-worker | |
| # tsc + deploy both need a local install. These steps also catch | |
| # stale per-worker lockfiles at PR time (previously a separate set | |
| # of steps at the end of this job; folded in so we don't install | |
| # twice). | |
| - name: Install workers/discovery deps | |
| working-directory: workers/discovery | |
| run: bun install --frozen-lockfile | |
| - name: Install workers/mcp deps | |
| working-directory: workers/mcp | |
| run: bun install --frozen-lockfile | |
| - name: Install workers/webhooks deps | |
| working-directory: workers/webhooks | |
| run: bun install --frozen-lockfile | |
| # workers/mcp is excluded from the root oxlint type-check (carved-out | |
| # workspace with its own bun.lock + Agents SDK tool Zod shapes that | |
| # typescript-go doesn't model yet). Keep a targeted tsc gate here; UI | |
| # sub-build has mcp:ui:typecheck separately. | |
| - name: Type-check (workers/mcp) | |
| working-directory: workers/mcp | |
| run: npx tsc --noEmit | |
| - name: Check migration filenames | |
| if: github.event_name == 'pull_request' | |
| env: | |
| BASE_REF: ${{ github.base_ref }} | |
| run: ./scripts/check-migration-filenames.sh "origin/$BASE_REF" | |
| - name: Pair schema changes with a wrangler migration (PR only) | |
| if: github.event_name == 'pull_request' | |
| env: | |
| BASE_REF: ${{ github.base_ref }} | |
| run: | | |
| # If a schema file changed on this PR, require at least one added | |
| # file under workers/api/migrations/. Catches schema changes that | |
| # ship without a paired wrangler migration. | |
| schema_changed=$(git diff --name-only "origin/$BASE_REF"...HEAD -- \ | |
| 'packages/core/src/schema.ts' \ | |
| 'packages/core-internal/src/schema-coverage.ts' \ | |
| 'workers/api/src/db/schema-cron.ts' \ | |
| 'workers/api/src/db/schema-auth.ts' \ | |
| 'workers/api/src/db/schema-follows.ts' || true) | |
| if [ -z "$schema_changed" ]; then | |
| echo "No schema files changed — pairing check skipped." | |
| exit 0 | |
| fi | |
| added=$(git diff --name-only --diff-filter=A "origin/$BASE_REF"...HEAD -- 'workers/api/migrations/*.sql' || true) | |
| if [ -z "$added" ]; then | |
| echo "ERROR: schema files changed, but this PR adds no file under workers/api/migrations/." >&2 | |
| echo "Changed schema files:" >&2 | |
| echo "$schema_changed" | sed 's/^/ /' >&2 | |
| echo "Hand-author the wrangler migration and commit it alongside the schema change." >&2 | |
| echo "Preview the SQL with: bun run db:generate (output goes to .drizzle-out/)." >&2 | |
| exit 1 | |
| fi | |
| echo "Schema files changed:" | |
| echo "$schema_changed" | sed 's/^/ /' | |
| echo "Found new wrangler migration file(s):" | |
| echo "$added" | sed 's/^/ /' | |
| - name: Verify GraphQL schema snapshot is up to date | |
| if: needs.changes.outputs.schema_inputs == 'true' | |
| run: | | |
| bun workers/api/scripts/print-graphql-schema.ts | |
| if ! git diff --exit-code packages/api-types/graphql/schema.graphql; then | |
| echo "ERROR: packages/api-types/graphql/schema.graphql is stale." >&2 | |
| echo "Regenerate with: bun workers/api/scripts/print-graphql-schema.ts" >&2 | |
| exit 1 | |
| fi | |
| - name: Verify GraphQL operation codegen is up to date | |
| if: needs.changes.outputs.codegen_inputs == 'true' | |
| # graphql-codegen emits single-quoted, unformatted output; the | |
| # committed copy is oxfmt-normalised by lint-staged on commit. | |
| # Mirror that here before diffing or every PR would fail. | |
| working-directory: web | |
| run: | | |
| bun run codegen | |
| bunx oxfmt --write src/lib/graphql/__generated__/ | |
| if ! git diff --exit-code src/lib/graphql/__generated__/; then | |
| echo "ERROR: web/src/lib/graphql/__generated__/ is stale." >&2 | |
| echo "Regenerate with: cd web && bun run codegen && bunx oxfmt --write src/lib/graphql/__generated__/" >&2 | |
| exit 1 | |
| fi | |
| - name: Verify OpenAPI spec covers public-read routes | |
| # Diffs every method registered under a publicReadRoutes prefix | |
| # against the generated /v1/openapi.json. Fails if any public-read | |
| # route is missing describeRoute(...) and isn't on the script's | |
| # ALLOWLIST. Sub-second pure-JS check; no network, D1, or Wrangler. | |
| run: bun scripts/check-openapi-coverage.ts | |
| - name: Verify managed-agent YAML is up to date | |
| # Re-renders the committed managed-agents/*.agent.yaml from the prompt | |
| # builders, AGENT_TOOLS, and skill IDs, and fails if any file drifted. | |
| # Pure codegen — no network or API calls. Regenerate locally with: | |
| # bun scripts/render-managed-agents.ts | |
| run: bun scripts/render-managed-agents.ts --check | |
| - name: Run tests | |
| run: bun run test | |
| # Same production Next build Vercel runs for web/ (Next 16 + Turbopack). Root | |
| # oxlint ignorePatterns include web/**, so type/export errors that fail the | |
| # Vercel deploy never fail lint/test alone. Path filter mirrors | |
| # web/vercel.json ignoreCommand so docs-only PRs stay skipped. | |
| web-build: | |
| name: Web build (Next) | |
| needs: changes | |
| if: needs.changes.outputs.web_build == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| env: | |
| # Public prod API is fine for compile + static generation; no secrets | |
| # required. Matches web/.env.example defaults for a green build. | |
| RELEASES_API_URL: https://api.releases.sh | |
| NEXT_PUBLIC_BETTER_AUTH_URL: https://api.releases.sh | |
| NEXT_PUBLIC_USER_API_KEYS: "false" | |
| NEXT_PUBLIC_DEVICE_AUTH_ENABLED: "false" | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| # Root install: web is a workspace package and consumes packages/* via | |
| # workspace:*. Matches Vercel installCommand ("bun install") at monorepo root. | |
| - run: bun install --frozen-lockfile | |
| # web/package.json "build": copy install script, build .well-known assets, | |
| # then `next build` (Turbopack in Next 16). Catches missing exports, | |
| # bad imports, and Next compile errors before Vercel. | |
| - name: Next production build | |
| working-directory: web | |
| run: bun run build | |
| # Non-blocking reminder: a PR that touches a published package (packages/core, | |
| # packages/api-types) should usually add a changeset so the release notes + | |
| # version bump are captured. `continue-on-error` makes this advisory — a | |
| # missing changeset surfaces as a warning but never fails the PR. Do NOT add | |
| # this to branch protection. | |
| changeset-check: | |
| name: Changeset present? | |
| needs: changes | |
| if: github.event_name == 'pull_request' && needs.changes.outputs.publishable == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - run: bun install --frozen-lockfile | |
| - name: Check for a changeset | |
| env: | |
| BASE_REF: ${{ github.base_ref }} | |
| run: | | |
| if bunx changeset status --since="origin/$BASE_REF"; then | |
| echo "Changeset present (or no publishable change detected)." | |
| else | |
| echo "::warning::This PR changes a published package but adds no changeset. Run 'bun run changeset' to capture the version bump + changelog. (Advisory — not required to merge.)" | |
| fi |