From 5234375e156f8505efc19248cd1e297e1d03c76d Mon Sep 17 00:00:00 2001 From: Josh Palmer Date: Thu, 8 Jan 2026 15:22:48 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20codex:=20add=20linux=20builds=20?= =?UTF-8?q?(no-issue)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What: - add linux outputs and source build for summarize; plugin flakes now follow current system - expand update-tools to handle per-system assets and summarize source/pnpm hash - include linux checks in garnix and docs updates Why: - enable linux users to consume supported tools and summarize via Nix Tests: - ubs --diff - nix flake check - nix flake check --all-systems - nix build .#summarize - ./result/bin/summarize --version --- README.md | 10 +- cmd/update-tools/main.go | 186 ++++++++++++++++++++++++++++++++---- flake.nix | 84 ++++++++++------ garnix.yaml | 2 + internal/nix.go | 9 ++ nix/pkgs/bird.nix | 15 ++- nix/pkgs/camsnap.nix | 23 ++++- nix/pkgs/gogcli.nix | 23 ++++- nix/pkgs/imsg.nix | 15 ++- nix/pkgs/oracle.nix | 2 +- nix/pkgs/peekaboo.nix | 15 ++- nix/pkgs/poltergeist.nix | 15 ++- nix/pkgs/sag.nix | 19 +++- nix/pkgs/sonoscli.nix | 23 ++++- nix/pkgs/summarize.nix | 126 ++++++++++++++++++++---- tools/bird/flake.nix | 10 +- tools/camsnap/flake.nix | 10 +- tools/gogcli/flake.nix | 10 +- tools/imsg/flake.nix | 10 +- tools/oracle/flake.nix | 10 +- tools/peekaboo/flake.nix | 10 +- tools/poltergeist/flake.nix | 10 +- tools/sag/flake.nix | 10 +- tools/sonoscli/flake.nix | 10 +- tools/summarize/flake.nix | 10 +- 25 files changed, 505 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index 57d942c..d49de58 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ Nix packaging for [Peter Steinberger's](https://github.com/steipete) tools, with per-tool clawdbot plugins. Part of the [nix-clawdbot](https://github.com/clawdbot/nix-clawdbot) ecosystem. -Darwin/aarch64 only (Apple Silicon Macs). +Darwin/aarch64 plus Linux (x86_64/aarch64) for tools that ship Linux builds. +On Linux, `summarize` is built from source (Node 22 + pnpm) since upstream only ships a macOS Bun binary. ## Why this exists @@ -58,6 +59,11 @@ inputs.nix-steipete-tools.url = "github:clawdbot/nix-steipete-tools"; inputs.nix-steipete-tools.packages.aarch64-darwin.camsnap inputs.nix-steipete-tools.packages.aarch64-darwin.peekaboo # etc. + +# Linux examples: +inputs.nix-steipete-tools.packages.x86_64-linux.camsnap +inputs.nix-steipete-tools.packages.aarch64-linux.gogcli +inputs.nix-steipete-tools.packages.x86_64-linux.summarize ``` ## Skills syncing @@ -86,7 +92,7 @@ Fetches latest release versions/URLs/hashes and updates the Nix expressions. Ora |----------|----------|--------------| | **sync-skills** | Every 30 min | Pulls latest skills from clawdbot main | | **update-tools** | Every 10 min | Checks for new tool releases | -| **Garnix** | On push | Builds all packages via `checks.*` | +| **Garnix** | On push | Builds all packages via `checks.*` (darwin + linux) | Automated PRs keep everything fresh without manual intervention. diff --git a/cmd/update-tools/main.go b/cmd/update-tools/main.go index ec7ae54..e7ce9ba 100644 --- a/cmd/update-tools/main.go +++ b/cmd/update-tools/main.go @@ -12,10 +12,15 @@ import ( ) type Tool struct { - Name string - Repo string - AssetRegex *regexp.Regexp - NixFile string + Name string + Repo string + Assets []AssetSpec + NixFile string +} + +type AssetSpec struct { + System string + Regex *regexp.Regexp } func updateTool(tool Tool) error { @@ -25,31 +30,107 @@ func updateTool(tool Tool) error { return err } version := strings.TrimPrefix(rel.TagName, "v") + if err := internal.ReplaceOnce(tool.NixFile, regexp.MustCompile(`version = "[^"]+";`), fmt.Sprintf(`version = "%s";`, version)); err != nil { + return err + } + + for _, asset := range tool.Assets { + var assetURL string + for _, a := range rel.Assets { + if asset.Regex.MatchString(a.Name) { + assetURL = a.BrowserDownloadURL + break + } + } + if assetURL == "" { + return fmt.Errorf("no asset matched for %s (%s)", tool.Name, asset.System) + } + hash, err := internal.PrefetchHash(assetURL) + if err != nil { + return err + } + if err := updateSourceBlock(tool.NixFile, asset.System, assetURL, hash); err != nil { + return err + } + } + + return nil +} + +func updateSourceBlock(path, system, url, hash string) error { + blockRe := regexp.MustCompile(fmt.Sprintf(`(?s)"%s" = \{.*?\};`, regexp.QuoteMeta(system))) + return internal.ReplaceOnceFunc(path, blockRe, func(s string) string { + out := regexp.MustCompile(`url = "[^"]+";`).ReplaceAllString(s, fmt.Sprintf(`url = "%s";`, url)) + out = regexp.MustCompile(`hash = "sha256-[^"]+";`).ReplaceAllString(out, fmt.Sprintf(`hash = "%s";`, hash)) + return out + }) +} + +func updateSummarize(repoRoot string) error { + log.Printf("[update-tools] summarize") + summarizeFile := filepath.Join(repoRoot, "nix", "pkgs", "summarize.nix") + orig, err := os.ReadFile(summarizeFile) + if err != nil { + return err + } + + rel, err := internal.LatestRelease("steipete/summarize") + if err != nil { + return err + } + version := strings.TrimPrefix(rel.TagName, "v") var assetURL string for _, a := range rel.Assets { - if tool.AssetRegex.MatchString(a.Name) { + if matched, _ := regexp.MatchString(`summarize-macos-arm64-v[0-9.]+\.tar\.gz`, a.Name); matched { assetURL = a.BrowserDownloadURL break } } if assetURL == "" { - return fmt.Errorf("no asset matched for %s", tool.Name) + return fmt.Errorf("no asset matched for summarize") } - hash, err := internal.PrefetchHash(assetURL) + assetHash, err := internal.PrefetchHash(assetURL) + if err != nil { + return err + } + srcURL := fmt.Sprintf("https://github.com/steipete/summarize/archive/refs/tags/v%s.tar.gz", version) + srcHash, err := internal.PrefetchHash(srcURL) if err != nil { return err } - if err := internal.ReplaceOnce(tool.NixFile, regexp.MustCompile(`version = "[^"]+";`), fmt.Sprintf(`version = "%s";`, version)); err != nil { + if err := internal.ReplaceOnce(summarizeFile, regexp.MustCompile(`version = "[^"]+";`), fmt.Sprintf(`version = "%s";`, version)); err != nil { return err } - if err := internal.ReplaceOnce(tool.NixFile, regexp.MustCompile(`url = "[^"]+";`), fmt.Sprintf(`url = "%s";`, assetURL)); err != nil { + if err := updateSourceBlock(summarizeFile, "aarch64-darwin", assetURL, assetHash); err != nil { return err } - if err := internal.ReplaceOnce(tool.NixFile, regexp.MustCompile(`hash = "sha256-[^"]+";`), fmt.Sprintf(`hash = "%s";`, hash)); err != nil { + srcRe := regexp.MustCompile(`(?s)src = fetchurl \{[^}]*hash = "sha256-[^"]+";`) + if err := internal.ReplaceOnceFunc(summarizeFile, srcRe, func(s string) string { + return regexp.MustCompile(`hash = "sha256-[^"]+";`).ReplaceAllString(s, fmt.Sprintf(`hash = "%s";`, srcHash)) + }); err != nil { + return err + } + pnpmRe := regexp.MustCompile(`(?s)pnpmDeps.*hash = "sha256-[^"]+";`) + if err := internal.ReplaceOnceFunc(summarizeFile, pnpmRe, func(s string) string { + return regexp.MustCompile(`hash = "sha256-[^"]+";`).ReplaceAllString(s, `hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";`) + }); err != nil { return err } + log.Printf("[update-tools] summarize: deriving pnpm hash") + logText, buildErr := internal.NixBuildSummarize() + pnpmHash := internal.ExtractGotHash(logText) + if pnpmHash == "" { + _ = os.WriteFile(summarizeFile, orig, 0644) + log.Printf("[update-tools] summarize pnpm hash not found; restored original file (build err: %v)", buildErr) + return nil + } + if err := internal.ReplaceOnceFunc(summarizeFile, pnpmRe, func(s string) string { + return regexp.MustCompile(`hash = "sha256-[^"]+";`).ReplaceAllString(s, fmt.Sprintf(`hash = "%s";`, pnpmHash)) + }); err != nil { + return err + } return nil } @@ -95,7 +176,7 @@ func updateOracle(repoRoot string) error { if err := internal.ReplaceOnce(oracleFile, regexp.MustCompile(`hash = "sha256-[^"]+";`), fmt.Sprintf(`hash = "%s";`, assetHash)); err != nil { return err } - lockRe := regexp.MustCompile(`(?s)lockSrc = fetchFromGitHub \{[^}]*hash = "sha256-[^"]+";`) + lockRe := regexp.MustCompile(`(?s)lockSrc = fetchFromGitHub \{[^}]*hash = "sha256-[^"]+";`) if err := internal.ReplaceOnceFunc(oracleFile, lockRe, func(s string) string { return regexp.MustCompile(`hash = "sha256-[^"]+";`).ReplaceAllString(s, fmt.Sprintf(`hash = "%s";`, lockHash)) }); err != nil { @@ -131,17 +212,82 @@ func main() { } tools := []Tool{ - {"summarize", "steipete/summarize", regexp.MustCompile(`summarize-macos-arm64-v[0-9.]+\.tar\.gz`), filepath.Join(repoRoot, "nix", "pkgs", "summarize.nix")}, - {"gogcli", "steipete/gogcli", regexp.MustCompile(`gogcli_[0-9.]+_darwin_arm64\.tar\.gz`), filepath.Join(repoRoot, "nix", "pkgs", "gogcli.nix")}, - {"camsnap", "steipete/camsnap", regexp.MustCompile(`camsnap-macos-arm64\.tar\.gz`), filepath.Join(repoRoot, "nix", "pkgs", "camsnap.nix")}, - {"sonoscli", "steipete/sonoscli", regexp.MustCompile(`sonoscli-macos-arm64\.tar\.gz`), filepath.Join(repoRoot, "nix", "pkgs", "sonoscli.nix")}, - {"bird", "steipete/bird", regexp.MustCompile(`bird-macos-universal-v[0-9.]+\.tar\.gz`), filepath.Join(repoRoot, "nix", "pkgs", "bird.nix")}, - {"peekaboo", "steipete/peekaboo", regexp.MustCompile(`peekaboo-macos-universal\.tar\.gz`), filepath.Join(repoRoot, "nix", "pkgs", "peekaboo.nix")}, - {"poltergeist", "steipete/poltergeist", regexp.MustCompile(`poltergeist-macos-universal-v[0-9.]+\.tar\.gz`), filepath.Join(repoRoot, "nix", "pkgs", "poltergeist.nix")}, - {"sag", "steipete/sag", regexp.MustCompile(`sag_[0-9.]+_darwin_universal\.tar\.gz`), filepath.Join(repoRoot, "nix", "pkgs", "sag.nix")}, - {"imsg", "steipete/imsg", regexp.MustCompile(`imsg-macos\.zip`), filepath.Join(repoRoot, "nix", "pkgs", "imsg.nix")}, + { + Name: "gogcli", + Repo: "steipete/gogcli", + Assets: []AssetSpec{ + {System: "aarch64-darwin", Regex: regexp.MustCompile(`gogcli_[0-9.]+_darwin_arm64\.tar\.gz`)}, + {System: "x86_64-linux", Regex: regexp.MustCompile(`gogcli_[0-9.]+_linux_amd64\.tar\.gz`)}, + {System: "aarch64-linux", Regex: regexp.MustCompile(`gogcli_[0-9.]+_linux_arm64\.tar\.gz`)}, + }, + NixFile: filepath.Join(repoRoot, "nix", "pkgs", "gogcli.nix"), + }, + { + Name: "camsnap", + Repo: "steipete/camsnap", + Assets: []AssetSpec{ + {System: "aarch64-darwin", Regex: regexp.MustCompile(`camsnap-macos-arm64\.tar\.gz`)}, + {System: "x86_64-linux", Regex: regexp.MustCompile(`camsnap_[0-9.]+_linux_amd64\.tar\.gz`)}, + {System: "aarch64-linux", Regex: regexp.MustCompile(`camsnap_[0-9.]+_linux_arm64\.tar\.gz`)}, + }, + NixFile: filepath.Join(repoRoot, "nix", "pkgs", "camsnap.nix"), + }, + { + Name: "sonoscli", + Repo: "steipete/sonoscli", + Assets: []AssetSpec{ + {System: "aarch64-darwin", Regex: regexp.MustCompile(`sonoscli-macos-arm64\.tar\.gz`)}, + {System: "x86_64-linux", Regex: regexp.MustCompile(`sonoscli_[0-9.]+_linux_amd64\.tar\.gz`)}, + {System: "aarch64-linux", Regex: regexp.MustCompile(`sonoscli_[0-9.]+_linux_arm64\.tar\.gz`)}, + }, + NixFile: filepath.Join(repoRoot, "nix", "pkgs", "sonoscli.nix"), + }, + { + Name: "bird", + Repo: "steipete/bird", + Assets: []AssetSpec{ + {System: "aarch64-darwin", Regex: regexp.MustCompile(`bird-macos-universal-v[0-9.]+\.tar\.gz`)}, + }, + NixFile: filepath.Join(repoRoot, "nix", "pkgs", "bird.nix"), + }, + { + Name: "peekaboo", + Repo: "steipete/peekaboo", + Assets: []AssetSpec{ + {System: "aarch64-darwin", Regex: regexp.MustCompile(`peekaboo-macos-universal\.tar\.gz`)}, + }, + NixFile: filepath.Join(repoRoot, "nix", "pkgs", "peekaboo.nix"), + }, + { + Name: "poltergeist", + Repo: "steipete/poltergeist", + Assets: []AssetSpec{ + {System: "aarch64-darwin", Regex: regexp.MustCompile(`poltergeist-macos-universal-v[0-9.]+\.tar\.gz`)}, + }, + NixFile: filepath.Join(repoRoot, "nix", "pkgs", "poltergeist.nix"), + }, + { + Name: "sag", + Repo: "steipete/sag", + Assets: []AssetSpec{ + {System: "aarch64-darwin", Regex: regexp.MustCompile(`sag_[0-9.]+_darwin_universal\.tar\.gz`)}, + {System: "x86_64-linux", Regex: regexp.MustCompile(`sag_[0-9.]+_linux_amd64\.tar\.gz`)}, + }, + NixFile: filepath.Join(repoRoot, "nix", "pkgs", "sag.nix"), + }, + { + Name: "imsg", + Repo: "steipete/imsg", + Assets: []AssetSpec{ + {System: "aarch64-darwin", Regex: regexp.MustCompile(`imsg-macos\.zip`)}, + }, + NixFile: filepath.Join(repoRoot, "nix", "pkgs", "imsg.nix"), + }, } + if err := updateSummarize(repoRoot); err != nil { + log.Fatalf("update summarize failed: %v", err) + } for _, tool := range tools { if err := updateTool(tool); err != nil { log.Fatalf("update %s failed: %v", tool.Name, err) diff --git a/flake.nix b/flake.nix index 5a232e5..007bf15 100644 --- a/flake.nix +++ b/flake.nix @@ -7,40 +7,66 @@ outputs = { self, nixpkgs }: let - systems = [ "aarch64-darwin" ]; - forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); + lib = nixpkgs.lib; + systems = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ]; + forAllSystems = f: lib.genAttrs systems (system: f system); + packageSystems = { + summarize = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ]; + gogcli = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ]; + camsnap = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ]; + sonoscli = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ]; + bird = [ "aarch64-darwin" ]; + peekaboo = [ "aarch64-darwin" ]; + poltergeist = [ "aarch64-darwin" ]; + sag = [ "aarch64-darwin" "x86_64-linux" ]; + imsg = [ "aarch64-darwin" ]; + oracle = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ]; + }; in { packages = forAllSystems (system: let pkgs = import nixpkgs { inherit system; }; - in { - summarize = pkgs.callPackage ./nix/pkgs/summarize.nix {}; - gogcli = pkgs.callPackage ./nix/pkgs/gogcli.nix {}; - camsnap = pkgs.callPackage ./nix/pkgs/camsnap.nix {}; - sonoscli = pkgs.callPackage ./nix/pkgs/sonoscli.nix {}; - bird = pkgs.callPackage ./nix/pkgs/bird.nix {}; - peekaboo = pkgs.callPackage ./nix/pkgs/peekaboo.nix {}; - poltergeist = pkgs.callPackage ./nix/pkgs/poltergeist.nix {}; - sag = pkgs.callPackage ./nix/pkgs/sag.nix {}; - imsg = pkgs.callPackage ./nix/pkgs/imsg.nix {}; - oracle = pkgs.callPackage ./nix/pkgs/oracle.nix { - pkgs = pkgs; - pnpm = if pkgs ? pnpm_10 then pkgs.pnpm_10 else pkgs.pnpm; - }; - } + supports = name: lib.elem system packageSystems.${name}; + in + (lib.optionalAttrs (supports "summarize") { + summarize = pkgs.callPackage ./nix/pkgs/summarize.nix { + pkgs = pkgs; + pnpm = if pkgs ? pnpm_10 then pkgs.pnpm_10 else pkgs.pnpm; + nodejs = if pkgs ? nodejs_22 then pkgs.nodejs_22 else pkgs.nodejs; + }; + }) + // (lib.optionalAttrs (supports "gogcli") { + gogcli = pkgs.callPackage ./nix/pkgs/gogcli.nix {}; + }) + // (lib.optionalAttrs (supports "camsnap") { + camsnap = pkgs.callPackage ./nix/pkgs/camsnap.nix {}; + }) + // (lib.optionalAttrs (supports "sonoscli") { + sonoscli = pkgs.callPackage ./nix/pkgs/sonoscli.nix {}; + }) + // (lib.optionalAttrs (supports "bird") { + bird = pkgs.callPackage ./nix/pkgs/bird.nix {}; + }) + // (lib.optionalAttrs (supports "peekaboo") { + peekaboo = pkgs.callPackage ./nix/pkgs/peekaboo.nix {}; + }) + // (lib.optionalAttrs (supports "poltergeist") { + poltergeist = pkgs.callPackage ./nix/pkgs/poltergeist.nix {}; + }) + // (lib.optionalAttrs (supports "sag") { + sag = pkgs.callPackage ./nix/pkgs/sag.nix {}; + }) + // (lib.optionalAttrs (supports "imsg") { + imsg = pkgs.callPackage ./nix/pkgs/imsg.nix {}; + }) + // (lib.optionalAttrs (supports "oracle") { + oracle = pkgs.callPackage ./nix/pkgs/oracle.nix { + pkgs = pkgs; + pnpm = if pkgs ? pnpm_10 then pkgs.pnpm_10 else pkgs.pnpm; + }; + }) ); - checks = forAllSystems (system: { - summarize = self.packages.${system}.summarize; - gogcli = self.packages.${system}.gogcli; - camsnap = self.packages.${system}.camsnap; - sonoscli = self.packages.${system}.sonoscli; - bird = self.packages.${system}.bird; - peekaboo = self.packages.${system}.peekaboo; - poltergeist = self.packages.${system}.poltergeist; - sag = self.packages.${system}.sag; - imsg = self.packages.${system}.imsg; - oracle = self.packages.${system}.oracle; - }); + checks = forAllSystems (system: self.packages.${system}); }; } diff --git a/garnix.yaml b/garnix.yaml index 4773751..4cf51d6 100644 --- a/garnix.yaml +++ b/garnix.yaml @@ -1,3 +1,5 @@ builds: include: - "checks.aarch64-darwin.*" + - "checks.x86_64-linux.*" + - "checks.aarch64-linux.*" diff --git a/internal/nix.go b/internal/nix.go index 59d21e6..1ff9ff3 100644 --- a/internal/nix.go +++ b/internal/nix.go @@ -39,6 +39,15 @@ func NixBuildOracle() (string, error) { return out.String(), err } +func NixBuildSummarize() (string, error) { + cmd := exec.Command("nix", "build", ".#summarize") + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &out + err := cmd.Run() + return out.String(), err +} + func ExtractGotHash(log string) string { for _, line := range strings.Split(log, "\n") { if idx := strings.Index(line, "got: sha256-"); idx != -1 { diff --git a/nix/pkgs/bird.nix b/nix/pkgs/bird.nix index 09c4a31..6c952d3 100644 --- a/nix/pkgs/bird.nix +++ b/nix/pkgs/bird.nix @@ -1,13 +1,18 @@ { lib, stdenv, fetchurl }: +let + sources = { + "aarch64-darwin" = { + url = "https://github.com/steipete/bird/releases/download/v0.6.0/bird-macos-universal-v0.6.0.tar.gz"; + hash = "sha256-HdvyNBkq96RUr7vNtDHvqbKv57w/SVLV3AWiaiMUOdo="; + }; + }; +in stdenv.mkDerivation { pname = "bird"; version = "0.6.0"; - src = fetchurl { - url = "https://github.com/steipete/bird/releases/download/v0.6.0/bird-macos-universal-v0.6.0.tar.gz"; - hash = "sha256-HdvyNBkq96RUr7vNtDHvqbKv57w/SVLV3AWiaiMUOdo="; - }; + src = fetchurl sources.${stdenv.hostPlatform.system}; dontConfigure = true; dontBuild = true; @@ -28,7 +33,7 @@ stdenv.mkDerivation { description = "Fast X CLI for tweeting, replying, and reading"; homepage = "https://github.com/steipete/bird"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = builtins.attrNames sources; mainProgram = "bird"; }; } diff --git a/nix/pkgs/camsnap.nix b/nix/pkgs/camsnap.nix index d51b65b..df8da94 100644 --- a/nix/pkgs/camsnap.nix +++ b/nix/pkgs/camsnap.nix @@ -1,13 +1,26 @@ { lib, stdenv, fetchurl, ffmpeg }: +let + sources = { + "aarch64-darwin" = { + url = "https://github.com/steipete/camsnap/releases/download/v0.2.0/camsnap-macos-arm64.tar.gz"; + hash = "sha256-YSgnkN9HuSPbYC0ioR95blkUfcHEye6aQSW7lqKzgz4="; + }; + "x86_64-linux" = { + url = "https://github.com/steipete/camsnap/releases/download/v0.2.0/camsnap_0.2.0_linux_amd64.tar.gz"; + hash = "sha256-3UCuaKFaf9i3ohEBjzKkyiY7Aw4GlG5Bj+58gmdTMds="; + }; + "aarch64-linux" = { + url = "https://github.com/steipete/camsnap/releases/download/v0.2.0/camsnap_0.2.0_linux_arm64.tar.gz"; + hash = "sha256-hOcMkScldSUwj23to75jSyVr6gqojx6o0cJeN8j0ALA="; + }; + }; +in stdenv.mkDerivation { pname = "camsnap"; version = "0.2.0"; - src = fetchurl { - url = "https://github.com/steipete/camsnap/releases/download/v0.2.0/camsnap-macos-arm64.tar.gz"; - hash = "sha256-YSgnkN9HuSPbYC0ioR95blkUfcHEye6aQSW7lqKzgz4="; - }; + src = fetchurl sources.${stdenv.hostPlatform.system}; dontConfigure = true; dontBuild = true; @@ -36,7 +49,7 @@ stdenv.mkDerivation { description = "One command to grab frames, clips, or motion alerts from RTSP/ONVIF cams"; homepage = "https://github.com/steipete/camsnap"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = builtins.attrNames sources; mainProgram = "camsnap"; }; } diff --git a/nix/pkgs/gogcli.nix b/nix/pkgs/gogcli.nix index 7736dda..37484c5 100644 --- a/nix/pkgs/gogcli.nix +++ b/nix/pkgs/gogcli.nix @@ -1,13 +1,26 @@ { lib, stdenv, fetchurl }: +let + sources = { + "aarch64-darwin" = { + url = "https://github.com/steipete/gogcli/releases/download/v0.4.2/gogcli_0.4.2_darwin_arm64.tar.gz"; + hash = "sha256-RC08Z/iBORPv/1Amt7nONFEj9j6OXkXN0RsDNuR8qBM="; + }; + "x86_64-linux" = { + url = "https://github.com/steipete/gogcli/releases/download/v0.4.2/gogcli_0.4.2_linux_amd64.tar.gz"; + hash = "sha256-dktV5uti+/av5gAg7hblkHtMf6I6gaEjXN+29NnpG28="; + }; + "aarch64-linux" = { + url = "https://github.com/steipete/gogcli/releases/download/v0.4.2/gogcli_0.4.2_linux_arm64.tar.gz"; + hash = "sha256-8Y4ax0Y/WLrHVPgR1B5ld79ulHtNrpWiASCI89Vdpic="; + }; + }; +in stdenv.mkDerivation { pname = "gogcli"; version = "0.4.2"; - src = fetchurl { - url = "https://github.com/steipete/gogcli/releases/download/v0.4.2/gogcli_0.4.2_darwin_arm64.tar.gz"; - hash = "sha256-RC08Z/iBORPv/1Amt7nONFEj9j6OXkXN0RsDNuR8qBM="; - }; + src = fetchurl sources.${stdenv.hostPlatform.system}; dontConfigure = true; dontBuild = true; @@ -28,7 +41,7 @@ stdenv.mkDerivation { description = "Google CLI for Gmail, Calendar, Drive, and Contacts"; homepage = "https://github.com/steipete/gogcli"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = builtins.attrNames sources; mainProgram = "gog"; }; } diff --git a/nix/pkgs/imsg.nix b/nix/pkgs/imsg.nix index f438b72..8503658 100644 --- a/nix/pkgs/imsg.nix +++ b/nix/pkgs/imsg.nix @@ -1,13 +1,18 @@ { lib, stdenv, fetchurl, unzip }: +let + sources = { + "aarch64-darwin" = { + url = "https://github.com/steipete/imsg/releases/download/v0.4.0/imsg-macos.zip"; + hash = "sha256-0OXjM+6IGS1ZW/7Z7s5g417K0DABRZZtWtJ0WMM+QHs="; + }; + }; +in stdenv.mkDerivation { pname = "imsg"; version = "0.4.0"; - src = fetchurl { - url = "https://github.com/steipete/imsg/releases/download/v0.4.0/imsg-macos.zip"; - hash = "sha256-0OXjM+6IGS1ZW/7Z7s5g417K0DABRZZtWtJ0WMM+QHs="; - }; + src = fetchurl sources.${stdenv.hostPlatform.system}; nativeBuildInputs = [ unzip ]; dontConfigure = true; @@ -32,7 +37,7 @@ stdenv.mkDerivation { description = "Send and read iMessage / SMS from the terminal"; homepage = "https://github.com/steipete/imsg"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = builtins.attrNames sources; mainProgram = "imsg"; }; } diff --git a/nix/pkgs/oracle.nix b/nix/pkgs/oracle.nix index 36f28ee..8be31bf 100644 --- a/nix/pkgs/oracle.nix +++ b/nix/pkgs/oracle.nix @@ -122,7 +122,7 @@ PY description = "Bundle prompts + files for second-model review"; homepage = "https://github.com/steipete/oracle"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ]; mainProgram = "oracle"; }; }) diff --git a/nix/pkgs/peekaboo.nix b/nix/pkgs/peekaboo.nix index 9f29b11..28b317c 100644 --- a/nix/pkgs/peekaboo.nix +++ b/nix/pkgs/peekaboo.nix @@ -1,13 +1,18 @@ { lib, stdenv, fetchurl }: +let + sources = { + "aarch64-darwin" = { + url = "https://github.com/steipete/Peekaboo/releases/download/v3.0.0-beta3/peekaboo-macos-universal.tar.gz"; + hash = "sha256-d+rfb9XFTqxktIRNXMiHiQttb0XUmvYbBcbinqLL0kU="; + }; + }; +in stdenv.mkDerivation { pname = "peekaboo"; version = "3.0.0-beta3"; - src = fetchurl { - url = "https://github.com/steipete/Peekaboo/releases/download/v3.0.0-beta3/peekaboo-macos-universal.tar.gz"; - hash = "sha256-d+rfb9XFTqxktIRNXMiHiQttb0XUmvYbBcbinqLL0kU="; - }; + src = fetchurl sources.${stdenv.hostPlatform.system}; dontConfigure = true; dontBuild = true; @@ -28,7 +33,7 @@ stdenv.mkDerivation { description = "Lightning-fast macOS screenshots & AI vision analysis"; homepage = "https://github.com/steipete/peekaboo"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = builtins.attrNames sources; mainProgram = "peekaboo"; }; } diff --git a/nix/pkgs/poltergeist.nix b/nix/pkgs/poltergeist.nix index 77dd299..5034e25 100644 --- a/nix/pkgs/poltergeist.nix +++ b/nix/pkgs/poltergeist.nix @@ -1,13 +1,18 @@ { lib, stdenv, fetchurl, watchman }: +let + sources = { + "aarch64-darwin" = { + url = "https://github.com/steipete/poltergeist/releases/download/v2.1.1/poltergeist-macos-universal-v2.1.1.tar.gz"; + hash = "sha256-plQQjbB0QV7UY7U3ZdhfAZsAY/5m0G1E1WEgMm+elk8="; + }; + }; +in stdenv.mkDerivation { pname = "poltergeist"; version = "2.1.1"; - src = fetchurl { - url = "https://github.com/steipete/poltergeist/releases/download/v2.1.1/poltergeist-macos-universal-v2.1.1.tar.gz"; - hash = "sha256-plQQjbB0QV7UY7U3ZdhfAZsAY/5m0G1E1WEgMm+elk8="; - }; + src = fetchurl sources.${stdenv.hostPlatform.system}; dontConfigure = true; dontBuild = true; @@ -31,7 +36,7 @@ stdenv.mkDerivation { description = "Universal file watcher with auto-rebuild for any language or build system"; homepage = "https://github.com/steipete/poltergeist"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = builtins.attrNames sources; mainProgram = "poltergeist"; }; } diff --git a/nix/pkgs/sag.nix b/nix/pkgs/sag.nix index 3306f96..abb9cc2 100644 --- a/nix/pkgs/sag.nix +++ b/nix/pkgs/sag.nix @@ -1,13 +1,22 @@ { lib, stdenv, fetchurl }: +let + sources = { + "aarch64-darwin" = { + url = "https://github.com/steipete/sag/releases/download/v0.2.1/sag_0.2.1_darwin_universal.tar.gz"; + hash = "sha256-ORwAi0fgn2S8p7HmrhEmIQ5gYatf3bzLgDtkUZVMy54="; + }; + "x86_64-linux" = { + url = "https://github.com/steipete/sag/releases/download/v0.2.1/sag_0.2.1_linux_amd64.tar.gz"; + hash = "sha256-Ti9i8IfPQZn9ZTcrgipbP+du8Rlgiu/vWpqMEYWeg4I="; + }; + }; +in stdenv.mkDerivation { pname = "sag"; version = "0.2.1"; - src = fetchurl { - url = "https://github.com/steipete/sag/releases/download/v0.2.1/sag_0.2.1_darwin_universal.tar.gz"; - hash = "sha256-ORwAi0fgn2S8p7HmrhEmIQ5gYatf3bzLgDtkUZVMy54="; - }; + src = fetchurl sources.${stdenv.hostPlatform.system}; dontConfigure = true; dontBuild = true; @@ -28,7 +37,7 @@ stdenv.mkDerivation { description = "Command-line ElevenLabs TTS with mac-style flags"; homepage = "https://github.com/steipete/sag"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = builtins.attrNames sources; mainProgram = "sag"; }; } diff --git a/nix/pkgs/sonoscli.nix b/nix/pkgs/sonoscli.nix index 8ae2a53..c8706d2 100644 --- a/nix/pkgs/sonoscli.nix +++ b/nix/pkgs/sonoscli.nix @@ -1,13 +1,26 @@ { lib, stdenv, fetchurl }: +let + sources = { + "aarch64-darwin" = { + url = "https://github.com/steipete/sonoscli/releases/download/v0.1.0/sonoscli-macos-arm64.tar.gz"; + hash = "sha256-t5VUWXPrxgYXopiQEuO7k91Gx70oefyhbOZmF/XDwaw="; + }; + "x86_64-linux" = { + url = "https://github.com/steipete/sonoscli/releases/download/v0.1.0/sonoscli_0.1.0_linux_amd64.tar.gz"; + hash = "sha256-8g/sTD4P8Ctbpv5N0nZ1SpP+UH6CUuUwNEo4VjW01ZM="; + }; + "aarch64-linux" = { + url = "https://github.com/steipete/sonoscli/releases/download/v0.1.0/sonoscli_0.1.0_linux_arm64.tar.gz"; + hash = "sha256-EtBtsNcvD5OvryUjCQ5oy3H7w4etgfXs7PkdsefWdE0="; + }; + }; +in stdenv.mkDerivation { pname = "sonoscli"; version = "0.1.0"; - src = fetchurl { - url = "https://github.com/steipete/sonoscli/releases/download/v0.1.0/sonoscli-macos-arm64.tar.gz"; - hash = "sha256-t5VUWXPrxgYXopiQEuO7k91Gx70oefyhbOZmF/XDwaw="; - }; + src = fetchurl sources.${stdenv.hostPlatform.system}; dontConfigure = true; dontBuild = true; @@ -34,7 +47,7 @@ stdenv.mkDerivation { description = "Control Sonos speakers from the command-line"; homepage = "https://github.com/steipete/sonoscli"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = builtins.attrNames sources; mainProgram = "sonos"; }; } diff --git a/nix/pkgs/summarize.nix b/nix/pkgs/summarize.nix index 1757167..82822eb 100644 --- a/nix/pkgs/summarize.nix +++ b/nix/pkgs/summarize.nix @@ -1,34 +1,120 @@ -{ lib, stdenv, fetchurl }: +{ lib +, stdenv +, fetchurl +, nodejs +, pnpm +, python3 +, python3Packages +, pkg-config +, makeWrapper +, pkgs +, zstd +}: -stdenv.mkDerivation { +let pname = "summarize"; version = "0.9.0"; - - src = fetchurl { - url = "https://github.com/steipete/summarize/releases/download/v0.9.0/summarize-macos-arm64-v0.9.0.tar.gz"; - hash = "sha256-B6/eUcbv4K9kgozo1fELFX+NNGa0C64dB6OSydwu6A8="; + binSources = { + "aarch64-darwin" = { + url = "https://github.com/steipete/summarize/releases/download/v0.9.0/summarize-macos-arm64-v0.9.0.tar.gz"; + hash = "sha256-B6/eUcbv4K9kgozo1fELFX+NNGa0C64dB6OSydwu6A8="; + }; }; - dontConfigure = true; - dontBuild = true; + src = fetchurl { + url = "https://github.com/steipete/summarize/archive/refs/tags/v${version}.tar.gz"; + hash = "sha256-HQ/jboAN+g7Mz41ayDAt0thR5kuJjttgfJTXE7IRSzQ="; + }; - unpackPhase = '' - tar -xzf "$src" - ''; + pnpmFetchDepsPkg = pkgs.callPackage "${pkgs.path}/pkgs/build-support/node/fetch-pnpm-deps" { + inherit pnpm; + }; - installPhase = '' - runHook preInstall - mkdir -p "$out/bin" - cp summarize "$out/bin/summarize" - chmod 0755 "$out/bin/summarize" - runHook postInstall - ''; + pnpmDeps = (pnpmFetchDepsPkg.fetchPnpmDeps { + pname = pname; + version = version; + src = src; + hash = "sha256-3BRbu9xNYUpsUkC1DKXKl8iv5GO9rZqE2eqRVDh8DTA="; + fetcherVersion = 3; + }); meta = with lib; { description = "Link → clean text → summary"; homepage = "https://github.com/steipete/summarize"; license = licenses.mit; - platforms = [ "aarch64-darwin" ]; + platforms = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ]; mainProgram = "summarize"; }; -} +in +if stdenv.isLinux then + stdenv.mkDerivation { + inherit pname version src meta; + + nativeBuildInputs = [ + nodejs + pnpm + python3 + python3Packages.setuptools + pkg-config + makeWrapper + zstd + ]; + + env = { + PNPM_IGNORE_PACKAGE_MANAGER_CHECK = "1"; + CI = "1"; + HOME = "/tmp"; + PNPM_HOME = "/tmp/pnpm-home"; + PNPM_CONFIG_HOME = "/tmp/pnpm-config"; + XDG_CACHE_HOME = "/tmp/pnpm-cache"; + NPM_CONFIG_USERCONFIG = "/tmp/pnpm-config/.npmrc"; + npm_config_nodedir = "${nodejs.dev}"; + npm_config_build_from_source = "1"; + PNPM_CONFIG_IGNORE_SCRIPTS = "1"; + }; + + buildPhase = '' + runHook preBuild + mkdir -p "$HOME" "$PNPM_HOME" "$PNPM_CONFIG_HOME" "$XDG_CACHE_HOME" + export PNPM_STORE_PATH="$TMPDIR/pnpm-store" + mkdir -p "$PNPM_STORE_PATH" + tar --zstd -xf ${pnpmDeps}/pnpm-store.tar.zst -C "$PNPM_STORE_PATH" + pnpm install --offline --frozen-lockfile --store-dir "$PNPM_STORE_PATH" --ignore-scripts + pnpm build + pnpm prune --prod + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + mkdir -p "$out/libexec" "$out/bin" + cp -r dist package.json node_modules "$out/libexec/" + chmod 0755 "$out/libexec/dist/cli.js" + makeWrapper "${nodejs}/bin/node" "$out/bin/summarize" \ + --add-flags "$out/libexec/dist/cli.js" + runHook postInstall + ''; + } +else + stdenv.mkDerivation { + pname = pname; + version = version; + src = fetchurl binSources.${stdenv.hostPlatform.system}; + + dontConfigure = true; + dontBuild = true; + + unpackPhase = '' + tar -xzf "$src" + ''; + + installPhase = '' + runHook preInstall + mkdir -p "$out/bin" + cp summarize "$out/bin/summarize" + chmod 0755 "$out/bin/summarize" + runHook postInstall + ''; + + inherit meta; + } diff --git a/tools/bird/flake.nix b/tools/bird/flake.nix index d772dd4..60bc30e 100644 --- a/tools/bird/flake.nix +++ b/tools/bird/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - bird = root.packages.${system}.bird; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + bird = packagesForSystem.bird or null; in { - packages.${system}.bird = bird; + packages.${system} = if bird == null then {} else { bird = bird; }; - clawdbotPlugin = { + clawdbotPlugin = if bird == null then null else { name = "bird"; skills = [ ./skills/bird ]; packages = [ bird ]; diff --git a/tools/camsnap/flake.nix b/tools/camsnap/flake.nix index af7fbd4..10deeaa 100644 --- a/tools/camsnap/flake.nix +++ b/tools/camsnap/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - camsnap = root.packages.${system}.camsnap; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + camsnap = packagesForSystem.camsnap or null; in { - packages.${system}.camsnap = camsnap; + packages.${system} = if camsnap == null then {} else { camsnap = camsnap; }; - clawdbotPlugin = { + clawdbotPlugin = if camsnap == null then null else { name = "camsnap"; skills = [ ./skills/camsnap ]; packages = [ camsnap ]; diff --git a/tools/gogcli/flake.nix b/tools/gogcli/flake.nix index 705b829..1e432a5 100644 --- a/tools/gogcli/flake.nix +++ b/tools/gogcli/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - gogcli = root.packages.${system}.gogcli; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + gogcli = packagesForSystem.gogcli or null; in { - packages.${system}.gogcli = gogcli; + packages.${system} = if gogcli == null then {} else { gogcli = gogcli; }; - clawdbotPlugin = { + clawdbotPlugin = if gogcli == null then null else { name = "gogcli"; skills = [ ./skills/gog ]; packages = [ gogcli ]; diff --git a/tools/imsg/flake.nix b/tools/imsg/flake.nix index 4d24c90..fec7d78 100644 --- a/tools/imsg/flake.nix +++ b/tools/imsg/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - imsg = root.packages.${system}.imsg; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + imsg = packagesForSystem.imsg or null; in { - packages.${system}.imsg = imsg; + packages.${system} = if imsg == null then {} else { imsg = imsg; }; - clawdbotPlugin = { + clawdbotPlugin = if imsg == null then null else { name = "imsg"; skills = [ ./skills/imsg ]; packages = [ imsg ]; diff --git a/tools/oracle/flake.nix b/tools/oracle/flake.nix index 960c630..55f2eb2 100644 --- a/tools/oracle/flake.nix +++ b/tools/oracle/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - oracle = root.packages.${system}.oracle; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + oracle = packagesForSystem.oracle or null; in { - packages.${system}.oracle = oracle; + packages.${system} = if oracle == null then {} else { oracle = oracle; }; - clawdbotPlugin = { + clawdbotPlugin = if oracle == null then null else { name = "oracle"; skills = [ ./skills/oracle ]; packages = [ oracle ]; diff --git a/tools/peekaboo/flake.nix b/tools/peekaboo/flake.nix index 4c589f6..34f54cf 100644 --- a/tools/peekaboo/flake.nix +++ b/tools/peekaboo/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - peekaboo = root.packages.${system}.peekaboo; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + peekaboo = packagesForSystem.peekaboo or null; in { - packages.${system}.peekaboo = peekaboo; + packages.${system} = if peekaboo == null then {} else { peekaboo = peekaboo; }; - clawdbotPlugin = { + clawdbotPlugin = if peekaboo == null then null else { name = "peekaboo"; skills = [ ./skills/peekaboo ]; packages = [ peekaboo ]; diff --git a/tools/poltergeist/flake.nix b/tools/poltergeist/flake.nix index 62f3888..8f6a41d 100644 --- a/tools/poltergeist/flake.nix +++ b/tools/poltergeist/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - poltergeist = root.packages.${system}.poltergeist; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + poltergeist = packagesForSystem.poltergeist or null; in { - packages.${system}.poltergeist = poltergeist; + packages.${system} = if poltergeist == null then {} else { poltergeist = poltergeist; }; - clawdbotPlugin = { + clawdbotPlugin = if poltergeist == null then null else { name = "poltergeist"; skills = [ ./skills/poltergeist ]; packages = [ poltergeist ]; diff --git a/tools/sag/flake.nix b/tools/sag/flake.nix index 4ac7f7c..acb303b 100644 --- a/tools/sag/flake.nix +++ b/tools/sag/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - sag = root.packages.${system}.sag; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + sag = packagesForSystem.sag or null; in { - packages.${system}.sag = sag; + packages.${system} = if sag == null then {} else { sag = sag; }; - clawdbotPlugin = { + clawdbotPlugin = if sag == null then null else { name = "sag"; skills = [ ./skills/sag ]; packages = [ sag ]; diff --git a/tools/sonoscli/flake.nix b/tools/sonoscli/flake.nix index ec4cbc5..18cbc65 100644 --- a/tools/sonoscli/flake.nix +++ b/tools/sonoscli/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - sonoscli = root.packages.${system}.sonoscli; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + sonoscli = packagesForSystem.sonoscli or null; in { - packages.${system}.sonoscli = sonoscli; + packages.${system} = if sonoscli == null then {} else { sonoscli = sonoscli; }; - clawdbotPlugin = { + clawdbotPlugin = if sonoscli == null then null else { name = "sonoscli"; skills = [ ./skills/sonoscli ]; packages = [ sonoscli ]; diff --git a/tools/summarize/flake.nix b/tools/summarize/flake.nix index 7fe7891..34b78be 100644 --- a/tools/summarize/flake.nix +++ b/tools/summarize/flake.nix @@ -8,13 +8,13 @@ outputs = { self, nixpkgs, root }: let - system = "aarch64-darwin"; - pkgs = import nixpkgs { inherit system; }; - summarize = root.packages.${system}.summarize; + system = builtins.currentSystem; + packagesForSystem = root.packages.${system} or {}; + summarize = packagesForSystem.summarize or null; in { - packages.${system}.summarize = summarize; + packages.${system} = if summarize == null then {} else { summarize = summarize; }; - clawdbotPlugin = { + clawdbotPlugin = if summarize == null then null else { name = "summarize"; skills = [ ./skills/summarize ]; packages = [ summarize ];