Skip to content

feat(cli): add DEB and RPM system packages for Linux - #54024

Open
zetneteork wants to merge 1 commit into
anomalyco:v2from
opentreecz:cli-system-packages-v2
Open

zetneteork wants to merge 1 commit into
anomalyco:v2from
opentreecz:cli-system-packages-v2

Conversation

@zetneteork

Copy link
Copy Markdown

Issue for this PR

Closes #45767
Supersedes #45766 and #53917 (both targeted v1 dev branch, now ported to v2)

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds .deb and .rpm system packages for amd64 and arm64 to the v2 publish pipeline. Users can install opencode via their OS package manager instead of curl | bash.

Packages are built using nfpm from the existing signed CLI binaries during the publish job. No new CI jobs are added — nfpm cross-builds arm64 packages without QEMU. Packages are uploaded to R2 and registered with the update service alongside existing npm, Homebrew, and AUR distributions.

The x64-baseline binary variant is used for x86_64 packages (broadest compatibility, matching the AUR PKGBUILD). Shell completions are omitted since the v2 CLI does not yet have a completion subcommand.

Files added:

  • packages/cli/packaging/nfpm.yaml — nfpm config for DEB/RPM with expand: true for env var expansion
  • packages/cli/packaging/distribution.xml — macOS productbuild descriptor (reserved for future .pkg support)
  • packages/cli/packaging/scripts/postinstall — macOS pkg postinstall (reserved)
  • packages/cli/packaging/validate.sh — CI validation script for DEB/RPM
  • packages/cli/script/publish-system-packages.ts — packaging and upload script

Files modified:

  • packages/cli/script/publish.ts — invoke publish-system-packages.ts for beta/latest releases
  • .github/workflows/publish.yml — install nfpm+rpm tools, validate packages after build
  • README.md — add DEB/RPM install commands

Design decisions

  1. No new CI jobs — packages are built inline during the existing publish job, matching the pattern used by AUR and Homebrew publishers. nfpm handles cross-arch without QEMU.

  2. x64-baseline for amd64 packages — uses the non-AVX2 build for maximum compatibility, same as the AUR PKGBUILD.

  3. recommends not depends for ripgrep — soft dependency so the package installs on systems without ripgrep in their repos. The AUR package uses depends because Arch users can always install ripgrep; DEB/RPM distros are more varied.

  4. R2 + update service — packages are uploaded to Cloudflare R2 and registered with the update API using the existing UpdateArtifact pattern, keeping them consistent with other distribution channels.

  5. No macOS .pkg yet — the distribution.xml and postinstall are included as placeholders but not wired up, since .pkg building requires a macOS runner (pkgbuild). This can be added in a follow-up PR.

  6. No shell completions — the v2 CLI does not have a completion subcommand (v1 used yargs). Completions can be added to the packages when v2 gains completion support.

How did you verify your code works?

  • Validated YAML, XML, and shell script syntax locally
  • Verified nfpm source code: expand: true required for content field env var expansion
  • Verified nfpm maps recommends to RPM RPMTAG_RECOMMENDNAME (queried via rpm --recommends)
  • Script follows the same pattern as existing publish-aur.ts and publish-homebrew.ts
  • Workflow integration uses the same artifact paths and action SHAs as existing steps
  • Binary paths match build.ts output layout (cli-linux-{target}/bin/opencode)

Screenshots / recordings

No UI changes.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Add .deb and .rpm system packages for amd64 and arm64, built and
published alongside existing release assets (npm, Homebrew, AUR).

Uses nfpm to cross-build 4 Linux packages per release from the
existing signed CLI binaries. Packages include the binary at
/usr/bin/opencode with ripgrep as a recommended dependency.

New files:
- packages/cli/packaging/nfpm.yaml: nfpm config for DEB/RPM
- packages/cli/packaging/distribution.xml: macOS productbuild
  descriptor (reserved for future macOS .pkg support)
- packages/cli/packaging/scripts/postinstall: macOS pkg postinstall
- packages/cli/packaging/validate.sh: CI validation script
- packages/cli/script/publish-system-packages.ts: packaging script

Modified files:
- packages/cli/script/publish.ts: invoke system package publisher
- .github/workflows/publish.yml: install nfpm, validate packages
- README.md: add DEB/RPM install commands

Packages are uploaded to R2 and registered with the update service.
CI validates package metadata, contents, and architecture after build.

Closes anomalyco#45767
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written, this would break every run of the publish job, so it needs fixes before it can go in. I checked the type check, ran nfpm 2.41.1 with this nfpm.yaml, and ran validate.sh on the packages it built. Package contents, ripgrep Recommends and architecture mapping look right. Blocking issues are inline: the nfpm download URL returns 404, UpdateArtifact.upload is called with the wrong shape (fails tsgo and would throw mid-release), and validation fails for every beta version.

Other points:

  • distribution.xml and scripts/postinstall aren't used anywhere. Please drop them and add them with the macOS .pkg work.
  • The README edits go beyond this feature: they move scoop/choco and rename the "YOLO" line. The README also says the packages are on the GitHub releases page, but they are only uploaded to R2 (opencode.ai/files/bin/<version>/). The dpkg -i opencode_*_amd64.deb lines also don't say where to get the file.
  • The validation step runs after publish.ts has already uploaded the packages, so it can't stop a bad upload. Validating inside publish-system-packages.ts before uploading would.
  • Both this and the desktop app's Linux packages are named opencode until #48654 lands. Please check the two can be installed together.
  • distribution: "system-packages" is new to the update service and nothing reads it yet. Please confirm the publish API accepts it.


- name: Install nfpm and rpm
run: |
curl -sfL https://github.com/goreleaser/nfpm/releases/download/v2.41.1/nfpm_2.41.1_linux_amd64.tar.gz | tar xz -C /usr/local/bin nfpm

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This URL returns 404: the release asset is nfpm_2.41.1_Linux_x86_64.tar.gz. The step has no if: and runs before ./script/publish.ts, so it would fail every publish run, npm releases included. tar -C /usr/local/bin also needs sudo on the runner. Please pin a checksum for the download too.

for (const pkg of packages) {
const filename = path.basename(pkg)
const result = await UpdateArtifact.upload({
source: pkg,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpdateArtifact.upload takes { version, files, dryRun } and returns a map of { url, sha256, size }. It has no source/key and no result.url. bun run typecheck in packages/cli fails here (TS2353). At runtime it throws on input.files.map, and by then npm, AUR and Homebrew have already published. Call it once with { version: Script.version, files: packages, dryRun } and put the returned file metadata in the artifact's metadata, like publish.ts does. That also makes the dry-run path go through the same code.

if [[ -n "$expected_version" ]]; then
local pkg_version
pkg_version=$(dpkg-deb --field "$pkg_path" Version)
[[ "$pkg_version" == "$expected_version" ]] || fail "Version mismatch: expected $expected_version, got $pkg_version"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nfpm turns semver prereleases into ~. With VERSION=2.0.27-beta.1 I got opencode_2.0.27~beta.1_amd64.deb, and this check fails with expected 2.0.27-beta.1, got 2.0.27~beta.1. The RPM check at line 129 has the same problem. Every beta release would fail here. Please normalise the expected version (- → ~) before comparing, for both deb and rpm.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant