github-community-projects / github-community-projects/safe-settings

Feature proposal: Generate safe-settings YAML from existing GitHub configuration (reverse sync)

Open
#994 0 comments 0 reactions 0 assignees Claimed by @decyjphr View on GitHub
Dominant language
JavaScript
Stars
921
Forks
226
Avg merge
18h 3m
Merged PRs (30d)
14

Description

### Summary

Today safe-settings applies declarative YAML config *to* GitHub. This proposal adds the **reverse**: generate `repos/.yml`, `suborgs/.yml`, and `settings.yml` *from* the current state of an org/repo. This helps teams onboard onto safe-settings without hand-authoring config for existing repositories.

### Motivation

- Onboarding existing orgs/repos requires manually transcribing current settings into YAML.
- A generator bootstraps an accurate baseline config, which can then be refined and committed.
- The generated files can be validated by feeding them back through safe-settings in nop (dry-run) mode — expecting zero diffs.

### Proposed delivery

Two entry points sharing one extraction core:

1. **Standalone CLI** (`generate-settings.js`, mirroring `full-sync.js`) — writes generated files to the local filesystem.
2. **App trigger** via a `repository_dispatch` event (`event_type: safe-settings-generate`) — opens a **PR** against the admin repo with the generated file(s).

**Client payload:**

```jsonc
{
"event_type": "safe-settings-generate",
"client_payload": {
"source_type": "repo | custom-property | org",
"source_value": " | | ",
"overwrite": false
}
}
```

When `overwrite: false` (default) and the target file already exists, the generator writes `.sample.yml` instead of replacing it.

### Design

A new `lib/settingsGenerator.js` defines an extractor registry: section → "read current state".

- **Diffable plugins** (labels, collaborators, teams, milestones, autolinks, environments, custom_properties, variables, rulesets) reuse each plugin's existing `find()` method, then sanitize API-only fields (`id`, `node_id`, `url`, timestamps, read-only props).
- **Non-diffable** sections need custom reads: `repository` (`repos.get`) and `branches` (list protected branches + `getBranchProtection`).
- Output serialized with `js-yaml`, optionally validated against `schema/settings.json`.

**Scope behavior:**

| `source_type` | What we can extract | Output file |
|---|---|---|
| `repo` | All repo-level plugins | `repos/.yml` |
| `org` | Org rulesets + custom repository roles only | `settings.yml` |
| `custom-property` (suborg) | Repo-level settings **common to all matching repos** | `suborgs/.yml` |

### Open question: how to derive sub-org settings

Sub-orgs represent settings common to a *collection* of repos. The proposed approach: discover all repos matching the custom property value, extract each repo's config, then reduce by **intersection** — keep only settings identical across **all** matching repos (arrays matched by `MergeDeep.NAME_FIELDS`). A `suborgproperties` selector is prepended. Alternatives considered: union (risks conflicts) or a single representative repo. **Feedback welcome on the intersection strategy and whether to also report per-repo differences.**

### Scope boundaries

- Secrets are never read (no secrets plugin exists); only API-readable values are emitted, with names/placeholders where values are unreadable.
- `validator` is excluded (meta plugin).
- Round-trip fidelity for `branches`/`rulesets` is the riskiest area; generated output will be gated by a nop round-trip test.

### Verification plan

- Unit tests per extractor + intersection + overwrite/`.sample` logic (mocked octokit).
- Round-trip: feed generated `repos/.yml` through safe-settings nop mode → expect zero diffs.
- Manual dispatch produces a PR (or `.sample` file when `overwrite: false`).

Looking for community feedback before/while implementing, particularly on the sub-org intersection approach and the `repository_dispatch` payload shape.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.