Skip to content

Commit

Permalink
util: More coverage report tweaks (#572)
Browse files Browse the repository at this point in the history
- Tooltips were still not quite correct, fixed them
- Tags weren't sorted properly
- simple-git interactions simplified
- Fixed bug with unreleased status for zones where all files have
pending changes for next release
- Fixed edge case where no skeleton file for zone was initially added,
so pending PRs weren't being picked up

~~Still tweaking/testing changes for this one.~~

Should be good for review now.
  • Loading branch information
valarnin authored Jan 13, 2025
1 parent 8223857 commit 83441b5
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 67 deletions.
6 changes: 6 additions & 0 deletions util/coverage/coverage.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type CoverageEntry = {
name: string;
commit?: string;
tag?: string;
tagHash?: string;
}[];
lastModified: number;
};
Expand Down Expand Up @@ -61,6 +62,10 @@ export type Tags = {
[tagName: string]: {
tagDate: number;
tagHash: string;
files: {
name: string;
hash: string;
}[];
};
};

Expand All @@ -69,4 +74,5 @@ export type Pulls = {
number: number;
title: string;
files: string[];
zones: number[];
}[];
19 changes: 11 additions & 8 deletions util/coverage/coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,14 @@ const buildZoneGrid = (container: HTMLElement, lang: Lang, coverage: Coverage) =
const hasOopsy = zoneCoverage.oopsy && zoneCoverage.oopsy.num > 0;
const hasTriggers = zoneCoverage.triggers.num > 0;

if (!hasTriggers && !hasOopsy && !zoneCoverage.timeline.hasFile) {
const openPRs = pulls
.filter((pr) =>
(pr.files.find((file) => zoneCoverage.files.find((file2) => file === file2.name)) !==
undefined) ||
pr.zones.includes(zoneId)
);

if (!hasTriggers && !hasOopsy && !zoneCoverage.timeline.hasFile && openPRs.length === 0) {
const div = addDiv(container, 'text', translate(miscStrings.unsupported, lang));
div.style.color = 'red';
return;
Expand All @@ -516,14 +523,10 @@ const buildZoneGrid = (container: HTMLElement, lang: Lang, coverage: Coverage) =
),
].sort((left, right) => right?.tagDate - left?.tagDate)[0];

const openPRs = pulls
.filter((pr) =>
pr.files.find((file) => zoneCoverage.files.find((file2) => file === file2.name))
);

let color = 'green';

const unreleased = version?.tag === undefined;
const unreleased = version?.tag === undefined ||
(version?.tagDate ?? 0) < zoneCoverage.lastModified;

