diff --git a/.github/workflows/pr-review-merge-scheduler.yml b/.github/workflows/pr-review-merge-scheduler.yml index ad65bf2..8278ab9 100644 --- a/.github/workflows/pr-review-merge-scheduler.yml +++ b/.github/workflows/pr-review-merge-scheduler.yml @@ -19,63 +19,35 @@ on: required: false default: true type: boolean - enable_auto_merge: - description: Enable auto-merge for current-head approved PRs + merge_mode: + description: "Merge behavior for current-head approved PRs: auto, direct, or disabled" + required: false + default: auto + type: choice + options: + - auto + - direct + - disabled + update_branches: + description: Update outdated PR branches after OpenCode approval required: false default: true type: boolean -concurrency: - group: pr-review-merge-scheduler - cancel-in-progress: false - jobs: scan-pr-queue: - runs-on: ubuntu-latest permissions: actions: write checks: read contents: write pull-requests: write - env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == true }} - MAX_PRS: ${{ inputs.max_prs || '100' }} - PROJECT_FLOW: ${{ vars.PROJECT_FLOW || 'git-flow' }} - TRIGGER_REVIEWS: ${{ github.event_name != 'workflow_dispatch' || inputs.trigger_reviews == true }} - ENABLE_AUTO_MERGE: ${{ github.event_name != 'workflow_dispatch' || inputs.enable_auto_merge == true }} - steps: - - name: Checkout trusted scheduler - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 1 - - - name: Self-test scheduler - run: python3 scripts/ci/pr_review_merge_scheduler.py --self-test - - - name: Inspect PR review and merge queue - run: | - set -euo pipefail - args=( - --repo "$GITHUB_REPOSITORY" - --base-branch "$DEFAULT_BRANCH" - --max-prs "$MAX_PRS" - --project-flow "$PROJECT_FLOW" - --review-workflow "OpenCode Review" - ) - if [ "$DRY_RUN" = "true" ]; then - args+=(--dry-run) - fi - if [ "$TRIGGER_REVIEWS" = "true" ]; then - args+=(--trigger-reviews) - else - args+=(--no-trigger-reviews) - fi - if [ "$ENABLE_AUTO_MERGE" = "true" ]; then - args+=(--enable-auto-merge) - else - args+=(--no-enable-auto-merge) - fi - python3 scripts/ci/pr_review_merge_scheduler.py "${args[@]}" + uses: ContextualWisdomLab/.github/.github/workflows/pr-review-merge-scheduler.yml@main + with: + dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == true }} + max_prs: ${{ inputs.max_prs || '100' }} + trigger_reviews: ${{ github.event_name == 'schedule' || inputs.trigger_reviews == true }} + merge_mode: ${{ inputs.merge_mode || vars.PR_MERGE_MODE || 'auto' }} + update_branches: ${{ github.event_name == 'schedule' || inputs.update_branches == true }} + project_flow: github-flow + base_branch: ${{ github.event.repository.default_branch }} + canonical_ref: main diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index 0ea8fa8..33a3890 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -110,6 +110,9 @@ jobs: # refs/pull//head has already advanced before this queued run starts. if git -C "$TRUSTED_WORKSPACE" fetch --no-tags --depth=1 origin "$PR_HEAD_SHA"; then git -C "$TRUSTED_WORKSPACE" cat-file -e "$PR_HEAD_SHA^{commit}" + if git -C "$TRUSTED_WORKSPACE" cat-file -e "$PR_HEAD_SHA:opencode.jsonc" 2>/dev/null; then + git -C "$TRUSTED_WORKSPACE" show "$PR_HEAD_SHA:opencode.jsonc" > "$TRUSTED_WORKSPACE/opencode.jsonc" + fi git -C "$TRUSTED_WORKSPACE" update-ref "refs/remotes/pull/${PR_NUMBER}/head" "$PR_HEAD_SHA" exit 0 fi @@ -118,6 +121,9 @@ jobs: fetched_head_sha="$(git -C "$TRUSTED_WORKSPACE" rev-parse "refs/remotes/pull/${PR_NUMBER}/head")" if [ "$fetched_head_sha" = "$PR_HEAD_SHA" ]; then git -C "$TRUSTED_WORKSPACE" cat-file -e "$PR_HEAD_SHA^{commit}" + if git -C "$TRUSTED_WORKSPACE" cat-file -e "$PR_HEAD_SHA:opencode.jsonc" 2>/dev/null; then + git -C "$TRUSTED_WORKSPACE" show "$PR_HEAD_SHA:opencode.jsonc" > "$TRUSTED_WORKSPACE/opencode.jsonc" + fi exit 0 fi if [ "$pr_head_fetch_attempt" -lt 6 ]; then diff --git a/opencode.jsonc b/opencode.jsonc new file mode 100644 index 0000000..06e68e7 --- /dev/null +++ b/opencode.jsonc @@ -0,0 +1,148 @@ +{ + "$schema": "https://opencode.ai/config.json", + "model": "github-models/openai/gpt-5", + "small_model": "github-models/deepseek/deepseek-v3-0324", + "enabled_providers": ["github-models"], + "lsp": true, + "mcp": { + "codegraph": { + "type": "local", + "command": ["npx", "-y", "@colbymchenry/codegraph@0.9.9", "serve", "--mcp"], + "enabled": true + }, + "deepwiki": { + "type": "remote", + "url": "https://mcp.deepwiki.com/mcp", + "enabled": true, + "timeout": 10000 + }, + "context7": { + "type": "local", + "command": ["npx", "-y", "@upstash/context7-mcp@3.1.0", "--transport", "stdio"], + "enabled": true, + "timeout": 10000, + "environment": { + "NPM_CONFIG_IGNORE_SCRIPTS": "true", + "NPM_CONFIG_LOGLEVEL": "error" + } + }, + "web_search": { + "type": "local", + "command": ["npx", "-y", "@guhcostan/web-search-mcp@1.0.5"], + "enabled": true, + "timeout": 10000, + "environment": { + "NPM_CONFIG_IGNORE_SCRIPTS": "true", + "NPM_CONFIG_LOGLEVEL": "error" + } + } + }, + "permission": { + "edit": "deny", + "bash": "allow", + "read": "allow", + "grep": "allow", + "glob": "allow", + "list": "allow", + "task": "allow", + "webfetch": "allow", + "websearch": "allow", + "lsp": "allow", + "external_directory": "allow" + }, + "agent": { + "ci-review": { + "description": "Compact read-only CI pull request reviewer", + "mode": "primary", + "prompt": "{file:./ci-review-prompt.md}", + "steps": 4, + "permission": { + "edit": "deny", + "bash": "allow", + "read": "allow", + "grep": "allow", + "glob": "allow", + "list": "allow", + "task": "allow", + "webfetch": "allow", + "websearch": "allow", + "lsp": "allow", + "external_directory": "allow" + } + }, + "ci-review-fallback": { + "description": "Expanded read-only CI pull request reviewer fallback", + "mode": "primary", + "prompt": "{file:./ci-review-prompt.md}", + "steps": 12, + "permission": { + "edit": "deny", + "bash": "allow", + "read": "allow", + "grep": "allow", + "glob": "allow", + "list": "allow", + "task": "allow", + "webfetch": "allow", + "websearch": "allow", + "lsp": "allow", + "external_directory": "allow" + } + } + }, + "provider": { + "github-models": { + "npm": "@ai-sdk/openai-compatible", + "name": "GitHub Models", + "options": { + "baseURL": "https://models.github.ai/inference", + "apiKey": "{env:STRIX_GITHUB_MODELS_TOKEN}" + }, + "models": { + "openai/gpt-5": { + "name": "OpenAI GPT-5", + "tool_call": true, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "deepseek/deepseek-r1-0528": { + "name": "DeepSeek R1 0528", + "tool_call": true, + "reasoning": true, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "deepseek/deepseek-v3-0324": { + "name": "DeepSeek V3 0324", + "tool_call": true, + "limit": { + "context": 128000, + "output": 4096 + } + }, + "openai/o3": { + "name": "OpenAI o3", + "tool_call": true, + "reasoning": true, + "limit": { + "context": 200000, + "output": 100000 + } + }, + "openai/o4-mini": { + "name": "OpenAI o4-mini", + "tool_call": true, + "reasoning": true, + "limit": { + "context": 200000, + "output": 100000 + } + } + } + } + } +}