Skip to content

Commit bb2ea25

Browse files
reuzelantongolub
authored andcommitted
fix: misalignment between manifest and notes
1 parent ee2dee6 commit bb2ea25

File tree

4 files changed

+274
-157
lines changed

4 files changed

+274
-157
lines changed

lib/createInlinePluginCreator.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ function createInlinePluginCreator(packages, multiContext, synchronizer, flags)
183183
if (subs) notes.push(subs.replace(/^(#+) (\[?\d+\.\d+\.\d+\]?)/, `$1 ${name} $2`));
184184

185185
// If it has upgrades add an upgrades section.
186-
const upgrades = pkg.localDeps.filter((d) => d._nextRelease);
187-
if (upgrades.length) {
186+
const upgrades = pkg._depsChanged.filter((d) => d._nextRelease && d._nextRelease.version);
187+
if (upgrades && upgrades.length > 0) {
188188
notes.push(`### Dependencies`);
189189
const bullets = upgrades.map((d) => `* **${d.name}:** upgraded to ${d._nextRelease.version}`);
190190
notes.push(bullets.join("\n"));

lib/updateDeps.js

Lines changed: 126 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -130,93 +130,95 @@ const _nextPreHighestVersion = (latestTag, lastVersion, pkgPreRelease) => {
130130
* @param {Package} pkg Package object.
131131
* @param {string|undefined} bumpStrategy Dependency resolution strategy: override, satisfy, inherit.
132132
* @param {string|undefined} releaseStrategy Release type triggered by deps updating: patch, minor, major, inherit.
133-
* @param {Package[]} ignore=[] Packages to ignore (to prevent infinite loops).
133+
* @param {Package[]} ignore=[] Packages to ignore (to prevent infinite loops when traversing graphs of dependencies).
134134
* @returns {string|undefined} Resolved release type.
135135
* @internal
136136
*/
137137
const resolveReleaseType = (pkg, bumpStrategy = "override", releaseStrategy = "patch", ignore = []) => {
138-
// NOTE This fn also updates pkg deps, so it must be invoked anyway.
139-
const dependentReleaseType = getDependentRelease(pkg, bumpStrategy, releaseStrategy, ignore);
140-
141-
// Release type found by commitAnalyzer.
142-
if (pkg._nextType) {
143-
return pkg._nextType;
144-
}
145-
146-
if (!dependentReleaseType) {
147-
return undefined;
138+
//make sure any dependency changes are resolved before returning the release type
139+
if (!pkg._depsResolved) {
140+
//create a list of dependencies that require change
141+
pkg._depsChanged = pkg.localDeps
142+
.filter((d) => !ignore.includes(d))
143+
.filter((d) => needsDependencyUpdate(pkg, d, bumpStrategy, releaseStrategy, [pkg, ...ignore]));
144+
145+
//get the (preliminary) release type of the package based on release strategy (and analyzed changed dependencies)
146+
pkg._nextType = getDependentRelease(pkg, releaseStrategy);
147+
148+
//indicate that all deps are resolved (fixates the next type and depsChanged)
149+
pkg._depsResolved = ignore.length === 0;
148150
}
149151

150-
// Define release type for dependent package if any of its deps changes.
151-
// `patch`, `minor`, `major` — strictly declare the release type that occurs when any dependency is updated.
152-
// `inherit` — applies the "highest" release of updated deps to the package.
153-
// For example, if any dep has a breaking change, `major` release will be applied to the all dependants up the chain.
154-
155-
pkg._nextType = releaseStrategy === "inherit" ? dependentReleaseType : releaseStrategy;
156-
157152
return pkg._nextType;
158153
};
159154

160155
/**
161-
* Get dependent release type by recursive scanning and updating its deps.
162-
*
163-
* @param {Package} pkg The package with local deps to check.
164-
* @param {string} bumpStrategy Dependency resolution strategy: override, satisfy, inherit.
165-
* @param {string} releaseStrategy Release type triggered by deps updating: patch, minor, major, inherit.
156+
* Indicates if the manifest file requires a change for the given dependency
157+
* @param {Package} pkg Package object.
158+
* @param {string|undefined} bumpStrategy Dependency resolution strategy: override, satisfy, inherit.
159+
* @param {string|undefined} releaseStrategy Release type triggered by deps updating: patch, minor, major, inherit.
166160
* @param {Package[]} ignore Packages to ignore (to prevent infinite loops).
167-
* @returns {string|undefined} Returns the highest release type if found, undefined otherwise
168-
* @internal
169161
*/
170-
const getDependentRelease = (pkg, bumpStrategy, releaseStrategy, ignore) => {
171-
const severityOrder = ["patch", "minor", "major"];
172-
const { localDeps, manifest = {} } = pkg;
173-
const { dependencies = {}, devDependencies = {}, peerDependencies = {}, optionalDependencies = {} } = manifest;
162+
163+
const needsDependencyUpdate = (pkg, dependency, bumpStrategy, releaseStrategy, ignore) => {
164+
//get last release of dependency
165+
const depLastVersion = dependency._lastRelease && dependency._lastRelease.version;
166+
167+
// 3. check if dependency was released before (if not, this is assumed to be a new package + dependency)
168+
const wasReleased = depLastVersion !== undefined;
169+
if (!wasReleased) return true; //new packages always require a package re-release
170+
171+
//get nextType of the dependency (recursion occurs here!)
172+
// Has changed if...
173+
// 1. Any local dep package itself triggered changed
174+
// 2. Any local dep package has local deps that triggered change.
175+
const depNextType = resolveReleaseType(dependency, bumpStrategy, releaseStrategy, ignore);
176+
177+
//get estimated next version of dependency (which is lastVersion if no change expected)
178+
const depNextVersion = depNextType
179+
? dependency._preRelease
180+
? getNextPreVersion(dependency)
181+
: getNextVersion(dependency)
182+
: depLastVersion;
183+
184+
//get list of manifest dependencies
185+
const { dependencies = {}, devDependencies = {}, peerDependencies = {}, optionalDependencies = {} } = pkg.manifest;
174186
const scopes = [dependencies, devDependencies, peerDependencies, optionalDependencies];
175-
const bumpDependency = (scope, name, nextVersion) => {
176-
const currentVersion = scope[name];
177-
if (!nextVersion || !currentVersion) {
178-
return false;
179-
}
180187

181-
const resolvedVersion = resolveNextVersion(currentVersion, nextVersion, bumpStrategy);
182-
if (currentVersion !== resolvedVersion) {
183-
scope[name] = resolvedVersion;
184-
return true;
185-
}
188+
// 4. Check if the manifest dependency rules warrants an update (in any of the dependency scopes)
189+
const requireUpdate = scopes.some((scope) =>
190+
manifestUpdateNecessary(scope, dependency.name, depNextVersion, bumpStrategy)
191+
);
192+
193+
//return if update is required
194+
return requireUpdate;
195+
};
186196

197+
/**
198+
* Checks if an update of a package is necessary in the given list of dependencies, based on semantic versioning rules
199+
* and the bumpStrategy.
200+
* @param {Object} scope object containing dependencies. Dependency names are the keys, dependency rule the values.
201+
* @param {string} name name of the dependency to update
202+
* @param {string} nextVersion the new version of the dependency
203+
* @param {string} bumpStrategy Dependency resolution strategy: override, satisfy, inherit.
204+
* @returns {boolean} true if a the dependency exists in the scope and requires a version update
205+
*/
206+
const manifestUpdateNecessary = (scope, name, nextVersion, bumpStrategy) => {
207+
const currentVersion = scope[name];
208+
if (!nextVersion || !currentVersion) {
187209
return false;
188-
};
189-
190-
// prettier-ignore
191-
return localDeps
192-
.filter((p) => !ignore.includes(p))
193-
.reduce((releaseType, p) => {
194-
const name = p.name;
195-
196-
// Has changed if...
197-
// 1. Any local dep package itself has changed
198-
// 2. Any local dep package has local deps that have changed.
199-
const nextType = resolveReleaseType(p, bumpStrategy, releaseStrategy,[...ignore, ...localDeps]);
200-
201-
// Set the nextVersion fallback to the last local dependency package last version
202-
let nextVersion = p._lastRelease && p._lastRelease.version;
203-
204-
// Update the nextVersion only if there is a next type to be bumped
205-
if (nextType) nextVersion = p._preRelease ? getNextPreVersion(p) : getNextVersion(p);
206-
const lastVersion = pkg._lastRelease && pkg._lastRelease.version;
207-
208-
// 3. And this change should correspond to manifest updating rule.
209-
const requireRelease = scopes
210-
.reduce((res, scope) => bumpDependency(scope, name, nextVersion) || res, !lastVersion)
211-
212-
return requireRelease && (severityOrder.indexOf(nextType) > severityOrder.indexOf(releaseType))
213-
? nextType
214-
: releaseType;
215-
}, undefined);
210+
}
211+
212+
//calculate the next version of the manifest dependency, given the current version
213+
//this checks the semantic versioning rules. Resolved version will remain
214+
//current version if the currentVersion "encompasses" the next version
215+
const resolvedVersion = resolveNextVersion(currentVersion, nextVersion, bumpStrategy);
216+
217+
return currentVersion !== resolvedVersion;
216218
};
217219

218220
/**
219-
* Resolve next version of dependency.
221+
* Resolve next version of dependency based on bumpStrategy
220222
*
221223
* @param {string} currentVersion Current dep version
222224
* @param {string} nextVersion Next release type: patch, minor, major
@@ -254,6 +256,46 @@ const resolveNextVersion = (currentVersion, nextVersion, bumpStrategy = "overrid
254256
return nextVersion;
255257
};
256258

259+
/**
260+
* Get dependent release type by analyzing the current nextType and changed dependencies
261+
* @param {Package} pkg The package to determine next type of release of
262+
* @param {string} releaseStrategy Release type triggered by deps updating: patch, minor, major, inherit.
263+
* @returns {string|undefined} Returns the highest release type if found, undefined otherwise
264+
* @internal
265+
*/
266+
const getDependentRelease = (pkg, releaseStrategy) => {
267+
const severityOrder = ["patch", "minor", "major"];
268+
269+
// Define release type for dependent package if any of its deps changes.
270+
// `patch`, `minor`, `major` — strictly declare the release type that occurs when any dependency is updated.
271+
// `inherit` — applies the "highest" release of updated deps to the package.
272+
// For example, if any dep has a breaking change, `major` release will be applied to the all dependants up the chain.
273+
274+
//return type set by commit analyzer if no deps changed
275+
if (
276+
!pkg._lastRelease || //new package
277+
!pkg._depsChanged || //no deps analyzed
278+
pkg._depsChanged.length === 0 || //no deps available
279+
pkg._depsChanged.every((dep) => !dep._nextType && dep._lastRelease) //no new deps or deps upgraded
280+
)
281+
return pkg._nextType;
282+
283+
if (releaseStrategy === "inherit") {
284+
//find highest release type if strategy is inherit, starting of type set by commit analyzer
285+
return pkg._depsChanged.reduce((maxReleaseType, dependency) => {
286+
return severityOrder.indexOf(dependency._nextType) > severityOrder.indexOf(maxReleaseType)
287+
? dependency._nextType
288+
: maxReleaseType;
289+
}, pkg._nextType);
290+
}
291+
292+
//return highest of commit analyzer found change and releaseStrategy
293+
//releaseStrategy of major could override local update of minor
294+
return severityOrder.indexOf(pkg._nextType) > severityOrder.indexOf(releaseStrategy)
295+
? pkg._nextType
296+
: releaseStrategy;
297+
};
298+
257299
/**
258300
* Update pkg deps.
259301
*
@@ -265,14 +307,26 @@ const updateManifestDeps = (pkg) => {
265307
const { manifest, path } = pkg;
266308
const { indent, trailingWhitespace } = recognizeFormat(manifest.__contents__);
267309

268-
// Loop through localDeps to verify release consistency.
269-
pkg.localDeps.forEach((d) => {
310+
// Loop through changed deps to verify release consistency.
311+
pkg._depsChanged.forEach((dependency) => {
270312
// Get version of dependency.
271-
const release = d._nextRelease || d._lastRelease;
313+
const release = dependency._nextRelease || dependency._lastRelease;
272314

273315
// Cannot establish version.
274316
if (!release || !release.version)
275-
throw Error(`Cannot release because dependency ${d.name} has not been released`);
317+
throw Error(`Cannot release because dependency ${dependency.name} has not been released`);
318+
319+
//update changed dependencies
320+
const {
321+
dependencies = {},
322+
devDependencies = {},
323+
peerDependencies = {},
324+
optionalDependencies = {},
325+
} = pkg.manifest;
326+
const scopes = [dependencies, devDependencies, peerDependencies, optionalDependencies];
327+
scopes.forEach((scope) => {
328+
if (scope[dependency.name]) scope[dependency.name] = release.version;
329+
});
276330
});
277331

278332
if (!auditManifestChanges(manifest, path)) {

0 commit comments

Comments
 (0)