
Python CLI, который автоматически создаёт репозитории GitHub с безопасными настройками по умолчанию — защита веток, Dependabot, сканирование секретов и предварительное сканирование безопасности.
Создавайте репозитории GitHub с автоматически применяемыми безопасными настройками. Заменяет пятиминутный чек-лист настроек после создания одной командой.``` gh-safe-repo create <owner/repo>
Защита веток, неизменяемые теги, Dependabot, ограниченные разрешения Actions, сканирование секретов с защитой от отправки (push protection), а также отключенные вики и проекты — всё настраивается до того, как вы напишете свою первую строку кода.
gh-safe-repo находится в стадии активной разработки. Он хорошо работает для сценария создания нового репозитория с безопасными настройками по умолчанию. Я работаю над доработкой опций CLI, чтобы они наилучшим образом соответствовали ожиданиям пользователей. Ожидайте критических изменений, пока мы не достигнем этапа, когда я буду делать релизы и налажу CI/CD. ✌️
---
## Содержание
- [Зачем](#why)
- [Что меняется](#what-it-changes)
- [Требования](#requirements)
- [Установка](#installation)
- [Быстрый старт](#quick-start)
- [Справочник по CLI](#cli-reference)
- [Пробный запуск / Вывод плана](#dry-run--plan-output)
- [Режим исправления (аудит существующих репозиториев)](#fix-mode-audit-existing-repos)
- [Зеркалирование репозиториев (`--from`)](#mirroring-repos---from)
- [Создание репозитория из локальной директории (`--local`)](#creating-a-repo-from-a-local-directory---local)
- [Предварительный сканер безопасности](#pre-flight-security-scanner)
- [Автономное сканирование](#standalone-scan)
- [Подавление ложных срабатываний](#suppressing-false-positives)
- [Конфигурация](#configuration)
- [Ограничения планов GitHub](#github-plan-limitations)
- [Как это работает](#how-it-works)
- [Разработка](#development)
---
## Зачем
Стандартные настройки репозитория GitHub оптимизированы для обнаружения и гибкости, а не для безопасности. Каждый новый репозиторий поставляется с:
- Включенные вики и проекты (поверхность атаки, даже если не используются)
- Разрешены коммиты слияния (беспорядочная история, но это не главная проблема)
- Отсутствует защита веток (любой с правами записи может отправлять напрямую в `main`)
- Отсутствуют уведомления Dependabot
- GitHub Actions с правами записи в репозиторий
- Actions разрешены для одобрения pull request'ов
Исправление всего этого вручную занимает минуты на репозиторий и легко забывается. `gh-safe-repo` применяет субъективный, но практичный набор настроек по умолчанию одним выстрелом, с предварительным просмотром плана, чтобы вы точно знали, что изменится до того, как что-либо изменится.
---
## Что меняется
### Настройки репозитория
| Настройка | По умолчанию GitHub | Безопасное значение | Примечания |
|---|---|---|---|
| Видимость | Публичный | **Приватный** | Передайте `--public`, чтобы переопределить |
| Вики | Включено | **Отключено** | |
| Проекты | Включено | **Отключено** | |
| Issues | Включено | Включено | |
| Удалять ветку при слиянии | Выкл | Выкл | Установите `true` в конфиге для автоочистки |
| Разрешить коммиты слияния | Вкл | Вкл | Установите `false` в конфиге только для squash |
| Разрешить squash-слияние | Вкл | Вкл | |
| Разрешить rebase-слияние | Вкл | Вкл | |
### GitHub Actions
| Настройка | По умолчанию GitHub | Безопасное значение |
|---|---|---|
| Разрешённые actions | Все | **Выбранные** (принадлежащие GitHub + проверенные создатели; настраивается) |
| Разрешения рабочего процесса по умолчанию | Чтение/запись | **Только чтение** |
| Actions могут одобрять PR | Да | **Нет** |
| Требовать SHA-привязку | Нет | **Да** (рабочие процессы должны привязывать actions к SHA коммита, а не к изменяемому тегу) |
| Политика одобрения fork PR | Только новые участники на GitHub | **Все внешние участники** — требуется одобрение перед запуском CI для рабочих процессов fork PR. Варианты: только новые учётные записи GitHub (по умолчанию GitHub), участники впервые в репозитории, или все fork PR (самый безопасный) |
### Защита веток (публичные репозитории или любой репозиторий на платном тарифе)
| Правило | Значение |
|---|---|
| Требовать pull request перед слиянием | Да |
| Требуемое количество одобряющих рецензий | 1 |
| Отклонять устаревшие рецензии при push | Да |
| Требовать разрешения обсуждения | Да |
| Разрешить принудительные push | Нет |
| Разрешить удаление ветки | Нет |
| Применять к администраторам | Нет (позволяет инструментам владельца делать push) |
Защита веток применяется через **Rulesets API** по умолчанию (`use_rulesets = true`): единый ruleset `gh-safe-repo defaults` охватывает каждую настроенную ветку и выражает «администраторы могут обойти» через bypass actor, а не через классический флаг `enforce_admins`. Установите `use_rulesets = false` для устаревшего классического пути для каждой ветки (сохраняется на один цикл релиза).
**Миграция существующего репозитория с классической защиты:** если `fix` находит классическую защиту веток в репозитории, он отказывается преобразовывать её в ruleset, если вы не передадите `--migrate-branch-protection`. Правила, доступные только в классике, не имеют эквивалента в ruleset, который строит этот инструмент, и будут молча отброшены — известные пробелы:
- `required_status_checks` — требуемые проверки CI не моделируются в теле ruleset.
- `restrictions` (ограничения push по пользователю/команде) — Rulesets моделируют это иначе через bypass actors; соответствие не 1:1.
- Различия между ветками — единый ruleset с общим условием не может выражать разные правила для `master` и `main`.
С этим флагом `fix` создаёт/обновляет ruleset, а затем удаляет классическую защиту на каждой ветке, чтобы два слоя не накладывались.
### Защита тегов (публичные репозитории или любой репозиторий на платном тарифе)
Защита тегов создает GitHub Ruleset, нацеленный на все теги (`*` по умолчанию, настраивается через `protected_tags`). Применяются следующие правила:
| Правило ruleset | Применяется? | Примечания |
|---|---|---|
| Ограничить создание | Нет | |
| **Ограничить обновление** | **Да** | Предотвращает перезапись / принудительный push тегов |
| **Ограничить удаление** | **Да** | Предотвращает `git push --delete` тегов |
| Требовать линейную историю | Нет | |
| Требовать успешных развертываний | Нет | |
| Требовать подписанные коммиты | Нет | |
| Требовать прохождения проверок статуса | Нет | |
| Блокировать принудительные push | Нет | |
Администраторы репозитория находятся в списке обхода (согласуется с настройкой по умолчанию защиты веток `enforce_admins = false`). Работает только на публичных репозиториях или платных планах GitHub (то же ограничение, что и для защиты веток). В приватных репозиториях бесплатного плана это будет пропущено в выводе плана.
### Безопасность
| Функция | Поведение |
|---|---|
| Уведомления Dependabot | Включено (публичные репозитории / платные планы) |
| Обновления безопасности Dependabot | Включено (автоматически открывает PR для уязвимых зависимостей) |
| Сканирование секретов | Автоматически на публичных репозиториях; включено на приватных платных планах |
| Защита от отправки (push protection) | Включено (блокирует коммиты, содержащие поддерживаемые секреты) |
| Приватное сообщение об уязвимостях | Включено (позволяет исследователям безопасности сообщать приватно) |
| Граф зависимостей | Автоматически на публичных репозиториях; нет REST API для приватных (только UI) |
---
## Требования
- Python 3.8+
- `gh` CLI установлен и аутентифицирован (`gh auth login`), **или** `GITHUB_TOKEN` установлен в вашем окружении
- Для `--local` / `--from` (которые отправляют или клонируют код): ваши обычные учётные данные git должны быть настроены — либо SSH-ключ, загруженный в `ssh-agent` (когда `gh config get git_protocol` равен `ssh`), либо помощник с HTTPS-учётными данными (`gh auth setup-git` настраивает такой автоматически). Токен OAuth **не** используется для git push, поэтому файлы рабочих процессов (`.github/workflows/*`) отправляются без необходимости области OAuth `workflow`.
- `uv` для установки из исходников (рекомендуется)
- `truffleHog` v3 (опционально — используется предварительным сканером; автоматически определяется из PATH или запускается через podman/docker; переходит к regex, если ни то, ни другое недоступно)
---
## Установка
### Из исходников с помощью uv (рекомендуется)```bash
git clone https://github.com/your-username/gh-safe-repo
cd gh-safe-repo
uv tool install .
Это устанавливает gh-safe-repo в среду инструментов uv и добавляет его в ваш PATH.
git clone https://github.com/your-username/gh-safe-repo cd gh-safe-repo uv sync # creates .venv ./gh-safe-repo create <owner/repo>
### Проверка```bash
gh-safe-repo --help
gh-safe-repo create <owner/repo>
gh-safe-repo create <owner/repo> --dry-run
gh-safe-repo create <owner/repo> --public
gh-safe-repo create <owner/repo> --from <owner/source>
gh-safe-repo create <owner/pub> --from <owner/priv> --public
gh-safe-repo create <owner/repo> --local ~/projects/myapp
gh-safe-repo create <owner/repo> --local ~/projects/myapp --public
gh-safe-repo fix <owner/repo>
gh-safe-repo fix <owner/repo> --dry-run
gh-safe-repo fix <owner/repo> --yes
gh-safe-repo scan . gh-safe-repo scan ~/projects/myapp
---
## Справочник по CLI```
gh-safe-repo create <owner/repo> [OPTIONS]
gh-safe-repo fix <owner/repo> [OPTIONS]
gh-safe-repo scan <path> [OPTIONS]
Все команды, взаимодействующие с GitHub, требуют формат owner/repo (например, myuser/my-repo). Для create владелец проверяется по вашему аутентифицированному аккаунту GitHub, чтобы предотвратить ошибки в системах с несколькими аккаунтами. Для fix вместо этого требуются права администратора на целевой репозиторий, что позволяет вам исправлять репозитории, принадлежащие организациям или другим аккаунтам, где у вас есть административный доступ.
create — Создание нового репозитория| Параметр | Описание |
|---|---|
--public | Создать как публичный репозиторий (по умолчанию: приватный) |
--local PATH | Отправить код из локального git-репозитория в новый репозиторий. Сначала выполняется предварительная проверка. Взаимоисключающий с --from. |
--from OWNER/REPO | Скопировать код из существующего репозитория в новый. Выполняется предварительная проверка. Взаимоисключающий с --local. |
--yes / -y | Пропустить подтверждение и применить немедленно (для использования в скриптах/пакетной обработке) |
--dry-run | Показать план без внесения изменений |
--json | Вывести план в формате JSON в stdout вместо таблицы ANSI |
--config [PATH] | Путь к файлу конфигурации; пустой --config использует только встроенные настройки по умолчанию |
--debug | Выводить каждый API-вызов и ответ |
Простой create (без --local/--from) инициализирует репозиторий, чтобы существовала ветка по умолчанию для защиты веток, а затем удаляет автоматически сгенерированный README.md, чтобы новый репозиторий начинался чистым. Установите auto_init = true в конфигурации, чтобы сохранить README. --local/--from отправляют вашу собственную историю и никогда не создают README.
fix — Аудит и исправление существующего репозитория| Параметр | Описание |
|---|---|
--yes / -y | Пропустить подтверждение и применить немедленно (для использования в скриптах/пакетной обработке) |
--dry-run | Показать разницу настроек без применения изменений |
--json | Вывести план в формате JSON в stdout вместо таблицы ANSI |
--config [PATH] | Путь к файлу конфигурации; пустой --config использует только встроенные настройки по умолчанию |
--debug | Выводить каждый API-вызов и ответ, а также разрешённую идентификацию репозитория (id, полное имя, тип владельца) |
scan — Локальное сканирование секретов| Параметр | Описание |
|---|---|
--config [PATH] | Путь к файлу конфигурации; пустой --config использует только встроенные настройки по умолчанию |
--debug | Показать детали сканера |
Код выхода — 0, если критических находок нет, 1, если найдены критические.
--dry-run показывает точно, что сделал бы gh-safe-repo, без внесения изменений или вызовов API. Используйте перед реальным запуском. Комбинируйте с --json для вывода плана в машиночитаемом формате:```bash
gh-safe-repo create <owner/repo> --dry-run --json
gh-safe-repo fix <owner/repo> --dry-run --json
Когда `--json` активен, план записывается в stdout как JSON-объект, а все остальные сообщения (прогресс, предупреждения, нижний колонтитул "Dry run") направляются в stderr, так что вывод чист для конвейерной обработки или скриптов.```
$ gh-safe-repo create <owner/repo> --dry-run
Plan for my-project (private)
Category Action Setting Value
──────────────────────────────────────────────────────────────────
Repository ADD repository my-project (private)
Repository ADD has_wiki false
Repository ADD has_projects false
Actions ADD default_workflow_permissions read
Actions ADD can_approve_pull_request_reviews false
Branch Protection SKIP branch_protection Not available for private repos on free plan
Security SKIP dependabot_alerts Not available for private repos on free plan
1 setting skipped (GitHub plan limitation).
Dry run — no changes made.
Цвета действий:
| Действие | Значение |
|---|---|
ADD (зелёный) | Применяется новый параметр |
UPDATE (жёлтый) | Изменение существующего параметра (режим аудита) |
DELETE (красный) | Параметр удаляется |
SKIP (тусклый) | Действие не требуется — уже установлено нужное значение, или функция недоступна в вашей комбинации тарифа/видимости |
Вывод JSON (--json):```json
{
"changes": [
{ "type": "add", "category": "repository", "key": "has_wiki", "old": null, "new": false, "reason": null },
{ "type": "skip", "category": "branch_protection", "key": "branch_protection", "old": null, "new": null, "reason": "Not available for private repos on free plan" }
],
"summary": { "add": 5, "skip": 2 }
}
`summary` включает только те типы, которые присутствуют в плане. Потребители должны использовать `.get("delete", 0)` и т.д., а не предполагать, что присутствуют все четыре ключа.
---
## Режим исправления (Аудит существующих репозиториев)
`fix` сравнивает текущие настройки существующего репозитория с безопасными значениями по умолчанию и применяет все исправления. Без сканирования секретов — `fix` занимается исключительно настройками репозитория.```bash
# See what's out of compliance
gh-safe-repo fix <owner/repo> --dry-run
# Apply missing safe defaults
gh-safe-repo fix <owner/repo>
# Apply without confirmation prompt (scripting/batch use)
gh-safe-repo fix <owner/repo> --yes
Режим исправления:
UPDATE для изменённых параметров и SKIP для параметров, уже имеющих нужное значение (обнаружение бездействия — он никогда не совершает вызовы API, которые ничего не меняют)--yes)Применяются только реальные изменения — параметры, уже имеющие нужное значение, отображаются как SKIP и не генерируют вызовов API.
--from)--from зеркалирует существующий репозиторий в новый с безопасными значениями по умолчанию. Работает как для частных, так и для публичных целевых репозиториев:```bash
gh-safe-repo create <owner/repo> --from <owner/source>
gh-safe-repo create <owner/pub> --from <owner/priv> --public
**Что происходит по порядку:**
1. Ваши git-учётные данные для `github.com` проверяются заранее (SSP-зонд, если `gh config get git_protocol` — `ssh`; HTTPS считается доверенным), поэтому отсутствующий ключ приводит к быстрому сбою до создания репозитория.
2. Исходный репозиторий клонируется локально (полный клон, без `--depth`, чтобы truffleHog мог просмотреть всю историю коммитов).
3. [Предварительный сканер безопасности](#pre-flight-security-scanner) запускается на локальном клоне.
4. Вы просматриваете результаты и подтверждаете (или прерываете).
5. Создаётся новый репозиторий (по умолчанию приватный, или публичный с `--public`).
6. Применяются разрешения Actions и настройки безопасности (Dependabot, сканирование секретов, защита от отправки).
7. Вся история зеркалируется: `git clone --mirror` + `git push --mirror`.
8. Применяется защита веток и тегов (после отправки кода, чтобы целевая ветка существовала).
Если сканирование выявит проблему и вы прервёте процесс, код никогда не будет скопирован на GitHub.
> **Примечание:** `--from` использует формат `owner/repo` как для источника, так и для назначения.
---
## Создание репозитория из локальной директории (`--local`)
`--local PATH` — это локальный аналог `--from`. Он создаёт новый репозиторий на GitHub и отправляет код из локального git-репозитория. `PATH` должен быть инициализированным git-репозиторием (`git init` или клон).```bash
gh-safe-repo create <owner/repo> --local ~/projects/myapp
gh-safe-repo create <owner/repo> --local ~/projects/myapp --public
Что происходит по порядку:
github.com проверяются заранее (SSH-зонд, когда gh config get git_protocol равно ssh; HTTPS считается доверенным), поэтому отсутствующий ключ быстро приводит к ошибке до создания любого репозиторияpush --all --tags (все ветки и теги)origin добавляется в исходный локальный репозиторий, указывая на новый URL GitHub, и настраивается отслеживание текущей ветки вверх по потоку — так что git push и git pull работают сразу без дополнительной настройки.Оба параметра --local и --from работают для частных и публичных репозиториев. Они взаимно исключают друг друга.
Локальная ветка по умолчанию (через git -C PATH symbolic-ref HEAD) используется для нацеливания правил защиты ветки, поэтому защита применяется к правильной ветке, даже если это не main.
Совет: Сначала выполните
gh-safe-repo scan PATH, если хотите просмотреть результаты без создания чего-либо.
Сканер запускается локально и никогда не отправляет код на GitHub. Используйте его отдельно перед любой отправкой, или он запускается автоматически в рамках рабочих процессов --from и --local.
gh-safe-repo scan .
gh-safe-repo scan ~/projects/myapp
Код завершения `0`, если критических находок нет, `1`, если критические найдены — так что он хорошо компонуется с другими командами:```bash
gh-safe-repo scan . && git push
Полная конфигурация [pre_flight_scan] применяется: banned_strings, max_file_size_mb, trufflehog_mode и т.д.
| Категория | Серьёзность | Примеры |
|---|---|---|
| Жёстко закодированные секреты | Критический | AWS keys (AKIA…), GitHub tokens (ghp_…, github_pat_…), закрытые ключи, URL баз данных |
| Запрещённые строки | Критический | Любые литеральные строки, которые вы настраиваете (имена пользователей, внутренние имена хостов, кодовые имена) |
| Файлы контекста ИИ | Критический | CLAUDE.md, AGENTS.md, .cursorrules, copilot-instructions.md, .cursor/ — могут содержать внутренние заметки разработчиков; история git может быть более чувствительной, чем текущая версия |
| Адреса электронной почты | Предупреждение | Любой шаблон [email protected] в рабочем дереве и истории git |
| Большие файлы | Предупреждение | Файлы, превышающие заданный порог размера (по умолчанию: 100 МБ) |
| Комментарии TODO/FIXME | Информация | # TODO, # FIXME, # HACK, # XXX |
gh-safe-repo автоматически выбирает лучший доступный сканер, используя трёхэтапную цепочку обнаружения:
trufflehog --version, проверяет, что это v3, и использует его. Установка v2 или нераспознанная версия выводят предупреждение и переходят к шагу 2.ghcr.io/trufflesecurity/trufflehog:latest) с помощью podman run или docker run, монтируя путь сканирования только для чтения по тому же абсолютному пути, чтобы пути JSON-вывода были идентичны нативному запуску.Выбранный сканер отображается в заголовке "Running pre-flight security scan..." и в записи SCAN таблицы плана, например:``` Running pre-flight security scan... (truffleHog v3.93.4) Running pre-flight security scan... (truffleHog via podman) Running pre-flight security scan... (regex only — see warning above)
Переменные окружения, учитываемые контейнерным путем: `CONTAINER_RUNTIME` для переопределения выбора среды выполнения (например, `CONTAINER_RUNTIME=docker`), и `TRUFFLEHOG_IMAGE` для закрепления конкретного тега образа.
### Запуск truffleHog через podman или Docker (без локальной установки)
Ручная настройка не требуется. `gh-safe-repo` автоматически определяет podman или docker (шаг 2 выше) и запускает truffleHog в контейнере с правильными монтированиями томов. Учитываются переменные окружения `CONTAINER_RUNTIME` и `TRUFFLEHOG_IMAGE`.
Оболочка-обертка (`tools/trufflehog`) и `Containerfile` для сборки закрепленного локального образа предоставлены в [`tools/`](https://github.com/ariesq/gh-safe-repo/blob/master/tools/README.md) для пользователей, которые хотят иметь контейнерный truffleHog, доступный на уровне всей системы, или которым нужен образ для изолированной среды.
### Интерактивная проверка```
Pre-flight scan: my-private-project
CRITICAL my_private_project/config.py:12 AWS Access Key ID
[redacted]
WARNING my_private_project/setup.py:3 Email address
author_email="[email protected]"
1 critical finding, 1 warning.
Critical findings detected. Continue anyway? [y/N]:
N). Вы должны явно ввести y, чтобы продолжить.Y). Нажмите Enter, чтобы продолжить, или введите n, чтобы прервать.Секреты скрыты в выводе. Адреса электронной почты и TODO показывают соответствующую строку.
Директории с артефактами сборки (node_modules, __pycache__, .venv, venv, dist, build) по умолчанию пропускаются для ускорения сканирования. В git-репозиториях этот пропуск является условным: перед удалением директории из сканирования сканер выполняет git ls-files -- <dir>, чтобы проверить, отслеживаются ли какие-либо файлы внутри. Если да, директория сканируется обычным образом.
Это означает, что зафиксированные в репозитории деревья node_modules или dist — необычно, но бывает — не пропускаются незаметно. Незафиксированные директории (обычный случай) по-прежнему пропускаются, как и раньше.
Предупреждение всё равно выводится, когда поддиректории SKIP_DIRS найдены в клонированном исходном репозитории, поскольку их наличие может указывать на то, что было зафиксировано больше контента, чем ожидалось.
Два ключа конфигурации позволяют подавлять известные безопасные находки без отключения целых категорий проверок.
scan_exclude_paths — полностью пропускать файлы или директории. Значения — разделённые новой строкой или запятой regex-шаблоны, сопоставляемые с относительным путём файла. Соответствующий файл исключается из всех проверок: секреты, email-адреса, TODO, большие файлы и обнаружение файлов контекста ИИ. Те же шаблоны также передаются truffleHog через --exclude-paths, поэтому охват остаётся согласованным независимо от того, какой движок сканирования активен.```ini
[pre_flight_scan]
scan_exclude_paths = docs/api.github.com.json tests/fixtures/
**`exclude_emails`** — подавлять результаты поиска email для конкретных адресов или целых доменов. Значения разделяются новой строкой или запятой, регистронезависимые. Записи, начинающиеся с `@`, соответствуют всем email в этом домене; в противном случае запись должна точно совпадать с полным адресом. Применяется как к результатам в рабочем дереве, так и к истории git.```ini
[pre_flight_scan]
# Suppress bot addresses and placeholder domains
exclude_emails = [email protected], [email protected], @example.com
[pre_flight_scan] scan_for_secrets = true scan_for_emails = true scan_for_todos = true max_file_size_mb = 100
Когда найдены запрещенные строки или файлы контекста ИИ, сканер выводит готовую к запуску команду `git filter-repo` для их удаления из истории исходного репозитория перед повторным запуском.
---
## Конфигурация
`gh-safe-repo` ищет конфигурацию в следующем порядке (первое совпадение используется):
1. **`--config PATH`** — явное переопределение
2. **`./gh-safe-repo.ini`** — текущая рабочая директория
3. **`$XDG_CONFIG_HOME/gh-safe-repo/gh-safe-repo.ini`** — по умолчанию `~/.config`, если `$XDG_CONFIG_HOME` не задан
Голый `--config` (без пути) полностью пропускает поиск файла и использует только встроенные значения по умолчанию.
Все значения имеют безопасные значения по умолчанию — для начала работы файл конфигурации не требуется.
Полностью аннотированный пример конфигурации включен в репозиторий как `gh-safe-repo.ini.example`. Скопируйте его, чтобы начать:```bash
# User-level config (XDG)
mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/gh-safe-repo"
cp gh-safe-repo.ini.example "${XDG_CONFIG_HOME:-$HOME/.config}/gh-safe-repo/gh-safe-repo.ini"
# Or project-level config (current directory)
cp gh-safe-repo.ini.example ./gh-safe-repo.ini
[repo]
private = true
has_wiki = false has_projects = false has_issues = true
delete_branch_on_merge = false
allow_squash_merge = true allow_merge_commit = true allow_rebase_merge = true
create leaves an initialized README in the new repo.auto_init = false
[actions]
allowed_actions = selected
github_owned_allowed = true # actions maintained by GitHub (e.g. actions/checkout) verified_allowed = true # actions from Marketplace verified creators
default_workflow_permissions = read
can_approve_pull_request_reviews = false
sha_pinning_required = true
[branch_protection]
protected_branch = main
require_pull_request = true
required_approving_reviews = 1
dismiss_stale_reviews = true
require_conversation_resolution = true
enforce_admins = false
allow_force_pushes = false
allow_deletions = false
use_rulesets = true
[tag_protection]
protected_tags = *
prevent_tag_deletion = true
prevent_tag_update = true
[security]
enable_dependabot_alerts = true
enable_dependabot_security_updates = true
enable_private_vulnerability_reporting = true
enable_secret_scanning_push_protection = true
[pre_flight_scan] scan_for_secrets = true scan_for_emails = true scan_for_todos = true
max_file_size_mb = 100
[git_transport]
workflow token scope to pushworkflow scope intentionally.---
## Ограничения планов GitHub
Некоторые функции доступны только в зависимости от видимости репозитория и вашего плана GitHub.
| Функция | Бесплатный + Общедоступный | Бесплатный + Частный | Pro/Team + Частный |
|---|:---:|:---:|:---:|
| Защита веток / Наборы правил | Да | Нет | Да |
| Защита тегов (Наборы правил) | Да | Нет | Да |
| Оповещения Dependabot | Да | Нет | Да |
| Обновления безопасности Dependabot | Да | Нет | Да |
| Сканирование секретов | Авто | Нет | Да |
| Защита при отправке | Да | Нет | Да |
| Приватное сообщение об уязвимостях | Да | Да | Да |
| Граф зависимостей | Авто | Нет | Да |
`gh-safe-repo` определяет уровень вашего плана и видимость репозитория во время выполнения. Недоступные функции отображаются как `SKIP` в выводе плана с понятной причиной — инструмент никогда не завершается молча.
---
## Как это работает```
gh-safe-repo create <owner/repo>
│
├─ Parse owner/repo, validate owner matches authenticated user (create only)
├─ Load config (./gh-safe-repo.ini or $XDG_CONFIG_HOME/gh-safe-repo/gh-safe-repo.ini)
├─ Apply CLI flag overrides (--public, etc.)
├─ Authenticate via gh CLI or GITHUB_TOKEN
├─ GET /user → owner login + plan level (single cached call)
│
├─ Build plan (each plugin compares desired vs. current state)
│ ├─ RepositoryPlugin → repo creation + basic settings
│ ├─ ActionsPlugin → allowed actions, workflow permissions, SHA pinning
│ ├─ BranchProtectionPlugin → Rulesets API (default; classic if use_rulesets = false)
│ ├─ SecurityPlugin → Dependabot, secret scanning, push protection, private vuln reporting
│ └─ TagProtectionPlugin → immutable tags via Rulesets API
│
├─ Print plan table
│
└─ Apply (unless --dry-run)
├─ POST /user/repos
├─ PATCH /repos/{owner}/{repo} (settings)
├─ PUT /repos/{owner}/{repo}/actions/permissions/workflow
├─ POST/PATCH /repos/{owner}/{repo}/rulesets (branch protection; default)
│ or PUT /repos/{owner}/{repo}/branches/main/protection (if use_rulesets = false)
├─ PUT /repos/{owner}/{repo}/vulnerability-alerts
├─ PUT /repos/{owner}/{repo}/automated-security-fixes
├─ PUT /repos/{owner}/{repo}/private-vulnerability-reporting
├─ PATCH /repos/{owner}/{repo} (security_and_analysis: push protection)
├─ POST /repos/{owner}/{repo}/rulesets (tag protection ruleset)
├─ git clone --mirror + git push --mirror (if --from)
└─ git clone <local> + git push --all --tags (if --local, git repo)
or git init + add -A + commit + push (if --local, plain dir)
Каждая категория настроек представляет собой самодостаточный класс плагина (gh_safe_repo/plugins/). Каждый плагин:
Plan (список объектов Change: ADD / UPDATE / DELETE / SKIP)Это означает, что режим аудита и режим создания используют один и тот же путь планирования/применения. Единственное отличие — получается ли текущее состояние из существующего репозитория или предполагается, что оно соответствует настройкам GitHub по умолчанию.
API-вызовы разрешают токен в следующем порядке:
GITHUB_TOKEN — позволяет нацелиться на определённую учётную запись без переключения активного сеанса gh (и это единственные учётные данные, необходимые в CI)gh auth token — то, что настроено с помощью gh auth loginТокены передаются дочерним процессам gh api как GH_TOKEN в окружении подпроцесса и никогда не логируются.
Git-операции (--local / --from push и clone) по умолчанию используют ваши собственные учётные данные git — SSH-ключ или помощник ввода учётных данных, а не токен API. В средах, где нет ни того, ни другого (например, CI только с GITHUB_TOKEN), инструмент понижает уровень до отправки по HTTPS с токеном в URL; настройка конфига [git_transport] mode управляет этим (см. справочник по конфигурации). URL-адреса, содержащие токен, никогда не записываются в .git/config вашего репозитория и скрыты из всего вывода.
Все вызовы GitHub API выполняются через gh api с помощью subprocess. Это полностью выносит аутентификацию в gh CLI — никакого кода управления токенами, OAuth-потока, привязки к версии PyGithub. Тела JSON-запросов передаются через --input - (stdin), а не через флаги --field.
git clone https://github.com/your-username/gh-safe-repo cd gh-safe-repo uv sync # creates .venv, installs pytest
uv run pytest tests/ -v
./gh-safe-repo create <owner/repo> --dry-run
uv tool install .
Смотрите [`tests/README.md`](https://github.com/ariesq/gh-safe-repo/blob/master/tests/README.md) для описания тестовых файлов, соглашений по мокированию и инструкций по добавлению новых тестов.
### Структура проекта```
gh-safe-repo/
├── gh-safe-repo # Thin launcher (entry point for direct use)
├── gh_safe_repo/ # Package — see gh_safe_repo/README.md for internals
│ ├── cli.py # Subparser dispatch (create, fix, scan)
│ ├── commands/ # Subcommand implementations
│ │ ├── _common.py # Shared helpers, CLIContext, plan formatting
│ │ ├── create.py # create subcommand
│ │ ├── fix.py # fix subcommand
│ │ └── scan.py # scan subcommand
│ └── plugins/ # Settings plugins (one per category)
├── pyproject.toml # Build config, entry points
├── gh-safe-repo.ini.example # Fully annotated example config
└── tests/
См. gh_safe_repo/README.md для карты модулей, архитектуры плагинов и руководства по добавлению новых настроек.
Нет зависимостей времени выполнения. Всё использует стандартную библиотеку Python (argparse, configparser, subprocess, json, re). Не добавляйте сторонние пакеты без обсуждения.
pytest — единственная зависимость для разработки, объявленная как UV-native запись [dependency-groups] в pyproject.toml.
Эти проекты были изучены при проектировании и повлияли на архитектуру gh-safe-repo. Это отдельные инструменты с разной областью применения и моделями пользователей — см. docs/LEARNINGS.md для подробных технических заметок о том, как были адаптированы шаблоны.
github/safe-settings — Приложение GitHub уровня организации (Node.js/Probot), которое применяет настройки репозитория из центральной конфигурации. Источник паттерна архитектуры плагинов (один класс на категорию настроек, fetch → diff → apply) и подхода сравнения mergeDeep.
repository-settings/app — Более простой вариант safe-settings для каждого репозитория, также на Node.js/Probot. Предоставил более чистый эталон для базового паттерна плагина Diffable.
nicholasgasior/gh-repo-settings — Расширение для CLI, написанное на Go с рабочим процессом plan/apply. Основное вдохновение для паттерна обёртки подпроцесса gh api и дизайна вывода плана в режиме пробного запуска.