Rollback Release #74
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: Rollback Release | |
permissions: | |
contents: write | |
packages: write | |
issues: write | |
on: | |
workflow_run: | |
workflows: ['E2E Tests (Release)'] | |
types: | |
- completed | |
jobs: | |
prepare: | |
runs-on: ubuntu-latest | |
if: github.event.workflow_run.conclusion == 'failure' | |
outputs: | |
version: ${{ steps.extract_version.outputs.version }} | |
tag_name: ${{ steps.extract_version.outputs.tag_name }} | |
triggered_by: ${{ steps.extract_version.outputs.triggered_by }} | |
reason: ${{ steps.extract_version.outputs.reason }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Extract version from workflow run | |
id: extract_version | |
run: | | |
# Extract from the triggering workflow's commit | |
git fetch --tags | |
# Find the tag that points to the head_sha | |
TAG_NAME=$(git tag --points-at ${{ github.event.workflow_run.head_sha }} | grep '^v' | head -1) | |
if [ -z "$TAG_NAME" ]; then | |
echo "❌ No version tag found for commit ${{ github.event.workflow_run.head_sha }}" | |
echo "Available tags pointing to this commit:" | |
git tag --points-at ${{ github.event.workflow_run.head_sha }} || echo "None" | |
exit 1 | |
fi | |
# Extract version from tag (remove 'v' prefix) | |
if [[ $TAG_NAME =~ ^v(.+)$ ]]; then | |
VERSION="${BASH_REMATCH[1]}" | |
else | |
echo "❌ Tag $TAG_NAME does not match expected format (v*)" | |
exit 1 | |
fi | |
echo "Found tag: $TAG_NAME" | |
echo "Extracted version: $VERSION" | |
echo "version=$VERSION" >> $GITHUB_OUTPUT | |
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT | |
echo "triggered_by=${{ github.event.workflow_run.id }}" >> $GITHUB_OUTPUT | |
echo "reason=E2E tests failed" >> $GITHUB_OUTPUT | |
rollback: | |
needs: prepare | |
runs-on: ubuntu-latest | |
steps: | |
- name: Generate token | |
id: generate_token | |
uses: actions/create-github-app-token@v1 | |
with: | |
app-id: ${{ secrets.MOTIA_CI_APP_ID }} | |
private-key: ${{ secrets.MOTIA_CI_APP_PRIVATE_KEY }} | |
permissions: >- | |
{ | |
"contents": "write", | |
"packages": "write", | |
"issues": "write", | |
"pull_requests": "write" | |
} | |
- uses: actions/checkout@v4 | |
with: | |
token: ${{ steps.generate_token.outputs.token }} | |
- name: Setup | |
uses: ./.github/actions/setup | |
- name: Setup NPM authentication | |
env: | |
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
run: | | |
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc | |
- name: Remove pre-release packages from NPM | |
run: | | |
npm unpublish motia@${{ needs.prepare.outputs.version }} --force || echo "Package not found or already removed" | |
npm unpublish @motiadev/workbench@${{ needs.prepare.outputs.version }} --force || echo "Package not found or already removed" | |
npm unpublish @motiadev/stream-client-react@${{ needs.prepare.outputs.version }} --force || echo "Package not found or already removed" | |
npm unpublish @motiadev/stream-client-browser@${{ needs.prepare.outputs.version }} --force || echo "Package not found or already removed" | |
npm unpublish @motiadev/test@${{ needs.prepare.outputs.version }} --force || echo "Package not found or already removed" | |
npm unpublish @motiadev/core@${{ needs.prepare.outputs.version }} --force || echo "Package not found or already removed" | |
npm unpublish @motiadev/ui@${{ needs.prepare.outputs.version }} --force || echo "Package not found or already removed" | |
- name: Remove pre-release tags | |
run: | | |
npm dist-tag rm motia pre-release || true | |
npm dist-tag rm @motiadev/workbench pre-release || true | |
npm dist-tag rm @motiadev/stream-client-react pre-release || true | |
npm dist-tag rm @motiadev/stream-client-browser pre-release || true | |
npm dist-tag rm @motiadev/stream-client-node pre-release || true | |
npm dist-tag rm @motiadev/stream-client pre-release || true | |
npm dist-tag rm @motiadev/test pre-release || true | |
npm dist-tag rm @motiadev/core pre-release || true | |
npm dist-tag rm @motiadev/ui pre-release || true | |
- name: Delete Git tag | |
run: | | |
git push --delete origin ${{ needs.prepare.outputs.tag_name }} || echo "Tag already deleted" | |
env: | |
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} | |
- name: Get latest successful release | |
id: latest_release | |
run: | | |
LATEST_RELEASE=$(curl -s -H "Authorization: token ${{ steps.generate_token.outputs.token }}" \ | |
"https://api.github.com/repos/${{ github.repository }}/releases/latest" | \ | |
jq -r '.tag_name') | |
echo "latest_tag=$LATEST_RELEASE" >> $GITHUB_OUTPUT | |
- name: Download E2E test artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
pattern: e2e-test-results-${{ needs.prepare.outputs.version }}-* | |
path: ./e2e-artifacts | |
merge-multiple: true | |
continue-on-error: true | |
- name: Create failure issue | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ steps.generate_token.outputs.token }} | |
script: | | |
const fs = require('fs'); | |
let testResults = ''; | |
try { | |
const artifactPath = './e2e-artifacts'; | |
if (fs.existsSync(artifactPath)) { | |
testResults = '\n\n## Test Results Available\nE2E test artifacts have been uploaded and are available for analysis.'; | |
} | |
} catch (error) { | |
testResults = '\n\n## Test Results\nNo test artifacts available.'; | |
} | |
const issueBody = `## Release Rollback: ${{ needs.prepare.outputs.version }} | |
**Reason:** ${{ needs.prepare.outputs.reason }} | |
**Actions Taken:** | |
- ❌ Pre-release packages removed from NPM | |
- ❌ Git tag \`${{ needs.prepare.outputs.tag_name }}\` deleted | |
- ❌ Pre-release NPM tags cleaned up | |
**Current Status:** | |
- Latest stable release: \`${{ steps.latest_release.outputs.latest_tag }}\` | |
- Failed version: \`${{ needs.prepare.outputs.version }}\` | |
**Investigation Required:** | |
- Review E2E test failures | |
- Fix underlying issues before next release attempt | |
- Consider hotfix if critical | |
${testResults} | |
**Related Workflow Run:** https://github.com/${{ github.repository }}/actions/runs/${{ needs.prepare.outputs.triggered_by }} | |
cc: @motiadev/core-team`; | |
try { | |
const issue = await github.rest.issues.create({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: `🚨 Release Rollback: ${{ needs.prepare.outputs.version }} - ${{ needs.prepare.outputs.reason }}`, | |
body: issueBody, | |
labels: ['release-failure', 'high-priority', 'bug'] | |
}); | |
console.log('Issue created:', issue.data.html_url); | |
} catch (error) { | |
console.error('Failed to create issue:', error.message); | |
console.log('Issue content that would have been created:'); | |
console.log('Title:', `🚨 Release Rollback: ${{ needs.prepare.outputs.version }} - ${{ needs.prepare.outputs.reason }}`); | |
console.log('Body:', issueBody); | |
// Don't fail the workflow if issue creation fails | |
console.log('Continuing workflow despite issue creation failure...'); | |
} | |
- name: Create failure issue (fallback) | |
if: failure() | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const issueBody = `## Release Rollback: ${{ needs.prepare.outputs.version }} | |
**Reason:** ${{ needs.prepare.outputs.reason }} | |
**Actions Taken:** | |
- ❌ Pre-release packages removed from NPM | |
- ❌ Git tag \`${{ needs.prepare.outputs.tag_name }}\` deleted | |
- ❌ Pre-release NPM tags cleaned up | |
**Current Status:** | |
- Latest stable release: (check manually) | |
- Failed version: \`${{ needs.prepare.outputs.version }}\` | |
**Investigation Required:** | |
- Review E2E test failures | |
- Fix underlying issues before next release attempt | |
- Consider hotfix if critical | |
**Related Workflow Run:** https://github.com/${{ github.repository }}/actions/runs/${{ needs.prepare.outputs.triggered_by }} | |
cc: @motiadev/core-team`; | |
try { | |
const issue = await github.rest.issues.create({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: `🚨 Release Rollback: ${{ needs.prepare.outputs.version }} - ${{ needs.prepare.outputs.reason }}`, | |
body: issueBody, | |
labels: ['release-failure', 'high-priority', 'bug'] | |
}); | |
console.log('Fallback issue created:', issue.data.html_url); | |
} catch (error) { | |
console.error('Fallback issue creation also failed:', error.message); | |
} | |
- name: Send notification | |
run: | | |
echo "🚨 RELEASE ROLLBACK COMPLETED" | |
echo "Version: ${{ needs.prepare.outputs.version }}" | |
echo "Reason: ${{ needs.prepare.outputs.reason }}" | |
echo "Current stable: ${{ steps.latest_release.outputs.latest_tag }}" | |
echo "" | |
echo "Action items:" | |
echo "1. Review test failures" | |
echo "2. Fix issues" | |
echo "3. Test locally" | |
echo "4. Create new release" |