This guide covers setting up your development environment, building the buildpack, running tests, and common development workflows for the Go-based Cloud Foundry Java Buildpack.
- Prerequisites
- Getting Started
- Project Structure
- Building the Buildpack
- Running Tests
- Development Workflow
- Local Testing with Cloud Foundry
- Packaging the Buildpack
- Debugging
- Common Tasks
Before you begin, ensure you have the following installed:
-
Go 1.21 or later - Download
go version # Should show 1.21 or higher -
Git - For version control
git --version
-
jq - For JSON processing in build scripts
jq --version
-
Docker - For running integration tests locally
docker --version
-
Cloud Foundry CLI (cf) - For testing against a real CF deployment
cf version
-
Ginkgo - Test framework (will be installed automatically by scripts)
go install github.com/onsi/ginkgo/v2/ginkgo@latest
git clone https://github.com/cloudfoundry/java-buildpack.git
cd java-buildpackThe buildpack uses Go modules with vendored dependencies. Verify that all dependencies are present:
# Check vendored dependencies
ls vendor/
# Download dependencies if needed (usually not required)
go mod downloadThe build scripts will automatically install required tools (like ginkgo) when needed:
./scripts/install_tools.shThis installs:
- Ginkgo v2 test framework
- Buildpack packager (for creating distributable packages)
Build the buildpack binaries:
./scripts/build.shThis creates executables in the bin/ directory:
bin/supply- Staging phase binary (downloads and installs dependencies)bin/finalize- Finalization phase binary (configures runtime)
java-buildpack/
├── bin/ # Phase scripts
│ ├── compile # Compile phase script
│ ├── detect # Detect phase script
│ ├── supply # Supply phase script
│ ├── release # Release phase script
│ └── finalize # Finalize phase script
├── ci/ # ci scripts
├── docs/ # Detailes docs about frameworks, development and testing
├── src/java/ # Go source code
| ├── common/ # Common code and libbuildpack context
│ ├── containers/ # Container implementations (8 types)
│ ├── frameworks/ # Framework implementations (38 types)
│ ├── hooks/ # libbuildpack hooks
│ ├── jres/ # JRE implementations (7 providers)
│ ├── supply/cli/ # Supply phase entrypoint
│ ├── finalize/cli/ # Finalize phase entrypoint
│ ├── resources/ # Resource configuration files
│ └── integration/ # Integration tests
├── scripts/ # Build and test scripts
│ ├── build.sh # Build binaries
│ ├── unit.sh # Run unit tests
│ ├── integration.sh # Run integration tests
│ └── package.sh # Package buildpack for deployment
├── vendor/ # Vendored Go dependencies
├── ARCHITECTURE.md # Detailed architecture guide
├── RUBY_VS_GO_BUILDPACK_COMPARISON.md # Ruby vs Go buildpack implementations comparison info
├── CONTRIBUTING.md # Contribution guide
├── go.mod # Go module definition
├── go.sum # Dependency checksums
├── manifest.yml # Buildpack manifest
└── VERSION # Version number
Key Go Packages:
- containers/ - Application container implementations (Tomcat, Spring Boot, etc.)
- frameworks/ - Framework integrations (APM agents, security providers, etc.)
- jres/ - JRE providers (OpenJDK, Zulu, GraalVM, etc.)
- supply/ - Staging phase logic
- finalize/ - Runtime configuration logic
Build for the default platform (Linux):
./scripts/build.shOutput:
-----> Building supply for linux
-----> Building finalize for linux
-----> Build complete
The buildpack supports building for multiple platforms defined in config.json:
{
"oses": ["linux", "windows"]
}The build script automatically builds for all configured platforms:
./scripts/build.sh
# Creates: bin/supply, bin/finalize, bin/supply.exe, bin/finalize.exeThe build uses these Go build flags:
-mod vendor- Use vendored dependencies-ldflags="-s -w"- Strip debug symbols (smaller binary size)CGO_ENABLED=0- Static linking (no external dependencies)
To build manually for development:
# Build supply
go build -mod vendor -o bin/supply src/java/supply/cli/main.go
# Build finalize
go build -mod vendor -o bin/finalize src/java/finalize/cli/main.goThe buildpack has comprehensive test coverage with unit tests and integration tests.
Run all unit tests:
./scripts/unit.shWhat it does:
- Runs all Ginkgo tests in
src/java/(excluding integration tests) - Tests containers, frameworks, JREs, and utility packages
- Fast execution (~30 seconds)
Sample output:
-----> Running unit tests
Running Suite: Containers
...
Running Suite: Frameworks
...
Ran 427 of 427 Specs in 28.543 seconds
SUCCESS! -- 427 Passed | 0 Failed | 0 Pending | 0 Skipped
-----> Unit tests complete
Using Ginkgo directly:
# Test a specific package
cd src/java
ginkgo frameworks/
# Test a specific file
ginkgo frameworks/new_relic_test.go
# Run tests matching a pattern
ginkgo --focus="NewRelic" frameworks/
# Run tests with verbose output
ginkgo -v frameworks/Integration tests require a packaged buildpack and either Docker or a Cloud Foundry deployment.
Prerequisites:
- Package the buildpack (see Packaging)
- Set
BUILDPACK_FILEenvironment variable
Run with Docker:
# Package the buildpack first
./scripts/package.sh --version dev
# Run integration tests
export BUILDPACK_FILE="${PWD}/build/buildpack.zip"
./scripts/integration.sh --platform dockerRun with Cloud Foundry:
export BUILDPACK_FILE="${PWD}/build/buildpack.zip"
./scripts/integration.sh --platform cf --stack cflinuxfs4Integration test options:
# Run in parallel (faster, uses GOMAXPROCS=2)
./scripts/integration.sh --platform docker --parallel true
# Run cached/offline tests
./scripts/integration.sh --platform docker --cached true
# Keep failed containers for debugging
./scripts/integration.sh --platform docker --keep-failed-containers
# Specify GitHub token for API rate limiting
./scripts/integration.sh --platform docker --github-token YOUR_TOKENIntegration test suites:
dist_zip_test.go- DistZip container testsframeworks_test.go- Framework detection and installationgroovy_test.go- Groovy application testsjava_main_test.go- Java Main container testsplay_test.go- Play Framework testsratpack_test.go- Ratpack testsspring_boot_test.go- Spring Boot testsspring_boot_cli_test.go- Spring Boot CLI teststomcat_test.go- Tomcat container testsoffline_test.go- Offline buildpack tests
Check test coverage:
cd src/java
go test -cover ./containers/...
go test -cover ./frameworks/...
go test -cover ./jres/...Watch for changes and re-run tests:
cd src/java
ginkgo watch -r frameworks/For detailed guidelines about setting up and running tests you can also check Testing Guide
-
Make changes to Go source files in
src/java/ -
Run unit tests to verify changes:
./scripts/unit.sh
-
Build the buildpack to ensure it compiles:
./scripts/build.sh
-
Run integration tests (optional, for significant changes):
./scripts/package.sh --version dev export BUILDPACK_FILE="${PWD}/build/buildpack.zip" ./scripts/integration.sh --platform docker
-
Test with a real application (see Local Testing)
-
Commit changes following the Contributing Guide
See Implementing Frameworks for detailed instructions.
Quick overview:
- Create
src/java/frameworks/my_framework.go - Implement the
Componentinterface - Create
src/java/frameworks/my_framework_test.go - Add configuration to
config/my_framework.yml - Register in
config/components.yml - Add documentation to
docs/framework-my_framework.md
-
Find the component:
- Containers:
src/java/containers/ - Frameworks:
src/java/frameworks/ - JREs:
src/java/jres/
- Containers:
-
Edit the Go file and its corresponding test file
-
Update configuration if needed (in
config/directory) -
Run tests:
# Test the specific component cd src/java ginkgo frameworks/my_framework_test.go # Run all unit tests cd ../.. ./scripts/unit.sh
The buildpack uses Go modules with vendored dependencies:
# Add a new dependency
go get github.com/example/package@v1.2.3
# Update dependencies
go get -u ./...
# Vendor dependencies
go mod vendor
# Verify
go mod verifyThe fastest way to test changes locally:
# 1. Build and package
./scripts/build.sh
./scripts/package.sh --version dev
# 2. Run integration tests with Docker
export BUILDPACK_FILE="${PWD}/build/buildpack.zip"
./scripts/integration.sh --platform docker
# 3. Test specific application types
./scripts/integration.sh --platform docker --focus="Spring Boot"For testing against a real Cloud Foundry deployment:
# 1. Target your CF environment
cf api https://api.your-cf.com
cf login
# 2. Package the buildpack
./scripts/package.sh --version dev
# 3. Create/update custom buildpack
cf create-buildpack java-buildpack-dev build/buildpack.zip 99 --enable
# OR update existing:
cf update-buildpack java-buildpack-dev -p build/buildpack.zip
# 4. Deploy a test application
cd /path/to/test/app
cf push my-test-app -b java-buildpack-dev
# 5. Check logs
cf logs my-test-app --recentThe Java Test Applications repository contains sample apps for testing:
git clone https://github.com/cloudfoundry/java-test-applications.git
cd java-test-applications
# Build a test app (requires Maven/Gradle)
cd web-servlet
./mvnw package
# Deploy with your custom buildpack
cf push servlet-test -b java-buildpack-dev -p target/web-servlet-1.0.0.BUILD-SNAPSHOT.warCreate a minimal package that downloads dependencies at runtime:
./scripts/package.sh --version 1.0.0Output: build/buildpack.zip (~250KB)
Create a package with all dependencies cached (no internet required at runtime):
./scripts/package.sh --version 1.0.0 --cachedOutput: build/buildpack.zip (~500MB, varies based on cached dependencies)
# Specify version
./scripts/package.sh --version 4.50.0
# Specify output location
./scripts/package.sh --version dev --output /tmp/my-buildpack.zip
# Specify stack
./scripts/package.sh --version dev --stack cflinuxfs4
# Offline with custom stack
./scripts/package.sh --version 1.0.0 --cached --stack cflinuxfs4The ci/ directory contains scripts for automated packaging:
# Package and test in CI environment
./ci/package-test.shSet the JBP_LOG_LEVEL environment variable:
cf set-env my-app JBP_LOG_LEVEL DEBUG
cf restage my-appLog levels: DEBUG, INFO, WARN, ERROR
View buildpack output during staging:
cf push my-app -b java-buildpack-dev
# Watch output in real-timeEnable remote debugging framework:
cf set-env my-app JBP_CONFIG_DEBUG '{enabled: true}'
cf restage my-app
cf ssh -N -T -L 8000:localhost:8000 my-appThen connect your IDE debugger to localhost:8000.
See Framework Debug for details.
Extract buildpack contents from a running container:
# SSH into the container
cf ssh my-app
# Check installed components
ls -la /home/vcap/app/.java-buildpack/
# View profile.d scripts (executed at startup)
cat /home/vcap/app/.profile.d/*.shKeep failed test containers for inspection:
export BUILDPACK_FILE="${PWD}/build/buildpack.zip"
./scripts/integration.sh --platform docker --keep-failed-containers
# Find the container
docker ps -a | grep failed
# Inspect the container
docker exec -it <container-id> /bin/bashRun tests with verbose output:
cd src/java
ginkgo -v frameworks/new_relic_test.go
# Add print statements in test or source code
fmt.Printf("DEBUG: value = %+v\n", someVar)-
Edit
config/my_framework.yml:version: 1.2.3 repository_root: "{default.repository.root}/my-framework"
-
Test the change:
./scripts/unit.sh ./scripts/build.sh
-
Update the config struct in
src/java/frameworks/my_framework.go:type Config struct { Enabled bool `yaml:"enabled"` Version string `yaml:"version"` NewOption string `yaml:"new_option"` // Add this }
-
Update default configuration in
config/my_framework.yml:enabled: true version: 1.+ new_option: "default_value"
-
Update tests in
src/java/frameworks/my_framework_test.go
export BUILDPACK_FILE="${PWD}/build/buildpack.zip"
cd src/integration
go test -v -run TestSpringBoot# Verify Go formatting
gofmt -d src/java/
# Format all code
gofmt -w src/java/
# Run go vet
go vet ./src/java/...
# Check for common mistakes
golint ./src/java/... # Install with: go install golang.org/x/lint/golint@latest# Remove built binaries
rm -rf bin/
# Remove packaged buildpacks
rm -rf build/
# Clean and rebuild
./scripts/build.sh# Update a specific dependency
go get github.com/cloudfoundry/libbuildpack@latest
# Update all dependencies
go get -u ./...
# Re-vendor
go mod tidy
go mod vendor
# Test everything still works
./scripts/unit.sh- Implementing Frameworks - Learn how to add new framework support
- Implementing Containers - Learn how to add new container types
- Testing Guide - Comprehensive testing patterns and best practices
- Contributing Guidelines - Contribution standards and code style
- Architecture Overview - Deep dive into buildpack architecture
- Documentation:
docs/directory contains comprehensive guides - Issues: GitHub Issues
- Slack: Cloud Foundry Slack - #buildpacks channel
- Mailing List: cf-dev mailing list
Install Ginkgo:
go install github.com/onsi/ginkgo/v2/ginkgo@latest
export PATH="${PATH}:${HOME}/go/bin"Set the environment variable:
export BUILDPACK_FILE="${PWD}/build/buildpack.zip"Ensure dependencies are vendored:
go mod vendor
go mod verify- Rebuild binaries:
./scripts/build.sh - Check Go formatting:
gofmt -d src/java/ - Run tests with verbose output:
cd src/java && ginkgo -v - Check for missing configuration in
config/files
Increase Docker resources (memory/CPU) or run tests serially:
./scripts/integration.sh --platform docker --parallel false