-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
reuse.ts
130 lines (119 loc) · 5 KB
/
reuse.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { getGlobalConfig } from '../../config/global';
import { logger } from '../../logger';
import { platform } from '../../platform';
import { branchExists, isBranchModified, isBranchStale } from '../../util/git';
import type { BranchConfig } from '../types';
type ParentBranch = {
reuseExistingBranch: boolean;
isModified?: boolean;
};
export async function shouldReuseExistingBranch(
config: BranchConfig
): Promise<ParentBranch> {
const { branchName } = config;
// Check if branch exists
if (!branchExists(branchName)) {
logger.debug(`Branch needs creating`);
return { reuseExistingBranch: false };
}
logger.debug(`Branch already exists`);
// Check for existing PR
const pr = await platform.getBranchPr(branchName);
if (pr) {
if (pr.title?.startsWith('rebase!')) {
logger.debug(`Manual rebase requested via PR title for #${pr.number}`);
return { reuseExistingBranch: false };
}
if (pr.body?.includes(`- [x] <!-- rebase-check -->`)) {
logger.debug(`Manual rebase requested via PR checkbox for #${pr.number}`);
return { reuseExistingBranch: false };
}
if (pr.labels?.includes(config.rebaseLabel)) {
logger.debug(`Manual rebase requested via PR labels for #${pr.number}`);
// istanbul ignore if
if (getGlobalConfig().dryRun) {
logger.info(
`DRY-RUN: Would delete label ${config.rebaseLabel} from #${pr.number}`
);
} else {
await platform.deleteLabel(pr.number, config.rebaseLabel);
}
return { reuseExistingBranch: false };
}
}
if (
config.rebaseWhen === 'behind-base-branch' ||
(config.rebaseWhen === 'auto' &&
(config.automerge || (await platform.getRepoForceRebase())))
) {
if (await isBranchStale(branchName)) {
logger.debug(`Branch is stale and needs rebasing`);
// We can rebase the branch only if no PR or PR can be rebased
if (await isBranchModified(branchName)) {
// TODO: Warn here so that it appears in PR body (#9720)
logger.debug('Cannot rebase branch as it has been modified');
return { reuseExistingBranch: true, isModified: true };
}
return { reuseExistingBranch: false };
}
logger.debug('Branch is up-to-date');
} else {
logger.debug(
`Skipping stale branch check due to rebaseWhen=${config.rebaseWhen}`
);
}
// Now check if PR is unmergeable. If so then we also rebase
if (pr?.isConflicted) {
logger.debug('PR is conflicted');
if ((await isBranchModified(branchName)) === false) {
logger.debug(`Branch is not mergeable and needs rebasing`);
if (config.rebaseWhen === 'never') {
logger.debug('Rebasing disabled by config');
return { reuseExistingBranch: true, isModified: false };
}
// Setting reuseExistingBranch back to undefined means that we'll use the default branch
return { reuseExistingBranch: false };
}
// Don't do anything different, but warn
// TODO: Add warning to PR (#9720)
logger.debug(`Branch is not mergeable but can't be rebased`);
}
logger.debug(`Branch does not need rebasing`);
// Branches can get in an inconsistent state if "update-lockfile" is used at the same time as other strategies
// On the first execution, everything is executed, but if on a second execution the package.json modification is
// skipped but the lockfile update is executed, the lockfile will have a different result than if it was executed
// along with the changes to the package.json. Thus ending up with an incomplete branch update
// This is why we are skipping branch reuse in this case (#10050)
const groupedByPackageFile = {};
for (const upgrade of config.upgrades) {
groupedByPackageFile[upgrade.packageFile] =
groupedByPackageFile[upgrade.packageFile] || new Set();
groupedByPackageFile[upgrade.packageFile].add(upgrade.rangeStrategy);
if (
groupedByPackageFile[upgrade.packageFile].size > 1 &&
groupedByPackageFile[upgrade.packageFile].has('update-lockfile')
) {
logger.debug(
`Detected multiple rangeStrategies along with update-lockfile`
);
return {
reuseExistingBranch: false,
isModified: false,
};
}
}
// Branches can get in an inconsistent state if postUpdateOptions is used.
// package.json updates are run conditionally, but postUpdateOptions are run everytime
// On the first execution, everything is executed, but if on a second execution the package.json modification is
// skipped but the postUpdateOptions is executed, the lockfile will have a different result than if it was executed
// along with the changes to the package.json. Thus ending up with an incomplete branch update
// This is why we are skipping branch reuse when postUpdateOptions is used (#10050)
if ((config.postUpdateOptions as string[])?.length > 0) {
logger.debug(`Branch is using postUpdateOptions`);
return {
reuseExistingBranch: false,
isModified: false,
};
}
return { reuseExistingBranch: true, isModified: false };
}