|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +This is a GitLab CLI tool built with the official GitLab Go SDK for automating user, group, and project management. The tool uses a layered architecture with clear separation between CLI commands, business logic, and API client operations. |
| 8 | + |
| 9 | +## Build and Development Commands |
| 10 | + |
| 11 | +### Building |
| 12 | +```bash |
| 13 | +# Build for current platform |
| 14 | +make build |
| 15 | + |
| 16 | +# Build for all platforms (Linux, macOS, ARM64/AMD64) |
| 17 | +make build-all |
| 18 | + |
| 19 | +# Install to /usr/local/bin |
| 20 | +make install |
| 21 | +``` |
| 22 | + |
| 23 | +### Testing and Quality |
| 24 | +```bash |
| 25 | +# Run tests |
| 26 | +make test |
| 27 | +# Or: go test -v -race -coverprofile=coverage.out ./... |
| 28 | + |
| 29 | +# Format code |
| 30 | +make fmt |
| 31 | +# Or: go fmt ./... |
| 32 | + |
| 33 | +# Run linter (if golangci-lint is installed) |
| 34 | +make lint |
| 35 | +``` |
| 36 | + |
| 37 | +### Running |
| 38 | +```bash |
| 39 | +# Show help |
| 40 | +./bin/gitlab-cli --help |
| 41 | + |
| 42 | +# Create users/groups/projects from config |
| 43 | +./bin/gitlab-cli user create -f config.yaml --host https://gitlab.example.com --token <token> |
| 44 | + |
| 45 | +# With output file |
| 46 | +./bin/gitlab-cli user create -f config.yaml -o output.yaml |
| 47 | + |
| 48 | +# With custom template |
| 49 | +./bin/gitlab-cli user create -f config.yaml -o output.yaml -t template.yaml |
| 50 | + |
| 51 | +# Cleanup users (default: only delete users created 2+ days ago) |
| 52 | +./bin/gitlab-cli user cleanup -f config.yaml |
| 53 | +./bin/gitlab-cli user cleanup -f config.yaml --days-old 7 # Delete users 7+ days old |
| 54 | +./bin/gitlab-cli user cleanup -f config.yaml --days-old 0 # Delete all (ignore date) |
| 55 | + |
| 56 | +# Delete specific users |
| 57 | +./bin/gitlab-cli user delete --username user1,user2 |
| 58 | + |
| 59 | +# List users by prefix |
| 60 | +./bin/gitlab-cli user list --prefix tektoncd |
| 61 | + |
| 62 | +# Delete users by prefix (default: only delete users created 2+ days ago) |
| 63 | +./bin/gitlab-cli user delete-by-prefix --prefix tektoncd --dry-run # Preview |
| 64 | +./bin/gitlab-cli user delete-by-prefix --prefix tektoncd --days-old 7 # Delete 7+ days old |
| 65 | +./bin/gitlab-cli user delete-by-prefix --prefix tektoncd --days-old 0 # Delete all |
| 66 | +``` |
| 67 | + |
| 68 | +## Architecture |
| 69 | + |
| 70 | +### Layered Structure |
| 71 | + |
| 72 | +The codebase follows a strict layered architecture (top to bottom): |
| 73 | + |
| 74 | +1. **Entry Layer** (`cmd/gitlab-cli/main.go`): Application entry point, minimal logic |
| 75 | +2. **Command Layer** (`internal/cli/cmd.go`): Cobra command definitions and orchestration |
| 76 | +3. **Business Logic Layer** (`internal/processor/processor.go`): Core resource processing logic |
| 77 | +4. **API Client Layer** (`pkg/client/client.go`): GitLab API abstraction |
| 78 | +5. **External Service**: GitLab REST API |
| 79 | + |
| 80 | +### Package Visibility |
| 81 | + |
| 82 | +- **`internal/`**: Private to this project only |
| 83 | + - `internal/cli`: CLI command builders and flow orchestration |
| 84 | + - `internal/config`: Configuration loading and credential management |
| 85 | + - `internal/processor`: Business logic for creating/deleting resources |
| 86 | + - `internal/template`: Go template rendering for custom output |
| 87 | + - `internal/utils`: Utility functions (timestamp generation, visibility conversion) |
| 88 | + |
| 89 | +- **`pkg/`**: Public packages that can be imported by external projects |
| 90 | + - `pkg/client`: GitLab API client wrapper (built on official SDK) |
| 91 | + - `pkg/types`: Data structure definitions (UserSpec, GroupSpec, ProjectSpec, etc.) |
| 92 | + |
| 93 | +### Key Design Patterns |
| 94 | + |
| 95 | +1. **Dependency Injection**: `ResourceProcessor` receives `GitLabClient` as a field |
| 96 | +2. **Idempotent Operations**: `ensure*` methods check existence before creating |
| 97 | +3. **Single Responsibility**: Each function does one thing, typically under 50 lines |
| 98 | +4. **Builder Pattern**: Commands are built using `build*Command()` functions |
| 99 | + |
| 100 | +### Data Flow |
| 101 | + |
| 102 | +1. User runs CLI command → `cmd/gitlab-cli/main.go` |
| 103 | +2. Cobra routes to command handler → `internal/cli/cmd.go` |
| 104 | +3. Command handler initializes client and loads config |
| 105 | +4. Business logic processes resources → `internal/processor/processor.go` |
| 106 | +5. API client makes GitLab API calls → `pkg/client/client.go` |
| 107 | +6. Results are collected and optionally saved to YAML/template output |
| 108 | + |
| 109 | +## Important Implementation Details |
| 110 | + |
| 111 | +### Naming Modes |
| 112 | + |
| 113 | +The tool supports two naming modes for resources: |
| 114 | + |
| 115 | +- **`prefix` mode (default)**: Appends timestamp to username/email/group/project paths |
| 116 | + - Example: `tektoncd` → `tektoncd-20251030150000` |
| 117 | + - Used for test environments and creating multiple similar resources |
| 118 | + - **Important**: Cleanup must use the output file from creation (not config file) |
| 119 | + |
| 120 | +- **`name` mode**: Uses names directly from config file without modification |
| 121 | + - Example: `test-user-001` → `test-user-001` |
| 122 | + - Used for production or fixed-name resources |
| 123 | + - Can use config file directly for cleanup |
| 124 | + |
| 125 | +### Token Management |
| 126 | + |
| 127 | +- Personal Access Tokens are created with configurable scopes |
| 128 | +- If `expires_at` is not specified in config, defaults to 2 days from today |
| 129 | +- Token values are included in output files for automation purposes |
| 130 | +- The admin token (used for API calls) requires `api` + `sudo` scopes |
| 131 | + |
| 132 | +### Resource Hierarchy |
| 133 | + |
| 134 | +Resources are created in this order: |
| 135 | +1. User |
| 136 | +2. Personal Access Token (if configured) |
| 137 | +3. Groups (with nested projects) |
| 138 | +4. User-level projects (projects not belonging to any group) |
| 139 | + |
| 140 | +Deletion happens in reverse order to avoid dependency issues. |
| 141 | + |
| 142 | +### User Age Filtering (Cleanup/Delete Safety Feature) |
| 143 | + |
| 144 | +Both `user cleanup` and `user delete-by-prefix` commands include a safety mechanism to prevent accidental deletion of recently created users: |
| 145 | + |
| 146 | +- **Default behavior**: Only delete users created 2 or more days ago |
| 147 | +- **Configurable**: Use `--days-old` flag to adjust the threshold |
| 148 | + - `--days-old 7`: Only delete users 7+ days old |
| 149 | + - `--days-old 0`: Disable age filtering, delete all matching users |
| 150 | +- **Implementation**: Compares user's `CreatedAt` field with current time |
| 151 | +- **Logging**: Shows creation date and days since creation for each user |
| 152 | + |
| 153 | +This prevents accidental deletion of users created in recent test runs or CI/CD pipelines. |
| 154 | + |
| 155 | +### Error Handling |
| 156 | + |
| 157 | +- 404 errors from GitLab API are handled specially (resource doesn't exist) |
| 158 | +- Each operation includes comprehensive logging with emoji indicators (✓, ⚠) |
| 159 | +- Errors are wrapped with context using `fmt.Errorf` |
| 160 | +- Failed operations log warnings but don't always stop the entire batch |
| 161 | + |
| 162 | +### Environment Variables |
| 163 | + |
| 164 | +- `GITLAB_URL`: GitLab instance URL (can be overridden by `--host`) |
| 165 | +- `GITLAB_TOKEN`: Personal Access Token (can be overridden by `--token`) |
| 166 | +- Command-line flags take precedence over environment variables |
| 167 | + |
| 168 | +## Configuration File Structure |
| 169 | + |
| 170 | +YAML files define users with nested groups and projects: |
| 171 | +- `nameMode`: "prefix" or "name" (default: "prefix") |
| 172 | +- `token`: Optional PAT configuration with scopes and expiration |
| 173 | +- `groups[]`: Array of groups, each with their own projects |
| 174 | +- `projects[]`: User-level projects (not under any group) |
| 175 | + |
| 176 | +See `examples/user.yaml` for a complete example. |
| 177 | + |
| 178 | +## Template System |
| 179 | + |
| 180 | +The tool supports Go template rendering for custom output formats: |
| 181 | +- Template file specified with `-t` flag |
| 182 | +- Has access to `OutputConfig` struct with all created resource data |
| 183 | +- Dynamic fields available: `$.Endpoint`, `$.Host`, `$.Scheme`, `$.Port` |
| 184 | +- User data: `.Username`, `.UserID`, `.Token.Value`, `.Groups`, etc. |
| 185 | + |
| 186 | +See `template-example.yaml` for template syntax. |
| 187 | + |
| 188 | +## CI/CD |
| 189 | + |
| 190 | +GitHub Actions workflow (`.github/workflows/ci.yml`) runs: |
| 191 | +- Lint (golangci-lint) |
| 192 | +- Test (with race detection and coverage) |
| 193 | +- Build (verifies binary creation) |
| 194 | + |
| 195 | +All checks must pass on Go 1.23. |
| 196 | + |
| 197 | +## Adding New Features |
| 198 | + |
| 199 | +### To add a new command: |
| 200 | +1. Create `build*Command()` function in `internal/cli/cmd.go` |
| 201 | +2. Create `run*()` orchestration function |
| 202 | +3. Add business logic method to `ResourceProcessor` in `internal/processor/processor.go` |
| 203 | +4. If needed, add new API methods to `pkg/client/client.go` |
| 204 | + |
| 205 | +### To add a new resource type: |
| 206 | +1. Define data structures in `pkg/types/types.go` |
| 207 | +2. Add API methods in `pkg/client/client.go` |
| 208 | +3. Add processing logic in `internal/processor/processor.go` |
| 209 | +4. Wire up commands in `internal/cli/cmd.go` |
| 210 | + |
| 211 | +## Code Style |
| 212 | + |
| 213 | +- Package names: lowercase singular (`config`, not `configs`) |
| 214 | +- Exported types/functions: PascalCase (`GitLabClient`, `NewGitLabClient`) |
| 215 | +- Private functions: camelCase (`ensureUser`, `deleteProjects`) |
| 216 | +- Chinese comments are used throughout for team readability |
| 217 | +- Log messages use structured format with emoji indicators |
0 commit comments