Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ea663c2
adding hub-sync feature code
jefeish Aug 28, 2025
6457790
adjusting ui
jefeish Sep 1, 2025
498bbbd
hub improvements
jefeish Sep 10, 2025
dad3fe8
Add ui screen
jefeish Sep 24, 2025
5188801
updated README
jefeish Oct 2, 2025
de8bab4
handle multiple changes as a batch
decyjphr Oct 3, 2025
fa00d78
Update index.js
decyjphr Oct 3, 2025
b87397c
Update index.js
decyjphr Oct 3, 2025
6b358e5
depup files in a push
decyjphr Oct 5, 2025
c971041
Update index.js
decyjphr Oct 5, 2025
ac8e195
moved the dedup logic
decyjphr Oct 5, 2025
8bc76fc
Update index.js
decyjphr Oct 5, 2025
a5ef531
improved ui
jefeish May 8, 2026
b6887a2
Start at 2.1.18-rc1 and add roles plugin and enhance settings integr…
decyjphr May 15, 2026
bdcc6b5
Add custom repository roles schema to settings.json
decyjphr May 15, 2026
1d739f9
Add sub-org reevaluation logic and smoke tests
decyjphr May 19, 2026
baaa9d5
Add external group linking functionality for teams and update smoke t…
decyjphr May 19, 2026
6ca72a7
feat: add disable_plugins configuration to settings schema
decyjphr May 24, 2026
3cac68b
fix: add action.msg to dedup key so multiple disable_plugins NopComma…
decyjphr May 24, 2026
6938bf2
merge ydhav-issue-fix
jefeish May 26, 2026
a294cbd
feat: add support for additive_plugins in settings
decyjphr May 26, 2026
d03062a
fix: update .gitignore to ignore all .env files
decyjphr May 27, 2026
3312795
added base_url support
jefeish Jun 1, 2026
bfbd874
updated docs
jefeish Jun 1, 2026
4cb5e10
fixed sync log page
jefeish Jun 1, 2026
fa5020e
fix: update app.yml to remove empty line and add organization custom …
decyjphr Jun 2, 2026
d9be605
Refactor Variables Plugin: Simplify methods and add NopCommand support
decyjphr Jun 4, 2026
6a38988
fix: update variables handling in smoke test and add new repository Y…
decyjphr Jun 7, 2026
16b9375
Added PR989 changes
decyjphr Jun 7, 2026
1892ac7
Add reverse settings generator (issue #994)
decyjphr Jun 13, 2026
5fac715
feat: enhance smoke tests with custom repository roles and rulesets
decyjphr Jun 17, 2026
b250312
fix: enhance ruleset handling in MergeDeep and add tests for required…
decyjphr Jun 17, 2026
2bfc2e8
test: add ruleset comparison tests for required_reviewers and unnamed…
decyjphr Jun 17, 2026
e4498f1
added architecture diagram
jefeish Jun 18, 2026
79a8d43
hub-sync ui update
jefeish Jun 18, 2026
2d2f92f
feat: implement name-based resolution for ruleset bypass actors and r…
decyjphr Jun 23, 2026
2876b3f
Fix suborg-applied settings not removed when targeting rules change
decyjphr Jun 23, 2026
50fef03
Merge remote-tracking branch 'origin/decyjphr-fix-suborg-targeting-re…
jefeish Jun 23, 2026
4b9c44e
mergeConfig added
jefeish Jun 24, 2026
03b94b9
added yml merge-sync
jefeish Jul 6, 2026
16ce389
added PR Yaml validation
jefeish Jul 6, 2026
342f9f3
added ADR
jefeish Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,32 @@

# Uncomment this to get GitHub comments for the Pull Request Workflow.
# ENABLE_PR_COMMENT=true

# ADMIN_REPO=safe-settings-config
CONFIG_PATH=.github
SETTINGS_FILE_PATH=settings.yml

# URL prefix for deployment behind a proxy (appears in browser address bar)
# Default: /safe-settings
# Set to empty string for root path deployment: SAFE_SETTINGS_HUB_URL_PREFIX=
# SAFE_SETTINGS_HUB_URL_PREFIX=/safe-settings

# Configuration support for Hub-Sync safe-settings feature
# SAFE_SETTINGS_HUB_REPO=safe-settings-config-master
# SAFE_SETTINGS_HUB_ORG=foo-training
# A subfolder under 'CONFIG_PATH' where the 'organizations/<org>/<repo>' structure is found
# SAFE_SETTINGS_HUB_PATH=safe-settings
# SAFE_SETTINGS_HUB_DIRECT_PUSH=true



# ┌────────────── second (optional)
# │ ┌──────────── minute
# │ │ ┌────────── hour
# │ │ │ ┌──────── day of month
# │ │ │ │ ┌────── month
# │ │ │ │ │ ┌──── day of week
# │ │ │ │ │ │
# │ │ │ │ │ │
# * * * * * *
# CRON=* * * * * # Run every minute
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ npm-debug.log
.DS_Store
node_modules/
private-key.pem
.env
*.env
*.pem
.vscode
yarn.lock
Expand All @@ -140,3 +140,6 @@ samconfig.toml
# test file to be ignored
test.log
reports

# all general log files
*.log
422 changes: 420 additions & 2 deletions README.md

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ default_events:
- pull_request
- push
- repository
- repository_dispatch
- repository_ruleset
- team


# The set of permissions needed by the GitHub App. The format of the object uses
# the permission name for the key (for example, issues) and the access type for
# the value (for example, write).
Expand Down Expand Up @@ -114,6 +114,14 @@ default_permissions:
# https://developer.github.com/v3/apps/permissions/
organization_administration: write

# Manage custom organization roles.
# https://docs.github.com/en/enterprise-cloud@latest/rest/authentication/permissions-required-for-github-apps?apiVersion=2026-03-10#organization-permissions-for-custom-organization-roles
organization_custom_org_roles: write

# Manage custom repository roles.
# https://docs.github.com/en/enterprise-cloud@latest/rest/authentication/permissions-required-for-github-apps?apiVersion=2026-03-10#organization-permissions-for-custom-repository-roles
organization_custom_roles: write

# Manage Actions variables.
# https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28
actions_variables: write
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
| Configure deployment environments | [Deployment Environments](github-settings/6.%20deployment-environments.md) |
| Configure auto-link references | [AutoLinks](github-settings/7.%20autolinks.md) |
| Configure pre-defined labels for issues and pull requests | [Labels](github-settings/8.%20labels.md) |

For information on disabling plugins, see [Disabling plugins](../README.md#disabling-plugins-disable_plugins) in the root README.
15 changes: 14 additions & 1 deletion docs/github-settings/4. teams.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ teams:
</td></tr>
<tr><td>
<p>&emsp;<code>permission</code><span style="color:gray;">&emsp;<i>string</i>&emsp;</span></p>
<p>The permission to grant the team on this repository. We accept the following permissions to be set: <code>pull</code>, <code>triage</code>, <code>push</code>, <code>maintain</code>, <code>admin</code> and you can also specify a custom repository role name, if the owning organization has defined any. If no permission is specified, the team's <code>permission</code> attribute will be used to determine what permission to grant the team on this repository.</p>
<p>The permission to grant the team on this repository. We accept the following permissions to be set: <code>pull</code>, <code>triage</code>, <code>push</code>, <code>maintain</code>, <code>admin</code> and you can also specify a custom repository role name, if the owning organization has defined any.</p>
<p>Default: <code>push</code></p>
</td><td style="vertical-align:top">

Expand All @@ -48,5 +48,18 @@ teams:
permission: maintain
```

</td></tr>
<tr><td>
<p>&emsp;<code>external_group</code><span style="color:gray;">&emsp;<i>string</i>&emsp;</span></p>
<p>Optional. The <strong>display name</strong> of an external IdP group (as listed under your organization's external groups) to link to the team. <code>safe-settings</code> looks up the group's id by display name via <a href="https://docs.github.com/en/enterprise-cloud@latest/rest/teams/external-groups?apiVersion=2022-11-28#list-external-groups-available-to-an-organization"><code>GET /orgs/{org}/external-groups</code></a> and links the team via <a href="https://docs.github.com/en/enterprise-cloud@latest/rest/teams/external-groups?apiVersion=2022-11-28#update-the-connection-between-an-external-group-and-a-team"><code>PATCH /orgs/{org}/teams/{team_slug}/external-groups</code></a>. The link is reconciled on every sync and is idempotent (it skips the PATCH when the team is already linked to the same group). The external-groups list is fetched at most once per org per sync, only when at least one team entry uses this property. If the named group does not exist for the org, an error is logged and the team-repo association still applies.</p>
</td><td style="vertical-align:top">

```yaml
teams:
- name: expert-services-developers
permission: push
external_group: "Engineering - Expert Services"
```

</td></tr>
</table>
174 changes: 174 additions & 0 deletions docs/hubSyncHandler/ADR-manifest-control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# ADR: Manifest-Based Hub-Sync Control with Include/Exclude Rules

## Context and Problem Statement

The current hub-sync manifest (`globals/manifest.yml`) uses a simple rules-based structure with basic target organization selection. Users need more fine-grained control over:
1. Which organizations participate in hub-sync operations (blast radius control)
2. Which files/patterns should be synchronized from the master config repo
3. Ability to exclude specific organizations or file patterns from synchronization

Current limitations:
- Cannot easily exclude specific organizations from receiving updates
- Cannot exclude specific file patterns (e.g., repos/*, suborgs/*) from syncing
- Users must manually manage which files exist vs. which should sync
- No clear way to quarantine test/admin organizations from production syncs

## Decision Drivers

- **Safety**: Prevent accidental configuration pushes to production/sensitive orgs
- **Flexibility**: Support different sync strategies for different org/file combinations
- **Clarity**: Make it obvious which orgs and files are managed by hub-sync
- **Backward Compatibility**: Existing manifest configurations should continue to work
- **Simplicity**: Users should be able to understand and configure the manifest easily

## Considered Options

### Option 1: Manifest Controls Globals Only
- Manifest only controls distribution of `globals/` files
- Organization-specific changes (`organizations/{org}/`) always sync to their respective org
- Simple but limited blast radius control

### Option 2: Manifest Controls Everything (CHOSEN)
- Manifest controls ALL hub-sync operations (both globals/ and organizations/)
- Organizations not in `org_targets` receive NO hub-sync updates
- Files not matching `files_to_sync` patterns are never synchronized
- Full blast radius control at both org and file level

### Option 3: Per-Rule Include/Exclude Within Existing Structure
- Keep current rules structure, add include/exclude within each rule
- More complex but allows multiple policies (e.g., prod vs dev rules)

## Decision Outcome

**Chosen option: Option 2 - Manifest Controls Everything**

The manifest acts as the master control for all hub-sync operations, providing comprehensive blast radius control.

### Manifest Schema

```yaml
rules:
- name: global-defaults
org_targets:
include:
- "*" # All organizations
exclude:
- magh-admin # Admin org excluded
- test-* # Test orgs excluded

files_to_sync:
include:
- "*.yml" # All YAML files
exclude:
- repos/* # No repo configs
- suborgs/* # No suborg configs

mergeStrategy: merge # merge | overwrite | preserve
enabled: true

- name: security-baseline
org_targets:
include:
- prod-*
files_to_sync:
include:
- settings.yml
mergeStrategy: overwrite
```

### Behavior Examples

| Scenario | File Changed | Manifest Rule | Result |
|----------|-------------|---------------|---------|
| 1 | `globals/settings.yml` | Org: all except test-*, File: *.yml included | Syncs to prod orgs only |
| 2 | `globals/repos/common.yml` | File: repos/* excluded | NO SYNC (pattern excluded) |
| 3 | `organizations/test-org/settings.yml` | Org: test-* excluded | NO SYNC (org excluded) |
| 4 | `organizations/prod-org/settings.yml` | Org: prod-* included, File: *.yml included | Syncs to prod-org |
| 5 | `organizations/prod-org/repos/repo.yml` | File: repos/* excluded | NO SYNC (pattern excluded) |

### Positive Consequences

- **Full Control**: Organizations can be completely quarantined from hub-sync
- **File-Level Safety**: Dangerous file patterns can be excluded globally
- **Clear Boundaries**: Explicit include/exclude makes intent obvious
- **Flexibility**: Multiple rules support different policies
- **Non-Managed Files**: Files can exist in repo for reference without triggering sync

### Negative Consequences

- **Breaking Change**: Org-specific files can now be blocked by manifest (previously always synced)
- **Complexity**: Users must understand both org and file filtering
- **Migration**: Existing manifests may need updates to work as expected
- **Surprising Behavior**: Editing `organizations/my-org/settings.yml` might not sync if org is excluded

## Implementation Notes

### Key Changes Required

1. **Manifest Loading** (`hubSyncHandler.js`):
- Add `loadManifest(context, ref)` function
- Parse and validate manifest structure
- Cache manifest for performance

2. **Pattern Matching**:
- Use `minimatch` library for glob pattern matching
- Support wildcards: `*`, `test-*`, `*/repos/*`

3. **Filtering Logic**:
- Apply org filtering in `syncHubGlobalsUpdate` and `syncHubOrgUpdate`
- Apply file filtering before creating commits
- Short-circuit sync if org or file is excluded

4. **PR Validation Enhancement** (`validateAndReportHubSync`):
- Show which orgs will receive updates based on manifest
- Warn if changed files are excluded by manifest
- Display filtered org list in PR comment

5. **Manifest Validation**:
- Validate manifest YAML syntax in PRs
- Check for valid include/exclude structure
- Warn about conflicting rules

### Backward Compatibility Strategy

Support both old and new manifest formats:

**Old Format (still supported):**
```yaml
rules:
- name: global-defaults
targets:
- "*"
files:
- "*.yml"
```

**New Format:**
```yaml
rules:
- name: global-defaults
org_targets:
include:
- "*"
files_to_sync:
include:
- "*.yml"
```

**Migration Logic:**
- If `org_targets` exists, use new format
- If only `targets` exists, convert to `org_targets.include`
- Default behavior without manifest: sync everything (backward compatible)

## Links

- Related Documentation: [docs/hubSyncHandler/README.md](./README.md)
- Use Case: [docs/hubSyncHandler/usecase-merge-behavior.md](./usecase-merge-behavior.md)
- Implementation: [lib/hubSyncHandler.js](../../lib/hubSyncHandler.js)

---

**Status**: Proposed
**Date**: 2026-07-07
**Author**: Safe-Settings Team
**Decision**: Manifest-based hub-sync control with include/exclude rules for organizations and files
Loading