Skip to content

Generate documentation from GIR <doc> text#1

Merged
redvers merged 19 commits into
mainfrom
docs-generation
May 28, 2026
Merged

Generate documentation from GIR <doc> text#1
redvers merged 19 commits into
mainfrom
docs-generation

Conversation

@redvers
Copy link
Copy Markdown
Contributor

@redvers redvers commented May 28, 2026

Summary

  • Builds a documentation pipeline alongside the existing compile-mode generator: GIR <doc> text is ingested, translated from gtk-doc / gi-docgen markup to Pony-docstring markdown, attached to every emitted type and method, and the resulting package tree is consumable by pony-doc.
  • Adds a sibling gir-docs binary that runs the pipeline in full-namespace mode (no closure analysis) against Gtk-4.0 / Gio-2.0 / GObject-2.0 / GLib-2.0 and produces a buildable Pony tree that ponyc parses + name-checks with zero errors.
  • Along the way, fixes the underlying emitter issues that previously prevented full-namespace emission from compiling: c:type-based class naming, varargs / array / reserved-word skipping, cross-namespace use directives, duplicate type dedup, the GtkRuntime cycle (via a PinnedRuntime structural interface in gobject_runtime), record vs class wrapper shapes, and return-value wrapping for utf8 / object types.

Commits

Foundation: capture <doc> text on every raw GIR entity · GirModel.resolve_by_c_type · doc_translate package · EverythingPlanner · gir-docs binary + Makefile · emitter docstring hook · compile-mode parity.

Emitter correctness: skip varargs and array<T> methods · munge reserved-word method names via shared PonyIdent · use c:type for class names · disambiguate constructor names · drop compile_error skip stubs · sibling-package use directives · dedupe types declared in multiple namespaces · break GtkRuntime cycle · wrap return values.

Verification

pony-doc -o /tmp/docs-mkdocs gtk against the full emit succeeds and produces a 3624-file MkDocs tree. ponyc errors per package on the full Gtk-4.0/Gio-2.0/GLib-2.0/GObject-2.0 emit: 0 across all four packages.

Test plan

  • make hello in the gtk4 consumer still produces a binary that runs (activate handler running on the pinned thread / window presented).
  • Package tests pass: corral run -- ponyc gir, corral run -- ponyc doc_translate, corral run -- ponyc emitter (gir 10, doc_translate 23, emitter 4).
  • gir-docs --gir Gtk-4.0,Gio-2.0,GObject-2.0,GLib-2.0 --target <dir> emits a Pony tree that ponyc parses without errors.
  • pony-doc -o <out> <gen-dir>/gtk succeeds and produces an MkDocs site.

redvers added 18 commits May 27, 2026 00:10
Add doc: String val to RawGirNamespace, RawGirClass, RawGirInterface,
RawGirRecord, RawGirEnumeration, RawGirBitfield, RawGirMember,
RawGirCallback, RawGirAlias, RawGirMethod, RawGirParameter,
RawGirReturnValue, RawGirProperty, RawGirSignal. Empty string when
no <doc> child is present.

Loader gains a _load_doc helper that finds the first <doc> child and
returns its text content; every _load_* helper calls it and threads
the result into the corresponding constructor.

No semantic change to compile-mode emit — the new field is unused
until the doc_translate package lands and the emitter is wired to
read it.
Populate a by_c_type index in the validator alongside by_qname; expose
resolve_by_c_type on GirModel. The doc translator needs this to map
legacy gtk-doc "#GtkWidget"-style references to their owning GIR
type so it can emit cross-page markdown links.

c_type values that are empty strings (rare but possible — GIR allows
omitting the C type for some entities) are skipped so they don't all
collide on a single key.
Translates the raw <doc> text captured by the gir loader into a
markdown body suitable for use verbatim as a Pony docstring (which
pony-doc then passes through to MkDocs).

