Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1107316
fix: apply org-level settings before loading repository configurations
madkoo Feb 12, 2026
0051d0e
fix: enhance descriptions and add new properties for security feature…
madkoo Feb 12, 2026
bb1b033
fix: update description for deprecated squash-merge commit title prop…
madkoo Feb 12, 2026
e5bd8dd
Merge branch 'main-enterprise' into bug/issue-842
madkoo Mar 12, 2026
9d141d8
test: update branch protection tests to handle null restrictions and …
madkoo Mar 12, 2026
dce5977
fix: normalize branch protection restrictions and preserve existing s…
madkoo Mar 13, 2026
873206c
Merge branch 'main-enterprise' into bug/issue-842
madkoo Mar 16, 2026
3c8839d
Merge branch 'main-enterprise' into bug/issue-842
madkoo Mar 18, 2026
2ec910d
Merge branch 'main-enterprise' into bug/issue-842
decyjphr Mar 23, 2026
6a96579
Merge branch 'github:main-enterprise' into main-enterprise
madkoo Mar 27, 2026
d57526e
Update api endpoint version
madkoo Mar 27, 2026
8450339
Add JSON schemas for safe-settings configuration at repo, org, and su…
madkoo Mar 27, 2026
8a5f2ca
chores: fix branches tests
madkoo Mar 30, 2026
70cad39
fix: update GitHub API calls to use the correct namespace for branch …
madkoo Mar 30, 2026
1c081a1
fix(build-schema): enhance schema dereferencing with GitHub API spec
madkoo Apr 7, 2026
be0a6a2
Merge branch 'main-enterprise' into bug/issue-465
madkoo Apr 7, 2026
ca31ca1
Merge remote-tracking branch 'refs/remotes/origin/main-enterprise' in…
madkoo Apr 7, 2026
f255a63
Update docs/github-settings/5. branch-protection.md
madkoo Apr 9, 2026
cdea52e
Update index.js
madkoo Apr 9, 2026
33531b1
fix(schema): simplify description for force_create property
madkoo Apr 9, 2026
efc5df9
feat: improve PR comment output with empty-change filtering and pagin…
madkoo May 5, 2026
f37336f
feat: add isDeepEmpty function for recursive empty detection and enha…
madkoo May 7, 2026
eb4d8aa
Merge branch 'bug/issue-842' into feature/improve-validation-results
madkoo May 7, 2026
3c9dd59
manual merge bug-465
madkoo May 7, 2026
d595cc2
feat: filter NOP results to PR-introduced changes
madkoo May 11, 2026
5d4b448
feat: compact NOP comment output
madkoo May 12, 2026
7c6123d
feat: expand NOP comment change details
madkoo May 19, 2026
ed58fb6
fix: preserve same-value fields in NOP comments
madkoo May 19, 2026
0e31a8c
feat: simplify NOP comment layout
madkoo May 21, 2026
d86fa03
update schemas
madkoo May 27, 2026
abffac0
Merge remote-tracking branch 'refs/remotes/origin/main-enterprise' in…
madkoo May 27, 2026
fdd5430
First implementation of comment compression
madkoo May 29, 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
48 changes: 39 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,29 @@ let deploymentConfig

