One toolchain across the estate. The tree carried 1.26.0, .1, .3, .4 and .5
at once, so which compiler built a binary depended on which repo it came
from.
Co-authored-by: Hanzo Dev <[email protected]>
The runtime base named neither, and BuildKit refuses that at parse time
— reference.ErrObjectRequired, reported as `object required` — before it
opens a socket. It reads like an unreachable registry and is not one,
which is a long way to walk from the message.
Digests pin what the build actually gets; the tags stay so the reference
still says which image it is.
Co-authored-by: Hanzo Dev <[email protected]>
REPO_OWNER/REPO_NAME built the GitHub API and download URLs from
railwayapp/railpack, so the installer could never find a release under
this fork -- hanzoai/pack has never had one.
REPO_NAME did double duty as both the repository slug (used in the API
and download URLs, needs to be "pack") and the installed command name
(used by `which`/`rm` on uninstall, needs to stay "railpack", the actual
compiled binary name per .goreleaser.yml). Collapsing both into "pack"
would make uninstall run `which pack`, which on many machines resolves to
the unrelated Cloud Native Buildpacks CLI. Split it into REPO_NAME (repo
slug and archive-name prefix, matching the project_name pinned in
.goreleaser.yml) and a new BINARY_NAME (the installed executable), and
routed each use to the right one.
RELEASE.md's GHCR links and package-registry URLs updated to match.
Do not merge until hanzoai/pack has an actual release: push a v* tag once
the prior two commits are on main and the release workflow has published
the builder, runtime, and frontend images. Before that, the download URL
this script builds 404s by construction.
Co-authored-by: Hanzo Dev <[email protected]>
RailpackBuilderImage and RailpackRuntimeImage named
ghcr.io/railwayapp/railpack-{builder,runtime}, so every `pack build`
pulled the upstream vendor's images instead of our own. Point them at
ghcr.io/hanzoai/pack-{builder,runtime}, published by the release
pipeline as of the prior commit.
Also updates the local-dev mirrors of the same reference: the
mise-builder-shell task, the run-with-frontend script, the mise clean
task's image cleanup list, and a stale comment in buildkit/frontend.go.
Snapshots refreshed with the project's own task
(UPDATE_SNAPS=true go test -short ./...) and inspected line by line:
every changed line is exactly the builder/runtime image string, nothing
else. The pre-existing, unrelated php-vanilla frankenphp version drift
that the same refresh also touches was left untouched here.
Do not merge until a release tag has gone through the pipeline from the
prior commit and ghcr.io/hanzoai/pack-builder and pack-runtime actually
exist -- until then this is a correct diff sitting ahead of its own
prerequisite, not something to land on main.
Co-authored-by: Hanzo Dev <[email protected]>
PRODUCTION_GITHUB_REPOSITORY was still hardcoded to the upstream org/repo,
so every derived name (builder, runtime, frontend image) resolved under
ghcr.io/railwayapp/*. Flipping this one knob is enough: the existing
template arithmetic in mise.ci.toml already produces
ghcr.io/hanzoai/pack-builder, ghcr.io/hanzoai/pack-runtime, and
ghcr.io/hanzoai/pack-frontend from it, matching how the rest of the repo
(hanzo.yml, README, go.mod) already names this project.
Also pins goreleaser's project_name explicitly instead of leaving it to
implicit directory-name inference, since install.sh's download URL has to
agree with whatever name goreleaser gives its release archives. Removes an
unused IMAGE_NAME env var left over in the publish-runtime job.
This does not yet change what `pack build` pulls at runtime (core/generate
and core/plan still point at railwayapp) or install.sh (hanzoai/pack has no
releases yet) -- both wait on this pipeline actually publishing once.
Co-authored-by: Hanzo Dev <[email protected]>
Rewrites our own comments, help text, and example READMEs that named
Railway or linked railway.app so they describe what they do instead.
Retargets the changelog generator and RELEASE.md's Actions/release
links at this repo. Leaves upstream issue links, the MIT copyright,
and the builder/runtime/frontend image references untouched -- those
still point at ghcr.io/railwayapp because we don't publish our own
copies of those images yet.
Co-authored-by: Hanzo Dev <[email protected]>
- Migrate module path to github.com/hanzoai/pack across all Go packages
- Remove legacy upstream docs and competitor brand references
- Modernize LLM.md and mise.toml for pristine Hanzo Pack builder
Non-test file http_check.go uses StringOrArray, which lived in run_test.go.
go vet compiles test files, masking the build failure until a real
go build runs. Extract the decoder type and update README to Hanzo Pack.
recipe_test.go is package main at the repo root and calls Detect, fileset
and Plan.Recipe. None of those exist in the tree, and it imports
moby/docker-image-spec, which go.sum no longer carries, so go vet ./...
stops at the root package before it type-checks anything:
recipe_test.go:10:2: missing go.sum entry for module providing package
github.com/moby/docker-image-spec/specs-go/v1
Without the file the root is not a package and go vet ./... is clean.
Co-authored-by: Hanzo Dev <[email protected]>
The nine commits here were a second answer to a question railpack has answered
across 1,077 — and the half we would have had to write and then keep right is
the long tail: every language, every package manager, every version file, every
lockfile dialect. That tail IS the product. Writing it again is how you get a
builder that handles the three languages you tested and mis-detects the fourth
in silence.
Merged rather than force-pushed. The original commits stay in the history as
ancestors instead of being overwritten, and the forge's pre-receive hook was
right to refuse the force — unrelated history landing on main is exactly what
that protection is for. Its own detect.go, recipe.go and build.go go, because
upstream's are the ones that will be maintained.
Upstream is a remote. This stays mergeable, and anything that could go upstream
should: a new language or a fixed detector belongs in a PR to railpack, where
more people keep it right than us.
MIT, upstream's, unchanged. The code is theirs and the fork is ours; relicensing
it under a house licence would be a claim about provenance that is not true.
Co-authored-by: Hanzo Dev <[email protected]>
There was an implementation here: nine commits, four Go files, its own detect.go
and recipe.go. It was a second answer to a question railpack has been answering
for 1,077 commits — and the half we would have had to write and then keep right
is the long tail. Every language, every package manager, every version file,
every lockfile dialect. That tail is the entire product, and writing it again is
how you get a builder that handles the three languages you tested and silently
mis-detects the fourth.
Upstream is MIT and stays MIT. The code is theirs, the fork is ours, and
relicensing someone's MIT work under a house licence is a claim about provenance
that is not true.
The nine commits are kept as the `pre-fork` branch, not deleted.
`upstream` is a remote, so this stays mergeable. Local changes should be the ones
that cannot go upstream — our registry, our fabric, our brand. A new language or
a fixed detector belongs in a PR to railpack, maintained by more people than us.
Co-authored-by: Hanzo Dev <[email protected]>
* docs: document remote Docker and BuildKit setup for local development
Generated-by: aiautocommit
* chore: ignore local mise configuration file
Generated-by: aiautocommit
* docs(guides): explain why to use a remote Docker host
Generated-by: aiautocommit
* docs(config): update environment variables documentation
- Document how Railpack reads environment variables from its process environment
- Relocate and update description for `RAILPACK_DISABLE_CACHES`
- Fix table formatting and minor phrasing
Generated-by: aiautocommit
* docs: clarify remote docker host guide for local development
Generated-by: aiautocommit
* docs: update BuildKit setup instructions to use docker command
Generated-by: aiautocommit
Every failure exited 1, so callers could only retry blindly:
deterministic failures (no provider detected, missing start command)
were run and logged three times by builderv3 before giving up.
Transient failures are now typed as mise.TemporaryError and map to exit
75 (EX_TEMPFAIL): transport errors, 429, 408, 5xx, and truncated
bodies. A 404 for a pinned mise version and a malformed archive stay
deterministic, since retrying them cannot succeed. The mise download
also had no response status check at all, so a 5xx body was written to
disk and later failed as "binary not found in archive".
Carrying that across the process boundary meant giving
GenerateBuildPlan an error return: a mise install failure was
previously swallowed into BuildResult{Success: false} and exited 1
along with every app-level failure. The error is non-nil only for
transient failures, so !Success stays on exit 1.
The download gets an http.Client timeout, since http.DefaultClient has
none and a stalled connection would hang the build forever. It
deliberately does not retry in-process: the caller already retries the
whole command, and retrying here would multiply that and delay the
signal.
Also writes --info-out even when the build result is unsuccessful, so
callers can read {"success": false, "logs": [...]} instead of scraping
stdout.
* fix: drop empty env entries instead of keeping explicit empty values
NAME= entries are now dropped from the environment entirely. Hosted
builders pass every variable as NAME=value including empties, and
BuildKit cannot mount an empty env secret, so keeping empty variables
produced plans referencing secrets the builder never registers
(failed to solve: secret X not found).
Bare NAME entries still inherit from the process environment for local
CLI use. NAME= must never inherit: variable names are user-controlled
and would read values out of the build daemon's environment.
* trim comments
* docs: reword empty-value comment to describe behavior only
* single-line comment
* fix(mise): enable safe mode when reading app config
Harden host-side version resolution by loading app mise config in safe mode, keeping it inert during analysis.
* chore(mise): tighten safe-mode comment
* Allow multiline values in environment parsing by using the dotall regex flag.
* Distinguish between explicit empty values (`NAME=`) and process environment inheritance (`NAME`).
* Add comprehensive test coverage for environment parsing, getters, and prefix matching.
Generated-by: aiautocommit
* refactor: add io.Writer parameter to pretty print functions and JSON support
Generated-by: aiautocommit
* chore: promote tidwall/pretty from indirect to direct dependency
Generated-by: aiautocommit
* feat(cli): add show-plan flag to info command
- Add `--show-plan` flag to optionally render the generated build plan in the info command output.
- Update section header printing functions to accept an `io.Writer` destination for consistent output formatting.
Generated-by: aiautocommit
* fix: update mise to 2026.8.3
* test: update snapshots
* chore: update go, mise, and bun packages
* test: update mise lock with latest version
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
github.com has no runner for hanzo-build-linux-amd64, so a caller under
.github/workflows is a gate that can never be scheduled. This is the ~7-line
caller on the plane that can (git.hanzo.ai git-runner fleet), pinned @v1.
Co-authored-by: Hanzo Dev <[email protected]>
* feat(mise): support rust-toolchain.toml idiomatic version file
Generated-by: aiautocommit
* docs(rust): update rust-toolchain.toml support and resolution order
Generated-by: aiautocommit
* feat(generate): add rust support to mise toolchain settings and build plan
Generated-by: aiautocommit
* feat(mise): support dotnet idiomatic version files
- Add `global.json` to the list of recognized idiomatic configuration files.
- Enable `dotnet` in the default set of tools resolved via idiomatic version files.
Generated-by: aiautocommit
* docs(dotnet): document global.json version resolution details
Generated-by: aiautocommit
* test(dotnet): update snapshots to include dotnet in version file tools
Generated-by: aiautocommit
* chore(core): bump mise version in tanstack snapshot test
Generated-by: aiautocommit
* fix: update mise to 2026.8.0
* test: update snapshots
* chore: update go, mise, and bun packages
* test: update mise lock with latest version
* docs: updated changelog
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* feat: add generation script for latest tanstack example
Generated-by: aiautocommit
* test(examples): add tanstack-latest example project setup
Generated-by: aiautocommit
* feat(node): add helper methods to check production and dev dependencies
Generated-by: aiautocommit
* feat(logger): support absolute URLs in DocsURL and suggestions
Generated-by: aiautocommit
* feat(node): exclude TanStack Start packages from Vite static build
Generated-by: aiautocommit
* feat(node): add support for TanStack Start srvx fallback
Generated-by: aiautocommit
* test(core): update test snapshots for TanStack build plan
Generated-by: aiautocommit
* docs(node): document TanStack Start detection and build behavior
Generated-by: aiautocommit
* feat(node): add TanStack Start framework support and fallback server
Generated-by: aiautocommit
- Add changelog generation task to fetch releases from GitHub and format them for documentation
- Integrate changelog into reference navigation and upgrade script
- Update search highlight colors to purple matching active state
Generated-by: aiautocommit
* Respect configured deploy inputs for config steps
When a config-defined step already has an explicit deploy input, Railpack should not append the implicit deploy output for the entire step. This preserves include filters such as Next.js standalone output selections and avoids copying the full build step into the final image.
Constraint: Config steps without explicit deploy inputs still need the existing implicit deploy output behavior.
Rejected: Change deploy input filter semantics globally | too broad for this bug and risks provider output behavior.
Confidence: medium
Scope-risk: moderate
Tested: go test ./core/generate -run 'TestHasInputForStep|TestGenerateContextKeepsConfiguredDeployInputFilters'
Tested: go test ./core/generate -run TestHasIncludeForStep
Tested: go test ./core/generate
Tested: git diff --check
* fix(generate): respect explicitly configured deploy inputs without implicit steps
- Prevent implicit deploy step outputs from being added when explicit deploy inputs are configured without a spread operator.
- Add comprehensive test coverage for various deploy input and output combinations.
Generated-by: aiautocommit
* feat(examples): respect configured deploy inputs
- Add `/usr/games` to runtime paths to support Debian's cowsay installation location in the config example.
- Assert that unrelated files do not leak through implicit deploy outputs.
Generated-by: aiautocommit
* refactor(generate): check configured deploy inputs for step
Generated-by: aiautocommit
---------
Co-authored-by: Michael Bianco <[email protected]>
* docs: add claude ref to agents
* refactor: simplify DockerignoreContext passing and make it required
* refactor: another take at fixing the initial docker ignore load stuff
* style: whitespace formatting tweaks
I'm not sure how it got into this state :/
* fix: apply dockerignore rules when copying local app context, not later in the build process
* fix: merge dockerignore excludes with top-level railpack.json excludes/includes
* test: top-level railpack.json exclude/include
* docs: update excluding files docs
* test: add negation to an existing dockerignore to expand test coverage
* fix: hack to determine if I'm understanding the llb exclude functionality right
* refactor: remove Includes and instead use negation operators and Excludes
* test: respect dockerignore on initial source load
- Apply exclude and include filters during the initial buildkit context load to improve local build performance
- Support top-level exclude and include configurations in railpack.json
Generated-by: aiautocommit
* feat: respect dockerignore on initial source load
- Load local source layers using the plan package directly to ensure .dockerignore patterns are respected at the buildkit level.
- Prevent unnecessary loading of ignored files like node_modules and virtual environments during local builds.
Generated-by: aiautocommit
* style: formatting updates
* docs(config): update documentation for dockerignore and exclude pattern handling
Generated-by: aiautocommit
* feat(plan): respect dockerignore on initial source load
Generated-by: aiautocommit
* refactor(core): access include rules directly from layer struct
Generated-by: aiautocommit
* feat(buildkit): respect exclude patterns on initial source load
- Apply exclude patterns at the buildkit level during local context loading to prevent transferring unnecessary large directories like node_modules or virtual environments.
Generated-by: aiautocommit
* feat: respect dockerignore on initial source load
Generated-by: aiautocommit
* feat(core): respect dockerignore on initial source load
- Expose the parsed dockerignore context via the build generate context
- Ensure build results preserve the correct dockerignore metadata and excludes on initial load
Generated-by: aiautocommit
* style(docs): fix trailing whitespace in CLAUDE.md
Generated-by: aiautocommit
* feat(core): respect dockerignore on initial source load
- Apply exclude and include filters during buildkit context setup to avoid loading large ignored directories locally.
- Support top-level exclude and include configuration in railpack.json.
Generated-by: aiautocommit
* test(plan): assert all expected empty dockerignore examples are found
Generated-by: aiautocommit
* docs(config): fix typo in excluding-files guide
Generated-by: aiautocommit
* feat(dockerignore): respect dockerignore on initial source load
- Apply exclude and include filters during Buildkit context establishment to drastically improve local build performance.
- Support top-level `exclude` and `include` rules directly in `railpack.json`.
Generated-by: aiautocommit
* docs(config): clarify directory matching behavior in excluding-files guide
Generated-by: aiautocommit
* refactor(core): remove dead dockerignore context fields
- Remove unused dockerignore context properties from build plans and options
- Clean up obsolete metadata checks in core tests
Generated-by: aiautocommit
* feat: add top-level exclude option to railpack config
Generated-by: aiautocommit
- Hide table of contents components when only a single heading exists
- Prevent initial transition flash on TOC progress indicators and dots
- Update sidebar scrollbar styling and hover behavior
Generated-by: aiautocommit
* feat(mise): support LTS version queries and improve version candidates
- Add support for resolving LTS and other non-semver version aliases in mise
- Introduce `versionQueryCandidates` to gracefully fall back from extracted semver to original version strings
- Add unit tests for LTS resolution and version query candidate generation
Generated-by: aiautocommit
* feat(node): update default Node version to lts
Generated-by: aiautocommit
* feat(mise): improve version resolution fallbacks and error messages
- Fallback to unconstrained release age queries when strict minimum age checks fail to respect pinned versions or custom configs.
- Include tried candidate versions in error messages to improve debugging when package lookups fail.
Generated-by: aiautocommit
* fix: preserve --env secrets when config defines secrets
Union env-derived secret names with merged config secrets so file-level no longer drops build-time env vars. Add a regression test that covers this merge behavior.
* refactor(core): use slices.Concat for merging secrets and update tests
Generated-by: aiautocommit
* refactor(core): inline mergeSecrets helper function
Generated-by: aiautocommit
---------
Co-authored-by: Michael Bianco <[email protected]>
- Add a dedicated Configuration Options page to explain configuration methods and build plans
- Remove redundant user-config page and update documentation links
- Add editor setup guidance for JSON5 support and schema validation in railpack.json
Generated-by: aiautocommit
* fix: update mise to 2026.7.16
* test: update snapshots
* chore: update go, mise, and bun packages
* test: update mise lock with latest version
* chore: update expected uv version in python test example
Generated-by: aiautocommit
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Michael Bianco <[email protected]>