Skip to content

Commit 8b03201

Browse files
committed
add more version verification functionality to tool.ts script
1 parent ecd1eec commit 8b03201

1 file changed

Lines changed: 224 additions & 6 deletions

File tree

src/tool.ts

Lines changed: 224 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env -S deno run -A
22
import { encodeHex } from "jsr:@std/encoding/hex";
33
import { crypto } from "jsr:@std/crypto";
4+
import { auth, GridRange, Sheets, Spreadsheet } from 'https://googleapis.deno.dev/v1/sheets:v4.ts';
45

56
//import { MainManifest, VersionData, OmniarchiveMainManifest, OmniVersionManifest } from './types.d.ts';
67

@@ -213,8 +214,6 @@ const mirrorMap: Map<MirrorId, LocalOriginalId> = new Map([
213214
["13w16b", "13w16b-2151"],
214215
["13w23b", "13w23b-0101"],
215216
["1.6", "1.6-1517"],
216-
// TODO look at this
217-
["1.6.2", "1.6.2-091847"],
218217
["13w38c", "13w38c-1516"],
219218
["1.7", "1.7-1602"],
220219
["14w04b", "14w04b-1554"],
@@ -241,6 +240,7 @@ const mergedMirrorMap: Map<MirrorId, [client: LocalOriginalId, server: LocalOrig
241240
["b1.9-pre4", ["b1.9-pre4-1435", "b1.9-pre4-1441"]],
242241
["13w16a", ["13w16a-192037", "13w16a-191517"]],
243242
["13w22a", ["13w22a-1434", "13w22a-1608"]],
243+
["1.6.2", ["1.6.2-091847", "1.6.2-080933"]],
244244
["1.6.3", ["1.6.3-171231", "1.6.3-171031"]],
245245
["13w36a", ["13w36a-1446", "13w36a-1330"]],
246246
["13w36b", ["13w36b-1307", "13w36b-1233"]],
@@ -261,12 +261,20 @@ const weirdMergeMap: Map<LocalId, [client: ExternalOriginalId, server: IndexOrig
261261
["14w04a-1526", ["14w04a", "14w04a-1526"]],
262262
["1.7.5-02260922", ["1.7.5", "1.7.5-02260922"]]
263263
]);
264+
type Reason = string;
265+
const exemptOtherVersions: Map<VersionId, Reason> = new Map([
266+
["b1.1-1245", "The Omniarchive manifest version offers the server for b1.1_01; if the server downloads were switched, every download would match"],
267+
["b1.1_01", "The Omniarchive manifest version offers the server for b1.1-1245; if the server downloads were switched, every download would match"],
268+
["1.0.0", "The Omniarchive manifest has the server for 1.0.1; otherwise, every other download matches"],
269+
["12w18a", "The local manifest version has an extra \"client_zip\" download; otherwise, every other download matches"],
270+
["12w19a", "The local manifest version has an extra \"client_zip\" download; otherwise, every other download matches"],
271+
["1.7.7-091529", "The local manifest version includes the servers associated with this client; the client download otherwise matches"],
272+
["1.12-pre3-1316", "The Omniarchive manifest version offers downloads for 1.12-pre3-1409; none of the downloads match, but if the Omniarchive manifest gets fixed, the downloads would match"]
273+
]);
264274

265275
const shouldSkip = (key: string): boolean => !(standaloneSevers.includes(key) || orphanServers.includes(key) ||
266276
mirrorlessRenameMap.has(key) || mirrorMap.has(key) || reverseMirrorMap.has(key) || mergedMirrorMap.has(key) || weirdMergeMap.has(key));
267277

268-
export { standaloneSevers, renamedStandaloneServers, orphanServers, renamedOrphanServers, mirrorlessRenameMap, mirrorMap, reverseMirrorMap, mergedMirrorMap, weirdMergeMap }
269-
270278
function compareLocalWithOmniarchive(localVersionsMap: Map<string, VersionManifest>, remoteVersionsMap: Map<string, OmniVersionManifest>) {
271279
const missingExternal = [];
272280

@@ -306,6 +314,209 @@ function compareLocalWithOmniarchive(localVersionsMap: Map<string, VersionManife
306314
console.log(`\nLocal manifest is missing ${missingLocal.length} versions`);
307315
}
308316

317+
function isWithinRangePredicate(rowIndex: number) {
318+
return (range: GridRange) => rowIndex > range.startRowIndex! && rowIndex < range.endRowIndex!
319+
}
320+
321+
async function readSpreadsheetVersions(spreadsheetPromise: Promise<Spreadsheet>): Promise<[clients: Set<string>, servers: Set<string>]> {
322+
const [clients, servers] = [new Set<string>(), new Set<string>()];
323+
324+
const sheets = (await spreadsheetPromise).sheets!.filter((sheet) => sheet.properties!.sheetId !== 1427179805 && sheet.properties!.sheetId !== 1915497658);
325+
sheets.forEach((sheet) => {
326+
const sheetId = sheet.properties!.sheetId;
327+
const merges = sheet.merges!.filter((range) => range.startColumnIndex === 1);
328+
sheet.data![0].rowData!.forEach((row, rowIndex, rowData) => {
329+
const potentialVersion = row.values![1].formattedValue;
330+
if (potentialVersion !== 'ID') {
331+
if ((sheetId === 872531987 || sheetId === 804883379) && potentialVersion) clients.add(potentialVersion);
332+
else if ((sheetId === 2126693093 || sheetId === 59329510) && potentialVersion) servers.add(potentialVersion);
333+
else if (sheetId === 65188128) {
334+
const id = potentialVersion ?? rowData[merges.find(isWithinRangePredicate(rowIndex))!.startRowIndex!].values![1].formattedValue!;
335+
const type = row.values![6].formattedValue!;
336+
if (type.startsWith('Client') || type === 'EXE') clients.add(id);
337+
else if (type.startsWith('Server')) servers.add(id);
338+
}
339+
}
340+
});
341+
});
342+
343+
return [clients, servers];
344+
}
345+
346+
async function verifyVersions(localVersionsMap: Map<string, VersionManifest>, remoteVersionsMap: Map<string, OmniVersionManifest>, spreadsheetPromise: Promise<[clients: Set<string>, servers: Set<string>]>) {
347+
const [spreadsheetClients, spreadsheetServers] = await spreadsheetPromise;
348+
349+
const logServerResult = (version: string, isCorrect: boolean, renamedServerMap: Map<LocalId, IndexOriginalId>) => {
350+
const mappedVersion = renamedServerMap.get(version) ?? version;
351+
const exists = spreadsheetServers.has(mappedVersion);
352+
353+
const correctString = `${version} is ${isCorrect ? 'correct' : 'incorrect'}`;
354+
const isRenamed = version !== mappedVersion;
355+
const existsString = `${version} ${exists ? `exists${isRenamed ? ` as ${mappedVersion}` : ''}` : 'does not exist'}`;
356+
357+
const passingArgs = [`${isRenamed ? '%c' : ''}${correctString}, ${existsString}`];
358+
if (isRenamed) passingArgs.push('color: blue');
359+
isCorrect && exists ? console.log(...passingArgs) : console.warn(`%c-----> ${correctString}, ${existsString} <-----`, 'color: red');
360+
};
361+
362+
const standaloneServerMap = new Map(localVersionsMap.entries().filter((value) => standaloneSevers.includes(value[0])));
363+
if (standaloneServerMap.size !== standaloneSevers.length) console.warn('%cMap size mismatch!', 'color: red');
364+
365+
console.log('Standalone server check:');
366+
standaloneServerMap.forEach((local, version) => {
367+
const correct = remoteVersionsMap.values().some((remote) => {
368+
const remoteServer = remote.downloads!.server;
369+
return remoteServer && (local.downloads!.server ?? local.downloads!.server_zip).sha1 === remoteServer.sha1
370+
});
371+
372+
logServerResult(version, correct, renamedStandaloneServers);
373+
});
374+
375+
console.log();
376+
377+
const orphanServerMap = new Map(localVersionsMap.entries().filter((value) => orphanServers.includes(value[0])));
378+
if (orphanServerMap.size !== orphanServers.length) console.warn('%cMap size mismatch!', 'color: red');
379+
380+
console.log('Orphan server check:');
381+
orphanServerMap.forEach((local, version) => {
382+
const correct = !remoteVersionsMap.values().some((remote) => {
383+
const remoteServer = remote.downloads!.server;
384+
return remoteServer && (local.downloads!.server ?? local.downloads!.server_zip).sha1 === remoteServer.sha1;
385+
});
386+
387+
logServerResult(version, correct, renamedOrphanServers)
388+
});
389+
390+
console.log();
391+
392+
const downloadTypes = new Set<string>();
393+
localVersionsMap.forEach((version) => {
394+
Object.keys(version.downloads!).forEach((key) => downloadTypes.add(key));
395+
});
396+
397+
console.log(`All download types: ${downloadTypes.values().toArray()}`);
398+
399+
console.log();
400+
401+
const mirrorlessVersionMap = new Map(localVersionsMap.entries().filter((value) => mirrorlessRenameMap.has(value[0])));
402+
if (mirrorlessVersionMap.size !== mirrorlessRenameMap.size) console.warn('%cMap size mismatch!', 'color: red');
403+
404+
console.log('Mirrorless version rename check:');
405+
mirrorlessVersionMap.forEach((local, version) => {
406+
// let correct = !localVersionsMap.values().some((otherLocal) => {
407+
// return otherLocal.id !== version && Object.values(local.downloads!).some((localDownload) => {
408+
// return Object.values(otherLocal.downloads!).some((otherLocalDownload) => localDownload.url === otherLocalDownload.url);
409+
// });
410+
// });
411+
412+
const remoteVersion = mirrorlessRenameMap.get(version)!;
413+
const correct = !localVersionsMap.has(remoteVersion) && remoteVersionsMap.has(remoteVersion) && Object.entries(local.downloads!).every((localDownload) => {
414+
const localDownloadSide = localDownload[1];
415+
const remoteDownloadSide = remoteVersionsMap.get(remoteVersion)!.downloads![localDownload[0]];
416+
return remoteDownloadSide && remoteDownloadSide.sha1 === localDownloadSide.sha1 /*&& remoteDownloadSide.url === localDownloadSide.url*/;
417+
});
418+
419+
correct ? console.log(`${version} is correct`) : console.warn(`%c-----> ${version} is incorrect <-----`, 'color: red');
420+
});
421+
422+
console.log();
423+
424+
const mirrorVersionMap = new Map(localVersionsMap.entries().filter((value) => mirrorMap.has(value[0]) || reverseMirrorMap.has(value[0])));
425+
if (mirrorVersionMap.size !== mirrorMap.size + reverseMirrorMap.size) console.warn('%cMap size mismatch!', 'color: red');
426+
427+
console.log('Mirror/Reverse Mirror version check:');
428+
mirrorVersionMap.forEach((local1, version) => {
429+
const mirroredVersion = mirrorMap.get(version) ?? reverseMirrorMap.get(version)!;
430+
const correct = localVersionsMap.has(mirroredVersion) && Object.entries(local1.downloads!).every((local1Download) => {
431+
const local1DownloadInfo = local1Download[1];
432+
const local2DownloadInfo = localVersionsMap.get(mirroredVersion)!.downloads![local1Download[0]];
433+
return local2DownloadInfo && Object.entries(local2DownloadInfo).every((entry) => entry[1] === local1DownloadInfo[entry[0] as keyof DownloadInfo]);
434+
});
435+
436+
correct ? console.log(`${version} is correct`) : console.warn(`%c-----> ${version} is incorrect <-----`, 'color: red');
437+
});
438+
439+
console.log();
440+
441+
const mergedMirrorVersionMap = new Map(localVersionsMap.entries().filter((value) => mergedMirrorMap.has(value[0])));
442+
if (mergedMirrorVersionMap.size !== mergedMirrorMap.size) console.warn('%cMap size mismatch!', 'color: red');
443+
444+
console.log('Merged mirror version check:');
445+
mergedMirrorVersionMap.forEach((merged, version) => {
446+
const [originalClient, originalServer] = mergedMirrorMap.get(version)!;
447+
const correct = localVersionsMap.has(originalClient) && localVersionsMap.has(originalServer) && Object.entries(merged.downloads!).every((mergedDownload) => {
448+
const [downloadType, downloadInfo] = mergedDownload;
449+
const originalDownloadInfo = localVersionsMap.get(downloadType.includes('client') ? originalClient : originalServer)!.downloads![downloadType];
450+
return originalDownloadInfo && Object.entries(originalDownloadInfo).every((entry) => entry[1] === downloadInfo[entry[0] as keyof DownloadInfo]);
451+
});
452+
453+
correct ? console.log(`${version} is correct`) : console.warn(`%c-----> ${version} is incorrect <-----`, 'color: red');
454+
});
455+
456+
console.log();
457+
458+
const weirdMergeVersionMap = new Map(localVersionsMap.entries().filter((value) => weirdMergeMap.has(value[0])));
459+
if (weirdMergeVersionMap.size !== weirdMergeMap.size) console.warn('%cMap size mismatch!', 'color: red');
460+
461+
console.log('Weird merge version check:');
462+
weirdMergeVersionMap.forEach((weirdMerge, version) => {
463+
const [remoteClient, indexServer] = weirdMergeMap.get(version)!;
464+
const correct = remoteVersionsMap.has(remoteClient) && spreadsheetServers.has(indexServer) && Object.entries(weirdMerge.downloads!).every((weirdMergeDownload) => {
465+
const [downloadType, downloadInfo] = weirdMergeDownload;
466+
if (downloadType.includes('client')) {
467+
const clientDownloadInfo = remoteVersionsMap.get(remoteClient)!.downloads![downloadType];
468+
return clientDownloadInfo && Object.entries(clientDownloadInfo).every((entry) => entry[1] === downloadInfo[entry[0] as keyof DownloadInfo]);
469+
} else return !remoteVersionsMap.values().some((remote) => Object.values(remote.downloads!).some((download) => download.url === downloadInfo.url));
470+
});
471+
472+
correct ? console.log(`${version} is correct`) : console.warn(`%c-----> ${version} is incorrect <-----`, 'color: red');
473+
});
474+
475+
console.log();
476+
477+
const allOtherVersionsMap = new Map(localVersionsMap.entries().filter((value) => shouldSkip(value[0])));
478+
479+
console.log('All other versions check (correct versions will be skipped):');
480+
let mismatches = 0;
481+
const unindexedClients: Set<VersionId> = new Set();
482+
const unindexedServers: Set<VersionId> = new Set();
483+
allOtherVersionsMap.forEach((manifest, version) => {
484+
const correct = remoteVersionsMap.has(version) && Object.entries(manifest.downloads!).every((localDownload) => {
485+
const localDownloadInfo = localDownload[1];
486+
const downloadType = localDownload[0];
487+
if (downloadType === 'client' && !spreadsheetClients.has(version)) unindexedClients.add(version);
488+
if (downloadType === 'server' && !spreadsheetServers.has(version)) unindexedServers.add(version);
489+
const remoteDownloadInfo = remoteVersionsMap.get(version)!.downloads![downloadType];
490+
return remoteDownloadInfo && localDownloadInfo.sha1 === remoteDownloadInfo.sha1;
491+
});
492+
493+
if (!correct) {
494+
const message = [`%c${version} is %s`];
495+
496+
if (exemptOtherVersions.has(version)) {
497+
message.push('color: orange');
498+
message.push(`exempt, ${exemptOtherVersions.get(version)!}`);
499+
} else {
500+
unindexedClients.delete(version);
501+
unindexedServers.delete(version);
502+
message.push('color: red');
503+
message.push(`incorrect`);
504+
mismatches++;
505+
}
506+
507+
console.warn(...message);
508+
}
509+
});
510+
511+
console.log(`There are ${mismatches} incorrect versions`);
512+
513+
console.log();
514+
unindexedClients.forEach((client) => console.warn(`%c${client} is correct, but not on the client index!`, 'color: magenta'));
515+
516+
console.log();
517+
unindexedServers.forEach((server) => console.warn(`%c${server} is correct, but not on the server index!`, 'color: magenta'));
518+
}
519+
309520
function checkLocalManifestEntries(manifest: MainManifest, versionJsons: Map<string, VersionManifest>, detailsJsons: Map<string, VersionData>) {
310521
// log manifest entries with inconsistent references to version jsons and/or details jsons
311522
for (let i = 0; i < manifest.versions.length; i++) {
@@ -379,14 +590,18 @@ if (import.meta.main) (async () => {
379590
return await updateAndCacheExternalVersionJsons(remoteManifestJson);
380591
})();
381592

593+
const sheetsApi = new Sheets(auth.fromJSON(JSON.parse(await Deno.readTextFile('google-service-account.json'))));
594+
const spreadsheetPromise = readSpreadsheetVersions(sheetsApi.spreadsheetsGet('1OCxMNQLeZJi4BlKKwHx2OlzktKiLEwFXnmCrSdAFwYQ', {includeGridData: true}));
595+
382596
console.log('Locally stored manifests loaded');
383597

384598
while (true) {
385599
console.log('\nWelcome to the new version manifest update and compare tool!');
386600
console.log('1: Validate the main manifest and the local version jsons and details json');
387601
console.log('2: Update sha1 hashes in the main versions manifest');
388602
console.log('3: Compare local manifests with external (Omniarchive) manifests');
389-
console.log('4: Update and cache external (Omniarchive) manifests');
603+
console.log('4: Verify if lists and maps of modified version IDs are correct');
604+
console.log('5: Update and cache external (Omniarchive) manifests');
390605
console.log('E: Exit');
391606
const option = prompt('Choose an option: ');
392607
console.log();
@@ -401,7 +616,10 @@ if (import.meta.main) (async () => {
401616
case '3':
402617
compareLocalWithOmniarchive(localVersionJsonsMap, remoteVersionJsonsMap);
403618
break;
404-
case '4': {
619+
case '4':
620+
await verifyVersions(localVersionJsonsMap, remoteVersionJsonsMap, spreadsheetPromise);
621+
break;
622+
case '5': {
405623
const confirmation = confirm('Are you sure? This will take a while');
406624
console.log();
407625
if (confirmation) remoteVersionJsonsMap = await updateAndCacheExternalVersionJsons(remoteManifestJson);

0 commit comments

Comments
 (0)