|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Repository Overview |
| 6 | + |
| 7 | +This is a Pulumi component library for deploying Signet blockchain infrastructure to Kubernetes. It provides reusable infrastructure-as-code components for various blockchain services. |
| 8 | + |
| 9 | +## Development Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Build all packages |
| 13 | +go build -v ./... |
| 14 | + |
| 15 | +# Run all tests |
| 16 | +go test -v ./... |
| 17 | + |
| 18 | +# Run tests for a specific package (example) |
| 19 | +go test -v ./pkg/builder |
| 20 | + |
| 21 | +# Install/update dependencies |
| 22 | +go mod download |
| 23 | +go mod tidy |
| 24 | + |
| 25 | +# Verify module dependencies |
| 26 | +go mod verify |
| 27 | +``` |
| 28 | + |
| 29 | +## Architecture |
| 30 | + |
| 31 | +The repository follows a component-based architecture where each blockchain service is implemented as a self-contained Pulumi component under `pkg/`: |
| 32 | + |
| 33 | +- **AWS Components** (`pkg/aws/`): IAM roles and Postgres database components |
| 34 | +- **Blockchain Services**: |
| 35 | + - `pkg/builder/`: Builder service for blockchain operations |
| 36 | + - `pkg/erpc-proxy/`: eRPC proxy for RPC load balancing and failover |
| 37 | + - `pkg/ethereum/`: Composite Ethereum node management |
| 38 | + - `pkg/ethereum/consensus/`: Ethereum consensus layer (beacon chain) |
| 39 | + - `pkg/ethereum/execution/`: Ethereum execution layer |
| 40 | + - `pkg/pylon/`: Pylon service component |
| 41 | + - `pkg/quincey/`: Quincey service component |
| 42 | + - `pkg/signet_node/`: Core Signet node component |
| 43 | + - `pkg/txcache/`: Transaction cache component |
| 44 | +- **Utilities** (`pkg/utils/`): Shared helper functions |
| 45 | + |
| 46 | +### Component Structure Pattern |
| 47 | + |
| 48 | +Each component follows this consistent structure: |
| 49 | + |
| 50 | +#### Core Files (Required) |
| 51 | +1. **`types.go`** - Type definitions using dual-struct pattern: |
| 52 | + - Public structs with base Go types for external API |
| 53 | + - Internal structs with Pulumi types for resource creation |
| 54 | + - Conversion functions (`toInternal()`) between public and internal types |
| 55 | +2. **`[component].go`** - Main component implementation: |
| 56 | + - `New[Component]()` constructor function |
| 57 | + - Resource creation logic (ServiceAccount, ConfigMap, Deployment, Service) |
| 58 | + - Helper methods for accessing component outputs |
| 59 | +3. **`validation.go`** - Input validation logic: |
| 60 | + - `Validate()` methods for all argument structs |
| 61 | + - Field-level validation functions |
| 62 | + - Resource format validators (memory, CPU, etc.) |
| 63 | +4. **`validation_test.go`** - Comprehensive unit tests: |
| 64 | + - Table-driven tests for all validation functions |
| 65 | + - Coverage of valid and invalid cases |
| 66 | + - Proper error message assertions |
| 67 | + |
| 68 | +#### Optional Files |
| 69 | +5. **`constants.go`** - Component-specific constants: |
| 70 | + - Component kind identifier |
| 71 | + - Resource name suffixes |
| 72 | + - Default values for optional fields |
| 73 | + - Environment variable names |
| 74 | +6. **`helpers.go`** - Utility functions specific to the component: |
| 75 | + - Complex data transformations |
| 76 | + - Custom marshaling logic |
| 77 | + - Component-specific business logic |
| 78 | + |
| 79 | +### Key Design Patterns |
| 80 | + |
| 81 | +#### 1. Dual-Struct Pattern |
| 82 | +All components use a dual-struct pattern to separate public API from internal implementation: |
| 83 | +```go |
| 84 | +// Public struct with base Go types |
| 85 | +type ComponentArgs struct { |
| 86 | + Namespace string |
| 87 | + Name string |
| 88 | + Config ConfigStruct |
| 89 | +} |
| 90 | + |
| 91 | +// Internal struct with Pulumi types |
| 92 | +type componentArgsInternal struct { |
| 93 | + Namespace pulumi.StringInput |
| 94 | + Name pulumi.StringInput |
| 95 | + Config configStructInternal |
| 96 | +} |
| 97 | + |
| 98 | +// Conversion function |
| 99 | +func (args ComponentArgs) toInternal() componentArgsInternal { |
| 100 | + return componentArgsInternal{ |
| 101 | + Namespace: pulumi.String(args.Namespace), |
| 102 | + Name: pulumi.String(args.Name), |
| 103 | + Config: args.Config.toInternal(), |
| 104 | + } |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +#### 2. Component Registration Pattern |
| 109 | +```go |
| 110 | +func NewComponent(ctx *pulumi.Context, args ComponentArgs, opts ...pulumi.ResourceOption) (*Component, error) { |
| 111 | + // 1. Apply defaults to optional fields |
| 112 | + // 2. Validate arguments |
| 113 | + if err := args.Validate(); err != nil { |
| 114 | + return nil, fmt.Errorf("invalid component args: %w", err) |
| 115 | + } |
| 116 | + // 3. Convert to internal types |
| 117 | + internalArgs := args.toInternal() |
| 118 | + // 4. Register component |
| 119 | + component := &Component{} |
| 120 | + err := ctx.RegisterComponentResource(ComponentKind, args.Name, component) |
| 121 | + // 5. Create resources with pulumi.Parent(component) |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +#### 3. Resource Creation Pattern |
| 126 | +- Always create ServiceAccount first |
| 127 | +- Use consistent naming with suffixes (`-sa`, `-config`, `-deployment`, `-service`) |
| 128 | +- Apply standard labels using `utils.CreateResourceLabels()` |
| 129 | +- Set proper parent relationships with `pulumi.Parent(component)` |
| 130 | + |
| 131 | +#### 4. Validation Pattern |
| 132 | +```go |
| 133 | +func (args *ComponentArgs) Validate() error { |
| 134 | + // Required field validation |
| 135 | + if args.Namespace == "" { |
| 136 | + return fmt.Errorf("namespace is required") |
| 137 | + } |
| 138 | + // Nested validation |
| 139 | + if err := args.Config.Validate(); err != nil { |
| 140 | + return fmt.Errorf("invalid config: %w", err) |
| 141 | + } |
| 142 | + // Range validation |
| 143 | + if args.Port < 0 || args.Port > 65535 { |
| 144 | + return fmt.Errorf("invalid port: %d", args.Port) |
| 145 | + } |
| 146 | + return nil |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +#### 5. Complex Component Composition |
| 151 | +Some components (like `ethereum` and `pylon`) compose other components: |
| 152 | +- The `ethereum` package creates both execution and consensus clients |
| 153 | +- The `pylon` package wraps an ethereum node with additional configuration |
| 154 | +- Use clear ownership and dependency chains between sub-components |
| 155 | + |
| 156 | +### Best Practices for New Components |
| 157 | + |
| 158 | +1. **Start with Types**: Define your public API in `types.go` first |
| 159 | +2. **Implement Validation Early**: Write validation logic and tests before resource creation |
| 160 | +3. **Use Constants**: Define all magic numbers and strings in `constants.go` |
| 161 | +4. **Follow Naming Conventions**: |
| 162 | + - Public types/functions: PascalCase |
| 163 | + - Internal types: camelCase with "Internal" suffix |
| 164 | + - File names: lowercase with underscores |
| 165 | +5. **Document Complex Logic**: Add comments for non-obvious transformations |
| 166 | +6. **Test Thoroughly**: Aim for 100% coverage of validation logic |
| 167 | +7. **Handle Errors Gracefully**: Wrap errors with context using `fmt.Errorf` |
| 168 | +8. **Consider Defaults**: Provide sensible defaults for optional fields |
| 169 | +9. **Support Configuration**: Use ConfigMaps for configuration, Secrets for sensitive data |
| 170 | +10. **Implement Health Checks**: Add liveness, readiness, and startup probes where applicable |
| 171 | + |
| 172 | +## Testing Approach |
| 173 | + |
| 174 | +- Unit tests focus on validation logic and helper functions |
| 175 | +- Test files use the `testify/assert` library for assertions |
| 176 | +- No integration tests - components are tested when used in actual Pulumi programs |
| 177 | + |
| 178 | +## Adding New Components |
| 179 | + |
| 180 | +When creating a new component: |
| 181 | +1. Create a new directory under `pkg/` |
| 182 | +2. Follow the standard file structure (types.go, component.go, validation.go, etc.) |
| 183 | +3. Implement the `New[Component]` function that returns a `ComponentResource` |
| 184 | +4. Add comprehensive validation for all inputs |
| 185 | +5. Write unit tests for validation logic |
| 186 | +6. Update imports in consuming Pulumi programs to use the new component |
| 187 | + |
| 188 | +## Common Pitfalls |
| 189 | + |
| 190 | +1. **Go Version**: CI uses Go 1.22, but go.mod specifies 1.24.3 - ensure compatibility |
| 191 | +2. **Pulumi Context**: Always use the provided `*pulumi.Context` for resource creation |
| 192 | +3. **Resource Dependencies**: Use Pulumi's dependency system via `pulumi.DependsOn` when needed |
| 193 | +4. **Kubernetes Namespaces**: Components don't create namespaces - they expect them to exist |
0 commit comments