-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
parent-version.ts
120 lines (119 loc) · 4.21 KB
/
parent-version.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
import {
GetPkgReleasesConfig,
getPkgReleases,
} from '../../../../../datasource';
import { logger } from '../../../../../logger';
import { api as semver } from '../../../../../versioning/npm';
/**
* Finds the first stable version of parentName after parentStartingVersion which either:
* - depends on targetDepName@targetVersion or a range which it satisfies, OR
* - removes the dependency targetDepName altogether, OR
* - depends on any version of targetDepName higher than targetVersion
*/
export async function findFirstParentVersion(
parentName: string,
parentStartingVersion: string,
targetDepName: string,
targetVersion: string
): Promise<string | null> {
// istanbul ignore if
if (!semver.isVersion(parentStartingVersion)) {
logger.debug('parentStartingVersion is not a version - cannot remediate');
return null;
}
logger.debug(
`Finding first version of ${parentName} starting with ${parentStartingVersion} which supports >= ${targetDepName}@${targetVersion}`
);
try {
let lookupConfig: GetPkgReleasesConfig = {
datasource: 'npm',
depName: targetDepName,
};
const targetDep = await getPkgReleases(lookupConfig);
// istanbul ignore if
if (!targetDep) {
logger.warn(
{ targetDepName },
'Could not look up target dependency for remediation'
);
return null;
}
const targetVersions = targetDep.releases
.map((release) => release.version)
.filter(
(version) =>
semver.isVersion(version) &&
semver.isStable(version) &&
(version === targetVersion ||
semver.isGreaterThan(version, targetVersion))
);
lookupConfig = {
datasource: 'npm',
depName: parentName,
};
const parentDep = await getPkgReleases(lookupConfig);
// istanbul ignore if
if (!parentDep) {
logger.info(
{ parentName },
'Could not look up parent dependency for remediation'
);
return null;
}
const parentVersions = parentDep.releases
.map((release) => release.version)
.filter(
(version) =>
semver.isVersion(version) &&
semver.isStable(version) &&
(version === parentStartingVersion ||
semver.isGreaterThan(version, parentStartingVersion))
)
.sort((v1, v2) => semver.sortVersions(v1, v2));
// iterate through parentVersions in sorted order
for (const parentVersion of parentVersions) {
const constraint = parentDep.releases.find(
(release) => release.version === parentVersion
).dependencies?.[targetDepName];
if (!constraint) {
logger.debug(
`${targetDepName} has been removed from ${parentName}@${parentVersion}`
);
return parentVersion;
}
if (semver.matches(targetVersion, constraint)) {
// could be version or range
logger.debug(
`${targetDepName} needs ${parentName}@${parentVersion} which uses constraint "${constraint}" in order to update to ${targetVersion}`
);
return parentVersion;
}
if (semver.isVersion(constraint)) {
if (semver.isGreaterThan(constraint, targetVersion)) {
// it's not the version we were after - the parent skipped to a higher version
logger.debug(
`${targetDepName} needs ${parentName}@${parentVersion} which uses version "${constraint}" in order to update to greater than ${targetVersion}`
);
return parentVersion;
}
} else if (
// check the range against all versions
targetVersions.some((version) => semver.matches(version, constraint))
) {
// the constraint didn't match the version we wanted, but it matches one of the versions higher
logger.debug(
`${targetDepName} needs ${parentName}@${parentVersion} which uses constraint "${constraint}" in order to update to greater than ${targetVersion}`
);
return parentVersion;
}
}
} catch (err) /* istanbul ignore next */ {
logger.warn(
{ parentName, parentStartingVersion, targetDepName, targetVersion, err },
'findFirstParentVersion error'
);
return null;
}
logger.debug(`Could not find a matching version`);
return null;
}