feat(catalog-sync): report sources that can only be compared, not imported
CI / check (push) Successful in 47s
CI / tests-integration (push) Successful in 2m36s
CI / tests-unit (push) Successful in 2m52s
CI / tests-ui (push) Successful in 3m28s
CI / preflight (push) Skipped
CI / tests-quality (push) Successful in 1m30s
CI / tests-imaging-browsers (push) Successful in 2m47s
CI / deploy (push) Successful in 3m21s
CI / check (push) Successful in 47s
CI / tests-integration (push) Successful in 2m36s
CI / tests-unit (push) Successful in 2m52s
CI / tests-ui (push) Successful in 3m28s
CI / preflight (push) Skipped
CI / tests-quality (push) Successful in 1m30s
CI / tests-imaging-browsers (push) Successful in 2m47s
CI / deploy (push) Successful in 3m21s
The Habbo presets (and the other furnidata-only sources) carry no
`nitroBaseUrl`, so the clone importer's `${nitroBaseUrl}/${classname}.nitro`
download can never work against them — an `--import-new` run would have failed
for every single item instead of saying why. The SWF path that does work against
`images.habbo.com` lives in `furni-import.ts`, not in the clone importer this
CLI uses.
`stepImportNew` now skips those sources up-front, lists them under
"compared only" in the summary, and the menu prints the same warning when an
import step is chosen with such a source, pointing at the sources that do serve
bundles. Verified: Habbo (COM) reports compare-only, while Habboon and
VirtualCity list real candidates.
This commit is contained in:
1 parent
f56b345598
commit
77a0d1010c
3 files changed
+78
-4
No files matched your search
@@ -117,6 +117,21 @@ ACTIONS=(audit)
|
|||||||
CONFIRM=""
|
CONFIRM=""
|
||||||
# Empty for the audit-only branch on purpose: no repair flags are passed.
|
# Empty for the audit-only branch on purpose: no repair flags are passed.
|
||||||
ACTION_FLAGS=()
|
ACTION_FLAGS=()
|
||||||
|
# Prints a warning when the chosen source only serves furnidata (no .nitro
|
||||||
|
# bundles), so the import step would find nothing to download.
|
||||||
|
warn_compare_only_sources() {
|
||||||
|
case "$SOURCE_LABEL" in
|
||||||
|
"Habbo (COM)" | "Habbo (NL)" | "Habbo (DE)" | "Habbo (FR)" | \
|
||||||
|
"Habbo (ES)" | "Habbo (FI)" | "Habbo (BR)" | "Habbo (TR)" | \
|
||||||
|
"Habbo (IT - GitHub mirror)" | "YabboHotel" | "Wibbo" | "Hubba.cc")
|
||||||
|
echo
|
||||||
|
echo "Note: ${SOURCE_LABEL} only serves furnidata — there are no .nitro"
|
||||||
|
echo "bundles to import, so the import step can only COMPARE."
|
||||||
|
echo "To actually import furniture, pick a source that serves bundles"
|
||||||
|
echo "(Habblet City, VirtualCity, SodaStudios, Habboon, Leet, Hubbly) or 'a'."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
case "$ACTION_CHOICE" in
|
case "$ACTION_CHOICE" in
|
||||||
1)
|
1)
|
||||||
ACTIONS=(audit "[nothing written]")
|
ACTIONS=(audit "[nothing written]")
|
||||||
@@ -179,6 +194,9 @@ esac
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
printf 'Running: %s against %s\n' "${ACTIONS[1]}" "$SOURCE_LABEL"
|
printf 'Running: %s against %s\n' "${ACTIONS[1]}" "$SOURCE_LABEL"
|
||||||
|
case "$ACTION_FLAGS" in
|
||||||
|
*--import-new*) warn_compare_only_sources ;;
|
||||||
|
esac
|
||||||
if [ -n "$CONFIRM" ]; then
|
if [ -n "$CONFIRM" ]; then
|
||||||
read -r -p "$CONFIRM" REPLY
|
read -r -p "$CONFIRM" REPLY
|
||||||
if [ "$REPLY" != "y" ] && [ "$REPLY" != "Y" ]; then
|
if [ "$REPLY" != "y" ] && [ "$REPLY" != "Y" ]; then
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { tmpdir } from "node:os";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import {
|
import {
|
||||||
|
canImportBundles,
|
||||||
DEFAULT_CATALOG_SYNC_FLAGS,
|
DEFAULT_CATALOG_SYNC_FLAGS,
|
||||||
describeActions,
|
describeActions,
|
||||||
parseCatalogSyncArgs,
|
parseCatalogSyncArgs,
|
||||||
@@ -131,6 +132,36 @@ describe("catalog-sync import selection", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("catalog-sync import feasibility", () => {
|
||||||
|
it("imports from a source that serves bundles", () => {
|
||||||
|
expect(
|
||||||
|
canImportBundles({
|
||||||
|
id: "default-habblet-city",
|
||||||
|
name: "Habblet City",
|
||||||
|
furnidataUrl: "https://www.habblet.city/gamedata/furnidata.json",
|
||||||
|
nitroBaseUrl: "https://www.habblet.city/nitro/bundled/furniture",
|
||||||
|
iconBaseUrl: "",
|
||||||
|
hotel: "com",
|
||||||
|
} as never),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// The Habbo presets have no nitro/icon base URL: their furnidata can be
|
||||||
|
// compared, but there is nothing to download from.
|
||||||
|
it("does not import from a compare-only source", () => {
|
||||||
|
expect(
|
||||||
|
canImportBundles({
|
||||||
|
id: "default-habbo-com",
|
||||||
|
name: "Habbo (COM)",
|
||||||
|
furnidataUrl: "https://www.habbo.com/gamedata/furnidata_json/1",
|
||||||
|
nitroBaseUrl: "",
|
||||||
|
iconBaseUrl: "",
|
||||||
|
hotel: "com",
|
||||||
|
} as never),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("catalog-sync step composition", () => {
|
describe("catalog-sync step composition", () => {
|
||||||
it("--everything turns on every step except the writing ones", () => {
|
it("--everything turns on every step except the writing ones", () => {
|
||||||
const flags = parseCatalogSyncArgs(["--everything"]);
|
const flags = parseCatalogSyncArgs(["--everything"]);
|
||||||
|
|||||||
+29
-4
@@ -83,6 +83,22 @@ export interface ImportOutcome {
|
|||||||
skipped: number;
|
skipped: number;
|
||||||
failed: number;
|
failed: number;
|
||||||
failures: Array<{ classname: string; error: string }>;
|
failures: Array<{ classname: string; error: string }>;
|
||||||
|
/** Sources that were skipped because they cannot serve bundles. */
|
||||||
|
skippedSources: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a source can actually be imported from.
|
||||||
|
*
|
||||||
|
* The clone import downloads `${nitroBaseUrl}/${classname}.nitro` and gives up
|
||||||
|
* when that is not reachable. Habbo's own entries carry no nitro/icon base URLs
|
||||||
|
* (their furnidata is served, their assets are not), so they are compare-only:
|
||||||
|
* perfect for finding out what this hotel lacks, useless for actually fetching
|
||||||
|
* it. The SWF-based path that works against `images.habbo.com` lives in
|
||||||
|
* `furni-import.ts`, not in the clone importer this CLI uses.
|
||||||
|
*/
|
||||||
|
export function canImportBundles(source: SourceRef): boolean {
|
||||||
|
return Boolean(source.nitroBaseUrl?.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CatalogSyncFlags {
|
export interface CatalogSyncFlags {
|
||||||
@@ -420,8 +436,16 @@ async function stepImportNew(
|
|||||||
itemType: string;
|
itemType: string;
|
||||||
}> = [];
|
}> = [];
|
||||||
let lastSource = "";
|
let lastSource = "";
|
||||||
|
const skippedSources: string[] = [];
|
||||||
|
|
||||||
for (const source of sources) {
|
for (const source of sources) {
|
||||||
|
if (!canImportBundles(source)) {
|
||||||
|
log(
|
||||||
|
`! ${source.name} serves no .nitro bundles — it can be compared against, but nothing can be imported from it`,
|
||||||
|
);
|
||||||
|
skippedSources.push(source.name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (flags.importLimit > 0 && planned >= flags.importLimit) break;
|
if (flags.importLimit > 0 && planned >= flags.importLimit) break;
|
||||||
const remaining = flags.importLimit
|
const remaining = flags.importLimit
|
||||||
? flags.importLimit - planned
|
? flags.importLimit - planned
|
||||||
@@ -515,6 +539,7 @@ async function stepImportNew(
|
|||||||
skipped,
|
skipped,
|
||||||
failed,
|
failed,
|
||||||
failures: failures.slice(0, 25),
|
failures: failures.slice(0, 25),
|
||||||
|
skippedSources,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -736,12 +761,12 @@ function printSummary(report: AuditReport, started: number): void {
|
|||||||
console.log(
|
console.log(
|
||||||
` imported from source ${outcome.imported} new (${outcome.skipped} already present, ${outcome.failed} failed)`,
|
` imported from source ${outcome.imported} new (${outcome.skipped} already present, ${outcome.failed} failed)`,
|
||||||
);
|
);
|
||||||
|
if (outcome.skippedSources.length > 0)
|
||||||
|
console.log(
|
||||||
|
` compared only ${outcome.skippedSources.join(", ")} (no .nitro bundles to import from)`,
|
||||||
|
);
|
||||||
for (const failure of outcome.failures)
|
for (const failure of outcome.failures)
|
||||||
console.log(` ${failure.classname}: ${failure.error}`);
|
console.log(` ${failure.classname}: ${failure.error}`);
|
||||||
if (outcome.failures.length > 0)
|
|
||||||
console.log(
|
|
||||||
` import failures are per-item: a failed download never aborts the run.`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (report.missingFromSources.length > 0) {
|
if (report.missingFromSources.length > 0) {
|
||||||
|
|||||||
Reference in new issue
Block a user