Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(ally|-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]
Expand Down
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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-<system>` 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

Expand Down
Loading