build(flatpak): net.catcrafts.CrafterReview on the Crafter.Build base

The manifest builds on the Crafter.Build Flatpak (base:
net.catcrafts.CrafterBuild): its crafter-build, LLVM 23 and libc++
compile this project inside flatpak-builder, and crafter-build test runs
there too. Node 24.22.0 (for the Playwright MCP server) and a static
ripgrep 15.2.0 are bundled, pinned by sha256; claude is not, being
proprietary. finish-args: the network, the host's files and /tmp, and
gnupg's keyboxd socket only, never the agent.

The base's files belong to no module of this manifest, so `cleanup` does
not reach them: prune-base.sh keeps a named list (the binary, Node, rg,
the libc++ the binary links, share/crafter-review, this app's metainfo
and licences) and removes the rest, whatever a newer base adds.

`crafter-build run flatpak [--base=<branch>] [--jobs=N]` builds it and
smoke-tests --version and ldd through `flatpak build`; `run install`
installs it as the `local` branch. Until a Crafter.Build release with
scripts is on stable, `--base=local` builds against Crafter.Build's own
local install, from a gitignored manifest copy whose cache is its own.
Built that way: 4 tests green in the sandbox, the charter sha256s the
native build prints, no library missing, RUNPATH /app/lib only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-10-31 06:01:19 +01:00
commit b02de3ceff
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
6 changed files with 334 additions and 1 deletions

View file

@ -0,0 +1,13 @@
[Flatpak Ref]
Title=Crafter Review
Name=net.catcrafts.CrafterReview
Branch=stable
Url=https://flatpak.catcrafts.net/repo/
SuggestRemoteName=catcrafts
Homepage=https://forgejo.catcrafts.net/Catcrafts/Crafter.Review
Comment=Adversarial pre-push review of a frozen commit range
Description=crafter-review with its own Node and ripgrep, built on the Crafter.Build toolchain
Icon=
RuntimeRepo=https://dl.flathub.org/repo/flathub.flatpakrepo
IsRuntime=false
GPGKey=GPG_KEY_BASE64_PLACEHOLDER

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- SPDX-License-Identifier: CC0-1.0 -->
<component type="console-application">
<id>net.catcrafts.CrafterReview</id>
<name>Crafter Review</name>
<summary>Adversarial pre-push review of a frozen commit range</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>AGPL-3.0-only</project_license>
<developer id="net.catcrafts">
<name>Catcrafts</name>
</developer>
<description>
<p>
The pre-push review pipeline: crafter-review freezes a commit range,
partitions it into units, and runs adversarial lenses over it — the
approach, the running application through the Playwright MCP server,
a bug proof and a security review — one claude process per unit, on the
seats the repository provides. A range is send-ready only when every
gate passes on the exact frozen hash.
</p>
</description>
<url type="homepage">https://forgejo.catcrafts.net/Catcrafts/Crafter.Review</url>
<url type="bugtracker">https://forgejo.catcrafts.net/Catcrafts/Crafter.Review/issues</url>
<url type="vcs-browser">https://forgejo.catcrafts.net/Catcrafts/Crafter.Review</url>
<provides>
<binary>crafter-review</binary>
</provides>
<content_rating type="oars-1.1"/>
<releases>
<release version="1.0.0" date="2026-10-31">
<description>
<p>First release: the pre-push review gates in C++26, shipped as a Flatpak.</p>
</description>
</release>
</releases>
</component>

View file

