Skip to content

Rollback Release

Rollback Release #74

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"