nix-openclaw-tools/cmd/update-tools/main.go
joshp123 675f4ba420 rename OpenClaw tools flake
Move the flake identity from nix-steipete-tools to nix-openclaw-tools, update transferred upstream repos to openclaw, and drop stale CodexBar/bird packaging.

Tests: go test ./...; nix flake show --all-systems; nix build .#gogcli .#goplaces .#summarize .#camsnap .#sonoscli --no-link
2026-05-05 12:04:06 +02:00

260 lines
8.4 KiB
Go

package main
import (
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"github.com/openclaw/nix-openclaw-tools/internal"
)
type Tool struct {
Name string
Repo string
Assets []AssetSpec
NixFile string
Optional bool
}
type AssetSpec struct {
System string
Regex *regexp.Regexp
}
func updateTool(tool Tool) error {
log.Printf("[update-tools] %s", tool.Name)
rel, err := internal.LatestRelease(tool.Repo)
if err != nil {
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 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 summarize")
}
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(summarizeFile, regexp.MustCompile(`version = "[^"]+";`), fmt.Sprintf(`version = "%s";`, version)); err != nil {
return err
}
if err := updateSourceBlock(summarizeFile, "aarch64-darwin", assetURL, assetHash); err != nil {
return err
}
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 == "" && runtime.GOOS == "darwin" {
log.Printf("[update-tools] summarize: no pnpm hash on darwin, trying x86_64-linux")
logText, buildErr = internal.NixBuildSummarizeSystem("x86_64-linux")
pnpmHash = internal.ExtractGotHash(logText)
}
if pnpmHash == "" {
_ = os.WriteFile(summarizeFile, orig, 0644)
return fmt.Errorf("summarize pnpm hash not found (build err: %v)", buildErr)
}
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
}
func main() {
repoRoot, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
tools := []Tool{
{
Name: "discrawl",
Repo: "openclaw/discrawl",
Assets: []AssetSpec{
{System: "aarch64-darwin", Regex: regexp.MustCompile(`discrawl_[0-9.]+_darwin_arm64\.tar\.gz`)},
{System: "x86_64-linux", Regex: regexp.MustCompile(`discrawl_[0-9.]+_linux_amd64\.tar\.gz`)},
{System: "aarch64-linux", Regex: regexp.MustCompile(`discrawl_[0-9.]+_linux_arm64\.tar\.gz`)},
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "discrawl.nix"),
},
{
Name: "wacrawl",
Repo: "steipete/wacrawl",
Assets: []AssetSpec{
{System: "aarch64-darwin", Regex: regexp.MustCompile(`wacrawl_[0-9.]+_darwin_arm64\.tar\.gz`)},
{System: "x86_64-linux", Regex: regexp.MustCompile(`wacrawl_[0-9.]+_linux_amd64\.tar\.gz`)},
{System: "aarch64-linux", Regex: regexp.MustCompile(`wacrawl_[0-9.]+_linux_arm64\.tar\.gz`)},
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "wacrawl.nix"),
},
{
Name: "gogcli",
Repo: "openclaw/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: "goplaces",
Repo: "openclaw/goplaces",
Assets: []AssetSpec{
{System: "aarch64-darwin", Regex: regexp.MustCompile(`goplaces_[0-9.]+_darwin_arm64\.tar\.gz`)},
{System: "x86_64-darwin", Regex: regexp.MustCompile(`goplaces_[0-9.]+_darwin_amd64\.tar\.gz`)},
{System: "x86_64-linux", Regex: regexp.MustCompile(`goplaces_[0-9.]+_linux_amd64\.tar\.gz`)},
{System: "aarch64-linux", Regex: regexp.MustCompile(`goplaces_[0-9.]+_linux_arm64\.tar\.gz`)},
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "goplaces.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: "peekaboo",
Repo: "openclaw/Peekaboo",
Assets: []AssetSpec{
{System: "aarch64-darwin", Regex: regexp.MustCompile(`peekaboo-macos-(?:arm64|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: "openclaw/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 {
if tool.Optional {
log.Printf("[update-tools] skipping optional tool %s: %v", tool.Name, err)
continue
}
log.Fatalf("update %s failed: %v", tool.Name, err)
}
}
}