From a45ff06c34301084c36dd5e961f7e5183385af79 Mon Sep 17 00:00:00 2001 From: ldornele Date: Sun, 11 Jan 2026 18:24:00 -0300 Subject: [PATCH] HYPERFLEET-375: add dependency pinning standard with Bingo --- hyperfleet/standards/dependency-pinning.md | 412 +++++++++++++++++++++ 1 file changed, 412 insertions(+) create mode 100644 hyperfleet/standards/dependency-pinning.md diff --git a/hyperfleet/standards/dependency-pinning.md b/hyperfleet/standards/dependency-pinning.md new file mode 100644 index 0000000..88ea97b --- /dev/null +++ b/hyperfleet/standards/dependency-pinning.md @@ -0,0 +1,412 @@ +# Dependency and Tool Version Pinning Standard + +All HyperFleet repositories MUST use [Bingo](https://github.com/bwplotka/bingo) to pin development tool versions for reproducible builds across all environments. + +## Problem + +- 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 + +## Solution: Bingo + +| 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. + +--- + +## Directory Structure and File Naming + +### Standard Directory Layout + +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-v1.12.4 # Compiled binary (gitignored) +│ +├── Makefile # MUST include .bingo/Variables.mk +├── go.mod # Main module (NO tool dependencies) +└── go.sum # Main checksums (NO tool checksums) +``` + +### File Naming Conventions + +#### Tool Module Files + +Pattern: `.mod` and `.sum` + +- **Naming**: Use the tool's canonical binary name +- **Format**: Lowercase with hyphens (e.g., `golangci-lint`, NOT `golangci_lint`) +- **Derivation**: Bingo auto-derives from the import path's last component + + +#### Binary Files + +Pattern: `-v` + +- **Format**: `{tool-name}-v{semantic-version}` +- **Generated**: Automatically by Bingo +- **Gitignored**: MUST NOT be committed (pattern `*-v*` in `.gitignore`) + +#### Variables.mk + +- **Purpose**: Auto-generated Makefile variables for each tool +- **Format**: Tool name in uppercase with underscores +- **Location**: `.bingo/Variables.mk` +- **Generated by**: `bingo get` and `bingo init` + +--- + +## Quick Start + +### Initialize in a Repository + +```bash +# 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 +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" +``` + +--- + +## How to Add New Tools + +### Basic Command + +```bash +# Add specific version (recommended) +bingo get @ + +# Example +bingo get github.com/golangci/golangci-lint/cmd/golangci-lint@v2.1.6 +``` + +### Complete Workflow + +```bash +# 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 +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" +``` + +### Version Options + +```bash +# 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 +``` + +### Common Tools Quick Reference + +| Use Case | Tool | Command | +|----------|------|---------| +| **Linting** | golangci-lint | `bingo get github.com/golangci/golangci-lint/cmd/golangci-lint@v2.1.6` | +| **Mocks** | mockgen | `bingo get github.com/golang/mock/mockgen@v1.6.0` | +| **OpenAPI** | oapi-codegen | `bingo get github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4` | +| **Kubernetes CRDs** | controller-gen | `bingo get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.13.0` | +| **Protobuf** | protoc-gen-go | `bingo get google.golang.org/protobuf/cmd/protoc-gen-go@latest` | +| **gRPC** | protoc-gen-go-grpc | `bingo get google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest` | + +### Makefile Variable Mapping + +Bingo auto-generates uppercase variables: + +| Tool Name | Makefile Variable | +|-----------|-------------------| +| `golangci-lint` | `$(GOLANGCI_LINT)` | +| `mockgen` | `$(MOCKGEN)` | +| `oapi-codegen` | `$(OAPI_CODEGEN)` | +| `controller-gen` | `$(CONTROLLER_GEN)` | + +--- + +## Common Operations + +### Upgrade Tool + +```bash +# 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" +``` + +### Remove Tool + +```bash +bingo rm golangci-lint +``` + +### List Tools + +```bash +bingo list +``` + +--- + +## Makefile Targets for Tool Installation + +### Required Targets + +All repositories MUST provide these targets: + +```makefile +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 github.com/golang/mock/mockgen@v1.6.0 + bingo get github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 + @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 github.com/golang/mock/mockgen + bingo get github.com/deepmap/oapi-codegen/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 Descriptions + +| 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 | + +### How It Works + +```makefile +# 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 `.mod` files when missing +- Exact pinned versions always used (no surprises) +- `tools-install` useful for new developer onboarding +- `tools-update` updates versions in `.mod` files + +--- + +## CI Integration Pattern + +### GitHub Actions Example + +```yaml +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: '1.23' + cache: true + + # Cache Bingo tools + - uses: actions/cache@v4 + with: + path: .bingo + key: ${{ runner.os }}-bingo-${{ hashFiles('.bingo/*.mod') }} + restore-keys: | + ${{ runner.os }}-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: '1.23' + 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: '1.23' + cache: true + + - uses: actions/cache@v4 + with: + path: .bingo + key: ${{ runner.os }}-bingo-${{ hashFiles('.bingo/*.mod') }} + + - name: Regenerate code + run: make generate + + - name: Check for changes + run: git diff --exit-code # Fail if generated code is stale +``` + +**Key points**: +- No Bingo installation needed in CI +- Tools build automatically from `.mod` files +- Cache `.bingo/` using hash of `*.mod` files for faster builds +- Separate jobs for lint, test, and code generation verification + +--- + +## Template .bingo Directory + +### .bingo/.gitignore + +```gitignore +# Ignore compiled tool binaries +*-v* + +# Keep module definitions +!*.mod +!*.sum + +# Keep Makefile variables +!Variables.mk + +# Keep this gitignore +!.gitignore +``` + +### Makefile Integration + +See [Makefile Targets for Tool Installation](#makefile-targets-for-tool-installation) for complete Makefile examples. + +### GitHub Actions Integration + +See [CI Integration Pattern](#ci-integration-pattern) for complete GitHub Actions examples. + +--- + +## References + +### Bingo Documentation +- [Bingo GitHub](https://github.com/bwplotka/bingo) +- [Bingo Blog Post](https://www.bwplotka.dev/2020/bingo/) + +### Related HyperFleet Standards +- [Linting Standard](linting.md) - Uses Bingo for golangci-lint +- [Generated Code Policy](generated-code-policy.md) - Code generators via Bingo +- [Makefile Conventions](makefile-conventions.md) - Bingo integration patterns