Skip to content

Repository files navigation

binkit

Provision, verify, pin, and update external CLI binaries from Go.

Some tools you depend on aren't written in Go, so go tool can't manage them. binkit fetches them from GitHub releases, verifies them against a pinned SHA-256, caches them per version, and hands your program back a path.

Pin once, from the command line, and commit the result:

binkit pin typst        # writes tools.json — commit it

Then resolve it from your program:

path, err := resolver.Ensure(ctx, catalog.Typst())
if err != nil {
    return err
}
cmd := exec.CommandContext(ctx, path, "compile", "in.typ", "out.pdf")

Adding binkit to a Go application? INTEGRATION.md is the step-by-step guide.

CLI

binkit pin <tool>[@version]   resolve, install, and record the pin
binkit ensure <tool>          install if needed, print the path
binkit path <tool>            print an installed tool's path; never uses the network
binkit list                   list the pins in the lock file

Stdout carries only paths and the pin table, so command substitution is safe:

TYPST=$(binkit ensure typst) && "$TYPST" compile in.typ out.pdf

Install it with go install github.com/jroedel/binkit/cmd/binkit@latest, or manage it as a tool dependency with go get -tool — binkit is a Go program, so go tool handles it, which is exactly what it cannot do for Typst.

The one-dependency promise

go.mod has exactly one require line: github.com/ulikunitz/xz, which is itself stdlib-only with zero transitive dependencies.

That dependency exists for one reason — many projects (Typst among them) ship .tar.xz, and the Go standard library has archive/tar, archive/zip, and compress/gzip but no xz decoder. Shelling out to tar -xJf would trade a compile-time dependency for a runtime environment dependency and break on Windows and minimal containers, so it isn't done.

Everything else is stdlib or deliberately hand-rolled: semver comparison instead of golang.org/x/mod, TTY detection instead of mattn/go-isatty, atomic directory rename instead of a file-locking library.

Pinning

tools.json lives in your repo and is checked in:

{
  "typst": {
    "version": "0.15.1",
    "repo": "typst/typst",
    "digests": {
      "linux/amd64":   "sha256:...",
      "darwin/arm64":  "sha256:...",
      "windows/amd64": "sha256:..."
    }
  }
}

Digests are captured for every platform at pin time, so a colleague on macOS gets a verified install from a lockfile generated on Linux.

Ensure never resolves "latest": a pinned version downloads by direct URL, so provisioning makes no GitHub API call, needs no token, and cannot hit the API rate limit. The one API request Ensure can make is the advisory update check below, which changes nothing and is disableable. Only an explicit Update call changes the pin.

Update checks

Once every seven days, binkit checks whether a newer release exists and reports it — to stderr, and only when stderr is a terminal, so it never pollutes piped output or CI logs. When there is nowhere to show a notice the check is skipped entirely rather than performed and discarded.

binkit: typst 0.15.1 is pinned; 0.16.0 is available.
        run: massprep --update-tools typst

The second line appears only if you set Resolver.UpdateHint — binkit cannot know your CLI's flags.

A failed check backs off for an hour rather than consuming the seven-day window, so being offline for a week neither retries on every invocation nor silences the next real check. Nothing here can fail a build, and nothing is ever updated automatically. Set BINKIT_NO_UPDATE_CHECK=1, or Resolver.NoCheck, to disable.

Escape hatch

BINKIT_<TOOLNAME> (e.g. BINKIT_TYPST=/usr/bin/typst) short-circuits everything and uses that binary. Useful for CI images, Nix, and distro packages.

The tool name is upper-cased and every character outside A-Z0-9 becomes an underscore, so go-task reads BINKIT_GO_TASK. Don't name a tool cache or no-update-check — those collide with BINKIT_CACHE and BINKIT_NO_UPDATE_CHECK.

Stability

v0: the Go API may change in a minor release. The lock file format is held to a stricter standard, since it lives in your repository and a break there breaks builds. Changes are recorded in CHANGELOG.md.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages