core/Makefile
Alex Dunmow 704b046727 feat(abi): wasm plugin ABI protobuf schema v1 (WO-WZ-001)
Defines the host<->guest wire contract for the .so -> wazero migration as a
repo-local buf module (abi/proto/v1, generated Go in abi/v1):

- manifest.proto: PluginManifest mirroring every static field of
  plugin.PluginRegistration (abi_version, deps, block/template registrations,
  admin pages, settings schema, theme presets, fonts, master pages, AI
  actions, RBAC method roles, CSS manifest, icon packs, directory
  extensions, job types, RAG fetcher types, settings panel, hook flags,
  core service bindings)
- invoke.proto: bn_invoke envelope, Hook catalog (RENDER_BLOCK,
  RENDER_TEMPLATE, HANDLE_HTTP, JOB, LOAD, UNLOAD, RAG_FETCH, MEDIA_HOOK,
  DESCRIBE), AbiError, and job/lifecycle/rag/media/describe payloads
- render.proto: RenderBlock/RenderTemplate payloads + RenderContext
  enumerating every ctx value from blocks/context.go (incl. BlockContext 1:1)
- http.proto: buffered HttpRequest/HttpResponse for HANDLE_HTTP
- db.proto: guest sql-driver messages (DbValue oneof over pgx-mappable
  types, query/exec/tx with host-side tx handles, SQLSTATE-carrying DbError)
- capability.proto: HostCall envelope + one request/response pair per
  CoreServices interface method (content, settings+update, gating, crypto,
  menus, datasources, users, subscriptions, media.deposit, email.send,
  ai.text_call, ai.tools.register, bridge, jobs.submit, embeddings, rag,
  reviews, badges.refresh)

The abi/ buf module is deliberately separate from the repo-root config:
proto/ is the shared block/proto submodule (service API contracts); the ABI
is SDK-internal and versions in lockstep with the guest shim. New `make abi`
target runs buf lint + generate. docs/wasm-abi.md documents the ptr+len
calling convention (bn_alloc, bn_invoke, packed u64), hook catalog, error
semantics, abi_version evolution rules, and the full registration/
CoreServices coverage tables.

Verified: `cd abi && buf lint` exit 0; `go build ./...` exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 12:47:00 +08:00

141 lines
5.2 KiB
Makefile

