Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/homebrew-publishing.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,12 @@ those classes only through the canonical support require and a literal
changed metadata or predicates, and `:test`-only native Requirements fail
closed.

The bottle source-closure layer recognizes those literal Requirement lines as
references to the already-bound Formula support tree; it does not treat every
additional `KandeloFormulaSupport` token as a second source loader. Its line
allowlist rejects other module references, while the Ripper-based Formula
parser remains authoritative for the closed class and tag allowlists.

The static Formula parser recognizes that exact source shape without
evaluating Formula Ruby. Schema 4 of the protected host-dependency plan binds
the Requirement class, native Formula identity, sentinel executable, and
Expand Down
23 changes: 20 additions & 3 deletions scripts/homebrew-oci-layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@
COMMIT = re.compile(r"^[0-9a-f]{40}$")
TAP_REPOSITORY = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$")
CANONICAL_UINT = re.compile(r"^(0|[1-9][0-9]*)$")
# WHY: source closure needs to recognize references into the already-bound
# support tree without deciding which Requirements are trusted. The Ripper
# parser remains authoritative for AST placement, class names, and tags.
NATIVE_REQUIREMENT_REFERENCE = re.compile(
r"^ depends_on KandeloFormulaSupport::[A-Z][A-Za-z0-9]*Requirement"
r" => (:[a-z]+|\[(?::[a-z]+)(?:, :[a-z]+)*\])$"
)
OCI_REMOTE = re.compile(
r"^ghcr\.io/[a-z0-9][a-z0-9._-]*/[a-z0-9][a-z0-9._-]*/[a-z0-9][a-z0-9._-]*$"
)
Expand Down Expand Up @@ -442,15 +449,25 @@ def source_closure(
marker = "Kandelo/formula_support/kandelo_formula_support"
entries: list[dict[str, Any]] = []
if marker in formula_source:
if formula_source.splitlines().count(require_line) != 1:
formula_lines = formula_source.splitlines()
if formula_lines.count(require_line) != 1:
fail("Formula support require is not canonical")
support_reference_lines = [
line for line in formula_lines if "KandeloFormulaSupport" in line
]
allowed_support_references = [
line
for line in support_reference_lines
if line == " include KandeloFormulaSupport"
or NATIVE_REQUIREMENT_REFERENCE.fullmatch(line) is not None
]
if (
formula_source.count("Tap.fetch") != 1
or formula_source.count("KandeloFormulaSupport") != 1
or "require_relative" in formula_source
or allowed_support_references != support_reference_lines
):
fail("Formula support reference is not a bounded canonical closure")
if formula_source.splitlines().count(" include KandeloFormulaSupport") != 1:
if formula_lines.count(" include KandeloFormulaSupport") != 1:
fail("Formula support include is not canonical")
support_root = real_directory(
tap_root / "Kandelo/formula_support", "Formula support source root"
Expand Down
7 changes: 5 additions & 2 deletions scripts/homebrew-validate-formula-source-closure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,13 @@ if grep -Fq "$support_marker" "$BASE_FORMULA"; then
exit 1
fi
tap_fetch_count="$({ grep -Fo 'Tap.fetch' "$BASE_FORMULA" || true; } | wc -l | tr -d '[:space:]')"
support_constant_count="$({ grep -Fo 'KandeloFormulaSupport' "$BASE_FORMULA" || true; } | wc -l | tr -d '[:space:]')"
# WHY: canonical publisher-only Requirement declarations also name
# KandeloFormulaSupport. The source-closure generator below owns the exact
# line allowlist, and the Ripper-based runtime-closure parser owns the
# Requirement class and tag allowlist. Counting the module token here would
# reject those reviewed static declarations without adding source authority.
if [ "$tap_fetch_count" != "1" ] || \
[ "$(grep -Fxc ' include KandeloFormulaSupport' "$BASE_FORMULA" || true)" != "1" ] || \
[ "$support_constant_count" != "1" ] || \
grep -Fq 'require_relative' "$BASE_FORMULA"; then
echo "homebrew-validate-formula-source-closure.sh: Formula has an unsupported local source reference" >&2
exit 1
Expand Down
16 changes: 16 additions & 0 deletions scripts/test-homebrew-oci-layout.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ make_fixture() {
require (Tap.fetch("$(printf '%s' "$tap_owner" | tr '[:upper:]' '[:lower:]')", "$(printf '%s' "$tap_short_name" | tr '[:upper:]' '[:lower:]')").path/"Kandelo/formula_support/kandelo_formula_support").to_s

class Hello < Formula
depends_on KandeloFormulaSupport::BinaryenRequirement => :build
depends_on KandeloFormulaSupport::WabtRequirement => [:build, :test]
include KandeloFormulaSupport
desc "OCI fixture"
end
Expand Down Expand Up @@ -329,6 +331,20 @@ source_closure_args=(
)
python3 "$TOOL" "${source_closure_args[@]}" \
--out "$TMP_ROOT/source-closure-baseline.json"
cp "$source_closure_root/Formula/hello.rb" \
"$TMP_ROOT/source-closure-canonical-requirement.rb"
sed -i.bak \
's/KandeloFormulaSupport::WabtRequirement/KandeloFormulaSupport.const_get("WabtRequirement")/' \
"$source_closure_root/Formula/hello.rb"
rm "$source_closure_root/Formula/hello.rb.bak"
if python3 "$TOOL" "${source_closure_args[@]}" \
--out "$TMP_ROOT/source-closure-with-dynamic-requirement.json" \
>/dev/null 2>&1; then
echo "Formula support source closure accepted a dynamic Requirement reference" >&2
exit 1
fi
mv "$TMP_ROOT/source-closure-canonical-requirement.rb" \
"$source_closure_root/Formula/hello.rb"
mkdir -p "$source_closure_root/Kandelo/formula_support/test"
printf 'test-only-v1\n' \
>"$source_closure_root/Kandelo/formula_support/test/support_test.rb"
Expand Down
55 changes: 55 additions & 0 deletions scripts/test-homebrew-publish-workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6603,9 +6603,40 @@ class Escape < Formula
require_relative "../Kandelo/other"
end
end
EOF
cat >"$tap/Formula/native-support.rb" <<'EOF'
require (Tap.fetch("kandelo-dev", "tap-core").path/"Kandelo/formula_support/kandelo_formula_support").to_s

class NativeSupport < Formula
depends_on KandeloFormulaSupport::BinaryenRequirement => :build
depends_on KandeloFormulaSupport::WabtRequirement => [:build, :test]
include KandeloFormulaSupport
end
EOF
cat >"$tap/Formula/dynamic-support.rb" <<'EOF'
require (Tap.fetch("kandelo-dev", "tap-core").path/"Kandelo/formula_support/kandelo_formula_support").to_s

class DynamicSupport < Formula
depends_on KandeloFormulaSupport.const_get("WabtRequirement") => [:build, :test]
include KandeloFormulaSupport
end
EOF
write_canonical_formula_support \
"$tap/Kandelo/formula_support/kandelo_formula_support.rb" <<'EOF'
class BinaryenRequirement < Requirement
KANDELO_NATIVE_FORMULA = "binaryen"
KANDELO_NATIVE_SENTINEL = "wasm-opt"
fatal true
satisfy(build_env: false) { which("wasm-opt") }
end

class WabtRequirement < Requirement
KANDELO_NATIVE_FORMULA = "wabt"
KANDELO_NATIVE_SENTINEL = "wasm-validate"
fatal true
satisfy(build_env: false) { which("wasm-validate") }
end

def kandelo_runner_command
runner = Pathname(__dir__)/"run-network-wasm.ts"
command = +""
Expand Down Expand Up @@ -6659,6 +6690,30 @@ EOF
--base-ref "$base" \
--reviewed-tap-root "$reviewed" >/dev/null

# Canonical native Requirement declarations name the support module without
# loading another local source. Both closure validators must accept that
# static reference while the Formula parser retains semantic authority.
bash "$REPO_ROOT/scripts/homebrew-validate-formula-source-closure.sh" \
--tap-root "$tap" \
--tap-repository kandelo-dev/homebrew-tap-core \
--formula native-support \
--base-ref "$base" >/dev/null
bash "$REPO_ROOT/scripts/homebrew-validate-formula-source-closure.sh" \
--tap-root "$tap" \
--tap-repository kandelo-dev/homebrew-tap-core \
--formula native-support \
--base-ref "$base" \
--reviewed-tap-root "$reviewed" >/dev/null
if bash "$REPO_ROOT/scripts/homebrew-validate-formula-source-closure.sh" \
--tap-root "$tap" \
--tap-repository kandelo-dev/homebrew-tap-core \
--formula dynamic-support \
--base-ref "$base" >/dev/null 2>"$err"; then
fail "Formula source-closure validator accepted a dynamic Requirement reference"
fi
grep -F "Formula support reference is not a bounded canonical closure" "$err" >/dev/null ||
fail "Formula source-closure validator did not explain the dynamic Requirement reference"

printf 'test-only-v2\n' \
>"$tap/Kandelo/formula_support/test/kandelo_formula_support_test.rb"
git -C "$tap" add Kandelo/formula_support/test/kandelo_formula_support_test.rb
Expand Down
Loading