ci: the Flatpak workflow, local deps for the local build, the lens tools
- .forgejo/workflows/ci.yaml is Crafter.Build's workflow for this app: the base installed from the Catcrafts repo, the version stamped into the crafter-review module, the suite inside the build, then a smoke test of the app as it ships - the version and home, every library resolved and no RUNPATH outside /app, the bundled tools, a freeze and a dry run on a scratch repository. Publishing holds /srv/flatpak/.publish.lock and writes only this app's flatpakref. Unverified until a crafter-flatpak runner serves this repository. - `run flatpak --deps=local` builds against the Crafter.Network and Crafter.Thread checkouts next to this one, as a native --local build does, while their commits are not on forgejo: a gitignored manifest copy with three dir sources side by side and --local. - LensTools: every program the lens allowlist names, the shell and the MCP server's node and npx resolve on PATH; strict in the Flatpak build, where the suite runs in the sandbox that ships, a skip natively when this machine lacks one. Built on the stable base (Crafter.Build 3.0.0) with --deps=local: the suite passes inside flatpak-builder (25 passed, 2 skipped), and the workflow's smoke test passes against that build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7eccfd3b45
commit
5dab4b49d5
3 changed files with 373 additions and 17 deletions
74
project.cpp
74
project.cpp
|
|
@ -84,7 +84,7 @@ namespace {
|
|||
|
||||
// One per directory under tests/, each an ordinary main() that links the
|
||||
// library: exit 0 passes, nonzero fails, 77 skips.
|
||||
constexpr std::array<std::string_view, 25> Tests = {
|
||||
constexpr std::array<std::string_view, 26> Tests = {
|
||||
"Crypto",
|
||||
"Text",
|
||||
"Platform",
|
||||
|
|
@ -110,6 +110,7 @@ namespace {
|
|||
"ReviewFixes",
|
||||
"Runs",
|
||||
"Commands",
|
||||
"LensTools",
|
||||
};
|
||||
|
||||
// The version the binary reports: CI sets it from the tag, a checkout
|
||||
|
|
@ -129,7 +130,9 @@ namespace {
|
|||
// Crafter.Build's own scripts do. --base=<branch> builds on another branch
|
||||
// of the Crafter.Build base app than stable: until a Crafter.Build release
|
||||
// with scripts is on stable, `--base=local` (the branch Crafter.Build's own
|
||||
// `run install` makes) is what builds this project.
|
||||
// `run install` makes) is what builds this project. --deps=local takes
|
||||
// Crafter.Network and Crafter.Thread from the checkouts next to this one,
|
||||
// as a native --local build does, for commits not on forgejo yet.
|
||||
struct FlatpakPaths {
|
||||
fs::path manifest;
|
||||
// flatpak-builder's downloads, ccache and per-module cache.
|
||||
|
|
@ -142,14 +145,15 @@ namespace {
|
|||
// Everything under <project>/build/flatpak, a fixed name rather than a
|
||||
// BuildDir(): the Flatpak is compiled by the toolchain inside the sandbox,
|
||||
// not by the host variant. flatpak-builder keys its cache on the
|
||||
// manifest's absolute path, so a branch other than stable gets a manifest
|
||||
// copy of its own next to the original (gitignored), and with it a cache
|
||||
// of its own.
|
||||
FlatpakPaths FlatpakLayout(const fs::path& projectDir, std::string_view base) {
|
||||
// manifest's absolute path, so a branch other than stable, or local deps,
|
||||
// get a manifest copy of their own next to the original (gitignored), and
|
||||
// with it a cache of their own.
|
||||
FlatpakPaths FlatpakLayout(const fs::path& projectDir, std::string_view base, bool localDeps = false) {
|
||||
const fs::path root = projectDir / "build" / "flatpak";
|
||||
const fs::path packaging = projectDir / "packaging" / "flatpak";
|
||||
const bool original = base == "stable" && !localDeps;
|
||||
return {
|
||||
.manifest = base == "stable" ? packaging / "net.catcrafts.CrafterReview.yaml" : packaging / std::format("{}-base.yaml", base),
|
||||
.manifest = original ? packaging / "net.catcrafts.CrafterReview.yaml" : packaging / std::format("{}{}-base.yaml", base, localDeps ? "-localdeps" : ""),
|
||||
.state = root / "state",
|
||||
.repo = root / "repo",
|
||||
.buildDir = root / "build-dir",
|
||||
|
|
@ -187,18 +191,52 @@ namespace {
|
|||
return url;
|
||||
}
|
||||
|
||||
// `old` replaced by `replacement` once in `text`; false when it is not
|
||||
// there.
|
||||
bool ReplaceOnce(std::string& text, std::string_view old, std::string_view replacement) {
|
||||
const std::size_t at = text.find(old);
|
||||
if (at == std::string::npos) return false;
|
||||
text.replace(at, old.size(), replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The crafter-review module built from three dir sources side by side,
|
||||
// so --local finds ../Crafter.Network and ../Crafter.Thread.
|
||||
bool WithLocalDeps(std::string& manifest) {
|
||||
constexpr std::string_view Source = R"( - type: dir
|
||||
path: ../..
|
||||
skip:
|
||||
- .git
|
||||
- bin
|
||||
- build
|
||||
)";
|
||||
std::string sources;
|
||||
for (const std::string_view project : {"Crafter.Review", "Crafter.Network", "Crafter.Thread"}) {
|
||||
const std::string path = project == "Crafter.Review" ? std::string("../..") : std::format("../../../{}", project);
|
||||
sources += std::format(" - type: dir\n path: {}\n dest: {}\n skip:\n - .git\n - bin\n - build\n", path, project);
|
||||
}
|
||||
return ReplaceOnce(manifest, " - name: crafter-review\n", " - name: crafter-review\n subdir: Crafter.Review\n")
|
||||
&& ReplaceOnce(manifest, " - crafter-build\n - crafter-build test\n", " - crafter-build --local\n - crafter-build --local test\n")
|
||||
&& ReplaceOnce(manifest, Source, sources);
|
||||
}
|
||||
|
||||
std::int32_t BuildFlatpak(ScriptContext& ctx) {
|
||||
if (RefusedInSandbox("flatpak")) return 1;
|
||||
std::string jobs;
|
||||
std::string base = "stable";
|
||||
bool localDeps = false;
|
||||
for (const std::string& arg : ctx.args) {
|
||||
if (arg.starts_with("--base=") && arg.size() > 7) {
|
||||
base = arg.substr(7);
|
||||
continue;
|
||||
}
|
||||
if (arg == "--deps=local") {
|
||||
localDeps = true;
|
||||
continue;
|
||||
}
|
||||
const std::string_view count = arg.starts_with("--jobs=") ? std::string_view(arg).substr(7) : std::string_view();
|
||||
if (count.empty() || !std::ranges::all_of(count, [](char c) { return c >= '0' && c <= '9'; })) {
|
||||
std::println(std::cerr, "run flatpak: unknown argument '{}' (only --jobs=<N> and --base=<branch> are accepted)", arg);
|
||||
std::println(std::cerr, "run flatpak: unknown argument '{}' (only --jobs=<N>, --base=<branch> and --deps=local are accepted)", arg);
|
||||
return 1;
|
||||
}
|
||||
jobs = std::format(" --jobs={}", count);
|
||||
|
|
@ -216,18 +254,20 @@ namespace {
|
|||
std::println(std::cerr, "run flatpak: neither flatpak-builder nor the org.flatpak.Builder app is installed (flatpak install flathub org.flatpak.Builder)");
|
||||
return 1;
|
||||
}
|
||||
const FlatpakPaths paths = FlatpakLayout(ctx.projectDir, base);
|
||||
if (base != "stable") {
|
||||
// The manifest with only base-version changed.
|
||||
const FlatpakPaths paths = FlatpakLayout(ctx.projectDir, base, localDeps);
|
||||
if (base != "stable" || localDeps) {
|
||||
// The manifest with base-version changed, and the local deps.
|
||||
std::ifstream in(FlatpakLayout(ctx.projectDir, "stable").manifest);
|
||||
const std::string manifest((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
constexpr std::string_view Line = "base-version: stable\n";
|
||||
const std::size_t at = manifest.find(Line);
|
||||
if (at == std::string::npos) {
|
||||
std::println(std::cerr, "run flatpak: the manifest has no `{}` line to replace", Line.substr(0, Line.size() - 1));
|
||||
std::string manifest((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
if (!ReplaceOnce(manifest, "base-version: stable\n", std::format("base-version: {}\n", base))) {
|
||||
std::println(std::cerr, "run flatpak: the manifest has no `base-version: stable` line to replace");
|
||||
return 1;
|
||||
}
|
||||
std::ofstream(paths.manifest) << manifest.substr(0, at) << std::format("base-version: {}\n", base) << manifest.substr(at + Line.size());
|
||||
if (localDeps && !WithLocalDeps(manifest)) {
|
||||
std::println(std::cerr, "run flatpak: the crafter-review module is not in the shape --deps=local rewrites (its dir source and its two crafter-build lines)");
|
||||
return 1;
|
||||
}
|
||||
std::ofstream(paths.manifest) << manifest;
|
||||
}
|
||||
const std::string buildDir = ShellQuote(paths.buildDir.string());
|
||||
if (const std::int32_t code = ctx.Run(std::format("{}{} --user --force-clean --ccache --state-dir={} --repo={} --default-branch=local {} {}", builder, jobs, ShellQuote(paths.state.string()), ShellQuote(paths.repo.string()), buildDir, ShellQuote(paths.manifest.string()))); code != 0) return code;
|
||||
|
|
|
|||
Loading…
Reference in a new issue