Longhorn Build System
What I do
- •Explain the convention where
scripts/filenames automatically become Make targets - •Show how Dapper containers ensure reproducible builds across different machines
- •Provide examples of adding new build tasks without editing Makefiles
- •Document standard scripts:
build,test,validate,ci,package
When to use me
Use this skill when:
- •You need to understand how
make buildormake testactually works - •You're adding a new build task and want to follow Longhorn conventions
- •You're debugging build issues and need to understand the Dapper environment
- •You're new to Longhorn and wonder why Makefiles are so simple
How It Works
The Makefile Convention
All Longhorn component repos use this standard Makefile pattern:
PROJECT := longhorn-manager TARGETS := $(shell ls scripts) $(TARGETS): .dapper ./.dapper $@ .DEFAULT_GOAL := ci
What This Means
- •Script Discovery:
TARGETS := $(shell ls scripts)dynamically discovers all files in thescripts/directory - •Target Generation: Each script filename automatically becomes a Make target via the pattern rule
$(TARGETS): - •Dapper Execution: All targets are executed inside a Dapper containerized environment (
./.dapper $@) - •Default Behavior: Running
makewithout arguments executesmake ci
Concrete Example
Given this scripts/ directory structure:
longhorn-manager/scripts/ |-- build |-- ci |-- package |-- test +-- validate
You automatically get these Make targets:
make build # Executes scripts/build in Dapper container make ci # Executes scripts/ci in Dapper container make package # Executes scripts/package in Dapper container make test # Executes scripts/test in Dapper container make validate # Executes scripts/validate in Dapper container
No need to modify the Makefile when adding new scripts - they're automatically available as targets.
Special Cases
1. make generate (longhorn-manager only)
This is a special target defined separately in the Makefile:
generate: bash k8s/generate_code.sh
Purpose: Generate Kubernetes CRDs from Go source code
Output: /longhorn-manager/k8s/crds.yaml and generated clientsets
Note: This is NOT a script in scripts/ - it's explicitly defined in the Makefile
2. Workflow-specific targets
Some repos define additional targets for CI/CD workflows:
workflow-image-build-push: buildx-machine MACHINE=$(MACHINE) PUSH='true' IMAGE_NAME=$(PROJECT) bash scripts/package
These are used by GitHub Actions but typically not invoked manually.
Usage Guidelines
Adding a New Build Task
To add a new build operation:
- •
Create a shell script in
scripts/directory:bashvim scripts/integration-test
- •
Make it executable:
bashchmod +x scripts/integration-test
- •
Use the new target:
bashmake integration-test
That's it! No Makefile changes needed.
Debugging Build Issues
If make build fails:
- •
Check if script exists:
bashls -l scripts/build
- •
Run script directly (outside Dapper for debugging):
bashbash scripts/build
- •
Check Dapper logs (run via Make):
bashmake build
- •
Inspect Dapper environment:
bashcat Dockerfile.dapper
Common Script Patterns
Most scripts follow this structure:
#!/bin/bash set -e # Exit on error cd "$(dirname "$0")/.." # Change to repo root # Script logic here # Usually calls: go build, go test, golangci-lint, etc.
Dapper Environment
What is Dapper?
Dapper is a containerized build tool that ensures reproducible builds across different developer machines by running all build commands inside a Docker container.
Environment Details
Each repo's Dockerfile.dapper defines:
- •Base image: Usually
registry.suse.com/bci/golang:1.25 - •Go version: 1.24-1.25 (via toolchain)
- •Build tools: golangci-lint, Docker CLI, buildx
- •Dependencies: Uses vendored Go modules (
GOFLAGS=-mod=vendor)
How Scripts Access Dapper
The Makefile's .dapper target automatically downloads Dapper if missing:
.dapper: @echo Downloading dapper @curl -sL https://releases.rancher.com/dapper/latest/dapper-`uname -s`-`uname -m` > .dapper.tmp @chmod +x .dapper.tmp @./.dapper.tmp -v @mv .dapper.tmp .dapper
Discovering Available Environment Variables
Dapper uses the DAPPER_ENV directive in Dockerfile.dapper to define which
host environment variables are passed through into the container. To discover
what options a repo supports:
- •Open the repo's
Dockerfile.dapper - •Find the
ENV DAPPER_ENV=...line - •Variables listed there can be set on the host and will be available inside the Dapper container
Example from longhorn-manager/Dockerfile.dapper:
ENV DAPPER_ENV="IMAGE REPO VERSION TAG TESTS DRONE_REPO DRONE_PULL_REQUEST DRONE_COMMIT_REF NO_PACKAGE ARCHS"
Common pass-through variables across Longhorn repos:
| Variable | Purpose | Supported Repos |
|---|---|---|
TESTS | Filter test cases (via -check.f) | longhorn-manager |
ARCHS | Multi-arch build targets (e.g., amd64 arm64) | longhorn-manager, longhorn-share-manager, backing-image-manager |
SKIP_TASKS | Skip specific CI stages | longhorn-engine, longhorn-instance-manager |
NO_PACKAGE | Skip Docker image packaging | longhorn-manager |
TAG / REPO / IMAGE | Image tagging and registry | Most repos |
Note: Each repo may support different variables. Always check
Dockerfile.dapper for the authoritative list.
Standard Script Behavior
scripts/build
- •Builds binaries for current architecture (amd64/arm64)
- •Outputs to
bin/directory - •Uses
CGO_ENABLED=0for static linking - •Adds version/commit metadata via
-ldflags
scripts/test
- •Runs all Go tests with
-racedetector (amd64 only) - •Generates
coverage.out - •Supports
TESTSenv var for filtering tests (longhorn-manager only):bashTESTS="TestVolumeLifeCycle" make test
- •Other repos do not currently support test filtering via environment variables
scripts/validate
- •Runs
go vet(static analysis) - •Runs
golangci-lint run --timeout=5m - •Runs
go fmtcheck (must produce no output)
scripts/ci
- •Typically chains:
build->validate->test - •This is the default target (
make=make ci)
scripts/package
- •Builds Docker images
- •Supports multi-platform builds (buildx)
- •Tags images based on branch/tag
Quick Reference
| Command | Script Executed | Purpose |
|---|---|---|
make | scripts/ci | Full CI: build + validate + test |
make build | scripts/build | Build binaries |
make test | scripts/test | Run tests |
make validate | scripts/validate | Lint and format check |
make package | scripts/package | Build Docker images |
make generate | k8s/generate_code.sh | Generate CRDs (longhorn-manager only) |
Key Takeaways
- •Convention over configuration: Script names = Make targets
- •No Makefile edits needed: Just add scripts to
scripts/ - •Dapper ensures consistency: Same build environment for everyone
- •Standard patterns: All Longhorn repos follow this convention
- •Special case:
make generateis explicitly defined (not a script)
Guidelines for AI Agents
MUST: Use make test for Verification
Final verification and CI-equivalent checks MUST use make test (or other
make targets) instead of running go test directly on the host.
Rationale: Running go test directly on the host may produce inconsistent
results due to environment differences, making troubleshooting unreliable
without a common baseline.
Why Direct go test is Discouraged
The Dapper container provides a controlled environment that eliminates variability from:
- •Go toolchain version: Host may have different Go version than CI
- •System packages and libraries: Some tests require specific native dependencies (e.g., SPDK libraries, iSCSI tools)
- •Privileged operations: Tests may need access to
/dev,/sys, loop devices, or hugepages - •Docker availability: Some test suites spin up helper containers (e.g., NFS server for backupstore tests)
- •Build flags: Dapper sets
GOFLAGS=-mod=vendorand repo-specific tags (e.g.,-tags="test qcow"in longhorn-engine) - •Race detector and timeout: Scripts enforce consistent
-race(amd64) and-timeoutsettings
SHOULD: Use go test Only for Quick Local Debugging
go test may be used for rapid local iteration during development, but:
- •Never treat
go testresults as the authoritative outcome - •Always confirm with
make testbefore considering a fix complete - •Be aware that passing
go testlocally does not guarantee CI will pass
Troubleshooting Baseline
When investigating test failures:
- •Reproduce inside Dapper first: Use
make testto confirm the failure - •Compare environments: If
go testpasses locally butmake testfails, the difference is likely environmental - •Check Dockerfile.dapper: Review what system dependencies, mounts, or privileged access the test environment requires
References
- •See any component repo's
Makefilefor the actual implementation - •See
Dockerfile.dapperfor build environment specification - •Dapper documentation: https://github.com/rancher/dapper