name: CI on: push: branches: [main] pull_request: branches: [main] jobs: pr-title: name: Conventional PR title if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - name: Validate title for Release Please env: PR_TITLE: ${{ github.event.pull_request.title }} run: >- node -e 'const title = process.env.PR_TITLE ?? ""; const conventional = /^(?:feat|fix|docs|refactor|perf|test|build|ci|chore|revert)(?:\([a-z0-9._/-]+\))?!?: .+/u; if (!conventional.test(title)) { console.error("PR title must use Conventional Commits, for example: feat: add historical source import"); process.exit(1); }' typecheck: name: Typecheck runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run typecheck lint: name: Lint (oxlint) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run lint format: name: Format Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run format:check dead-code: name: Dead Code (knip) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run lint:dead duplicates: name: Duplicate Code (jscpd) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run jscpd tech-debt: name: Tech Debt Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run scan:todos test: name: Test (coverage + timing) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run test:coverage -- --reporter=verbose --reporter=junit --outputFile.junit=test-results.xml - uses: actions/upload-artifact@v4 if: always() with: name: test-results path: test-results.xml retention-days: 30 package-e2e: name: Package E2E (${{ matrix.os }}) strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-14] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run verify:package windows-curated: name: Windows Ghidra P0 and package lane runs-on: windows-latest timeout-minutes: 25 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run build - run: npm run typecheck - name: Record named Windows host capabilities run: node scripts/windows-capabilities.mjs | Out-File -Encoding utf8NoBOM windows-capabilities.json - name: Validate Windows host capability report run: >- node -e "const report = JSON.parse(require('node:fs').readFileSync('windows-capabilities.json', 'utf8')); if (report.platform !== 'win32' || report.architecture !== 'x64') throw new Error('expected a Windows x64 capability report');" - name: Run curated Windows P0 tests run: >- npx vitest run --coverage.enabled=false tests/binaryTarget.test.ts tests/windowsCapabilities.test.ts tests/ghidraWindowsFixture.test.ts tests/ghidraInstallation.test.ts tests/ghidraProvider.test.ts tests/ghidraTransport.test.ts tests/ghidraTargetSnapshot.test.ts tests/ghidraClient.test.ts tests/ghidraLauncher.test.ts tests/processOwnership.test.ts tests/realGhidraWindowsWorkflow.test.ts - run: npm run verify:package:windows - uses: actions/upload-artifact@v4 if: always() with: name: windows-capabilities path: windows-capabilities.json retention-days: 30 docs: name: Docs Generation runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .nvmrc cache: npm - run: npm ci - run: npm run docs:check