Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 14 additions & 37 deletions .github/workflows/cli-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,37 +45,23 @@ jobs:
lint-and-test:
runs-on: ubuntu-latest

# The mock-api lives in the private devicecloud-dev/dcd repo, checked out via an
# SSH deploy key. GitHub does NOT expose secrets to pull_request workflows
# triggered from forks, so that checkout (and the integration tests that need
# it) can only run for same-repo events. Fork PRs still run lint/typecheck/build.
# This repo is PUBLIC and runs no step that reaches into the private
# devicecloud-dev/dcd repo. It used to check out that repo's mock-api over an
# SSH deploy key to run test/integration/*, which meant a private-repo
# credential lived in a public repo's secrets and the API's OpenAPI spec was
# pulled onto the runner on every same-repo PR. dcd#1036 deleted that mock-api;
# rather than re-point at it, the linkage is gone.
#
# Dependabot PRs branch from this repo (so the fork check passes) but ALSO run
# without secrets — treat them like forks and skip the private checkout, or
# the mock-api clone fails with an empty DCD_SSH_DEPLOY_KEY.
env:
HAS_PRIVATE_ACCESS: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' }}

# The consequence is deliberate: test/integration/* does NOT run here, and
# neither does the swagger contract-drift check it provided (spec drift used to
# surface as a Prism 422). Only test/unit/* runs — pure, no backend. To run the
# integration suite locally, point MOCK_API_DIR at a mock; see CLAUDE.md.
steps:
- name: Checkout CLI
uses: actions/checkout@v7
with:
path: cli

- name: Checkout dcd (mock-api)
if: env.HAS_PRIVATE_ACCESS == 'true'
uses: actions/checkout@v7
with:
repository: devicecloud-dev/dcd
path: dcd
ssh-key: ${{ secrets.DCD_SSH_DEPLOY_KEY }}
# api/swagger.json is a file, which cone-mode sparse checkout rejects
# as of git 2.51 ("is not a directory") — use non-cone patterns.
sparse-checkout-cone-mode: false
sparse-checkout: |
/mock-api/
/api/swagger.json

- name: Setup pnpm
uses: pnpm/action-setup@v6.0.10
with:
Expand All @@ -93,11 +79,6 @@ jobs:
working-directory: ./cli
run: pnpm install --frozen-lockfile

- name: Install Mock API dependencies
if: env.HAS_PRIVATE_ACCESS == 'true'
working-directory: ./dcd/mock-api
run: pnpm install --frozen-lockfile

- name: Run CLI linter
working-directory: ./cli
run: pnpm lint
Expand All @@ -106,16 +87,12 @@ jobs:
working-directory: ./cli
run: pnpm typecheck

- name: Run CLI tests
if: env.HAS_PRIVATE_ACCESS == 'true'
- name: Run CLI unit tests
working-directory: ./cli
env:
MOCK_API_DIR: ${{ github.workspace }}/dcd/mock-api
run: pnpm test
run: pnpm test:unit

- name: Skip integration tests (fork PR — no mock-api access)
if: env.HAS_PRIVATE_ACCESS != 'true'
run: echo "::notice::Integration tests skipped — the mock-api (private devicecloud-dev/dcd) is not accessible from fork PRs. Lint, typecheck, and build still ran."
- name: Note skipped integration tests
run: echo "::notice::Integration tests are not run in CI — they need a mock of the dcd API, and this public repo does not reach into the private one. Lint, typecheck, unit tests, build and audit all ran."

- name: Build CLI
working-directory: ./cli
Expand Down
8 changes: 6 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- `pnpm build:binaries` — `scripts/build-binaries.mjs` produces the bun-compiled, self-contained `dcd-<platform>-<arch>` binaries published to GitHub Releases (the install `dcd upgrade` self-updates). The platform/arch keys must stay in sync with `ASSET_BY_PLATFORM` in `src/commands/upgrade.ts`.
- `pnpm lint` — ESLint over `src/` and `test/`.
- `pnpm typecheck` — `tsc --noEmit -p tsconfig.test.json` over `src/` and `test/` (strict mode; `pnpm build` only compiles `src/`). Requires Node `>=22`.
- `pnpm test` — runs `scripts/test-runner.mjs`: builds the CLI, boots the mock API, then runs all `test/**/*.test.ts` via mocha. TypeScript is loaded by **tsx** (`.mocharc.json`'s `node-option: ["import=tsx"]`), *not* ts-node — Mocha 11 imports specs as ESM, which bypasses the `require: ts-node/register` hook. The mock API lives in the **sibling `dcd/` repo** (`../dcd/mock-api`, started via `npm run start:auth` on port 3001). Override its location with `MOCK_API_DIR=/path/to/mock-api`. The runner isolates `DCD_CONFIG_DIR` to a temp dir so tests never touch your real `dcd login` session.
- Tests split into `test/unit/*` (pure, no backend) and `test/integration/*` (drive the built CLI against the mock API). Run a single test: `pnpm mocha test/integration/cloud.integration.test.ts --timeout 60000` (picks up `.mocharc.json` which wires tsx; integration specs require the mock API already running on port 3001).
- `pnpm test` — runs `scripts/test-runner.mjs`: builds the CLI, boots the mock API if one is available, then runs mocha. TypeScript is loaded by **tsx** (`.mocharc.json`'s `node-option: ["import=tsx"]`), *not* ts-node — Mocha 11 imports specs as ESM, which bypasses the `require: ts-node/register` hook. The runner isolates `DCD_CONFIG_DIR` to a temp dir so tests never touch your real `dcd login` session.
- `pnpm test:unit` — the same runner with `--unit`: unit specs only, no mock API. **This is what CI runs.**
- Tests split into `test/unit/*` (pure, no backend) and `test/integration/*` (drive the built CLI against a Prism mock of the dcd API on port 3001).
- **There is no default mock API any more.** It used to live in the sibling private `dcd/` repo; dcd#1036 deleted it, and this repo — which is public — deliberately no longer reaches into that one (no deploy key, no `swagger.json` pull). So `pnpm test` with no `MOCK_API_DIR` set **silently degrades to the unit suite** and prints a notice. To run `test/integration/*`, stand up a Prism mock over the API's `swagger.json` and point `MOCK_API_DIR=/path/to/mock-api` at it (it needs a `start:auth` npm script serving port 3001).
- Consequence worth knowing: CI no longer catches **CLI↔swagger contract drift**, which used to surface as a Prism 422 from the integration specs. Nothing replaces that check yet.
- Run a single test: `pnpm mocha test/integration/cloud.integration.test.ts --timeout 60000` (picks up `.mocharc.json` which wires tsx; integration specs require the mock API already running on port 3001).

## Entry point

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"lint": "eslint src test --ext .ts",
"prepare": "pnpm build && husky",
"test": "node scripts/test-runner.mjs",
"test:unit": "node scripts/test-runner.mjs --unit",
"typecheck": "tsc --noEmit -p tsconfig.test.json"
},
"version": "5.3.0",
Expand Down
Loading
Loading