module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) => {
let appSlug = 'safe-settings'
async function syncAllSettings (nop, context, repo = context.repo(), ref) {
async function syncAllSettings (nop, context, repo = context.repo(), ref, baseRef, changedFiles = {}) {
try {
deploymentConfig = await loadYamlFileSystem()
robot.log.debug(`deploymentConfig is ${JSON.stringify(deploymentConfig)}`)
const configManager = new ConfigManager(context, ref)
const runtimeConfig = await configManager.loadGlobalSettingsYaml()
const config = Object.assign({}, deploymentConfig, runtimeConfig)
robot.log.debug(`config for ref ${ref} is ${JSON.stringify(config)}`)

// Load base branch config for NOP filtering (only show PR-introduced changes)
let baseConfig = null
if (nop && baseRef) {
try {
const baseConfigManager = new ConfigManager(context, baseRef)
const baseRuntimeConfig = await baseConfigManager.loadGlobalSettingsYaml()
baseConfig = Object.assign({}, deploymentConfig, baseRuntimeConfig)
} catch (e) {
robot.log.debug(`Could not load base config for NOP filtering: ${e.message}`)
}
}

if (ref) {
return Settings.syncAll(nop, context, repo, config, ref)
return Settings.syncAll(nop, context, repo, config, ref, baseConfig, changedFiles)
} else {
return Settings.syncAll(nop, context, repo, config)
}
Expand Down Expand Up @@ -73,15 +86,28 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
}
}

async function syncSelectedSettings (nop, context, repos, subOrgs, ref) {
async function syncSelectedSettings (nop, context, repos, subOrgs, ref, baseRef) {
try {
deploymentConfig = await loadYamlFileSystem()
robot.log.debug(`deploymentConfig is ${JSON.stringify(deploymentConfig)}`)
const configManager = new ConfigManager(context, ref)
const runtimeConfig = await configManager.loadGlobalSettingsYaml()
const config = Object.assign({}, deploymentConfig, runtimeConfig)
robot.log.debug(`config for ref ${ref} is ${JSON.stringify(config)}`)
return Settings.syncSelectedRepos(nop, context, repos, subOrgs, config, ref)

// Load base branch config for NOP filtering
let baseConfig = null
if (nop && baseRef) {
try {
const baseConfigManager = new ConfigManager(context, baseRef)
const baseRuntimeConfig = await baseConfigManager.loadGlobalSettingsYaml()
baseConfig = Object.assign({}, deploymentConfig, baseRuntimeConfig)
} catch (e) {
robot.log.debug(`Could not load base config for NOP filtering: ${e.message}`)
}
}

return Settings.syncSelectedRepos(nop, context, repos, subOrgs, config, ref, baseConfig)
} catch (e) {
if (nop) {
let filename = env.SETTINGS_FILE_PATH
Expand Down Expand Up @@ -585,17 +611,21 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) =>
const files = changes.data.map(f => { return f.filename })

const settingsModified = files.includes(Settings.FILE_PATH)
const repoChanges = getChangedRepoConfigName(files, context.repo().owner)
const subOrgChanges = getChangedSubOrgConfigName(files)

if (settingsModified) {
robot.log.debug(`Changes in '${Settings.FILE_PATH}' detected, doing a full synch...`)
return syncAllSettings(true, context, context.repo(), pull_request.head.ref)
const baseRef = pull_request.base.ref || repository.default_branch
return syncAllSettings(true, context, context.repo(), pull_request.head.ref, baseRef, {
repos: repoChanges,
subOrgs: subOrgChanges
})
}

const repoChanges = getChangedRepoConfigName(files, context.repo().owner)
const subOrgChanges = getChangedSubOrgConfigName(files)

if (repoChanges.length > 0 || subOrgChanges.length > 0) {
return syncSelectedSettings(true, context, repoChanges, subOrgChanges, pull_request.head.ref)
const baseRef = pull_request.base.ref || repository.default_branch
return syncSelectedSettings(true, context, repoChanges, subOrgChanges, pull_request.head.ref, baseRef)
}

// if no safe-settings changes detected, send a success to the check run
Expand Down
43 changes: 22 additions & 21 deletions lib/commentmessage.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
module.exports = `* Run on: \` <%= new Date() %> \`
module.exports = `* Run on: \`<%= new Date() %>\`

* Number of repos that were considered: \`<%= Object.keys(it.reposProcessed).length %> \`
* Number of repos that were considered: \`<%= Object.keys(it.reposProcessed).length %>\`
* Number of repos affected: \`<%= it.reposAffected || 0 %>\`

### Breakdown of changes
| Repo <% Object.keys(it.changes).forEach(plugin => { %> | <%= plugin %> settings <% }) %> |
| -- <% Object.keys(it.changes).forEach(plugin => { -%> | -- <% }) %>
|
<% Object.keys(it.reposProcessed).forEach( repo => { -%>
| <%= repo -%>
<%- Object.keys(it.changes).forEach(plugin => { -%>
<%_ if (it.changes[plugin][repo]) { -%> | :hand: <% } else { %> | :grey_exclamation: <% } -%>
<%_ }) -%> |
<% }) -%>

:hand: -> Changes to be applied to the GitHub repository.
:grey_exclamation: -> nothing to be changed in that particular GitHub repository.
<% if (!it.changeSections || it.changeSections.length === 0) { %>

### Breakdown of errors
No changes to apply.
<% } else { %>

<%~ it.checkRunDetails %>
<% } %>

### Breakdown of errors
<% if (Object.keys(it.errors).length === 0) { %>

\`None\`
<% } else { %>
<% Object.keys(it.errors).forEach(repo => { %>
<%_= repo %>:
<% it.errors[repo].forEach(plugin => { %>
* <%= plugin.msg %>
<% }) %>

<% }) %>
<details>
<summary>Errors by repo</summary>

<% Object.keys(it.errors).forEach(function(repo) { %>
**<%= repo %>**

<% it.errors[repo].forEach(function(err) { %>* <%= err.msg %>
<% }) %>
<% }) %>

</details>
<% } %>`
47 changes: 11 additions & 36 deletions lib/plugins/custom_properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,7 @@ module.exports = class CustomProperties extends Diffable {

// Force all names to lowercase to avoid comparison issues.
normalizeEntries () {
this.entries = this.entries.reduce((normalizedEntries, entry) => {
if (!entry || typeof entry !== 'object') {
return normalizedEntries
}

const entryName = entry.name || entry.property_name

if (typeof entryName !== 'string') {
return normalizedEntries
}

normalizedEntries.push({
name: entryName.toLowerCase(),
value: entry.value
})

return normalizedEntries
}, [])
this.entries = this.normalize(this.entries)
}

async find () {
Expand All @@ -52,24 +35,16 @@ module.exports = class CustomProperties extends Diffable {

// Force all names to lowercase to avoid comparison issues.
normalize (properties) {
return properties.reduce((normalizedProperties, property) => {
if (!property || typeof property !== 'object') {
return normalizedProperties
}

const propertyName = property.property_name || property.name

if (typeof propertyName !== 'string') {
return normalizedProperties
}

normalizedProperties.push({
name: propertyName.toLowerCase(),
value: property.value
})

return normalizedProperties
}, [])
return properties
.map(({ property_name: propertyName, name, value }) => ({
name: propertyName ?? name,
value
}))
.filter(({ name }) => name != null)
.map(({ name, value }) => ({
name: name.toLowerCase(),
value
}))
Comment on lines +38 to +47
}

comparator (existing, attrs) {
Expand Down
Loading
Loading