Skip to main content
Deployment settings control how Safe Settings operates, including which repositories it manages, custom validation rules, and runtime behavior. These settings are stored in a separate file from your repository configurations.

File Location

Deployment settings are stored in deployment-settings.yml in the directory where Safe Settings is running:
/path/to/safe-settings/
└── deployment-settings.yml
Unlike repository settings (which are in the admin repo), deployment settings live alongside the Safe Settings application.You can customize the file location using the DEPLOYMENT_CONFIG_FILE environment variable (default: deployment-settings.yml).

Restricted Repositories

Control which repositories Safe Settings can manage using the restrictedRepos configuration.

Default Behavior

If no deployment-settings.yml file exists, Safe Settings excludes these repositories by default:
  • admin
  • .github
  • safe-settings

Exclude Repositories

Prevent Safe Settings from managing specific repositories:
deployment-settings.yml
restrictedRepos:
  exclude:
    - admin
    - .github
    - safe-settings
    - admin-*         # Exclude all repos starting with 'admin-'
    - test-*          # Exclude all test repositories
    - "*-archive"     # Exclude all archived repositories
Use exclude when you want Safe Settings to manage most repositories except a few:
  • Administrative repositories
  • Legacy repositories
  • Repositories managed by other tools
  • Personal/test repositories

Include Repositories

Only allow Safe Settings to manage specific repositories:
deployment-settings.yml
restrictedRepos:
  include:
    - api-*          # Only manage repos starting with 'api-'
    - core-*         # Only manage repos starting with 'core-'
    - docs           # Specific repo
Use include when you want to:
  • Gradually roll out Safe Settings to your organization
  • Limit Safe Settings to specific project areas
  • Test Safe Settings on a subset of repositories

Combine Include and Exclude

You can use both include and exclude together:
deployment-settings.yml
restrictedRepos:
  include:
    - "*"            # Include all repositories
  exclude:
    - admin
    - .github
    - safe-settings
    - test-*
    - "*-archive"
Or more specifically:
deployment-settings.yml
restrictedRepos:
  include:
    - api-*          # Include all API repositories
    - core-*         # Include all core repositories
  exclude:
    - api-test       # Except this one
    - core-legacy    # And this one
How include/exclude work together:
  • If only exclude is specified → Run on all repos except excluded ones
  • If only include is specified → Run only on included repos
  • If both are specified → Run only on included repos that aren’t excluded

Pattern Matching

Pattern matching uses glob expressions:
restrictedRepos:
  exclude:
    - admin          # Exact match
    - test-*         # Starts with 'test-'
    - "*-archive"    # Ends with '-archive'
    - "*-temp-*"     # Contains '-temp-'

Config Validators

Config validators enforce global rules on settings, regardless of hierarchy level. They validate a setting on its own.

Example: Prevent Admin Collaborators

Prevent anyone from assigning admin permissions to collaborators:
deployment-settings.yml
configvalidators:
  - plugin: collaborators
    error: |
      Admin role cannot be assigned to collaborators
    script: |
      console.log(`baseConfig ${JSON.stringify(baseconfig)}`)
      return baseconfig.permission != 'admin'

Example: Enforce Topics

Require all repositories to have at least one topic:
deployment-settings.yml
configvalidators:
  - plugin: repository
    error: |
      All repositories must have at least one topic
    script: |
      return baseconfig.topics && baseconfig.topics.length > 0

Example: Validate Branch Names

Ensure branch protection is only applied to valid branch names:
deployment-settings.yml
configvalidators:
  - plugin: branches
    error: |
      Branch name must be 'default', 'main', 'master', or match 'release/*'
    script: |
      const validNames = ['default', 'main', 'master']
      const validPatterns = [/^release\/.*/]
      
      if (validNames.includes(baseconfig.name)) {
        return true
      }
      
      return validPatterns.some(pattern => pattern.test(baseconfig.name))
Config validators have access to:
  • baseconfig - The configuration being validated
  • githubContext - GitHub API context
The script must return:
  • true if validation passes
  • false if validation fails (error message will be shown)

Override Validators

Override validators control whether suborg or repo-level settings can override org-level settings. They validate the relationship between base and override configurations.

Example: Prevent Weakening Branch Protection

Prevent repos from requiring fewer approvals than the org level:
deployment-settings.yml
overridevalidators:
  - plugin: branches
    error: |
      Branch protection required_approving_review_count cannot be overridden to a lower value
    script: |
      console.log(`baseConfig ${JSON.stringify(baseconfig)}`)
      console.log(`overrideConfig ${JSON.stringify(overrideconfig)}`)
      
      if (baseconfig.protection.required_pull_request_reviews.required_approving_review_count && 
          overrideconfig.protection.required_pull_request_reviews.required_approving_review_count) {
        return overrideconfig.protection.required_pull_request_reviews.required_approving_review_count >= 
               baseconfig.protection.required_pull_request_reviews.required_approving_review_count
      }
      return true

Example: Prevent Disabling Features

Prevent repos from disabling security features:
deployment-settings.yml
overridevalidators:
  - plugin: repository
    error: |
      Security features cannot be disabled at repo level
    script: |
      if (baseconfig.security && overrideconfig.security) {
        // Ensure alerts aren't disabled
        if (baseconfig.security.enableVulnerabilityAlerts === true && 
            overrideconfig.security.enableVulnerabilityAlerts === false) {
          return false
        }
        // Ensure automated fixes aren't disabled
        if (baseconfig.security.enableAutomatedSecurityFixes === true && 
            overrideconfig.security.enableAutomatedSecurityFixes === false) {
          return false
        }
      }
      return true

Example: Require Minimum Status Checks

