From 96d9b6930ad9ae00fa682b0104744e8c79b1b765 Mon Sep 17 00:00:00 2001 From: Gianni Stubbe Date: Thu, 23 Jul 2026 19:50:26 +0000 Subject: [PATCH] fix: Last cleanup before going live --- .github/scripts/opencode-target.mjs | 2 -- .github/scripts/opencode-target.test.mjs | 5 ++--- .github/workflows/opencode.yml | 7 +++---- .github/workflows/workflow-lint.yml | 12 +++++++++++- .opencode/agent/github-build.md | 2 +- docs/opencode-github.md | 11 +++++------ 6 files changed, 22 insertions(+), 17 deletions(-) diff --git a/.github/scripts/opencode-target.mjs b/.github/scripts/opencode-target.mjs index cf7523d..819984b 100644 --- a/.github/scripts/opencode-target.mjs +++ b/.github/scripts/opencode-target.mjs @@ -120,7 +120,6 @@ export async function inspectTarget(event, mode, options) { head_ref: "", head_sha: "", head_repo: "", - same_repo: "true", } } @@ -136,7 +135,6 @@ export async function inspectTarget(event, mode, options) { head_ref: pull.head?.ref, head_sha: pull.head?.sha, head_repo: pull.head?.repo?.full_name, - same_repo: String(sameRepo), } if (Object.values(result).some((value) => typeof value !== "string")) { throw new Error("GitHub returned incomplete pull request metadata.") diff --git a/.github/scripts/opencode-target.test.mjs b/.github/scripts/opencode-target.test.mjs index 3a12ffe..811b556 100644 --- a/.github/scripts/opencode-target.test.mjs +++ b/.github/scripts/opencode-target.test.mjs @@ -41,7 +41,7 @@ test("classifies issues, PR conversations, and PR review comments", () => { assert.deepEqual(classifyTarget({ pull_request: { number: 3 } }), { type: "pull_request", number: 3 }) }) -test("issues do not call the API", async () => { +test("captures current issue metadata", async () => { const result = await inspectTarget({ issue: { number: 4 } }, "build", { ...options, fetchImpl: response() }) assert.equal(result.target_type, "issue") assert.equal(result.target_number, "4") @@ -59,7 +59,6 @@ test("captures current pull request metadata", async () => { head_ref: "feature", head_sha: "abc123", head_repo: repository, - same_repo: "true", }) }) @@ -90,7 +89,7 @@ test("plan permits fork pull requests", async () => { ...options, fetchImpl: response({ pullValue: { ...pull, head: { ...pull.head, repo: { full_name: "fork/repo" } } } }), }) - assert.equal(result.same_repo, "false") + assert.equal(result.head_repo, "fork/repo") }) test("rejects oversized thread context before model access", async () => { diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml index 1dc41c8..7b01222 100644 --- a/.github/workflows/opencode.yml +++ b/.github/workflows/opencode.yml @@ -41,7 +41,6 @@ jobs: head_ref: ${{ steps.target.outputs.head_ref }} head_sha: ${{ steps.target.outputs.head_sha }} head_repo: ${{ steps.target.outputs.head_repo }} - same_repo: ${{ steps.target.outputs.same_repo }} steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: @@ -118,9 +117,9 @@ jobs: timeout-minutes: 45 environment: opencode-build permissions: - contents: write - issues: write - pull-requests: write + contents: read + issues: read + pull-requests: read id-token: write steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 diff --git a/.github/workflows/workflow-lint.yml b/.github/workflows/workflow-lint.yml index d4f0c06..28f74d4 100644 --- a/.github/workflows/workflow-lint.yml +++ b/.github/workflows/workflow-lint.yml @@ -60,8 +60,18 @@ jobs: run: | set -euo pipefail plan_job="$(awk '/^ plan:/{inside=1} /^ build:/{inside=0} inside' .github/workflows/opencode.yml)" + build_job="$(awk '/^ build:/{inside=1} inside' .github/workflows/opencode.yml)" grep -q 'id-token: write' <<<"$plan_job" - if grep -qE 'GITHUB_TOKEN:|USE_GITHUB_TOKEN' <<<"$plan_job"; then + grep -q 'id-token: write' <<<"$build_job" + grep -q 'contents: read' <<<"$build_job" + grep -q 'issues: read' <<<"$build_job" + grep -q 'pull-requests: read' <<<"$build_job" + if grep -qE '(contents|issues|pull-requests): write' <<<"$build_job"; then + exit 1 + fi + plan_run="$(awk '/^ - name: Run read-only OpenCode plan/{inside=1} inside {print} /run: opencode github run/{if (inside) exit}' .github/workflows/opencode.yml)" + build_run="$(awk '/^ - name: Run constrained OpenCode build/{inside=1} inside {print} /run: opencode github run/{if (inside) exit}' .github/workflows/opencode.yml)" + if grep -qE 'GITHUB_TOKEN:|USE_GITHUB_TOKEN|github\.token' <<<"$plan_run$build_run"; then exit 1 fi if grep -qE 'Configure OpenCode commit identity|github-actions\[bot\]' .github/workflows/opencode.yml; then diff --git a/.opencode/agent/github-build.md b/.opencode/agent/github-build.md index 9c86e04..b88fa2e 100644 --- a/.opencode/agent/github-build.md +++ b/.opencode/agent/github-build.md @@ -21,4 +21,4 @@ permission: "vault_pass.sh": deny --- -Implement the requested repository changes directly. Stay within the requested scope. Do not execute commands, alter agent or workflow policy, access external resources, commit, push, merge, or claim validation that was not performed. GitHub Actions will commit the resulting worktree and run repository validation for human review. +Implement the requested repository changes directly. Stay within the requested scope. Do not execute commands, alter agent or workflow policy, access external resources, commit, push, merge, or claim validation that was not performed. The native OpenCode GitHub App will publish the resulting worktree and GitHub Actions will run repository validation for human review. diff --git a/docs/opencode-github.md b/docs/opencode-github.md index 68f872e..46bd88b 100644 --- a/docs/opencode-github.md +++ b/docs/opencode-github.md @@ -11,7 +11,7 @@ OpenCode responds to explicit commands in GitHub issue comments, pull request co - The build agent statically denies edits. A trusted startup plugin enables edits only for ordinary repository files and retains canonical and symlink path checks. If the plugin or config hook fails, editing stays denied. - The build agent has no shell, network, subagent, external-directory, or merge access. It cannot edit `.github/`, `.opencode/`, `.git/`, `opencode.json`, `AGENTS.md`, `vault_pass.sh`, `.env`, or `.env.*` files. - Comment workflows start from the trusted default branch before OpenCode processes PR content. -- Authorization caps all context sent by OpenCode at ten issue/review/review-thread comments, 100 changed files, and 60,000 characters before OAuth is exposed. GitHub attachment URLs are rejected rather than downloaded. It captures current target state, `updated_at`, PR base branch, head branch, head SHA, and head repository. After environment approval, build mode rechecks the commenter's permission and fetches the target again immediately before OpenCode, rejecting any relevant change. +- Authorization bounds the repository-collected issue and pull-request context to ten issue/review/review-thread comments, 100 changed files, and 60,000 characters before OAuth is exposed. Native OpenCode may independently fetch additional GitHub context. GitHub attachment URLs are rejected rather than downloaded. Authorization captures current target state, `updated_at`, PR base branch, head branch, head SHA, and head repository. After environment approval, build mode rechecks the commenter's permission and fetches the target again immediately before OpenCode, rejecting any relevant change. - Builds may update any same-repository, non-default-branch PR head. A residual race remains: OpenCode v1.18.4 ultimately fetches and pushes by mutable branch name, so a branch can theoretically move after verification and before fetch or push. Execution is not immutable by SHA unless upstream changes this behavior; all such branches need equivalent protection where appropriate, and final human diff review remains mandatory. - OpenCode sessions are never shared. The workflow downloads OpenCode v1.18.4 and verifies its pinned SHA-256 digest. - OpenCode can create or update a branch and pull request but has no merge operation in its configured toolset. Protected branch rules provide the final merge boundary. @@ -32,22 +32,21 @@ Do not use build mode until all settings in this section are active. Create two GitHub environments: - `opencode-plan` has no required reviewer and contains only its copy of the four OAuth component secrets. -- `opencode-build` has a required reviewer, prevents self-review, disables administrator bypass, and contains a separate copy of the four OAuth component secrets. +- `opencode-build` has a required reviewer, disables administrator bypass, and contains a separate copy of the four OAuth component secrets. The installed OpenCode GitHub App must be installed only for this repository and have no branch/ruleset bypass. Both modes use the App through OIDC so App-created changes trigger normal pull-request validation workflows. The official App's installation token has App-defined permissions that cannot be reduced by per-job GitHub Actions permissions; owner-only plans remain read-only through the pinned OpenCode binary and the `github-plan` tool policy. Create a ruleset or branch protection rule for `main` with: - Pull requests required before merging. -- At least one human approving review and code owner review required. +- Code owner review enabled. The current zero-approval policy permits the repository owner to merge their own pull requests after required checks pass. - Stale approvals dismissed when new commits are pushed. -- Approval required for the latest reviewable push. - All conversations resolved before merging. -- `Ansible validation / validate` and `GitHub workflow validation / actionlint` required. +- `validate` and `actionlint` from GitHub Actions required. - Force pushes and branch deletion blocked. - Rule bypass disabled, including for administrators. -Do not enable the repository-wide "Allow GitHub Actions to create and approve pull requests" setting for OpenCode. The installed App creates pull requests instead. Required human approval, stale-review dismissal, latest-push approval, required checks, code ownership, and no bypass remain mandatory. Confirm neither workflows nor the App can satisfy required human review. +Do not enable the repository-wide "Allow GitHub Actions to create and approve pull requests" setting for OpenCode. The installed App creates pull requests instead. Required checks, code-owner settings, and no bypass remain mandatory. Confirm neither workflows nor the App can satisfy required human review when approvals are required. Issues must be enabled and limited to collaborators. After every environment, secret, Actions, and branch/ruleset control is verified, create the Actions repository variable `OPENCODE_BUILD_ENABLED` with value `true` as the final activation step. Build remains skipped while this variable is absent or has any other value.