From da020c660e0ded7461ff9d7e02186922f55913fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Daba=C5=A1inskas?= Date: Sat, 11 Jul 2026 13:05:51 +0300 Subject: [PATCH 1/2] feat(config): add CONFIG_REF environment variable for scheduled syncs Allow scheduled and full syncs to read configuration from a specific git ref instead of the default branch. This enables testing configuration changes from a branch before merging. Webhook-triggered syncs continue to use the ref from the triggering event and are not affected by this setting. The CONFIG_REF value is passed through to syncAll() and used when fetching configuration files from the admin repository. --- README.md | 4 ++ index.js | 4 +- lib/env.js | 3 +- test/unit/index.syncInstallation.test.js | 83 ++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 test/unit/index.syncInstallation.test.js diff --git a/README.md b/README.md index 07a626748..f8a755f8f 100644 --- a/README.md +++ b/README.md @@ -537,6 +537,10 @@ You can pass environment variables; the easiest way to do it is via a `.env` fil ``` SETTINGS_FILE_PATH=settings.yml ``` +1. Configure the git ref that scheduled and full syncs read the configs from using `CONFIG_REF` (default is the default branch of the admin repo). Webhook-triggered syncs are not affected. For e.g. + ``` + CONFIG_REF=my-config-branch + ``` 1. Configure the deployment settings file path using `DEPLOYMENT_CONFIG_FILE` (default is `deployment-settings.yml`). For e.g. ``` DEPLOYMENT_CONFIG_FILE=deployment-settings.yml diff --git a/index.js b/index.js index 12aae7c79..2993e1ebe 100644 --- a/index.js +++ b/index.js @@ -242,7 +242,9 @@ module.exports = (robot, { getRouter }, Settings = require('./lib/settings')) => log: robot.log, repo: () => { return { repo: env.ADMIN_REPO, owner: installation.account.login } } } - return syncAllSettings(nop, context) + // CONFIG_REF lets scheduled/CLI syncs read the config from a non-default + // ref of the admin repo (e.g. to test config changes from a branch). + return syncAllSettings(nop, context, context.repo(), env.CONFIG_REF) } return null } diff --git a/lib/env.js b/lib/env.js index 8ed5d927e..565e8e430 100644 --- a/lib/env.js +++ b/lib/env.js @@ -1,6 +1,7 @@ module.exports = { ADMIN_REPO: process.env.ADMIN_REPO || 'admin', CONFIG_PATH: process.env.CONFIG_PATH || '.github', + CONFIG_REF: process.env.CONFIG_REF, SETTINGS_FILE_PATH: process.env.SETTINGS_FILE_PATH || 'settings.yml', DEPLOYMENT_CONFIG_FILE_PATH: process.env.DEPLOYMENT_CONFIG_FILE || 'deployment-settings.yml', CREATE_PR_COMMENT: process.env.CREATE_PR_COMMENT || 'true', @@ -8,5 +9,5 @@ module.exports = { BLOCK_REPO_RENAME_BY_HUMAN: process.env.BLOCK_REPO_RENAME_BY_HUMAN || 'false', FULL_SYNC_NOP: process.env.FULL_SYNC_NOP === 'true', GHE_HOST: process.env.GHE_HOST, - GHE_PROTOCOL: process.env.GHE_PROTOCOL, + GHE_PROTOCOL: process.env.GHE_PROTOCOL } diff --git a/test/unit/index.syncInstallation.test.js b/test/unit/index.syncInstallation.test.js new file mode 100644 index 000000000..ebbe237a7 --- /dev/null +++ b/test/unit/index.syncInstallation.test.js @@ -0,0 +1,83 @@ +/* eslint-disable no-undef */ + +// Tests for the syncInstallation path in index.js, which serves scheduled +// (CRON) and full-sync runs. The app factory takes Settings as an injectable +// parameter, so the sync can be observed without touching the GitHub API. +describe('syncInstallation', () => { + const installation = { id: 123, account: { login: 'test-org' } } + let robot + let settingsMock + + function buildRobot () { + const content = Buffer.from('restrictedRepos: []').toString('base64') + const github = { + paginate: jest.fn().mockResolvedValue([installation]), + rest: { + apps: { + listInstallations: { endpoint: { merge: jest.fn().mockReturnValue({}) } }, + getAuthenticated: jest.fn().mockResolvedValue({ data: { slug: 'safe-settings' } }) + }, + repos: { + getContent: jest.fn().mockResolvedValue({ data: { content } }) + } + } + } + return { + log: Object.assign(jest.fn(), { + debug: jest.fn(), + trace: jest.fn(), + info: jest.fn(), + error: jest.fn() + }), + auth: jest.fn().mockResolvedValue(github), + on: jest.fn(), + github + } + } + + function loadApp () { + let app + jest.isolateModules(() => { + const appFn = require('../../index') + app = appFn(robot, {}, settingsMock) + }) + return app + } + + beforeEach(() => { + robot = buildRobot() + settingsMock = { + syncAll: jest.fn().mockResolvedValue({}), + handleError: jest.fn().mockResolvedValue({}) + } + }) + + afterEach(() => { + delete process.env.CONFIG_REF + }) + + describe('when CONFIG_REF is not set', () => { + it('reads the config from the default branch and passes no ref to syncAll', async () => { + const app = loadApp() + + await app.syncInstallation() + + expect(robot.github.rest.repos.getContent).toHaveBeenCalledWith(expect.objectContaining({ ref: undefined })) + expect(settingsMock.syncAll).toHaveBeenCalledTimes(1) + expect(settingsMock.syncAll.mock.calls[0][4]).toBeUndefined() + }) + }) + + describe('when CONFIG_REF is set', () => { + it('reads the config from that ref and passes it through to syncAll', async () => { + process.env.CONFIG_REF = 'my-config-branch' + const app = loadApp() + + await app.syncInstallation() + + expect(robot.github.rest.repos.getContent).toHaveBeenCalledWith(expect.objectContaining({ ref: 'my-config-branch' })) + expect(settingsMock.syncAll).toHaveBeenCalledTimes(1) + expect(settingsMock.syncAll.mock.calls[0][4]).toBe('my-config-branch') + }) + }) +}) From 7961ef66260d575431e652b040359a09eb066052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Daba=C5=A1inskas?= Date: Sat, 11 Jul 2026 13:21:55 +0300 Subject: [PATCH 2/2] docs(config): clarify CONFIG_REF environment variable description Improve readability and clarity of the CONFIG_REF documentation by: - Simplifying the sentence structure - Removing redundant "admin repo" reference (already clear from context) - Replacing "For e.g." with the grammatically correct "For example" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8a755f8f..5c3c2ef0e 100644 --- a/README.md +++ b/README.md @@ -537,7 +537,7 @@ You can pass environment variables; the easiest way to do it is via a `.env` fil ``` SETTINGS_FILE_PATH=settings.yml ``` -1. Configure the git ref that scheduled and full syncs read the configs from using `CONFIG_REF` (default is the default branch of the admin repo). Webhook-triggered syncs are not affected. For e.g. +1. Read the configs from a different git ref of the admin repo during scheduled and full syncs with `CONFIG_REF` (default is the default branch). Webhook-triggered syncs are not affected. For example: ``` CONFIG_REF=my-config-branch ```