Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions .github/workflows/dispatch-to-registry.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
# Auto-sync the FlutterSDK AI registry when a wind release is published.
#
# Fires repository_dispatch at fluttersdk/ai on a published GitHub release, or
# on a manual run. The registry's sync.yml receives the event, pulls the
# skills/wind-ui/ subtree, bumps the version triplet, and pushes to main.
# Fires repository_dispatch at fluttersdk/ai. The registry's sync.yml receives
# the event, pulls the skills/wind-ui/ subtree, bumps the version triplet, and
# pushes to main.
#
# Called by publish.yml after its github-release job, so the dispatch happens
# only once pub.dev has actually accepted the release. It is NOT triggered by
# `release: [published]`, which is what it used to declare: publish.yml creates
# the release with `gh release create` under `GH_TOKEN: ${{ github.token }}`,
# and GitHub does not start workflow runs from events raised by GITHUB_TOKEN.
# That trigger could never fire, and did not fire once between the day it was
# added (2026-08-03) and the 1.4.0 release, which shipped a skill the registry
# never received until it was dispatched by hand.
#
# Deliberately NOT on every push. A skill edit that lands on master between
# releases waits for the release that carries it, so the registry version
Expand All @@ -18,8 +27,12 @@
name: Dispatch skill update to registry

on:
release:
types: [published]
workflow_call:
secrets:
REGISTRY_BOT_APP_ID:
required: true
REGISTRY_BOT_PRIVATE_KEY:
required: true
workflow_dispatch:

permissions:
Expand All @@ -32,12 +45,22 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 1
# Every other checkout in this repo sets it, and zizmor's
# `artipacked` audit is on by default. This job only reads
# `pubspec.yaml`, so leaving the token in `.git/config` buys
# nothing and this workflow now runs inside the release
# pipeline.
persist-credentials: false

