Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. 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
```
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
Expand Down
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
3 changes: 2 additions & 1 deletion lib/env.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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',
CREATE_ERROR_ISSUE: process.env.CREATE_ERROR_ISSUE || 'true',
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
}
83 changes: 83 additions & 0 deletions test/unit/index.syncInstallation.test.js
Original file line number Diff line number Diff line change
@@ -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')
})
})
})