refactor: rewrite binary-builder in Go, remove Ruby implementation#97
Open
refactor: rewrite binary-builder in Go, remove Ruby implementation#97
Conversation
- Add complete Go binary-builder CLI (cmd/binary-builder/main.go) - Add Go recipes for all 33 in-scope deps: ruby, python, node, go, php, r, jruby, nginx, openresty, nginx-static, httpd, dotnet-*, libunwind, libgdiplus, hwc, composer, bundler, rubygems, yarn, bower, pip, pipenv, setuptools, tomcat, openjdk, zulu, sapmachine, skywalking-agent, appdynamics, jprofiler-profiler, your-kit-profiler, miniconda3-py39 - Add stack config as data (stacks/cflinuxfs4.yaml) - Add parity test infrastructure (test/parity/compare-builds.sh) - Add PHP extension support (php_extensions/, internal/php/) - Fix jruby: ArtifactVersion field keeps dep-metadata version as raw JRuby version; tar czf -C packDir . produces ./-prefixed entry list - All parity tests pass on cflinuxfs4; go test ./... passes - Update AGENTS.md with parity project context and results - Pin Gemfile to ruby 3.3.6 and update Gemfile.lock
…soft - Delete all Ruby source: recipe/, lib/, bin/, spec/, cflinuxfs4/ Ruby files - Remove Ruby project files: Gemfile, Gemfile.lock, .rubocop.yml, .ruby-version, .rspec, go-version.yml - Remove Ruby-era docs: PHP-Geoip.md, PHP-Oracle.md - Remove compiled binary artifact - Update .gitignore: add cflinuxfs4/, /binary-builder; remove .rspec - Rewrite README.md and AGENTS.md for Go-only builder - Fix stacks/cflinuxfs4.yaml: replace bionic openjdk-jdk-1.8.0_242 URL with jammy Bellsoft JDK 8u442 (correct stack, latest available) - Fix stacks/cflinuxfs5.yaml: replace XXX placeholder with same jammy Bellsoft JDK 8u442 (no noble bucket exists; Bellsoft JDK 8 is binary-compatible with Ubuntu 24.04)
- (#1) output: fix Commit() using shell || operator with Runner; now runs git diff --cached --quiet first and only commits when changes are staged - (#2) fetch: add 10-minute timeout to HTTP client (was http.DefaultClient) - (#3) archive: remove unused globs parameter from Pack - (#4) simple: fix YarnRecipe mutating src.Version; use local var and set outData.Version explicitly so findIntermediateArtifact can locate the file - (#5) nginx: fix misleading comment claiming custom args are prepended - (#6) nginx: use slices.Concat instead of append onto package-level slice - (#7) passthrough: extract moveFile into internal/fileutil.MoveFile with cross-device (EXDEV) fallback; use it in both passthrough.go and main.go - (#8) go_recipe: move bootstrap URL from hardcoded constant into stack YAML (go.bootstrap_url) following the same pattern as jruby.jdk_url
- stack_test: update TestJRubyConfigCflinuxfs4 to assert 'jammy' (was stale 'bionic'); update TestJRubyConfigCflinuxfs5 to assert 'jammy' with a comment explaining there is no noble JDK bucket yet so the jammy build is used as a binary-compatible fallback - recipe_compiled_test: change newCompiledStack JDKInstallDir from /opt/java to t.TempDir()+"/java" so FakeFetcher's os.MkdirAll succeeds without root
Remove omitempty from OutDataSource.SHA256 and OutDataSource.URL so they are present even when empty, matching Ruby builder output format where these fields are always included in builds JSON.
…ages - Bump bellsoft JDK URL/sha256 from 8u442+7 to 8u452+11 (URL-encodes '+' as '%2B' to avoid HTTP 404 from java-buildpack.cloudfoundry.org) - Add libunwind_build apt group (autoconf, automake, libtool) needed by the libunwind recipe to regenerate ./configure from autotools sources
GitHub source archives only contain autotools sources (configure.ac), not the generated ./configure script, so we must run autoreconf -i first. - Install autoconf/automake/libtool from stack's libunwind_build apt group - Run autoreconf -i before ./configure - Fix dirName derivation: GitHub archives extract to libunwind-X.Y.Z (repo name + version, stripping the 'v' prefix from the tag)
Maven's polyglot plugin invokes bin/jruby as an external subprocess to build gem native extensions. That subprocess runs a shell script which needs JAVACMD to find java — but env vars set via RunWithEnv were not visible to Maven's ProcessBuilder child processes. Fix by embedding 'export JAVA_HOME=... export JAVACMD=... export PATH=...' directly in the shell command string so all descendent processes inherit them without relying on process environment inheritance through JVM. Also add jdkSubdir() helper: bellsoft JDK tarballs extract into a subdirectory (e.g. jdk8u452/) inside the install dir, so JAVA_HOME must point to that subdir rather than the install dir itself.
strings.ToLower() was lowercasing the map key to 'rserve' but the Ruby builder uses the original package name 'Rserve' (capital R) as the key in the dep-metadata sub_dependencies object. Use pkg.name directly.
…ILTER_DEP Data fixes: - jruby: upgrade 9.4.5.0 → 9.4.14.0 (src.zip, correct sha256); 9.4.5.0 fails during JRuby Lib Setup due to resolv native ext build - r: upgrade 4.3.2 → 4.4.2 with correct URL and sha256 - go 1.22.0: correct sha256 - httpd 2.4.58: sha256 is for .tar.bz2 (which the Go recipe downloads) - hwc: upgrade 2.0.0 → 106.0.0 (current release) - jprofiler-profiler: upgrade 13.0.14 → 15.0.4 (current release) New helpers: - run_dep_r: generates the 4 sub-dep data.json dirs (forecast, plumber, Rserve, shiny) that the Go R recipe reads from source-*-latest/ dirs, then calls compare-builds.sh with --sub-deps-dir - FILTER_DEP support in run_dep, run_dep_r, skip_dep so that 'DEP=httpd make parity-test' only runs that one dep Makefile: fix parity-test target to use DEP=$(DEP) ./test/parity/run-all.sh instead of calling compare-builds.sh directly (so FILTER_DEP works and all dep metadata is consistent with the test matrix)
…ENTS.md README: add a full Parity Tests section documenting how compare-builds.sh works (source pre-download, Ruby/Go container layout, output comparison table, exit outcomes, input format, and running instructions). AGENTS.md: remove the Parity Results table — it was a point-in-time snapshot that became stale and misleading as fixes were applied. Current pass/fail status is always in /tmp/parity-logs/ from actual test runs.
… behaviour FakeRunner does not execute commands so the jdk*/ subdir was never created on disk by the tar extraction step, causing jdkSubdir() to fail. Pre-create the directory in test setup so the recipe can proceed. Add hasDownload / hasDownloadContaining helpers and replace the inline download-loop assertions. Remove the JAVA_HOME env-map assertion (env vars are now embedded in the shell command string, not passed via RunWithEnv). Re-add TestJRubyRecipeUnknownVersion which was accidentally dropped.
- Move php_extensions/*.yml into internal/php/assets/ (co-locate with owning package) - Replace per-file //go:embed + hardcoded maps with a single embed.FS glob and init()-time auto-discovery keyed by filename convention - Adding a new PHP minor/major version now only requires dropping a YAML file in assets/ — no Go code changes needed - Remove --php-extensions-dir CLI flag and ExtensionsDir struct field - Update tests, README, and parity script to reflect new design
… downloads - Wire Fetcher.Download (with checksum verification) into all recipes that previously used raw wget/curl: openresty, php, bundler, go, httpd, libunwind, and all PHP extension types (pecl, pkgconfig, native, special) - Add BootstrapSHA256 and HTTPDSubDepsConfig to stack structs; populate real SHA256s for all bootstrap and sub-dep URLs in cflinuxfs4.yaml / cflinuxfs5.yaml - Replace httpd's runtime git ls-remote APR version lookup with pinned values from stack config - Add ExtractFlag helper to portile to fix tar zstd mis-detection on cflinuxfs4 - Fix libunwind dirName logic to handle both URL styles without double-prefix - Update all unit tests to wire FakeFetcher and assert on Download calls
… URL) - Use Ruby 3.4.6 and mount local buildpacks-ci working tree (writable) to fix build.rb argument mismatch with master buildpacks-ci - Remove silent-skip hatch so Ruby builder failures are real failures - Add zstd to Go builder container apt-get to fix mise Go bootstrap extraction - Filter PHP snmp mibs paths from file-list diff (Ruby builder bug workaround) - Switch libunwind parity test to release download URL with correct SHA256 - Fix your-kit-profiler dispatch in builder.rb (sub -> gsub for method name)
…e helpers - Move fileSHA256 and mustCwd into helpers.go (phase 0) - Extract GoToolRecipe in dep.go; dep/glide/godep delegate to it (phase 1) - Add RepackRecipe in repack.go; bower/yarn/setuptools/rubygems delegate to it (phase 2) - Add BundleRecipe in bundle.go; pip/pipenv delegate to it (phase 3)
…diplus/openresty/nginx - Add internal/autoconf package with Recipe build engine and Hooks (10 hook fields including BeforeDownload and AfterPack); full unit test coverage (phase 5.1–5.2) - Extract removeNginxRuntimeDirs helper in nginx.go (phase 4.1) - Migrate libunwind, libgdiplus, openresty, nginx/nginx-static to delegate to autoconf.Recipe via thin wrapper structs; remove buildNginxVariant (phase 5.3–5.6)
- Add shared recipe abstractions table (GoToolRecipe, RepackRecipe, BundleRecipe, autoconf.Recipe, PassthroughRecipe) - Add full autoconf.Recipe usage example with thin wrapper pattern - Add hook reference table with defaults and typical overrides
…tack YAML Replace the three scattered bootstrap sections (ruby_bootstrap, go.bootstrap_*, jruby.jdk_*) with a single bootstrap: key containing go/jruby/ruby sub-entries, all using consistent url/sha256/install_dir field names. Sort top-level YAML sections alphabetically. Update stack.go (BootstrapBinary + BootstrapConfig), all three recipe consumers, and tests to match.
Replace `git clone --depth=1` (which tracked the default branch and would break once this Go-builder branch merges to main) with `git clone --depth=1 --branch ruby-builder-final` so the parity test always uses the last known-good Ruby builder tree regardless of what main contains.
…ect parity test flags
…nd parity test fixes - cmd/binary-builder/main.go: replace --artifacts-dir/--builds-dir/--dep-metadata-dir/--skip-commit flags with --output-file; add Mode 1 (--name/--version) and Mode 2 (--source-file) input modes; write JSON summary to file instead of stdout; route all build log output to stderr - internal/recipe/r.go: use devtools::install_version (matching ruby-builder-final tag, not remotes) - test/parity/compare-builds.sh: update Go builder invocation to --output-file flag; write dep-metadata and builds-artifacts JSON from summary in the test script (mirroring build.sh); treat R artifact file list diff as WARN-only (non-deterministic transitive R packages)
tnikolova82
previously approved these changes
Mar 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR replaces the Ruby implementation of
binary-builderwith a full Go rewrite that achieves parity with the original for all cflinuxfs4 dependencies.Related
Motivation
The Ruby binary-builder was tightly coupled to the cflinuxfs3/cflinuxfs4 stack lifecycle and increasingly difficult to maintain. A Go rewrite provides:
Runner/Fetcherinterface boundarystacks/*.yaml) rather than hardcoded Ruby conditionalsRecipeinterface) that makes adding new deps a one-file changeWhat Changed
Removed
bin/,lib/,recipe/,spec/,Gemfile,.rubocop.yml)cflinuxfs4/subtree (was a copy of the root Ruby builder)Added
cmd/binary-builder/main.go— CLI entry point with dual-mode input (direct flags orsource/data.json)internal/— Go packages for every concern:recipe,runner,fetch,stack,archive,artifact,output,source,apt,portile,autoconf,compiler,gpg,php,fileutilstacks/cflinuxfs4.yaml+stacks/cflinuxfs5.yaml— all stack-specific values (apt packages, compiler paths, bootstrap URLs) as datatest/parity/— shell-based parity test harness comparing Go builder output against the Ruby builder for each deptest/exerciser/— Docker-based exerciser tests that stage and run a built artifactRecipe architecture highlights
Runnerinterface — all subprocess execution goes throughrunner.Runner;FakeRunneris used in all unit testsFetcherinterface — all HTTP calls go throughfetch.Fetcher;FakeFetcheris used in all unit testsautoconf.Recipe— shared configure/make/install abstraction used by libunwind, libgdiplus, openresty, nginxRepackRecipe— download-and-optionally-strip abstraction used by bower, yarn, setuptools, flit-core, rubygemsPassthroughRecipe— download-only abstraction used by tomcat, composer, all JVM deps, and PyPI sdist depsPyPISourceRecipe— generic PyPI sdist recipe; new deps (e.g. flit-core) are a single line inpassthrough.goif stack == "cflinuxfs4"guards anywhere in recipe logicTesting
All unit tests pass with
-race. Parity has been verified for the full cflinuxfs4 dependency set.for backwards compatibility we have create a backup brach https://github.com/cloudfoundry/binary-builder/tree/backup/main-before-go-refactor