Skip to content

Commit 683eecd

Browse files
feat: create gh workflow to auto deploy sandbox web pages
1 parent 31eaf4c commit 683eecd

25 files changed

Lines changed: 2509 additions & 45 deletions

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,24 @@
1-
<!-- Please ensure your pull request title adheres to our [PR Title Convention](https://github.com/coinbase/cds/blob/master/CONTRIBUTING.md#pr-title-convention). -->
1+
## Summary
22

3-
## What changed? Why?
3+
<!-- Brief description of what this PR does -->
44

5-
### Root cause (required for bugfixes)
5+
## Changes
66

7-
## UI changes
8-
9-
| iOS Old | iOS New |
10-
| -------------- | -------------- |
11-
| old screenshot | new screenshot |
12-
13-
| Android Old | Android New |
14-
| -------------- | -------------- |
15-
| old screenshot | new screenshot |
16-
17-
| Web Old | Web New |
18-
| -------------- | -------------- |
19-
| old screenshot | new screenshot |
7+
<!-- List of key changes -->
208

219
## Testing
2210

23-
### How has it been tested?
24-
25-
- [ ] Unit tests
26-
- [ ] Interaction tests
27-
- [ ] Pseudo State tests
28-
- [ ] Manual - Web
29-
- [ ] Manual - Android (Emulator / Device)
30-
- [ ] Manual - iOS (Emulator / Device)
31-
32-
### Testing instructions
33-
34-
## Illustrations/Icons Checklist
11+
<!-- How was this tested? -->
3512

36-
Required if this PR changes files under `packages/illustrations/**` or `packages/icons/**`
13+
## Preview deployment
3714

38-
- [ ] verified visreg changes with Terran (include link to visreg run/approval)
39-
- [ ] all illustration/icons names have been reviewed by Dom and/or Terran
15+
Check to deploy previews for this PR. Leave unchecked to skip.
4016

41-
## Change management
17+
- [ ] Deploy documentation preview
18+
- [ ] Deploy Storybook preview
4219

43-
type=routine <!-- {routine,nonroutine,emergency} -->
44-
risk=low <!-- {low,medium,high} -->
45-
impact=sev5 <!--{sev1,sev2,sev3,sev4,sev5} -->
20+
## Checklist
4621

47-
automerge=false
22+
- [ ] I have tested these changes locally
23+
- [ ] I have updated relevant documentation
24+
- [ ] I have added/updated tests as needed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Remove a PR preview entry from manifest.json
5+
* Usage: node remove-from-manifest.mjs <pr-number>
6+
*/
7+
8+
import fs from 'node:fs';
9+
import path from 'node:path';
10+
import { fileURLToPath } from 'node:url';
11+
12+
const __filename = fileURLToPath(import.meta.url);
13+
const __dirname = path.dirname(__filename);
14+
15+
// Parse command line arguments
16+
const [prNumber] = process.argv.slice(2);
17+
18+
if (!prNumber) {
19+
console.error('Usage: node remove-from-manifest.mjs <pr-number>');
20+
process.exit(1);
21+
}
22+
23+
const manifestPath = path.join(process.cwd(), 'manifest.json');
24+
25+
// Read existing manifest
26+
if (!fs.existsSync(manifestPath)) {
27+
console.log('⚠️ Manifest does not exist, nothing to remove');
28+
process.exit(0);
29+
}
30+
31+
let manifest;
32+
try {
33+
const content = fs.readFileSync(manifestPath, 'utf-8');
34+
manifest = JSON.parse(content);
35+
} catch (error) {
36+
console.error('❌ Failed to parse manifest:', error.message);
37+
process.exit(1);
38+
}
39+
40+
// Remove preview entry
41+
const prNum = parseInt(prNumber, 10);
42+
const initialLength = manifest.previews.length;
43+
manifest.previews = manifest.previews.filter((p) => p.pr !== prNum);
44+
45+
if (manifest.previews.length === initialLength) {
46+
console.log(`⚠️ PR #${prNum} not found in manifest`);
47+
} else {
48+
console.log(`✅ Removed preview for PR #${prNum}`);
49+
}
50+
51+
// Update last updated timestamp
52+
manifest.lastUpdated = new Date().toISOString();
53+
54+
// Write manifest
55+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
56+
57+
console.log(`📝 Manifest updated successfully`);
58+
console.log(` Total previews: ${manifest.previews.length}`);
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Update manifest.json with a new or updated PR preview entry
5+
* Usage: node update-manifest.mjs <pr-number> <pr-title> <branch> <author> <commit> <has-docs> <has-storybook>
6+
*/
7+
8+
import fs from 'node:fs';
9+
import path from 'node:path';
10+
import { fileURLToPath } from 'node:url';
11+
12+
const __filename = fileURLToPath(import.meta.url);
13+
const __dirname = path.dirname(__filename);
14+
15+
// Parse command line arguments
16+
const [prNumber, prTitle, branch, author, commit, hasDocs, hasStorybook] = process.argv.slice(2);
17+
18+
if (!prNumber || !prTitle || !branch || !author || !commit) {
19+
console.error(
20+
'Usage: node update-manifest.mjs <pr-number> <pr-title> <branch> <author> <commit> <has-docs> <has-storybook>',
21+
);
22+
process.exit(1);
23+
}
24+
25+
const manifestPath = path.join(process.cwd(), 'manifest.json');
26+
27+
// Read existing manifest or create new one
28+
let manifest = {
29+
previews: [],
30+
lastUpdated: new Date().toISOString(),
31+
};
32+
33+
if (fs.existsSync(manifestPath)) {
34+
try {
35+
const content = fs.readFileSync(manifestPath, 'utf-8');
36+
manifest = JSON.parse(content);
37+
} catch (error) {
38+
console.warn('Failed to parse existing manifest, creating new one');
39+
}
40+
}
41+
42+
// Find existing preview or create new entry
43+
const prNum = parseInt(prNumber, 10);
44+
const existingIndex = manifest.previews.findIndex((p) => p.pr === prNum);
45+
46+
const previewEntry = {
47+
pr: prNum,
48+
title: prTitle,
49+
branch: branch,
50+
author: author,
51+
baseUrl: `/cds/pr-${prNum}/`,
52+
previews: {
53+
docs: hasDocs === 'true' ? `/cds/pr-${prNum}/docs/` : null,
54+
storybook: hasStorybook === 'true' ? `/cds/pr-${prNum}/storybook/` : null,
55+
},
56+
createdAt:
57+
existingIndex >= 0 ? manifest.previews[existingIndex].createdAt : new Date().toISOString(),
58+
updatedAt: new Date().toISOString(),
59+
commit: commit,
60+
};
61+
62+
if (existingIndex >= 0) {
63+
// Update existing preview
64+
manifest.previews[existingIndex] = previewEntry;
65+
console.log(`✅ Updated preview for PR #${prNum}`);
66+
} else {
67+
// Add new preview
68+
manifest.previews.push(previewEntry);
69+
console.log(`✅ Added preview for PR #${prNum}`);
70+
}
71+
72+
// Log which previews are available
73+
const availablePreviews = [];
74+
if (previewEntry.previews.docs) availablePreviews.push('docs');
75+
if (previewEntry.previews.storybook) availablePreviews.push('storybook');
76+
console.log(` Available previews: ${availablePreviews.join(', ') || 'none'}`);
77+
78+
// Sort by PR number (descending)
79+
manifest.previews.sort((a, b) => b.pr - a.pr);
80+
81+
// Update last updated timestamp
82+
manifest.lastUpdated = new Date().toISOString();
83+
84+
// Write manifest
85+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
86+
87+
console.log(`📝 Manifest updated successfully`);
88+
console.log(` Total PRs: ${manifest.previews.length}`);
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
name: Cleanup PR Preview
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
pr_number:
7+
description: 'PR number to clean up (for testing)'
8+
required: true
9+
type: number
10+
pull_request:
11+
types: [closed]
12+
branches: master
13+
14+
permissions:
15+
contents: write
16+
pull-requests: write
17+
18+
jobs:
19+
cleanup:
20+
name: Cleanup Preview
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: Harden the runner (Audit all outbound calls)
24+
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
25+
with:
26+
egress-policy: audit
27+
28+
# Checkout main branch to get scripts
29+
- name: Checkout main branch
30+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
31+
with:
32+
ref: master
33+
34+
# Get PR number
35+
- name: Get PR number
36+
id: pr-number
37+
run: |
38+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
39+
echo "pr_number=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT
40+
else
41+
echo "pr_number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
42+
fi
43+
44+
# Checkout gh-pages branch
45+
- name: Checkout gh-pages
46+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
47+
with:
48+
ref: gh-pages
49+
path: gh-pages-checkout
50+
51+
# Pull latest so concurrent cleanups/deploys don't overwrite each other
52+
- name: Pull latest gh-pages
53+
working-directory: gh-pages-checkout
54+
run: git pull origin gh-pages
55+
56+
# Remove PR directory
57+
- name: Remove preview directory
58+
run: |
59+
PR_DIR="gh-pages-checkout/pr-${{ steps.pr-number.outputs.pr_number }}"
60+
61+
if [ -d "$PR_DIR" ]; then
62+
echo "Removing preview directory: $PR_DIR"
63+
rm -rf "$PR_DIR"
64+
echo "✅ Directory removed"
65+
else
66+
echo "⚠️ Directory does not exist: $PR_DIR"
67+
fi
68+
69+
# Update manifest
70+
- name: Update manifest
71+
working-directory: gh-pages-checkout
72+
run: |
73+
node ../.github/scripts/remove-from-manifest.mjs "${{ steps.pr-number.outputs.pr_number }}"
74+
75+
# Commit and push to gh-pages (retry on race with concurrent deploy/cleanup)
76+
- name: Commit changes
77+
working-directory: gh-pages-checkout
78+
run: |
79+
git config user.name "github-actions[bot]"
80+
git config user.email "github-actions[bot]@users.noreply.github.com"
81+
82+
git add .
83+
if git diff --staged --quiet; then
84+
echo "No changes to commit"
85+
else
86+
git commit -m "Remove preview for PR #${{ steps.pr-number.outputs.pr_number }}"
87+
for i in 1 2 3; do
88+
if git push origin gh-pages; then
89+
echo "✅ Changes pushed to gh-pages"
90+
exit 0
91+
fi
92+
echo "Push failed (concurrent update?), pulling and retrying ($i/3)..."
93+
git pull origin gh-pages --rebase
94+
done
95+
echo "❌ Failed to push after retries"
96+
exit 1
97+
fi
98+
99+
# Comment on PR
100+
- name: Comment on PR
101+
if: github.event_name == 'pull_request'
102+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
103+
with:
104+
script: |
105+
const prNumber = ${{ steps.pr-number.outputs.pr_number }};
106+
107+
const comment = `## 🧹 Preview Cleaned Up
108+
109+
The documentation preview for this PR has been removed.
110+
111+
**Removed:** \`/cds/pr-${prNumber}/\`
112+
113+
---
114+
115+
🕐 Cleaned up at: \`${new Date().toISOString()}\``;
116+
117+
await github.rest.issues.createComment({
118+
owner: context.repo.owner,
119+
repo: context.repo.repo,
120+
issue_number: prNumber,
121+
body: comment,
122+
});

0 commit comments

Comments
 (0)