Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions .claude/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
"cd playground && flutter run -d web-server --web-port=8123"
],
"port": 8123
},
{
"name": "stacflow-example",
"runtimeExecutable": "sh",
"runtimeArgs": [
"-c",
"cd packages/stacflow/example && flutter run -d web-server --web-port=8124"
],
"port": 8124
}
]
}
10 changes: 5 additions & 5 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@

## Checklist

- [ ] `flutter analyze lib` and `flutter analyze` in `example/` and `playground/` are clean
- [ ] `dart format .` applied
- [ ] `dart run melos run analyze` is clean
- [ ] `dart run melos run format` applied
- [ ] Exercised in the playground — with a stage demo added or updated if this is a new component or variant
- [ ] Any new entry under `dependencies:` in `pubspec.yaml` is flutter.dev-published, forces no configuration on hosts that never use the feature, and is argued in this PR
- [ ] Any new entry under `dependencies:` in `packages/flow_ui/pubspec.yaml` is flutter.dev-published, forces no configuration on hosts that never use the feature, and is argued in this PR
- [ ] Nothing model-facing — no prompts, schemas, or provider/network calls
- [ ] New public API is exported from `lib/flow_ui.dart` and documented in `docs/` and the README table
- [ ] `CHANGELOG.md` updated for user-facing changes, with breaking changes called out
- [ ] New public API is exported from `packages/flow_ui/lib/flow_ui.dart` and documented in `docs/` and the README table
- [ ] `packages/flow_ui/CHANGELOG.md` updated for user-facing changes, with breaking changes called out
- [ ] PR title follows conventional commits (`feat:`, `fix:`, `refactor:`, `docs:`, `chore:`)
116 changes: 80 additions & 36 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ on:
pull_request:
workflow_dispatch:

# A new push to the same ref supersedes the run already in flight.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
Expand All @@ -15,8 +14,8 @@ permissions:
contents: read

jobs:
package:
name: Package
workspace:
name: Workspace
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
Expand All @@ -28,69 +27,80 @@ jobs:

- run: flutter pub get

- name: Example key file
run: cp packages/stacflow/example/lib/env.example.dart packages/stacflow/example/lib/env.dart

- name: Format
run: dart format --output=none --set-exit-if-changed .

# Scoped to lib/: from the repo root the analyzer also walks into
# example/ and playground/, which resolve their own dependencies and
# are analysed in the `apps` job instead.
- name: Analyze
run: flutter analyze lib
run: dart run melos run analyze

# test/ is deliberately empty while the component surface is still being
# reshaped design-first; this runs the day the first test lands.
- name: Test
run: |
if find test -name '*_test.dart' 2>/dev/null | grep -q .; then
flutter test
else
echo "No tests yet — skipping."
fi

# Catches packaging mistakes (a stale .pubignore, a missing asset, a
# broken README link) before a release tag, not after.
run: dart run melos run test

package:
name: flow_ui package
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/flow_ui
steps:
- uses: actions/checkout@v7

- uses: subosito/flutter-action@v2
with:
channel: stable
cache: true

- run: flutter pub get

- run: flutter analyze

- name: Publish dry run
run: flutter pub publish --dry-run

