https://github.com/klauern/ownershit
https://github.com/klauern/ownershit
Last synced: 2 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/klauern/ownershit
- Owner: klauern
- Created: 2020-01-25T21:16:40.000Z (over 6 years ago)
- Default Branch: main
- Last Pushed: 2026-01-19T12:56:08.000Z (5 months ago)
- Last Synced: 2026-04-11T03:47:37.947Z (2 months ago)
- Language: Go
- Homepage:
- Size: 1 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# Ownershit
A comprehensive CLI tool for managing GitHub repository ownership, permissions, and branch protection rules across your organization.
## Features
- **Team Permissions**: Manage admin/push/pull access control for teams across repositories
- **Branch Protection**: Advanced branch protection rules with status checks, admin enforcement, and push restrictions
- **Repository Settings**: Configure wiki, issues, and projects settings
- **Label Management**: Sync default labels across repositories with emoji support
- **Topic Management**: Mass-assign repository topics/tags additively or by replacement
- **Repository Archiving**: Find and archive inactive repositories based on configurable criteria
- **Merge Strategy Control**: Configure allowed merge types (merge commits, squash, rebase)
## Installation
### Using Go Install
```bash
go install github.com/klauern/ownershit/cmd/ownershit@latest
```
### Using Task (Development)
```bash
# Clone the repository
git clone https://github.com/klauern/ownershit.git
cd ownershit
# Set up development environment
task dev
# Build and install locally
task install
```
## Quick Start
1. Create a configuration file:
```bash
ownershit init
```
1. Edit `repositories.yaml` with your organization details
1. Set up your GitHub token:
```bash
export GITHUB_TOKEN=your_github_token_here
```
1. Synchronize your repositories:
```bash
ownershit sync
```
## Generate Configuration from Existing Repositories
If you already have repositories on GitHub and want to generate a configuration file automatically:
```bash
# Set your GitHub token
export GITHUB_TOKEN=your_github_token_here
# Generate configuration for your user
./scripts/generate-config.sh klauern
# Or specify a custom output file
./scripts/generate-config.sh klauern my-repositories.yaml
```
This will:
- Fetch all repositories you own (not forked repos)
- Analyze settings to determine sensible defaults
- Generate a complete YAML configuration file
- Include all repository metadata (descriptions, settings, etc.)
After generation, you should:
1. Review the generated file
2. Add team permissions (if working with an organization)
3. Configure branch protection rules
4. Add default labels and topics
5. Test with `--dry-run` before applying
For more details, see [scripts/README.md](scripts/README.md).
## Dry-Run Mode
Preview changes before applying them using the `--dry-run` flag:
```bash
# Preview all changes that would be made
ownershit sync --config repositories.yaml --dry-run
# With debug output for detailed analysis
ownershit sync --config repositories.yaml --dry-run --debug
```
### What Dry-Run Shows
Dry-run mode displays all planned changes without making any API calls to GitHub:
- Team permissions that would be added to repositories
- Repository features that would be changed (wiki, issues, projects)
- Branch protection rules that would be applied
- Branch merge strategies that would be updated
- Delete-branch-on-merge settings that would be configured
### Example Output
```text
INFO DRY RUN MODE - No changes will be applied
INFO DRY RUN: Analyzing configuration changes...
INFO Would process repository repository=account_vending_machine
INFO Would update branch merge strategies repository=account_vending_machine
INFO Would apply enhanced branch protection rules branch=main repository=account_vending_machine
INFO Would update repository features issues=false projects=false wiki=false repository=account_vending_machine
INFO Would update delete_branch_on_merge setting delete_branch_on_merge=true repository=account_vending_machine
...
INFO DRY RUN: Complete. No changes were applied.
```
### Use Cases
- **Safety**: Verify configuration changes before applying them to production repositories
- **Learning**: Understand what the tool will do without making actual changes
- **Debugging**: Identify configuration issues or unexpected behavior
- **CI/CD**: Validate configurations in automated pipelines
- **Documentation**: Generate reports of planned changes for team review
## Commands
### Core Commands
| Command | Description | Example |
| ------------- | --------------------------------------- | -------------------------------------------------- |
| `init` | Create a stub configuration file | `ownershit init` |
| `sync` | Synchronize all repository settings | `ownershit sync --config repositories.yaml`
`ownershit sync --dry-run` |
| `branches` | Update branch merge strategies | `ownershit branches` |
| `label` | Sync default labels across repositories | `ownershit label` |
| `topics` | Sync repository topics/tags | `ownershit topics --additive=true` |
| `import` | Import repository configuration as YAML | `ownershit import owner/repo --output config.yaml` |
| `permissions` | Show required GitHub token permissions | `ownershit permissions` |
| `ratelimit` | Check GitHub API rate limits | `ownershit ratelimit` |
### Archive Commands
| Command | Description | Example |
| ----------------- | ------------------------------------------- | ------------------------------------------------------- |
| `archive query` | Find repositories eligible for archiving | `ownershit archive query --username myuser --days 365` |
| `archive execute` | Archive selected repositories interactively | `ownershit archive execute --username myuser --stars 0` |
### Import/Export Commands
| Command | Description | Example |
| ------------ | ---------------------------------------------- | ----------------------------------------------------------------- |
| `import-csv` | Import multiple repositories and export as CSV | `ownershit import-csv owner/repo1 owner/repo2 --output repos.csv` |
### Global Flags
| Flag | Description | Default | Environment Variable |
| ------------- | ----------------------- | ------------------- | -------------------- |
| `--config` | Configuration file path | `repositories.yaml` | - |
| `--debug, -d` | Enable debug logging | `false` | `OWNERSHIT_DEBUG` |
## Configuration
### Basic Configuration
Create a `repositories.yaml` file with your organization settings:
```yaml
# Your GitHub organization name (REQUIRED)
organization: your-org-name
# Global defaults for repository features (optional)
# These apply to all repositories unless explicitly overridden
defaults:
wiki: false # Disable wikis by default
issues: true # Enable issues by default
projects: false # Disable projects by default
delete_branch_on_merge: true # Auto-delete head branches after PR merge
# Team permissions for repositories
team:
- name: developers
level: push
- name: maintainers
level: admin
- name: security-team
level: admin
# Repository configurations
repositories:
- name: my-app
wiki: true # Override: enable wiki for this repo
# issues: inherits default (true)
# projects: inherits default (false)
- name: internal-tool
# wiki: inherits default (false)
# issues: inherits default (true)
projects: true # Override: enable projects for this repo
```
### Advanced Branch Protection
Configure comprehensive branch protection rules:
```yaml
branches:
# Basic protection
require_pull_request_reviews: true
require_approving_count: 2
require_code_owners: true
# Merge strategy controls
allow_merge_commit: false
allow_squash_merge: true
allow_rebase_merge: true
# Status checks
require_status_checks: true
status_checks:
- "ci/build"
- "ci/test"
- "security/scan"
require_up_to_date_branch: true
# Advanced protection
enforce_admins: true
restrict_pushes: true
push_allowlist:
- "admin-team"
- "deploy-team"
require_conversation_resolution: true
require_linear_history: true
allow_force_pushes: false
allow_deletions: false
```
### Label Management
Define default labels for all repositories:
```yaml
default_labels:
- name: "bug"
color: "d73a4a"
emoji: "🐛"
description: "Something isn't working"
- name: "enhancement"
color: "a2eeef"
emoji: "✨"
description: "New feature or request"
- name: "security"
color: "ff6b6b"
emoji: "🔒"
description: "Security-related issue"
```
### Topic Management
Define default topics to apply across repositories:
```yaml
default_topics:
- "golang"
- "cli"
- "github-management"
- "internal-tool"
```
### Repository Feature Defaults
Configure global defaults for repository features. These defaults apply to all repositories unless explicitly overridden at the repository level.
#### Available Default Settings
Within the `defaults` block, you can configure:
- `wiki` - Enable/disable wikis for all repositories
- `issues` - Enable/disable issue tracking for all repositories
- `projects` - Enable/disable GitHub projects for all repositories
- `delete_branch_on_merge` - Automatically delete head branches after pull request merge
#### How It Works
1. **Global Defaults**: Set default values in the `defaults` block of your configuration
2. **Per-Repository Overrides**: Specify values at the repository level to override defaults
3. **Inheritance**: If a repository doesn't specify a value, it inherits from the default
4. **Nil Behavior**: If no default is set and no repository value is provided, no change is made
#### Example Configuration
```yaml
organization: my-org
# Set defaults - most repos don't need wikis or projects
defaults:
wiki: false
issues: true
projects: false
delete_branch_on_merge: true
repositories:
# Inherits all defaults (wiki: false, issues: true, projects: false)
- name: simple-tool
# Override wiki only - enable for documentation
- name: main-app
wiki: true
# issues: inherits default (true)
# projects: inherits default (false)
# Override projects only - needs project board
- name: team-planning
# wiki: inherits default (false)
# issues: inherits default (true)
projects: true
# Override all defaults
- name: special-repo
wiki: true
issues: false
projects: true
```
#### Migration from Explicit Settings
If you have an existing configuration with explicit settings on every repository, you can migrate to using defaults:
**Before** (explicit settings everywhere):
```yaml
repositories:
- name: repo1
wiki: false
issues: true
projects: false
- name: repo2
wiki: false
issues: true
projects: false
- name: repo3
wiki: false
issues: true
projects: false
```
**After** (using nested defaults):
```yaml
defaults:
wiki: false
issues: true
projects: false
delete_branch_on_merge: true
repositories:
- name: repo1
- name: repo2
- name: repo3
```
#### Backward Compatibility
This feature is fully backward compatible. Old-style `default_*` fields are automatically migrated to the new nested `defaults` block at runtime:
**Old format** (still supported):
```yaml
default_wiki: false
default_issues: true
default_projects: false
```
**New format** (recommended):
```yaml
defaults:
wiki: false
issues: true
projects: false
delete_branch_on_merge: true
```
When using the old format, you'll see a migration message in the logs, but everything will continue to work seamlessly.
## Development
This project uses [Task](https://taskfile.dev) for task management.
### Development Setup
```bash
# Set up development environment
task dev
# Install development dependencies
# - go mod tidy
# - install mockgen
# - install govulncheck
# - install bump tool
```
### Building and Testing
```bash
# Build binaries
task build
# Run tests with coverage
task test
# View test coverage in browser
task test-cover
# Run tests with security checks
task test-all
```
### Code Quality
```bash
# Format code
task fmt
# Run linter
task lint
# Generate mocks for testing
task mocks
# Run security vulnerability check
task security
```
### GraphQL Client Management
```bash
# Download latest GitHub GraphQL schema
task gql:download-schema
# Generate GraphQL client code
task gql:generate-client
```
### Releases
```bash
# Show release options
task release
# Create patch release (v0.6.0 → v0.6.1)
task release:patch
# Create minor release (v0.6.0 → v0.7.0)
task release:minor
# Create major release (v0.6.0 → v1.0.0)
task release:major
# Create release candidate (v0.6.0 → v0.7.0-rc1)
task release:rc
```
## Utilities
### Backfill Repository Features
The `scripts/backfill-repo-features.py` script helps migrate existing configurations by detecting actual feature usage and updating your YAML file:
```bash
# Requires uv or Python 3.11+ with PyGithub and PyYAML
export GITHUB_TOKEN=your_token
uv run scripts/backfill-repo-features.py repositories.yaml
```
This script:
- Checks actual wiki/issues/projects usage for each repository
- Adds explicit settings where they differ from defaults
- Removes redundant explicit settings that match defaults
- Creates a `.backup` file before making changes
## Architecture
### Core Components
- **GitHub REST v3 API**: Team permissions, issue labels, merge strategies
- **GitHub GraphQL v4 API**: Repository settings, branch protection, archiving
- **Dual API Approach**: Leverages strengths of both APIs for comprehensive coverage
### Configuration Structure
```
repositories.yaml
├── organization: string # GitHub organization name
├── defaults: RepositoryDefaults # Global defaults (optional)
│ ├── wiki: bool # Default wiki setting
│ ├── issues: bool # Default issues setting
│ ├── projects: bool # Default projects setting
│ └── delete_branch_on_merge: bool # Default auto-delete branches
├── branches: BranchPermissions # Branch protection rules
├── team: []TeamPermission # Team access levels
├── repositories: []Repository # Repository configurations
├── default_labels: []Label # Default labels for all repos
└── default_topics: []string # Default topics for all repos
```
### Key Files
- `cmd/ownershit/main.go` - CLI entry point and commands
- `config.go` - Configuration parsing and validation
- `github_v3.go` - REST API client implementation
- `github_v4.go` - GraphQL API client implementation
- `branch.go` - Branch protection management
- `archiving_v4.go` - Repository archiving functionality
## GitHub Token Setup
1. Create a Personal Access Token at:
1. Required scopes:
- `repo` - Full repository access
- `admin:org` - Organization admin access (for team management)
1. Set the token as an environment variable:
```bash
export GITHUB_TOKEN=your_token_here
```
## Examples
### Complete Organization Setup
```bash
# Initialize configuration
ownershit init
# Edit repositories.yaml with your settings
# Apply all configurations
ownershit sync --config repositories.yaml --debug
```
### Archive Inactive Repositories
```bash
# Find repositories inactive for 365+ days with 0 stars
ownershit archive query --username myuser --days 365 --stars 0
# Interactively select and archive repositories
ownershit archive execute --username myuser --days 365 --stars 0
```
### Update Branch Protection Only
```bash
# Apply only branch merge strategy changes
ownershit branches --config repositories.yaml
```
### Sync Labels Across Repositories
```bash
# Update labels on all configured repositories
ownershit label --config repositories.yaml
```
### Sync Topics Across Repositories
```bash
# Additively merge topics (default) - preserves existing topics
ownershit topics --config repositories.yaml
# Replace all topics with configured ones
ownershit topics --config repositories.yaml --additive=false
```
### Import Repository Configuration
```bash
# Import single repository configuration as YAML
ownershit import myorg/myrepo --output repo-config.yaml
# Import repository to stdout and preview settings
ownershit import myorg/myrepo
```
### Bulk Export Repository Data as CSV
```bash
# Export multiple repositories to CSV file
ownershit import-csv myorg/repo1 myorg/repo2 myorg/repo3 --output repositories.csv
# Export from batch file with repository list
ownershit import-csv --batch-file repo-list.txt --output export.csv
# Append to existing CSV file
ownershit import-csv myorg/new-repo --output existing-data.csv --append
```
## Troubleshooting
### Common Issues
**Configuration file not found**
```bash
# Create a new configuration file
ownershit init
```
**GitHub API rate limits**
```bash
# Check current rate limit status
ownershit ratelimit --debug
```
**Permission errors**
```bash
# Verify token has required scopes:
# - repo (full repository access)
# - admin:org (organization admin access)
```
**Debug mode**
```bash
# Enable verbose logging
ownershit sync --debug
# or
export OWNERSHIT_DEBUG=true
ownershit sync
```
### Getting Help
- Run `ownershit --help` for command help
- Run `ownershit --help` for command-specific help
- Check the [example configuration](testdata/enhanced-branch-protection-example.yaml) for advanced features
- Enable debug mode (`--debug`) for detailed logging
## Contributing
1. Fork the repository
1. Create a feature branch (`git checkout -b feature/amazing-feature`)
1. Make your changes
1. Run tests (`task test-all`)
1. Commit your changes (`git commit -m 'Add amazing feature'`)
1. Push to the branch (`git push origin feature/amazing-feature`)
1. Open a Pull Request
## License
This project is licensed under the MIT License - see the LICENSE file for details.
## Authors
- **Nick Klauer** - *Initial work* - [klauern](https://github.com/klauern)