Reverify staged gameplay sources before build publication
Native and manual checks / native (windows-2025) (push) Waiting to run
Native and manual checks / native (ubuntu-24.04) (push) Failing after 35s
Native and manual checks / manual (push) Successful in 28s
Windows editor and software Vulkan / windows-graphics (push) Canceled after 0s
Native and manual checks / native (windows-2025) (push) Waiting to run
Native and manual checks / native (ubuntu-24.04) (push) Failing after 35s
Native and manual checks / manual (push) Successful in 28s
Windows editor and software Vulkan / windows-graphics (push) Canceled after 0s
This commit is contained in:
@@ -19,9 +19,11 @@
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
namespace faset::editor {
|
||||
namespace fs = std::filesystem;
|
||||
@@ -472,14 +474,21 @@ struct BuildService::Impl {
|
||||
const auto compiled = std::chrono::steady_clock::now();
|
||||
auto player = build_executable(native_directory, configuration, "faset_player");
|
||||
auto exporter = build_executable(native_directory, configuration, "faset_schema_exporter");
|
||||
const auto package_key =
|
||||
build_package_key(inputs, native_directory, configuration, player, exporter);
|
||||
const auto source_unchanged = [&] {
|
||||
const auto snapshot_root = staged_scripts.parent_path();
|
||||
if (fs::is_symlink(fs::symlink_status(snapshot_root)) ||
|
||||
!fs::is_directory(staged_scripts) ||
|
||||
gameplay_source_hash(snapshot_root, job.lua) != inputs.source_hash)
|
||||
throw std::runtime_error(
|
||||
"Gameplay source snapshot changed during the build; build again");
|
||||
if (gameplay_source_hash(config.project_root,
|
||||
scripting::loadLuaProject(config.project_root)) !=
|
||||
inputs.source_hash)
|
||||
throw std::runtime_error("Gameplay sources changed during the build; build again");
|
||||
};
|
||||
source_unchanged();
|
||||
const auto package_key =
|
||||
build_package_key(inputs, native_directory, configuration, player, exporter);
|
||||
const auto result_for = [&](const std::string& id, const std::string& fingerprint,
|
||||
bool reused) -> Json {
|
||||
const auto directory = config.cache_root / "builds" / id;
|
||||
@@ -502,6 +511,7 @@ struct BuildService::Impl {
|
||||
{"total", milliseconds(started, finished)}}}};
|
||||
};
|
||||
const auto pointer_file = config.cache_root / "last_build.json";
|
||||
std::optional<std::pair<std::string, std::string>> cached_generation;
|
||||
if (fs::is_regular_file(pointer_file))
|
||||
try {
|
||||
const auto pointer = read_json(pointer_file);
|
||||
@@ -509,15 +519,19 @@ struct BuildService::Impl {
|
||||
const auto candidate =
|
||||
project_path(config.cache_root, fs::path("builds") / id);
|
||||
if (validate_build_generation(candidate, package_key)) {
|
||||
source_unchanged();
|
||||
checkpoint(job, "Reusing verified build generation", .68);
|
||||
const auto manifest = read_json(candidate / "manifest.json");
|
||||
log(job, "Verified schema/package cache hit: " + id + "\n");
|
||||
return result_for(id, manifest.at("fingerprint").get<std::string>(), true);
|
||||
cached_generation =
|
||||
{id, manifest.at("fingerprint").get<std::string>()};
|
||||
}
|
||||
} catch (const std::exception&) {
|
||||
// A bad pointer or old/corrupt generation is a cache miss.
|
||||
}
|
||||
if (cached_generation) {
|
||||
source_unchanged();
|
||||
checkpoint(job, "Reusing verified build generation", .68);
|
||||
log(job, "Verified schema/package cache hit: " + cached_generation->first + "\n");
|
||||
return result_for(cached_generation->first, cached_generation->second, true);
|
||||
}
|
||||
checkpoint(job, "Exporting gameplay schema", .58);
|
||||
const auto staging = config.cache_root / "builds" / (".staging-" + job.status.id);
|
||||
const auto generation = config.cache_root / "builds" / job.status.id;
|
||||
|
||||
@@ -246,11 +246,12 @@ int test_main(int argc, char** argv) {
|
||||
fs::remove(config.project_root / "fail-unparseable-build");
|
||||
atomic_write(config.project_root / "Scripts/Extensions/BuildOnly.hpp",
|
||||
"#define BUILD_ONLY 3\n");
|
||||
atomic_write(config.project_root / "mutate-cpp-header-during-build", "fixture\n");
|
||||
atomic_write(config.project_root / "mutate-cpp-snapshot-during-schema-export", "fixture\n");
|
||||
const auto raced_header = builds.wait(builds.start_build());
|
||||
check(raced_header.state == "failed" && read_text(last_build) == previous_pointer,
|
||||
"Header changed during schema export cannot publish a mixed build");
|
||||
fs::remove(config.project_root / "mutate-cpp-header-during-build");
|
||||
check(raced_header.state == "failed" && read_text(last_build) == previous_pointer &&
|
||||
raced_header.error.find("snapshot") != std::string::npos,
|
||||
"A mutated staged C++ header cannot publish a falsely keyed binary");
|
||||
fs::remove(config.project_root / "mutate-cpp-snapshot-during-schema-export");
|
||||
atomic_write(config.project_root / "Scripts/Extensions/BuildOnly.hpp",
|
||||
"#define BUILD_ONLY 1\n");
|
||||
check(!first.result.at("lua_enabled").get<bool>() &&
|
||||
|
||||
@@ -5,6 +5,17 @@
|
||||
// asynchronous BuildService and publication code without compiling a game.
|
||||
namespace fs = std::filesystem;
|
||||
using namespace faset;
|
||||
namespace {
|
||||
fs::path configured_gameplay_scripts() {
|
||||
for (const auto& argument : read_json("configure-fixture.json")) {
|
||||
const auto text = argument.get<std::string>();
|
||||
constexpr std::string_view prefix = "-DFASET_GAMEPLAY_SOURCE_DIR=";
|
||||
if (text.starts_with(prefix))
|
||||
return path_from_utf8(text.substr(prefix.size()));
|
||||
}
|
||||
throw std::runtime_error("Fixture has no configured gameplay source snapshot");
|
||||
}
|
||||
} // namespace
|
||||
int tool_main(int argc, char** argv) {
|
||||
try {
|
||||
if (argc >= 3 && std::string_view(argv[1]) == "--output") {
|
||||
@@ -24,8 +35,9 @@ int tool_main(int argc, char** argv) {
|
||||
if (fs::exists("mutate-lua-snapshot"))
|
||||
atomic_write(snapshot / "Scripts/main.lua", "-- corrupt snapshot\n");
|
||||
}
|
||||
if (fs::exists("mutate-cpp-header-during-build"))
|
||||
atomic_write("Scripts/Extensions/BuildOnly.hpp", "#define BUILD_ONLY 2\n");
|
||||
if (fs::exists("mutate-cpp-snapshot-during-schema-export"))
|
||||
atomic_write(configured_gameplay_scripts() / "Extensions/BuildOnly.hpp",
|
||||
"#define BUILD_ONLY 2\n");
|
||||
atomic_write_json(path_from_utf8(argv[2]), read_json("schema-fixture.json"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user