Created
July 28, 2025 08:22
-
-
Save javascripter/4b55a8fc218f79d622c782fbdb524f02 to your computer and use it in GitHub Desktop.
Canary EAS Rollout workflow (using PR label)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: EAS Rollout | |
on: | |
pull_request: | |
types: [labeled, unlabeled, closed, synchronize] | |
permissions: | |
pull-requests: write | |
contents: read | |
jobs: | |
rollout-start: | |
if: | |
| # Check if rollout label was added to PR or if new commit was pushed to PR with rollout label | |
(github.event.action == 'labeled' && | |
github.event.label.name == 'rollout') || | |
(github.event.action == 'synchronize' && | |
contains(github.event.pull_request.labels.*.name, 'rollout')) | |
runs-on: ubuntu-latest | |
concurrency: | |
group: rollout-${{ github.event.pull_request.number }} | |
cancel-in-progress: false | |
steps: | |
- name: π Setup repo | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 # π Required to retrieve git history | |
- name: π Setup Node.js | |
uses: actions/setup-node@v4 | |
with: | |
node-version: 20 | |
cache: 'npm' # or 'yarn', 'pnpm' depending on your package manager | |
- name: π¦ Install dependencies | |
run: npm ci # or yarn install, pnpm install | |
- name: π Setup Expo | |
uses: expo/expo-github-action@v8 | |
with: | |
eas-version: latest | |
token: ${{ secrets.EXPO_TOKEN }} | |
- name: π Setup Config | |
id: config | |
run: | # Modify this section to match your app's config | |
RUNTIME=$(npx expo config --json|jq -r '.runtimeVersion') | |
CHANNEL=$(jq -r '.build.production.channel' < eas.json) | |
echo "runtime=$RUNTIME" >> $GITHUB_OUTPUT | |
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT | |
echo "branch=rollout-pr-${{ github.event.number }}" >> $GITHUB_OUTPUT | |
echo "pr=${{ github.event.number }}" >> $GITHUB_OUTPUT | |
- name: π EAS Update | |
run: | | |
eas branch:create ${{ steps.config.outputs.branch }} --non-interactive || true | |
eas update \ | |
--branch=${{ steps.config.outputs.branch }} \ | |
--message "Canary rollout for PR #${{ steps.config.outputs.pr }}" | |
env: | |
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} | |
- name: π Start rollout | |
id: rollout | |
continue-on-error: true | |
run: | | |
BRANCH=${{ steps.config.outputs.branch }} | |
CHANNEL=${{ steps.config.outputs.channel }} | |
RUNTIME=${{ steps.config.outputs.runtime }} | |
echo "π Checking if rollout already exists on channel: $CHANNEL" | |
OUT=$(eas channel:rollout "$CHANNEL" \ | |
--action=view \ | |
--json \ | |
--non-interactive || echo "{}") | |
CURRENT_BRANCH=$(echo "$OUT" | jq -r '.currentRolloutInfo.rolledOutBranch.name // empty') | |
if [ "$CURRENT_BRANCH" = "$BRANCH" ]; then | |
echo "β Rollout already active for this PR branch ($BRANCH)" | |
echo "created=false" >> $GITHUB_OUTPUT | |
exit 0 | |
fi | |
if [ -n "$CURRENT_BRANCH" ]; then | |
echo "π« Rollout already active for another branch ($CURRENT_BRANCH)" | |
echo "created=failed" >> $GITHUB_OUTPUT | |
exit 1 | |
fi | |
echo "π Creating rollout for $BRANCH" | |
eas channel:rollout "$CHANNEL" \ | |
--action=create \ | |
--branch="$BRANCH" \ | |
--percent=5 \ | |
--runtime-version="$RUNTIME" \ | |
--non-interactive | |
echo "created=true" >> $GITHUB_OUTPUT | |
env: | |
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} | |
- name: π¬ Comment status on success | |
uses: marocchino/sticky-pull-request-comment@v2 | |
if: steps.rollout.outcome == 'success' && steps.rollout.outputs.created == 'true' | |
with: | |
number: ${{ steps.config.outputs.pr }} | |
header: 'Expo Canary Rollout Status' | |
message: | | |
# β Rollout Started | |
**Runtime:** `${{ steps.config.outputs.runtime }}` | |
**Channel:** `${{ steps.config.outputs.channel }}` | |
**Branch:** `${{ steps.config.outputs.branch }}` | |
**Rollout status:** `${{ steps.rollout.outcome }}` | |
PR #${{ steps.config.outputs.pr }} is now being rolled out to 5% of users on production. | |
Remove the label manually or close the PR to end the rollout. | |
- name: π¬ Comment status on failure | |
uses: marocchino/sticky-pull-request-comment@v2 | |
if: steps.rollout.outcome != 'success' | |
with: | |
number: ${{ steps.config.outputs.pr }} | |
header: 'Expo Canary Rollout Status' | |
message: | | |
# β Rollout Failed | |
**Runtime:** `${{ steps.config.outputs.runtime }}` | |
**Channel:** `${{ steps.config.outputs.channel }}` | |
**Branch:** `${{ steps.config.outputs.branch }}` | |
**Rollout status:** `${{ steps.rollout.outcome }}` | |
A rollout is already in progress on `${{ steps.config.outputs.channel }}`. | |
Try again after the current rollout ends by removing the label and re-adding it. | |
rollout-end: | |
if: | |
| # Check if rollout label was removed or PR was closed with rollout label | |
(github.event.action == 'unlabeled' && | |
github.event.label.name == 'rollout') || | |
(github.event.action == 'closed' && | |
contains(github.event.pull_request.labels.*.name, 'rollout')) | |
runs-on: ubuntu-latest | |
concurrency: | |
group: rollout-${{ github.event.pull_request.number }} | |
cancel-in-progress: false | |
steps: | |
- name: π Setup repo | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 # π Required to retrieve git history | |
- name: π Setup Node.js | |
uses: actions/setup-node@v4 | |
with: | |
node-version: 20 | |
cache: 'npm' # or 'yarn', 'pnpm' depending on your package manager | |
- name: π¦ Install dependencies | |
run: npm ci # or yarn install, pnpm install | |
- name: π Setup Expo | |
uses: expo/expo-github-action@v8 | |
with: | |
eas-version: latest | |
token: ${{ secrets.EXPO_TOKEN }} | |
- name: π Setup Config | |
id: config | |
run: | # Modify this section to match your app's config | |
# Adjust these paths to match your configuration files | |
RUNTIME=$(npx expo config --json|jq -r '.runtimeVersion') | |
CHANNEL=$(jq -r '.build.production.channel' < eas.json) | |
echo "runtime=$RUNTIME" >> $GITHUB_OUTPUT | |
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT | |
echo "branch=rollout-pr-${{ github.event.number }}" >> $GITHUB_OUTPUT | |
echo "pr=${{ github.event.number }}" >> $GITHUB_OUTPUT | |
- name: π End rollout | |
id: end-rollout | |
run: | | |
BRANCH=${{ steps.config.outputs.branch }} | |
CHANNEL=${{ steps.config.outputs.channel }} | |
RUNTIME=${{ steps.config.outputs.runtime }} | |
echo "π Checking rollout ownership for branch $BRANCH on channel $CHANNEL" | |
OUT=$(eas channel:rollout $CHANNEL \ | |
--action=view --json --non-interactive) | |
CURRENT_BRANCH=$(echo "$OUT" | jq -r '.currentRolloutInfo.rolledOutBranch.name // empty') | |
if [ -z "$CURRENT_BRANCH" ]; then | |
echo "βΉοΈ No active rollout on this channel." | |
exit 0 | |
fi | |
if [ "$CURRENT_BRANCH" = "$BRANCH" ]; then | |
echo "β Rollout belongs to this PR β ending rollout" | |
eas channel:rollout $CHANNEL \ | |
--action=end \ | |
--outcome=revert \ | |
--runtime-version=$RUNTIME \ | |
--non-interactive | |
else | |
echo "β οΈ Rollout is for branch '$CURRENT_BRANCH' (not this PR: '$BRANCH') β skipping end" | |
echo "failure_message=Rollout is for branch $CURRENT_BRANCH (not this PR: $BRANCH)" >> $GITHUB_OUTPUT | |
exit 1 | |
fi | |
env: | |
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} | |
- name: π¬ Comment status on success | |
uses: marocchino/sticky-pull-request-comment@v2 | |
if: steps.end-rollout.outcome == 'success' | |
with: | |
number: ${{ steps.config.outputs.pr }} | |
header: 'Expo Canary Rollout Status' | |
message: | | |
# β Rollout Ended | |
**Runtime:** `${{ steps.config.outputs.runtime }}` | |
**Channel:** `${{ steps.config.outputs.channel }}` | |
**Branch:** `${{ steps.config.outputs.branch }}` | |
**Reason:** `${{ github.event.action == 'closed' && 'PR closed' || 'rollout label removed' }}` | |
- name: π¬ Comment status on failure | |
uses: marocchino/sticky-pull-request-comment@v2 | |
if: steps.end-rollout.outcome != 'success' | |
with: | |
number: ${{ steps.config.outputs.pr }} | |
header: 'Expo Canary Rollout Status' | |
message: | | |
# β Rollout Failed to End | |
**Runtime:** `${{ steps.config.outputs.runtime }}` | |
**Channel:** `${{ steps.config.outputs.channel }}` | |
**Branch:** `${{ steps.config.outputs.branch }}` | |
**Reason:** `${{ github.event.action == 'closed' && 'PR closed' || 'rollout label removed' }}` | |
${{ steps.end-rollout.outputs.failure_message && format('{0}', steps.end-rollout.outputs.failure_message) || '' }} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment