From ab81ded6e5019ed000b7e898061f5a5426df32f6 Mon Sep 17 00:00:00 2001 From: Mike Landau Date: Fri, 30 Oct 2026 10:07:38 -0700 Subject: [PATCH] feat(pkgs): add the initial set of package definitions Add package definitions for nodejs (with npm), pnpm, yarn, python, uv, rust, java (Eclipse Temurin), gradle, maven, deno, bun, zig, terraform, kubectl and helm, next to the existing go definition. The go definition now sets GOTOOLCHAIN=local, like the nixpkgs go, so the version in devbox.json runs even when go.mod asks for a newer toolchain. The spec documents what these packages need from the format: lists of version sources, paginated sources, versions with +build metadata, install.lookup for vendors whose download names come from an index, install.version_overrides for vendors that changed how they ship, sha512 and github_release checksums, platforms a version is not published for, and binaries that install marks executable and verifies. It also lists the shipped packages and the PR 4-6 plan. The definitions parse once the next change implements these features. Co-Authored-By: Claude Opus 5.5 (1M context) --- pkgs/bun/bun.pkg.jsonc | 30 ++++ pkgs/deno/deno.pkg.jsonc | 34 +++++ pkgs/go/go.pkg.jsonc | 11 +- pkgs/gradle/gradle.pkg.jsonc | 33 +++++ pkgs/helm/helm.pkg.jsonc | 24 +++ pkgs/java/java.pkg.jsonc | 49 +++++++ pkgs/kubectl/kubectl.pkg.jsonc | 24 +++ pkgs/maven/maven.pkg.jsonc | 34 +++++ pkgs/nodejs/nodejs.pkg.jsonc | 43 ++++++ pkgs/pnpm/pnpm.pkg.jsonc | 43 ++++++ pkgs/python/python.pkg.jsonc | 50 +++++++ pkgs/rust/rust.pkg.jsonc | 51 +++++++ pkgs/terraform/terraform.pkg.jsonc | 29 ++++ pkgs/uv/uv.pkg.jsonc | 27 ++++ pkgs/yarn/yarn.pkg.jsonc | 54 +++++++ pkgs/zig/zig.pkg.jsonc | 35 +++++ specs/package-definitions.md | 226 ++++++++++++++++++++++++++--- 17 files changed, 770 insertions(+), 27 deletions(-) create mode 100644 pkgs/bun/bun.pkg.jsonc create mode 100644 pkgs/deno/deno.pkg.jsonc create mode 100644 pkgs/gradle/gradle.pkg.jsonc create mode 100644 pkgs/helm/helm.pkg.jsonc create mode 100644 pkgs/java/java.pkg.jsonc create mode 100644 pkgs/kubectl/kubectl.pkg.jsonc create mode 100644 pkgs/maven/maven.pkg.jsonc create mode 100644 pkgs/nodejs/nodejs.pkg.jsonc create mode 100644 pkgs/pnpm/pnpm.pkg.jsonc create mode 100644 pkgs/python/python.pkg.jsonc create mode 100644 pkgs/rust/rust.pkg.jsonc create mode 100644 pkgs/terraform/terraform.pkg.jsonc create mode 100644 pkgs/uv/uv.pkg.jsonc create mode 100644 pkgs/yarn/yarn.pkg.jsonc create mode 100644 pkgs/zig/zig.pkg.jsonc diff --git a/pkgs/bun/bun.pkg.jsonc b/pkgs/bun/bun.pkg.jsonc new file mode 100644 index 00000000000..dc0908a319f --- /dev/null +++ b/pkgs/bun/bun.pkg.jsonc @@ -0,0 +1,30 @@ +// Package definition for Bun, installed from its GitHub releases. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "bun", + "description": "JavaScript runtime, bundler, test runner and package manager", + "homepage": "https://bun.sh", + "license": "MIT", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + "arch_map": { "x86_64": "x64", "aarch64": "aarch64" }, + + "versions": { "type": "github_releases", "repo": "oven-sh/bun", "strip_prefix": "bun-v" }, + + "install": { + "type": "archive", + "url": "https://github.com/oven-sh/bun/releases/download/bun-v{{.Version}}/bun-{{.OS}}-{{.Arch}}.zip", + "checksum": { + "type": "sha256_url", + "url": "https://github.com/oven-sh/bun/releases/download/bun-v{{.Version}}/SHASUMS256.txt" + }, + "strip_components": 1 + }, + + // bunx is a link to bun, as Bun's own installer creates it. + "hooks": { "post_install": ["ln -sf bun bunx"] }, + + "bin": ["."], + "binaries": ["bun", "bunx"] +} diff --git a/pkgs/deno/deno.pkg.jsonc b/pkgs/deno/deno.pkg.jsonc new file mode 100644 index 00000000000..6147c6b2974 --- /dev/null +++ b/pkgs/deno/deno.pkg.jsonc @@ -0,0 +1,34 @@ +// Package definition for Deno, installed from its GitHub releases. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "deno", + "description": "JavaScript, TypeScript and WebAssembly runtime", + "homepage": "https://deno.com", + "license": "MIT", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + // Rust target triples. + "os_map": { "darwin": "apple-darwin", "linux": "unknown-linux-gnu" }, + "arch_map": { "x86_64": "x86_64", "aarch64": "aarch64" }, + + "versions": { "type": "github_releases", "repo": "denoland/deno" }, + + "install": { + "type": "archive", + "url": "https://github.com/denoland/deno/releases/download/v{{.Version}}/deno-{{.Arch}}-{{.OS}}.zip", + "checksum": { "type": "sha256_url", "url": "{{.URL}}.sha256sum" }, + "version_overrides": [ + { + // Releases before 2.0.1 published no checksums and their GitHub + // assets have no digest; the first install records one. + "versions": "<2.0.1", + "checksum": { "type": "none" } + } + ] + }, + + // The zip holds just the deno executable. + "bin": ["."], + "binaries": ["deno"] +} diff --git a/pkgs/go/go.pkg.jsonc b/pkgs/go/go.pkg.jsonc index e607b4f6606..103bfbe1492 100644 --- a/pkgs/go/go.pkg.jsonc +++ b/pkgs/go/go.pkg.jsonc @@ -33,8 +33,13 @@ }, "bin": ["bin"], - "binaries": ["go", "gofmt"] + "binaries": ["go", "gofmt"], - // GOROOT is intentionally not set: the go binary locates its own root and - // upstream discourages setting it. + // Run the version in devbox.json even when go.mod asks for a newer + // toolchain, instead of downloading that one (the same default as the + // nixpkgs go). GOROOT is intentionally not set: the go binary locates its + // own root and upstream discourages setting it. + "env": { + "GOTOOLCHAIN": "local" + } } diff --git a/pkgs/gradle/gradle.pkg.jsonc b/pkgs/gradle/gradle.pkg.jsonc new file mode 100644 index 00000000000..33fd2da336b --- /dev/null +++ b/pkgs/gradle/gradle.pkg.jsonc @@ -0,0 +1,33 @@ +// Package definition for Gradle, installed from services.gradle.org. Gradle +// runs on the JVM, so the project also needs java. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "gradle", + "description": "Build automation tool for the JVM", + "homepage": "https://gradle.org", + "license": "Apache-2.0", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + + // One JSON document lists every version with its download checksum. + "versions": { + "type": "http_json", + "url": "https://services.gradle.org/versions/all", + "select": ".[] | select(.snapshot or .nightly or .releaseNightly or .broken or .rcFor != \"\" or .milestoneFor != \"\" | not) | .version" + }, + + "install": { + "type": "archive", + "url": "https://services.gradle.org/distributions/gradle-{{.Version}}-bin.zip", + "checksum": { + "type": "http_json", + "url": "https://services.gradle.org/versions/all", + "select": ".[] | select(.version == \"{{.Version}}\") | .checksum" + }, + "strip_components": 1 + }, + + "bin": ["bin"], + "binaries": ["gradle"] +} diff --git a/pkgs/helm/helm.pkg.jsonc b/pkgs/helm/helm.pkg.jsonc new file mode 100644 index 00000000000..cff02698221 --- /dev/null +++ b/pkgs/helm/helm.pkg.jsonc @@ -0,0 +1,24 @@ +// Package definition for Helm, installed from get.helm.sh. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "helm", + "description": "The package manager for Kubernetes", + "homepage": "https://helm.sh", + "license": "Apache-2.0", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + + "versions": { "type": "github_releases", "repo": "helm/helm" }, + + "install": { + "type": "archive", + "url": "https://get.helm.sh/helm-v{{.Version}}-{{.OS}}-{{.Arch}}.tar.gz", + "checksum": { "type": "sha256_url", "url": "{{.URL}}.sha256sum" }, + "strip_components": 1 + }, + + // The tarball holds -/helm. + "bin": ["."], + "binaries": ["helm"] +} diff --git a/pkgs/java/java.pkg.jsonc b/pkgs/java/java.pkg.jsonc new file mode 100644 index 00000000000..2a07d1f22c1 --- /dev/null +++ b/pkgs/java/java.pkg.jsonc @@ -0,0 +1,49 @@ +// Package definition for Java, installed as the Eclipse Temurin JDK from +// Adoptium. Versions are OpenJDK version strings such as "21.0.5+11", so +// `java@21` picks the newest Java 21 build. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "java", + "description": "Eclipse Temurin, the Adoptium build of the OpenJDK Java Development Kit", + "homepage": "https://adoptium.net", + "license": "GPL-2.0-with-classpath-exception", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + "os_map": { "darwin": "mac", "linux": "linux" }, + "arch_map": { "x86_64": "x64", "aarch64": "aarch64" }, + + // The API pages its results 50 at a time, newest first. Long-term + // support builds carry a ".0.LTS" suffix, which is dropped. + "versions": { + "type": "http_json", + "url": "https://api.adoptium.net/v3/info/release_versions?release_type=ga&vendor=eclipse&image_type=jdk&sort_order=DESC&page_size=50&page={{.Page}}", + "select": ".versions[].semver | rtrimstr(\".0.LTS\") | rtrimstr(\".LTS\")" + }, + + // Download names can't be derived from the version (respins are renamed, + // Java 8 uses "8u432b06"), so the artifact and its checksum come from the + // API's list of releases for the version's feature release. Java 8 has no + // build for Apple silicon; the API returns 404 and that platform is + // skipped. + "install": { + "type": "archive", + "lookup": { + "url": "https://api.adoptium.net/v3/assets/feature_releases/{{ index (splitList \".\" .Version) 0 }}/ga?os={{.OS}}&architecture={{.Arch}}&image_type=jdk&jvm_impl=hotspot&heap_size=normal&vendor=eclipse&page_size=50&sort_order=DESC", + "select": ".[] | select((.version_data.semver | rtrimstr(\".0.LTS\") | rtrimstr(\".LTS\")) == \"{{.Version}}\") | .binaries[0].package | {url: .link, checksum: .checksum}" + }, + "strip_components": 1, + // macOS builds are bundles; the JDK is in /Contents/Home. + "overrides": { + "aarch64-darwin": { "strip_components": 3 }, + "x86_64-darwin": { "strip_components": 3 } + } + }, + + "bin": ["bin"], + "binaries": ["java", "javac", "jar"], + + "env": { + "JAVA_HOME": "{{ .InstallDir }}" + } +} diff --git a/pkgs/kubectl/kubectl.pkg.jsonc b/pkgs/kubectl/kubectl.pkg.jsonc new file mode 100644 index 00000000000..239efb132c1 --- /dev/null +++ b/pkgs/kubectl/kubectl.pkg.jsonc @@ -0,0 +1,24 @@ +// Package definition for kubectl, installed from the Kubernetes release +// downloads at dl.k8s.io. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "kubectl", + "description": "Kubernetes command-line tool", + "homepage": "https://kubernetes.io/docs/reference/kubectl/", + "license": "Apache-2.0", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + + // Kubernetes releases are GitHub releases; alphas, betas and release + // candidates are marked as pre-releases. + "versions": { "type": "github_releases", "repo": "kubernetes/kubernetes" }, + + "install": { + "type": "binary", + "url": "https://dl.k8s.io/release/v{{.Version}}/bin/{{.OS}}/{{.Arch}}/kubectl", + "checksum": { "type": "sha256_url", "url": "{{.URL}}.sha256" } + }, + + "binaries": ["kubectl"] +} diff --git a/pkgs/maven/maven.pkg.jsonc b/pkgs/maven/maven.pkg.jsonc new file mode 100644 index 00000000000..a23eccab279 --- /dev/null +++ b/pkgs/maven/maven.pkg.jsonc @@ -0,0 +1,34 @@ +// Package definition for Apache Maven, installed from Maven Central. Maven +// runs on the JVM, so the project also needs java. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "maven", + "description": "Build automation and dependency management for Java", + "homepage": "https://maven.apache.org", + "license": "Apache-2.0", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + + "versions": { + "type": "http_json", + "url": "https://search.maven.org/solrsearch/select?q=g:org.apache.maven+AND+a:apache-maven&core=gav&rows=500&wt=json", + "select": ".response.docs[].v" + }, + + // Downloads come from Maven Central's CDN. Central only has digests for + // some releases, so the sha512 comes from the Apache release archive, + // which publishes one for every release since 3.5. + "install": { + "type": "archive", + "url": "https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/{{.Version}}/apache-maven-{{.Version}}-bin.tar.gz", + "checksum": { + "type": "sha512_url", + "url": "https://archive.apache.org/dist/maven/maven-{{ index (splitList \".\" .Version) 0 }}/{{.Version}}/binaries/apache-maven-{{.Version}}-bin.tar.gz.sha512" + }, + "strip_components": 1 + }, + + "bin": ["bin"], + "binaries": ["mvn"] +} diff --git a/pkgs/nodejs/nodejs.pkg.jsonc b/pkgs/nodejs/nodejs.pkg.jsonc new file mode 100644 index 00000000000..901ec4bdad7 --- /dev/null +++ b/pkgs/nodejs/nodejs.pkg.jsonc @@ -0,0 +1,43 @@ +// Package definition for Node.js, installed from the official builds on +// nodejs.org. npm ships with every release; add pnpm or yarn as their own +// packages. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "nodejs", + "description": "JavaScript runtime built on Chrome's V8 engine, with npm", + "homepage": "https://nodejs.org", + "license": "MIT", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + "arch_map": { "x86_64": "x64", "aarch64": "arm64" }, + + // index.json lists every release, newest first, as "v22.11.0". + "versions": { + "type": "http_json", + "url": "https://nodejs.org/dist/index.json", + "select": ".[].version | ltrimstr(\"v\")" + }, + + // Releases that predate a platform (no darwin-arm64 before 16) have no + // entry in SHASUMS256.txt and are skipped for that platform. + "install": { + "type": "archive", + "url": "https://nodejs.org/dist/v{{.Version}}/node-v{{.Version}}-{{.OS}}-{{.Arch}}.tar.gz", + "checksum": { + "type": "sha256_url", + "url": "https://nodejs.org/dist/v{{.Version}}/SHASUMS256.txt" + }, + "strip_components": 1 + }, + + "bin": ["bin"], + "binaries": ["node", "npm", "npx"], + + // `npm install --global` writes into the project instead of the shared + // install, which every project using this version sees. + "env": { + "NPM_CONFIG_PREFIX": "{{ .Virtenv }}/npm-global", + "PATH": "{{ .Virtenv }}/npm-global/bin:$PATH" + } +} diff --git a/pkgs/pnpm/pnpm.pkg.jsonc b/pkgs/pnpm/pnpm.pkg.jsonc new file mode 100644 index 00000000000..99e981b3843 --- /dev/null +++ b/pkgs/pnpm/pnpm.pkg.jsonc @@ -0,0 +1,43 @@ +// Package definition for pnpm, installed as the standalone executable that +// bundles its own Node.js, so it works with or without nodejs installed. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "pnpm", + "description": "Fast, disk space efficient package manager", + "homepage": "https://pnpm.io", + "license": "MIT", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + "arch_map": { "x86_64": "x64", "aarch64": "arm64" }, + + "versions": { "type": "github_releases", "repo": "pnpm/pnpm" }, + + // Since v11 every GitHub release has a tarball per platform holding the + // pnpm executable and its dist/ directory. GitHub records a digest for + // each asset. + "install": { + "type": "archive", + "url": "https://github.com/pnpm/pnpm/releases/download/v{{.Version}}/pnpm-{{.OS}}-{{.Arch}}.tar.gz", + "checksum": { "type": "github_release", "repo": "pnpm/pnpm" }, + "version_overrides": [ + { + // Before v11 the executable was published to npm as + // @pnpm/-, and npm has a sha512 for every + // version. (The GitHub assets of that era have no digest.) + "versions": "<11", + "url": "https://registry.npmjs.org/@pnpm/{{ .OS | replace \"darwin\" \"macos\" }}-{{.Arch}}/-/{{ .OS | replace \"darwin\" \"macos\" }}-{{.Arch}}-{{.Version}}.tgz", + "checksum": { + "type": "http_json", + "url": "https://registry.npmjs.org/@pnpm/{{ .OS | replace \"darwin\" \"macos\" }}-{{.Arch}}/{{.Version}}", + "select": ".dist.integrity" + }, + "strip_components": 1 + } + ] + }, + + // Both layouts put the executable at the root of the install. + "bin": ["."], + "binaries": ["pnpm"] +} diff --git a/pkgs/python/python.pkg.jsonc b/pkgs/python/python.pkg.jsonc new file mode 100644 index 00000000000..233b3655bb3 --- /dev/null +++ b/pkgs/python/python.pkg.jsonc @@ -0,0 +1,50 @@ +// Package definition for CPython, installed from python-build-standalone, +// the relocatable builds that uv and mise install. The index of builds +// (URL and sha256 per version and platform) is the one uv maintains. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "python", + "description": "CPython. Devbox creates a virtual environment in $VENV_DIR (default .venv) and activates it, so pip installs stay out of the shared install. Switching versions recreates a virtual environment that Devbox created.", + "homepage": "https://www.python.org", + "license": "PSF-2.0", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + "arch_map": { "x86_64": "x86_64", "aarch64": "aarch64" }, + + // Versions are "3.13.1", or "3.14.0a1" for pre-releases. + "versions": { + "type": "http_json", + "url": "https://raw.githubusercontent.com/astral-sh/uv/main/crates/uv-python-managed/download-metadata.json", + "select": ".[] | select(.name == \"cpython\" and .variant == null) | \"\\(.major).\\(.minor).\\(.patch)\\(.prerelease)\"" + }, + + // The default build for a platform: no free-threading or debug variant, + // the baseline CPU level, glibc on Linux. + "install": { + "type": "archive", + "lookup": { + "url": "https://raw.githubusercontent.com/astral-sh/uv/main/crates/uv-python-managed/download-metadata.json", + "select": ".[] | select(.name == \"cpython\" and .variant == null and .arch.variant == null and .os == \"{{.OS}}\" and .arch.family == \"{{.Arch}}\" and (.libc == \"gnu\" or .libc == \"none\") and \"\\(.major).\\(.minor).\\(.patch)\\(.prerelease)\" == \"{{.Version}}\") | {url, checksum: .sha256}" + }, + "strip_components": 1 + }, + + "bin": ["bin"], + "binaries": ["python3", "pip3"], + + "env": { + "VENV_DIR": "{{ .DevboxProjectDir }}/.venv" + }, + "shell": { + "init_hook": [ + // Create the virtual environment, or recreate it when it was made + // by another version installed by Devbox. Leave any other one alone. + // Hooks run with `set -e`, hence `|| true` when there is no venv yet. + "_devbox_venv_home=\"$(sed -n 's/^home = //p' \"$VENV_DIR/pyvenv.cfg\" 2>/dev/null || true)\"", + "case \"$_devbox_venv_home\" in \"{{ .InstallDir }}/bin\") ;; \"{{ .CacheDir }}/python/\"*) \"{{ .InstallDir }}/bin/python3\" -m venv --clear \"$VENV_DIR\" ;; *) [ -e \"$VENV_DIR\" ] || \"{{ .InstallDir }}/bin/python3\" -m venv \"$VENV_DIR\" ;; esac", + "if [ \"$(sed -n 's/^home = //p' \"$VENV_DIR/pyvenv.cfg\" 2>/dev/null)\" = \"{{ .InstallDir }}/bin\" ]; then . \"$VENV_DIR/bin/activate\"; else echo \"devbox: not activating $VENV_DIR because it was not created from python {{ .Version }}; remove it to have Devbox recreate it\" >&2; fi", + "unset _devbox_venv_home" + ] + } +} diff --git a/pkgs/rust/rust.pkg.jsonc b/pkgs/rust/rust.pkg.jsonc new file mode 100644 index 00000000000..f20259cd491 --- /dev/null +++ b/pkgs/rust/rust.pkg.jsonc @@ -0,0 +1,51 @@ +// Package definition for Rust. Rust's own installer is rustup, which also +// provides `rustup target add` and `rustup component add`, so this +// definition installs rustup-init (verified against static.rust-lang.org) +// and has it install the requested toolchain. rustup verifies the +// toolchain against the channel manifest. +// +// Toolchains live in one shared RUSTUP_HOME in the Devbox cache, and +// RUSTUP_TOOLCHAIN selects the version, so switching versions in +// devbox.json switches the toolchain. RUSTUP_TOOLCHAIN takes precedence +// over a rust-toolchain.toml file. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "rust", + "description": "The Rust toolchain (rustc, cargo, rustfmt, clippy) managed by rustup", + "homepage": "https://www.rust-lang.org", + "license": "MIT OR Apache-2.0", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + // Rust target triples. + "os_map": { "darwin": "apple-darwin", "linux": "unknown-linux-gnu" }, + "arch_map": { "x86_64": "x86_64", "aarch64": "aarch64" }, + + // Every stable release is a GitHub release tagged "1.82.0". + "versions": { "type": "github_releases", "repo": "rust-lang/rust", "strip_prefix": "" }, + + // The rustup version is pinned so that installs are reproducible; it + // does not limit which toolchains can be installed. + "install": { + "type": "binary", + "url": "https://static.rust-lang.org/rustup/archive/1.29.2/{{.Arch}}-{{.OS}}/rustup-init", + "checksum": { "type": "sha256_url", "url": "{{.URL}}.sha256" }, + "bin_name": "rustup-init" + }, + + "hooks": { + "post_install": [ + // Installs rustup and its proxies (cargo, rustc, ...) into bin/ and + // the toolchain into the shared RUSTUP_HOME. + "RUSTUP_HOME=\"{{ .CacheDir }}/rustup\" CARGO_HOME=\"$DEVBOX_PKG_INSTALL_DIR\" RUSTUP_INIT_SKIP_PATH_CHECK=yes ./bin/rustup-init -y --no-modify-path --profile minimal --component rustfmt,clippy --default-toolchain {{.Version}}" + ] + }, + + "bin": ["bin"], + "binaries": ["rustup", "rustc", "cargo", "rustfmt", "cargo-clippy"], + + "env": { + "RUSTUP_HOME": "{{ .CacheDir }}/rustup", + "RUSTUP_TOOLCHAIN": "{{ .Version }}" + } +} diff --git a/pkgs/terraform/terraform.pkg.jsonc b/pkgs/terraform/terraform.pkg.jsonc new file mode 100644 index 00000000000..bf6eeca32fe --- /dev/null +++ b/pkgs/terraform/terraform.pkg.jsonc @@ -0,0 +1,29 @@ +// Package definition for Terraform, installed from releases.hashicorp.com. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "terraform", + "description": "Infrastructure as code tool", + "homepage": "https://www.terraform.io", + "license": "BUSL-1.1", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + + "versions": { + "type": "http_json", + "url": "https://releases.hashicorp.com/terraform/index.json", + "select": ".versions | keys[]" + }, + + "install": { + "type": "archive", + "url": "https://releases.hashicorp.com/terraform/{{.Version}}/terraform_{{.Version}}_{{.OS}}_{{.Arch}}.zip", + "checksum": { + "type": "sha256_url", + "url": "https://releases.hashicorp.com/terraform/{{.Version}}/terraform_{{.Version}}_SHA256SUMS" + } + }, + + "bin": ["."], + "binaries": ["terraform"] +} diff --git a/pkgs/uv/uv.pkg.jsonc b/pkgs/uv/uv.pkg.jsonc new file mode 100644 index 00000000000..633c053cfda --- /dev/null +++ b/pkgs/uv/uv.pkg.jsonc @@ -0,0 +1,27 @@ +// Package definition for uv, installed from its GitHub releases. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "uv", + "description": "An extremely fast Python package and project manager", + "homepage": "https://docs.astral.sh/uv", + "license": "MIT OR Apache-2.0", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + // Rust target triples. + "os_map": { "darwin": "apple-darwin", "linux": "unknown-linux-gnu" }, + "arch_map": { "x86_64": "x86_64", "aarch64": "aarch64" }, + + // Tags have no "v" prefix. + "versions": { "type": "github_releases", "repo": "astral-sh/uv", "strip_prefix": "" }, + + "install": { + "type": "archive", + "url": "https://github.com/astral-sh/uv/releases/download/{{.Version}}/uv-{{.Arch}}-{{.OS}}.tar.gz", + "checksum": { "type": "sha256_url", "url": "{{.URL}}.sha256" }, + "strip_components": 1 + }, + + "bin": ["."], + "binaries": ["uv", "uvx"] +} diff --git a/pkgs/yarn/yarn.pkg.jsonc b/pkgs/yarn/yarn.pkg.jsonc new file mode 100644 index 00000000000..8634ccc1fb8 --- /dev/null +++ b/pkgs/yarn/yarn.pkg.jsonc @@ -0,0 +1,54 @@ +// Package definition for Yarn, installed from the npm registry. Yarn is +// JavaScript, so the project also needs nodejs. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "yarn", + "description": "Package manager for JavaScript", + "homepage": "https://yarnpkg.com", + "license": "BSD-2-Clause", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + + // Yarn 2 and later ("berry") is published as @yarnpkg/cli-dist; Yarn 1 + // ("classic") stays in the yarn package. + "versions": [ + { + "type": "http_json", + "url": "https://registry.npmjs.org/@yarnpkg/cli-dist", + "select": ".versions | keys[]" + }, + { + "type": "http_json", + "url": "https://registry.npmjs.org/yarn", + "select": ".versions | keys[]" + } + ], + + "install": { + "type": "archive", + "url": "https://registry.npmjs.org/@yarnpkg/cli-dist/-/cli-dist-{{.Version}}.tgz", + "checksum": { + "type": "http_json", + "url": "https://registry.npmjs.org/@yarnpkg/cli-dist/{{.Version}}", + "select": ".dist.integrity" + }, + "strip_components": 1, + "version_overrides": [ + { + "versions": "<2", + "url": "https://registry.npmjs.org/yarn/-/yarn-{{.Version}}.tgz", + "checksum": { + "type": "http_json", + "url": "https://registry.npmjs.org/yarn/{{.Version}}", + "select": ".dist.integrity" + } + } + ] + }, + + "bin": ["bin"], + // The berry tarball ships bin/yarn without the executable bit; listing + // it here has Devbox set it. + "binaries": ["yarn"] +} diff --git a/pkgs/zig/zig.pkg.jsonc b/pkgs/zig/zig.pkg.jsonc new file mode 100644 index 00000000000..0f4f106a4eb --- /dev/null +++ b/pkgs/zig/zig.pkg.jsonc @@ -0,0 +1,35 @@ +// Package definition for Zig, installed from ziglang.org. +// Format: see specs/package-definitions.md. +{ + "schema": 1, + "name": "zig", + "description": "General-purpose programming language and toolchain", + "homepage": "https://ziglang.org", + "license": "MIT", + + "platforms": ["aarch64-darwin", "x86_64-darwin", "aarch64-linux", "x86_64-linux"], + "os_map": { "darwin": "macos", "linux": "linux" }, + "arch_map": { "x86_64": "x86_64", "aarch64": "aarch64" }, + + // index.json is keyed by version, plus a "master" entry for nightlies. + "versions": { + "type": "http_json", + "url": "https://ziglang.org/download/index.json", + "select": "keys[] | select(. != \"master\")" + }, + + // The index has the tarball URL and its sha256 per platform. Tarball + // names changed in 0.14 ("zig-macos-aarch64" became + // "zig-aarch64-macos"), which the lookup absorbs. + "install": { + "type": "archive", + "lookup": { + "url": "https://ziglang.org/download/index.json", + "select": ".[\"{{.Version}}\"][\"{{.Arch}}-{{.OS}}\"] // empty | {url: .tarball, checksum: .shasum}" + }, + "strip_components": 1 + }, + + "bin": ["."], + "binaries": ["zig"] +} diff --git a/specs/package-definitions.md b/specs/package-definitions.md index 7bb998ec1ec..bfe69b92556 100644 --- a/specs/package-definitions.md +++ b/specs/package-definitions.md @@ -8,8 +8,9 @@ lands. Let a Devbox project install a tool from the upstream vendor's own binary distribution (go.dev, nodejs.org, GitHub releases, ...) instead of nixpkgs, -driven by a small declarative *package definition* file. Go is the first -package (see [`pkgs/go/go.pkg.jsonc`](../pkgs/go/go.pkg.jsonc)). Later, the +driven by a small declarative *package definition* file. Go was the first +package (see [`pkgs/go/go.pkg.jsonc`](../pkgs/go/go.pkg.jsonc)); the initial +set of 16 is listed under [Shipped packages](#shipped-packages). Later, the search service returns these definitions so that `devbox add go@1.22` transparently picks the vendor build. @@ -130,25 +131,38 @@ hatch. | `description`, `homepage`, `license` | no | Informational. `homepage` is shown by `devbox info`. | | `platforms` | yes | Non-empty list of nix system names the definition supports. | | `os_map`, `arch_map` | no | Overrides for the `{{.OS}}` and `{{.Arch}}` template variables. Keys are the nix OS (`darwin`, `linux`) and nix arch (`x86_64`, `aarch64`). | -| `versions` | yes | A version source. See below. | +| `versions` | yes | A version source, or a list of them. See below. | | `install` | yes | An install strategy. See below. | | `bin` | no | Directories relative to the install root that go on `PATH`. Default `["bin"]`. | -| `binaries` | no | Executable names expected on `PATH` after install. | +| `binaries` | no | Executable names expected in a `bin` directory after install. Install marks them executable (npm tarballs drop the bit) and fails if one is missing. | | `env`, `shell`, `create_files` | no | Plugin-compatible activation. See Activation. | -| `hooks.post_install` | no | Shell commands run once per installed version. | +| `hooks.post_install` | no | Shell commands run once per installed version by `/bin/sh` in the install root, with `DEVBOX_PKG_INSTALL_DIR`, `DEVBOX_PKG_VERSION` and `DEVBOX_PKG_SYSTEM` set and `/usr/bin:/bin` appended to `PATH`. | ### Version sources (`versions`) | `type` | Fields | Behaviour | |---|---|---| | `static` | `versions: [string]` | The list is the universe of versions. | -| `http_json` | `url`, `select` | GET `url`, parse JSON, evaluate the jq expression in `select`. Every string (or number) it emits is a version. | -| `github_releases` | `repo` (`owner/name`), `strip_prefix` (default `"v"`) | List releases through the GitHub API, skip drafts and pre-releases, strip the prefix from tag names. Honors `GITHUB_TOKEN`. | +| `http_json` | `url`, `select` | GET `url`, parse JSON, evaluate the jq expression in `select`. Every string (or number) it emits is a version. When `url` contains `{{.Page}}`, pages 0, 1, ... are fetched until one returns 404 or no versions. | +| `github_releases` | `repo` (`owner/name`), `strip_prefix` (default `"v"`) | List releases through the GitHub API, 100 per page, skip drafts and pre-releases, strip the prefix from tag names. Honors `GITHUB_TOKEN`. | | `script` | `run` | Planned. Run `run` with `sh -c`; one version per output line. | +`versions` may also be a list of sources, whose versions are combined. Yarn +needs this: 1.x is the `yarn` npm package and 2.x onward is +`@yarnpkg/cli-dist`. + +Paginated sources (`github_releases`, and `http_json` with `{{.Page}}`) are +read lazily: resolution stops at the first page that has a version matching +the request, so `latest` costs one request and `kubectl@1.30` reads as many +pages as it takes to reach 1.30. Sources should list newest versions first. +At most 20 pages are read. + Version strings are compared as dotted numeric segments, falling back to -string comparison. Versions containing a hyphen (`1.23.0-rc1`) are treated as -pre-releases and only match when requested exactly. +string comparison. Build metadata after `+` (OpenJDK's `21.0.5+11`) only +breaks ties, so a vendor respin (`21.0.12+101`) sorts after the original +build (`21.0.12+8`). Versions whose part before `+` contains a hyphen or a +letter (`1.23.0-rc1`, `3.14.0a1`) are pre-releases and only match when +requested exactly. Matching a requested version against the list: @@ -157,21 +171,77 @@ Matching a requested version against the list: | `latest` | The highest non-pre-release version. | | `1.22` | The highest version equal to `1.22` or starting with `1.22.`. | | `1.22.3` | That version exactly (also by the prefix rule, so `1.22.3` matches `1.22.3.1`-style versions if the exact one is absent). | +| `21.0.5` | The highest `21.0.5+`. | ### Install strategies (`install`) -`install.type` picks how a version lands on disk. `install.overrides` is a -map from nix system to a partial `install` object whose fields replace the -defaults for that system (for example a different `url` or `strip_components` -on one platform). +`install.type` picks how a version lands on disk. | `type` | Fields | Behaviour | |---|---|---| -| `archive` | `url`, `checksum`, `strip_components` (default `0`) | Download the archive, verify it, extract it into the install root. Supports `.tar.gz`, `.tgz`, `.tar.xz`, `.tar.bz2`, `.tar.zst` and `.zip`. | -| `binary` | `url`, `checksum`, `bin_name` (default: `name`) | Download a single executable to `/bin/` and mark it executable. | +| `archive` | `url` and `checksum`, or `lookup`; `strip_components` (default `0`) | Download the archive, verify it, extract it into the install root. Supports `.tar.gz`, `.tgz`, `.tar.xz`, `.tar.bz2`, `.tar.zst` and `.zip`. | +| `binary` | `url` and `checksum`, or `lookup`; `bin_name` (default: `name`) | Download a single executable to `/bin/` and mark it executable. | | `manager` | `requires`, `env`, `install`, `locate` | Planned. Delegate to another definition that owns version management (see below). | | `script` | `run` | Planned. Escape hatch: run `run` with the install root as `$DEVBOX_PKG_INSTALL_DIR`. | +#### Looking up the artifact in an index (`install.lookup`) + +Some vendors' download names can't be derived from the version: +python-build-standalone puts a build date in every file name, Temurin renames +respins (`21.0.12+101` ships as `..._21.0.12.1_1.tar.gz`), and zig renamed +its tarballs in 0.14. Their indexes list the exact URL and checksum instead. +`lookup` replaces `url` and `checksum`: + +```jsonc +"install": { + "type": "archive", + "lookup": { + "url": "https://ziglang.org/download/index.json", + "select": ".[\"{{.Version}}\"][\"{{.Arch}}-{{.OS}}\"] // empty | {url: .tarball, checksum: .shasum}" + } +} +``` + +`url` and `select` are templates. `select` is a jq expression that must +produce an object with `url` and, optionally, `checksum` (any digest format +below). If it produces nothing, or `url` returns 404, the vendor has no +artifact of that version for that system. + +#### Overrides + +`install.overrides` maps a nix system to a partial `install` object whose +fields replace the defaults for that system (for example a different +`strip_components` for macOS bundles). + +`install.version_overrides` is a list of partial `install` objects for vendors +that changed how they ship artifacts. Each has a `versions` constraint: one or +more comparisons with `<`, `<=`, `>`, `>=`, `=` or `!=`, separated by spaces +or commas, that must all hold (`"<11"`, `">=2, <4"`). A bare version matches +itself and every more specific version. The first entry that matches applies. +An entry may have its own `overrides` per system but no nested +`version_overrides`. + +```jsonc +"install": { + "type": "archive", + "url": "https://github.com/pnpm/pnpm/releases/download/v{{.Version}}/pnpm-{{.OS}}-{{.Arch}}.tar.gz", + "checksum": { "type": "github_release", "repo": "pnpm/pnpm" }, + "version_overrides": [ + { + "versions": "<11", + "url": "https://registry.npmjs.org/@pnpm/{{ .OS | replace \"darwin\" \"macos\" }}-{{.Arch}}/-/...", + "checksum": { "type": "http_json", "url": "...", "select": ".dist.integrity" }, + "strip_components": 1 + } + ] +} +``` + +Overrides apply in this order: the matching version override, the +definition's override for the system, then the version override's own +override for the system. `url` and `lookup` are alternatives, so an override +that sets one clears the other. + Why two real strategies: `archive` covers more than it first appears. Go, nodejs, deno, bun, zig, Temurin JDK and python (via python-build-standalone, the same builds uv installs) all ship per-platform archives, so fnm, nvm and @@ -179,6 +249,11 @@ pyenv are not needed for them. `manager` is for ecosystems where the vendor ships no usable archive or the manager is the ecosystem's contract: rustup for rust, ruby (no official binaries), php on Linux. +Until `manager` exists, the shipped `rust` definition gets the same result +with `binary` plus a `post_install` hook: it installs the checksummed +`rustup-init` and runs it to install the requested toolchain into a shared +`RUSTUP_HOME`, and its `env` sets `RUSTUP_TOOLCHAIN` to the version. + A manager-backed definition will look like this once implemented: ```jsonc @@ -217,14 +292,33 @@ verification through sum.golang.org, so it stays an alternate definition. | `type` | Fields | Behaviour | |---|---|---| -| `http_json` | `url`, `select` | GET `url`, evaluate the jq expression; the first string result is the hex sha256. | +| `http_json` | `url`, `select` | GET `url`, evaluate the jq expression; the first string result is the digest. Covers JSON indexes and the npm registry's `dist.integrity`. | | `sha256_url` | `url` | GET `url`. The body is either a bare hex digest or a `sha256sum`-style list (` ` per line) matched by `{{.Filename}}`. Covers nodejs `SHASUMS256.txt` and the common `.sha256` convention. | +| `sha512_url` | `url` | Like `sha256_url` for sha512 digests (Apache's `.sha512` files). | +| `github_release` | `repo`, `tag` (default `v{{.Version}}`) | The digest GitHub computes for every release asset, read through the GitHub API (honors `GITHUB_TOKEN`). Assets uploaded before GitHub started computing digests (mid 2025) have none, which is treated like `none`. | | `none` | | No vendor-published checksum. `devbox add` records the digest it observes for the current system (trust on first use). Other systems are filled in lazily when a machine of that system installs, matching how nix store paths are backfilled today. | +Digests may be sha256 or sha512, as hex (the algorithm follows from the +length), as `sha256:`/`sha512:`, or as an SRI string +(`sha512-`). + Checksums are mandatory when installing from an existing lockfile entry: the -downloaded artifact must match the `sha256` recorded for the system, or the +downloaded artifact must match the digest recorded for the system, or the install fails. +#### Platforms a version is not published for + +Vendors add and drop platforms over time: Node.js had no Apple silicon +build before 16, Java 8 has none at all, Temurin 27 has no Intel macOS +build. When the checksum source shows that an artifact does not exist (the +checksum file or lookup index returns 404, a checksum list has no entry for +the file, a jq expression matches nothing, the GitHub release has no such +asset), that system is left out of the lock entry. Locking fails only when no +platform has an artifact. Installing on a system that the lock entry leaves +out fails with an error that names the platforms the version is available +for. Definitions with checksum `none` cannot tell, so their lock entries +list every platform. + ### Template variables Templates use Go `text/template` with `missingkey=error`. Available @@ -238,10 +332,13 @@ variables: | `{{.URL}}`, `{{.Filename}}` | `checksum`, `hooks` | The rendered artifact URL and its last path segment. | | `{{.InstallDir}}` | `env`, `shell`, `create_files`, `hooks` | Install root for this version and system. | | `{{.CacheDir}}` | `env`, `shell`, `hooks`, `install.env` | `$XDG_CACHE_HOME/devbox/pkgs`. | +| `{{.Page}}` | `versions.url` (`http_json`) | The 0-based page number. See Version sources. | -Functions: `trimPrefix`, `trimSuffix`, `replace`, `lower`, `upper`. Arguments -follow the sprig order with the string last, so pipelines read naturally: -`{{ .Version | trimPrefix "v" }}`, `{{ replace "." "_" .Version }}`. +Functions: `trimPrefix`, `trimSuffix`, `replace`, `lower`, `upper`, +`splitList`, plus the `text/template` builtins (`eq`, `index`, `urlquery`, +...). Arguments follow the sprig order with the string last, so pipelines read +naturally: `{{ .Version | trimPrefix "v" }}`, `{{ replace "." "_" .Version }}`, +`{{ index (splitList "." .Version) 0 }}` for the major version. ### Activation @@ -284,8 +381,11 @@ for every install strategy. refuses to install from a definition whose hash differs. For local definitions it is informational; edits are picked up through the state hash and trigger a reinstall. -- `systems.*.url` and `systems.*.sha256` are new `omitempty` fields on the - existing per-system struct. `lockfile_version` stays `1`. +- `systems.*.url`, `systems.*.sha256` and `systems.*.sha512` are new + `omitempty` fields on the existing per-system struct. `lockfile_version` + stays `1`. +- A system missing from `systems` means the vendor does not publish this + version for it. - Older Devbox binaries reject the `pkg:` package string. Projects that adopt package definitions should set `devbox_version` in `devbox.json`. @@ -295,8 +395,9 @@ cache. Garbage collection of unused versions is a follow-up (`devbox cache gc`). Each install directory carries a marker with the *install fingerprint* of -the definition: a hash of the install strategy, `bin`, `hooks`, `os_map` -and `arch_map`. When a definition's install recipe changes for a version +the definition: a hash of the install strategy that applies to the version +and system (after overrides), `bin`, `binaries`, `hooks`, `os_map` and +`arch_map`. When a definition's install recipe changes for a version that is already installed, the next install redoes it. Activation-only edits (`env`, `shell`, `create_files`) do not trigger a reinstall; they are applied at shell time. @@ -305,6 +406,36 @@ Trust-on-first-use digests are written to `devbox.lock` only by the install step (`devbox add`, `devbox install`, `devbox update`), never while computing the environment for `devbox shell` or `devbox shellenv`. +## Shipped packages + +Definitions live in `pkgs//.pkg.jsonc`, each next to a +testscript (`.test.txt`) that installs two versions and switches +between them. All of them support the four platforms; a version a vendor does +not publish for a platform is skipped for it (see above). + +| Package | Source | Versions from | Checksum | Notes | +|---|---|---|---|---| +| `go` | go.dev | go.dev JSON index | index (`http_json`) | Sets `GOTOOLCHAIN=local` like the nixpkgs go, so the pinned version runs even when go.mod asks for a newer one. | +| `nodejs` | nodejs.org | `index.json` | `SHASUMS256.txt` | Includes npm (and corepack before 25). `npm install -g` goes to the project's virtenv. | +| `pnpm` | GitHub releases (11+), npm `@pnpm/-` (<11) | GitHub releases | GitHub asset digest; npm sha512 integrity | Standalone executable with its own Node.js. `version_overrides` for the layout change in 11. | +| `yarn` | npm `@yarnpkg/cli-dist` (2+), `yarn` (1.x) | both npm packages (list of sources) | npm sha512 integrity | Needs `nodejs`. `binaries` restores the executable bit npm drops. | +| `python` | python-build-standalone | uv's `download-metadata.json` | index (`lookup`) | Creates and activates `$VENV_DIR` (default `.venv`), recreating it when the version changes, so pip never writes to the shared install. | +| `uv` | GitHub releases | GitHub releases | `.sha256` | | +| `rust` | static.rust-lang.org (rustup-init) | rust-lang/rust GitHub releases | `rustup-init.sha256`, then rustup's manifest | `post_install` runs rustup-init; toolchains share `RUSTUP_HOME` in the cache; `RUSTUP_TOOLCHAIN` selects the version. | +| `java` | Adoptium (Eclipse Temurin JDK) | Adoptium API, paged | index (`lookup`) | Versions are `21.0.5+11`. `JAVA_HOME` is set. macOS builds are bundles, hence a per-system `strip_components`. | +| `gradle` | services.gradle.org | `versions/all` | index (`http_json`) | Needs `java`. | +| `maven` | Maven Central | Maven Central search API | Apache archive `.sha512` | Needs `java`. Central has digests for only some releases. | +| `deno` | GitHub releases | GitHub releases | `.sha256sum` | Releases before 2.0.1 have no checksum (trust on first use). | +| `bun` | GitHub releases | GitHub releases | `SHASUMS256.txt` | `post_install` links `bunx`. | +| `zig` | ziglang.org | `index.json` | index (`lookup`) | | +| `terraform` | releases.hashicorp.com | `index.json` | `SHA256SUMS` | | +| `kubectl` | dl.k8s.io | kubernetes/kubernetes GitHub releases | `.sha256` | | +| `helm` | get.helm.sh | helm/helm GitHub releases | `.sha256sum` | | + +Not shipped yet: ruby and php (no official binaries; `manager`), dotnet, +erlang/elixir, and tools that are a one-line `binary` definition each (gh, +jq, just, ...). + ## Security The trust model equals plugins today: a definition is remote JSON that can @@ -367,6 +498,38 @@ flag is off, `pkg:` strings are not recognized at all. - A definition whose `platforms` exclude the current system fails at install time with a hint to use `--exclude-platform`. +### PR 4: initial package set + +- `pkgs//.pkg.jsonc` for the packages in + [Shipped packages](#shipped-packages). +- This spec: lists of version sources, paginated sources, versions with + `+build`, `install.lookup`, `install.version_overrides`, `sha512_url` and + `github_release` checksums, sha512 and SRI digests, platforms a version is + not published for, and `binaries` checks. + +### PR 5: support for the initial package set + +- `internal/pkgdef`: everything listed under PR 4, plus memoized metadata + requests so locking four platforms fetches an index once, `/bin/sh` and a + fallback `PATH` for hooks, and an install fingerprint per version. +- `lock.SystemInfo.Sha512`; lock entries omit systems without an artifact, + and installing on such a system is an error that lists where the version + is available. +- `ensureStateIsUpToDate` reloads plugin configs after installing, so a + version changed in devbox.json gets its definition's env and init hook in + the same run. + +### PR 6: tests for the initial package set + +- `pkgs//.test.txt`: install a version from devbox.json, switch + with `devbox add`, switch back by editing devbox.json, checking the + version (and env) each time. Run by `TestPackageDefinitions` when + `DEVBOX_RUN_PKGDEF_TESTS=1`, and by a CI workflow on Linux and macOS when + `pkgs/` or the implementation changes and weekly to catch vendor changes. +- Every shipped definition parses and is named after its directory (always + runs); every shipped definition resolves `latest` and locks it against the + real vendor (network, same flag). + ### Later - Search service: `/v2/resolve` returns `source: "pkg-def"` plus the @@ -389,6 +552,18 @@ flag is off, `pkg:` strings are not recognized at all. entry today because canonical names differ (`pkg:./go.pkg.jsonc` vs `go`). Decide whether it should once search returns definitions. - `devbox global` shares `computeEnv` and should work unchanged; verify. +- Vendor metadata is a dependency: python's index is a file in uv's + repository (it moved once already), Java depends on the Adoptium API, and + Maven on search.maven.org. A broken source fails `devbox add` and + `devbox update` only; locked projects keep installing from the recorded + URLs. The weekly CI run is the early warning; the search service could + serve these indexes later. +- Version lookups through the GitHub API are limited to 60 requests an hour + without `GITHUB_TOKEN`. Pagination is lazy and responses are memoized, so a + typical `devbox add` makes one or two requests. +- Hooks run with the user's environment. The rust definition depends on + rustup-init flags staying compatible; the `manager` strategy would make + that contract explicit. - Windows and `devbox generate` images are out of scope for the first cut. ## Status @@ -401,6 +576,9 @@ flag is off, `pkg:` strings are not recognized at all. | Lockfile fields `definition_hash`, `systems.*.url`, `systems.*.sha256` | in review (PR 3) | | Activation through the plugin manager (`env`, `shell`, `create_files`) | in review (PR 3) | | Trust-on-first-use checksum recording | in review (PR 3) | +| Initial package set (16 definitions) | in review (PR 4) | +| `lookup`, `version_overrides`, version source lists, pagination, sha512, `github_release` checksums, per-platform availability | in review (PR 5) | +| Per-package install and version-switch tests, CI workflow | in review (PR 6) | | `manager` strategy | not started | | `script` strategy | not started | | `github:` refs | not started |