ci: automate pub.dev publishing in dependency order#176
Conversation
Add a one-click `release.yaml` (workflow_dispatch) that releases every package whose pubspec version is not yet on pub.dev, in dependency order (solidart -> flutter_solidart -> solidart_hooks/solidart_lint), waiting for each to become available before tagging its dependents, and skipping any version already published. - scripts/release.sh: read pubspec version, skip if already on pub.dev, guard against existing-but-unpublished tags, create GitHub release+tag (notes from CHANGELOG), then poll pub.dev until the version is live. - release.yaml: workflow_dispatch with a dry_run toggle; uses RELEASE_PAT so the pushed tags trigger the existing publish_*.yaml workflows (the default GITHUB_TOKEN would not). - publish_*.yaml: widen tag globs to also match prerelease (-dev.N) and build (+N) tags, e.g. `<pkg>-v[0-9]+.[0-9]+.[0-9]+*`, so current -dev versions actually trigger publishing. The solidart DevTools-extension build step is untouched; the orchestrator only triggers the existing per-package workflows via tags.
WalkthroughAdds a new ChangesRelease orchestration and tag trigger consolidation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #176 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 18 18
Lines 892 892
=========================================
Hits 892 892 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
.github/workflows/release.yaml (2)
26-28: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winDrop persisted checkout credentials.
This job only needs repository contents after checkout, but
actions/checkoutwill still leave a write-capable token in the local git config. Settingpersist-credentials: falseremoves an unnecessary credential from the workspace.Suggested change
- uses: actions/checkout@v4 with: fetch-depth: 0 # full history + tags + persist-credentials: false🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release.yaml around lines 26 - 28, The release workflow checkout step is leaving persisted git credentials in the workspace, which is unnecessary for this job. Update the existing actions/checkout@v4 step in the release workflow to disable credential persistence by setting persist-credentials to false alongside fetch-depth, so the job only keeps repository contents and not a write-capable token.Source: Linters/SAST tools
22-24: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSerialize manual release runs.
This workflow performs non-idempotent external writes, so two
workflow_dispatchruns can race past the early checks and leave one run failing mid-release. A workflow-level concurrency group would make retries deterministic.[details omitted in scratchpad]
Suggested change
permissions: contents: write +concurrency: + group: release-pubdev + cancel-in-progress: false + jobs:🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release.yaml around lines 22 - 24, Add a workflow-level concurrency guard to the release workflow so only one manual release run can execute at a time. Update the release job/workflow in release.yaml by introducing a concurrency group tied to the workflow or ref, and set it to cancel or queue overlapping workflow_dispatch runs; use the existing release job definition as the anchor for where to apply it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/publish_flutter_solidart.yaml:
- Line 6: The tag filter in the publish workflow is using regex-like syntax
instead of a GitHub Actions glob, so it will not match the release tags emitted
by scripts/release.sh. Update the tags pattern in this workflow to a valid glob
that matches the <name>-v<version> tag shape, and apply the same change to the
sibling publish workflows that duplicate this filter. If semver validation is
still required, enforce it inside the job rather than in the trigger pattern.
In `@scripts/release.sh`:
- Around line 48-55: The is_published helper currently swallows curl/HTTP
failures and returns “not published,” which can let release tagging proceed
during pub.dev outages. Update is_published to return a distinct
non-published/non-error status for successful lookups only, and make the caller
path that reaches gh release create abort on lookup failures while keeping the
polling logic retryable. Use is_published, curl, and the gh release create flow
in scripts/release.sh to locate the change.
---
Nitpick comments:
In @.github/workflows/release.yaml:
- Around line 26-28: The release workflow checkout step is leaving persisted git
credentials in the workspace, which is unnecessary for this job. Update the
existing actions/checkout@v4 step in the release workflow to disable credential
persistence by setting persist-credentials to false alongside fetch-depth, so
the job only keeps repository contents and not a write-capable token.
- Around line 22-24: Add a workflow-level concurrency guard to the release
workflow so only one manual release run can execute at a time. Update the
release job/workflow in release.yaml by introducing a concurrency group tied to
the workflow or ref, and set it to cancel or queue overlapping workflow_dispatch
runs; use the existing release job definition as the anchor for where to apply
it.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8114e959-bdbb-482c-9bc4-05c9a7c87a9d
📒 Files selected for processing (6)
.github/workflows/publish_flutter_solidart.yaml.github/workflows/publish_solidart.yaml.github/workflows/publish_solidart_hooks.yaml.github/workflows/publish_solidart_lint.yaml.github/workflows/release.yamlscripts/release.sh
| tags: | ||
| - 'flutter_solidart-v[0-9]+.[0-9]+.[0-9]+' # Matches flutter_solidart-v1.2.3 | ||
| - 'flutter_solidart-v[0-9]+.[0-9]+.[0-9]+\+[0-9]+' # Matches flutter_solidart-v1.2.3+1 | ||
| - "flutter_solidart-v[0-9]+.[0-9]+.[0-9]+*" # Matches flutter_solidart-v1.2.3, -v1.2.3+1 (build), -v1.2.3-dev.1 (prerelease) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
git ls-files .github/workflows scripts | sed -n '1,200p'Repository: nank1ro/solidart
Length of output: 439
🏁 Script executed:
sed -n '1,220p' .github/workflows/publish_flutter_solidart.yaml
printf '\n---\n'
sed -n '1,220p' .github/workflows/publish_solidart.yaml
printf '\n---\n'
sed -n '1,220p' .github/workflows/publish_solidart_hooks.yaml
printf '\n---\n'
sed -n '1,220p' .github/workflows/publish_solidart_lint.yaml
printf '\n---\n'
sed -n '1,220p' scripts/release.shRepository: nank1ro/solidart
Length of output: 7528
🌐 Web query:
GitHub Actions workflow syntax push tags glob pattern square brackets plus literal semver tag matching
💡 Result:
GitHub Actions workflow on.push.tags filters support glob patterns, not regular expressions [1]. While glob syntax includes some special characters, it does not support full regex character classes or quantifier-based pattern matching (such as [0-9]+) [1]. Key technical details regarding your query include: 1. Glob vs. Regex: The tags filter uses glob patterns (e.g., *, ``, ?, `!`) [1][4][8]. Attempts to use regex-style syntax like `[0-9]+` will not function as regex; GitHub Actions will interpret these literally or fail to match them against the tag string [1]. 2. Square Brackets in YAML: Square brackets `[` and `]` are special characters in YAML [2]. If your tag pattern starts with `[` or contains them in a way that YAML interprets as a flow sequence, you must enclose the entire pattern in quotes (e.g., `'[pattern]'`) to ensure it is treated as a string literal [2][3]. 3. Literal Matching: If a tag name happens to contain glob characters (like ``, `?`, or `+`) and you want to match that tag literally, you must escape those characters with a backslash `` [4][5][6]. 4. SemVer Matching: Because GitHub Actions does not support regex, there is no native way to perform complex SemVer range matching (e.g., "all tags between 1.0.0 and 2.0.0") directly in the workflow trigger. Common strategies include using broad glob patterns like `v1.` or `v*` and then using a conditional step within the workflow (e.g., `if: ${{ startsWith(github.ref_name, 'v1.') }}`) or a dedicated action to validate the version [7][1]. In summary, you cannot use regex for tag filtering. You should rely on glob patterns for broad inclusion/exclusion and handle specific version logic inside the workflow job itself.
Citations:
- 1: https://stackoverflow.com/questions/68078004/why-doesnt-my-github-action-trigger-on-a-regex-tag
- 2: Note on the use of square brackets in GitHub Actions workflows filter patterns github/docs#18969
- 3: https://github.com/github/docs/blob/main/content/actions/reference/workflows-and-actions/workflow-syntax.md
- 4: https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax
- 5: https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions
- 6: http://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow
- 7: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows
Fix the tag filter syntax GitHub Actions treats tags: as globs, not regex, so this pattern does not match the <name>-v<version> tags emitted by scripts/release.sh. Use a valid glob and validate the version in the job if semver constraints are needed. The same pattern is duplicated in the sibling publish workflows.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/publish_flutter_solidart.yaml at line 6, The tag filter in
the publish workflow is using regex-like syntax instead of a GitHub Actions
glob, so it will not match the release tags emitted by scripts/release.sh.
Update the tags pattern in this workflow to a valid glob that matches the
<name>-v<version> tag shape, and apply the same change to the sibling publish
workflows that duplicate this filter. If semver validation is still required,
enforce it inside the job rather than in the trigger pattern.
| # True when <version> ($2) is already published for package <name> ($1). | ||
| # A cache-busting query + no-cache header avoid a stale CDN response masking a | ||
| # just-published version. Network/HTTP errors are treated as "not published". | ||
| is_published() { | ||
| local body | ||
| body="$(curl -fsS -H 'Cache-Control: no-cache' "${PUB_API}/$1?_=${RANDOM}" 2>/dev/null || true)" | ||
| [ -n "$body" ] || return 1 | ||
| printf '%s' "$body" | jq -e --arg v "$2" '.versions[]?.version | select(. == $v)' >/dev/null 2>&1 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Fail closed when the pub.dev lookup errors.
Treating curl/HTTP failures as “not published” breaks the workflow’s main safety guarantee. A transient pub.dev outage can make this script create a new release/tag for a version that is already live, which then triggers a downstream publish that can only fail. Return a distinct status for lookup failures and abort before gh release create; only the polling path should treat lookup errors as retryable.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/release.sh` around lines 48 - 55, The is_published helper currently
swallows curl/HTTP failures and returns “not published,” which can let release
tagging proceed during pub.dev outages. Update is_published to return a distinct
non-published/non-error status for successful lookups only, and make the caller
path that reaches gh release create abort on lookup failures while keeping the
polling logic retryable. Use is_published, curl, and the gh release create flow
in scripts/release.sh to locate the change.
What
One-click release orchestration. Adds a
workflow_dispatchworkflow that publishes every package whose pubspec version is not yet on pub.dev, in dependency order, waiting for each to become available before releasing its dependents — and skipping any version already published. This automates the manual "create release+tag → wait for pub.dev → repeat for the next package" loop.Dependency order:
solidart → flutter_solidart → solidart_hooks / solidart_lint.Changes
scripts/release.sh(new) — for each package in order: readversion:, skip if already on pub.dev, abort with guidance if the tag exists but isn't published, create a GitHub Release + tag<name>-v<version>(notes from the matching CHANGELOG section,--prereleasefor-devversions), then poll pub.dev (up to 30 min, cache-busted) until the version is live..github/workflows/release.yaml(new) —workflow_dispatchwith adry_runtoggle. Usessecrets.RELEASE_PATso the tags it creates trigger the existingpublish_*.yamlworkflows (the defaultGITHUB_TOKENwould not, due to GitHub's anti-recursion rule).publish_*.yaml(4 files) — widen each tag glob to<pkg>-v[0-9]+.[0-9]+.[0-9]+*so current-dev.Nprerelease tags (and+Nbuild tags) actually trigger publishing. ThesolidartDevTools-extension build step is untouched.Required setup
RELEASE_PAT: fine-grained PAT scoped to this repo with Contents: write (already added).How it was verified
bash -nclean; fullDRY_RUNrun listed all 4 packages in order with correct tags.is_publishedcorrectly distinguished published (solidart 2.8.6) from unpublished (3.0.0-dev.1,999.0.0).How to use after merge
Actions → "Release to pub.dev (orchestrated)" → Run workflow. Start with
dry_run: trueto preview, then run withfalse.Summary by CodeRabbit
New Features
Bug Fixes