From c0398b2ed501f991ae6f2ac5866c890756464dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Daba=C5=A1inskas?= Date: Sat, 11 Jul 2026 13:17:55 +0300 Subject: [PATCH] feat(logging): add info-level logging for applied configuration changes When safe-settings applies changes to repositories, users need visibility into what actions are being taken without enabling debug logging. This change adds informational log messages when changes are applied, while keeping detailed diffs at debug level. The info messages indicate: - Which plugin is applying changes (branches, repository settings, topics, labels, etc.) - Which repository is being modified - That detailed diffs are available at debug level This improves operational visibility and makes it easier to track configuration changes in production environments without overwhelming logs with detailed diffs. --- lib/plugins/branches.js | 2 ++ lib/plugins/diffable.js | 2 ++ lib/plugins/repository.js | 10 ++++++++-- test/unit/lib/plugins/autolinks.test.js | 2 +- test/unit/lib/plugins/branches.test.js | 16 ++++++++++++++-- test/unit/lib/plugins/collaborators.test.js | 2 +- test/unit/lib/plugins/custom_properties.test.js | 2 +- test/unit/lib/plugins/environments.test.js | 4 ++-- test/unit/lib/plugins/labels.test.js | 4 +++- test/unit/lib/plugins/repository.test.js | 5 ++++- test/unit/lib/plugins/rulesets.test.js | 1 + test/unit/lib/plugins/teams.test.js | 2 +- test/unit/lib/plugins/variables.test.js | 2 +- 13 files changed, 41 insertions(+), 13 deletions(-) diff --git a/lib/plugins/branches.js b/lib/plugins/branches.js index d60a20cc3..0afe267f5 100644 --- a/lib/plugins/branches.js +++ b/lib/plugins/branches.js @@ -79,6 +79,8 @@ module.exports = class Branches extends ErrorStash { this.log.debug(`There are changes for branch ${JSON.stringify(params)}\n ${JSON.stringify(changes)} \n Branch protection will be applied`) if (this.nop) { resArray.push(new NopCommand(this.constructor.name, this.repo, null, results)) + } else { + this.log.info(`Applying branch protection changes to ${params.branch} branch of ${this.repo.owner}/${this.repo.repo} (the diff is logged at debug level)`) } Object.assign(params, requiredBranchProtectionDefaults, this.reformatAndReturnBranchProtection(structuredClone(result.data)), Overrides.removeOverrides(overrides, branch.protection, result.data), { headers: previewHeaders }) diff --git a/lib/plugins/diffable.js b/lib/plugins/diffable.js index 069c68c78..3128aeb03 100644 --- a/lib/plugins/diffable.js +++ b/lib/plugins/diffable.js @@ -78,6 +78,8 @@ module.exports = class Diffable extends ErrorStash { } else { if (this.nop) { resArray.push(new NopCommand(this.constructor.name, this.repo, null, results, 'INFO')) + } else { + this.log.info(`Applying ${this.constructor.name} changes to ${this.repo.owner}/${this.repo.repo} (the diff is logged at debug level)`) } } diff --git a/lib/plugins/repository.js b/lib/plugins/repository.js index 8f1a3f5dc..bb3154a33 100644 --- a/lib/plugins/repository.js +++ b/lib/plugins/repository.js @@ -77,11 +77,11 @@ module.exports = class Repository extends ErrorStash { // const results = JSON.stringify(changes, null, 2) const results = { msg: `${this.constructor.name} settings changes`, additions: changes.additions, modifications: changes.modifications, deletions: changes.deletions } - this.log.debug(`Result of comparing repo for changes = ${results}`) + this.log.debug(`Result of comparing repo for changes = ${JSON.stringify(results)}`) // const topicResults = JSON.stringify(topicChanges, null, 2) const topicResults = { msg: `${this.constructor.name} settings changes for topics`, additions: topicChanges.additions, modifications: topicChanges.modifications, deletions: topicChanges.deletions } - this.log.debug(`Result of comparing topics for changes source ${JSON.stringify(resp.data.topics)} target ${JSON.stringify(this.topics)} = ${topicResults}`) + this.log.debug(`Result of comparing topics for changes source ${JSON.stringify(resp.data.topics)} target ${JSON.stringify(this.topics)} = ${JSON.stringify(topicResults)}`) if (this.nop && changes.hasChanges) { resArray.push(new NopCommand('Repository', this.repo, null, results)) @@ -91,10 +91,16 @@ module.exports = class Repository extends ErrorStash { } const promises = [] if (topicChanges.hasChanges) { + if (!this.nop) { + this.log.info(`Applying topic changes to ${this.repo.owner}/${this.repo.repo} (the diff is logged at debug level)`) + } promises.push(this.updatetopics(resp.data, resArray)) } if (changes.hasChanges) { this.log.debug('There are repo changes') + if (!this.nop) { + this.log.info(`Applying repository settings changes to ${this.repo.owner}/${this.repo.repo} (the diff is logged at debug level)`) + } let updateDefaultBranchPromise = Promise.resolve() if (this.settings.default_branch && (resp.data.default_branch !== this.settings.default_branch)) { this.log.debug('There is a rename of the default branch') diff --git a/test/unit/lib/plugins/autolinks.test.js b/test/unit/lib/plugins/autolinks.test.js index 02829daa0..9d5d61f0b 100644 --- a/test/unit/lib/plugins/autolinks.test.js +++ b/test/unit/lib/plugins/autolinks.test.js @@ -5,7 +5,7 @@ describe('Autolinks', () => { let github function configure (config) { - const log = { debug: jest.fn(), error: console.error } + const log = { debug: jest.fn(), info: jest.fn(), error: console.error } const nop = false const errors = [] return new Autolinks(nop, github, repo, config, log, errors) diff --git a/test/unit/lib/plugins/branches.test.js b/test/unit/lib/plugins/branches.test.js index 49549cc87..9296d9d7d 100644 --- a/test/unit/lib/plugins/branches.test.js +++ b/test/unit/lib/plugins/branches.test.js @@ -8,6 +8,7 @@ describe('Branches', () => { const log = jest.fn() log.debug = jest.fn() log.error = jest.fn() + log.info = jest.fn() function configure (config) { const nop = false @@ -73,6 +74,19 @@ describe('Branches', () => { }) }) + it('logs the applied branch protection change at info level', () => { + const plugin = configure( + [{ + name: 'master', + protection: { enforce_admins: true } + }] + ) + + return plugin.sync().then(() => { + expect(log.info).toHaveBeenCalledWith(expect.stringContaining('Applying branch protection changes to master branch of bkeepers/test')) + }) + }) + describe('when the "protection" config is empty object', () => { it('removes branch protection', () => { const plugin = configure( @@ -183,7 +197,6 @@ describe('Branches', () => { ) return plugin.sync().then(() => { - expect(github.rest.repos.updateBranchProtection).toHaveBeenCalledWith(expect.objectContaining({ owner: 'bkeepers', repo: 'test', @@ -305,7 +318,6 @@ describe('Branches', () => { ) return plugin.sync().then(() => { - expect(github.rest.repos.updateBranchProtection).toHaveBeenCalledWith(expect.objectContaining({ owner: 'bkeepers', repo: 'test', diff --git a/test/unit/lib/plugins/collaborators.test.js b/test/unit/lib/plugins/collaborators.test.js index f01007953..135876423 100644 --- a/test/unit/lib/plugins/collaborators.test.js +++ b/test/unit/lib/plugins/collaborators.test.js @@ -4,7 +4,7 @@ describe('Collaborators', () => { let github function configure (config) { - const log = { debug: jest.fn(), error: console.error } + const log = { debug: jest.fn(), info: jest.fn(), error: console.error } return new Collaborators(undefined, github, { owner: 'bkeepers', repo: 'test' }, config, log) } diff --git a/test/unit/lib/plugins/custom_properties.test.js b/test/unit/lib/plugins/custom_properties.test.js index a9e878d58..e34a980d8 100644 --- a/test/unit/lib/plugins/custom_properties.test.js +++ b/test/unit/lib/plugins/custom_properties.test.js @@ -23,7 +23,7 @@ describe('CustomProperties', () => { } } - log = { debug: jest.fn(), error: console.error } + log = { debug: jest.fn(), info: jest.fn(), error: console.error } }) describe('Custom Properties plugin', () => { diff --git a/test/unit/lib/plugins/environments.test.js b/test/unit/lib/plugins/environments.test.js index 31fbb1cdf..7eaa22d0a 100644 --- a/test/unit/lib/plugins/environments.test.js +++ b/test/unit/lib/plugins/environments.test.js @@ -10,7 +10,7 @@ describe('Environments Plugin test suite', () => { const PrimaryEnvironmentNamesBeingTested = ['wait-timer_environment', 'wait-timer_2_environment', 'reviewers_environment', 'prevent-self-review_environment', 'deployment-branch-policy_environment', 'deployment-branch-policy-custom_environment', 'deployment-branch-policy-custom_environment_legacy', 'variables_environment', 'deployment-protection-rules_environment', 'new_environment', 'old_environment'] const EnvironmentNamesForTheNewEnvironmentsTest = ['new-wait-timer', 'new-reviewers', 'new-prevent-self-review', 'new-deployment-branch-policy', 'new-deployment-branch-policy-custom', 'new-deployment-branch-policy-custom-legacy', 'new-variables', 'new-deployment-protection-rules'] const AllEnvironmentNamesBeingTested = PrimaryEnvironmentNamesBeingTested.concat(EnvironmentNamesForTheNewEnvironmentsTest) - const log = { debug: jest.fn(), error: console.error } + const log = { debug: jest.fn(), info: jest.fn(), error: console.error } const errors = [] function fillEnvironment (attrs) { @@ -1409,7 +1409,7 @@ describe('nopifyRequest', () => { github = { request: jest.fn(() => Promise.resolve(true)) }; - plugin = new Environments(undefined, github, { owner: org, repo }, [], { debug: jest.fn(), error: console.error }, []); + plugin = new Environments(undefined, github, { owner: org, repo }, [], { debug: jest.fn(), info: jest.fn(), error: console.error }, []); }); it('should make a request when nop is false', async () => { diff --git a/test/unit/lib/plugins/labels.test.js b/test/unit/lib/plugins/labels.test.js index 1d1bd51b1..3f6ebe9d6 100644 --- a/test/unit/lib/plugins/labels.test.js +++ b/test/unit/lib/plugins/labels.test.js @@ -28,7 +28,7 @@ describe('Labels', () => { } } } - log = { debug: jest.fn(), error: console.error } + log = { debug: jest.fn(), info: jest.fn(), error: console.error } }) describe('sync', () => { @@ -50,6 +50,8 @@ describe('Labels', () => { ]) return plugin.sync().then(() => { + expect(log.info).toHaveBeenCalledWith(expect.stringContaining('Applying Labels changes to bkeepers/test')) + expect(github.rest.issues.deleteLabel).toHaveBeenCalledWith({ owner: 'bkeepers', repo: 'test', diff --git a/test/unit/lib/plugins/repository.test.js b/test/unit/lib/plugins/repository.test.js index 75b1199fc..057b4fa58 100644 --- a/test/unit/lib/plugins/repository.test.js +++ b/test/unit/lib/plugins/repository.test.js @@ -17,6 +17,7 @@ describe('Repository', () => { const log = jest.fn() log.debug = jest.fn() log.error = jest.fn() + log.info = jest.fn() function configure (config) { const nop = false @@ -43,6 +44,7 @@ describe('Repository', () => { description: 'Hello World!', mediaType: { previews: ['nebula-preview'] } }) + expect(log.info).toHaveBeenCalledWith(expect.stringContaining('Applying repository settings changes to bkeepers/test')) }) }) @@ -60,7 +62,7 @@ describe('Repository', () => { }) }) - it.only('syncs topics', () => { + it('syncs topics', () => { const plugin = configure({ topics: ['foo', 'bar'] }) @@ -74,6 +76,7 @@ describe('Repository', () => { previews: ['mercy'] } }) + expect(log.info).toHaveBeenCalledWith(expect.stringContaining('Applying topic changes to bkeepers/test')) }) }) }) diff --git a/test/unit/lib/plugins/rulesets.test.js b/test/unit/lib/plugins/rulesets.test.js index 678e97779..bf240fbbf 100644 --- a/test/unit/lib/plugins/rulesets.test.js +++ b/test/unit/lib/plugins/rulesets.test.js @@ -86,6 +86,7 @@ describe('Rulesets', () => { let github const log = jest.fn() log.debug = jest.fn() + log.info = jest.fn() log.error = jest.fn() function configure (config, scope='repo') { diff --git a/test/unit/lib/plugins/teams.test.js b/test/unit/lib/plugins/teams.test.js index de16965a6..36d502c5b 100644 --- a/test/unit/lib/plugins/teams.test.js +++ b/test/unit/lib/plugins/teams.test.js @@ -15,7 +15,7 @@ describe('Teams', () => { const org = 'bkeepers' function configure (config) { - const log = { debug: jest.fn(), error: console.error } + const log = { debug: jest.fn(), info: jest.fn(), error: console.error } const errors = [] return new Teams(undefined, github, { owner: 'bkeepers', repo: 'test' }, config, log, errors) } diff --git a/test/unit/lib/plugins/variables.test.js b/test/unit/lib/plugins/variables.test.js index 72ae6293f..9c3baa6f7 100644 --- a/test/unit/lib/plugins/variables.test.js +++ b/test/unit/lib/plugins/variables.test.js @@ -8,7 +8,7 @@ describe('Variables', () => { const repo = 'test' function configure (nop = false, entries = [{ name: 'test', value: 'test' }]) { - const log = { debug: jest.fn(), error: console.error } + const log = { debug: jest.fn(), info: jest.fn(), error: console.error } const errors = [] return new Variables(nop, github, { owner: org, repo }, entries, log, errors) }