- name: Extract version
id: ver
run: |
set -euo pipefail
if [[ -n "${GITHUB_REF_NAME}" && "${GITHUB_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
# `v?` because this repo tags WITHOUT the prefix: publish.yml
# matches '[0-9]+.[0-9]+.[0-9]+*' and CLAUDE.md says
# `git tag X.Y.Z`. The old `^v` test could never match a real
# tag here, so this always fell through to pubspec.yaml.
if [[ -n "${GITHUB_REF_NAME}" && "${GITHUB_REF_NAME}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+ ]]; then
Comment thread
coderabbitai[bot] marked this conversation as resolved.
VER="${GITHUB_REF_NAME#v}"
else
VER=$(grep -E '^version:' pubspec.yaml | head -1 | awk '{print $2}')
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,38 @@ jobs:
--notes "$NOTES" \
--verify-tag \
$PRERELEASE

# Called here rather than triggered by `release: [published]`, which is what
# dispatch-to-registry.yml used to declare. The release above is created with
# `GH_TOKEN: ${{ github.token }}`, and GitHub does not start workflow runs
# from events raised by GITHUB_TOKEN, so that trigger could never fire and
# never did: the 1.4.0 skill reached the registry only because it was
# dispatched by hand. Sequencing it after github-release also keeps the
# registry version tracking packages pub.dev actually accepted.
registry:
name: Sync skill to registry
needs: github-release
# Stable releases only. The tag filter above is `[0-9]+.[0-9]+.[0-9]+*`, so
# `1.5.0-beta.1` matches it and this repo has shipped `1.0.0-alpha.*` tags
# before. The reason to skip is distribution, not a malformed version: the
# registry's `sync.yml` validates the upstream version as
# `^[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9.]+)?$` (a prerelease is explicitly
# allowed) and derives its own version by bumping its own manifest's patch
# number, so the string we send only reaches its release notes. What a sync
# does do is rsync `skills/wind-ui/` onto the registry's `main`, which is
# what `npx skills add fluttersdk/ai` serves. A beta tag would therefore
# hand beta skill content to every consumer, which is what a beta is meant
# not to do.
#
# A branch name carrying a dash cannot be caught by this: on a
# `workflow_dispatch` the `github-release` job is skipped by its own tag
# guard, and a dependant of a skipped job is skipped too, so `registry` only
# ever evaluates this on a tag push.
if: ${{ !contains(github.ref_name, '-') }}
uses: ./.github/workflows/dispatch-to-registry.yml
# Named rather than `secrets: inherit`: the called workflow needs exactly
# these two, and this repo pins every action by SHA and runs zizmor over
# the result, so handing it the whole secret store would be out of step.
secrets:
REGISTRY_BOT_APP_ID: ${{ secrets.REGISTRY_BOT_APP_ID }}
REGISTRY_BOT_PRIVATE_KEY: ${{ secrets.REGISTRY_BOT_PRIVATE_KEY }}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ This project follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.

- **A multi-line `className` hid its flex tokens from the row and column composers, and this project's own style guide is what put them there.** `.claude/rules/widgets.md` and `SKILL.md` both instruct a className covering 3+ concerns to be a triple-quoted string with one concern per line, so `flex-1` at the end of a line arrives at the composition helpers as `flex-1\n`. Three of them split on a single space and therefore never matched it. Two consequences, both reproduced: a `flex-1` child of an `overflow-hidden` row got wrapped a second time and threw `Incorrect use of ParentDataWidget` (`_selfWrapsInFlex`), and a `w-24 shrink-0` child in a crowded row shrank to its 50pt flex share instead of holding 96pt (`_hasShrinkZero`). The third site, `_hasExplicitCrossWidth`, is corrected for consistency with its own documented "in ANY state or breakpoint variant" intent, but no observable failure could be produced for it: a stretched column child still renders at the width it asked for, so the miss costs a redundant wrapper rather than a wrong layout. All five token scans in `WDiv` now share one hoisted `_whitespaceRegex`, which also stops the two that already split on whitespace from allocating a fresh `RegExp` on every pass through the composition path. (`lib/src/widgets/w_div.dart`, `test/widgets/w_div/multiline_classname_test.dart`)

### Quality

- **The registry dispatch could never fire, so 1.4.0 shipped a skill the registry never received.** `dispatch-to-registry.yml` declared `release: [published]`, but the release is created inside `publish.yml`'s `github-release` job by `gh release create` running under `GH_TOKEN: ${{ github.token }}`, and GitHub does not start workflow runs from events raised by `GITHUB_TOKEN`. The trigger was added on 2026-08-03 and 1.4.0 was the first release after it, so it had exactly one chance and missed: the run history showed nothing since 2026-08-03, both entries there being `workflow_dispatch` and the retired `push` trigger. It reached `fluttersdk/ai` only because it was dispatched by hand. The cost of this failure mode is that it is silent, since the publish workflow goes green either way and the only symptom is end users installing a skill a version behind. `publish.yml` now calls the workflow directly with `needs: github-release`, which removes the cross-workflow event entirely and also guarantees the dispatch happens after pub.dev has accepted the release rather than in parallel with it. The dead `release` trigger is gone and `workflow_call` replaces it; `workflow_dispatch` stays as the manual escape hatch. Secrets are passed by name rather than `secrets: inherit`, since the called workflow needs exactly two and this repo pins every action by SHA and runs zizmor over the result. (`.github/workflows/dispatch-to-registry.yml`, `.github/workflows/publish.yml`)

- **The version extractor tested the ref for a `v` prefix this repo never tags with, so it always fell through to `pubspec.yaml`.** The step matched `^v[0-9]+\.[0-9]+\.[0-9]+`, while `publish.yml`'s tag filter is `[0-9]+.[0-9]+.[0-9]+*` and CLAUDE.md's release step is `git tag X.Y.Z`: that branch could not match a real tag here. Nothing surfaced it because the fallback happens to be correct on master after a release bump, so the dead branch and the working one returned the same string. The test is now `^v?[0-9]+\.[0-9]+\.[0-9]+`, which reads the tag when there is one and keeps the pubspec fallback for a manual run off a branch. (`.github/workflows/dispatch-to-registry.yml`)

- **The registry sync now skips a prerelease tag, so a beta release does not hand beta skill content to every consumer.** `publish.yml`'s tag filter is `[0-9]+.[0-9]+.[0-9]+*`, so `1.5.0-beta.1` matches it and this repo has shipped `1.0.0-alpha.*` tags. The reason to skip is distribution rather than a malformed version: `fluttersdk/ai`'s `sync.yml` validates the upstream version as `^[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9.]+)?$` (a prerelease is explicitly allowed) and derives its own version by bumping its own manifest's patch number, so the string sent only reaches its release notes. What a sync does do is rsync `skills/wind-ui/` onto the registry's `main`, which is what `npx skills add fluttersdk/ai` serves. A branch name containing a dash cannot trip the guard, because on a `workflow_dispatch` the `github-release` job is skipped by its own tag check and a dependant of a skipped job is skipped with it. (`.github/workflows/publish.yml`)

- **The called workflow's checkout stops carrying a token it does not use.** It now sets `persist-credentials: false` like every other checkout here: the job only reads `pubspec.yaml`, zizmor's `artipacked` audit is on by default, and this change is what puts the workflow inside the release pipeline. (`.github/workflows/dispatch-to-registry.yml`)

## [1.4.0] - 2026-08-21

### Added
Expand Down
Loading