README
¶
github-org-manager-go (gomgr)
A fast, idempotent GitHub Organization Manager written in Go. Define your org as YAML and apply it with a single command. Ships with a release workflow and a CI workflow to run sync against one or many org-config folders.
Highlights
- ✅ YAML-driven org config (
app.yaml,org.yaml,teams/*.yaml) - ✅ Teams, maintainers, members (idempotent add/update)
- ✅ Repo permission grants (pull/triage/push/maintain/admin)
- ✅ Repository topics: add topics/labels to repositories for organization
- ✅ Repository pinning: pin important repositories to organization profile (⚠️ GitHub API limitation: not currently supported for organizations - configuration accepted but manual pinning required via web UI)
- ✅ Optional: create repos that don’t exist (
create_repo: true) - ✅ Optional: inject
.github/renovate.jsoninto repos - ✅ Warnings & cleanups: unmanaged teams, members without team, unmanaged repos
- ✅ Optional hard cleanups: delete unmanaged teams, remove members without team, delete unmanaged repos
- ✅ Auth: GitHub App (recommended) or PAT
- ✅
--dryplan with state comparison showing current GitHub state vs desired config state - ✅ Cross‑platform binaries via GitHub Releases;
gomgr versionstamped at build
Install
Option 1 — Download a release
Grab a binary from Releases and put it on your $PATH.
gomgr version
Option 2 — Build from source
go build -trimpath -buildvcs=true -ldflags "-s -w -X github.com/DragonSecurity/github-org-manager-go/internal/version.Version=$(git describe --tags --always --dirty)" -o gomgr .
Quickstart
- Prepare config directory (example shown below):
<config>/
├─ app.yaml
├─ org.yaml
└─ teams/
└─ platform-team.yaml
- Auth (choose one):
-
GitHub App (recommended)
export GITHUB_APP_ID=1719369 export GITHUB_APP_PRIVATE_KEY="$(cat /path/to/private-key.pem)" # or set app.yaml: private_key: <path or PEM>Installation: install the app on your target org and grant the permissions listed below.
-
PAT
export GITHUB_TOKEN=<personal-access-token>
- Run a dry run, then apply
gomgr sync -c <config> --dry # Shows JSON plan + summary of changes
gomgr sync -c <config> # Actually applies changes
The dry run output includes:
- Complete JSON plan with all change details
- Current vs Desired State comparison - shows what exists in GitHub vs what's in your config
- Summary showing counts by scope and action
- List of any warnings
Example summary output:
================================================================
Summary of Proposed Changes
================================================================
Current State vs Desired State:
--------------------------------
Teams: 5 → 6 (+1)
Team Members: 12 → 14 (+2)
Repositories: 15 → 18 (+3)
Repo Permissions: 22 → 28 (+6)
Total changes: 7
Changes by scope:
repo-file: 3
repo-pin: 1
repo-topics: 1
team-repo: 3
Changes by action:
ensure: 5
grant: 3
Warnings: 1
- Skipping pin for KaMuses/platform-index: GitHub API does not support pinning to organization profiles
================================================================
Configuration
app.yaml
org: KaMuses
# GitHub App auth (preferred):
app_id: 1719369 # or set via env GITHUB_APP_ID
private_key: ./app-private.pem # file path or raw PEM; env GITHUB_APP_PRIVATE_KEY also works
dry_warnings:
warn_unmanaged_teams: true
warn_members_without_any_team: true
warn_unmanaged_repos: true # warn about repos not defined in any team
# Optional enforcement / extras:
remove_members_without_team: true # remove org members not in any team
delete_unconfigured_teams: true # delete teams not defined in YAML
delete_unmanaged_repos: false # delete repos not defined in any team (DESTRUCTIVE!)
create_repo: true # create repos if missing when referenced by teams
add_renovate_config: true # create .github/renovate.json in repos
renovate_config: |
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>DragonSecurity/renovate-presets"]
}
org.yaml
Currently used for owners (extension point):
owners:
- alice
- bob
teams/*.yaml
name: Platform Team
slug: platform-team # optional; default = kebab(name)
description: Core platform engineers
privacy: closed # closed | secret
parents: [] # (future enhancement)
maintainers:
- alice
members:
- bob
repositories:
# Simple permission string (backward compatible)
infra: maintain # pull|triage|push|maintain|admin
# Advanced config with topics and pinning
api:
permission: push
topics:
- backend
- api
- project-platform
# Repository with pinning (note: pinning is not supported by GitHub API for organizations)
platform-index:
permission: admin
topics:
- project-platform
- documentation
pinned: true # Will be shown in plan but skipped with a warning - pin manually via GitHub web UI
Loader ignores non‑YAML files in
teams/and skips empty/invalid entries.
Project Organization Pattern
gomgr supports organizing repositories by project with topics, pinning, and naming conventions:
Note: Repository pinning is not currently supported by the GitHub API for organization profiles. The
pinnedfield is accepted in configuration but the actual pinning operation will be skipped with a warning. You can manually pin repositories through the GitHub web interface.
Example: Multi-repo project setup
- Define a project name (slug), e.g.,
platform - Prefix all project repositories:
platform-api,platform-web,platform-infra - Tag all repos with topic:
project-platform - Create an index repository:
platform-indexwith README linking to all project repos - Pin the index repo to make it prominent on the org profile (must be done manually via GitHub web UI due to API limitations)
Example configuration:
name: Platform Team
repositories:
platform-index:
permission: admin
topics:
- project-platform
- documentation
pinned: true
platform-api:
permission: push
topics:
- project-platform
- backend
platform-web:
permission: push
topics:
- project-platform
- frontend
platform-infra:
permission: maintain
topics:
- project-platform
- infrastructure
This pattern makes it easy to:
- Discover all repositories belonging to a project using GitHub's topic search
- Provide project documentation via the index repository (can be manually pinned via GitHub web UI)
- Maintain consistent naming and organization across projects
Auth & Permissions
GitHub App (recommended)
Set GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY (or app_id/private_key in app.yaml). The app must be installed on the org.
Typical minimum permissions (tighten to least privilege for your use-case):
- Organization: Members (Read/Write), Teams (Read/Write)
- Repository: Administration (Read/Write) to grant team access & create repos; Contents (Read/Write) to create files like Renovate config; Metadata (Read)
Exact permissions depend on which features you enable (e.g., if you don’t create repos or write files, you can drop those).
Personal Access Token (PAT)
Use a classic PAT with scopes:
admin:org(manage teams/members)repo(set team repo access and create repos)read:org(read org metadata)
CLI
-
gomgr sync -c <config> [--dry] [--debug]
Plans and applies org state. With--dry, shows a JSON plan followed by a human-readable summary of proposed changes without applying them. -
gomgr setup-team -n "Team Name" -c <config> [-f out/path.yaml]
Bootstraps a team YAML. -
gomgr version
Prints version (stamped at build). If built with VCS info, also prints revision/dirty/commit time.
Order of operations (apply):
create teams → set memberships → ensure repos → grant permissions → write renovate (optional) → set topics & pins → cleanups (optional)
CI: Releases
This repo includes .github/workflows/release.yml:
- Trigger on tag push (
v*.*.*) or manual. - Builds for linux/darwin/windows × amd64/arm64.
- Stamps
gomgr versionvia-ldflags. - Uploads packaged artifacts and
checksums.txt.
Tag to release:
git tag v0.1.0
git push origin v0.1.0
CI: Org sync workflow (for your org-config repo)
Example (.github/workflows/org-sync.yml):
name: Synchronise organization users and teams (gomgr)
on:
push:
branches: [ "main" ]
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
config:
- { folder: "dragonsecurity/dragonsecurity", gom_version: "v0.12.2" }
- { folder: "dragonsecurity/dragondevcc", gom_version: "v0.10.2" }
- { folder: "dragonsecurity/kamuses", gom_version: "v0.10.2" }
continue-on-error: true # allow other matrix jobs to finish if one fails
steps:
- uses: actions/checkout@v4
- name: Install gh
uses: cli/gh-action@v2
- name: Download gomgr
env: { GH_TOKEN: ${{ github.token }} }
run: |
VERSION="${{ matrix.config.gom_version }}"
OS=linux ARCH=amd64
ASSET="gomgr_${VERSION}_${OS}_${ARCH}.tar.gz"
mkdir -p .gomgr
gh release download "$VERSION" --repo DragonSecurity/github-org-manager-go --pattern "$ASSET" --dir .gomgr
tar -xzf ".gomgr/$ASSET" -C .gomgr
sudo mv $(find .gomgr -type f -name gomgr) /usr/local/bin/gomgr
sudo chmod +x /usr/local/bin/gomgr
- run: gomgr version
- name: Synchronise settings
run: gomgr sync -c ${{ matrix.config.folder }}
env:
GITHUB_APP_PRIVATE_KEY: ${{ secrets.DSEC_USER_MANAGEMENT_APP_PRIVATE_KEY }}
GITHUB_APP_ID: "1719369"
Development
Testing
The project includes a comprehensive test suite and Makefile for development:
# Run all tests
make test
# Run tests with coverage report
make test-coverage
# Run verbose tests
make test-verbose
# Check code formatting
make fmt-check
# Format code
make fmt
# Run go vet
make vet
Code Quality Tools
Install development tools (golangci-lint, gosec):
make install-tools
Run code quality checks:
# Run linter
make lint
# Run security scanner
make security
# Run all basic checks (format, vet, test)
make check
# Run all checks including lint and security
make check-all
Building
# Build binary
make build
# Clean build artifacts
make clean
# Run full CI pipeline
make ci
CI/CD
The repository includes GitHub Actions workflows:
.github/workflows/ci.yaml: Runs tests, linting, and security checks on every push/PR.github/workflows/release.yaml: Builds and releases binaries on version tags
Troubleshooting
- 404 on
/teams//members: empty/invalid team YAML or calling membership on a team that doesn’t exist yet. Loader ignores non‑YAML files and planner guards empty slugs; team creation happens before membership. gomgr versionshowsdev: build without-ldflags -Xor not from a tag. Use the release workflow or pass a version when building.- Renovate config not created: ensure
add_renovate_config: trueandrenovate_configis non‑empty; repo must exist orcreate_repo: true. - Repository pinning warnings: The GitHub API does not support pinning repositories to organization profiles programmatically. The
pinned: trueconfiguration is accepted but the operation is skipped with a warning. You must manually pin repositories through the GitHub web interface.
Roadmap / TODO
- Compare & update team fields (description/privacy/parents)
- Optionally remove extra team members / revoke extra repo perms
- Optionally remove extra topics from repos (current behavior: union of all topics)
- Custom default branch for file writes
- Parallel apply with rate‑limit aware workers
- More comprehensive plan diff output
Contributing
PRs welcome! Please:
- open an issue first for larger changes,
- keep commits small & focused,
- add tests where practical,
- run
make checkbefore submitting (ormake check-allfor full checks including linting), - ensure
make buildsucceeds.
See the Development section above for available commands and tooling.
Security
This tool modifies org membership and repository access. Use dry‑run in CI and restrict credentials using least privilege. Prefer GitHub Apps over PATs.
License
See LICENSE.
Documentation
¶
There is no documentation for this package.