Skip to content

Commit

Permalink
feat: onboarding branch cache (#21768)
Browse files Browse the repository at this point in the history
Co-authored-by: Rhys Arkins <rhys@arkins.net>
  • Loading branch information
RahulGautamSingh and rarkins committed Apr 27, 2023
1 parent a6dfa70 commit fc0fb19
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 3 deletions.
7 changes: 7 additions & 0 deletions lib/util/cache/repository/types.ts
Expand Up @@ -26,6 +26,12 @@ export interface BranchUpgradeCache {
sourceUrl?: string;
}

export interface OnboardingBranchCache {
onboardingBranch: string;
defaultBranchSha: string;
onboardingBranchSha: string;
}

export interface PrCache {
fingerprint: string;
/**
Expand Down Expand Up @@ -100,6 +106,7 @@ export interface RepoCacheData {
github?: Record<string, unknown>;
};
prComments?: Record<number, Record<string, string>>;
onboardingBranchCache?: OnboardingBranchCache;
}

export interface RepoCache {
Expand Down
66 changes: 66 additions & 0 deletions lib/workers/repository/onboarding/branch/check.spec.ts
@@ -0,0 +1,66 @@
import {
RenovateConfig,
git,
mocked,
partial,
platform,
} from '../../../../../test/util';
import { REPOSITORY_CLOSED_ONBOARDING } from '../../../../constants/error-messages';
import { logger } from '../../../../logger';
import type { Pr } from '../../../../modules/platform/types';
import * as _cache from '../../../../util/cache/repository';
import { isOnboarded } from './check';

jest.mock('../../../../util/cache/repository');
jest.mock('../../../../util/git');

const cache = mocked(_cache);

describe('workers/repository/onboarding/branch/check', () => {
const config = partial<RenovateConfig>({
requireConfig: 'required',
suppressNotifications: [],
});

it('skips normal onboarding check if onboardingCache is valid', async () => {
cache.getCache.mockReturnValueOnce({
onboardingBranchCache: {
onboardingBranch: 'configure/renovate',
defaultBranchSha: 'default-sha',
onboardingBranchSha: 'onboarding-sha',
},
});
git.getBranchCommit
.mockReturnValueOnce('default-sha')
.mockReturnValueOnce('onboarding-sha');
const res = await isOnboarded(config);
expect(res).toBeFalse();
expect(logger.debug).toHaveBeenCalledWith(
'Onboarding cache is valid. Repo is not onboarded'
);
});

it('continues with normal logic if onboardingCache is invalid', async () => {
cache.getCache.mockReturnValueOnce({
onboardingBranchCache: {
onboardingBranch: 'configure/renovate',
defaultBranchSha: 'default-sha',
onboardingBranchSha: 'onboarding-sha',
},
});
git.getFileList.mockResolvedValue([]);
await isOnboarded(config);
expect(logger.debug).not.toHaveBeenCalledWith(
'Onboarding cache is valid. Repo is not onboarded'
);
});

it('continues with normal logic if closedPr exists', async () => {
cache.getCache.mockReturnValue({});
platform.findPr.mockResolvedValue(partial<Pr>());
git.getFileList.mockResolvedValue([]);
await expect(isOnboarded(config)).rejects.toThrow(
REPOSITORY_CLOSED_ONBOARDING
);
});
});
20 changes: 18 additions & 2 deletions lib/workers/repository/onboarding/branch/check.ts
Expand Up @@ -9,7 +9,7 @@ import { Pr, platform } from '../../../../modules/platform';
import { ensureComment } from '../../../../modules/platform/comment';
import { getCache } from '../../../../util/cache/repository';
import { readLocalFile } from '../../../../util/fs';
import { getFileList } from '../../../../util/git';
import { getBranchCommit, getFileList } from '../../../../util/git';

async function findFile(fileName: string): Promise<boolean> {
logger.debug(`findFile(${fileName})`);
Expand Down Expand Up @@ -61,7 +61,24 @@ export async function isOnboarded(config: RenovateConfig): Promise<boolean> {
logger.debug('Config file will be ignored');
return true;
}

const pr = await closedPrExists(config);
const cache = getCache();
const onboardingBranchCache = cache?.onboardingBranchCache;
// if onboarding cache is present and base branch has not been updated branch is not onboarded
// if closed pr exists then presence of onboarding cache doesn't matter as we need to skip onboarding
if (
!pr &&
onboardingBranchCache &&
onboardingBranchCache.defaultBranchSha ===
getBranchCommit(config.defaultBranch!) &&
onboardingBranchCache.onboardingBranchSha ===
getBranchCommit(config.onboardingBranch!)
) {
logger.debug('Onboarding cache is valid. Repo is not onboarded');
return false;
}

if (cache.configFileName) {
logger.debug('Checking cached config file name');
try {
Expand Down Expand Up @@ -104,7 +121,6 @@ export async function isOnboarded(config: RenovateConfig): Promise<boolean> {
throw new Error(REPOSITORY_NO_CONFIG);
}

const pr = await closedPrExists(config);
if (!pr) {
logger.debug('Found no closed onboarding PR');
return false;
Expand Down
14 changes: 14 additions & 0 deletions lib/workers/repository/onboarding/branch/index.spec.ts
Expand Up @@ -21,6 +21,7 @@ import * as _cache from '../../../../util/cache/repository';
import type { FileAddition } from '../../../../util/git/types';
import { OnboardingState } from '../common';
import * as _config from './config';
import * as _onboardingCache from './onboarding-branch-cache';
import * as _rebase from './rebase';
import { checkOnboardingBranch } from '.';

Expand All @@ -32,12 +33,21 @@ jest.mock('../../../../util/cache/repository');
jest.mock('../../../../util/fs');
jest.mock('../../../../util/git');
jest.mock('./config');
jest.mock('./onboarding-branch-cache');

const cache = mocked(_cache);
const onboardingCache = mocked(_onboardingCache);

describe('workers/repository/onboarding/branch/index', () => {
describe('checkOnboardingBranch', () => {
let config: RenovateConfig;
const dummyCache = {
onboardingBranchCache: {
onboardingBranch: 'configure/renovate',
defaultBranchSha: 'default-sha',
onboardingBranchSha: 'onboarding-sha',
},
};

beforeEach(() => {
memCache.init();
Expand Down Expand Up @@ -168,9 +178,11 @@ describe('workers/repository/onboarding/branch/index', () => {
});

it('detects repo is onboarded via file', async () => {
cache.getCache.mockReturnValue(dummyCache);
git.getFileList.mockResolvedValueOnce(['renovate.json']);
const res = await checkOnboardingBranch(config);
expect(res.repoIsOnboarded).toBeTrue();
expect(onboardingCache.deleteOnboardingCache).toHaveBeenCalledTimes(1); // removes onboarding cache when repo is onboarded
});

it('handles removed cached file name', async () => {
Expand Down Expand Up @@ -251,6 +263,7 @@ describe('workers/repository/onboarding/branch/index', () => {
});

it('updates onboarding branch', async () => {
cache.getCache.mockReturnValue(dummyCache);
git.getFileList.mockResolvedValue(['package.json']);
platform.findPr.mockResolvedValue(null);
platform.getBranchPr.mockResolvedValueOnce(mock<Pr>());
Expand All @@ -259,6 +272,7 @@ describe('workers/repository/onboarding/branch/index', () => {
expect(res.repoIsOnboarded).toBeFalse();
expect(res.branchList).toEqual(['renovate/configure']);
expect(git.checkoutBranch).toHaveBeenCalledTimes(1);
expect(onboardingCache.setOnboardingCache).toHaveBeenCalledTimes(1); // update onboarding cache
expect(scm.commitAndPush).toHaveBeenCalledTimes(0);
});

Expand Down
27 changes: 26 additions & 1 deletion lib/workers/repository/onboarding/branch/index.ts
Expand Up @@ -8,13 +8,21 @@ import {
} from '../../../../constants/error-messages';
import { logger } from '../../../../logger';
import { Pr, platform } from '../../../../modules/platform';
import { checkoutBranch, setGitAuthor } from '../../../../util/git';
import {
checkoutBranch,
getBranchCommit,
setGitAuthor,
} from '../../../../util/git';
import { extractAllDependencies } from '../../extract';
import { mergeRenovateConfig } from '../../init/merge';
import { OnboardingState } from '../common';
import { getOnboardingPr, isOnboarded } from './check';
import { getOnboardingConfig } from './config';
import { createOnboardingBranch } from './create';
import {
deleteOnboardingCache,
setOnboardingCache,
} from './onboarding-branch-cache';
import { rebaseOnboardingBranch } from './rebase';

export async function checkOnboardingBranch(
Expand All @@ -26,6 +34,9 @@ export async function checkOnboardingBranch(
const repoIsOnboarded = await isOnboarded(config);
if (repoIsOnboarded) {
logger.debug('Repo is onboarded');

// delete onboarding cache
deleteOnboardingCache();
return { ...config, repoIsOnboarded };
}
if (config.isFork && config.forkProcessing !== 'enabled') {
Expand All @@ -47,6 +58,13 @@ export async function checkOnboardingBranch(
{ branch: config.onboardingBranch, commit, onboarding: true },
'Branch updated'
);

// update onboarding cache
setOnboardingCache(
config.onboardingBranch!,
getBranchCommit(config.defaultBranch!)!,
commit
);
}
// istanbul ignore if
if (platform.refreshPr) {
Expand Down Expand Up @@ -78,6 +96,13 @@ export async function checkOnboardingBranch(
{ branch: onboardingBranch, commit, onboarding: true },
'Branch created'
);

// set onboarding branch cache
setOnboardingCache(
config.onboardingBranch!,
getBranchCommit(config.defaultBranch!)!,
commit
);
}
}
if (!GlobalConfig.get('dryRun')) {
Expand Down
@@ -0,0 +1,61 @@
import { mocked } from '../../../../../test/util';
import * as _cache from '../../../../util/cache/repository';
import type { RepoCacheData } from '../../../../util/cache/repository/types';
import {
deleteOnboardingCache,
setOnboardingCache,
} from './onboarding-branch-cache';

jest.mock('../../../../util/cache/repository');
const cache = mocked(_cache);

describe('workers/repository/onboarding/branch/onboarding-branch-cache', () => {
it('sets new cache', () => {
const dummyCache = {} satisfies RepoCacheData;
cache.getCache.mockReturnValueOnce(dummyCache);
setOnboardingCache('configure/renovate', 'default-sha', 'onboarding-sha');
expect(dummyCache).toEqual({
onboardingBranchCache: {
onboardingBranch: 'configure/renovate',
defaultBranchSha: 'default-sha',
onboardingBranchSha: 'onboarding-sha',
},
});
});

it('updates old cache', () => {
const dummyCache = {
onboardingBranchCache: {
onboardingBranch: 'configure/renovate',
defaultBranchSha: 'default-sha',
onboardingBranchSha: 'onboarding-sha',
},
} satisfies RepoCacheData;
cache.getCache.mockReturnValueOnce(dummyCache);
setOnboardingCache(
'configure/renovate',
'default-sha-1',
'onboarding-sha-1'
);
expect(dummyCache).toEqual({
onboardingBranchCache: {
onboardingBranch: 'configure/renovate',
defaultBranchSha: 'default-sha-1',
onboardingBranchSha: 'onboarding-sha-1',
},
});
});

it('deletes cache', () => {
const dummyCache = {
onboardingBranchCache: {
onboardingBranch: 'configure/renovate',
defaultBranchSha: 'default-sha',
onboardingBranchSha: 'onboarding-sha',
},
} satisfies RepoCacheData;
cache.getCache.mockReturnValueOnce(dummyCache);
deleteOnboardingCache();
expect(dummyCache.onboardingBranchCache).toBeUndefined();
});
});
@@ -0,0 +1,30 @@
import { logger } from '../../../../logger';
import { getCache } from '../../../../util/cache/repository';

export function setOnboardingCache(
onboardingBranch: string,
defaultBranchSha: string,
onboardingBranchSha: string
): void {
const cache = getCache();
const onboardingCache = {
onboardingBranch,
defaultBranchSha,
onboardingBranchSha,
};
if (cache.onboardingBranchCache) {
logger.debug('Update Onboarding Cache');
} else {
logger.debug('Create Onboarding Cache');
}
cache.onboardingBranchCache = onboardingCache;
}

export function deleteOnboardingCache(): void {
const cache = getCache();

if (cache?.onboardingBranchCache) {
logger.debug('Delete Onboarding Cache');
delete cache.onboardingBranchCache;
}
}

0 comments on commit fc0fb19

Please sign in to comment.