if (unreleased || openPRs.length > 0) {
color = 'orange';
Expand All @@ -535,7 +538,7 @@ const buildZoneGrid = (container: HTMLElement, lang: Lang, coverage: Coverage) =
lastUpdated.toString();

if (openPRs.length > 0) {
titleText += `Open PRs: ${openPRs.map((pr) => `#${pr.number}`).join(', ')} | `;
titleText = `Open PRs: ${openPRs.map((pr) => `#${pr.number}`).join(', ')} | ${titleText}`;
}

const div = document.createElement('div');
Expand Down
155 changes: 96 additions & 59 deletions util/gen_coverage_report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,6 @@ import {
import { findMissingTranslations, MissingTranslationErrorType } from './find_missing_translations';
import findManifestFiles from './manifest';

// eslint can't find these types from simple-git for some reason
// Redefine the parts that we actually use
type TagResult = {
all: string[];
};

type LogResult = {
latest: {
date: string;
hash: string;
} | null;
};

type MissingTranslations = {
file: string;
line?: number;
Expand All @@ -50,6 +37,17 @@ type MissingTranslationsDict = {
[lang in Lang]?: MissingTranslations[];
};

type SimpleGit = ReturnType<typeof simpleGit>;

// This hash is the default "initial commit" hash for git, all repos have it
// If for some reason the entire git tree is re-imported into a newer version of git,
// the default commit hash will be an SHA-256 hash as follows instead:
// 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321
// This can be derived as needed via `git hash-object -t tree /dev/null`
const DEFAULT_COMMIT_HASH = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';

const notUndefined = <T>(v: T | undefined): v is T => v !== undefined;

// Paths are relative to current file.
// We can't import the manifest directly from util/ because that's webpack magic,
// so need to do the same processing its loader would do.
Expand Down Expand Up @@ -482,20 +480,48 @@ const writeMissingTranslations = (missing: MissingTranslations[], outputFileName
}
};

(async () => {
const git = simpleGit();
const mapCoverageTags = async (coverage: Coverage, git: SimpleGit, tags: Tags) => {
const reverseOrderTags = Object.keys(tags).reverse();

for (const coverageEntry of Object.values(coverage)) {
for (const file of coverageEntry.files) {
const logData = await git.log({
file: file.name,
maxCount: 1,
});

if (logData === undefined)
continue;

let tagData: TagResult | undefined;
const latest = logData.latest;
if (latest !== null) {
coverageEntry.lastModified = Math.max(
coverageEntry.lastModified,
(new Date(latest.date)).getTime(),
);
file.commit = latest.hash;
}

await git.tags({
'--sort': '-authordate',
if (file.commit !== undefined) {
for (const tag of reverseOrderTags) {
const tagFile = tags[tag]?.files.find((tagFile) => tagFile.name === file.name);
if (tagFile) {
file.tag = tag;
file.tagHash = tagFile.hash;
break;
}
}
}
}
}
};

const extractTagsAndPulls = async (git: SimpleGit) => {
const tagData = await git.tags({
'--format': '%(objectname)|%(refname:strip=2)|%(authordate)|%(*authordate)',
}, (_err, data) => {
tagData = data;
});
tagData ??= undefined;

const tags: Tags = {};
const unsortedTags: (Tags[string] & { name: string })[] = [];

for (const tag of tagData?.all ?? []) {
const [tagHash, tagName, tagDate, commitDate] = tag.split('|', 4);
Expand All @@ -508,9 +534,35 @@ const writeMissingTranslations = (missing: MissingTranslations[], outputFileName
if (isNaN(tagDateObj.getTime())) {
tagDateObj = new Date(commitDate);
}
tags[tagName] = {
unsortedTags.push({
name: tagName,
tagDate: tagDateObj.getTime(),
tagHash: tagHash,
files: [],
});
}

const tags: Tags = {};

let lastVersion = DEFAULT_COMMIT_HASH;

for (const tag of unsortedTags.sort((l, r) => l.tagDate - r.tagDate)) {
const result = await git.raw(['diff-tree', '-r', lastVersion, tag.name]);
lastVersion = tag.name;

tags[tag.name] = {
tagDate: tag.tagDate,
tagHash: tag.tagHash,
files: result.split('\n').map((line) => {
const matches =
/^:(?<oldMode>[^\s]+) (?<newMode>[^\s]+) (?<oldHash>[^\s]+) (?<newHash>[^\s]+) (?<action>[^\s]+)\s*(?<name>[^\s].*?)$/
.exec(line);

return {
hash: matches?.groups?.['newHash'] ?? '',
name: matches?.groups?.['name'] ?? '',
};
}),
};
}

Expand All @@ -537,13 +589,33 @@ const writeMissingTranslations = (missing: MissingTranslations[], outputFileName

const files = pullFiles.map((f) => f.filename);

const zones = pullFiles
.filter((f) =>
(f.filename.startsWith('ui/raidboss/data') ||
f.filename.startsWith('ui/oopsyraidsy/data')) && f.filename.endsWith('.ts')
)
.map((f) => /ZoneId\.([a-zA-Z0-9]+)/.exec(f.patch ?? '')?.[1])
.filter(notUndefined)
.map((zoneId) =>
zoneId in ZoneId ? ZoneId[zoneId as keyof typeof ZoneId] as number : undefined
)
.filter(notUndefined);

pulls.push({
number: openPull.number,
title: openPull.title,
url: openPull.html_url,
files: files,
zones: zones,
});
}
return { tags, pulls };
};

(async () => {
const git = simpleGit();

const { tags, pulls }: { tags: Tags; pulls: Pulls } = await extractTagsAndPulls(git);

// Do this prior to chdir which conflicts with find_missing_timeline_translations.ts.
// FIXME: make that script more robust to cwd.
Expand All @@ -564,42 +636,7 @@ const writeMissingTranslations = (missing: MissingTranslations[], outputFileName
await processRaidbossCoverage(raidbossManifest, coverage, missingTranslations);
await processOopsyCoverage(oopsyManifest, coverage);

for (const coverageEntry of Object.values(coverage)) {
for (const file of coverageEntry.files) {
let logData: undefined | LogResult;
await git.log({
file: file.name,
maxCount: 1,
}, (_err, data) => logData = data);

logData ??= undefined;

if (logData === undefined)
continue;

const latest = logData.latest;
if (latest !== null) {
coverageEntry.lastModified = Math.max(
coverageEntry.lastModified,
(new Date(latest.date)).getTime(),
);
file.commit = latest.hash;
}

if (file.commit !== undefined) {
let tagData: undefined | string;
await git.tag({
'--sort': 'authordate',
'--contains': file.commit,
}, (_err, data) => tagData = data.split('\n')[0]?.trim());
tagData ??= undefined;

if (tagData !== undefined) {
file.tag = tagData;
}
}
}
}
await mapCoverageTags(coverage, git, tags);

const { totals, translationTotals } = buildTotals(coverage, missingTranslations);
writeCoverageReport(
Expand Down

0 comments on commit 83441b5

Please sign in to comment.