Ensure repos don’t remove required status checks:
deployment-settings.yml
overridevalidators:
  - plugin: branches
    error: |
      Required status checks cannot be removed or reduced
    script: |
      const baseChecks = baseconfig.protection?.required_status_checks?.contexts || []
      const overrideChecks = overrideconfig.protection?.required_status_checks?.contexts || []
      
      // All base checks must be present in override
      return baseChecks.every(check => overrideChecks.includes(check))
Override validators have access to:
  • baseconfig - The base configuration (from org or suborg level)
  • overrideconfig - The overriding configuration (from suborg or repo level)
  • githubContext - GitHub API context
The script must return:
  • true if the override is allowed
  • false if the override should be rejected (error message will be shown)

Validator Plugin Names

Validators use plugin names to determine which settings they validate:
Plugin NameSettings Validated
repositoryRepository settings
branchesBranch protection rules
collaboratorsCollaborator access
teamsTeam access
labelsLabels
milestonesMilestones
autolinksAutolinks
validatorRepository name validation
rulesetsRepository rulesets
environmentsDeployment environments
custom_propertiesCustom properties
variablesRepository variables

Complete Example

Here’s a comprehensive deployment settings file:
deployment-settings.yml
# Restrict which repositories Safe Settings manages
restrictedRepos:
  include:
    - "prod-*"
    - "api-*"
    - "core-*"
    - docs
  exclude:
    - admin
    - .github
    - safe-settings
    - "*-test"
    - "*-archive"

# Config validators - validate settings independently
configvalidators:
  # Prevent admin permissions for collaborators
  - plugin: collaborators
    error: |
      Admin role cannot be assigned to collaborators
    script: |
      console.log(`baseConfig ${JSON.stringify(baseconfig)}`)
      return baseconfig.permission != 'admin'
  
  # Require topics on all repos
  - plugin: repository
    error: |
      All repositories must have at least one topic
    script: |
      return baseconfig.topics && baseconfig.topics.length > 0
  
  # Ensure security features are enabled
  - plugin: repository
    error: |
      Security features must be enabled for all repositories
    script: |
      if (!baseconfig.security) {
        return false
      }
      return baseconfig.security.enableVulnerabilityAlerts === true && 
             baseconfig.security.enableAutomatedSecurityFixes === true

# Override validators - control what can be overridden
overridevalidators:
  # Prevent reducing required approvals
  - plugin: branches
    error: |
      Branch protection required_approving_review_count cannot be overridden to a lower value
    script: |
      console.log(`baseConfig ${JSON.stringify(baseconfig)}`)
      console.log(`overrideConfig ${JSON.stringify(overrideconfig)}`)
      
      if (baseconfig.protection?.required_pull_request_reviews?.required_approving_review_count && 
          overrideconfig.protection?.required_pull_request_reviews?.required_approving_review_count) {
        return overrideconfig.protection.required_pull_request_reviews.required_approving_review_count >= 
               baseconfig.protection.required_pull_request_reviews.required_approving_review_count
      }
      return true
  
  # Prevent disabling enforce_admins
  - plugin: branches
    error: |
      enforce_admins cannot be disabled if enabled at org level
    script: |
      if (baseconfig.protection?.enforce_admins === true && 
          overrideconfig.protection?.enforce_admins === false) {
        return false
      }
      return true
  
  # Prevent making repos public
  - plugin: repository
    error: |
      Repositories cannot be changed from private to public
    script: |
      if (baseconfig.private === true && overrideconfig.private === false) {
        return false
      }
      return true
  
  # Placeholder for labels (must return true)
  - plugin: labels
    error: |
      Label validation error
    script: |
      return true

Testing Validators

Test your validators by creating a PR in your admin repo that should fail validation:
1

Create Invalid Config

Create a repo config that violates your validator:
.github/repos/test.yml
collaborators:
  - username: testuser
    permission: admin  # This should fail validation
2

Create Pull Request

Create a PR with this change. Safe Settings will run in dry-run mode.
3

Check Results

The check run should fail with your custom error message. Verify the error is clear and helpful.
4

Iterate

Adjust your validator script and error message based on the results.

Validator Best Practices

Clear Error Messages

Write descriptive error messages that explain what’s wrong and how to fix it.

Handle Edge Cases

Check for null, undefined, and missing properties before accessing nested values.

Log Context

Use console.log to debug your validators. Logs appear in Safe Settings output.

Return Boolean

Always return true or false. Other return values will cause errors.

Common Validator Patterns

configvalidators:
  - plugin: branches
    error: Required reviewers must be between 1 and 6
    script: |
      const count = baseconfig.protection?.required_pull_request_reviews?.required_approving_review_count
      if (count === undefined) return true
      return count >= 1 && count <= 6

Troubleshooting

Check:
  1. Is the plugin name correct? Must match one of the valid plugin names.
  2. Is the YAML valid? Validate with a YAML linter.
  3. Is the deployment-settings.yml file in the correct location?
  4. Check Safe Settings logs for script errors.
Common issues:
  • Missing return statement
  • Using var instead of const or let
  • Accessing properties that might not exist (use optional chaining: ?.)
  • Incorrect comparison operators
Check Safe Settings logs for detailed JavaScript errors.
Add console.log statements to debug:
script: |
  console.log('baseconfig:', JSON.stringify(baseconfig))
  console.log('overrideconfig:', JSON.stringify(overrideconfig))
  const result = /* your validation logic */
  console.log('result:', result)
  return result

Next Steps

Sample Deployment Settings

View complete sample configuration

Configuration Hierarchy

Understand when validators run

Environment Variables

Configure Safe Settings runtime behavior

Custom Validators Guide

Advanced validator examples

Build docs developers (and LLMs) love