SDK_MODULE := git.dev.alexdunmow.com/block/core
SDK_VERSION ?= $(shell git describe --tags --abbrev=0)
SDK_DOWNSTREAM_DIRS := \
$(HOME)/src/blockninja/cms/backend \
$(HOME)/src/blockninja/orchestrator/backend \
$(wildcard $(HOME)/src/blockninja/themes/*) \
$(wildcard $(HOME)/src/blockninja/sites/*) \
$(wildcard $(HOME)/src/blockninja/plugins/*)
.PHONY: install-ninja
install-ninja:
go install ./cmd/ninja
# Regenerate Go bindings from the proto/ submodule. We narrow to
# plugin_registry.proto because other orchestrator/v1 protos (accounts.proto
# etc.) are owned by the orchestrator's generated package; registering them
# from core too would panic at startup with "file ... is already registered".
.PHONY: proto
proto:
buf generate --path proto/orchestrator/v1/plugin_registry.proto
# Lint + regenerate Go bindings for the wasm plugin ABI (repo-local buf
# module under abi/ — deliberately not part of the proto/ submodule; see
# abi/buf.yaml and docs/wasm-abi.md). Emits Go into abi/v1/.
.PHONY: abi
abi:
cd abi && buf lint && buf generate
.PHONY: update-sdk
update-sdk:
@set -e; \
for dir in $(SDK_DOWNSTREAM_DIRS); do \
if [ ! -f "$$dir/go.mod" ]; then \
echo "skip $$dir (no go.mod)"; \
continue; \
fi; \
echo "==> $$dir: $(SDK_MODULE)@$(SDK_VERSION)"; \
( \
cd "$$dir"; \
go mod edit -dropreplace=$(SDK_MODULE) 2>/dev/null || true; \
go get $(SDK_MODULE)@$(SDK_VERSION); \
go mod tidy; \
if grep -q '^replace $(SDK_MODULE)' go.mod; then \
echo "replace directive still present in $$dir/go.mod" >&2; \
exit 1; \
fi; \
); \
done
# Cut a new SDK release: tag, push, bump every downstream's pin, and commit
# + push the pin bump in each downstream.
#
# Defaults: version is auto-bumped from the last vX.Y.Z tag. Level is
# inferred from conventional-commits since that tag: BREAKING/`!:` → major,
# `feat` → minor, otherwise → patch.
#
# Overrides:
# make release # auto-bump (recommended)
# make release LEVEL=minor # force level
# make release VERSION=v1.2.3 # explicit version
.PHONY: release
release:
@set -e; \
if [ -n "$(VERSION)" ]; then \
case "$(VERSION)" in v[0-9]*.[0-9]*.[0-9]*) ;; *) echo "VERSION must look like vX.Y.Z (got $(VERSION))" >&2; exit 1 ;; esac; \
next="$(VERSION)"; \
else \
current=$$(git describe --tags --abbrev=0 --match='v[0-9]*' 2>/dev/null | sed 's/^v//'); \
if [ -z "$$current" ]; then echo "no existing vX.Y.Z tag; pass VERSION=vX.Y.Z" >&2; exit 1; fi; \
commits=$$(git log "v$$current..HEAD" --pretty=format:%s); \
if [ -z "$$commits" ]; then echo "no commits since v$$current; nothing to release" >&2; exit 1; fi; \
level=$${LEVEL:-}; \
if [ -z "$$level" ]; then \
if echo "$$commits" | grep -qE "(^|[[:space:]])(BREAKING[ -]CHANGE|[a-z]+(\([^)]+\))?!:)"; then level=major; \
elif echo "$$commits" | grep -qE "^feat(\(|:)"; then level=minor; \
else level=patch; fi; \
echo "==> auto-detected $$level bump from conventional-commits since v$$current"; \
fi; \
major=$$(echo $$current | cut -d. -f1); \
minor=$$(echo $$current | cut -d. -f2); \
patch=$$(echo $$current | cut -d. -f3); \
case "$$level" in \
patch) patch=$$((patch+1)) ;; \
minor) minor=$$((minor+1)); patch=0 ;; \
major) major=$$((major+1)); minor=0; patch=0 ;; \
*) echo "LEVEL must be patch|minor|major (got $$level)" >&2; exit 1 ;; \
esac; \
next="v$$major.$$minor.$$patch"; \
echo "==> bumping v$$current -> $$next"; \
fi; \
if ! git diff-index --quiet HEAD --; then echo "working tree dirty; commit before releasing" >&2; exit 1; fi; \
git push origin HEAD; \
git tag -a "$$next" -m "release $$next"; \
git push origin "$$next"; \
$(MAKE) update-sdk SDK_VERSION=$$next; \
$(MAKE) distribute-sdk SDK_VERSION=$$next; \
echo "==> $$next tagged, pushed, and distributed."
# Commit + push the SDK pin bump in each downstream. Surgical: only commits
# go.mod / go.sum so any unrelated WIP in the downstream is left alone (and
# unpushed). Repos without an `origin` remote are committed locally only.
.PHONY: distribute-sdk
distribute-sdk:
@set -e; \
if [ -z "$(SDK_VERSION)" ]; then echo "usage: make distribute-sdk SDK_VERSION=vX.Y.Z" >&2; exit 1; fi; \
for dir in $(SDK_DOWNSTREAM_DIRS); do \
if [ ! -f "$$dir/go.mod" ]; then continue; fi; \
( \
cd "$$dir"; \
if git diff --quiet HEAD -- go.mod go.sum; then \
exit 0; \
fi; \
echo "==> $$dir: commit + push pin bump"; \
git add -- go.mod go.sum; \
git commit -m "chore: bump core to $(SDK_VERSION)" -- go.mod go.sum >/dev/null; \
if git config --get remote.origin.url >/dev/null 2>&1; then \
git push origin HEAD 2>&1 | tail -1; \
else \
echo " (no origin remote — committed locally only)"; \
fi; \
); \
done
.PHONY: check-sdk-pins
check-sdk-pins:
@set -e; \
for dir in $(SDK_DOWNSTREAM_DIRS); do \
if [ ! -f "$$dir/go.mod" ]; then \
continue; \
fi; \
if grep -q '^replace $(SDK_MODULE)' "$$dir/go.mod"; then \
echo "replace directive found in $$dir/go.mod" >&2; \
exit 1; \
fi; \
if ! grep -Eq '^[[:space:]]*(require[[:space:]]+)?$(SDK_MODULE)[[:space:]]+v[0-9]+\.[0-9]+\.[0-9]+' "$$dir/go.mod"; then \
echo "$(SDK_MODULE) is not pinned in $$dir/go.mod" >&2; \
exit 1; \
fi; \
done