From 1c0eb6c668c1ab2fef2a960fac1bb24dc67ae118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=E2=88=86I=20Platform?= Date: Sat, 30 May 2026 00:46:49 +0200 Subject: [PATCH] feat: seed Homebrew formula + auto-update workflow Mirror of installer/homebrew/fai.rb in fai/platform. Source of truth is the platform repo; this is a passive distribution mirror. Auto-update workflow at .forgejo/workflows/update.yml runs every 4 hours, diffs the latest platform release against Formula/fai.rb, and rewrites version + sha256 when a new version ships. Manual trigger via workflow_dispatch. Users install with: brew tap fai/tap https://git.flemming.ai/fai/homebrew-tap brew install fai End-state matches Phase 1 of docs/releases/go-live-checklist.md. --- .forgejo/workflows/update.yml | 133 ++++++++++++++++++++++++++++++++++ Formula/fai.rb | 78 ++++++++++++++++++++ README.md | 25 +++++++ 3 files changed, 236 insertions(+) create mode 100644 .forgejo/workflows/update.yml create mode 100644 Formula/fai.rb create mode 100644 README.md diff --git a/.forgejo/workflows/update.yml b/.forgejo/workflows/update.yml new file mode 100644 index 0000000..c76505d --- /dev/null +++ b/.forgejo/workflows/update.yml @@ -0,0 +1,133 @@ +# Auto-update workflow for the fai/homebrew-tap repo. +# +# Lives here as the canonical source of truth. The bootstrap +# script (scripts/bootstrap-distribution-repos.sh in +# fai/platform) seeds this file into the tap repo at +# .forgejo/workflows/update.yml. Edit it here, run the +# bootstrap script, the tap repo picks up the new version. +# +# What it does: +# * Every 4 hours, fetches the latest release manifest for +# fai/platform. +# * If the version differs from the one currently in +# Formula/fai.rb, rewrites the formula's version + sha256 +# entries (macos-aarch64 + macos-x86_64) and commits. +# * Workflow_dispatch trigger lets you force an update from +# the Forgejo UI without waiting for the cron tick. +# +# Manifest contract: fai/platform's release.yml writes +# .binaries.{macos-aarch64,macos-x86_64}.sha256 — see +# crates/fai_hub/src/update.rs::ReleaseManifest for the +# authoritative schema. + +name: Update Homebrew formula +on: + schedule: + - cron: '0 */4 * * *' + workflow_dispatch: {} + +jobs: + update: + name: Sync formula to latest platform release + runs-on: ubuntu-latest + steps: + - name: Checkout via external URL + run: | + set -eu + mkdir -p "$GITHUB_WORKSPACE" + cd "$GITHUB_WORKSPACE" + git init -q + git remote add origin \ + "https://x-access-token:${GITHUB_TOKEN}@git.flemming.ai/${GITHUB_REPOSITORY}.git" + git fetch --depth=1 origin "$GITHUB_REF_NAME" + git checkout -q FETCH_HEAD + + - name: Install jq + curl + run: | + apt-get update -qq + apt-get install -y --no-install-recommends jq curl ca-certificates + + - name: Fetch latest manifest + id: manifest + run: | + set -eu + # Forgejo's releases API returns newest-first. + REL=$(curl -fsSL \ + "https://git.flemming.ai/api/v1/repos/fai/platform/releases?limit=1" \ + | jq -r '.[0]') + TAG=$(echo "$REL" | jq -r '.tag_name') + VERSION="${TAG#v}" + MANIFEST=$(curl -fsSL \ + "https://git.flemming.ai/fai/platform/releases/download/${TAG}/manifest.json") + MAC_ARM=$(echo "$MANIFEST" | jq -r '.binaries["macos-aarch64"].sha256 // empty') + MAC_X64=$(echo "$MANIFEST" | jq -r '.binaries["macos-x86_64"].sha256 // empty') + if [ -z "$MAC_ARM" ] || [ -z "$MAC_X64" ]; then + echo "manifest for $TAG has no macOS entries yet — likely the local" + echo "publish step hasn't run. Skipping update." + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "sha_aarch64=$MAC_ARM" >> "$GITHUB_OUTPUT" + echo "sha_x86_64=$MAC_X64" >> "$GITHUB_OUTPUT" + + - name: Detect formula version + id: current + if: steps.manifest.outputs.skip != 'true' + run: | + CUR=$(grep -E '^\s*version\s+"' Formula/fai.rb \ + | head -1 | sed -E 's/.*"([^"]+)".*/\1/') + echo "version=$CUR" >> "$GITHUB_OUTPUT" + + - name: Rewrite Formula/fai.rb + id: rewrite + if: | + steps.manifest.outputs.skip != 'true' + && steps.manifest.outputs.version != steps.current.outputs.version + env: + NEW_VERSION: ${{ steps.manifest.outputs.version }} + NEW_SHA_AARCH64: ${{ steps.manifest.outputs.sha_aarch64 }} + NEW_SHA_X86_64: ${{ steps.manifest.outputs.sha_x86_64 }} + run: | + set -eu + python3 - <<'PY' + import os, re, pathlib + p = pathlib.Path("Formula/fai.rb") + src = p.read_text() + new_version = os.environ["NEW_VERSION"] + arm_sha = os.environ["NEW_SHA_AARCH64"] + x64_sha = os.environ["NEW_SHA_X86_64"] + # version line + src = re.sub(r'(\bversion\s+)"[^"]+"', + f'\\1"{new_version}"', src, count=1) + # find the two sha256 lines, one in the arm branch, one in + # the else branch. The formula's structure is fixed — + # aarch64 block comes first. + lines = src.splitlines() + replaced = 0 + for i, line in enumerate(lines): + m = re.match(r'(\s*sha256\s+)"[^"]+"', line) + if not m: + continue + sha = arm_sha if replaced == 0 else x64_sha + lines[i] = f'{m.group(1)}"{sha}"' + replaced += 1 + if replaced == 2: + break + if replaced != 2: + raise SystemExit(f"expected 2 sha256 lines, rewrote {replaced}") + p.write_text("\n".join(lines) + "\n") + PY + echo "rewritten=true" >> "$GITHUB_OUTPUT" + + - name: Commit + push + if: steps.rewrite.outputs.rewritten == 'true' + env: + NEW_VERSION: ${{ steps.manifest.outputs.version }} + run: | + set -eu + git config user.email "platform@flemming.ai" + git config user.name "fai-tap-bot" + git add Formula/fai.rb + git commit -s -m "chore: formula -> v${NEW_VERSION}" + git push origin "HEAD:${GITHUB_REF_NAME}" diff --git a/Formula/fai.rb b/Formula/fai.rb new file mode 100644 index 0000000..5b13865 --- /dev/null +++ b/Formula/fai.rb @@ -0,0 +1,78 @@ +# Homebrew formula skeleton for the F∆I Platform CLI. +# +# Lives in this repo as the canonical source — the actual +# distribution is a checkout of `fai/homebrew-tap` (a separate +# Forgejo repo, seeded by +# `scripts/bootstrap-distribution-repos.sh`) that carries a copy +# of this file at `Formula/fai.rb`. The tap repo's +# `.forgejo/workflows/update.yml` (sourced from +# `installer/homebrew/forgejo-workflow-update.yml` in this repo) +# auto-bumps the version + sha256 entries against each new +# platform release on a 4-hourly cron. +# +# To install a release from the tap: +# +# brew tap fai/tap https://git.flemming.ai/fai/homebrew-tap +# brew install fai +# +# To install from this checkout for testing: +# +# brew install --build-from-source ./installer/homebrew/fai.rb +# +# This file is a *skeleton*. The :url / :sha256 placeholders are +# populated by the release pipeline once macOS builds land on the +# Forgejo Release page. The class name (`Fai`) is what Homebrew +# uses to address the formula; lowercase `fai` is the binary on +# disk after install. + +class Fai < Formula + desc "Deterministic workflow engine for AI-assisted data " \ + "processing in regulated environments" + homepage "https://flemming.ai" + version "0.12.1" + license "Apache-2.0" + + # Platform-specific binary URLs + sha256 are filled in per + # release. cargo-zigbuild-built linux artefacts come from the + # same release; the formula consumes them on Apple Silicon and + # Intel macs via stanza selectors. + if Hardware::CPU.arm? + url "https://git.flemming.ai/fai/platform/releases/download/" \ + "v#{version}/fai-macos-aarch64" + sha256 "REPLACE_WITH_AARCH64_SHA256_FROM_MANIFEST" + else + url "https://git.flemming.ai/fai/platform/releases/download/" \ + "v#{version}/fai-macos-x86_64" + sha256 "REPLACE_WITH_X86_64_SHA256_FROM_MANIFEST" + end + + # Plain binary — no source build needed. We rely on the upstream + # CI to have produced + signed it. Notarization is a separate + # follow-up. + def install + bin.install Dir["*"].first => "fai" + end + + # `brew test fai` runs this — keeps the formula honest. We don't + # start the daemon; we only verify the binary is callable. + test do + assert_match version.to_s, shell_output("#{bin}/fai --version") + end + + def caveats + <<~EOS + The F∆I daemon is not started automatically. After install: + + fai bootstrap # writes ~/.fai/ layout + PATH marker + fai daemon start # starts the local hub + fai install debug.echo # smoke-test the install path + + For Studio, install separately from + https://flemming.ai/get. + + Note: this binary is not yet notarized. macOS Gatekeeper will + ask for confirmation on first launch. The notarization step + is tracked as the remaining half of system-gap S-16. + EOS + end +end diff --git a/README.md b/README.md new file mode 100644 index 0000000..87e54e6 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# homebrew-tap + +Distribution mirror for the [F∆I Platform](https://git.flemming.ai/fai/platform). + +This repo holds one file — `Formula/fai.rb` — which is a copy of +[`installer/homebrew/fai.rb`](https://git.flemming.ai/fai/platform/src/branch/main/installer/homebrew/fai.rb) +in the platform repo. The platform repo is the source of +truth; this repo exists as a separate target so end users can +add it as a tap / bucket / channel without subscribing to +the whole platform tree. + +Updates land here automatically via +`.forgejo/workflows/update.yml` — a 4-hourly cron that +diffs the latest platform release against this repo's +manifest and rewrites it when a new version ships. To force +an immediate update, trigger the **Workflow_dispatch** action +in the Forgejo UI. + +When the workflow itself or the underlying manifest format +changes, re-seed from the platform repo by running +`scripts/bootstrap-distribution-repos.sh`. + +Issues, PRs, and ad-hoc questions belong at +`https://git.flemming.ai/fai/platform` — this repo is intentionally +quiet.