From 0118661f74f9fc2493fd91c9bb9ce831e00eae76 Mon Sep 17 00:00:00 2001 From: Jeremie Drouet Date: Tue, 18 Aug 2026 22:57:31 +0200 Subject: [PATCH 1/5] feat: add cargo-deb package metadata Describes how to build a Debian package from the crate: payload is /usr/bin/fnm only, section utils, and the full GPL text as the copyright file. An explicit extended-description is required because cargo-deb otherwise inlines the entire README into the control file. fnm is a statically linked musl binary with TLS roots compiled in, so it has no runtime dependencies. --- Cargo.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 6fd2ebd89..e324f99dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,17 @@ license = "GPL-3.0" repository = "https://github.com/Schniz/fnm" description = "Fast and simple Node.js version manager" +[package.metadata.deb] +maintainer = "Gal Schlezinger " +section = "utils" +depends = "" +license-file = ["LICENSE", "0"] +assets = [["target/release/fnm", "usr/bin/", "755"]] +extended-description = """\ +fnm is a fast and simple Node.js version manager. It works with .node-version +and .nvmrc files, and is enabled in your shell by evaluating the output of +`fnm env`. See https://github.com/Schniz/fnm for shell setup instructions.""" + [dependencies] serde = { version = "1.0.203", features = ["derive"] } clap = { version = "4.5.4", features = ["derive", "env"] } From 57110784755bdf0e3141edb24f6ad1cb9d16ac12 Mon Sep 17 00:00:00 2001 From: Jeremie Drouet Date: Tue, 18 Aug 2026 23:01:40 +0200 Subject: [PATCH 2/5] ci: build Debian packages for amd64 and arm64 Packages the static musl binaries the linux and arm jobs already produce, so no new build or toolchain is needed. Both packages are uploaded as workflow artifacts, like the existing zip assets. --no-strip is required because this job has no aarch64 strip. cargo-deb is pinned since renovate cannot see cargo install lines in workflow YAML. --- .github/workflows/rust.yml | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index bff8a273e..ffa057178 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -334,6 +334,60 @@ jobs: name: fnm-${{ matrix.arch }} path: target/${{ env.RUST_TARGET }}/release/fnm + build_deb: + name: "Build Debian packages" + runs-on: ubuntu-latest + needs: [build_static_linux_binary, build_static_arm_binary] + steps: + - uses: actions/checkout@v4 + - uses: hecrj/setup-rust-action@v2 + with: + rust-version: ${{env.RUST_VERSION}} + - uses: Swatinem/rust-cache@v2 + with: + key: deb-packages + - name: "Download `cargo-deb` crate" + run: cargo install cargo-deb@3.7.0 --locked + - uses: actions/download-artifact@v4 + with: + name: fnm-linux + path: target/x86_64-unknown-linux-musl/release + - uses: actions/download-artifact@v4 + with: + name: fnm-arm64 + path: target/aarch64-unknown-linux-musl/release + - name: mark binaries as executable + run: | + chmod +x target/x86_64-unknown-linux-musl/release/fnm + chmod +x target/aarch64-unknown-linux-musl/release/fnm + - name: Build Debian packages + run: | + cargo deb --target x86_64-unknown-linux-musl --no-build --no-strip + cargo deb --target aarch64-unknown-linux-musl --no-build --no-strip + - name: Sanity test amd64 package + run: | + version="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')" + sudo dpkg -i "target/debian/fnm_${version}-1_amd64.deb" + test "$(fnm --version)" = "fnm $version" + - name: Verify arm64 package + run: | + version="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')" + deb="target/debian/fnm_${version}-1_arm64.deb" + dpkg-deb -I "$deb" + test "$(dpkg-deb -f "$deb" Architecture)" = "arm64" + dpkg-deb -c "$deb" | grep -q '^-rwxr-xr-x .*\./usr/bin/fnm$' + test "$(dpkg-deb -c "$deb" | grep -c './usr/bin/[^/]')" -eq 1 + dpkg-deb --fsys-tarfile "$deb" | tar -xO ./usr/bin/fnm > /tmp/fnm-arm64 + readelf -h /tmp/fnm-arm64 | grep -qi aarch64 + - uses: actions/upload-artifact@v4 + with: + name: fnm-deb-amd64 + path: target/debian/fnm_*_amd64.deb + - uses: actions/upload-artifact@v4 + with: + name: fnm-deb-arm64 + path: target/debian/fnm_*_arm64.deb + ensure_commands_markdown_is_up_to_date: runs-on: ubuntu-latest name: Ensure command docs are up-to-date From e6e066f5f723bdec73e7dbbe324798d50dbfe28e Mon Sep 17 00:00:00 2001 From: Jeremie Drouet Date: Tue, 18 Aug 2026 23:02:57 +0200 Subject: [PATCH 3/5] docs: add changeset for Debian packages --- .changeset/quiet-plums-deliver.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/quiet-plums-deliver.md diff --git a/.changeset/quiet-plums-deliver.md b/.changeset/quiet-plums-deliver.md new file mode 100644 index 000000000..8f3b65804 --- /dev/null +++ b/.changeset/quiet-plums-deliver.md @@ -0,0 +1,7 @@ +--- +"fnm": patch +--- + +Add Debian packages for amd64 and arm64 + +`.deb` packages are now built for amd64 and arm64, wrapping the same statically linked musl binary already shipped in the `fnm-linux` / `fnm-arm64` zip assets. They're a convenience download — install with `sudo dpkg -i fnm_-1_.deb` — not a package submitted to Debian's official archive. Like every other release asset, they're produced as CI artifacts and attached to releases by hand. From 076850f19d9196d027e458e692bc244d59558eba Mon Sep 17 00:00:00 2001 From: Jeremie Drouet Date: Tue, 18 Aug 2026 23:21:08 +0200 Subject: [PATCH 4/5] ci: locate Debian packages by glob instead of by constructed name cargo-deb rewrites a semver prerelease dash to a tilde for Debian version ordering, so a 1.39.0-beta.1 crate produces fnm_1.39.0~beta.1-1_amd64.deb. Reconstructing the filename from the crate version missed it and failed the verification steps on valid packages. Glob for the package instead, assert exactly one match exists, and check the name and architecture from its own control fields. --- .github/workflows/rust.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ffa057178..b12ff3d12 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -366,14 +366,16 @@ jobs: cargo deb --target aarch64-unknown-linux-musl --no-build --no-strip - name: Sanity test amd64 package run: | - version="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')" - sudo dpkg -i "target/debian/fnm_${version}-1_amd64.deb" - test "$(fnm --version)" = "fnm $version" + set -- target/debian/fnm_*_amd64.deb + test "$#" -eq 1 && test -f "$1" + sudo dpkg -i "$1" + test "$(fnm --version)" = "fnm $(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')" - name: Verify arm64 package run: | - version="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')" - deb="target/debian/fnm_${version}-1_arm64.deb" - dpkg-deb -I "$deb" + set -- target/debian/fnm_*_arm64.deb + test "$#" -eq 1 && test -f "$1" + deb="$1" + test "$(dpkg-deb -f "$deb" Package)" = "fnm" test "$(dpkg-deb -f "$deb" Architecture)" = "arm64" dpkg-deb -c "$deb" | grep -q '^-rwxr-xr-x .*\./usr/bin/fnm$' test "$(dpkg-deb -c "$deb" | grep -c './usr/bin/[^/]')" -eq 1 From 6b57fb382fded21beef51177af76103412a396ed Mon Sep 17 00:00:00 2001 From: Jeremie Drouet Date: Tue, 18 Aug 2026 23:25:14 +0200 Subject: [PATCH 5/5] ci: assert the package glob on its own line errexit is suppressed on the left of &&, so `test "$#" -eq 1 && test -f "$1"` let a multiple-match glob through: the count check failed, the file check was skipped, and the step carried on with the first match. Splitting the two assertions makes both of them fail the step. --- .github/workflows/rust.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b12ff3d12..3ec1594af 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -367,13 +367,15 @@ jobs: - name: Sanity test amd64 package run: | set -- target/debian/fnm_*_amd64.deb - test "$#" -eq 1 && test -f "$1" + test "$#" -eq 1 + test -f "$1" sudo dpkg -i "$1" test "$(fnm --version)" = "fnm $(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')" - name: Verify arm64 package run: | set -- target/debian/fnm_*_arm64.deb - test "$#" -eq 1 && test -f "$1" + test "$#" -eq 1 + test -f "$1" deb="$1" test "$(dpkg-deb -f "$deb" Package)" = "fnm" test "$(dpkg-deb -f "$deb" Architecture)" = "arm64"