| Status | Active |
|---|---|
| Owner | HyperFleet Platform Team |
| Last Updated | 2026-01-26 |
- Overview
- Problem
- Solution: Bingo
- Directory Structure and File Naming
- Quick Start
- How to Add New Tools
- Common Operations
- Makefile Targets for Tool Installation
- CI Integration Pattern
- Template .bingo Directory
- References
Prescribes how dependencies must be pinned for reproducible builds across all HyperFleet repositories. Covers Go module pinning, container image digest pinning, and tooling version management. Ensures that builds are deterministic and that supply chain risks from floating dependency versions are minimized.
All HyperFleet repositories containing Go code MUST use Bingo to pin development tool versions for reproducible builds across all environments.
- Repos use different approaches for managing tool versions
- CI builds produce inconsistent results based on installed tool versions
- Non-reproducible builds due to lack of explicit version pinning
- Tool dependency conflicts break builds unpredictably
| Feature | Bingo | tools.go | Go 1.24+ tool |
|---|---|---|---|
| Mechanism | Nested modules in .bingo/ |
tools.go file with build tags | tool directive in go.mod |
| Dependency Isolation | ✅ Total (Each tool has its own .mod) | ❌ None (Pollutes go.mod) | ❌ None (Pollutes go.mod) |
| Version Conflicts | ✅ Zero (Tools are isolated islands) | ❌ High (Tool vs. App conflicts) | ❌ High (Graph is shared) |
| Go version | Any with modules | Any with modules | Requires 1.24+ |
| CI Cache Efficiency | ✅ Excellent (Binaries cached by hash) | ❌ Poor (Recompiles often) | Medium (Go build cache) |
Rationale: In a microservices architecture like HyperFleet, stability is paramount. We cannot accept a risk where updating a CLI tool (like a linter or generator) forces an upgrade of production libraries due to a shared dependency graph in go.mod.
Bingo provides the necessary "firewall" between development tools and production code. Your root go.mod remains clean, containing only the code required to run your service. The linter's heavy dependencies never touch your production dependency graph.
Every repository using Bingo MUST follow this structure:
repo-root/
├── .bingo/
│ ├── .gitignore # Git ignore rules for binaries
│ ├── Variables.mk # Makefile integration (auto-generated)
│ │
│ ├── golangci-lint.mod # Tool version specification
│ ├── golangci-lint.sum # Dependency checksums
│ ├── golangci-lint-v2.1.6 # Compiled binary (gitignored)
│ │
│ ├── mockgen.mod # Tool version specification
│ ├── mockgen.sum # Dependency checksums
│ ├── mockgen-v1.6.0 # Compiled binary (gitignored)
│ │
│ ├── oapi-codegen.mod # Tool version specification
│ ├── oapi-codegen.sum # Dependency checksums
│ └── oapi-codegen-v2.5.0 # Compiled binary (gitignored)
│
├── Makefile # MUST include .bingo/Variables.mk
├── go.mod # Main module (NO tool dependencies)
└── go.sum # Main checksums (NO tool checksums)
Pattern: <tool-name>.mod and <tool-name>.sum
- Naming: Use the tool's canonical binary name
- Format: Lowercase with hyphens (e.g.,
golangci-lint, NOTgolangci_lint) - Derivation: Bingo auto-derives from the import path's last component
Pattern: <tool-name>-v<version>
- Format:
{tool-name}-v{semantic-version} - Generated: Automatically by Bingo
- Gitignored: MUST NOT be committed (pattern
*-v[0-9]*in.gitignore)
- Purpose: Auto-generated Makefile variables for each tool
- Format: Tool name in uppercase with underscores
- Location:
.bingo/Variables.mk - Generated by:
bingo getandbingo init
# 1. Install Bingo (optional - can bootstrap from .mod files)
go install github.com/bwplotka/bingo@latest
# 2. Initialize .bingo directory
bingo init
# 3. Add required tools
bingo get github.com/golangci/golangci-lint/cmd/golangci-lint@v2.1.6
# 4. Update Makefile
grep -q '.bingo/Variables.mk' Makefile || cat >> Makefile <<'EOF'
include .bingo/Variables.mk
.PHONY: lint
lint: $(GOLANGCI_LINT)
$(GOLANGCI_LINT) run ./...
EOF
# 5. Test
make lint
# 6. Commit
git add .bingo/ Makefile
git commit -m "build: add Bingo tool version pinning"# Add specific version (recommended)
bingo get <import-path>@<version>
# Example
bingo get github.com/golangci/golangci-lint/cmd/golangci-lint@v2.1.6# 1. Add tool
bingo get github.com/golangci/golangci-lint/cmd/golangci-lint@v2.1.6
# 2. Verify
bingo list
ls .bingo/golangci-lint* # Should show .mod, .sum, binary
# 3. Add Makefile target (skip if already exists)
grep -q 'lint.*GOLANGCI_LINT' Makefile || cat >> Makefile <<'EOF'
.PHONY: lint
lint: $(GOLANGCI_LINT)
$(GOLANGCI_LINT) run ./...
EOF
# 4. Test
make lint
# 5. Commit
git add .bingo/golangci-lint.{mod,sum} .bingo/Variables.mk Makefile
git commit -m "build: add golangci-lint v2.1.6"# Specific version (recommended)
bingo get github.com/tool/cmd/tool@v1.2.3
# Latest version
bingo get github.com/tool/cmd/tool
# Commit SHA (for unreleased fixes)
bingo get github.com/tool/cmd/tool@abc1234
# Custom name (avoid conflicts)
bingo get -n custom-name github.com/tool/cmd/tool| Use Case | Tool | Command |
|---|---|---|
| Linting | golangci-lint | bingo get github.com/golangci/golangci-lint/cmd/golangci-lint@v2.1.6 |
| Mocks | mockgen | bingo get go.uber.org/mock/mockgen@v0.5.0 |
| OpenAPI | oapi-codegen | bingo get github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.5.0 |
| Kubernetes CRDs | controller-gen | bingo get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.20.0 |
| Protobuf | protoc-gen-go | bingo get google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11 |
| gRPC | protoc-gen-go-grpc | bingo get google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.78.0 |
Bingo auto-generates uppercase variables:
| Tool Name | Makefile Variable |
|---|---|
golangci-lint |
$(GOLANGCI_LINT) |
mockgen |
$(MOCKGEN) |
oapi-codegen |
$(OAPI_CODEGEN) |
controller-gen |
$(CONTROLLER_GEN) |
# Upgrade to latest
bingo get github.com/golangci/golangci-lint/cmd/golangci-lint
# Upgrade to specific version
bingo get github.com/golangci/golangci-lint/cmd/golangci-lint@v2.2.0
# Review and commit
git diff .bingo/golangci-lint.mod
git add .bingo/golangci-lint.{mod,sum}
git commit -m "build: upgrade golangci-lint to v2.2.0"bingo rm golangci-lintbingo listAll repositories MUST provide these targets:
include .bingo/Variables.mk
# Install all required tools (for initial setup)
.PHONY: tools-install
tools-install:
@echo "Installing Bingo tools..."
bingo get github.com/golangci/golangci-lint/cmd/golangci-lint@v2.1.6
bingo get go.uber.org/mock/mockgen@v0.5.0
bingo get github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.5.0
@echo "Tools installed successfully!"
# List installed tools
.PHONY: tools-list
tools-list:
@bingo list
# Update all tools to latest versions
.PHONY: tools-update
tools-update:
@echo "Updating tools to latest versions..."
bingo get github.com/golangci/golangci-lint/cmd/golangci-lint
bingo get go.uber.org/mock/mockgen
bingo get github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen
@echo "Tools updated!"
# Use tools in standard targets
.PHONY: lint
lint: $(GOLANGCI_LINT)
$(GOLANGCI_LINT) run ./...
.PHONY: generate
generate: $(MOCKGEN) $(OAPI_CODEGEN)
go generate ./...| Target | Purpose | When to Use |
|---|---|---|
tools-install |
Install all tools at pinned versions | First-time setup, onboarding new developers |
tools-list |
List all installed tools and versions | Check current tool versions |
tools-update |
Update all tools to latest versions | Monthly maintenance, security updates |
lint |
Run linters using pinned tools | Pre-commit, CI pipeline |
generate |
Generate code using pinned tools | After spec changes, pre-commit |
# Variables.mk provides tool paths
$(GOLANGCI_LINT) → .bingo/golangci-lint-v2.1.6
# If binary missing, auto-builds from .mod file
lint: $(GOLANGCI_LINT) # Triggers build if needed
$(GOLANGCI_LINT) run ./...Key behaviors:
- Tool binaries auto-build from
.modfiles when missing - Exact pinned versions always used (no surprises)
tools-installuseful for new developer onboardingtools-updateupdates versions in.modfiles
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
# Cache Bingo tools
- uses: actions/cache@v4
with:
path: .bingo
key: ${{ runner.os }}-${{ runner.arch }}-bingo-${{ hashFiles('.bingo/*.mod') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-bingo-
- name: Run linter
run: make lint # Bingo auto-builds tool
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
- name: Run tests
run: make test
verify-generated:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
- uses: actions/cache@v4
with:
path: .bingo
key: ${{ runner.os }}-${{ runner.arch }}-bingo-${{ hashFiles('.bingo/*.mod') }}
- name: Regenerate code
run: make generate
- name: Check for changes
run: |
if ! git diff --exit-code; then
echo "::error::Generated code is out of sync. Run 'make generate' locally and commit the changes."
git diff --stat
exit 1
fiKey points:
- No Bingo installation needed in CI
- Tools build automatically from
.modfiles - Cache
.bingo/using hash of*.modfiles for faster builds - Separate jobs for lint, test, and code generation verification
# Ignore compiled tool binaries (Bingo format: tool-name-v1.2.3)
*-v[0-9]*
# Keep module definitions
!*.mod
!*.sum
# Keep Makefile variables
!Variables.mk
# Keep this gitignore
!.gitignoreSee Makefile Targets for Tool Installation for complete Makefile examples.
See CI Integration Pattern for complete GitHub Actions examples.
- Bingo GitHub
- Bingo Blog Post
- gomock
- grpc-go releases
- protobuf-go releases
- oapi-codegen releases
- controller-tools releases
- Linting Standard - Uses Bingo for golangci-lint
- Generated Code Policy - Code generators via Bingo
- Makefile Conventions - Bingo integration patterns