From 10b9413009829bcc42bb7cd0b7430e6f33e9c512 Mon Sep 17 00:00:00 2001 From: Matt Apperson Date: Fri, 3 Apr 2026 12:04:14 -0400 Subject: [PATCH 1/2] ci: split CI into parallel lint, typecheck, unit test, and e2e jobs The single sequential `validate` job is now four independent jobs that run in parallel, reducing PR feedback time. --- .github/workflows/ci.yaml | 46 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1a39cd4..1fe6296 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,7 +5,7 @@ on: branches: [main] jobs: - validate: + lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -21,6 +21,50 @@ jobs: - run: pnpm run lint + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - run: pnpm install --frozen-lockfile + - run: pnpm run typecheck + unit-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - run: pnpm install --frozen-lockfile + - run: pnpm run test + + e2e-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - run: pnpm run test:e2e From 36eda074cb0616122892f9e79c60c06977d94750 Mon Sep 17 00:00:00 2001 From: Matt Apperson Date: Thu, 16 Apr 2026 17:03:25 -0400 Subject: [PATCH 2/2] ci: fix lint formatting and skip e2e tests when API key is absent - Apply Biome formatter to tests/unit/conversation-state.test.ts to break long array literals across multiple lines. - Have the e2e-tests job short-circuit with a warning when OPENROUTER_API_KEY is not set instead of hard-failing, so the check passes in contexts where the secret is unavailable (e.g. forks). --- .github/workflows/ci.yaml | 7 ++++++- tests/unit/conversation-state.test.ts | 28 ++++++++++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7041f5c..de1434c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -73,4 +73,9 @@ jobs: - name: E2E tests env: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} - run: pnpm run test:e2e + run: | + if [ -z "$OPENROUTER_API_KEY" ]; then + echo "::warning::OPENROUTER_API_KEY is not set; skipping e2e tests." + exit 0 + fi + pnpm run test:e2e diff --git a/tests/unit/conversation-state.test.ts b/tests/unit/conversation-state.test.ts index f4381e1..81c60e2 100644 --- a/tests/unit/conversation-state.test.ts +++ b/tests/unit/conversation-state.test.ts @@ -597,7 +597,9 @@ describe('Conversation State Utilities', () => { it('should stringify error outputs', () => { const result = createRejectedResult('call-1', 'test_tool', 'Something went wrong'); - const formatted = unsentResultsToAPIFormat([result]); + const formatted = unsentResultsToAPIFormat([ + result, + ]); expect(formatted[0]?.output).toBe('{"error":"Something went wrong"}'); }); @@ -609,7 +611,9 @@ describe('Conversation State Utilities', () => { text: 'Hello world', }, ]; - const results = [createUnsentResult('call-1', 'test_tool', contentArray)]; + const results = [ + createUnsentResult('call-1', 'test_tool', contentArray), + ]; const formatted = unsentResultsToAPIFormat(results); @@ -625,7 +629,9 @@ describe('Conversation State Utilities', () => { imageUrl: 'data:image/png;base64,abc123', }, ]; - const results = [createUnsentResult('call-1', 'image_gen', contentArray)]; + const results = [ + createUnsentResult('call-1', 'image_gen', contentArray), + ]; const formatted = unsentResultsToAPIFormat(results); @@ -645,7 +651,9 @@ describe('Conversation State Utilities', () => { imageUrl: 'data:image/png;base64,abc123', }, ]; - const results = [createUnsentResult('call-1', 'image_gen', contentArray)]; + const results = [ + createUnsentResult('call-1', 'image_gen', contentArray), + ]; const formatted = unsentResultsToAPIFormat(results); @@ -657,7 +665,9 @@ describe('Conversation State Utilities', () => { 'item1', 'item2', ]; - const results = [createUnsentResult('call-1', 'test_tool', regularArray)]; + const results = [ + createUnsentResult('call-1', 'test_tool', regularArray), + ]; const formatted = unsentResultsToAPIFormat(results); @@ -665,7 +675,9 @@ describe('Conversation State Utilities', () => { }); it('should stringify empty arrays', () => { - const results = [createUnsentResult('call-1', 'test_tool', [])]; + const results = [ + createUnsentResult('call-1', 'test_tool', []), + ]; const formatted = unsentResultsToAPIFormat(results); @@ -679,7 +691,9 @@ describe('Conversation State Utilities', () => { data: 'test', }, ]; - const results = [createUnsentResult('call-1', 'test_tool', invalidArray)]; + const results = [ + createUnsentResult('call-1', 'test_tool', invalidArray), + ]; const formatted = unsentResultsToAPIFormat(results);