mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-07 17:43:51 +00:00
feat(editor): add --export-project-checksum with project fingerprint
Project-wide companion to --export-zone-checksum. Walks every zone in <projectDir>, hashes every source file, and emits PROJECT_SHA256SUMS in the standard sha256sum format with paths kept relative to projectDir (so entries look like "<hex> <zone>/<file>"). Also computes a single SHA-256 fingerprint over the manifest body — a one-line identity for the whole project, useful for CI release gates and reproducibility. Verified: external 'sha256sum -c PROJECT_SHA256SUMS' passes all 9 entries on a 2-zone test project, and standalone 'sha256sum PROJECT_SHA256SUMS' matches the emitted fingerprint byte-for-byte. Adds wowee_sha256::hex(buf, len) helper. Brings command count to 169.
This commit is contained in:
parent
82fc1dbd80
commit
576ffc38e0
1 changed files with 90 additions and 1 deletions
|
|
@ -123,6 +123,11 @@ static std::string fileHex(const std::string& path) {
|
|||
}
|
||||
return hexFinal(s);
|
||||
}
|
||||
static std::string hex(const uint8_t* data, size_t len) {
|
||||
State s;
|
||||
update(s, data, len);
|
||||
return hexFinal(s);
|
||||
}
|
||||
} // namespace wowee_sha256
|
||||
|
||||
static std::vector<std::string> validateWomErrors(
|
||||
|
|
@ -681,6 +686,8 @@ static void printUsage(const char* argv0) {
|
|||
std::printf(" Emit creatures.csv / objects.csv / quests.csv for spreadsheet workflows\n");
|
||||
std::printf(" --export-zone-checksum <zoneDir> [out.sha256]\n");
|
||||
std::printf(" Emit a SHA-256 manifest of every source file in a zone (for integrity checks)\n");
|
||||
std::printf(" --export-project-checksum <projectDir> [out.sha256]\n");
|
||||
std::printf(" Project-wide SHA-256 manifest (paths are zone-relative) + single project fingerprint\n");
|
||||
std::printf(" --export-zone-html <zoneDir> [out.html]\n");
|
||||
std::printf(" Emit a single-file HTML viewer next to the zone .glb (model-viewer based)\n");
|
||||
std::printf(" --export-project-html <projectDir> [out.html]\n");
|
||||
|
|
@ -857,7 +864,7 @@ int main(int argc, char* argv[]) {
|
|||
"--info-zone-density",
|
||||
"--export-zone-summary-md", "--export-quest-graph",
|
||||
"--export-zone-csv", "--export-zone-html", "--export-project-html",
|
||||
"--export-project-md", "--export-zone-checksum",
|
||||
"--export-project-md", "--export-zone-checksum", "--export-project-checksum",
|
||||
"--scaffold-zone", "--mvp-zone", "--add-tile", "--remove-tile", "--list-tiles",
|
||||
"--for-each-zone", "--for-each-tile", "--zone-stats", "--info-tilemap",
|
||||
"--list-zone-deps", "--check-zone-refs", "--check-zone-content",
|
||||
|
|
@ -5999,6 +6006,88 @@ int main(int argc, char* argv[]) {
|
|||
entries.size());
|
||||
std::printf(" verify with: sha256sum -c %s\n", outPath.c_str());
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--export-project-checksum") == 0 && i + 1 < argc) {
|
||||
// Project-wide manifest in the same sha256sum format, with
|
||||
// paths kept relative to <projectDir> (so entries look like
|
||||
// "<hex> <zoneName>/<file>"). Also emits a single SHA-256
|
||||
// fingerprint over the manifest itself — a one-line
|
||||
// identity for the whole project, handy for CI release
|
||||
// gates and reproducibility checks.
|
||||
//
|
||||
// wowee_editor --export-project-checksum custom_zones
|
||||
// sha256sum -c custom_zones/PROJECT_SHA256SUMS
|
||||
std::string projectDir = argv[++i];
|
||||
std::string outPath;
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') outPath = argv[++i];
|
||||
namespace fs = std::filesystem;
|
||||
if (!fs::exists(projectDir) || !fs::is_directory(projectDir)) {
|
||||
std::fprintf(stderr,
|
||||
"export-project-checksum: %s is not a directory\n",
|
||||
projectDir.c_str());
|
||||
return 1;
|
||||
}
|
||||
if (outPath.empty()) outPath = projectDir + "/PROJECT_SHA256SUMS";
|
||||
// Same derived-output filter as --export-zone-checksum.
|
||||
auto isDerived = [](const fs::path& p) {
|
||||
std::string ext = p.extension().string();
|
||||
std::string name = p.filename().string();
|
||||
if (ext == ".glb" || ext == ".obj" || ext == ".stl" ||
|
||||
ext == ".html" || ext == ".dot" || ext == ".csv") return true;
|
||||
if (name == "ZONE.md" || name == "DEPS.md" ||
|
||||
name == "SHA256SUMS" || name == "PROJECT_SHA256SUMS" ||
|
||||
name == "Makefile") return true;
|
||||
if (ext == ".png") return true;
|
||||
return false;
|
||||
};
|
||||
std::vector<std::string> zones;
|
||||
for (const auto& entry : fs::directory_iterator(projectDir)) {
|
||||
if (!entry.is_directory()) continue;
|
||||
if (!fs::exists(entry.path() / "zone.json")) continue;
|
||||
zones.push_back(entry.path().string());
|
||||
}
|
||||
std::sort(zones.begin(), zones.end());
|
||||
std::vector<std::pair<std::string, std::string>> entries;
|
||||
for (const auto& zoneDir : zones) {
|
||||
std::error_code ec;
|
||||
for (const auto& e : fs::recursive_directory_iterator(zoneDir, ec)) {
|
||||
if (!e.is_regular_file()) continue;
|
||||
if (isDerived(e.path())) continue;
|
||||
std::string hex = wowee_sha256::fileHex(e.path().string());
|
||||
if (hex.empty()) continue;
|
||||
std::string rel = fs::relative(e.path(), projectDir, ec).string();
|
||||
if (ec) rel = e.path().string();
|
||||
entries.push_back({hex, rel});
|
||||
}
|
||||
}
|
||||
std::sort(entries.begin(), entries.end(),
|
||||
[](const auto& a, const auto& b) { return a.second < b.second; });
|
||||
std::ofstream out(outPath);
|
||||
if (!out) {
|
||||
std::fprintf(stderr,
|
||||
"export-project-checksum: cannot write %s\n", outPath.c_str());
|
||||
return 1;
|
||||
}
|
||||
// Hash the manifest body inline so the project fingerprint
|
||||
// is byte-identical to what `sha256sum PROJECT_SHA256SUMS`
|
||||
// would yield on the written file.
|
||||
std::string body;
|
||||
body.reserve(entries.size() * 80);
|
||||
for (const auto& [hash, path] : entries) {
|
||||
body += hash;
|
||||
body += " ";
|
||||
body += path;
|
||||
body += "\n";
|
||||
}
|
||||
out << body;
|
||||
out.close();
|
||||
std::string fingerprint = wowee_sha256::hex(
|
||||
reinterpret_cast<const uint8_t*>(body.data()), body.size());
|
||||
std::printf("Wrote %s\n", outPath.c_str());
|
||||
std::printf(" zones : %zu\n", zones.size());
|
||||
std::printf(" files hashed : %zu\n", entries.size());
|
||||
std::printf(" fingerprint : %s\n", fingerprint.c_str());
|
||||
std::printf(" verify with : sha256sum -c %s\n", outPath.c_str());
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--export-zone-html") == 0 && i + 1 < argc) {
|
||||
// Generate a single-file HTML viewer next to the zone .glb.
|
||||
// Anyone with a modern browser can open it — no installs, no
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue