Skip to content

feat: structured CI extensibility modules + matrix orchestration + release artifact aggregation #20

@tsavo-at-pieces

Description

@tsavo-at-pieces

Overview

Evolve runtime_ci_tooling from today’s user-preserved YAML injection (pre-test/post-test/extra-jobs) into a structured, config-driven CI orchestrator that:

  • Runs multi-platform CI (macOS/Linux/Windows) across x64 + arm64 using org-managed GitHub-hosted runners where configured.
  • Lets downstream consumers register reusable workflow modules (extension blocks) that manage_cicd init/update/validate can discover/install/validate/wire automatically.
  • Supports multi-package repos (root + sub_packages) with configurable matrix strategies to avoid job explosion.
  • Enables single consolidated release that aggregates multi-platform build artifacts from the CI run and attaches them to the GitHub Release.

This should generalize the orchestration pattern used in aot_monorepo/.github/workflows/workflow.yaml + reusable-module-composition.yaml into a reusable system for any repo that consumes runtime_ci_tooling.


Background / Current State

What works today

  • CI workflow generation is templated (templates/github/workflows/ci.skeleton.yaml) via Mustache, driven by .runtime_ci/config.json ci.*.
  • Update flow is manifest-driven (templates/manifest.json) and supports categories: overwritable/cautious/templated/mergeable/regeneratable.
  • CI supports user-preserved sections via markers:
    • # --- BEGIN USER: pre-test ---
    • # --- BEGIN USER: post-test ---
    • # --- BEGIN USER: extra-jobs --- (added recently)

Gaps / Misalignments we must fix

  • init hardcodes config defaults (lib/src/cli/commands/init_command.dart) instead of using templates/config.jsonconfig drift is real (missing keys like ci.platforms / ci.runner_overrides, and differing defaults).
  • validate only checks syntactic validity (JSON/YAML parse) and does not validate the semantics of .runtime_ci/config.json or CI config consistency.
  • Extensions are currently “copy/paste YAML blocks” with no schema or validation.
  • Release flow (.github/workflows/release.yaml) does not yet aggregate artifacts produced by CI matrix jobs into a single release.

Goals

  1. Structured extensibility

    • Downstream repos can declare extension workflows/modules in .runtime_ci/config.json.
    • manage_cicd update --workflows:
      • installs extension reusable workflows into .github/workflows/
      • validates their workflow_call interface
      • generates static uses: ./.github/workflows/<ext>.yaml jobs (GitHub Actions requires static uses:)
  2. Platform matrix orchestration

    • Standardize on a platform config JSON schema usable by base CI + extension modules:
      • { platform_id, runner, name, system_information, architecture }
    • Support per-org runner overrides (already started via ci.runner_overrides).
  3. Multi-package repos

    • Extend beyond “extra analyze steps” to a pipeline model: platforms × packages × phases.
    • Provide configurable strategies to avoid N×M job explosions.
  4. Release aggregation

    • Release workflow downloads artifacts from the triggering CI run (workflow_run event) using github.event.workflow_run.id.
    • Aggregate per-platform/per-package build artifacts and attach to the GitHub Release.

Non-goals (v1)

  • Dynamic uses: in YAML (not supported by GitHub Actions); generation must emit static uses blocks.
  • Arbitrary remote fetching of extension workflows from other repos (v2+). Start with local/monorepo file sources.

Proposed Design (v1)

A) Make init template-driven (eliminate drift)

Problem: init_command.dart currently hardcodes the default config.

Solution:

  • Load templates/config.json as the base document.
  • Overlay detected values:
    • repository.name, repository.owner, derived labels, etc.
  • Write the merged result to .runtime_ci/config.json.

Files:

  • Modify: lib/src/cli/commands/init_command.dart
  • Read from: templates/config.json

Acceptance: init output includes all keys from the template (including new ones as templates evolve).


B) Make validate semantic and CI-aware

Problem: validate only checks parseability.

Solution:

  • Parse .runtime_ci/config.json and validate:
    • ci.* via WorkflowGenerator.validate()
    • extensions.* via new ExtensionValidator
  • Validate that ci.platforms and ci.runner_overrides are consistent.
  • Validate that extension workflows exist and have expected workflow_call inputs/outputs.

Files:

  • Modify: lib/src/cli/commands/validate_command.dart
  • Modify: lib/src/cli/utils/ci_constants.dart to include .runtime_ci/config.json (or explicitly load it in validate)
  • Add: lib/src/cli/utils/extension_validator.dart (or similar)