apps:
name: ${{ matrix.app }}
name: ${{ matrix.name }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
app: [example, playground]
include:
- name: example
dir: packages/flow_ui/example
- name: stacflow example
dir: packages/stacflow/example
- name: playground
dir: playground
steps:
- uses: actions/checkout@v7

# The example reads its Gemini key from lib/env.g.dart, which is
# gitignored so a real key can never land in a commit — meaning a
# fresh checkout doesn't have it. Stub it with an empty key, the
# same state a fresh clone runs with (see example/README.md).
- name: Stub the example's key file
if: matrix.app == 'example'
run: |
printf '// CI stub — the real file is gitignored.\nconst String apiKey = %s;\n' "''" \
> example/lib/env.g.dart

- uses: subosito/flutter-action@v2
with:
channel: stable
cache: true

- name: Example key file
if: matrix.dir == 'packages/stacflow/example'
run: cp lib/env.example.dart lib/env.dart
working-directory: ${{ matrix.dir }}

- run: flutter pub get
working-directory: ${{ matrix.app }}
working-directory: ${{ matrix.dir }}

- run: flutter analyze
working-directory: ${{ matrix.app }}
working-directory: ${{ matrix.dir }}

- run: flutter build web --release
working-directory: ${{ matrix.dir }}

docs:
name: Docs site
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

# `build:site` compiles the playground to wasm before Astro builds, so
# this job needs both toolchains.
- uses: subosito/flutter-action@v2
with:
channel: stable
Expand All @@ -105,7 +115,41 @@ jobs:
- run: npm ci
working-directory: docs

# The same build the deploy runs: playground → public/playground, then
# the site. Keeps a broken playground from reaching flowui.stac.dev.
- run: npm run build:site
working-directory: docs

# Dependency-direction rules the analyzer cannot express. Greps anchor to
# import/export directives so prose in doc comments never trips them.
boundaries:
name: Boundaries
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: flow_ui never depends on the SDK package
run: |
! grep -rnE "^\s*(import|export)\s+'package:stacflow/" packages/flow_ui/lib \
|| { echo "::error::flow_ui is the presentation layer; the SDK depends on it, never the reverse"; exit 1; }
! grep -nE "^\s*stacflow:" packages/flow_ui/pubspec.yaml \
|| { echo "::error::flow_ui must not depend on the SDK package"; exit 1; }

contracts:
name: Contracts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: subosito/flutter-action@v2
with:
channel: stable
cache: true

- run: flutter pub get

- name: Generated Dart matches contracts/
run: |
dart run tool/contracts_gen.dart
dart format packages/stacflow/lib/src/generated >/dev/null
git add -N packages/stacflow/lib/src/generated
git diff --exit-code -- packages/stacflow/lib/src/generated \
|| { echo "::error::generated types are stale: run dart run tool/contracts_gen.dart && dart format packages/stacflow/lib/src/generated"; exit 1; }
80 changes: 54 additions & 26 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,62 @@
# One release trigger, everything ships: pushing a version tag publishes
# the package to pub.dev AND deploys the docs site — playground included,
# it's built into dist/playground/ — to Cloudflare Pages.
# One tag per package release:
#
# git tag v0.2.0 && git push origin v0.2.0
# git tag flow_ui-v0.4.0 && git push origin flow_ui-v0.4.0
#
# Package authentication is OIDC — no token lives in this repo. It requires
# automated publishing to be enabled once on pub.dev (flow_ui → Admin →
# Automated publishing) for StacDev/flow_ui with the tag pattern
# `v{{version}}`.
# publishes packages/flow_ui to pub.dev and deploys the docs site (playground
# included) to Cloudflare Pages. Publishing is OIDC: pub.dev → flow_ui → Admin
# → Automated publishing must list StacDev/flow_ui with the tag pattern
# `flow_ui-v{{version}}`. A future package gets its own tag glob below, a
# `case` line in `meta`, and its own pub.dev tag pattern.
#
# The site deploy needs one-time repo configuration:
# secrets: CLOUDFLARE_API_TOKEN (Pages:Edit permission)
# CLOUDFLARE_ACCOUNT_ID
# variable: CLOUDFLARE_PAGES_PROJECT (the Pages project serving
# flowui.stac.dev)
# Site deploy configuration: secrets CLOUDFLARE_API_TOKEN (Pages:Edit) and
# CLOUDFLARE_ACCOUNT_ID; variable CLOUDFLARE_PAGES_PROJECT.
name: Publish

on:
push:
tags: ['v[0-9]+.[0-9]+.[0-9]+*']
# Redeploy the site without cutting a release, e.g. after a docs fix.
tags:
- 'flow_ui-v[0-9]+.[0-9]+.[0-9]+*'
- 'stacflow-v[0-9]+.[0-9]+.[0-9]+*'
# Redeploy the site without cutting a release.
workflow_dispatch:

jobs:
# The quality gate: the release only ships with full pub.dev points.
# Comparing granted against pana's own max (160 today) keeps the gate
# honest if pub.dev ever raises the ceiling.
meta:
name: Resolve package from tag
runs-on: ubuntu-latest
outputs:
package: ${{ steps.tag.outputs.package }}
dir: ${{ steps.tag.outputs.dir }}
version: ${{ steps.tag.outputs.version }}
steps:
- uses: actions/checkout@v7

- id: tag
shell: bash
run: |
if [ "$GITHUB_EVENT_NAME" != push ]; then
printf 'package=flow_ui\ndir=packages/flow_ui\nversion=\n' >> "$GITHUB_OUTPUT"
exit 0
fi
tag="$GITHUB_REF_NAME"
package="${tag%-v*}"
version="${tag#"$package-v"}"
case "$package" in
flow_ui) dir=packages/flow_ui ;;
stacflow) dir=packages/stacflow ;;
*) echo "::error::no package for tag $tag"; exit 1 ;;
esac
pubspec_version=$(sed -n 's/^version:[[:space:]]*//p' "$dir/pubspec.yaml" | tr -d '[:space:]')
if [ "$version" != "$pubspec_version" ]; then
echo "::error::tag says $version, $dir/pubspec.yaml says $pubspec_version"
exit 1
fi
printf 'package=%s\ndir=%s\nversion=%s\n' "$package" "$dir" "$version" >> "$GITHUB_OUTPUT"

# The release only ships with full pub.dev points.
pana:
name: Pana score gate
needs: meta
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
Expand All @@ -42,11 +71,11 @@ jobs:
- name: Require a perfect score
run: |
dart pub global activate pana
dart pub global run pana --no-warning --json . > pana.json
dart pub global run pana --no-warning --json "${{ needs.meta.outputs.dir }}" > pana.json
granted=$(jq -r '.scores.grantedPoints' pana.json)
max=$(jq -r '.scores.maxPoints' pana.json)
echo "pana: $granted / $max"
if [ "$granted" != "$max" ]; then
if [ "$granted" -lt "$max" ]; then
echo "Points were dropped in these sections:"
jq -r '.report.sections[]
| select(.grantedPoints < .maxPoints)
Expand All @@ -56,22 +85,21 @@ jobs:
fi

package:
# Manual dispatches redeploy the site only — pub.dev is tags-only.
if: github.event_name == 'push'
needs: pana
needs: [meta, pana]
permissions:
id-token: write # Required to mint the OIDC token pub.dev verifies.
id-token: write
uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1
with:
working-directory: ${{ needs.meta.outputs.dir }}

site:
name: Docs + playground
needs: pana
needs: [meta, pana]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

# `build:site` compiles the playground to wasm before Astro builds,
# so this job needs both toolchains — the same build CI validates.
- uses: subosito/flutter-action@v2
with:
channel: stable
Expand Down
16 changes: 6 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,13 @@ migrate_working_dir/
*.iws
.idea/
.env
.codex/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
# Flutter/Dart/Pub related. The root pubspec.lock is committed: the
# workspace holds apps.
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/
build/
coverage/
pubspec_overrides.yaml
39 changes: 0 additions & 39 deletions .pubignore

This file was deleted.

Loading
Loading