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
104 changes: 55 additions & 49 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,64 +20,63 @@ on:
- windows-latest
- all

jobs:
test-single:
name: Integration Tests (${{ matrix.os }})
if: inputs.os != 'all'
runs-on: ${{ inputs.os }}
env:
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
# Least privilege by default; no job in this workflow writes to the repository.
permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
# actions/checkout runs `git init` before any repository config exists, which
# prints a hint about the default branch name on every job. Setting it via
# GIT_CONFIG_* silences the hint without an extra step.
GIT_CONFIG_COUNT: '1'
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: main

jobs:
# Turn the `os` input into a matrix so the test job exists exactly once.
# Previously the same job body was duplicated for the single-OS and the
# all-OS case, and both were named "Integration Tests (${{ matrix.os }})" -
# which rendered as "Integration Tests ()" for the job without a matrix.
matrix:
name: Resolve OS Matrix
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
os: ${{ steps.resolve.outputs.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: false

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install
working-directory: js

- name: Run integration tests
- name: Resolve OS list
id: resolve
env:
OS_INPUT: ${{ inputs.os }}
run: |
if [ -n "${{ inputs.test_pattern }}" ]; then
bun test "${{ inputs.test_pattern }}"
if [ "$OS_INPUT" = "all" ]; then
echo 'os=["ubuntu-latest","macos-latest","windows-latest"]' >> "$GITHUB_OUTPUT"
else
bun test ./tests/integration/*.js
printf 'os=["%s"]\n' "$OS_INPUT" >> "$GITHUB_OUTPUT"
fi
working-directory: js

- name: Test MCP CLI commands
run: |
# Test help command
bun run src/index.js mcp --help

# Test mcp add command for Playwright
bun run src/index.js mcp add playwright npx @playwright/mcp@latest

# Verify configuration was created
cat ~/.config/opencode/opencode.json || echo "Config not found"

# Test mcp list command
bun run src/index.js mcp list
working-directory: js

test-all:
test:
name: Integration Tests (${{ matrix.os }})
if: inputs.os == 'all'
needs: [matrix]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
os: ${{ fromJSON(needs.matrix.outputs.os) }}
env:
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}

defaults:
run:
# windows-latest defaults to pwsh, where the POSIX test syntax below
# is a syntax error.
shell: bash

steps:
- name: Checkout repository
uses: actions/checkout@v6
Expand All @@ -94,11 +93,16 @@ jobs:
working-directory: js

- name: Run integration tests
# TEST_PATTERN is passed through env instead of being interpolated into
# the script: a `${{ inputs.* }}` expression is substituted before the
# shell runs, so a value with shell metacharacters would be executed.
env:
TEST_PATTERN: ${{ inputs.test_pattern }}
run: |
if [ -n "${{ inputs.test_pattern }}" ]; then
bun test "${{ inputs.test_pattern }}"
if [ -n "$TEST_PATTERN" ]; then
bun test --timeout 30000 "$TEST_PATTERN"
else
bun test ./tests/integration/*.js
bun test --timeout 30000 ./tests/integration/*.js
fi
working-directory: js

Expand All @@ -110,8 +114,10 @@ jobs:
# Test mcp add command for Playwright
bun run src/index.js mcp add playwright npx @playwright/mcp@latest

# Verify configuration was created
cat ~/.config/opencode/opencode.json || echo "Config not found"
# Verify the configuration was actually created. `|| echo "not found"`
# used to swallow the failure here, so a broken `mcp add` still
# reported a green step.
cat ~/.config/opencode/opencode.json

# Test mcp list command
bun run src/index.js mcp list
Expand Down
72 changes: 61 additions & 11 deletions .github/workflows/js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,30 @@ on:
required: false
type: string

# Least privilege by default; jobs that need to write escalate individually.
permissions:
contents: read

concurrency:
group: js-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# A release runs on push to main. Cancelling it mid-publish would leave a
# version bumped and tagged but not published, so main runs always finish.
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

env:
# actions/checkout runs `git init` before any repository config exists, which
# prints a hint about the default branch name on every job. Setting it via
# GIT_CONFIG_* silences the hint without an extra step.
GIT_CONFIG_COUNT: '1'
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: main

jobs:
# Changeset check - only runs on PRs
changeset-check:
name: Check for Changesets
runs-on: ubuntu-latest
timeout-minutes: 10
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v6
Expand All @@ -62,9 +77,13 @@ jobs:
working-directory: js

- name: Check for changesets
# HEAD_REF is a branch name chosen by the PR author. Interpolating it
# into the script would let a branch name execute shell code.
env:
HEAD_REF: ${{ github.head_ref }}
run: |
# Skip changeset check for automated version PRs
if [[ "${{ github.head_ref }}" == "changeset-release/"* ]]; then
if [[ "$HEAD_REF" == "changeset-release/"* ]]; then
echo "Skipping changeset check for automated release PR"
exit 0
fi
Expand All @@ -76,10 +95,23 @@ jobs:
lint:
name: Lint and Format Check
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [changeset-check]
if: always() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success')
if: ${{ !cancelled() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success') }}
steps:
- uses: actions/checkout@v6
with:
# Needed to merge the base branch below.
fetch-depth: 0

# refs/pull/N/merge is built when the pull request is synchronized, so
# commits landing on the base branch afterwards are not checked. Merging
# them here makes the checks below validate the real merge result.
- name: Simulate fresh merge with base branch (PR only)
if: github.event_name == 'pull_request'
env:
BASE_REF: ${{ github.base_ref }}
run: bash scripts/simulate-fresh-merge.sh

- name: Setup Node.js
uses: actions/setup-node@v6
Expand All @@ -90,6 +122,9 @@ jobs:
run: npm install --legacy-peer-deps
working-directory: js

- name: Scan for committed secrets
run: npx --yes -p secretlint -p @secretlint/secretlint-rule-preset-recommend secretlint "**/*"

- name: Run ESLint
run: npm run lint
working-directory: js
Expand All @@ -108,8 +143,9 @@ jobs:
test:
name: Unit Tests (Bun on ${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 20
needs: [changeset-check]
if: always() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success')
if: ${{ !cancelled() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success') }}
strategy:
fail-fast: false
matrix:
Expand All @@ -133,8 +169,11 @@ jobs:
run: bun install
working-directory: js

# Use the package script so CI and local runs execute the same suite.
# A hardcoded four-file list silently skipped 48 of the 52 unit test
# files, making CI green while regressions went unnoticed (issue #287).
- name: Run unit tests
run: bun test ./tests/json-standard-unit.js ./tests/process-name.js ./tests/cli.ts ./tests/cli_options.ts
run: bun run test
working-directory: js

- name: Commit cached API responses
Expand Down Expand Up @@ -166,8 +205,9 @@ jobs:
verbose-integration:
name: Verbose HTTP Logging Test
runs-on: ubuntu-latest
timeout-minutes: 15
needs: [changeset-check]
if: always() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success')
if: ${{ !cancelled() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success') }}
steps:
- uses: actions/checkout@v6
with:
Expand All @@ -191,8 +231,9 @@ jobs:
package-install:
name: Clean Package Install
runs-on: ubuntu-latest
timeout-minutes: 15
needs: [changeset-check]
if: always() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success')
if: ${{ !cancelled() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success') }}
steps:
- uses: actions/checkout@v6

Expand Down Expand Up @@ -227,8 +268,9 @@ jobs:
needs: [lint, test, verbose-integration, package-install]
# Use always() to ensure this job runs even if changeset-check was skipped
# This is needed because lint/test jobs have a transitive dependency on changeset-check
if: always() && github.ref == 'refs/heads/main' && github.event_name == 'push' && needs.lint.result == 'success' && needs.test.result == 'success' && needs.verbose-integration.result == 'success' && needs.package-install.result == 'success'
if: ${{ !cancelled() && github.ref == 'refs/heads/main' && github.event_name == 'push' && needs.lint.result == 'success' && needs.test.result == 'success' && needs.verbose-integration.result == 'success' && needs.package-install.result == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 30
concurrency:
group: release-main
cancel-in-progress: false
Expand Down Expand Up @@ -293,6 +335,7 @@ jobs:
name: Instant Release
if: github.event_name == 'workflow_dispatch' && github.event.inputs.release_mode == 'instant'
runs-on: ubuntu-latest
timeout-minutes: 30
concurrency:
group: release-main
cancel-in-progress: false
Expand Down Expand Up @@ -321,7 +364,10 @@ jobs:

- name: Version packages and commit to main
id: version
run: node scripts/version-and-commit.mjs --mode instant --bump-type "${{ github.event.inputs.bump_type }}" --description "${{ github.event.inputs.description }}"
env:
BUMP_TYPE: ${{ github.event.inputs.bump_type }}
DESCRIPTION: ${{ github.event.inputs.description }}
run: node scripts/version-and-commit.mjs --mode instant

- name: Publish to npm
# Run if version was committed OR if a previous attempt already committed (for re-runs)
Expand All @@ -346,6 +392,7 @@ jobs:
name: Create Changeset PR
if: github.event_name == 'workflow_dispatch' && github.event.inputs.release_mode == 'changeset-pr'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
pull-requests: write
Expand All @@ -364,7 +411,10 @@ jobs:
working-directory: js

- name: Create changeset file
run: node scripts/create-manual-changeset.mjs --bump-type "${{ github.event.inputs.bump_type }}" --description "${{ github.event.inputs.description }}"
env:
BUMP_TYPE: ${{ github.event.inputs.bump_type }}
DESCRIPTION: ${{ github.event.inputs.description }}
run: node scripts/create-manual-changeset.mjs

- name: Format changeset with Prettier
run: |
Expand All @@ -374,7 +424,7 @@ jobs:
echo "Formatted changeset files"

- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: add changeset for manual ${{ github.event.inputs.bump_type }} release'
Expand Down
Loading