@ -0,0 +1,100 @@
# SPDX-License-Identifier: AGPL-3.0-only
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
#
# crafter-review as a Flatpak. The runtime is the Freedesktop *Sdk*, as
# Crafter.Build's is, not for a compiler but for what a lens runs through its
# Bash tool: git, python3 for `python3 -` heredocs, curl, jq and coreutils.
# Node (for the Playwright MCP server) and ripgrep (`Bash(rg:*)`, which the Sdk
# lacks) are bundled. claude is not: it is a proprietary binary, found in the
# host's home where its installers put it, and run inside the sandbox like
# everything else, so the memory sampler sees every pass and a pass's whole
# tree can be killed.
#
# The base app is the Crafter.Build Flatpak: its crafter-build, LLVM 23 and
# libc++ build this project inside flatpak-builder, so no second LLVM is
# built. Everything of it but the libc++ the binary links is pruned at the
# end (cleanup-commands below); bump `runtime-version` together with the
# base's.
app-id: net.catcrafts.CrafterReview
runtime: org.freedesktop.Sdk
runtime-version: '26.08'
sdk: org.freedesktop.Sdk
base: net.catcrafts.CrafterBuild
base-version: stable
command: crafter-review
finish-args:
# claude to the API, npx to the npm registry, the local stacks on the
# host's loopback, and the driver's SQL broker on 127.0.0.1
- --share=network
# repositories, ~/.claude, ~/.cache/review-gates, ~/.cache/ms-playwright,
# ~/.npm: anywhere a developer keeps them. A convenience for a developer
# tool, not an isolation boundary.
- --filesystem=host
# The sandbox's own /tmp is private; this makes /tmp the host's, so a
# repository, a config or an artifact under /tmp works as it does natively.
- --filesystem=/tmp
# freeze reads `%G?`; gnupg with use-keyboxd keeps its public keys behind
# this socket. Only this one: the agent, which signs, stays out of reach.
- --filesystem=xdg-run/gnupg/S.keyboxd
- --env=PATH=/app/bin:/usr/bin
modules:
- name: ripgrep
buildsystem: simple
build-commands:
- install -Dm755 rg /app/bin/rg
sources:
- type: archive
url: https://github.com/BurntSushi/ripgrep/releases/download/15.2.0/ripgrep-15.2.0-x86_64-unknown-linux-musl.tar.gz
sha256: 33e15bcf1624b25cdd2a55813a47a2f95dbe126268203e76aa6a585d1e7b149c
- name: node
# For the Playwright MCP server, which the claude CLI starts through
# `npx`. Unpacked whole, with links in /app/bin.
buildsystem: simple
build-commands:
- mkdir -p /app/lib/node /app/bin
- cp -a bin include lib share /app/lib/node/
- ln -sf ../lib/node/bin/node /app/bin/node
- ln -sf ../lib/node/bin/npm /app/bin/npm
- ln -sf ../lib/node/bin/npx /app/bin/npx
sources:
- type: archive
url: https://nodejs.org/dist/v24.22.0/node-v24.22.0-linux-x64.tar.xz
sha256: cc2f601cee82d9a5a233302fe307abc600727035fd7d2c3a3571755989e95008
- name: crafter-review
buildsystem: simple
build-options:
# crafter-build fetches Crafter.Network, Crafter.Thread and
# Crafter.LintRules from git, each at the commit project.cpp pins.
build-args:
- --share=network
# On this module, not at the top: top-level build options are part of
# every module's cache key, and the version changes with every commit.
env:
# The shipped binary runs on any x86_64 from the last decade.
CRAFTER_BUILD_MARCH: x86-64-v2
CRAFTER_BUILD_MTUNE: generic
# CI replaces this with the version; a local build is a development
# build.
CRAFTER_REVIEW_VERSION: 0.0.0-dev
build-commands:
- crafter-build
- crafter-build test
- install -Dm755 bin/crafter.review-exe-*/crafter-review /app/bin/crafter-review
- mkdir -p /app/share/crafter-review && cp -r process /app/share/crafter-review/
- install -Dm644 packaging/flatpak/prune-base.sh /app/share/crafter-review/prune-base.sh
- install -Dm644 LICENSE -t /app/share/licenses/net.catcrafts.CrafterReview/
- install -Dm644 packaging/flatpak/net.catcrafts.CrafterReview.metainfo.xml -t /app/share/metainfo/
sources:
- type: dir
path: ../..
skip:
- .git
- bin
- build
# The base's files are no module's of this manifest, so `cleanup` patterns do
# not reach them: the prune keeps a named list and removes the rest.
cleanup-commands:
- sh /app/share/crafter-review/prune-base.sh

View file

@ -0,0 +1,41 @@
#!/bin/sh
# SPDX-License-Identifier: AGPL-3.0-only
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
#
# The base app, net.catcrafts.CrafterBuild, brings Crafter.Build's whole
# toolchain into /app so this project can be built inside flatpak-builder.
# The shipped app keeps what crafter-review runs on and nothing else: a list
# of what stays rather than of what goes, so whatever a newer base adds goes
# without anyone having to know about it.
set -eu
cd /app
keep() {
case "$1" in
bin | bin/crafter-review | bin/node | bin/npm | bin/npx | bin/rg) return 0 ;;
lib | lib/node | lib/node/*) return 0 ;;
# What a binary built by the base's crafter-build links through its
# /app/lib rpath.
lib/libc++.so.1* | lib/libc++abi.so.1* | lib/libunwind.so.1*) return 0 ;;
# Split into the .Debug extension at export.
lib/debug | lib/debug/*) return 0 ;;
share | share/crafter-review | share/crafter-review/*) return 0 ;;
# Named, not whole: the base's CrafterBuild metainfo and licences go.
share/metainfo | share/metainfo/net.catcrafts.CrafterReview.metainfo.xml) return 0 ;;
share/licenses | share/licenses/net.catcrafts.CrafterReview | share/licenses/net.catcrafts.CrafterReview/*) return 0 ;;
manifest*.json) return 0 ;;
esac
return 1
}
# Depth first: a directory comes after its contents, and goes only once it
# is empty.
find . -mindepth 1 -depth | sed 's|^\./||' | while IFS= read -r path; do
keep "$path" && continue
if [ -d "$path" ] && [ ! -L "$path" ]; then
rmdir "$path" 2>/dev/null || true
else
rm -f "$path"
fi
done
rm -f share/crafter-review/prune-base.sh