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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 15 additions & 18 deletions .github/scripts/check-release-metadata.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,27 @@
# is asked to approve it, if that package is inconsistent: pubspec, changelog,
# version.dart and the tag must all agree on the version.
#
# Inputs (env): EVENT github.event_name (push of a tag, or workflow_dispatch)
# TAG github.ref_name, read only for push
# PACKAGE workflow_dispatch input: path under packages/
# Inputs (env): REF_TYPE github.ref_type; must be `tag` (pub.dev rejects OIDC
# tokens from branch refs, even for workflow_dispatch)
# TAG github.ref_name: <pubspec name>-v<version>
# GITHUB_OUTPUT receives `package=<path under packages/>`
set -euo pipefail

fail() { echo "::error::$*"; exit 1; }

if [[ "$EVENT" == "push" ]]; then
# pub.dev only accepts tag pushes for push-triggered publishing, and the tag
# is the sole selector of the package: <pubspec name>-v<version>.
name="${TAG%%-v[0-9]*}"
case "$name" in
segment_analytics) PACKAGE=core ;;
segment_analytics_plugin_[a-z_]*) PACKAGE="plugins/plugin_${name#segment_analytics_plugin_}" ;;
*) fail "tag '$TAG' does not name a package in this repo" ;;
esac
fi
# pub.dev only accepts tokens from tag refs, for push and workflow_dispatch
# alike, so the tag is the sole selector of the package: <pubspec name>-v<version>.
[[ "${REF_TYPE:-}" == "tag" ]] || fail "run from a release tag (gh workflow run publish.yml --ref <tag>), not a ${REF_TYPE:-unknown} ref"
name="${TAG%%-v[0-9]*}"
case "$name" in
segment_analytics) PACKAGE=core ;;
segment_analytics_plugin_[a-z_]*) PACKAGE="plugins/plugin_${name#segment_analytics_plugin_}" ;;
*) fail "tag '$TAG' does not name a package in this repo" ;;
esac

# PACKAGE reaches working-directory in the workflow; reject anything that could
# escape packages/.
[[ "${PACKAGE:-}" =~ ^(core|plugins/plugin_[a-z_]+)$ ]] || fail "invalid package '${PACKAGE:-}'"
[[ "$PACKAGE" =~ ^(core|plugins/plugin_[a-z_]+)$ ]] || fail "invalid package '$PACKAGE'"
dir="packages/$PACKAGE"
[[ -f "$dir/pubspec.yaml" ]] || fail "$dir/pubspec.yaml not found"

Expand All @@ -33,11 +32,9 @@ version=$(sed -n 's/^version: *//p' "$dir/pubspec.yaml" | head -1)
[[ -n "$name" && -n "$version" ]] || fail "could not read name/version from $dir/pubspec.yaml"

# pub.dev publishes whatever pubspec.yaml says, so the tag must say the same.
if [[ "$EVENT" == "push" ]]; then
[[ "$TAG" == "$name-v$version" ]] || fail "tag '$TAG' != '$name-v$version' (pubspec.yaml in $dir)"
fi
[[ "$TAG" == "$name-v$version" ]] || fail "tag '$TAG' != '$name-v$version' (pubspec.yaml in $dir)"

# Whatever triggered the run, the code being published must be on main.
# The code being published must be on main.
git fetch -q origin main
git merge-base --is-ancestor "$GITHUB_SHA" origin/main || fail "$GITHUB_SHA is not on main"

Expand Down
17 changes: 6 additions & 11 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
name: Publish to pub.dev

# pub.dev automated publishing accepts only tag pushes and workflow_dispatch; a
# `release` event is rejected. The tag selects the package (see
# .github/scripts/check-release-metadata.sh). workflow_dispatch is for re-running
# a publish whose tag-triggered run failed.
# pub.dev automated publishing accepts only tag pushes and workflow_dispatch, and
# only from a tag ref: a dispatch on a branch is rejected ("this token has
# 'branch' refType"). The tag selects the package (see
# .github/scripts/check-release-metadata.sh). workflow_dispatch re-runs a release
# tag's publish: gh workflow run publish.yml --ref <tag>.
on:
push:
tags:
- 'segment_analytics-v[0-9]+.[0-9]+.[0-9]+*'
- 'segment_analytics_plugin_*-v[0-9]+.[0-9]+.[0-9]+*'
workflow_dispatch:
inputs:
package:
description: "Package to publish (core, or plugins/plugin_<name>)"
required: true
default: "core"

permissions:
id-token: write
Expand All @@ -41,9 +37,8 @@ jobs:
id: check
run: .github/scripts/check-release-metadata.sh
env:
EVENT: ${{ github.event_name }}
REF_TYPE: ${{ github.ref_type }}
TAG: ${{ github.ref_name }}
PACKAGE: ${{ github.event.inputs.package }}

test:
name: Pre-publish tests
Expand Down
14 changes: 11 additions & 3 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,21 @@ When releasing several packages, wait for core to appear on pub.dev before taggi

## Re-running a failed publish

If the tag-triggered run failed for a transient reason, re-run it from the Actions tab. If the tag points at the wrong commit, fix `main`, then delete and recreate the tag.
pub.dev only accepts publishes from a tag ref. A run dispatched on a branch, `main` included, fails with "this token has 'branch' refType".

The **Publish to pub.dev** workflow can also be started by hand (`workflow_dispatch`) with `package` set to `core` or `plugins/plugin_<name>`. It runs the same checks. Do not tag afterwards: the tag would start a second publish of a version that already exists, which fails.
- **Transient failure:** re-run the tag's run from the Actions tab, or `gh workflow run publish.yml --ref <tag>`. Both use the workflow and code as of the tag.
- **Wrong commit, or the workflow itself needed a fix:** fix `main`, then delete and recreate the tag on it. This is safe only while the version is not on pub.dev.

```
git push origin --delete <tag>
git tag -d <tag>
git tag <tag> # on the updated main
git push origin <tag>
```

## One-time setup

**GitHub:** the `production` environment (Settings → Environments) needs required reviewers, and its deployment branches and tags limited to `main` and the tag patterns `segment_analytics-v*` and `segment_analytics_plugin_*-v*`. The pub.dev environment requirement below only checks that the job names the environment; the reviewers are what make it a gate.
**GitHub:** the `production` environment (Settings → Environments) needs required reviewers, and its deployment branches and tags limited to the tag patterns `segment_analytics-v*` and `segment_analytics_plugin_*-v*`. The pub.dev environment requirement below only checks that the job names the environment; the reviewers are what make it a gate.

**pub.dev, per package:** on the package's **Admin** tab, under **Automated publishing**, choose **Publishing from GitHub Actions** (not the Google Cloud service account option) and set:

Expand Down
Loading