Skip to content

ci: Build doc versions in parallel and fix the failing app builds#22

Merged
spydon merged 2 commits into
mainfrom
ci/parallel-doc-builds
Jul 19, 2026
Merged

ci: Build doc versions in parallel and fix the failing app builds#22
spydon merged 2 commits into
mainfrom
ci/parallel-doc-builds

Conversation

@spydon

@spydon spydon commented Jul 19, 2026

Copy link
Copy Markdown
Member

Description

Two things, both found while investigating why run 29694895994 took 4h17m: the embedded
Flutter apps were failing to compile for every version older than v1.36.0, and the versions
were built sequentially when they are independent of each other.

Fix the failing app builds

Every version older than v1.36.0 failed to compile its embedded Flutter apps with:

Error: Couldn't resolve the package 'rive_native' in 'package:rive_native/rive_native_plugin_web.dart'
Error: Undefined name 'RiveNativePlugin'.
Error: Compilation failed.

590 of the 647 compiles in that run failed this way. It is not a dependency problem: rive
0.13.20 does not depend on rive_native at all, only 0.14.0+ does. It is state leaking
between versions.

All versions are built in the same clone, newest first. git checkout -f does not touch
ignored files, and .dart_tool/, .flutter-plugins-dependencies and **/build/ are all
gitignored. .dart_tool/ holds a generated web plugin registrant, so once v1.36.0 (rive
^0.14) has been built, a registrant importing package:rive_native survives the checkout
into v1.35.1 (rive ^0.13), which does not depend on it. The import no longer resolves and
every app build fails from there on down. The version boundary matches exactly where flame
moved from rive ^0.13.20 to ^0.14.0.

Adding git clean -xfd after the checkout makes each version start from a pristine tree.
Note -x is required: plain git clean -fd would skip these files precisely because they
are ignored.

Reproduced in one clone, which is exactly what CI does:

step state handling result
v1.36.0 clean ✓ Built build/web
v1.35.1 git checkout -f only Couldn't resolve the package 'rive_native'
v1.35.1 git clean -xfd first ✓ Built build/web

This is a correctness fix as much as a speed one: the embedded apps in every published
version below v1.36.0 are currently broken.

Build the versions in parallel

The versions are independent, so they are now built by a matrix with one job per version, plus
a single job that assembles the results and pushes. publish.sh gains list, build and
assemble subcommands which the workflow drives:

  • list clones the Flame repo blob-filtered and no-checkout (it only needs the refs) and
    emits latest and targets for the matrix.
  • build <version> <latest> builds one version into out/.
  • assemble <targets> merges the artifacts into docs/ and pushes.

Running ./publish.sh without arguments still performs the full sequential build locally, so
the build logic is not duplicated between the script and the workflow.

The push stays in the single publish job so the builders never race on the branch, and
fail-fast: false means one broken version does not prevent the others from being published.

Note the matrix also makes the state leak structurally impossible, since every version gets a
fresh runner and a fresh clone. The git clean -xfd is kept anyway: it is what makes the
local sequential path correct, and it should not depend on how CI happens to be sharded.

Expected wall clock is roughly 5 minutes, down from 4h17m.

Action and runtime updates

Bundled here since they touch the same file:

from to
actions/checkout v4 v7
actions/setup-node v3 v7
Node 16 (EOL since 2023) 22
Melos 7.0.0-dev.7 8.2.2

subosito/flutter-action@v2 and bluefireteam/melos-action@v3 are already on their latest
majors. The old Melos pin could not satisfy v1.38.0, which requires ^8.1.0, so main and
the newest tag were relying on melos bootstrap || echo ... swallowing the failure. Melos
8.2.2 was checked against v1.25.0, the oldest tag in the build list: it does not reject a
workspace declaring ^7, and loads all packages and the script registry, so
melos run doc-setup resolves.

Related

flame-engine/flame#3953 stops the flutter-app directive from recompiling an app that
already failed, so a broken app costs one compile rather than one per referencing page. It is
independent of this PR, but it is what bounds the damage if a version ever does fail again.

Testing instructions

The workflow is workflow_dispatch only, so the real test is dispatching it from this branch
and checking that:

  1. The versions job outputs 17 targets with latest=v1.38.0.
  2. 17 build jobs run in parallel, each uploading a docs-<version> artifact.
  3. No job logs Couldn't resolve the package 'rive_native', and each version logs 5
    successful Compiling Flutter app lines rather than dozens of repeats.
  4. The publish job produces a docs/ tree matching the sequential build: one directory per
    version, a docs/latest copy, and docs/versions.txt listing main first then newest to
    oldest.
  5. docs/latest has no noindex marker while every versioned directory does. The ordering in
    generate_docs_for_version matters here: latest is copied before the marker is added.

Worth spot-checking a page like flame/effects/anchor_effects on an older version once
published, since those embedded apps should now actually run.

spydon added 2 commits July 20, 2026 00:50
The docs build ran every version sequentially in a single job. The versions
are independent of each other, so they are now built by a matrix, with one
job per version, and a single job that assembles the results and pushes. The
push stays in one job so the builders never race on the branch.

publish.sh gains `list`, `build` and `assemble` subcommands, which the
workflow drives. Running it without arguments still performs the full
sequential build locally, so the build logic is not duplicated between the
script and the workflow.

Also updates the actions and their runtimes: checkout v4 -> v7, setup-node
v3 -> v7, Node 16 (EOL since 2023) -> 22, and Melos 7.0.0-dev.7 -> 8.2.2.
The old Melos pin could not satisfy v1.38.0, which requires ^8.1.0.
The Flutter apps embedded in the docs failed to compile for every version
older than v1.36.0, with "Couldn't resolve the package 'rive_native'".

The versions are built one after another in the same clone, and
`git checkout -f` leaves ignored files alone. `.dart_tool/` holds a
generated web plugin registrant, so once a version using rive ^0.14 has
been built, the registrant importing `package:rive_native` survives into
the next version. Versions using rive ^0.13 do not depend on rive_native,
so the stale import no longer resolves and the app fails to compile.

Reproduced with a clone checked out at v1.36.0, built, then moved to
v1.35.1: the build fails exactly as in CI, and succeeds again once
`git clean -xfd` is added after the checkout.
@spydon spydon changed the title ci: Build doc versions in parallel and update actions ci: Build doc versions in parallel and fix the failing app builds Jul 19, 2026
@spydon
spydon merged commit b1f9680 into main Jul 19, 2026
@spydon
spydon deleted the ci/parallel-doc-builds branch July 19, 2026 23:12
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