Thank you for your interest in contributing to the Google Admin Client! We welcome contributions of all kinds: bug reports, feature requests, documentation improvements, and code contributions.
- Code of Conduct
- Getting Started
- Development Setup
- How to Contribute
- Development Guidelines
- Project Structure
- Testing
- Release Process
This project follows a Code of Conduct to ensure a welcoming environment for all contributors. By participating, you are expected to:
- Be respectful and inclusive
- Accept constructive criticism gracefully
- Focus on what is best for the community
- Show empathy towards other community members
- Fork the repository on GitHub
- Clone your fork locally:
git clone https://github.com/YOUR-USERNAME/google-admin-client.git cd google-admin-client - Add the upstream repository as a remote:
git remote add upstream https://github.com/acockrell/google-admin-client.git
- Create a feature branch from
main:git checkout -b feature/your-feature-name
- Go 1.25 or later - Install Go
- Make - For build automation
- Git - For version control
- Google Cloud account - For testing with Google Workspace APIs
-
Install dependencies:
go mod download
-
Build the project:
make build
-
Run tests:
make test -
Run linters:
make lint
We provide a VS Code devcontainer for consistent development environments:
- Install Docker and VS Code
- Install the Remote - Containers extension
- Open the project in VS Code
- Click "Reopen in Container" when prompted
The devcontainer includes:
- Go 1.25
- All development tools (golangci-lint, delve, etc.)
- Recommended VS Code extensions
- Credential file mounting support
make help # Show all available targets
make build # Build the binary
make test # Run all tests
make test-coverage # Run tests with coverage report
make lint # Run golangci-lint
make fmt # Format code with go fmt
make vet # Run go vet
make clean # Clean build artifacts
make docker-build # Build Docker image
make release-test # Test release configurationBefore creating a bug report:
- Search existing issues to avoid duplicates
- Update to the latest version to see if the issue persists
- Gather information about your environment
When creating a bug report, include:
- Clear title and description
- Steps to reproduce the issue
- Expected behavior vs. actual behavior
- Environment details:
- OS and version
- Go version
gacversion (gac version)- Relevant configuration (redact sensitive info)
- Error messages and logs
- Screenshots if applicable
Example bug report:
## Bug Description
User creation fails when adding user to multiple groups
## Steps to Reproduce
1. Run: `gac user create -g group1 -g group2 newuser@example.com`
2. Observe error: "failed to add user to group2"
## Expected Behavior
User should be created and added to both groups
## Actual Behavior
User is created but only added to group1
## Environment
- OS: macOS 14.5
- Go: 1.25.1
- gac: v0.2.0Feature requests are welcome! Before submitting:
- Check existing feature requests in GitHub Issues
- Consider if it fits the project scope
- Think about backward compatibility
When suggesting a feature:
- Describe the problem you're trying to solve
- Propose a solution with examples
- Explain why it's useful to the broader community
- Consider implementation details if possible
Example feature request:
## Feature Request: Bulk User Import from CSV
### Problem
Creating multiple users one-by-one is time-consuming for onboarding
### Proposed Solution
Add a `gac user import` command that accepts a CSV file:
```csv
email,firstName,lastName,department,groups
jdoe@example.com,John,Doe,Engineering,"dev,all-staff"- Faster bulk user creation
- Easier migration from other systems
- Reduced human error
- Support dry-run mode
- Validate all entries before creating
- Provide detailed error reporting
### Submitting Pull Requests
1. **Create an issue first** for significant changes to discuss the approach
2. **Keep PRs focused** - one feature or bug fix per PR
3. **Write tests** for new functionality
4. **Update documentation** as needed
5. **Follow code style** guidelines
6. **Ensure CI passes** before requesting review
**PR Checklist**:
- [ ] Tests pass locally (`make test`)
- [ ] Linters pass (`make lint`)
- [ ] Code is formatted (`make fmt`)
- [ ] New features have tests
- [ ] Documentation is updated
- [ ] Commit messages follow conventions
- [ ] PR description explains the change
**Example PR description**:
```markdown
## Summary
Adds input validation for phone numbers to prevent invalid formats
## Changes
- Created `ValidatePhoneNumber()` function in `cmd/validation.go`
- Added comprehensive test cases in `cmd/validation_test.go`
- Integrated validation into `user create` and `user update` commands
- Updated README with phone number format requirements
## Testing
- Added 15 test cases covering valid and invalid formats
- Tested manually with various phone number inputs
- All existing tests still pass
## Related Issues
Fixes #42
We follow standard Go conventions:
-
Use
gofmtfor formatting:make fmt
-
Follow Go Code Review Comments: https://go.dev/wiki/CodeReviewComments
-
Run linters:
make lint
We use
golangci-lintwith the configuration in.golangci.yml -
Keep functions focused - functions should do one thing well
-
Use meaningful names - prioritize clarity over brevity
-
Add comments for:
- Exported functions and types (required)
- Complex logic
- Non-obvious decisions
-
Error handling:
- Always check errors
- Provide context with error wrapping:
fmt.Errorf("operation failed: %w", err) - Use meaningful error messages
We aim for high test coverage (>80%) to ensure reliability:
-
Write tests for new code:
func TestValidateEmail(t *testing.T) { tests := []struct { name string email string wantErr bool }{ {"valid email", "user@example.com", false}, {"invalid format", "not-an-email", true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateEmail(tt.email) if (err != nil) != tt.wantErr { t.Errorf("ValidateEmail() error = %v, wantErr %v", err, tt.wantErr) } }) } }
-
Run tests before submitting:
make test -
Check coverage:
make test-coverage
-
Test types:
- Unit tests: Test individual functions in isolation
- Integration tests: Test components working together (use mocks for external APIs)
- Edge cases: Test boundary conditions and error paths
-
Use table-driven tests for testing multiple inputs
-
Mock external dependencies (Google APIs) for reliable tests
Good documentation is crucial:
-
Code comments:
- All exported functions, types, and constants must have doc comments
- Start with the name of the thing being documented
- Use complete sentences
// ValidateEmail checks if the provided email address is valid according to RFC 5322. // It returns an error if the email is invalid or exceeds length limits. func ValidateEmail(email string) error { // ... }
-
README updates:
- Add new features to the usage examples
- Update command reference for new commands/flags
- Add troubleshooting entries for common issues
-
Inline comments:
- Explain "why" not "what" (the code shows what)
- Document non-obvious decisions
- Mark TODOs clearly:
// TODO: Add retry logic
We follow Conventional Commits:
Format: <type>(<scope>): <description>
Types:
feat:- New featurefix:- Bug fixdocs:- Documentation changesstyle:- Code style changes (formatting, missing semi-colons, etc.)refactor:- Code refactoring without changing functionalitytest:- Adding or updating testschore:- Maintenance tasks (dependencies, build config, etc.)perf:- Performance improvementsci:- CI/CD changes
Examples:
feat(user): add bulk user import from CSV
fix(auth): resolve token refresh race condition
docs(readme): add installation instructions for Windows
test(validation): add test cases for phone number validation
refactor(client): simplify OAuth2 client initialization
chore(deps): update google.golang.org/api to v0.251.0Commit message guidelines:
- Use imperative mood: "add feature" not "added feature"
- Keep subject line under 72 characters
- Add body for complex changes explaining what and why
- Reference issues:
Fixes #123orCloses #456
google-admin-client/
├── cmd/ # Command implementations
│ ├── root.go # Root command and configuration
│ ├── user.go # User command group
│ ├── user-create.go # User creation command
│ ├── user-list.go # User listing command
│ ├── user-update.go # User update command
│ ├── group.go # Group command group
│ ├── group-list.go # Group listing command
│ ├── calendar.go # Calendar command group
│ ├── transfer.go # Transfer command
│ ├── client.go # Google API client setup
│ ├── validation.go # Input validation functions
│ └── *_test.go # Test files
├── .github/
│ └── workflows/ # GitHub Actions workflows
├── examples/ # Example configurations
├── main.go # Application entry point
├── go.mod # Go module definition
├── go.sum # Go module checksums
├── Makefile # Build automation
├── Dockerfile # Docker image definition
├── .goreleaser.yml # Release configuration
├── README.md # User documentation
├── CONTRIBUTING.md # This file
├── ARCHITECTURE.md # Technical architecture docs
├── DEBUGGING.md # Debugging guide
├── CREDENTIALS.md # OAuth2 setup guide
└── TODO.md # Project roadmap
-
Create the command file in
cmd/:// cmd/myfeature-action.go package cmd import ( "github.com/spf13/cobra" ) var myfeatureActionCmd = &cobra.Command{ Use: "action [args]", Short: "Brief description", Long: `Detailed description with examples`, RunE: myfeatureActionRunFunc, } func init() { myfeatureCmd.AddCommand(myfeatureActionCmd) // Add flags here } func myfeatureActionRunFunc(cmd *cobra.Command, args []string) error { // Implementation return nil }
-
Write tests in
cmd/myfeature-action_test.go -
Update README with usage examples
-
Add integration with existing client setup
# Run all tests
make test
# Run tests with coverage
make test-coverage
# Run tests with race detection
go test -race ./...
# Run specific test
go test -v -run TestValidateEmail ./cmd
# Run tests in a specific package
go test ./cmd- Place test files alongside the code they test
- Name test files with
_test.gosuffix - Use table-driven tests for multiple test cases
- Group related tests with subtests
For Google API clients, use interfaces and provide mock implementations:
type UserService interface {
Insert(*admin.User) (*admin.User, error)
Get(userKey string) (*admin.User, error)
}
type mockUserService struct {
insertFunc func(*admin.User) (*admin.User, error)
getFunc func(string) (*admin.User, error)
}
func (m *mockUserService) Insert(u *admin.User) (*admin.User, error) {
return m.insertFunc(u)
}Releases are automated via GitHub Actions and GoReleaser. See RELEASE.md for details.
We use Semantic Versioning:
- MAJOR version for incompatible API changes
- MINOR version for backward-compatible functionality
- PATCH version for backward-compatible bug fixes
- Update version in relevant files
- Update CHANGELOG (if maintained)
- Create and push a version tag:
git tag -a v0.3.0 -m "Release v0.3.0" git push origin v0.3.0 - GitHub Actions will automatically build and publish the release
- Documentation: Check README.md, ARCHITECTURE.md, and DEBUGGING.md
- Issues: Search existing GitHub Issues
- Discussions: Start a GitHub Discussion for questions
By contributing to this project, you agree that your contributions will be licensed under the same license as the project (MIT License).
Thank you for contributing to making Google Admin Client better for everyone!