GitHub Safe-Settings

repository·main-enterprise·Indexed 21 days ago

https://github.com/github-community-projects/safe-settings

A policy-as-code tool for GitHub Organizations to centrally manage and enforce repository settings, branch protections, teams, and collaborators via YAML configuration files. It supports a hierarchical configuration model (Organization, Sub-organization, and Repository levels), drift prevention through scheduled syncs, and dry-run validation for pull requests. Safe-Settings can be deployed via Docker, AWS Lambda, or Kubernetes using an official Helm chart.

Tokens
23.6K
Snippets
56
Records
83
Agent score
75%

What's inside safe-settings

  1. What is GitHub Safe-Settings?

    main-enterprise

    GitHub Safe-Settings is a policy-as-code tool for GitHub Organizations. It allows you to centrally manage and enforce repository settings, branch protections, teams, and other configurations across your entire organization using YAML files stored in a central repository.

    Key features include:

    • Centralized Management: All settings are stored in a single admin repository rather than in individual target repositories.
    • Hierarchical Configuration: Settings can be applied at the Organization, Sub-organization, or Repository level.
    • Dry-Run Validation: When changes are proposed via Pull Request to the admin repository, the app runs in dry-run mode to validate changes before they are applied to the default branch.
    • Drift Prevention: Supports scheduled syncs to ensure target repositories stay in compliance with the defined configuration.
  2. Configure GitHub repository settings as code

    main-enterprise

    GitHub Safe-Settings allows you to manage GitHub repository configurations using a declarative 'settings as code' approach. Instead of manual configuration via the GitHub UI, you define your desired state in configuration files, which the tool then enforces across your organization's repositories.

    You can manage several categories of settings, including:

    • Repository Settings: General repository configurations.
    • Repository Variables: Repository-level environment or configuration variables.
    • Collaborators: Individual user permissions.
    • Teams: Team-based access and permissions.
    • Branch Protection: Rules for protecting specific branches.
    • Deployment Environments: Configuration for deployment targets.
    • AutoLinks: Automatic linking of references (e.g., issues, PRs).
    • Labels: Pre-defined labels for issues and pull requests.
  3. Configure GitHub Repository settings as code with Safe-Settings

    main-enterprise

    Safe-Settings allows you to manage GitHub repository configurations using a declarative 'settings as code' approach. Instead of manual configuration via the GitHub UI, you define your desired state in configuration files, which Safe-Settings then enforces across your organization.

    You can manage the following configuration areas:

    • Repository Settings: General settings for a specific repository or a group of repositories.
    • Repository Variables: Repository-level variables.
    • Collaborators: Individual collaborator permissions.
    • Teams: Team-based permissions.
    • Branch Protection: Rules for protecting branches.
    • Deployment Environments: Configuration for deployment environments.
    • AutoLinks: Auto-link reference configurations.
    • Labels: Pre-defined labels for issues and pull requests.
  4. How the configuration hierarchy works

    main-enterprise

    Safe-Settings uses a hierarchical model to apply settings. Configurations are merged in a specific order, where more specific settings override more general ones.

    Precedence Order (Highest to Lowest):

    1. Repository Settings: Specific to a single repo. Located in .github/repos/*.yml.
    2. Sub-Organization Settings: Applied to a collection of repos (based on names, teams, or custom properties). Located in .github/suborgs/*.yml.
    3. Organization Settings: Applied to the entire organization. Located in .github/settings.yml.

    Target Types:

    • org: Settings applied to the organization level (currently only rulesets are supported).
    • repo: Settings applied to individual repositories.

    It is recommended to split settings into these three units to allow different teams to manage their own policies using CODEOWNERS on the admin repository.

  5. Combine multiple Probot apps into one instance

    main-enterprise

    You can deploy multiple apps within a single instance by creating a new Probot app and listing the existing apps as dependencies in package.json. Use the probot.apps key to specify which apps to run.

    {
      "name": "my-probot-app",
      "private": true,
      "dependencies": {
        "probot-autoresponder": "probot/autoresponder",
        "probot-settings": "probot/settings"
      },
      "scripts": {
        "start": "probot run"
      },
      "probot": {
        "apps": ["probot-autoresponder", "probot-settings"]
      }
    }
  6. How Safe-settings works and reacts to events

    main-enterprise

    Safe-settings operates as a service that listens for GitHub webhook events, runs as a scheduled job, or is triggered via GitHub Actions. It ensures repository settings remain consistent with your configuration files.

    Key Webhook Events

    • push: Triggered when .github/settings.yml, .github/repos/*.yml, or .github/suborgs/*.yml are modified in the default branch of the admin repo. Settings are applied using a hierarchy: Repo settings > Sub-org settings > Org settings.
    • repository.created: Automatically applies the hierarchy of settings to new repositories.
    • branch_protection_rule & repository_ruleset: If modified via the GitHub UI, Safe-settings will sync the settings to revert unauthorized changes.
    • repository.edited: Syncs settings if properties like the default branch or topics change.
    • repository.renamed: By default, it ignores renames. If BLOCK_REPO_RENAME_BY_HUMAN=true, it reverts renames unless performed by a bot. If a bot renames a repo, it attempts to copy <old-repo>.yml to <new-repo>.yml.
    • pull_request.opened/reopened & check_suite.requested: If settings are changed in a non-default branch, Safe-settings runs in nop mode and updates the PR with a dry-run status report.
    • member/team events: Syncs settings to prevent unauthorized permission changes.
    • custom_property_values: Applies sub-org configurations if a repository's custom properties match a defined sub-org scope.
  7. Understand status check inheritance for Rulesets

    main-enterprise

    When using GitHub Rulesets, status checks defined at the Organization, Sub-organization, and Repository levels are independent. However, there are specific constraints and behaviors regarding how Safe-Settings manages them:

    Deployment Constraints

    • Duplicate Definition Error: You cannot define the same required_status_checks at both a Sub-org level and a Repository level for the same repository. If you attempt to deploy such a configuration, the repository-level deployment will fail with the error: required_status_checks can't be defined twice in both sub-org and repo level.

    Behavior with Custom (Externally Defined) Checks

    Safe-Settings manages the reconciliation of status checks based on where custom checks are introduced:

    1. No Custom Checks: If no custom checks are defined in your configuration, Safe-Settings will revert any manual changes made via the GitHub UI back to the safe settings at the Org and Sub-org levels.
    2. Custom Checks at Org/Sub-org Level: If you use {{EXTERNALLY_DEFINED}} at the Org or Sub-org level, Safe-Settings will retain those custom checks when you update via the GitHub UI. However, if a repository (e.g., Repo2) has its own specific status checks defined in the configuration, Safe-Settings will revert those repository-level checks to the safe settings if you try to add custom ones via the UI.
    3. Custom Checks at Repo Level: If custom checks are defined at the Repository level, Safe-Settings will retain them when updated via the GitHub UI, but it will revert the Org and Sub-org status checks to the safe settings if they are modified manually.
  8. Understand status check inheritance for Branch Protection Rules

    main-enterprise

    Branch protection rules follow a hierarchical inheritance model where checks from higher levels (Org/Sub-org) flow down to repositories. Safe-Settings manages these as follows:

    Inheritance Logic

    • Standard Inheritance: By default, a repository inherits all status checks defined at the Organization and Sub-organization levels. For example, if main is protected at the Org level with Org Check and at the Sub-org level with Sub-org Check, a repository (Repo1) will automatically have both applied.

    Managing Custom Checks

    Safe-Settings allows for 'custom' (externally defined) checks, but their placement affects how the hierarchy is enforced:

    • Custom Checks at Org or Sub-org Level: If you define custom checks at these higher levels, Safe-Settings will allow those custom checks to be retained when modified via the GitHub UI. However, during a new deployment of rules, the inherited checks from higher levels may be cleared ([]) at the repository level to ensure the higher-level configuration is the source of truth.
    • Custom Checks at Repo Level: If you define custom checks at the Repository level, Safe-Settings will retain them when updated via the GitHub UI. However, it will revert the status checks at the Org and Sub-org levels to the safe settings if they are manually changed, and it will not automatically inherit the higher-level checks into that specific repository's protection rule if the repository-level rule is being managed as a custom set.
  9. How the dual Lambda architecture works

    main-enterprise

    The AWS Lambda deployment uses a dual Lambda function architecture to separate real-time event handling from background maintenance tasks. Both functions are powered by the same Docker image but are configured with different entry points (commands) via the image-config parameter.

    • safe-settings-lambda (Webhook Handler): Triggered by GitHub webhooks via a Function URL. It processes real-time events using the safe-settings-handler.webhooks command.
    • safe-settings-scheduler (Scheduler Handler): Triggered by Amazon EventBridge on a schedule. It performs periodic synchronization tasks using the safe-settings-handler.scheduler command.

    Inside the code, the safe-settings-handler.js uses smart routing logic to determine which handler to execute based on the incoming event source.

    // EventBridge scheduled events → Scheduler handler
    if (event.source === 'aws.events' || event.sync === true) {
      return await schedulerHandler(event, context)
    }
    
    // GitHub webhooks → Webhook handler  
    return await webhookHandler(event, context)
  10. Pull and test the GHCR Docker image

    main-enterprise

    To test the published image from the GitHub Container Registry (GHCR), pull the specific version and run it with the same runtime flags used for local testing to ensure consistent behavior.

    # Pull the published image
    docker pull ghcr.io/github/safe-settings:2.1.19
    
    # Run the container
    docker run --name safe-settings-ghcr \
    	--env-file ./.env \
    	--env NODE_ENV=development \
    	--env HOST=0.0.0.0 \
    	-p 3000:3000 \
    	-it ghcr.io/github/safe-settings:2.1.19
  11. Configure externally defined status checks

    main-enterprise

    You can allow status checks to be managed outside of Safe-settings (e.g., by other CI tools) while still using Safe-settings for branch protection or rulesets. Use the {{EXTERNALLY_DEFINED}} placeholder in the contexts or required_status_checks field.

    Usage

    • Branch Protection: Specify {{EXTERNALLY_DEFINED}} under branches.protection.required_status_checks.contexts.
    • Rulesets: Specify {{EXTERNALLY_DEFINED}} under rulesets.rules.parameters.required_status_checks.

    Important Behaviors

    • Merging: Contexts defined at the org level are merged into sub-org and repo levels. Rules from sub-orgs are merged into repo-level rules if they share the same name.
    • Deployment: When first deployed with {{EXTERNALLY_DEFINED}}, the rule is created with no status checks. Existing rules in GitHub remain unchanged.
    • ⚠️ Warning: If you remove {{EXTERNALLY_DEFINED}} from a configuration, Safe-settings will revert the GitHub settings to match exactly what is defined in your YAML files, potentially overwriting manual UI changes.
    # Branch protection example
    branches:
      - name: main
        protection:
          ...
          required_status_checks:
            contexts:
              - "{{EXTERNALLY_DEFINED}}"
    
    # Ruleset example
    rulesets:
      - name: Status Checks
        ...
        rules:
          - type: required_status_checks
            parameters:
              required_status_checks:
                - context: "{{EXTERNALLY_DEFINED}}"
  12. Configure team permissions in repository settings

    main-enterprise

    You can explicitly grant specific teams access to a repository using the teams key in your configuration files. This is particularly useful for ensuring CODEOWNERS recognizes teams as valid owners, even if your organization defaults to push/write permissions.

    This configuration can be applied within repos/<file>.yml or suborgs/<file>.yml files.

    # These settings are synced to GitHub by https://github.com/github/safe-settings
    teams:
      - name: JusticeLeague
        permission: maintain
      - name: Avengers
        permission: maintain