From 6aad56baf30e301e3cec0169a7c15c155af8569b Mon Sep 17 00:00:00 2001 From: Svetlin Ralchev Date: Tue, 6 Oct 2026 09:32:44 +0400 Subject: [PATCH 1/2] fix: release binaries that run without Nix The release binaries were built with Nix and loaded libraries from /nix/store: libiconv on macOS, the dynamic loader on Linux. They only started on machines with those exact Nix paths. Build them with plain cargo on the GitHub runners instead: macOS for Apple Silicon and Intel, and static musl binaries for x86-64 and arm64 Linux, which run on any distribution. Name them after their target (e.g. keysafe-aarch64-apple-darwin), so `cargo binstall keysafe` downloads them. The binaries are now built on every CI run and checked to start on their own, not only when a release is cut. The README installs from crates.io and shows the crates.io badges. --- .github/workflows/ci.yml | 49 ++++++++++++++++++++++++++++++++-------- Cargo.toml | 6 +++++ README.md | 21 +++++++++++++---- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddd8042..7839a15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,15 +64,20 @@ jobs: target-branch: main build: - needs: release - if: ${{ needs.release.outputs.release_created }} + # Builds on every run, so a broken binary shows up before a release; uploads on releases. + needs: [test, release] + if: ${{ !cancelled() && needs.test.result == 'success' && needs.release.result != 'failure' }} strategy: matrix: include: - os: ubuntu-latest - nix_system: x86_64-linux + target: x86_64-unknown-linux-musl + - os: ubuntu-24.04-arm + target: aarch64-unknown-linux-musl + - os: macos-15 + target: aarch64-apple-darwin - os: macos-15 - nix_system: aarch64-darwin + target: x86_64-apple-darwin runs-on: ${{ matrix.os }} permissions: contents: write @@ -81,21 +86,45 @@ jobs: - name: Checkout uses: actions/checkout@v7 - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@v23 + # Plain cargo, not Nix: the binaries must not depend on /nix/store + - name: Install Rust + run: | + rustup show active-toolchain + rustup target add ${{ matrix.target }} + + - name: Install musl + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y musl-tools - - name: Build with Nix - run: nix build . --system ${{ matrix.nix_system }} + - name: Build + run: cargo build --release --locked --target ${{ matrix.target }} + + - name: Check the binary runs on its own + run: | + bin=target/${{ matrix.target }}/release/keysafe + file "$bin" + if [ "$RUNNER_OS" = macOS ]; then + otool -L "$bin" + ! otool -L "$bin" | grep -q /nix/store + else + file "$bin" | grep -Eq 'static(-pie)? linked' + fi + case "${{ matrix.target }}" in + x86_64-apple-darwin) ;; # cross-compiled; the arm64 runner can't always run it + *) "$bin" --version ;; + esac - name: Prepare Release Assets + if: ${{ needs.release.outputs.release_created }} run: | - mkdir -p release && install -m 0755 result/bin/keysafe release/keysafe-${{ matrix.nix_system }} + mkdir -p release && install -m 0755 target/${{ matrix.target }}/release/keysafe release/keysafe-${{ matrix.target }} - name: Upload Release Assets + if: ${{ needs.release.outputs.release_created }} uses: softprops/action-gh-release@v3 with: tag_name: ${{ needs.release.outputs.tag_name }} - files: release/keysafe-${{ matrix.nix_system }} + files: release/keysafe-${{ matrix.target }} publish: needs: [release, build] diff --git a/Cargo.toml b/Cargo.toml index fff18f9..07d9a80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,12 @@ documentation = "https://github.com/keysafe-dev/keysafe" homepage = "https://github.com/keysafe-dev/keysafe" repository = "https://github.com/keysafe-dev/keysafe" + +# `cargo binstall keysafe` downloads the binary attached to the GitHub release +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }" +pkg-fmt = "bin" + [dependencies] anyhow = "1.0.104" clap = { version = "4.6.7", features = ["derive", "env", "string"] } diff --git a/README.md b/README.md index 6120a92..13e78d8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Your 1Password secrets in every shell: cached in the system keychain, exported as environment variables or files, and added to ssh-agent. -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![CI](https://github.com/keysafe-dev/keysafe/actions/workflows/ci.yml/badge.svg)](https://github.com/keysafe-dev/keysafe/actions/workflows/ci.yml) +[![crates.io](https://img.shields.io/crates/v/keysafe.svg)](https://crates.io/crates/keysafe) [![Downloads](https://img.shields.io/crates/d/keysafe.svg)](https://crates.io/crates/keysafe) [![CI](https://github.com/keysafe-dev/keysafe/actions/workflows/ci.yml/badge.svg)](https://github.com/keysafe-dev/keysafe/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) `keysafe` is not another password manager: 1Password stays the source of truth. It reads a YAML config of profiles, fetches each secret from 1Password on first use, and caches it in the macOS Keychain or the Linux Secret Service. After that, secrets come from the keychain: your shell starts without waiting for 1Password or asking for Touch ID. @@ -21,19 +21,32 @@ Add one line to your shell's startup file and `keysafe` sets up your secrets in ## Installation +**Cargo** (builds from [crates.io](https://crates.io/crates/keysafe)): + +```bash +cargo install keysafe +``` + +**Prebuilt binary** with [cargo-binstall](https://github.com/cargo-bins/cargo-binstall), for macOS (Apple Silicon, Intel) and Linux (x86-64, arm64): + +```bash +cargo binstall keysafe +``` + **Nix:** ```bash nix profile install github:keysafe-dev/keysafe ``` -**Cargo:** +**Download:** each [release](https://github.com/keysafe-dev/keysafe/releases/latest) has a binary per platform: `keysafe-aarch64-apple-darwin`, `keysafe-x86_64-apple-darwin`, `keysafe-x86_64-unknown-linux-musl` and `keysafe-aarch64-unknown-linux-musl`. The Linux binaries are static and run on any distribution. ```bash -cargo install --git https://github.com/keysafe-dev/keysafe +curl -fsSL --create-dirs -o ~/.local/bin/keysafe https://github.com/keysafe-dev/keysafe/releases/latest/download/keysafe-aarch64-apple-darwin +chmod +x ~/.local/bin/keysafe ``` -**Prebuilt:** download `keysafe-` from the [latest release](https://github.com/keysafe-dev/keysafe/releases/latest). +Then set up your shell (see [Shell integration](#shell-integration)) and run `keysafe doctor` to check everything. ## Configuration From 3be826831139739e954f2c4743792efc3a8ac22e Mon Sep 17 00:00:00 2001 From: Svetlin Ralchev Date: Tue, 6 Oct 2026 09:36:50 +0400 Subject: [PATCH 2/2] fix(ci): accept both ways file(1) reports static binaries --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7839a15..8a26f8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: otool -L "$bin" ! otool -L "$bin" | grep -q /nix/store else - file "$bin" | grep -Eq 'static(-pie)? linked' + file "$bin" | grep -Eq 'static(ally|-pie)? linked' fi case "${{ matrix.target }}" in x86_64-apple-darwin) ;; # cross-compiled; the arm64 runner can't always run it