Files
ComfyUI_frontend/apps/website
DrJKL 6a4cf6023b refactor(website): harden ashby fetcher and tests
Follow-ups to the merged Ashby careers integration (PR #11590).

Type safety
- Add FailureCode discriminant ('missing-env' | 'auth' | 'schema' |
  'network') to FetchOutcome stale/failed variants. Replaces fragile
  reason.startsWith() pattern matching in staleAnnotation() with an
  exhaustive switch.
- Harden isRolesSnapshot() to validate departments[].roles is an array.
- Fix capitalize() so multi-word department names retain title case
  (e.g. "Product Engineering" no longer becomes "Product engineering").
- Simplify extractTitle() to drop redundant 'in' check and one as cast.
- Extract formatZodIssues() helper, used by both envelope and per-job
  parse error paths.

CI reporter
- escapeAnnotation() now escapes % first per GitHub Actions spec.
- describeSnapshotAge() returns 'unknown' for future dates instead of
  collapsing to 'today'.
- Extract MS_PER_DAY constant.

Tests
- Replace 13 inline `as unknown as typeof fetch` casts with a single
  mockFetch() helper.
- Track temp dirs in a Set, clean in afterEach so failed assertions no
  longer leak directories (was 9 inline rmSync calls).
- Add reasonCode assertions across stale/failed cases.
- Add network-error retry test, multi-department grouping test,
  capitalize multi-word test, describeSnapshotAge edge cases (today,
  1 day, n days, NaN, future).
- Add schema and network stale-annotation tests.
- Strengthen 403 test to check status, reason, and reasonCode.
- Fix freshOutcome() helper to derive droppedRoles from droppedCount.
- Tighten tautological careers.spec.ts filter assertion
  (toBeLessThanOrEqual -> toBeLessThan).

Schema
- Document why jobs uses z.unknown() (per-job validation in parseRoles
  for error isolation).

Tests: 30 passing (was 20).
Amp-Thread-ID: https://ampcode.com/threads/T-019dc0d6-da09-7654-b22d-16903e2fee71
Co-authored-by: Amp <amp@ampcode.com>
2026-04-27 12:24:17 -07:00
..

@comfyorg/website

Marketing/brand website built with Astro + Vue.

Ashby careers integration

/careers and /zh-CN/careers are rendered from Ashby's public job board API at build time. Data flow:

  1. src/pages/careers.astro awaits fetchRolesForBuild() during the Astro build.
  2. src/utils/ashby.ts calls GET https://api.ashbyhq.com/posting-api/job-board/{board}?includeCompensation=false, validates the envelope and each posting with Zod (src/utils/ashby.schema.ts), and maps to the domain type in src/data/roles.ts.
  3. On any failure (network, HTTP 4xx/5xx, envelope schema drift), the fetcher falls back to the committed JSON snapshot at src/data/ashby-roles.snapshot.json.
  4. src/utils/ashby.ci.ts emits GitHub Actions annotations and a $GITHUB_STEP_SUMMARY block so stale fetches are visible on green builds.

Required environment variables

Both are build-time only. Never prefix with PUBLIC_ (Astro would inline that into the client bundle).

Name Purpose Default (when unset)
WEBSITE_ASHBY_API_KEY Ashby API key (Basic auth) Build uses the committed snapshot
WEBSITE_ASHBY_JOB_BOARD_NAME Ashby public job board slug Build uses the committed snapshot

CI wiring (manual step — required)

This repo's .github/workflows/*.yaml changes cannot be pushed by a GitHub App. A maintainer must apply the following edits once:

.github/workflows/ci-website-build.yaml — pass the env into the build step and run the unit tests before it:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: Setup frontend
        uses: ./.github/actions/setup-frontend

      - name: Run website unit tests
        run: pnpm --filter @comfyorg/website test:unit

      - name: Build website
        env:
          WEBSITE_ASHBY_API_KEY: ${{ secrets.WEBSITE_ASHBY_API_KEY }}
          WEBSITE_ASHBY_JOB_BOARD_NAME: ${{ vars.WEBSITE_ASHBY_JOB_BOARD_NAME || 'comfy-org' }}
        run: pnpm --filter @comfyorg/website build

      - name: Verify API key is not leaked into build output
        env:
          WEBSITE_ASHBY_API_KEY: ${{ secrets.WEBSITE_ASHBY_API_KEY }}
        run: |
          set +x
          if [ -z "${WEBSITE_ASHBY_API_KEY:-}" ]; then
            echo "Secret not available in this run; skipping leak check."
            exit 0
          fi
          # grep -rlF prints only file paths (never match content).
          MATCHES=$(grep -rlF --exclude-dir=node_modules --null \
            -e "$WEBSITE_ASHBY_API_KEY" apps/website/dist/ 2>/dev/null \
            | tr '\0' '\n' || true)
          if [ -n "$MATCHES" ]; then
            echo "::error title=Ashby API key leaked into build output::$MATCHES"
            exit 1
          fi

.github/workflows/ci-vercel-website-preview.yaml — add the two env vars to the top-level env: block so vercel build (both deploy-preview and deploy-production jobs) sees them:

env:
  VERCEL_ORG_ID: ${{ secrets.VERCEL_WEBSITE_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_WEBSITE_PROJECT_ID }}
  VERCEL_TOKEN: ${{ secrets.VERCEL_WEBSITE_TOKEN }}
  VERCEL_SCOPE: comfyui
  WEBSITE_ASHBY_API_KEY: ${{ secrets.WEBSITE_ASHBY_API_KEY }}
  WEBSITE_ASHBY_JOB_BOARD_NAME: ${{ vars.WEBSITE_ASHBY_JOB_BOARD_NAME || 'comfy-org' }}

The secret must also be added to the Vercel project environment (vercel env add WEBSITE_ASHBY_API_KEY … or via the Vercel UI) so that vercel build in the preview job has access to it.

Fork PRs do not exercise this path: ci-vercel-website-preview.yaml receives an empty VERCEL_TOKEN for forks and fails at vercel pull before the build runs. Fork-safe PR interactions (the preview-URL comment) are handled by pr-vercel-website-preview.yaml.

Refreshing the snapshot

When a maintainer wants to update the committed snapshot (e.g. after onboarding/offboarding roles):

WEBSITE_ASHBY_API_KEY=WEBSITE_ASHBY_JOB_BOARD_NAME=comfy-org \
  pnpm --filter @comfyorg/website ashby:refresh-snapshot
git commit apps/website/src/data/ashby-roles.snapshot.json

The script exits non-zero on any non-fresh outcome so stale/empty snapshots can't be accidentally committed.

Scripts

  • pnpm dev — Astro dev server
  • pnpm build — production build to dist/
  • pnpm typecheckastro check
  • pnpm test:unit — Vitest unit tests
  • pnpm test:e2e — Playwright E2E tests (requires pnpm build first)
  • pnpm ashby:refresh-snapshot — refresh the committed careers snapshot