Failed runs end with one fatal line; failure lines are errors
check / check (push) Waiting to run

A command that exits 1 because files or albums failed now ends with one
fatal line saying how many failed and where to look, so -q never leaves
a non-zero exit unexplained: backup points to failures.json, backup-metadata
to mlDataError in each file's JSON, and fix-missing-thumbnails to its
Failed files list. The progress callbacks of the backup, the metadata
backup and the thumbnail helpers take a level, so every line that reports
a failure is logged at error. The library's operation names are shown in
plain words. The README says which lines are error and warning, and which
summary lines --json replaces.

Model: opus-5-5
This commit is contained in:
2026-10-18 19:15:16 +00:00
parent f9969b2525
commit d87a5e7ef5
12 changed files with 253 additions and 63 deletions
+26 -3
View File
@@ -544,11 +544,22 @@ describe("listMissingThumbnails", () => {
const client = await login(counted.fetch, { ...noWait });
const lib = await openLib(client);
const missing = await listMissingThumbnails(lib, client);
const errors: string[] = [];
const missing = await listMissingThumbnails(
lib,
client,
(msg, level) => {
if (level === "error") errors.push(msg);
},
);
lib.close();
// Only the genuinely empty thumbnail is reported.
// Only the genuinely empty thumbnail is reported. The file that
// could not be checked is reported as an error.
expect(missing.map((m) => m.fileID)).toEqual([101]);
expect(errors).toEqual([
expect.stringMatching(/^\[.*\] Could not check file-102\.jpg: /),
]);
// And the 500 was retried rather than accepted as an answer: four
// attempts is the library default.
expect(counted.matched()).toBe(4);
@@ -699,7 +710,15 @@ describe("fixMissingThumbnails", () => {
const client = await login(buildThumbFetch(fixMock));
const lib = await openLib(client);
const results = await fixMissingThumbnails(lib, client, [101, 102]);
const errors: string[] = [];
const results = await fixMissingThumbnails(
lib,
client,
[101, 102],
(msg, level) => {
if (level === "error") errors.push(msg);
},
);
lib.close();
expect(results.length).toBe(2);
@@ -707,6 +726,10 @@ describe("fixMissingThumbnails", () => {
const failure = results.find((r) => r.fileID === 102)!;
expect(success.status).toBe("fixed");
expect(failure.status).toBe("failed");
// The failed file, and only it, is reported as an error.
expect(errors).toEqual([
expect.stringMatching(/^\[.*\] FAILED file-102\.jpg: /),
]);
});
it("skips a non-JPEG image and a video as unsupported, not failed (issue #17)", async () => {