Translation set (v1):
  - HTML entity decode (&amp; &lt; &gt;) — defensive; libxml2 normally
    handles these
  - Modern gi-docgen [kind@NS.Name] refs (full set: class, iface,
    struct, enum, flags, error, type, alias, method, ctor, vfunc,
    func, signal, property, const)
  - Legacy gtk-doc #CType refs resolved via GirModel.resolve_by_c_type
  - %TRUE / %FALSE / %NULL -> `true` / `false` / `None`
  - %CONSTANT -> `CONSTANT` (fallback; constants not modelled yet)
  - @param -> `param`
  - |[ <!-- language="X" --> … ]| -> ```X … ```
  - ATX heading demotion (# -> ##, capped at H6) so docstring
    headings don't collide with the page's own H1

Refs inside fenced ``` blocks, legacy |[…]| blocks, and inline
`…` spans are not rewritten — code stays code.

Diagnostics are typed (UnresolvedTypeRef, UnresolvedMethodRef,
UnresolvedConstantRef, InvalidMarkup) and accumulate in the
TranslatedDoc — callers aggregate them; the body is always usable
because unresolved refs fall back to inline-code rendering.

22 tests; counterfactual-verified by breaking diagnostic-emit,
heading-demotion, and code-block-isolation paths and confirming the
relevant assertions fired.
ClosurePlanner emits only what user code references; EverythingPlanner
emits every type and every method in every loaded namespace. Used by
the gir-docs binary, which generates documentation for the whole API
surface regardless of what (if any) application code touches it.

Also adds ScanResult.empty() — a constructor that yields a result with
no source-derived inputs. The docs-mode EmitPlan stores this in its
scan field; the emitter never reads scan today, but keeping the field
populated preserves the invariant that an EmitPlan always has one.
Emitter.apply gains a new emit_docstrings: Bool = false parameter.
When true, each per-kind generator (class, interface, record, enum,
bitfield, callback, alias) prepends the GIR <doc> text, translated
via doc_translate, as a Pony docstring inside the generated type.

MethodSpec and SkippedSpec gain a doc field; MethodEmitter threads
the translate context into _emit_trivial_void, _emit_trivial_return,
_emit_constructor_floating, _emit_signal_connect, and _emit_skip_stub
so method-level docstrings land alongside the method signature.

A new DocstringWriter helper handles indent + triple-quote formatting,
so each generator just appends a single helper call. Empirically
verified that ''' does not appear in any of Gtk-4.0, Gio-2.0,
GLib-2.0, or GObject-2.0 doc text — no need to escape v1.

Default emit_docstrings=false preserves compile-mode output exactly;
the gtk4 hello example still builds and runs unchanged.

4 emitter tests + counterfactual-verified the writer's closing
fence assertion fires when broken.
Sibling executable to gir-compiler that runs the full pipeline with
emit_docstrings=true and EverythingPlanner. Output is a self-
contained Pony tree of every GIR type with translated docstrings
that pony-doc can ingest into an MkDocs site.

The Makefile now builds both binaries by default. Each has its own
SRC_DIR (bin/ for gir-compiler, gir-docs/ for gir-docs) but shares
the gir/scanner/planner/emitter/doc_translate library packages.

Smoke test against the four target namespaces (Gtk-4.0, Gio-2.0,
GObject-2.0, GLib-2.0) emits 1530 files / 3.7MB. Sample inspection
confirms type-level and method-level docstrings are attached.

Note: pony-doc against the full output currently fails because the
compile-mode emitter has pre-existing bugs — variadic params,
array<T> syntax, methods named 'new', etc. — that didn't show up in
the small hello closure. Those are tracked separately; gir-docs as a
plumbing piece is complete.
The compile-mode emitter now also attaches translated GIR <doc> text
to generated bindings, on parity with gir-docs. Closure-mode users
get hover-doc / pony-doc benefits on whatever subset of the API
their code touches.

Verified: gtk4 hello example still builds; generated
build/hello/gtk/Application.pony carries the GtkApplication
docstring; the binary runs end-to-end.
Each Gen* iterates node.target.constructors directly to emit
constructors via MethodEmitter.classify_constructor. Adding the same
constructors to plan.method_calls caused double-emission — once as
'new create(...)' from the constructor pass, once as 'fun ref <name>()'
from method_calls (which collides with Pony's 'new' keyword).

Drops 180 of 244 syntax errors from a full Gtk-4.0 emit.
The GIR loader maps <varargs/> to RawGirType("varargs", "").
_pony_type_for had no case for the varargs sentinel, so it fell
through to _resolve_object_type and produced PtGObject("Gtk.varargs",
"Gtkvarargs"). That made the whole method classify successfully and
emit invalid FFI like:

  use @gtk_alert_dialog_new[Pointer[U8] tag](
    format: Pointer[U8] tag, ...: Pointer[U8] tag)

(the literal '...' GIR parameter name reaches the Pony source).

Adding 'varargs' to the type-name match returns UnemittableVariadic;
the method becomes a SkippedSpec with a compile_error stub. Drops
the remaining FFI-parameter errors (30 -> 0) and several 'parameters'
errors from a full Gtk-4.0 emit.
The GIR loader maps <array><type name=X/></array> to RawGirType(
"array<X>", c_type). _pony_type_for had no case for this sentinel,
so it fell through to _resolve_object_type and produced
PtGObject("Gtk.array<utf8>", "Gtkarray<utf8>"). The emitted Pony
then had things like `people: Gtkarray<utf8> box` in parameters
and return positions, both unparseable.

Same posture as the varargs fix: detect the sentinel at the type-
mapping boundary and surface as UnemittableUnsupportedShape so the
method becomes a compile_error skip stub. The full v1 array shape
(probably Array[T] for safe inner types) is deferred until the FFI
marshalling is designed.

Drops 22 of the remaining 25 syntax errors in a full Gtk-4.0 emit
(13 "parameters" + 9 "< after type").
Extract the GIR-name -> Pony-identifier munge into a new primitive
PonyIdent in the gir/ package. It owns the reserved-word catalog
(match, ref, error, box, val, iso, trn, new, etc.), hyphen-to-
underscore conversion, trailing-underscore strip, and the prime-
suffix collision rule.

Three callers now route through the same function:

  - emitter/TypeNaming.safe_param_name delegates (was previously the
    sole owner of the munge and reserved-word list)
  - emitter/MethodEmitter._classify_raw munges pony_name into
    MethodSpec so the emitted Pony method has a legal identifier
  - emitter/MethodEmitter._wrap_skip munges into SkippedSpec so
    skip-stub methods also get the safe name (otherwise reserved
    words slip past the compile_error stub path)
  - doc_translate/UrlBuilder.member_link munges the URL anchor and
    visible label so the doc cross-reference matches the actual
    emitted method name

Without the doc-side munge, [method@Gtk.Filter.match] in GIR docs
would render as a link to #match but pony-doc would generate the
anchor as #match' (matching the method's Pony identifier), and the
link would dangle.

Adds gir/_test.pony with 8 PonyIdent example tests plus a totality
property; doc_translate gains a test that the URL anchor uses the
munged name (counterfactual-verified by bypassing the munge and
confirming the assertion fires).

Drops the last 3 syntax errors from a full Gtk-4.0 emit; all four
target packages (Gtk-4.0, Gio-2.0, GObject-2.0, GLib-2.0) now parse
cleanly via ponyc.
The prior commit used PonyIdent.safe for both parameter and method
names, which suffixes a prime (') on reserved-word collisions. That
works for parameters and let/var bindings (Pony accepts primes in
those positions) but fails on method declarations:

  /tmp/gen-docs/gtk/Filter.pony: method name "match'" cannot contain prime

Pony's method-name grammar also rejects trailing underscores and
double underscores, leaving no inline-suffix scheme. Prefix it is:
PonyIdent.safe_method munges reserved words with a 'g' prefix
(echoing the GObject naming convention), keeping the method name a
legal identifier.

PonyIdent now exposes two routes:
  - safe_param  — for parameter / binding names    (match -> match')
  - safe_method — for method / function names      (match -> gmatch)

A shared _normalize_and_trim helper handles the hyphen / trailing-
underscore steps both routes share.

Callers updated:
  - TypeNaming.safe_param_name              -> safe_param
  - MethodEmitter._classify_raw             -> safe_method
  - MethodEmitter._wrap_skip                -> safe_method
  - doc_translate UrlBuilder.member_link    -> safe_method
  - gir/_test.pony tests rewritten for both routes
  - doc_translate test for the match reservation now expects gmatch

ponyc against the full Gtk-4.0/Gio-2.0/GLib-2.0/GObject-2.0 emit
now parses with zero syntax errors. pony-doc reaches the semantic
phase and surfaces a new bug class (duplicate method names from
multiple constructors all rendering as `create`) — tracked
separately.
The Pony spelling for a GIR type now comes from the GIR-declared
c:type directly. So Gio.Application -> GApplication (not
GioApplication), GObject.Object -> GObject (not GObjectObject),
GLib.Bytes -> GBytes (not GLibBytes). For namespaces where the c:type
already matches the GIR namespace prefix (most of Gtk-4.0), this is a
no-op: Gtk.Widget stays GtkWidget either way.

Why: the C-side ecosystem already uses these names — gtk-doc,
pkg-config, header files, and downstream library tutorials all spell
the types as the c:type. Prepending the GIR namespace ('Gio' instead
of 'G') produces names that don't appear anywhere in C-side prior art
and require constant mental translation when reading GTK docs and
the Pony bindings side-by-side.

Changes:
  - TypeNaming.pony_type_name now takes (c_type, qname_fallback). The
    c_type takes precedence; the namespace-prepended form is the
    fallback only when c:type is empty.
  - TypeNaming.pony_name_for_node centralizes the per-kind c:type
    extraction (class/iface/record/enum/bitfield/callback/alias).
  - MethodEmitter._pony_type_for now reads c:type off the resolved
    node via pony_name_for_node, so PtGObject / PtBitfield / PtEnum
    carry the c:type spelling.
  - _resolve_object_type became _resolve_object_qname — returns just
    the qname; pony-side name now sourced from the model lookup.
  - doc_translate's UrlBuilder.type_page / type_link / member_link
    take c_type and produce <pkg>-<c_type>.md, matching what
    pony-doc actually emits when it compiles the generated package.
  - doc_translate gains a _node_c_type helper paralleling
    _node_namespace / _node_local_name. The legacy #CType resolver
    and the modern [class@…] / [method@…] resolvers both route
    through it.
  - Tests updated: doc_translate's link-format expectations now
    use the c:type ('GtkWidget' instead of 'Widget' in the label,
    'gtk-GtkWidget.md' instead of 'gtk-Widget.md' in the URL).

Smoke-test on Gtk-4.0/Gio-2.0/GLib-2.0/GObject-2.0 full emit: 0
syntax errors across all four packages; hello example still
builds and runs.
Three changes that together drive a full Gtk-4.0/Gio-2.0/GLib-2.0/
GObject-2.0 emit to zero parse + name errors:

1. Constructor names derived from raw GIR name
   - 'new'             -> 'create'
   - 'new_with_label'  -> 'with_label'   (strip the 'new_' prefix)
   - 'new_from_file'   -> 'from_file'
   - 'newv'            -> 'newv'         (no underscore, used verbatim)
   _emit_constructor_floating now uses spec.pony_name instead of
   hardcoded 'create'. Classes with multiple GIR constructors
   (gtk_button_new + gtk_button_new_with_label + ...) get distinct
   Pony constructor slots; 44 'can't reuse name create' errors gone.
   The public helper MethodEmitter.constructor_pony_name exposes the
   mapping so gen_class can compute the same name up front.

2. Class-wide parameter name disambiguation
   gen_class builds a Set[String val] of every Pony name that will
   land on the class — every constructor's slot name, every method
   call name, and the synthetic _h / _runtime / _wrap / _handle /
   _runtime_ref names. The set threads through classify_method /
   classify_constructor / _classify_raw. When a safe parameter name
   matches anything in the set, the param gets a prime suffix.
   Catches the 'page_num / has_audio / event' family of collisions
   where one method on the class is named e.g. 'page_num' and other
   methods take a 'page_num' parameter.

3. Skip stubs removed entirely
   When MethodEmitter.classify_* returns SkippedSpec, the emitter
   now emits nothing at all — no compile_error stub, no ifdef block.
   If a method isn't representable in Pony, the binding simply
   doesn't expose it. Users who try to call something that isn't
   there get ponyc's normal 'method not found', which is fine since
   the method genuinely doesn't exist in the Pony API surface. The
   _emit_skip_stub and _describe helpers are now dead and deleted.

Hello example still builds + runs; all package tests still pass
(gir 10, emitter 4, doc_translate 23).
Generated classes that reference types from other GIR namespaces
now declare a `use "../<ns>"` directive for each such namespace,
alongside the existing `use "../gobject_runtime"` and `use
"lib:..."` lines. gen_class walks every MethodSpec's parameter
and return types, extracts the qname of each PtGObject / PtBitfield
/ PtEnum, and collects the set of distinct namespaces that aren't
the receiver's own.

Companion change in method_emitter: when a type reference resolves
to None (the type lives in a namespace we didn't load) or to a kind
we don't yet emit (callbacks, aliases via the catch-all arm), the
classifier now produces UnemittableUnknownType instead of falling
back to PtGObject. Methods touching such types are skipped — which
also means we don't emit `use "../<unloaded-ns>"` directives for
packages that don't exist in the target tree.

Cross-package types that *are* loaded resolve correctly: a Gtk
method taking Gio.Application as a parameter now emits with the
receiver class declaring `use "../gio"` and the parameter typed
as `GApplication box`.

Hello example still builds + runs; full Gtk/Gio/GLib/GObject emit
goes from 'can't find definition of <Type>' (one per cross-package
reference) down to a much narrower set of remaining issues that
are unrelated to imports (duplicate type names across packages
like GIOCondition in both GLib and GObject; record marshalling for
PtGObject; return-type wrapping for utf8 returns; GtkRuntime cycle
for the rare non-Gtk classes that we declare `_runtime: GtkRuntime
tag` on).
GLib and GObject both declare a bitfield named IOCondition with the
same c:type (GIOCondition); GIR carries them in two separate
namespaces but they represent the same C type. The previous emit
produced two Pony declarations (glib/IOCondition.pony and
gobject/IOCondition.pony, both defining a 'GIOCondition' primitive),
which clashed at every consumer that imported both packages.

Three coordinated changes:

  1. GirValidator's by_c_type index becomes first-wins: when a c:type
     is already registered, the later declaration is left out of the
     index. Whichever namespace appears first in the user's --gir
     list owns the slot.

  2. MethodEmitter._pony_type_for routes through by_c_type after
     resolving the gir_name to a node. The PonyType variants
     (PtBitfield / PtEnum / PtGObject) carry the canonical qname, so
     the foreign-namespace tracker in gen_class adds a use directive
     for the canonical package — not whatever package happened to
     own the gir_name's owner namespace.

  3. Emitter._content_for_node detects when a node's c:type maps to
     a different canonical node and emits a comment-only stub file
     ('// Duplicate declaration — canonically declared as …')
     instead of dispatching to the per-kind generator. The package
     directory layout stays intact; pony-doc just sees one
     definition globally.

A new NodeHelpers primitive in gir/ centralizes
namespace_of/local_name_of/c_type_of/qname_of on GirNodeRef so the
emitter doesn't repeat seven-arm matches for the same field reads.

Full-corpus emit drops 'duplicate type clash' errors from 14 to 0;
remaining errors are unrelated (#100, #101, #102). Hello example
still builds + runs; all package tests pass.
Generated classes previously declared `let _runtime: GtkRuntime tag`
on every emitted type. That works inside the gtk package (where
GtkRuntime is defined) but creates a cycle for any non-gtk package
that emits classes: gobject/Object.pony, gio/File.pony, etc. would
need `use "../gtk"` to find GtkRuntime, but gtk imports gobject/
gio/glib for its own type references — closing the loop.

Fix: declare a structural `interface tag PinnedRuntime` in
gobject_runtime/pinned_runtime.pony with the minimal set of methods
generated bindings invoke (currently just `register_close_request`).
Generated classes now hold `_runtime: PinnedRuntime tag` and use
`_wrap(h, runtime: PinnedRuntime tag)` / `_runtime_ref():
PinnedRuntime tag`. GtkRuntime's matching behavior makes a
`GtkRuntime tag` structurally assignable to a `PinnedRuntime tag`,
so the user's hello example still passes its concrete GtkRuntime to
generated constructors and signal-connect helpers.

Embedded changes:
  - new embedded/gobject_runtime/pinned_runtime.pony declaring
    CloseRequestHandler (moved here from gtk/runtime.pony) and the
    PinnedRuntime interface.
  - embedded/gtk/runtime.pony's `_register_close_request` is now
    public `register_close_request` (Pony's package-private
    underscore convention would otherwise bar cross-package calls
    through the structural interface).
  - tools/bake_embedded.sh ships the new file; the bake target
    re-runs and emitter/embedded_resources.pony picks up the third
    entry.

Emitter changes:
  - gen_class emits `_runtime: PinnedRuntime tag` and updates the
    _wrap / _runtime_ref signatures to match.
  - MethodEmitter._emit_signal_connect calls
    `_runtime.register_close_request` (no leading underscore).

Hello example still builds + runs unchanged — the user types the
runtime as GtkRuntime and structural matching handles the rest.
All 'can't find definition of GtkRuntime' errors are gone (4 packages
× 1 each → 0). Remaining errors in the full-corpus emit are all
#100 (return-type wrapping) and #101 (record marshalling),
previously masked by the runtime error stopping ponyc per-file.
The emitted method bodies previously just dumped the FFI call result,
declaring a typed Pony return that didn't match — `fun ref get_title():
String` with a body of `@ffi(_h.raw())` (which is Pointer[U8] tag).
ponyc rejected with 'function body isn't the result type', and a
parallel mismatch happened on the parameter side when records used
the wrong accessor (`closure._handle().raw()` on a record that only
has `.raw()`).

Coordinated changes:

  1. PtGObject gains a `kind` field — PtGObjectClass, PtGObjectInterface,
     or PtGObjectRecord — set in `_pony_type_for` based on the
     resolved GIR node kind. The marshaller and return wrapper both
     dispatch on it.

  2. Underscore-prefixed helpers `_wrap`, `_handle`, `_runtime_ref`
     are now public `wrap`, `handle`, `runtime_ref` so cross-package
     wrapping at return sites works. The leading underscore was making
     them package-private, which blocked emitting `OtherPkg.Type.wrap(...)`
     from a different package.

  3. Interfaces emit as class-shaped wrappers (with `_h`, `_runtime`,
     `wrap`, `handle`, `runtime_ref`) instead of empty traits, so an
     interface-typed return value has a wrapper shape to land in.

  4. New `_wrap_return` helper in MethodEmitter wraps the FFI call
     result based on return type:
        utf8       -> `String.from_cstring(<ffi>).clone()`
        class/iface-> `<Type>.wrap(GObjectHandle.adopt_borrowed(<ffi>), _runtime)`
        record     -> `<Type>.wrap(<ffi>)`
        primitive  -> pass through

  5. The FFI declaration's return type splits from the param type
     for utf8: returns are typed `Pointer[U8]` (ref, what String.from_cstring
     expects) while params stay `Pointer[U8] tag` (what .cstring() produces).

  6. `_marshal_arg` dispatches on PtGObjectKind: `.handle().raw()` for
     class/interface, `.raw()` for record. Closes #101.

  7. Constructor _runtime sourcing skips PtGObjectRecord params (they
     have no runtime_ref to read).

  8. Methods that can't be wrapped become UnemittableUnsupportedShape
     and are skipped:
        - methods returning PtBitfield/PtEnum (no from-integer wrap yet)
        - constructors with no class/interface GObject parameter to
          source _runtime from (would leave _runtime uninitialized)

Full Gtk-4.0/Gio-2.0/GLib-2.0/GObject-2.0 emit:
   gtk    1456 errors -> 0  (the 'no Main actor' notice that ponyc emits
   gio     507 errors -> 0   when run directly on a library package is
   gobject  30 errors -> 0   informational only — pony-doc reaches the
   glib      1 error  -> 0   generator without it.)

pony-doc end-to-end on the gtk package now succeeds and produces a
3624-file MkDocs tree. Hello example still builds + runs; all package
tests pass (gir 10, emitter 4, doc_translate 23).
@redvers redvers changed the title Docs generation Generate documentation from GIR <doc> text May 28, 2026
Brings gir-compiler's GitHub Actions setup in line with libxml2:

  - pr.yml — adds super-linter and changelog-tool verification
    alongside the existing build job; smoke-tests both binaries
    (gir-compiler + gir-docs) after `make`.
  - main.yml — also smoke-tests both binaries on push to main.
  - lint-action-workflows.yml — actionlint on PRs.
  - changelog-bot.yml — keeps CHANGELOG.md current per push.
  - release-notes.yml + release-notes-reminder.yml — manage the
    .release-notes/ folder (created with an empty next-release.md).
  - prepare-for-a-release.yml — bumps CHANGELOG/VERSION/README/
    corral.json on release-X.Y.Z tags, then triggers tag-release.
  - release.yml — validates CHANGELOG, rebuilds, and triggers
    announce on X.Y.Z tags.
  - announce-a-release.yml — publishes release notes to GitHub +
    Zulip on announce-X.Y.Z tags.
  - generate-documentation.yml — manual-trigger workflow that
    deploys pony-doc output to GitHub Pages at gir-compiler.contact.red,
    with the docs-theme/ contact.red branding applied (CSS copied
    from libxml2).

These run against the same ghcr.io/redvers/pony-dependency-builder
and ghcr.io/ponylang/* images libxml2 uses. The release workflows
need the same secrets the libxml2 release pipeline uses
(RELEASE_TOKEN, ZULIP_*, CONTACTRED_MAIN_API_TOKEN) to fire end-to-end.
@redvers redvers force-pushed the docs-generation branch from ed3738a to e946476 Compare May 28, 2026 17:57
@redvers redvers merged commit 7b83c22 into main May 28, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant