Migrate duo-cli installation in CI workloads from npm to binary
Problem
The Duo CLI (@gitlab/duo-cli) is currently installed inside CI workloads via npm, as seen in ee/app/services/ai/duo_workflows/start_workflow_service.rb:
DUO_CLI_VERSION = "8.92.0"
# In set_up_executor_commands:
cli_install_command = [
"command -v duo > /dev/null 2>&1 && ",
"echo \"duo-cli already present, skipping installation\" || ",
"{ echo \"Installing @gitlab/duo-cli@#{DUO_CLI_VERSION}...\" && ",
"npm install -g @gitlab/duo-cli@#{DUO_CLI_VERSION}; }"
].joinThis approach has several significant drawbacks:
- Node.js/npm version conflicts — the CI runner image must have a compatible Node.js and npm version. Mismatches cause hard-to-debug installation failures that are outside our control.
- npm registry dependency — a CI job failure can be caused by npm registry availability or package publishing issues, as evidenced by the existing CI check in
.gitlab/ci/setup.gitlab-ci.ymlthat verifies npm availability before every pipeline. - PATH fragility — after
npm install -g, the binary must be explicitly added to$PATHviaexport PATH="$(npm bin -g):$PATH", adding brittle shell scripting to every workload. - Blocks OpenTUI migration — the planned migration of Duo CLI's TUI from
inkto OpenTUI requires the Bun runtime, which is incompatible with npm distribution. This blocks improvements tracked in the parent epic gitlab-org&22002. - Doubles release surface — every Duo CLI release must be published and validated through both npm and binary channels, increasing maintenance overhead.
Desired Outcome
The set_up_executor_commands method in StartWorkflowService installs the duo binary by downloading a precompiled binary directly (e.g., from the GitLab package registry or a CDN), rather than using npm install -g. This mirrors how glab duo cli already works — auto-downloading the binary on first run.
Specifically:
- The
cli_install_commandinStartWorkflowService#set_up_executor_commandsis replaced with a binary download script (e.g., usingcurlorwget) that fetches the correct versioned binary for the runner's platform/architecture. - The
DUO_CLI_VERSIONconstant continues to pin the version, but now resolves to a binary artifact URL rather than an npm package version. - The CI check in
.gitlab/ci/setup.gitlab-ci.yml(verify-start-workflow-service-assets) is updated to validate binary artifact availability instead of npm registry availability. - No Node.js or npm is required in the runner environment for CLI installation.
Implementation Plan
-
Update
StartWorkflowService#set_up_executor_commandsinee/app/services/ai/duo_workflows/start_workflow_service.rb- Replace the
npm install -g @gitlab/duo-cli@#{DUO_CLI_VERSION}install command with acurl/wgetbinary download andchmod +x - Detect platform/arch in the shell script if multiple runner architectures must be supported
- Remove npm-specific PATH export and diagnostic commands; replace with binary-appropriate equivalents (e.g.,
which duo) - Use https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/blob/main/packages/cli/scripts/install_duo_cli.sh?ref_type=heads as inspiration.
- Replace the
-
Update the CI asset verification job in
.gitlab/ci/setup.gitlab-ci.yml- Replace the
npm view @gitlab/duo-cli@${DUO_CLI_VERSION} versioncheck with a check that the binary artifact URL for the pinned version is reachable (e.g.,curl --headthe download URL)
- Replace the
-
Update specs in
ee/spec/services/ai/duo_workflows/start_workflow_service_spec.rb- Update
cli_install_commandandcli_install_commandslet blocks to reflect the new binary download command - Ensure platform detection logic (if any) is covered
- Update
-
Smoke test the updated workload in a real CI environment to confirm the binary is downloaded, made executable, and
duo runexecutes successfully without Node.js present