{"id":51549282,"url":"https://github.com/unurgunite/gitorules","last_synced_at":"2026-07-09T22:01:42.049Z","repository":{"id":369531357,"uuid":"1290228656","full_name":"unurgunite/gitorules","owner":"unurgunite","description":"Manage branch protection rules across all your repositories from a single YAML config.","archived":false,"fork":false,"pushed_at":"2026-07-05T19:58:22.000Z","size":10,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-07-05T20:12:45.403Z","etag":null,"topics":["crystal","git","github","github-config","github-rulesets"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unurgunite.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-05T18:13:05.000Z","updated_at":"2026-07-05T18:23:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/unurgunite/gitorules","commit_stats":null,"previous_names":["unurgunite/gitorules"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/unurgunite/gitorules","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unurgunite%2Fgitorules","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unurgunite%2Fgitorules/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unurgunite%2Fgitorules/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unurgunite%2Fgitorules/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unurgunite","download_url":"https://codeload.github.com/unurgunite/gitorules/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unurgunite%2Fgitorules/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35313756,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-09T02:00:07.329Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crystal","git","github","github-config","github-rulesets"],"created_at":"2026-07-09T22:01:41.155Z","updated_at":"2026-07-09T22:01:42.040Z","avatar_url":"https://github.com/unurgunite.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003egitorules\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/unurgunite/gitorules/actions\"\u003e\u003cimg src=\"https://github.com/unurgunite/gitorules/actions/workflows/ci.yml/badge.svg?branch=master\" alt=\"CI\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/unurgunite/gitorules/blob/master/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/unurgunite/gitorules.svg\" alt=\"License\"\u003e\u003c/a\u003e\n\u003ca href=\"https://crystal-lang.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/crystal-%3E%3D%201.14-blue.svg\" alt=\"Crystal\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nDeclarative GitHub Ruleset Manager.\n\nManage branch protection rules across all your repositories from a single YAML config.\n\n## Quick start\n\n```shell\n# Check current ruleset state\ngitorules status\n\n# Preview pending changes\ngitorules diff\n\n# Apply configuration\ngitorules apply\n\n# Generate .gitorules.yml from existing rulesets\ngitorules init --org myorg\n```\n\n## Installation\n\n### GitHub Releases\n\nDownload the latest binary from [releases](https://github.com/unurgunite/gitorules/releases).\n\n### Build from source\n\n```shell\ngit clone https://github.com/unurgunite/gitorules.git\ncd gitorules\nshards install\ncrystal build src/gitorules.cr --release -o gitorules\n```\n\nRequires Crystal 1.14+.\n\n## CLI\n\n```shell\ngitorules \u003cstatus|apply|diff|init\u003e [options]\n```\n\n### Commands\n\n| Command  | Description                                       |\n|----------|---------------------------------------------------|\n| `status` | Show ruleset status for repositories              |\n| `apply`  | Apply ruleset configuration from `.gitorules.yml` |\n| `diff`   | Show pending changes without applying             |\n| `init`   | Generate `.gitorules.yml` from existing rulesets  |\n\n### Options\n\n| Flag                      | Description                                     |\n|---------------------------|-------------------------------------------------|\n| `--dry-run`               | Preview apply changes without making them       |\n| `--diff`                  | Show pending changes (same as `diff` command)   |\n| `--repo REPO`             | Target a single repository (`owner/name`)       |\n| `--org ORG`               | GitHub organization name (for `init`)           |\n| `--json`                  | Machine-readable JSON output                    |\n| `--quiet`                 | Suppress all output except errors               |\n| `--token TOKEN`           | GitHub personal access token                    |\n| `--app-id ID`             | GitHub App ID (for GitHub App auth)             |\n| `--private-key PEM`       | GitHub App private key (PEM content)            |\n| `--private-key-path PATH` | Path to GitHub App private key PEM file         |\n| `--installation-id ID`    | GitHub App installation ID                      |\n| `--config PATH`           | Path to config file (default: `.gitorules.yml`) |\n| `--version`               | Show version                                    |\n| `-h`, `--help`            | Show help                                       |\n\n### Exit codes\n\n- **0** — all rulesets are up to date (no changes needed)\n- **1** — changes detected (in status/diff mode) or changes applied (in apply mode)\n- **2** — execution error (config error, API error, etc.)\n\n### Authentication\n\ngitorules supports two authentication methods:\n\n**Personal Access Token:**\n\n```shell\ngitorules --token ghp_xxx status\n```\n\nOr set `GITHUB_TOKEN` environment variable.\n\n**GitHub App (JWT):**\n\n```shell\ngitorules --app-id 123456 --private-key-path ./key.pem --installation-id 789012 status\n```\n\nEnvironment variables: `GITHUB_APP_ID`, `GITHUB_APP_PRIVATE_KEY`, `GITHUB_APP_INSTALLATION_ID`.\n\nIf no auth method is configured, gitorules prints an error and exits with code 2.\n\n## Configuration\n\ngitorules uses a `.gitorules.yml` file in the project root. Example:\n\n```yaml\norgs:\n  myorg:\n    repos:\n      - repo-a\n      - repo-b\n    rules:\n      default_branch:\n        merge: only\n        checks:\n          - \"check / check\"\n\n      release:\n        pattern: v*\n        squash: only\n\n      system:\n        pattern: system/*\n        merge: only\n```\n\n### Rule types\n\n| Key                  | Description                        | Values                                             |\n|----------------------|------------------------------------|----------------------------------------------------|\n| `pattern`            | Branch pattern (default: `master`) | Glob pattern or branch name                        |\n| `merge`              | Merge method restriction           | `only`, `false` (omit for all methods)             |\n| `rebase`             | Rebase method restriction          | `only`, `false`                                    |\n| `squash`             | Squash method restriction          | `only`, `false`                                    |\n| `checks`             | Required status checks             | List of check context strings                      |\n| `required_approvals` | Required approving reviews         | Integer (default: 0)                               |\n| `dismiss_stale`      | Dismiss approvals on new push      | `true`, `false` (default: `true`)                  |\n| `require_code_owner` | Require code owner review          | `true`, `false` (default: `false`)                 |\n| `enforce_admins`     | Enforce rules for admins           | `true`, `false` (default: `true`)                  |\n| `deletion`           | Allow deletion (default: `true`)   | `true`, `false` — set to `false` to protect branch |\n| `non_fast_forward`   | Allow non-fast-forward pushes      | `true`, `false` (default: `false`)                 |\n\n### Single-org mode\n\nFor a single organization, you can use the shorthand:\n\n```yaml\norg: myorg\nrepos:\n  - repo-a\nrules:\n  default_branch:\n    merge: only\n```\n\n## Development\n\n```shell\nshards install\ncrystal spec\n./bin/ameba\ncrystal tool format --check\n```\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funurgunite%2Fgitorules","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funurgunite%2Fgitorules","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funurgunite%2Fgitorules/lists"}