This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
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.
# Build for current platform
make build
# Build for all platforms (Linux, macOS, ARM64/AMD64)
make build-all
# Install to /usr/local/bin
make install# Run tests
make test
# Or: go test -v -race -coverprofile=coverage.out ./...
# Format code
make fmt
# Or: go fmt ./...
# Run linter (if golangci-lint is installed)
make lint# Show help
./bin/gitlab-cli --help
# Create users/groups/projects from config
./bin/gitlab-cli user create -f config.yaml --host https://gitlab.example.com --token <token>
# With output file
./bin/gitlab-cli user create -f config.yaml -o output.yaml
# With custom template
./bin/gitlab-cli user create -f config.yaml -o output.yaml -t template.yaml
# Cleanup users (default: only delete users created 2+ days ago)
./bin/gitlab-cli user cleanup -f config.yaml
./bin/gitlab-cli user cleanup -f config.yaml --days-old 7 # Delete users 7+ days old
./bin/gitlab-cli user cleanup -f config.yaml --days-old 0 # Delete all (ignore date)
# Delete specific users
./bin/gitlab-cli user delete --username user1,user2
# List users by prefix
./bin/gitlab-cli user list --prefix tektoncd
# Delete users by prefix (default: only delete users created 2+ days ago)
./bin/gitlab-cli user delete-by-prefix --prefix tektoncd --dry-run # Preview
./bin/gitlab-cli user delete-by-prefix --prefix tektoncd --days-old 7 # Delete 7+ days old
./bin/gitlab-cli user delete-by-prefix --prefix tektoncd --days-old 0 # Delete allThe codebase follows a strict layered architecture (top to bottom):
- Entry Layer (
cmd/gitlab-cli/main.go): Application entry point, minimal logic - Command Layer (
internal/cli/cmd.go): Cobra command definitions and orchestration - Business Logic Layer (
internal/processor/processor.go): Core resource processing logic - API Client Layer (
pkg/client/client.go): GitLab API abstraction - External Service: GitLab REST API
-
internal/: Private to this project onlyinternal/cli: CLI command builders and flow orchestrationinternal/config: Configuration loading and credential managementinternal/processor: Business logic for creating/deleting resourcesinternal/template: Go template rendering for custom outputinternal/utils: Utility functions (millisecond timestamp + suffix generation, visibility conversion)
-
pkg/: Public packages that can be imported by external projectspkg/client: GitLab API client wrapper (built on official SDK)pkg/types: Data structure definitions (UserSpec, GroupSpec, ProjectSpec, etc.)
- Dependency Injection:
ResourceProcessorreceivesGitLabClientas a field - Idempotent Operations:
ensure*methods check existence before creating - Single Responsibility: Each function does one thing, typically under 50 lines
- Builder Pattern: Commands are built using
build*Command()functions
- User runs CLI command →
cmd/gitlab-cli/main.go - Cobra routes to command handler →
internal/cli/cmd.go - Command handler initializes client and loads config
- Business logic processes resources →
internal/processor/processor.go - API client makes GitLab API calls →
pkg/client/client.go - Results are collected and optionally saved to YAML/template output
The tool supports two naming modes for resources:
-
prefixmode (default): Appendsmillisecond timestamp + suffixto username/email/group/project paths- Example:
tektoncd→tektoncd-20251030150000123-a1b2 - Used for test environments and creating multiple similar resources
- Important: Cleanup must use the output file from creation (not config file)
- Example:
-
namemode: Uses names directly from config file without modification- Example:
test-user-001→test-user-001 - Used for production or fixed-name resources
- Can use config file directly for cleanup
- Example:
- Personal Access Tokens are created with configurable scopes
- If
expires_atis not specified in config, defaults to 2 days from today - Token values are included in output files for automation purposes
- The admin token (used for API calls) requires
api+sudoscopes
Resources are created in this order:
- User
- Personal Access Token (if configured)
- Groups (with nested projects)
- User-level projects (projects not belonging to any group)
Deletion happens in reverse order to avoid dependency issues.
Both user cleanup and user delete-by-prefix commands include a safety mechanism to prevent accidental deletion of recently created users:
- Default behavior: Only delete users created 2 or more days ago
- Configurable: Use
--days-oldflag to adjust the threshold--days-old 7: Only delete users 7+ days old--days-old 0: Disable age filtering, delete all matching users
- Implementation: Compares user's
CreatedAtfield with current time - Logging: Shows creation date and days since creation for each user
This prevents accidental deletion of users created in recent test runs or CI/CD pipelines.
- 404 errors from GitLab API are handled specially (resource doesn't exist)
- Each operation includes comprehensive logging with emoji indicators (✓, ⚠)
- Errors are wrapped with context using
fmt.Errorf - Failed operations log warnings but don't always stop the entire batch
GITLAB_URL: GitLab instance URL (can be overridden by--host)GITLAB_TOKEN: Personal Access Token (can be overridden by--token)- Command-line flags take precedence over environment variables
YAML files define users with nested groups and projects:
nameMode: "prefix" or "name" (default: "prefix")token: Optional PAT configuration with scopes and expirationgroups[]: Array of groups, each with their own projectsprojects[]: User-level projects (not under any group)
See examples/user.yaml for a complete example.
The tool supports Go template rendering for custom output formats:
- Template file specified with
-tflag - Has access to
OutputConfigstruct with all created resource data - Dynamic fields available:
$.Endpoint,$.Host,$.Scheme,$.Port - User data:
.Username,.UserID,.Token.Value,.Groups, etc.
See template-example.yaml for template syntax.
GitHub Actions workflow (.github/workflows/ci.yml) runs:
- Lint (golangci-lint)
- Test (with race detection and coverage)
- Build (verifies binary creation)
All checks must pass on Go 1.23.
- Create
build*Command()function ininternal/cli/cmd.go - Create
run*()orchestration function - Add business logic method to
ResourceProcessorininternal/processor/processor.go - If needed, add new API methods to
pkg/client/client.go
- Define data structures in
pkg/types/types.go - Add API methods in
pkg/client/client.go - Add processing logic in
internal/processor/processor.go - Wire up commands in
internal/cli/cmd.go
- Package names: lowercase singular (
config, notconfigs) - Exported types/functions: PascalCase (
GitLabClient,NewGitLabClient) - Private functions: camelCase (
ensureUser,deleteProjects) - Chinese comments are used throughout for team readability
- Log messages use structured format with emoji indicators