diff --git a/github/action.yml b/github/action.yml index 3d983a160995..85e14f064ef0 100644 --- a/github/action.yml +++ b/github/action.yml @@ -38,6 +38,14 @@ inputs: description: "Base URL for OIDC token exchange API. Only required when running a custom GitHub App install. Defaults to https://api.opencode.ai" required: false + server_url: + description: "URL of a remote OpenCode server to connect to (e.g., https://agent.acme.com:4096). Enables remote execution on persistent servers." + required: false + + server_password: + description: "Password for the remote OpenCode server (should be passed via GitHub secrets). Required if server_url is provided and server is password protected." + required: false + runs: using: "composite" steps: @@ -77,3 +85,5 @@ runs: MENTIONS: ${{ inputs.mentions }} VARIANT: ${{ inputs.variant }} OIDC_BASE_URL: ${{ inputs.oidc_base_url }} + SERVER_URL: ${{ inputs.server_url }} + SERVER_PASSWORD: ${{ inputs.server_password }} diff --git a/github/index.ts b/github/index.ts index da310178a7dc..10a411674c5a 100644 --- a/github/index.ts +++ b/github/index.ts @@ -228,6 +228,33 @@ try { process.exit(exitCode) function createOpencode() { + const serverUrl = useEnvServerUrl() + const serverPassword = useEnvServerPassword() + + if (serverPassword && !serverUrl) { + throw new Error("server_password provided without server_url. Remote authentication requires a target host.") + } + + if (serverUrl) { + const headers: Record = {} + if (serverPassword) { + headers["Authorization"] = `Bearer ${serverPassword}` + } + + const client = createOpencodeClient({ + baseUrl: serverUrl, + headers: Object.keys(headers).length > 0 ? headers : undefined, + }) + + return { + server: { + url: serverUrl, + close: () => {}, + }, + client, + } + } + const host = "127.0.0.1" const port = 4096 const url = `http://${host}:${port}` @@ -337,6 +364,14 @@ function useEnvMock() { } } +function useEnvServerUrl() { + return process.env["SERVER_URL"] || undefined +} + +function useEnvServerPassword() { + return process.env["SERVER_PASSWORD"] || undefined +} + function useEnvGithubToken() { return process.env["TOKEN"] } diff --git a/packages/web/src/content/docs/github.mdx b/packages/web/src/content/docs/github.mdx index a31fe1e7be82..4693f28d5f4e 100644 --- a/packages/web/src/content/docs/github.mdx +++ b/packages/web/src/content/docs/github.mdx @@ -85,6 +85,8 @@ Or you can set it up manually. - `agent`: The agent to use. Must be a primary agent. Falls back to `default_agent` from config or `"build"` if not found. - `share`: Whether to share the OpenCode session. Defaults to **true** for public repositories. - `prompt`: Optional custom prompt to override the default behavior. Use this to customize how OpenCode processes requests. +- `server_url`: URL of a remote OpenCode server to connect to (e.g., `https://agent.acme.com:4096`). When provided, the GitHub Action will connect to an existing OpenCode server instead of spawning a local ephemeral instance. This enables execution on persistent servers with deep codebase context, warm caches, or specialized MCP tool configurations. **Optional**. +- `server_password`: Password for authenticating with the remote OpenCode server. Should be passed via GitHub secrets. Only used when `server_url` is also provided. **Optional**. - `token`: Optional GitHub access token for performing operations such as creating comments, committing changes, and opening pull requests. By default, OpenCode uses the installation access token from the OpenCode GitHub App, so commits, comments, and pull requests appear as coming from the app. Alternatively, you can use the GitHub Action runner's [built-in `GITHUB_TOKEN`](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token) without installing the OpenCode GitHub App. Just make sure to grant the required permissions in your workflow: @@ -101,6 +103,64 @@ Or you can set it up manually. --- +## Remote Server Connection + +By default, the GitHub Action spawns an ephemeral OpenCode instance for each run. For teams running persistent OpenCode servers, you can connect the Action to an existing server for deeper codebase context and faster execution. + +### Use Cases + +- **Persistent Context**: Run PR reviews on servers that "know" the codebase long-term +- **Hardware Offloading**: Move heavy computation from standard GitHub runners to high-performance private servers +- **Unified Agent State**: Ensure automated tasks share the same session history as your team's interactive TUI sessions + +### Configuration + +Add `server_url` and optionally `server_password` to your workflow: + +```yaml title=".github/workflows/opencode-remote.yml" +name: opencode-remote + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + +jobs: + opencode: + if: | + contains(github.event.comment.body, '/oc') || + contains(github.event.comment.body, '/opencode') + runs-on: ubuntu-latest + permissions: + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + persist-credentials: false + + - name: Run OpenCode on Remote Server + uses: anomalyco/opencode/github@latest + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + with: + model: anthropic/claude-sonnet-4-20250514 + server_url: https://agent.acme.com:4096 + server_password: ${{ secrets.OPENCODE_SERVER_PASSWORD }} +``` + +### Server Setup + +To accept connections from the GitHub Action, your remote server should: + +1. Run `opencode serve` with appropriate port and hostname +2. Set `OPENCODE_SERVER_PASSWORD` environment variable if using password protection +3. Ensure the server is accessible from GitHub Actions runners (public internet or self-hosted runners with network access) + +--- + ## Supported Events OpenCode can be triggered by the following GitHub events: