ci: publish Dart packages with OIDC#4
Conversation
jwfing
left a comment
There was a problem hiding this comment.
Summary
Adds OIDC-based per-package pub.dev publishing workflows, a version-lockstep validate gate, CI format + dry-run checks, and a repo-wide dart format sweep to make the new format gate green — solid and mostly idiomatic, but the publish validate job can't resolve the workspace without the Flutter SDK, which will fail every release.
Requirements context
No spec/plan under docs/superpowers/ matches this change — the specs there (docs/superpowers/specs/2026-06-08-insforge-flutter-sdk-design.md and the plans/ files) cover the SDK's core/auth/database/storage/functions/ai design, not CI or release automation. Assessed against the PR description, CONTRIBUTING.md, and repo conventions (analysis_options.yaml, melos.yaml, root pub workspace). External action/workflow references were verified live: actions/checkout@v7 (exists, Jun 2026), dart-lang/setup-dart@v1.7.2 and the reusable .../publish.yml@v1.7.2 (exists, Mar 2026; sets up both Flutter and Dart, so the publish jobs themselves are fine).
Findings
Critical
Functionality — the publish validate job installs only Dart but must resolve a Flutter workspace — .github/workflows/publish-insforge.yml:18,31 and .github/workflows/publish-insforge-flutter.yml:18,31
The validate job sets up only the standalone Dart SDK (dart-lang/setup-dart@v1.7.2, line 18) and then runs dart run tool/set_version.dart --check (line 31). On a fresh runner there is no resolved package config — .dart_tool/ and pubspec.lock are both gitignored (.gitignore), so nothing is checked out. dart run therefore triggers an implicit resolution of the root pub workspace (pubspec.yaml: _insforge_workspace), whose members include packages/insforge_flutter, which depends on flutter: sdk: flutter. Pub cannot resolve an sdk: flutter dependency without the Flutter SDK installed, so the step fails before _check()'s file-reading logic ever runs — and because publish has needs: validate, publication never happens.
This is exactly the constraint the author already documented in .github/workflows/ci.yaml:9-11: "the whole workspace must be resolved with the Flutter toolchain (which bundles Dart)." The analyze-and-test job honors it (subosito/flutter-action + flutter pub get); the two new validate jobs do not. The PR's "version sync check" validation was presumably run in that Flutter-enabled context, so this path is untested (it only fires on a tag push).
Suggested fix: set up Flutter in the validate job (mirror CI with subosito/flutter-action@v2.23.0 + flutter pub get before the check), or make the check self-contained so it doesn't require workspace resolution.
Suggestion
- Security / supply chain — pin third-party actions by commit SHA (
publish-*.yml:16-18,ci.yaml). These workflows carryid-token: writepublish privileges.subosito/flutter-action@v2.23.0in particular is a third-party action pinned to a mutable tag; pinning to a full commit SHA is the standard hardening for release-privileged workflows.dart-lang/setup-dartandactions/checkoutare first-party/reputable, so this is lower risk there. Non-blocking. - Functionality — core-before-flutter release order is doc-only (
CONTRIBUTING.md:139-160,publish-insforge-flutter.yml). Nothing stops someone pushinginsforge_flutter-vX.Y.Zbefore the matchinginsforgeversion is live on pub.dev. The failure mode is a failed publish (fail-safe, not data loss), but the flutter validate job could assert the core version is already available on pub.dev to fail fast with a clear message. Non-blocking.
Information
- Software engineering — large formatting sweep bundled with the feature (~25 lib/test files). This is justified — the new
dart format --set-exit-if-changedgate plus the existingrequire_trailing_commaslint (analysis_options.yaml) require the tree to be pre-formatted, and the PR body calls this out. It does enlarge the diff and mix concerns; a separate "style" PR (like the earlier9833ade style: apply Dart formatter) would keep the feature diff focused. No behavior change in the reformatted code. - Scope —
actions/checkout@v4 → v7(ci.yaml) is unrelated to the OIDC goal, but v7 is valid and harmless. - Functionality — tag/version
sedextraction is whitespace-sensitive (publish-*.yml:24).sed -n 's/^version:[[:space:]]*//p'is correct for the current cleanversion: 0.1.0line, but would misbehave if a trailing comment were ever added to the version line. Minor. - Security (positive) — good practices: OIDC with no stored pub.dev token, minimal
permissions(contents: readfor validate,id-token: writefor publish), andRELEASE_TAGpassed viaenv:rather than interpolated into the shell script (avoids injection). - Performance — no performance-relevant changes; this is CI/release tooling only.
Verdict
request_changes — one Critical: the Dart-only publish validate jobs cannot resolve the Flutter-containing workspace and will fail on every tagged release, blocking publication. Everything else is non-blocking. Once the validate jobs set up Flutter (or the check is made resolution-free), this is in good shape.
There was a problem hiding this comment.
All reported issues were addressed across 34 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
jwfing
left a comment
There was a problem hiding this comment.
Review: ci: publish Dart packages with OIDC
Summary: A well-constructed release-infra PR that adds OIDC-based pub.dev publishing for both monorepo packages with lockstep version gating; the substantive workflow logic is correct and security-conscious, and the large remaining diff is behavior-preserving auto-formatting to make the new dart format gate start green.
Requirements context
docs/superpowers/ contains the SDK design spec and per-feature plans (specs/2026-06-08-insforge-flutter-sdk-design.md, plans/…-01…07), but none cover release/CI/publishing — this PR is release infrastructure not described by any spec. Assessed against the PR description, CONTRIBUTING.md, and the official dart.dev automated-publishing guidance. I verified the OIDC setup against dart.dev docs via context7 and the pinned action SHAs against the GitHub API.
Findings
Critical
(none)
Suggestion
- Software engineering / DRY — version parsing duplicated (
.github/workflows/publish-insforge.yml:24-29,.github/workflows/publish-insforge-flutter.yml:26-31). The tag/version match uses ased -n 's/^version:[[:space:]]*//p'one-liner, whiletool/set_version.dartalready owns version parsing. Thesedform doesn't strip a trailing inline comment or CR and re-implements logic that lives elsewhere. Low blast radius (pubspecversion:lines are simple), but reusing the tool (or a shared composite step) would keep a single source of truth. - Testing — new strict SemVer regex is untested (
tool/set_version.dart:21-26)._semverwas rewritten from a loose pattern to a strict SemVer regex, but there's notest/fortool/, and the regex is only exercised on thesetpath (not--check), so CI won't catch a regression. A handful of table cases (accept0.1.0,1.2.3-beta.1,1.2.3+build; reject01.2.3,1.2) would lock the behavior cheaply.
Information
- Security / hardening — no deployment environment (
publish-insforge.yml:57-63,publish-insforge-flutter.yml:70-76). The PR deliberately opts out of a GitHub Actionsenvironment: pub.dev(documented by dart.dev as optional). That's a valid choice; noting only that an environment with protection rules (required reviewers / ref restrictions) is the available defense-in-depth layer if you later want to constrain who/what can trigger a publish. - Security — action pinning verified. All three pinned SHAs resolve to exactly their claimed tags via the GitHub API:
actions/checkout@9c091bb= v7,subosito/flutter-action@1a44944= v2.23.0,dart-lang/setup-dart@65eb853= v1.7.2 (action + reusablepublish.ymlpinned to the same SHA). Per-jobpermissionsare minimal and thepublishjob'sid-token: write-only grant matches the official dart.dev reusable-workflow pattern. OIDC replaces a stored pub.dev token — good. - Functionality — pub.dev propagation window (
publish-insforge-flutter.yml:47-56). The "matching insforge version is published" gate polls with--retry 5 --retry-delay 10(~50s). pub.dev propagation after a publish can occasionally exceed that; if it does, the flutter release job fails but is safely re-runnable, so this is acceptable — just flagging the tight-ish window. - Functionality — build-metadata tag pattern (
publish-insforge.yml:8,publish-insforge-flutter.yml:9). The…\+*pattern admits+buildtags, but build metadata is unusual for published packages and pub.dev strips it, so the…/versions/$RELEASE_VERSIONlookup may not resolve a+buildversion. Edge case only. - Software engineering — the bulk diff is formatting. The library/test changes across
packages/insforge/**andintegration_tests/**aredart formatreflows (line-wrapping, trailing-comma normalization, blank-line trims) landed to make the newdart format --set-exit-if-changedgate green. Spot-checked several semantically load-bearing spots (rpc_test.dartchained.rpc(...).execute(),database_test.dartinFilter(...).execute(), batch-insert maps) — all behavior-preserving. - Correctness cross-check.
resolution: workspaceinpackages/insforge_flutter/pubspec.yamlmeansdart pub publish --dry-runresolves theinsforgedep from the local workspace, so the dry-run inci.yamldoesn't depend on pub.dev availability; the runtime ordering constraint (core live before flutter tag) is enforced only at the real publish step, which is correct. Current versions are in lockstep (0.1.0across both pubspecs +insforge: ^0.1.0), soset_version.dart --checkpasses.
Verdict
approved (informational — a human still gives the explicit GitHub approval). Zero Critical findings; the Suggestions and Information items are non-blocking. This is a clean, docs-aligned release pipeline: OIDC over a stored token, SHA-pinned + verified actions, minimal per-job permissions, lockstep version validation, and a green-on-arrival formatter gate.
What changed
insforgeandinsforge_flutterpubspec.yamland enforce lockstep package/runtime versionsRelease behavior
The packages remain versioned in lockstep but are published with separate tags:
insforge-vX.Y.Zinsforge_flutter-vX.Y.Zfrom the same release commitNo pub.dev token or GitHub deployment environment is used.
Validation
0.1.0in sync across both packages and runtime constantpub.dev setup after merge
Enable GitHub Actions automated publishing on both existing packages:
insforge: repositoryInsForge/insforge-flutter, tag patterninsforge-v{{version}}insforge_flutter: repositoryInsForge/insforge-flutter, tag patterninsforge_flutter-v{{version}}Do not require a GitHub Actions environment.
Summary by cubic
Adds OIDC-based publishing for
insforgeandinsforge_flutterusing package-specific tags, with release gating and auto GitHub Releases for stable tags. CI enforces format/analyze/tests, runs publish dry-runs, pins actions, and trims published artifacts.New Features
dart-lang/setup-dart/.github/workflows/publish.yml@v1.7.2using OIDC; stable tags auto-create a GitHub Release after pub.dev succeeds; no pub.dev token or GitHub environment.pubspec.yaml, lockstep versions viatool/set_version.dart --check, andinsforge_flutterwaits for the matchinginsforgeversion to be live on pub.dev; release jobs reuse CI viaworkflow_call.dart format --set-exit-if-changed, analyze, tests, anddart pub publish --dry-runfor both packages; actions pinned by commit SHA; weekly Dependabot forgithub-actions..pubignoreupdated in both packages to exclude.dart_tool/,build/, andtest/.Migration
insforge: repoInsForge/insforge-flutter, tag patterninsforge-v{{version}}insforge_flutter: repoInsForge/insforge-flutter, tag patterninsforge_flutter-v{{version}}insforge-vX.Y.Z, wait until live on pub.dev, then pushinsforge_flutter-vX.Y.Zfrom the same commit.Written for commit 9e9917f. Summary will update on new commits.