name: CI
on:
pull_request:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with:
persist-credentials: false
- uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d - name: Install Rust components
run: rustup component add rustfmt clippy
- name: Install cargo-nextest
run: cargo install cargo-nextest --locked --version 0.9.135
- name: Install Miri toolchain
run: rustup toolchain install nightly --profile minimal --component miri --component rust-src
- name: Format
run: cargo fmt --check
- name: Clippy
run: cargo clippy --all-targets --locked -- -D warnings
- name: Test
run: |
cargo nextest run --all-targets --locked --profile ci
cargo test --doc --locked
- name: Miri smoke
run: cargo +nightly miri test --locked miri_smoke
- name: Rustdoc
run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --locked
- name: mdBook
run: mdbook build
- name: CLI help smoke
run: |
cargo run --locked -- --help
cargo run --locked -- setup --help
cargo run --locked -- mcp --help
cargo run --locked -- run --help
cargo run --locked -- audit --help | tee /tmp/oy-audit-help.txt
grep -F -- "--max-chunks <N>" /tmp/oy-audit-help.txt
cargo run --locked -- audit prepare --help
cargo run --locked -- audit finalize --help
cargo run --locked -- review --help
cargo run --locked -- review prepare --help
cargo run --locked -- review finalize --help
cargo run --locked -- enhance --help
cargo run --locked -- recover --help
cargo run --locked -- doctor --help
cargo run --locked -- upgrade --help
- name: OpenCode 2 integration smoke
run: |
npm install --global @opencode-ai/cli@0.0.0-next-15353
opencode2 --version | grep -F '0.0.0-next-15353'
opencode2 run --help | grep -F -- '--agent string'
cargo build --locked
export PATH="$PWD/target/debug:$PATH"
oy setup
grep -F '"@oy-cli/opencode@0.13.3"' "$HOME/.config/opencode/opencode.json"
python3 -c 'import json, pathlib; config=json.loads(pathlib.Path.home().joinpath(".config/opencode/opencode.json").read_text()); assert "command" not in config; assert "oy-audit" not in config.get("commands", {}); assert "oy" not in config.get("mcp", {}).get("servers", {}); assert "tool_output" not in config'
# The release version does not exist in npm until the tag publishes it.
# Load this checkout directly for the pre-publish runtime smoke instead.
npm ci --prefix packages/opencode --ignore-scripts
python3 - <<'PY'
import json
from pathlib import Path
path = Path.home() / ".config/opencode/opencode.json"
config = json.loads(path.read_text())
config["plugins"] = [str(Path("packages/opencode/src/index.js").resolve())]
path.write_text(json.dumps(config, indent=2) + "\n")
PY
opencode2 service start
# The first location-scoped query boots and caches project services.
opencode2 api v2.agent.list --param "location[directory]=$PWD" > /dev/null
opencode2 api v2.agent.list --param "location[directory]=$PWD" > /tmp/opencode-agents.json
opencode2 api v2.command.list --param "location[directory]=$PWD" > /tmp/opencode-commands.json
grep -F '"id":"oy"' /tmp/opencode-agents.json
! grep -F '"id":"oy-auto"' /tmp/opencode-agents.json
grep -F '"name":"oy-audit"' /tmp/opencode-commands.json
grep -F '"agent":"oy"' /tmp/opencode-commands.json
oy doctor --check --json | tee /tmp/oy-doctor.json
python3 -c 'import json; data=json.load(open("/tmp/oy-doctor.json")); assert data["check_ok"] is True; assert "mcp_connected" not in data["runtime"]'
opencode2 service stop
- name: Documentation drift checks
run: |
! grep -R -n -E 'src/(app|chat|config|model|session|ui)\.rs' README.md CONTRIBUTING.md docs
- name: Installer smoke
run: sh scripts/test_install.sh
- name: Package crate
run: cargo package --locked
opencode-package:
name: OpenCode npm package
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/opencode
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with:
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e with:
node-version: "24"
package-manager-cache: false
- name: Check Cargo and npm version alignment
working-directory: .
run: |
python3 - <<'PY'
import json
import tomllib
from pathlib import Path
cargo = tomllib.loads(Path("Cargo.toml").read_text())["package"]["version"]
npm = json.loads(Path("packages/opencode/package.json").read_text())["version"]
assert cargo == npm, f"Cargo version {cargo} != npm version {npm}"
PY
- name: Install locked dependencies
run: npm ci --ignore-scripts
- name: Build and test
run: |
npm run build
npm test
- name: Pack publishable tarball
run: |
mkdir -p ../../.tmp/npm-package
npm pack --pack-destination ../../.tmp/npm-package
- name: Verify the packed package installs
working-directory: .
run: |
mkdir -p .tmp/npm-smoke
cd .tmp/npm-smoke
npm init --yes >/dev/null
npm install --ignore-scripts ../npm-package/*.tgz
node --input-type=module -e 'import("@oy-cli/opencode").then(({default: plugin}) => { if (plugin.id !== "oy") process.exit(1) })'
- name: Upload npm package artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with:
name: opencode-npm-package
path: .tmp/npm-package/*.tgz
if-no-files-found: error