Skip to content
Open
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
119 changes: 119 additions & 0 deletions .github/workflows/nix-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: Update Nix flake

# Checks whether flake.nix lags behind the latest GitHub release. If it does,
# prefetches the new release's per-platform SRI hashes, rewrites flake.nix,
# and opens a PR.
#
# Runs on a schedule instead of release: published because releases are created
# with GITHUB_TOKEN, which does not start new workflow runs. A daily lag-check
# is fully decoupled from how releases are created and needs no PAT.

on:
schedule:
- cron: "17 6 * * *"
workflow_dispatch:

permissions:
contents: write
pull-requests: write

concurrency:
group: nix-flake-release
cancel-in-progress: true

jobs:
update-flake:
name: Bump flake version + hashes if lagging
runs-on: ubuntu-latest
if: github.repository == 'clidey/deptrust'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Install Nix
uses: cachix/install-nix-action@v31

- name: Check for lag and rewrite flake.nix
env:
ASSET_MAP: |
x86_64-linux|linux_amd64.tar.gz
aarch64-linux|linux_arm64.tar.gz
x86_64-darwin|darwin_amd64.tar.gz
aarch64-darwin|darwin_arm64.tar.gz
run: |
set -euo pipefail
tag=$(curl -fsSL -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/latest" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])')
latest="${tag#v}"
current=$(python3 -c 'import re; s=open("flake.nix").read(); m=re.search(r"version = \"([^\"]*)\";", s); print(m.group(1))')
echo "flake.nix version: $current | latest release: $latest (tag $tag)"
if [ "$current" = "$latest" ]; then
echo "flake.nix is up to date; nothing to do."
echo "LAGGING=no" >> "$GITHUB_ENV"
exit 0
fi
echo "LAGGING=yes" >> "$GITHUB_ENV"
echo "VERSION=$latest" >> "$GITHUB_ENV"
export TAG="$tag"
python3 <<'PYEOF'
import json, os, re, subprocess, urllib.request
tag = os.environ["TAG"]
version = tag.lstrip("v")
repo = os.environ["GITHUB_REPOSITORY"]
with urllib.request.urlopen(
f"https://api.github.com/repos/{repo}/releases/latest") as r:
release = json.load(r)
names = {a["name"] for a in release["assets"]
if not a["name"].endswith(".sha256")}
asset_map = {}
for line in os.environ["ASSET_MAP"].splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
sys_, sub = line.split("|", 1)
asset_map[sys_.strip()] = sub.strip()
src = open("flake.nix").read()
src, n = re.subn(r'version = "[^"]*";', f'version = "{version}";', src, count=1)
if n != 1:
raise SystemExit('could not find version = "..." in flake.nix')
for sys_, sub in asset_map.items():
match = next((n for n in names if sub in n), None)
if not match:
raise SystemExit(f"no asset for {sys_} ({sub}) in {tag}; have: {sorted(names)}")
url = f"https://github.com/{repo}/releases/download/{tag}/{match}"
out = json.loads(subprocess.check_output(
["nix", "store", "prefetch-file", "--json", "--hash-type", "sha256", url]))
sri = out["hash"]
pat = re.compile(r'("' + re.escape(sys_) + r'" = \{[^}]*\})', re.S)
def repl(m):
b = m.group(1)
b = re.sub(r'file = "[^"]*";', f'file = "{match}";', b, count=1)
b = re.sub(r'sha256 = "[^"]*";', f'sha256 = "{sri}";', b, count=1)
return b
src, n = pat.subn(repl, src, count=1)
if n != 1:
raise SystemExit(f"could not find assets block for {sys_} in flake.nix")
open("flake.nix", "w").write(src)
print(f"bumped flake.nix to {version}: {list(asset_map)}")
PYEOF

- name: Open PR
if: env.LAGGING == 'yes'
uses: peter-evans/create-pull-request@v7
with:
commit-message: "chore(nix): bump flake to v${{ env.VERSION }}"
title: "chore(nix): bump flake to v${{ env.VERSION }}"
branch: chore/nix-flake-v${{ env.VERSION }}
base: main
body: |
Auto-generated by the `Update Nix flake` workflow (daily lag-check).
The latest GitHub release is v${{ env.VERSION }} but `flake.nix` was
pinned to an older version. This PR bumps `version` and refreshes the per-platform SRI
hashes by prefetching the new release assets.

Note: PRs opened by `GITHUB_TOKEN` do not trigger downstream workflow runs (e.g. CI),
so this PR will show no checks. The diff is a 5-line hash bump with no source changes —
safe to merge as-is.
56 changes: 56 additions & 0 deletions .github/workflows/nix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Nix flake

# Validates the flake (flake.nix). Nix is a side concern, so this job is
# path-filtered to the flake files — it fires only when they change.
#
# Three steps, in order of what they catch:
# 1. nix flake check --all-systems — every system's outputs evaluate
# 2. nix build .#default — fetchurl + autoPatchelf + install
# layout actually realises for the runner's system.
# 3. nix run .#default -- --version — the patched binary actually execs.

on:
push:
branches: [main]
paths:
- "flake.nix"
- "flake.lock"
- "**/*.nix"
- ".github/workflows/nix.yml"
pull_request:
branches: [main]
paths:
- "flake.nix"
- "flake.lock"
- "**/*.nix"
- ".github/workflows/nix.yml"

permissions:
contents: read

concurrency:
group: nix-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
check:
name: nix flake check
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v16

- name: nix flake check --all-systems
run: nix flake check --all-systems --print-build-logs

- name: nix build .#default
run: nix build .#default --print-build-logs

- name: nix run .#default -- version
run: nix run .#default -- version
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ go.work.sum
# Local sandbox/tool caches
.cache/

# Nix build results
/result
/result-*

# Editor/IDE
.idea/
.vscode/
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,45 @@ Go users can install directly:
go install github.com/clidey/deptrust/cmd/deptrust@latest
```

### Nix

The project provides optional Nix flake outputs for users who already use Nix. The flake wraps the prebuilt release binary.

```bash
# Run without installing
nix run github:clidey/deptrust

# Install into your profile
nix profile install github:clidey/deptrust
```

The flake tracks the default branch and is auto-bumped to the latest release by a
daily [workflow](.github/workflows/nix-release.yml), so `github:clidey/deptrust`
always serves the current release. (Release tags are cut before the bump lands,
so `github:clidey/deptrust/vX.Y.Z` is not a valid pin — use the
nixpkgs package or a specific commit SHA if you need reproducibility.)

### Devbox

For reproducible development environments, use Devbox:

```bash
# Install Devbox first (if not already installed)
curl -fsSL https://get.jetify.dev/devbox | bash

# Initialize the environment
devbox shell

# Build the project
devbox run build
```

Or install Devbox via Homebrew:

```bash
brew install jetify-com/devbox/devbox
```

## Agent Setup

To install deptrust and register everything the installer can configure without the guided prompts:
Expand Down
17 changes: 17 additions & 0 deletions devbox.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.12.0/.schema/devbox.schema.json",
"packages": [
"go",
"gopls"
],
"shell": {
"init_hook": [
"echo 'Welcome to the Devbox environment!'"
],
"scripts": {
"build": "go build ./cmd/deptrust",
"test": "go test ./...",
"run": "go run ./cmd/deptrust"
}
}
}
27 changes: 27 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 85 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
description = "deptrust — local package vulnerability checker and MCP server for AI agents";

inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

outputs = { self, nixpkgs }: let
version = "0.9.0";

assets = {
"x86_64-linux" = {
file = "deptrust_v${version}_linux_amd64.tar.gz";
sha256 = "sha256-qL/N68QDrzGB7wLsy0X3sk4bpgzKXexNKZQ/bEvZ0RA=";
};
"aarch64-linux" = {
file = "deptrust_v${version}_linux_arm64.tar.gz";
sha256 = "sha256-g6f5GH37PLP0A1zWb5mzlSVGk963ZkJztXkW0ZU8+iY=";
};
"x86_64-darwin" = {
file = "deptrust_v${version}_darwin_amd64.tar.gz";
sha256 = "sha256-vRch3/CkvkgKEB8sqi2WK4MlL/+ucDI9DgA9hqVi4VU=";
};
"aarch64-darwin" = {
file = "deptrust_v${version}_darwin_arm64.tar.gz";
sha256 = "sha256-yvSrmO55FFTyQAFDTyFAri4Xcp8Z/dUY7UYXtF6wjag=";
};
};

systems = builtins.attrNames assets;
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);

deptrustFor = system: let
pkgs = nixpkgs.legacyPackages.${system};
asset = assets.${system};
in pkgs.stdenv.mkDerivation {
pname = "deptrust";
inherit version;

src = pkgs.fetchurl {
url = "https://github.com/clidey/deptrust/releases/download/v${version}/${asset.file}";
sha256 = asset.sha256;
};

nativeBuildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.autoPatchelfHook ];
buildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.stdenv.cc.cc.lib ];

dontConfigure = true;
dontBuild = true;

installPhase = ''
runHook preInstall
mkdir -p $out/bin
install -Dm755 deptrust $out/bin/deptrust
runHook postInstall
'';

meta = with pkgs.lib; {
description = "Local package vulnerability checker and MCP server for AI agents";
homepage = "https://github.com/clidey/deptrust";
downloadPage = "https://github.com/clidey/deptrust/releases";
license = licenses.mit;
mainProgram = "deptrust";
platforms = systems;
sourceProvenance = [ sourceTypes.binaryNativeCode ];
};
};
in {
packages = forAllSystems (system: rec {
deptrust = deptrustFor system;
default = deptrust;
});

apps = forAllSystems (system: let
deptrustPkg = deptrustFor system;
in {
deptrust = {
type = "app";
program = "${deptrustPkg}/bin/deptrust";
};
default = {
type = "app";
program = "${deptrustPkg}/bin/deptrust";
};
});
};
}