- postinstall.mjs: fix alias package detection - migrate-legacy-plugin-entry: dedupe + regression tests - task_system: default consistency across runtime paths - task() contract: consistent tool behavior - runtime model selection, tool cap, stale-task cancellation - recovery sanitization, context-limit gating - Ralph semantic DONE hardening, Atlas fallback persistence - native-skill description/content, skill path traversal guard - publish workflow: platform awaited via reusable workflow job - release: version edits reapplied before commit/tag - JSONC plugin migration: top-level plugin key safety - cold-cache: user fallback models skip disconnected providers - docs/version/release framing updates Verified: bun test (4599 pass), tsc --noEmit clean, bun run build clean
398 lines
16 KiB
YAML
398 lines
16 KiB
YAML
name: publish
|
|
run-name: "${{ format('release {0}', inputs.version || inputs.bump) }}"
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
bump:
|
|
description: "Bump major, minor, or patch"
|
|
required: true
|
|
type: choice
|
|
default: patch
|
|
options:
|
|
- patch
|
|
- minor
|
|
- major
|
|
version:
|
|
description: "Override version (e.g., 3.0.0-beta.6). Takes precedence over bump."
|
|
required: false
|
|
type: string
|
|
skip_platform:
|
|
description: "Skip platform binary packages"
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
|
|
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
|
|
|
permissions:
|
|
contents: write
|
|
id-token: write
|
|
actions: write
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: latest
|
|
|
|
- name: Install dependencies
|
|
run: bun install
|
|
env:
|
|
BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi"
|
|
|
|
- name: Run mock-heavy tests (isolated)
|
|
run: |
|
|
# These files use mock.module() which pollutes module cache
|
|
# Run them in separate processes to prevent cross-file contamination
|
|
bun test src/plugin-handlers
|
|
bun test src/hooks/atlas
|
|
bun test src/hooks/compaction-context-injector
|
|
bun test src/features/tmux-subagent
|
|
bun test src/cli/doctor/formatter.test.ts
|
|
bun test src/cli/doctor/format-default.test.ts
|
|
bun test src/tools/call-omo-agent/sync-executor.test.ts
|
|
bun test src/tools/call-omo-agent/session-creator.test.ts
|
|
bun test src/tools/session-manager
|
|
bun test src/features/opencode-skill-loader/loader.test.ts
|
|
bun test src/hooks/anthropic-context-window-limit-recovery/recovery-hook.test.ts
|
|
bun test src/hooks/anthropic-context-window-limit-recovery/executor.test.ts
|
|
# src/shared mock-heavy files (mock.module pollutes connected-providers-cache and legacy-plugin-warning)
|
|
bun test src/shared/model-capabilities.test.ts
|
|
bun test src/shared/log-legacy-plugin-startup-warning.test.ts
|
|
bun test src/shared/model-error-classifier.test.ts
|
|
bun test src/shared/opencode-message-dir.test.ts
|
|
# session-recovery mock isolation (recover-tool-result-missing mocks ./storage)
|
|
bun test src/hooks/session-recovery/recover-tool-result-missing.test.ts
|
|
# legacy-plugin-toast mock isolation (hook.test.ts mocks ./auto-migrate)
|
|
bun test src/hooks/legacy-plugin-toast/hook.test.ts
|
|
|
|
- name: Run remaining tests
|
|
run: |
|
|
# Enumerate subdirectories/files explicitly to EXCLUDE mock-heavy files
|
|
# that were already run in isolation above.
|
|
# Excluded from src/shared: model-capabilities, log-legacy-plugin-startup-warning, model-error-classifier, opencode-message-dir
|
|
# Excluded from src/cli: doctor/formatter.test.ts, doctor/format-default.test.ts
|
|
# Excluded from src/tools: call-omo-agent/sync-executor.test.ts, call-omo-agent/session-creator.test.ts, session-manager (all)
|
|
# Excluded from src/hooks/anthropic-context-window-limit-recovery: recovery-hook.test.ts, executor.test.ts
|
|
# Build src/shared file list excluding mock-heavy files already run in isolation
|
|
SHARED_FILES=$(find src/shared -name '*.test.ts' \
|
|
! -name 'model-capabilities.test.ts' \
|
|
! -name 'log-legacy-plugin-startup-warning.test.ts' \
|
|
! -name 'model-error-classifier.test.ts' \
|
|
! -name 'opencode-message-dir.test.ts' \
|
|
| sort | tr '\n' ' ')
|
|
bun test bin script src/config src/mcp src/index.test.ts \
|
|
src/agents $SHARED_FILES \
|
|
src/cli/run src/cli/config-manager src/cli/mcp-oauth \
|
|
src/cli/index.test.ts src/cli/install.test.ts src/cli/model-fallback.test.ts \
|
|
src/cli/config-manager.test.ts \
|
|
src/cli/doctor/runner.test.ts src/cli/doctor/checks \
|
|
src/tools/ast-grep src/tools/background-task src/tools/delegate-task \
|
|
src/tools/glob src/tools/grep src/tools/interactive-bash \
|
|
src/tools/look-at src/tools/lsp \
|
|
src/tools/skill src/tools/skill-mcp src/tools/slashcommand src/tools/task \
|
|
src/tools/call-omo-agent/background-agent-executor.test.ts \
|
|
src/tools/call-omo-agent/background-executor.test.ts \
|
|
src/tools/call-omo-agent/subagent-session-creator.test.ts \
|
|
src/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.test.ts src/hooks/anthropic-context-window-limit-recovery/parser.test.ts src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.test.ts src/hooks/anthropic-context-window-limit-recovery/recovery-deduplication.test.ts src/hooks/anthropic-context-window-limit-recovery/storage.test.ts \
|
|
src/hooks/session-recovery/detect-error-type.test.ts src/hooks/session-recovery/index.test.ts src/hooks/session-recovery/recover-empty-content-message-sdk.test.ts src/hooks/session-recovery/resume.test.ts src/hooks/session-recovery/storage \
|
|
src/hooks/legacy-plugin-toast/auto-migrate.test.ts \
|
|
src/hooks/claude-code-compatibility \
|
|
src/hooks/context-injection \
|
|
src/hooks/provider-toast \
|
|
src/hooks/session-notification \
|
|
src/hooks/sisyphus \
|
|
src/hooks/todo-continuation-enforcer \
|
|
src/features/background-agent \
|
|
src/features/builtin-commands \
|
|
src/features/builtin-skills \
|
|
src/features/claude-code-session-state \
|
|
src/features/hook-message-injector \
|
|
src/features/opencode-skill-loader/config-source-discovery.test.ts \
|
|
src/features/opencode-skill-loader/merger.test.ts \
|
|
src/features/opencode-skill-loader/skill-content.test.ts \
|
|
src/features/opencode-skill-loader/blocking.test.ts \
|
|
src/features/opencode-skill-loader/async-loader.test.ts \
|
|
src/features/skill-mcp-manager
|
|
|
|
typecheck:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: latest
|
|
|
|
- name: Install dependencies
|
|
run: bun install
|
|
env:
|
|
BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi"
|
|
|
|
- name: Type check
|
|
run: bun run typecheck
|
|
|
|
publish-main:
|
|
runs-on: ubuntu-latest
|
|
needs: [test, typecheck]
|
|
if: github.repository == 'code-yeongyu/oh-my-openagent'
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
dist_tag: ${{ steps.version.outputs.dist_tag }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- run: git fetch --force --tags
|
|
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: latest
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "24"
|
|
registry-url: "https://registry.npmjs.org"
|
|
|
|
- name: Install dependencies
|
|
run: bun install
|
|
env:
|
|
BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi"
|
|
|
|
- name: Calculate version
|
|
id: version
|
|
env:
|
|
RAW_VERSION: ${{ inputs.version }}
|
|
BUMP: ${{ inputs.bump }}
|
|
run: |
|
|
VERSION="$RAW_VERSION"
|
|
if [ -z "$VERSION" ]; then
|
|
PREV=$(curl -s https://registry.npmjs.org/oh-my-opencode/latest | jq -r '.version // "0.0.0"')
|
|
BASE="${PREV%%-*}"
|
|
IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE"
|
|
case "$BUMP" in
|
|
major) VERSION="$((MAJOR+1)).0.0" ;;
|
|
minor) VERSION="${MAJOR}.$((MINOR+1)).0" ;;
|
|
*) VERSION="${MAJOR}.${MINOR}.$((PATCH+1))" ;;
|
|
esac
|
|
fi
|
|
|
|
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$ ]]; then
|
|
echo "::error::Invalid version: $VERSION"
|
|
exit 1
|
|
fi
|
|
|
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
|
|
if [[ "$VERSION" == *"-"* ]]; then
|
|
DIST_TAG=$(printf '%s' "$VERSION" | cut -d'-' -f2 | cut -d'.' -f1)
|
|
if ! [[ "$DIST_TAG" =~ ^[a-z][a-z0-9-]*$ ]]; then
|
|
echo "::error::Invalid dist_tag: $DIST_TAG"
|
|
exit 1
|
|
fi
|
|
echo "dist_tag=${DIST_TAG:-next}" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "dist_tag=" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
echo "Version: $VERSION"
|
|
|
|
- name: Check if already published
|
|
id: check
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
run: |
|
|
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/oh-my-opencode/${VERSION}")
|
|
if [ "$STATUS" = "200" ]; then
|
|
echo "skip=true" >> $GITHUB_OUTPUT
|
|
echo "✓ oh-my-opencode@${VERSION} already published"
|
|
else
|
|
echo "skip=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Update version
|
|
if: steps.check.outputs.skip != 'true'
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
run: |
|
|
jq --arg v "$VERSION" '.version = $v' package.json > tmp.json && mv tmp.json package.json
|
|
|
|
for platform in darwin-arm64 darwin-x64 darwin-x64-baseline linux-x64 linux-x64-baseline linux-arm64 linux-x64-musl linux-x64-musl-baseline linux-arm64-musl windows-x64 windows-x64-baseline; do
|
|
jq --arg v "$VERSION" '.version = $v' "packages/${platform}/package.json" > tmp.json
|
|
mv tmp.json "packages/${platform}/package.json"
|
|
done
|
|
|
|
jq --arg v "$VERSION" '.optionalDependencies = (.optionalDependencies | to_entries | map(.value = $v) | from_entries)' package.json > tmp.json && mv tmp.json package.json
|
|
|
|
- name: Build main package
|
|
if: steps.check.outputs.skip != 'true'
|
|
run: |
|
|
bun build src/index.ts --outdir dist --target bun --format esm --external @ast-grep/napi
|
|
bun build src/cli/index.ts --outdir dist/cli --target bun --format esm --external @ast-grep/napi
|
|
bunx tsc --emitDeclarationOnly
|
|
bun run build:schema
|
|
|
|
- name: Publish oh-my-opencode
|
|
if: steps.check.outputs.skip != 'true'
|
|
env:
|
|
DIST_TAG: ${{ steps.version.outputs.dist_tag }}
|
|
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
|
NPM_CONFIG_PROVENANCE: true
|
|
run: |
|
|
if [ -n "$DIST_TAG" ]; then
|
|
npm publish --access public --provenance --tag "$DIST_TAG"
|
|
else
|
|
npm publish --access public --provenance
|
|
fi
|
|
|
|
- name: Check if oh-my-openagent already published
|
|
id: check-openagent
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
run: |
|
|
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/oh-my-openagent/${VERSION}")
|
|
if [ "$STATUS" = "200" ]; then
|
|
echo "skip=true" >> $GITHUB_OUTPUT
|
|
echo "✓ oh-my-openagent@${VERSION} already published"
|
|
else
|
|
echo "skip=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Publish oh-my-openagent
|
|
if: steps.check-openagent.outputs.skip != 'true'
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
DIST_TAG: ${{ steps.version.outputs.dist_tag }}
|
|
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
|
NPM_CONFIG_PROVENANCE: true
|
|
run: |
|
|
# Update package name, version, and optionalDependencies for oh-my-openagent
|
|
jq --arg v "$VERSION" '
|
|
.name = "oh-my-openagent" |
|
|
.version = $v |
|
|
.optionalDependencies = (
|
|
.optionalDependencies | to_entries |
|
|
map(.key = (.key | sub("^oh-my-opencode-"; "oh-my-openagent-")) | .value = $v) |
|
|
from_entries
|
|
)
|
|
' package.json > tmp.json && mv tmp.json package.json
|
|
|
|
if [ -n "$DIST_TAG" ]; then
|
|
npm publish --access public --provenance --tag "$DIST_TAG"
|
|
else
|
|
npm publish --access public --provenance
|
|
fi
|
|
|
|
- name: Restore package.json
|
|
if: always() && steps.check-openagent.outputs.skip != 'true'
|
|
run: |
|
|
git checkout -- package.json
|
|
|
|
publish-platform:
|
|
needs: publish-main
|
|
if: inputs.skip_platform != true
|
|
uses: ./.github/workflows/publish-platform.yml
|
|
with:
|
|
version: ${{ needs.publish-main.outputs.version }}
|
|
dist_tag: ${{ needs.publish-main.outputs.dist_tag }}
|
|
secrets: inherit
|
|
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
needs: [publish-main, publish-platform]
|
|
if: always() && needs.publish-main.result == 'success' && (inputs.skip_platform == true || needs.publish-platform.result == 'success')
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- run: git fetch --force --tags
|
|
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: latest
|
|
|
|
- name: Install dependencies
|
|
run: bun install
|
|
env:
|
|
BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi"
|
|
|
|
- name: Generate changelog
|
|
run: |
|
|
bun run script/generate-changelog.ts > /tmp/changelog.md
|
|
cat /tmp/changelog.md
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Apply release version to source tree
|
|
env:
|
|
VERSION: ${{ needs.publish-main.outputs.version }}
|
|
run: |
|
|
jq --arg v "$VERSION" '.version = $v' package.json > tmp.json && mv tmp.json package.json
|
|
|
|
for platform in darwin-arm64 darwin-x64 darwin-x64-baseline linux-x64 linux-x64-baseline linux-arm64 linux-x64-musl linux-x64-musl-baseline linux-arm64-musl windows-x64 windows-x64-baseline; do
|
|
jq --arg v "$VERSION" '.version = $v' "packages/${platform}/package.json" > tmp.json
|
|
mv tmp.json "packages/${platform}/package.json"
|
|
done
|
|
|
|
jq --arg v "$VERSION" '.optionalDependencies = (.optionalDependencies | to_entries | map(.value = $v) | from_entries)' package.json > tmp.json && mv tmp.json package.json
|
|
|
|
- name: Commit version bump
|
|
env:
|
|
VERSION: ${{ needs.publish-main.outputs.version }}
|
|
run: |
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
git config user.name "github-actions[bot]"
|
|
git add package.json packages/*/package.json
|
|
git diff --cached --quiet || git commit -m "release: v${VERSION}"
|
|
|
|
- name: Create release tag
|
|
env:
|
|
VERSION: ${{ needs.publish-main.outputs.version }}
|
|
run: |
|
|
if git rev-parse "v${VERSION}" >/dev/null 2>&1; then
|
|
echo "::error::Tag v${VERSION} already exists"
|
|
exit 1
|
|
fi
|
|
git tag "v${VERSION}"
|
|
|
|
- name: Push release state
|
|
env:
|
|
VERSION: ${{ needs.publish-main.outputs.version }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
git push origin HEAD
|
|
git push origin "v${VERSION}"
|
|
|
|
- name: Create GitHub release
|
|
env:
|
|
VERSION: ${{ needs.publish-main.outputs.version }}
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh release view "v${VERSION}" >/dev/null 2>&1 || \
|
|
gh release create "v${VERSION}" --title "v${VERSION}" --notes-file /tmp/changelog.md
|
|
|
|
- name: Delete draft release
|
|
run: gh release delete next --yes 2>/dev/null || true
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Merge to master
|
|
continue-on-error: true
|
|
env:
|
|
VERSION: ${{ needs.publish-main.outputs.version }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
git stash --include-untracked || true
|
|
git checkout master
|
|
git reset --hard "v${VERSION}"
|
|
git push -f origin master || echo "::warning::Failed to push to master"
|