Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
30 changes: 26 additions & 4 deletions lib/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,26 @@ ${this.results.reduce((x, y) => {
if (overrideRepoConfig) {
repoConfig = this.mergeDeep.mergeDeep({}, repoConfig, overrideRepoConfig)
}

// Archived repos are already skipped below, but only after
// archivePlugin.getState() has spent a repos.get on each one. When the caller
// knows the repo is archived — eachRepositoryRepos gets `archived` for free
// from GET /installation/repositories — skip before making any request. On a
// large org this is the difference between one wasted call per archived repo
// and none; orgs where most repos are archived spend the bulk of a full
// sync's rate-limit budget here.
//
// An explicit `archived: false` in config is a request to unarchive and must
// still be processed, so the desired state decides. getDesiredArchiveState()
// reads config only and issues no request.
if (repo.archived === true) {
const desiredArchiveState = new Archive(this.nop, this.github, repo, repoConfig, this.log).getDesiredArchiveState()
if (desiredArchiveState !== false) {
this.log.debug(`Skipping archived repo ${repo.repo} without fetching it`)
return
}
}
Comment thread
rafaelleonardocruz marked this conversation as resolved.
Outdated

if (repoConfig) {
try {
this.log.debug(`found a matching repoconfig for this repo ${JSON.stringify(repoConfig)}`)
Expand Down Expand Up @@ -540,18 +560,20 @@ ${this.results.reduce((x, y) => {
log.debug('Fetching repositories')
return github.paginate('GET /installation/repositories').then(repositories => {
return Promise.all(repositories.map(repository => {
const { owner, name } = repository
return this.checkAndProcessRepo(owner.login, name)
// `archived` is already part of the listing payload, so passing it down
// lets updateRepos skip archived repos without spending an API call.
const { owner, name, archived } = repository
return this.checkAndProcessRepo(owner.login, name, archived)
})
)
})
}

async checkAndProcessRepo (owner, name) {
async checkAndProcessRepo (owner, name, archived) {
if (this.isRestricted(name)) {
return null
}
return this.updateRepos({ owner, repo: name })
return this.updateRepos({ owner, repo: name, archived })
}

/**
Expand Down
50 changes: 50 additions & 0 deletions test/unit/lib/settings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,56 @@ repository:
})
})
}) // repoOverrideConfig

describe('updateRepos with a known-archived repo', () => {
let settings

beforeEach(() => {
stubConfig = { repository: { has_wiki: true }, restrictedRepos: { exclude: [] } }
// Built without a suborg on purpose: passing one sets subOrgConfigMap, and
// updateRepos then returns early for any repo outside that suborg.
settings = new Settings(false, stubContext, mockRepo, stubConfig, mockRef)
settings.subOrgConfigs = {}
settings.repoConfigs = {}
// repos.get is what archivePlugin.getState() calls. Asserting on it proves
// whether the archived repo was skipped before any request was made.
settings.github.rest.repos.get = jest.fn().mockResolvedValue({ data: { archived: true } })
settings.github.rest.repos.update = jest.fn().mockResolvedValue({ data: {} })
})

it('Skips without fetching the repo when the caller reports it archived', async () => {
await settings.updateRepos({ owner: 'test', repo: 'archived-repo', archived: true })
expect(settings.github.rest.repos.get).not.toHaveBeenCalled()
})

it('Still processes the repo when config asks to unarchive it', async () => {
settings.config.repository.archived = false
await settings.updateRepos({ owner: 'test', repo: 'archived-repo', archived: true })
expect(settings.github.rest.repos.get).toHaveBeenCalled()
})

it('Still processes the repo when the caller does not report archived state', async () => {
await settings.updateRepos({ owner: 'test', repo: 'some-repo' })
expect(settings.github.rest.repos.get).toHaveBeenCalled()
})

it('Passes the archived flag from the repository listing through to updateRepos', async () => {
settings.github.paginate = jest.fn().mockResolvedValue([
{ name: 'active-repo', archived: false, owner: { login: 'test' } },
{ name: 'archived-repo', archived: true, owner: { login: 'test' } }
])
const seen = []
settings.updateRepos = jest.fn(async (repo) => { seen.push(repo) })

await settings.eachRepositoryRepos(settings.github, settings.log)

expect(seen).toEqual([
{ owner: 'test', repo: 'active-repo', archived: false },
{ owner: 'test', repo: 'archived-repo', archived: true }
])
})
}) // updateRepos with a known-archived repo

describe('loadConfigs', () => {
describe('load suborg configs', () => {
beforeEach(() => {
Expand Down