From ddae9865ba4f267ab47e4a51fc865d115452c3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 9 Oct 2025 19:18:40 +0200 Subject: [PATCH 1/6] docs: add AI coding agent guidelines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive AI.md file to guide AI coding agents working on the repository. This document combines best practices learned from module development with practical workflow guidelines. Key sections: - Repository overview and structure - Environment setup (Go version, gvm usage) - Development workflow (testing, git, PRs) - Module development best practices: - Container struct design (use Container, not module-specific names) - Run function pattern (5-step implementation) - Container options (when to use built-in vs custom) - State inspection with strings.CutPrefix - Variadic arguments usage - Testing guidelines and common pitfalls - Reference documentation pointers This helps AI agents understand the codebase architecture, follow established patterns, and avoid common mistakes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- AI.md | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 AI.md diff --git a/AI.md b/AI.md new file mode 100644 index 0000000000..afb71814ae --- /dev/null +++ b/AI.md @@ -0,0 +1,340 @@ +# AI Coding Agent Guidelines + +This document provides guidelines for AI coding agents working on the Testcontainers for Go repository. + +## Repository Overview + +This is a **Go monorepo** containing: +- **Core library**: Root directory contains the main testcontainers-go library +- **Modules**: `./modules/` directory with 50+ technology-specific modules (postgres, redis, kafka, etc.) +- **Examples**: `./examples/` directory with example implementations +- **Module generator**: `./modulegen/` directory with tools to generate new modules +- **Documentation**: `./docs/` directory with MkDocs-based documentation + +## Environment Setup + +### Go Version +- **Required**: Go 1.24.7 (arm64 for Apple Silicon, amd64 for others) +- **Tool**: Use [gvm](https://github.com/andrewkroh/gvm) for version management +- **CRITICAL**: Always run this before ANY Go command: + ```bash + eval "$(gvm 1.24.7 --arch=arm64)" + ``` + +### Project Structure +Each module in `./modules/` is a separate Go module with: +- `go.mod` / `go.sum` - Module dependencies +- `{module}.go` - Main module implementation +- `{module}_test.go` - Unit tests +- `examples_test.go` - Testable examples for documentation +- `Makefile` - Standard targets: `pre-commit`, `test-unit` + +## Development Workflow + +### Before Making Changes +1. **Read existing code** in similar modules for patterns +2. **Check documentation** in `docs/modules/index.md` for best practices +3. **Run tests** to ensure baseline passes + +### Working with Modules +1. **Change to module directory**: `cd modules/{module-name}` +2. **Run pre-commit checks**: `make pre-commit` (linting, formatting, tidy) +3. **Run tests**: `make test-unit` +4. **Both together**: `make pre-commit test-unit` + +### Git Workflow +- **Branch naming**: Use descriptive names like `chore-module-use-run`, `feat-add-xyz`, `fix-module-issue` +- **Commit format**: Conventional commits + ``` + type(scope): description + + Longer explanation if needed. + + 🤖 Generated with [Claude Code](https://claude.com/claude-code) + + Co-Authored-By: Claude + ``` +- **Commit types**: `feat`, `fix`, `chore`, `docs`, `test`, `refactor` +- **Always include co-author footer** when AI assists with changes + +### Pull Requests +- **Title format**: `type(scope): description` +- **Labels**: Use appropriate labels (`chore`, `breaking change`, etc.) +- **Body template**: + ```markdown + ## What does this PR do? + + Brief description of changes. + + ## Why is it important? + + Context and rationale. + + ## Related issues + + - Relates to #issue-number + ``` + +## Module Development Best Practices + +### Container Struct Design +```go +// ✅ Correct: Use "Container" not "ModuleContainer" or "PostgresContainer" +type Container struct { + testcontainers.Container + // Use private fields for state + dbName string + user string + password string +} +``` + +**Key principles:** +- **Name**: Use `Container`, not module-specific names (some legacy modules use old naming, they will be updated) +- **Fields**: Private fields for internal state, public only when needed for API +- **Embedding**: Always embed `testcontainers.Container` to promote methods + +### The Run Function Pattern + +```go +func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*Container, error) { + // 1. Process custom options first (if using intermediate settings struct) + var settings options + for _, opt := range opts { + if opt, ok := opt.(Option); ok { + if err := opt(&settings); err != nil { + return nil, err + } + } + } + + // 2. Build moduleOpts with defaults + moduleOpts := []testcontainers.ContainerCustomizer{ + testcontainers.WithExposedPorts("5432/tcp"), + testcontainers.WithEnv(map[string]string{"DB": "default"}), + testcontainers.WithWaitStrategy(wait.ForListeningPort("5432/tcp")), + } + + // 3. Add conditional options based on settings + if settings.tlsEnabled { + moduleOpts = append(moduleOpts, /* TLS config */) + } + + // 4. Append user options (allows users to override defaults) + moduleOpts = append(moduleOpts, opts...) + + // 5. Call testcontainers.Run + ctr, err := testcontainers.Run(ctx, img, moduleOpts...) + var c *Container + if ctr != nil { + c = &Container{Container: ctr, settings: settings} + } + + // 6. Return with proper error wrapping + if err != nil { + return c, fmt.Errorf("run modulename: %w", err) + } + + return c, nil +} +``` + +**Key patterns:** +- Process custom options **before** building moduleOpts +- Module defaults → user options → post-processing (order matters!) +- Always initialize container variable before error check +- Error format: `fmt.Errorf("run modulename: %w", err)` + +### Container Options + +#### When to Use Built-in Options +Use `testcontainers.With*` for simple configuration: + +```go +// ✅ Good: Simple env var setting +func WithDatabase(dbName string) testcontainers.CustomizeRequestOption { + return testcontainers.WithEnv(map[string]string{"POSTGRES_DB": dbName}) +} +``` + +#### When to Use CustomizeRequestOption +Use for complex logic requiring multiple operations: + +```go +// ✅ Good: Multiple operations needed +func WithConfigFile(cfg string) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) error { + if err := testcontainers.WithFiles(cfgFile)(req); err != nil { + return err + } + return testcontainers.WithCmdArgs("-c", "config_file=/etc/app.conf")(req) + } +} +``` + +#### When to Create Custom Option Types +Create custom `Option` type when you need to transfer state: + +```go +type options struct { + tlsEnabled bool + tlsConfig *tls.Config +} + +type Option func(*options) error + +func (o Option) Customize(req *testcontainers.GenericContainerRequest) error { + return nil // Can be empty if only setting internal state +} + +func WithTLS() Option { + return func(opts *options) error { + opts.tlsEnabled = true + return nil + } +} +``` + +#### Critical Rules for Options + +**✅ DO:** +- Return struct types (`testcontainers.CustomizeRequestOption` or custom `Option`) +- Call built-in options directly: `testcontainers.WithFiles(f)(req)` +- Use variadic arguments correctly: `WithCmd("arg1", "arg2")` +- Handle errors properly in option functions + +**❌ DON'T:** +- Return interface types (`testcontainers.ContainerCustomizer`) +- Use `.Customize()` method: `WithFiles(f).Customize(req)` +- Pass slices to variadic functions: `WithCmd([]string{"arg1", "arg2"})` +- Ignore errors from built-in options + +### Container State Inspection + +When reading environment variables after container creation: + +```go +inspect, err := ctr.Inspect(ctx) +if err != nil { + return c, fmt.Errorf("inspect modulename: %w", err) +} + +// Use strings.CutPrefix with early exit optimization +var foundDB, foundUser, foundPass bool +for _, env := range inspect.Config.Env { + if v, ok := strings.CutPrefix(env, "POSTGRES_DB="); ok { + c.dbName, foundDB = v, true + } + if v, ok := strings.CutPrefix(env, "POSTGRES_USER="); ok { + c.user, foundUser = v, true + } + if v, ok := strings.CutPrefix(env, "POSTGRES_PASSWORD="); ok { + c.password, foundPass = v, true + } + + // Early exit once all values found + if foundDB && foundUser && foundPass { + break + } +} +``` + +**Key techniques:** +- Use `strings.CutPrefix` (standard library) for prefix checking +- Set default values when creating container struct +- Use individual `found` flags for each variable +- Check all flags together and break early + +### Variadic Arguments + +**Correct usage:** +```go +// ✅ Pass arguments directly +testcontainers.WithCmd("redis-server", "--port", "6379") +testcontainers.WithFiles(file1, file2, file3) +testcontainers.WithWaitStrategy(wait1, wait2) + +// For initial setup +testcontainers.WithCmd(...) // Sets command +testcontainers.WithEntrypoint(...) // Sets entrypoint + +// For appending to user values +testcontainers.WithCmdArgs(...) // Appends to command +testcontainers.WithEntrypointArgs(...) // Appends to entrypoint +``` + +**Wrong usage:** +```go +// ❌ Don't pass slices to variadic functions +testcontainers.WithCmd([]string{"redis-server", "--port", "6379"}) +testcontainers.WithFiles([]ContainerFile{file1, file2}) +``` + +## Testing Guidelines + +### Running Tests +- **From module directory**: `cd modules/{module} && make test-unit` +- **Pre-commit checks**: `make pre-commit` (run this first to catch lint issues) +- **Full check**: `make pre-commit test-unit` + +### Test Patterns +- Use testable examples in `examples_test.go` +- Follow existing test patterns in similar modules +- Test both success and error cases +- Use `t.Parallel()` when tests are independent + +### When Tests Fail +1. **Read the error message carefully** - it usually tells you exactly what's wrong +2. **Check if it's a lint issue** - run `make pre-commit` first +3. **Verify Go version** - ensure using Go 1.24.7 +4. **Check Docker** - some tests require Docker daemon running + +## Common Pitfalls to Avoid + +### Code Issues +- ❌ Using interface types as return values +- ❌ Forgetting to run `eval "$(gvm 1.24.7 --arch=arm64)"` +- ❌ Not handling errors from built-in options +- ❌ Using module-specific container names (`PostgresContainer`) +- ❌ Calling `.Customize()` method instead of direct function call + +### Git Issues +- ❌ Forgetting co-author footer in commits +- ❌ Not running tests before committing +- ❌ Committing files outside module scope (use `git add modules/{module}/`) + +### Testing Issues +- ❌ Running tests without pre-commit checks first +- ❌ Not changing to module directory before running make +- ❌ Forgetting to set Go version before testing + +## Reference Documentation + +For detailed information, see: +- **Module development**: `docs/modules/index.md` - Comprehensive best practices +- **Contributing**: `docs/contributing.md` - General contribution guidelines +- **Modules catalog**: [testcontainers.com/modules](https://testcontainers.com/modules/?language=go) +- **API docs**: [pkg.go.dev/github.com/testcontainers/testcontainers-go](https://pkg.go.dev/github.com/testcontainers/testcontainers-go) + +## Module Generator + +To create a new module: + +```bash +cd modulegen +go run . new module --name mymodule --image "docker.io/myimage:tag" +``` + +This generates: +- Module scaffolding with proper structure +- Documentation template +- Test files with examples +- Makefile with standard targets + +The generator uses templates in `modulegen/_template/` that follow current best practices. + +## Need Help? + +- **Slack**: [testcontainers.slack.com](https://slack.testcontainers.org/) +- **GitHub Discussions**: [github.com/testcontainers/testcontainers-go/discussions](https://github.com/testcontainers/testcontainers-go/discussions) +- **Issues**: Check existing issues or create a new one with detailed context From 2c5e7545f0a9b9fc0e9d5ab008a58edbb3854b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 9 Oct 2025 19:22:51 +0200 Subject: [PATCH 2/6] docs: add conventional commit validation details to AI guidelines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update AI.md with specific details from the conventions.yml GitHub workflow: - Enforced commit types: security, fix, feat, docs, chore, deps - Scope rules: optional, must be lowercase - Subject rules: must not start with uppercase - Breaking change notation with ! - PR title validation details - Warning about main branch PRs being auto-closed This ensures AI agents follow the exact conventions enforced by CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- AI.md | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/AI.md b/AI.md index afb71814ae..cce98eed02 100644 --- a/AI.md +++ b/AI.md @@ -44,7 +44,8 @@ Each module in `./modules/` is a separate Go module with: ### Git Workflow - **Branch naming**: Use descriptive names like `chore-module-use-run`, `feat-add-xyz`, `fix-module-issue` -- **Commit format**: Conventional commits + - **NEVER** use `main` branch for PRs (they will be auto-closed) +- **Commit format**: Conventional commits (enforced by CI) ``` type(scope): description @@ -54,12 +55,24 @@ Each module in `./modules/` is a separate Go module with: Co-Authored-By: Claude ``` -- **Commit types**: `feat`, `fix`, `chore`, `docs`, `test`, `refactor` +- **Commit types** (enforced): `security`, `fix`, `feat`, `docs`, `chore`, `deps` +- **Scope rules**: + - Optional (can be omitted for repo-level changes) + - Must be lowercase (uppercase scopes are rejected) + - Examples: `feat(redis)`, `chore(kafka)`, `docs`, `fix(postgres)` +- **Subject rules**: + - Must NOT start with uppercase letter + - ✅ Good: `feat(redis): add support for clustering` + - ❌ Bad: `feat(redis): Add support for clustering` +- **Breaking changes**: Add `!` after type: `feat(redis)!: remove deprecated API` - **Always include co-author footer** when AI assists with changes ### Pull Requests -- **Title format**: `type(scope): description` -- **Labels**: Use appropriate labels (`chore`, `breaking change`, etc.) +- **Title format**: Same as commit format (validated by CI) + - `type(scope): description` + - Examples: `feat(redis): add clustering support`, `docs: improve module guide`, `chore(kafka): update tests` +- **Title validation** enforced by `.github/workflows/conventions.yml` +- **Labels**: Use appropriate labels (`chore`, `breaking change`, `documentation`, etc.) - **Body template**: ```markdown ## What does this PR do? @@ -302,6 +315,10 @@ testcontainers.WithFiles([]ContainerFile{file1, file2}) - ❌ Forgetting co-author footer in commits - ❌ Not running tests before committing - ❌ Committing files outside module scope (use `git add modules/{module}/`) +- ❌ Using uppercase in scope: `feat(Redis)` → use `feat(redis)` +- ❌ Starting subject with uppercase: `fix: Add feature` → use `fix: add feature` +- ❌ Using wrong commit type (only: `security`, `fix`, `feat`, `docs`, `chore`, `deps`) +- ❌ Creating PR from `main` branch (will be auto-closed) ### Testing Issues - ❌ Running tests without pre-commit checks first From 538772da944721877adc50927bd67a04fdae5c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 9 Oct 2025 19:23:50 +0200 Subject: [PATCH 3/6] docs: simplify AI guidelines by linking to detailed docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace lengthy module development examples with quick reference that links to docs/modules/index.md for complete details. This: - Avoids duplication between AI.md and existing documentation - Keeps AI.md concise and scannable - Ensures single source of truth for detailed patterns - Provides just enough context for AI agents to get started 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- AI.md | 206 +++++++++++----------------------------------------------- 1 file changed, 37 insertions(+), 169 deletions(-) diff --git a/AI.md b/AI.md index cce98eed02..7bd906751a 100644 --- a/AI.md +++ b/AI.md @@ -90,198 +90,66 @@ Each module in `./modules/` is a separate Go module with: ## Module Development Best Practices -### Container Struct Design +**📖 Detailed guide**: See [`docs/modules/index.md`](docs/modules/index.md) for comprehensive module development documentation. + +### Quick Reference + +#### Container Struct +- **Name**: Use `Container`, not module-specific names like `PostgresContainer` +- **Fields**: Use private fields for state management +- **Embedding**: Always embed `testcontainers.Container` + ```go -// ✅ Correct: Use "Container" not "ModuleContainer" or "PostgresContainer" type Container struct { testcontainers.Container - // Use private fields for state - dbName string - user string - password string + dbName string // private + user string // private } ``` -**Key principles:** -- **Name**: Use `Container`, not module-specific names (some legacy modules use old naming, they will be updated) -- **Fields**: Private fields for internal state, public only when needed for API -- **Embedding**: Always embed `testcontainers.Container` to promote methods - -### The Run Function Pattern +#### Run Function Pattern +Five-step implementation: +1. Process custom options (if using intermediate settings) +2. Build `moduleOpts` with defaults +3. Add conditional options based on settings +4. Append user options (allows overrides) +5. Call `testcontainers.Run` and return with proper error wrapping ```go func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*Container, error) { - // 1. Process custom options first (if using intermediate settings struct) - var settings options - for _, opt := range opts { - if opt, ok := opt.(Option); ok { - if err := opt(&settings); err != nil { - return nil, err - } - } - } - - // 2. Build moduleOpts with defaults + // See docs/modules/index.md for complete implementation moduleOpts := []testcontainers.ContainerCustomizer{ testcontainers.WithExposedPorts("5432/tcp"), - testcontainers.WithEnv(map[string]string{"DB": "default"}), - testcontainers.WithWaitStrategy(wait.ForListeningPort("5432/tcp")), - } - - // 3. Add conditional options based on settings - if settings.tlsEnabled { - moduleOpts = append(moduleOpts, /* TLS config */) + // ... defaults } - - // 4. Append user options (allows users to override defaults) moduleOpts = append(moduleOpts, opts...) - // 5. Call testcontainers.Run ctr, err := testcontainers.Run(ctx, img, moduleOpts...) - var c *Container - if ctr != nil { - c = &Container{Container: ctr, settings: settings} - } - - // 6. Return with proper error wrapping if err != nil { - return c, fmt.Errorf("run modulename: %w", err) - } - - return c, nil -} -``` - -**Key patterns:** -- Process custom options **before** building moduleOpts -- Module defaults → user options → post-processing (order matters!) -- Always initialize container variable before error check -- Error format: `fmt.Errorf("run modulename: %w", err)` - -### Container Options - -#### When to Use Built-in Options -Use `testcontainers.With*` for simple configuration: - -```go -// ✅ Good: Simple env var setting -func WithDatabase(dbName string) testcontainers.CustomizeRequestOption { - return testcontainers.WithEnv(map[string]string{"POSTGRES_DB": dbName}) -} -``` - -#### When to Use CustomizeRequestOption -Use for complex logic requiring multiple operations: - -```go -// ✅ Good: Multiple operations needed -func WithConfigFile(cfg string) testcontainers.CustomizeRequestOption { - return func(req *testcontainers.GenericContainerRequest) error { - if err := testcontainers.WithFiles(cfgFile)(req); err != nil { - return err - } - return testcontainers.WithCmdArgs("-c", "config_file=/etc/app.conf")(req) - } -} -``` - -#### When to Create Custom Option Types -Create custom `Option` type when you need to transfer state: - -```go -type options struct { - tlsEnabled bool - tlsConfig *tls.Config -} - -type Option func(*options) error - -func (o Option) Customize(req *testcontainers.GenericContainerRequest) error { - return nil // Can be empty if only setting internal state -} - -func WithTLS() Option { - return func(opts *options) error { - opts.tlsEnabled = true - return nil - } -} -``` - -#### Critical Rules for Options - -**✅ DO:** -- Return struct types (`testcontainers.CustomizeRequestOption` or custom `Option`) -- Call built-in options directly: `testcontainers.WithFiles(f)(req)` -- Use variadic arguments correctly: `WithCmd("arg1", "arg2")` -- Handle errors properly in option functions - -**❌ DON'T:** -- Return interface types (`testcontainers.ContainerCustomizer`) -- Use `.Customize()` method: `WithFiles(f).Customize(req)` -- Pass slices to variadic functions: `WithCmd([]string{"arg1", "arg2"})` -- Ignore errors from built-in options - -### Container State Inspection - -When reading environment variables after container creation: - -```go -inspect, err := ctr.Inspect(ctx) -if err != nil { - return c, fmt.Errorf("inspect modulename: %w", err) -} - -// Use strings.CutPrefix with early exit optimization -var foundDB, foundUser, foundPass bool -for _, env := range inspect.Config.Env { - if v, ok := strings.CutPrefix(env, "POSTGRES_DB="); ok { - c.dbName, foundDB = v, true - } - if v, ok := strings.CutPrefix(env, "POSTGRES_USER="); ok { - c.user, foundUser = v, true - } - if v, ok := strings.CutPrefix(env, "POSTGRES_PASSWORD="); ok { - c.password, foundPass = v, true - } - - // Early exit once all values found - if foundDB && foundUser && foundPass { - break + return nil, fmt.Errorf("run modulename: %w", err) } + return &Container{Container: ctr}, nil } ``` -**Key techniques:** -- Use `strings.CutPrefix` (standard library) for prefix checking -- Set default values when creating container struct -- Use individual `found` flags for each variable -- Check all flags together and break early +#### Container Options +- **Simple config**: Use built-in `testcontainers.With*` options +- **Complex logic**: Use `testcontainers.CustomizeRequestOption` +- **State transfer**: Create custom `Option` type -### Variadic Arguments +**Critical rules:** +- ✅ Return struct types (not interfaces) +- ✅ Call built-in options directly: `testcontainers.WithFiles(f)(req)` +- ❌ Don't use `.Customize()` method +- ❌ Don't pass slices to variadic functions -**Correct usage:** -```go -// ✅ Pass arguments directly -testcontainers.WithCmd("redis-server", "--port", "6379") -testcontainers.WithFiles(file1, file2, file3) -testcontainers.WithWaitStrategy(wait1, wait2) - -// For initial setup -testcontainers.WithCmd(...) // Sets command -testcontainers.WithEntrypoint(...) // Sets entrypoint - -// For appending to user values -testcontainers.WithCmdArgs(...) // Appends to command -testcontainers.WithEntrypointArgs(...) // Appends to entrypoint -``` +#### Common Patterns +- **Env inspection**: Use `strings.CutPrefix` with early exit +- **Variadic args**: Pass directly, not as slices +- **Option order**: defaults → user options → post-processing +- **Error format**: `fmt.Errorf("run modulename: %w", err)` -**Wrong usage:** -```go -// ❌ Don't pass slices to variadic functions -testcontainers.WithCmd([]string{"redis-server", "--port", "6379"}) -testcontainers.WithFiles([]ContainerFile{file1, file2}) -``` +**For complete examples and detailed explanations**, see [`docs/modules/index.md`](docs/modules/index.md). ## Testing Guidelines From d2cb4f47cf8045ae57a0291e26fb96f3cfbe6ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 9 Oct 2025 19:26:30 +0200 Subject: [PATCH 4/6] docs: add language hint to commit message code fence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 'text' language hint to the commit message example code fence for proper syntax highlighting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- AI.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AI.md b/AI.md index 7bd906751a..4a02461caa 100644 --- a/AI.md +++ b/AI.md @@ -46,7 +46,7 @@ Each module in `./modules/` is a separate Go module with: - **Branch naming**: Use descriptive names like `chore-module-use-run`, `feat-add-xyz`, `fix-module-issue` - **NEVER** use `main` branch for PRs (they will be auto-closed) - **Commit format**: Conventional commits (enforced by CI) - ``` + ```text type(scope): description Longer explanation if needed. From 906e2ca5b0116a9baccba9ca9604f4e1d7d5b8a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 9 Oct 2025 20:33:16 +0200 Subject: [PATCH 5/6] docs: add gvm command for both arm64 and amd64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include gvm commands for both Apple Silicon (arm64) and Intel/AMD (amd64) architectures to support all users. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- AI.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AI.md b/AI.md index 4a02461caa..9d1e91d321 100644 --- a/AI.md +++ b/AI.md @@ -14,11 +14,15 @@ This is a **Go monorepo** containing: ## Environment Setup ### Go Version -- **Required**: Go 1.24.7 (arm64 for Apple Silicon, amd64 for others) +- **Required**: Go 1.24.7 - **Tool**: Use [gvm](https://github.com/andrewkroh/gvm) for version management - **CRITICAL**: Always run this before ANY Go command: ```bash + # For Apple Silicon (M1/M2/M3) eval "$(gvm 1.24.7 --arch=arm64)" + + # For Intel/AMD (x86_64) + eval "$(gvm 1.24.7 --arch=amd64)" ``` ### Project Structure From a2ad6b055e99f725e39bb0be8e582b8ef97b220c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 9 Oct 2025 20:34:48 +0200 Subject: [PATCH 6/6] chore: exclude AI.md from triggering CI builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add AI.md to the excluded_files list in changed-modules.sh to prevent documentation-only changes from triggering unnecessary module builds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- scripts/changed-modules.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/changed-modules.sh b/scripts/changed-modules.sh index 65d804d626..42419653a1 100755 --- a/scripts/changed-modules.sh +++ b/scripts/changed-modules.sh @@ -89,6 +89,7 @@ readonly excluded_files=( "scripts/bump-*.sh" "scripts/check_environment.sh" "scripts/*release.sh" + "AI.md" "CONTRIBUTING.md" "LICENSE" "mkdocs.yml"