Skip to content

opencode engine: --ignore-scripts install cannot satisfy the adjacent opencode --version verify step for opencode-ai 1.15.1+ #50114

Description

@funkymonkeyjam

Summary

The compiled workflow for engine: opencode emits two adjacent steps that contradict each other. The install step skips npm lifecycle scripts; the verify step immediately requires the binary that only the skipped postinstall provides.

This is masked today by the default version pin. gh-aw pins opencode-ai@1.2.14, which predates the point at which that package's postinstall became load-bearing, so smoke-opencode passes. Any user who sets engine.version to 1.15.1 or later -- including the current latest, 1.16.2 -- gets a guaranteed hard failure before the agent container starts.

The generated steps

From .github/workflows/smoke-opencode.lock.yml on main (lines 580-584, and again at 1608-1612 for the non-rootless job):

- name: Install OpenCode
  run: npm install --ignore-scripts -g opencode-ai@1.2.14
  env:
    NPM_CONFIG_MIN_RELEASE_AGE: '3'
- name: Verify OpenCode CLI installation
  run: opencode --version

Observed failure

With engine: { id: opencode, version: "1.18.4" }:

Error: opencode-ai's postinstall script was not run.

This occurs when using --ignore-scripts during installation, or when using a
package manager like pnpm that does not run postinstall scripts by default.

To fix this, run the postinstall script manually:
  cd node_modules/opencode-ai && node postinstall.mjs

Or reinstall opencode-ai without the --ignore-scripts flag.
##[error]Process completed with exit code 1.

Reproduced on gh-aw v0.83.2 and v0.83.3, byte-identical in both. The two steps are unchanged on main as of v0.84.3.

Why the pinned version hides it

The postinstall script exists in both old and new opencode-ai; what changed is whether it is mandatory.

1.2.14 (gh-aw default) 1.15.0 1.15.1 1.16.2 (latest)
bin.opencode bin/opencode bin/opencode bin/opencode.exe bin/opencode.exe
scripts.postinstall present present present present
survives --ignore-scripts yes yes no no

At tag v1.15.1 of anomalyco/opencode, packages/opencode/script/publish.ts began generating the published bin entry as a stub whose entire body is the error above followed by exit 1:

await Bun.file(`./dist/${pkg.name}/bin/${pkg.name}.exe`).write(
  [
    `echo "Error: ${pkg.name}-ai's postinstall script was not run." >&2`,
    ...
    "exit 1",

At v1.15.0 the same script ships the real launcher instead (cp -r ./bin ./dist/${pkg.name}/bin), and that launcher resolves the native binary at runtime by walking node_modules, with the native binaries arriving as optionalDependencies -- which npm installs regardless of --ignore-scripts. So before 1.15.1 the postinstall was an optimization; from 1.15.1 it is the only thing that puts a working binary in place.

The v1.15.1 release notes do not describe this as a breaking change, which is likely why it has gone unnoticed here.

Environment

  • gh-aw v0.83.2, v0.83.3; steps confirmed unchanged at v0.84.3
  • engine: { id: opencode, version: "1.18.4" }
  • runner ubuntu-latest, workflow_dispatch
  • AWF v0.27.41 (repo main now pins v0.27.43), rootless
  • Job order observed: Install AWF binary -> Install OpenCode -> Verify OpenCode CLI installation (fails) -> all container steps skipped

Why a user cannot work around it

Root-level steps: are emitted before Install OpenCode in the agent job, so a user-supplied step cannot run the postinstall after the fact. There is no frontmatter knob to drop --ignore-scripts, and editing the compiled .lock.yml defeats the point of compiling. The only available workaround is to stay on a pre-1.15.1 opencode-ai.

Suggested fix

I want to be explicit that I am not asking for --ignore-scripts to be dropped globally. #30832 established that it is a deliberate supply-chain control -- the npm install runs on the host runner before the AWF sandbox starts, with the workflow's secrets in scope -- and PR #31379 restored it after a regression. That reasoning is sound and this report does not ask to weaken it.

Preferred: treat opencode the way crush was treated in #29735. That PR changed GetInstallationSteps to call GenerateNpmInstallSteps with runInstallScripts=true for the crush engine only, described as "mirroring how the Claude engine handles its install", and added a post-install crush --version step to force deferred binary downloads. The justification given there -- "requires post-install scripts to download native binaries. With --ignore-scripts, these scripts are skipped and the binary is never downloaded, causing the crush command to fail at runtime" -- now describes opencode-ai at 1.15.1+ exactly. opencode already has the equivalent verify step; that step is precisely what fails. The global default and every other engine stay untouched.

One nit worth not copying forward: the review on #29735 flagged the commandName / EngineConfig.Command block preceding versionStep as unreachable dead code, and it merged unfixed.

Alternative, if a narrower blast radius is preferred: keep --ignore-scripts and run only the top-level package's postinstall explicitly, so transitive dependency scripts still never execute:

- name: Install OpenCode
  run: |
    npm install --ignore-scripts -g opencode-ai@1.2.14
    node "$(npm root -g)/opencode-ai/postinstall.mjs"

This is strictly tighter than the crush precedent, since it executes exactly one known script rather than the whole tree's. It does mean the compiler carries a package-specific path.

Either way, worth adding a smoke case pinned to a 1.15.1+ opencode-ai. The current smoke-opencode pin cannot catch this class of regression.

Context

Found while running a security probe that treats the agent as hostile and tests whether the provider credential is recoverable from inside the agent container. The probe is designed so that a run which does not happen cannot be reported as a pass -- which is how this surfaced as a hard stop rather than a silent clean sweep.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions