Add publish workflow for pub.dev#109
Conversation
Automates package publishing on tag push (v*) or manual dispatch. Publishes all 3 packages in order with version consistency checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR introduces a GitHub Actions workflow to automate publishing the three Purchasely Flutter packages ( The overall structure is sound and follows the existing CI pattern in the repository. However, there are a few issues worth addressing before relying on this in production:
Confidence Score: 3/5
|
| Filename | Overview |
|---|---|
| .github/workflows/publish.yml | New GitHub Actions workflow that automates pub.dev publishing for three packages sequentially. Key concerns: dry-run failures for dependent packages are fully suppressed masking real validation errors (P1), a hardcoded 30-second sleep may be insufficient for pub.dev indexing between sequential publishes, and no GitHub Environment approval gate protects against accidental irreversible publishes triggered by any tag push. |
Sequence Diagram
sequenceDiagram
participant Dev as Developer
participant GH as GitHub Actions
participant PubDev as pub.dev
Dev->>GH: Push tag v* (or manual dispatch)
GH->>GH: validate job: check version consistency
GH->>GH: validate job: flutter pub publish --dry-run (purchasely_flutter)
GH->>GH: validate job: dry-run purchasely_google (failures suppressed)
GH->>GH: validate job: dry-run purchasely_android_player (failures suppressed)
GH->>PubDev: publish job: flutter pub publish --force (purchasely_flutter)
GH->>GH: sleep 30s (fixed delay)
GH->>PubDev: publish job: flutter pub publish --force (purchasely_google)
Note over GH,PubDev: purchasely_flutter: any resolved from pub.dev<br/>(may not be indexed yet after only 30s)
GH->>PubDev: publish job: flutter pub publish --force (purchasely_android_player)
GH->>GH: Write summary to GITHUB_STEP_SUMMARY
Prompt To Fix All With AI
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 116-117
Comment:
**Hardcoded sleep may be insufficient for pub.dev indexing**
A static `sleep 30` is commonly not enough for pub.dev to fully index a newly published package. pub.dev indexing can take anywhere from 1 to 5+ minutes depending on load. When `flutter pub get` subsequently runs for `purchasely_google`, it resolves `purchasely_flutter: any` from pub.dev. If the new version isn't indexed yet, it silently resolves to the previous version, meaning `purchasely_google` could be published with a stale resolved dependency.
Consider replacing the static sleep with a retry loop that polls the pub.dev API until the new version is confirmed available:
```yaml
- name: Wait for pub.dev indexing
run: |
VERSION=$(grep '^version:' purchasely/pubspec.yaml | awk '{print $2}')
echo "Waiting for purchasely_flutter $VERSION to be indexed on pub.dev..."
for i in $(seq 1 20); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pub.dev/api/packages/purchasely_flutter/versions/$VERSION")
if [[ "$STATUS" == "200" ]]; then
echo "Package indexed successfully."
break
fi
echo "Attempt $i: not indexed yet (HTTP $STATUS), retrying in 30s..."
sleep 30
done
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 74-84
Comment:
**Suppressed dry-run failures hide real validation errors**
Both the `purchasely_google` and `purchasely_android_player` dry-run steps use `|| echo "::warning::"` to swallow failures. The intent is to tolerate the expected failure caused by the local path override making the package unpublishable. However, this also silently suppresses **any other real issue** — e.g., missing files, invalid `pubspec.yaml` entries, or analysis errors — giving false confidence that validation passed when it may not have.
Since the `purchasely_flutter` path override is already applied via `pubspec_overrides.yaml` before these steps, the dry-run has some value. Consider at least capturing the exit code and emitting a more visible warning, or validating only the fields that don't require the dependency to be live (e.g., `dart pub publish --dry-run 2>&1 | grep -v "path dependency"`). At minimum, document in a comment that *any* failure (not just the dependency one) is being masked:
```yaml
- name: Dry run (purchasely_google)
working-directory: purchasely_google
# NOTE: This dry-run is expected to fail due to the local path override for
# purchasely_flutter. ALL failures are suppressed — fix real issues before merging.
run: flutter pub publish --dry-run || echo "::warning::purchasely_google dry-run failed (expected if purchasely_flutter not yet published)"
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 89-93
Comment:
**No environment protection gate for irreversible publish**
Publishing to pub.dev is irreversible — a version can only be "retracted" (not deleted), and retraction still leaves the version visible and accessible. The `publish` job triggers automatically on any `v*` tag push with no additional human approval step. An accidental `git push origin v5.8.0` immediately publishes all three packages to pub.dev.
Consider adding a [GitHub Environment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment) with required reviewers:
```yaml
publish:
name: Publish packages
runs-on: ubuntu-latest
needs: [validate]
environment: pub-dev-production # requires manual approval
if: ${{ github.ref_type == 'tag' || (github.event_name == 'workflow_dispatch' && inputs.dry_run == false) }}
```
Then define `pub-dev-production` in GitHub → Settings → Environments with at least one required reviewer. This adds a human gate before any publication occurs.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: 3f850fb
| - name: Wait for pub.dev indexing | ||
| run: sleep 30 |
There was a problem hiding this comment.
Hardcoded sleep may be insufficient for pub.dev indexing
A static sleep 30 is commonly not enough for pub.dev to fully index a newly published package. pub.dev indexing can take anywhere from 1 to 5+ minutes depending on load. When flutter pub get subsequently runs for purchasely_google, it resolves purchasely_flutter: any from pub.dev. If the new version isn't indexed yet, it silently resolves to the previous version, meaning purchasely_google could be published with a stale resolved dependency.
Consider replacing the static sleep with a retry loop that polls the pub.dev API until the new version is confirmed available:
- name: Wait for pub.dev indexing
run: |
VERSION=$(grep '^version:' purchasely/pubspec.yaml | awk '{print $2}')
echo "Waiting for purchasely_flutter $VERSION to be indexed on pub.dev..."
for i in $(seq 1 20); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pub.dev/api/packages/purchasely_flutter/versions/$VERSION")
if [[ "$STATUS" == "200" ]]; then
echo "Package indexed successfully."
break
fi
echo "Attempt $i: not indexed yet (HTTP $STATUS), retrying in 30s..."
sleep 30
donePrompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 116-117
Comment:
**Hardcoded sleep may be insufficient for pub.dev indexing**
A static `sleep 30` is commonly not enough for pub.dev to fully index a newly published package. pub.dev indexing can take anywhere from 1 to 5+ minutes depending on load. When `flutter pub get` subsequently runs for `purchasely_google`, it resolves `purchasely_flutter: any` from pub.dev. If the new version isn't indexed yet, it silently resolves to the previous version, meaning `purchasely_google` could be published with a stale resolved dependency.
Consider replacing the static sleep with a retry loop that polls the pub.dev API until the new version is confirmed available:
```yaml
- name: Wait for pub.dev indexing
run: |
VERSION=$(grep '^version:' purchasely/pubspec.yaml | awk '{print $2}')
echo "Waiting for purchasely_flutter $VERSION to be indexed on pub.dev..."
for i in $(seq 1 20); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pub.dev/api/packages/purchasely_flutter/versions/$VERSION")
if [[ "$STATUS" == "200" ]]; then
echo "Package indexed successfully."
break
fi
echo "Attempt $i: not indexed yet (HTTP $STATUS), retrying in 30s..."
sleep 30
done
```
How can I resolve this? If you propose a fix, please make it concise.| - name: Dry run (purchasely_google) | ||
| working-directory: purchasely_google | ||
| run: flutter pub publish --dry-run || echo "::warning::purchasely_google dry-run failed (expected if purchasely_flutter not yet published)" | ||
|
|
||
| - name: Install dependencies (purchasely_android_player) | ||
| working-directory: purchasely_android_player | ||
| run: flutter pub get | ||
|
|
||
| - name: Dry run (purchasely_android_player) | ||
| working-directory: purchasely_android_player | ||
| run: flutter pub publish --dry-run || echo "::warning::purchasely_android_player dry-run failed (expected if purchasely_flutter not yet published)" |
There was a problem hiding this comment.
Suppressed dry-run failures hide real validation errors
Both the purchasely_google and purchasely_android_player dry-run steps use || echo "::warning::" to swallow failures. The intent is to tolerate the expected failure caused by the local path override making the package unpublishable. However, this also silently suppresses any other real issue — e.g., missing files, invalid pubspec.yaml entries, or analysis errors — giving false confidence that validation passed when it may not have.
Since the purchasely_flutter path override is already applied via pubspec_overrides.yaml before these steps, the dry-run has some value. Consider at least capturing the exit code and emitting a more visible warning, or validating only the fields that don't require the dependency to be live (e.g., dart pub publish --dry-run 2>&1 | grep -v "path dependency"). At minimum, document in a comment that any failure (not just the dependency one) is being masked:
- name: Dry run (purchasely_google)
working-directory: purchasely_google
# NOTE: This dry-run is expected to fail due to the local path override for
# purchasely_flutter. ALL failures are suppressed — fix real issues before merging.
run: flutter pub publish --dry-run || echo "::warning::purchasely_google dry-run failed (expected if purchasely_flutter not yet published)"Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 74-84
Comment:
**Suppressed dry-run failures hide real validation errors**
Both the `purchasely_google` and `purchasely_android_player` dry-run steps use `|| echo "::warning::"` to swallow failures. The intent is to tolerate the expected failure caused by the local path override making the package unpublishable. However, this also silently suppresses **any other real issue** — e.g., missing files, invalid `pubspec.yaml` entries, or analysis errors — giving false confidence that validation passed when it may not have.
Since the `purchasely_flutter` path override is already applied via `pubspec_overrides.yaml` before these steps, the dry-run has some value. Consider at least capturing the exit code and emitting a more visible warning, or validating only the fields that don't require the dependency to be live (e.g., `dart pub publish --dry-run 2>&1 | grep -v "path dependency"`). At minimum, document in a comment that *any* failure (not just the dependency one) is being masked:
```yaml
- name: Dry run (purchasely_google)
working-directory: purchasely_google
# NOTE: This dry-run is expected to fail due to the local path override for
# purchasely_flutter. ALL failures are suppressed — fix real issues before merging.
run: flutter pub publish --dry-run || echo "::warning::purchasely_google dry-run failed (expected if purchasely_flutter not yet published)"
```
How can I resolve this? If you propose a fix, please make it concise.| publish: | ||
| name: Publish packages | ||
| runs-on: ubuntu-latest | ||
| needs: [validate] | ||
| if: ${{ github.ref_type == 'tag' || (github.event_name == 'workflow_dispatch' && inputs.dry_run == false) }} |
There was a problem hiding this comment.
No environment protection gate for irreversible publish
Publishing to pub.dev is irreversible — a version can only be "retracted" (not deleted), and retraction still leaves the version visible and accessible. The publish job triggers automatically on any v* tag push with no additional human approval step. An accidental git push origin v5.8.0 immediately publishes all three packages to pub.dev.
Consider adding a GitHub Environment with required reviewers:
publish:
name: Publish packages
runs-on: ubuntu-latest
needs: [validate]
environment: pub-dev-production # requires manual approval
if: ${{ github.ref_type == 'tag' || (github.event_name == 'workflow_dispatch' && inputs.dry_run == false) }}Then define pub-dev-production in GitHub → Settings → Environments with at least one required reviewer. This adds a human gate before any publication occurs.
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 89-93
Comment:
**No environment protection gate for irreversible publish**
Publishing to pub.dev is irreversible — a version can only be "retracted" (not deleted), and retraction still leaves the version visible and accessible. The `publish` job triggers automatically on any `v*` tag push with no additional human approval step. An accidental `git push origin v5.8.0` immediately publishes all three packages to pub.dev.
Consider adding a [GitHub Environment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment) with required reviewers:
```yaml
publish:
name: Publish packages
runs-on: ubuntu-latest
needs: [validate]
environment: pub-dev-production # requires manual approval
if: ${{ github.ref_type == 'tag' || (github.event_name == 'workflow_dispatch' && inputs.dry_run == false) }}
```
Then define `pub-dev-production` in GitHub → Settings → Environments with at least one required reviewer. This adds a human gate before any publication occurs.
How can I resolve this? If you propose a fix, please make it concise.Replace PUB_CREDENTIALS secret with pub.dev OIDC automated publishing. Each package publishes in its own job with its own OIDC token. No secrets to maintain or rotate. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document the automated publish workflow (OIDC via GitHub Actions). Update release checklist to use tag-based publishing instead of local publish. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
v*) or manual dispatchpurchasely_flutter→purchasely_google→purchasely_android_playerPUB_CREDENTIALSsecret (already configured) for authenticationUsage
Automatic (next release)
Just push a tag and it publishes:
git tag -a v5.8.0 -m "Release 5.8.0" git push origin v5.8.0Manual
Go to Actions → "Publish to pub.dev" → Run workflow (uncheck "dry run" to actually publish)
Test plan
🤖 Generated with Claude Code