For general work context, refer to the AGENTS.md file at the repository root.
This file provides specific guidance for code review in the concierge repository.
-
Unit Tests: All new business logic must include table-driven unit tests
- Use
system.NewMockSystem()for testing code that interacts with the system - Test happy paths AND error conditions
- Verify command execution using
system.ExecutedCommands - Example pattern: See
internal/providers/lxd_test.go
- Use
-
Integration Tests: Provider changes and major features require spread tests
- Add tests in the
tests/directory - Ensure tests work with both
lxd:andgithub-ci:backends - Test both
prepareandrestoreoperations
- Add tests in the
- Always wrap errors with context using
fmt.Errorf("context: %w", err) - Check return values: Never ignore errors from system operations
- Fail early: Validate inputs before executing expensive operations
- Log at appropriate levels:
slog.Debug()for verbose operational detailsslog.Info()for user-facing progress messagesslog.Error()for failures (though errors should typically be returned)
-
Use the Worker interface: All system commands must go through
system.Worker- Direct use of
exec.Command()is prohibited - Use
system.NewCommand()to construct commands - Prefer
system.RunMany(w, ...)helper for sequential independent operations
- Direct use of
-
Exclusive operations: Use
Run(cmd, system.Exclusive())for operations requiring locks- Package installations (snap, apt)
- State-modifying operations that can conflict
- Use errgroups for concurrent operations (see
plan.go) - Parallel execution: Snaps and debs should install concurrently
- Sequential dependencies: Juju bootstrap must wait for providers
- Avoid blocking: Don't introduce unnecessary sequential execution
-
Respect override priority:
- CLI flags (highest)
- Environment variables
- Config file
- Presets (lowest)
-
Channel handling: Always check
config.Overrides.*Channelbeforeconfig.*Channel -
Validate early: Use plan validators for configuration errors (see
plan_validators.go)
All providers must implement the complete Provider interface:
Prepare()- Install and configure the providerRestore()- Remove the providerBootstrap()- Whether to bootstrap JujuCloudName()- Juju cloud nameCredentials()- Juju credentials mapModelDefaults()- Juju model-defaultsBootstrapConstraints()- Juju bootstrap-constraintsName()- Concierge provider name
-
Command injection: Never use string concatenation to build commands
- Use
system.NewCommand(cmd, []string{arg1, arg2})pattern - Each argument must be a separate string in the slice
- Use
-
Privilege escalation: The binary runs as root/sudo
- Minimize operations requiring root
- Use
system.User()to get the non-root user for usermod operations - Be cautious with file permissions and ownership
-
Credentials handling:
- Never log credentials or sensitive data
- Credentials files should be read-only by owner
- Clear credential data from memory when no longer needed
- Exported vs unexported: Only export what's necessary for the public API
- Struct initialization: Use named fields in struct literals
- Interface satisfaction: Ensure types implement interfaces at compile time
- nil checks: Check for nil before dereferencing pointers
- String formatting: Use
fmt.Sprintf()for complex strings, avoid concatenation
-
Snap channel changes: Refreshing snaps to different channels may require stopping them first
- See
lxd.go:workaroundRefresh()for the pattern
- See
-
Race conditions: The runtime config is cached to
~/.cache/concierge/concierge.yamlrestoremust read this cached config, not a new config file
-
File paths: Use
system.Worker.UserHomeDir()instead of hardcoding/home/user -
Test isolation: Mock system must be reset between test cases
- Public functions: Must have godoc comments starting with the function name
- Complex logic: Inline comments explaining the "why", not the "what"
- Configuration changes: Update the README.md schema section
- New providers: Update the "Adding New Providers" section in AGENTS.md
When reviewing a PR, verify:
- Unit tests exist and cover new/modified code paths
- Integration tests added for provider or major feature changes
- Errors are properly wrapped with context
- All system operations use the
system.Workerinterface - Configuration override priority is respected
- No command injection vulnerabilities
- No hardcoded file paths or user assumptions
- Exported functions have godoc comments
- Code follows Go idioms and project patterns
- Changes don't introduce unnecessary complexity
- Concurrent operations use proper synchronization
- Mock system is used correctly in tests