C) Add extension registry schema to .runtime_ci/config.json

Add a top-level extensions key (or ci.extensions; top-level recommended).

{
  "extensions": {
    "workflows": [
      {
        "id": "ipc-fixtures",
        "source": ".runtime_ci/extensions/workflows/ipc-fixtures.yaml",
        "destination": ".github/workflows/runtime-ci-ext-ipc-fixtures.yaml",
        "required": true,
        "validation": {
          "require_workflow_call": true,
          "required_inputs": ["matrix-config", "dart-sdk"],
          "required_outputs": []
        }
      }
    ],
    "modules": [
      {
        "id": "ipc-fixtures",
        "enabled": true,
        "uses": "./.github/workflows/runtime-ci-ext-ipc-fixtures.yaml",
        "needs": ["test"],
        "platforms": ["windows-x64", "windows-arm64"],
        "with": {
          "some-module-input": "value"
        }
      }
    ]
  }
}

Notes:

  • workflows[] tells update how to install reusable workflows into .github/workflows/.
  • modules[] tells the CI generator how to wire jobs that call those workflows.
  • Each extension workflow must expose on: workflow_call.

D) Standardize platform config JSON for base + extensions

We already have a JSON-like schema in aot_monorepo/.github/workflows/workflow.yaml and reusable-module-composition.yaml.

Adopt (or align to) that object schema so modules can simply do:

strategy:
  matrix:
    config:
      - ${{ fromJSON(inputs.matrix-config) }}

And then runs-on: ${{ matrix.config.runner }}.

runtime_ci_tooling should generate the same shape for its platform include list, so extensions can be platform-aware without ad hoc logic.


E) Update manage_cicd update --workflows to install + wire extensions

Enhance UpdateCommand templated workflow generation:

  • Before rendering CI workflow, load extensions.workflows.
  • Copy workflow sources to destinations (create directories as needed).
  • Validate installed workflows.
  • Provide extensions.modules to the workflow template rendering context.

Template change:

  • In templates/github/workflows/ci.skeleton.yaml, add a section that emits extension module jobs from context.

Keep existing user-preserved sections for backward compatibility.


F) Multi-package pipeline model (incremental)

Current ci.sub_packages only adds extra analyze steps; it does not provide per-package CI phases or per-platform runs.

Introduce ci.pipeline (or similar) to model:

  • packages: root + sub_packages
  • phases: analyze/test/build/docs
  • matrix strategy:
    • sequential_steps (today)
    • per_platform
    • per_package
    • per_package_per_platform (opt-in)

Start with a conservative default that avoids job explosion.

Related: #17 (Multi-package support)


G) Release: aggregate artifacts from CI workflow run

The release pipeline is workflow_run driven. For multi-platform builds, we need a first-class aggregation step.

Implement an aggregate-artifacts job in release workflow that:

  • downloads artifacts from the triggering CI run (github.event.workflow_run.id)
  • normalizes into a directory like:
    • release-artifacts/<platform_id>/<package_name>/...
  • uploads a single artifact release-artifacts

Then create-release downloads it and uses gh release upload.

Docs reference: GitHub Docs note that downloading artifacts from a different run requires token + run identifier.


Documentation Updates

Add a clear "Downstream Extensibility" section to:

  • SETUP.md
  • USAGE.md

Include:

  • What extensions is
  • The workflow_call interface contract
  • Examples (local extensions and monorepo package-provided extensions)
  • How validate reports extension errors

Test Plan

  • Unit tests:
    • init produces config matching template keys (no missing keys)
    • validate fails on invalid ci.platforms values
    • validate fails when extension workflow is missing / lacks workflow_call
    • update --workflows installs extension workflows and emits CI jobs calling them
  • Golden file tests (optional but valuable):
    • render CI YAML from a sample config with extensions and compare to expected output

Acceptance Criteria

  • manage_cicd init output config matches templates/config.json schema (no drift).
  • manage_cicd validate performs semantic validation of .runtime_ci/config.json (platform IDs, runner overrides, extensions).
  • Consumer repo can define an extension reusable workflow + register it in config; update --workflows installs + wires it into CI.
  • CI generation emits static uses: jobs for extension modules (no runtime YAML hacks).
  • Release workflow can download artifacts from the triggering CI run, aggregate them, and attach them to a single GitHub Release.
  • Multi-package repos have a documented/configurable path to run platform CI without uncontrolled job explosion.

Related Issues

This issue is intended to act as the umbrella/architecture item tying those together with reusable workflow module composition + release artifact aggregation.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions