Skip to content

Commit

Permalink
Also re-generate specs in the same series when building diff (#1261)
Browse files Browse the repository at this point in the history
So far, the build-diff tool only generated specs that effectively changed in
`specs.json`. With this change, it now also re-generate the entries for the
specs that are in the same series, and report possible changes to these
specs in a new `seriesUpdate` property.

The change also switches to active forms for the property names: `add`,
`update` and `delete` (instead of `added`, `updated`, `deleted`), because the
changes to `index.json` are typically not yet done.

This change gets us closer to a mode where we can pre-compute the `index.json`
that a pull request would trigger and run tests on them.

Still missing: forks. If changes add a `forkOf` property, include a rename of a
spec that has a `forkOf` property, or delete a fork, the upstream spec entry
needs to be re-generated as well. The code still does not handle that.
  • Loading branch information
tidoust authored Mar 15, 2024
1 parent 5894649 commit 660669e
Showing 1 changed file with 105 additions and 31 deletions.
136 changes: 105 additions & 31 deletions src/build-diff.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,77 @@
const assert = require("assert");
const path = require("path");
const { execSync } = require("child_process");
const { generateIndex } = require("./build-index");
const computeShortname = require("./compute-shortname.js");

/**
* Spec sort function.
*
* Lists are sorted by ascending URL.
*/
function compareSpecs(a, b) {
return a.url.localeCompare(b.url);
}

/**
* Return the URL of the provided `specs.json` entry.
*
* The argument may be a string or a spec object.
*/
function getUrl(spec) {
return (typeof spec === "string") ? spec.split(' ')[0] : spec.url;
}

/**
* Return true if provided `specs.json` entries have the same URL
*
* The arguments may be strings or spec objects.
*/
function haveSameUrl(s1, s2) {
const url1 = getUrl(s1);
const url2 = getUrl(s2);
return url1 === url2;
}

/**
* Return true if provided `specs.json` entries are distinct specs in the same
* series.
*
* The arguments may be strings or spec objects.
*/
function areDistinctSpecsInSameSeries(s1, s2) {
const spec1 = (typeof s1 === "string") ? { url: getUrl(s1) } : s1;
const spec2 = (typeof s2 === "string") ? { url: getUrl(s2) } : s2;
if (spec1.url === spec2.url) {
return false;
}
try {
const computed1 = computeShortname(spec1.shortname ?? spec1.url, spec1.forkOf);
const computed2 = computeShortname(spec2.shortname ?? spec2.url, spec2.forkOf);
const series1 = spec1?.series?.shortname ?? computed1.series.shortname;
const series2 = spec2?.series?.shortname ?? computed2.series.shortname;
return series1 === series2;
}
catch {
return false;
}
}

/**
* Return true if provided spec entries are identical.
*
* The arguments must be spec objects.
*/
function areIdentical(s1, s2) {
try {
assert.deepStrictEqual(s1, s2);
return true;
}
catch {
return false;
}
}

/**
* Generate the new index file from the given initial list file.
*
Expand Down Expand Up @@ -34,47 +100,55 @@ async function compareIndex(newRef, baseRef, { diffType = "diff", log = console.
log(`Retrieve index.json at "${baseRef}"... done`);

log(`Compute specs.json diff...`);
function hasSameUrl(s1, s2) {
const url1 = (typeof s1 === "string") ? s1 : s1.url;
const url2 = (typeof s2 === "string") ? s2 : s2.url;
return url1 === url2;
}
const diff = {
added: newSpecs.filter(spec => !baseSpecs.find(s => hasSameUrl(s, spec))),
updated: newSpecs.filter(spec =>
!!baseSpecs.find(s => hasSameUrl(s, spec)) &&
add: newSpecs.filter(spec => !baseSpecs.find(s => haveSameUrl(s, spec))),
update: newSpecs.filter(spec =>
!!baseSpecs.find(s => haveSameUrl(s, spec)) &&
!baseSpecs.find(s => JSON.stringify(s) === JSON.stringify(spec))),
deleted: baseSpecs.filter(spec => !newSpecs.find(s => hasSameUrl(s, spec)))
delete: baseSpecs.filter(spec => !newSpecs.find(s => haveSameUrl(s, spec)))
};
if (diff.add.length || diff.update.length || diff.delete.length) {
for (const type of ['add', 'update', 'delete']) {
for (const spec of diff[type]) {
log(`- ${type} ${getUrl(spec)}`);
}
}
}
else {
log(`- no diff found`);
}
log(`Compute specs.json diff... done`);

log(`Build specs that were added...`);
diff.added = (diff.added.length === 0) ? [] :
await generateIndex(diff.added, {
previousIndex: baseIndex,
log: function(...msg) { log(' ', ...msg); } });
log(`Build specs that were added... done`);
log(`Delete specs that were dropped...`);
diff.delete = baseIndex.filter(spec => diff.delete.find(s => haveSameUrl(s, spec)));
let newIndex = baseIndex.filter(spec => !diff.delete.find(s => haveSameUrl(s, spec)));
log(`Delete specs that were dropped... done`);

log(`Build specs that were updated...`);
diff.updated = (diff.updated.length === 0) ? [] :
await generateIndex(diff.updated, {
previousIndex: baseIndex,
log(`Build new/updated entries...`);
const needBuild = []
.concat(diff.add)
.concat(diff.update)
.map(spec => [spec].concat(baseSpecs.filter(s => areDistinctSpecsInSameSeries(s, spec))))
.concat(diff.delete.map(spec => baseSpecs.filter(s => areDistinctSpecsInSameSeries(s, spec))))
.flat();
const built = (needBuild.length === 0) ? [] :
await generateIndex(needBuild, {
previousIndex: newIndex,
log: function(...msg) { log(' ', ...msg); } });
log(`Build specs that were updated... done`);

log(`Retrieve specs that were dropped...`);
diff.deleted = diff.deleted.map(spec => baseIndex.find(s => hasSameUrl(s, spec)));
log(`Retrieve specs that were dropped... done`);
diff.add = diff.add.map(spec => built.find(s => haveSameUrl(s, spec)));
diff.update = diff.update.map(spec => built.find(s => haveSameUrl(s, spec)));
diff.seriesUpdate = built
.filter(spec =>
!diff.add.find(s => haveSameUrl(s, spec)) &&
!diff.update.find(s => haveSameUrl(s, spec)))
.filter(spec => !areIdentical(spec, baseIndex.find(s => s.url === spec.url)));
log(`Build new/updated entries... done`);

if (diffType === "full") {
log(`Create full new index...`);
const newIndex = baseIndex
.map(spec => {
const updated = diff.updated.find(s => hasSameUrl(s, spec));
return updated ?? spec;
})
.filter(spec => !diff.deleted.find(s => hasSameUrl(s, spec)));
diff.added.forEach(spec => newIndex.push(spec));
newIndex = newIndex
.filter(spec => !built.find(s => haveSameUrl(s, spec)))
.concat(built);
newIndex.sort(compareSpecs);
log(`Create full new index... done`);
return newIndex;
Expand Down

0 comments on commit 660669e

Please sign in to comment.