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
83 changes: 80 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,91 @@ jobs:
HAPPO_API_KEY: ${{ secrets.HAPPO_API_KEY }}
HAPPO_API_SECRET: ${{ secrets.HAPPO_API_SECRET }}

deploy-docs:
deploy-picasso-docs:
if: ${{ github.event.pull_request.head.ref != 'changeset-release/master' }}
name: Deploy Picasso docs
runs-on: ubuntu-latest
concurrency:
group: gh-pages-deployment
cancel-in-progress: false
permissions:
contents: read
contents: write
pull-requests: write
pages: write
id-token: write
needs: [static-checks]
steps:
- name: Checkout
uses: actions/checkout@v4

# Some new deployment process is needed here
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20.18

- name: Install dependencies from cache
uses: ./.github/actions/yarn-install

- name: Build Storybook
run: yarn build:storybook

- name: Checkout existing gh-pages content
id: checkout-gh-pages
uses: actions/checkout@v4
continue-on-error: true
with:
ref: gh-pages
path: gh-pages-content
token: ${{ secrets.GITHUB_TOKEN }}

- name: Create gh-pages directory if branch doesn't exist
if: steps.checkout-gh-pages.outcome == 'failure'
run: |
echo "🆕 gh-pages branch doesn't exist, creating organized directory structure"
mkdir -p gh-pages-content/prs
echo "# Picasso Storybook" > gh-pages-content/README.md
echo "This directory contains Storybook deployments for Picasso." >> gh-pages-content/README.md
echo "" >> gh-pages-content/README.md
echo "- Production: Root directory" >> gh-pages-content/README.md
echo "- PR Previews: prs/{number}/" >> gh-pages-content/README.md

- name: Prepare preview deployment
run: |
# Create prs/{number} directory in organized structure
PR_DIR="gh-pages-content/prs/${{ github.event.pull_request.number }}"

# Remove existing PR directory if it exists
rm -rf "$PR_DIR"

# Create new PR directory and copy storybook build
mkdir -p "$PR_DIR"
cp -r build/storybook/* "$PR_DIR/"

echo "📂 Prepared preview in: $PR_DIR"
ls -la "$PR_DIR"

- name: Setup Pages
uses: actions/configure-pages@v4

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: gh-pages-content

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
const prNumber = '${{ github.event.pull_request.number }}';
const deployUrl = `https://toptal.github.io/picasso/prs/${prNumber}/`;

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '📖 **Storybook Preview**\n\n🚀 Your Storybook preview is ready: **[View Storybook](' + deployUrl + ')**\n\n📍 Preview URL: `' + deployUrl + '`\n\nThis preview is updated automatically when you push changes to this PR.'
});
175 changes: 175 additions & 0 deletions .github/workflows/cleanup-previews.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
name: Cleanup PR Previews

on:
# Immediate cleanup when PR closes
pull_request:
types: [closed]
branches:
- master
- 'feature/**'

# Scheduled garbage collection (runs weekly on Sundays at 2 AM UTC)
schedule:
- cron: '0 2 * * 0'

# Manual trigger for emergency cleanup
workflow_dispatch:
inputs:
cleanup_mode:
description: 'Cleanup mode'
required: true
default: 'single'
type: choice
options:
- single
- all_closed
- force_all

concurrency:
group: gh-pages-deployment
cancel-in-progress: false

jobs:
cleanup-single-pr:
if: ${{ github.event_name == 'pull_request' }}
name: Cleanup Single PR Preview
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
pages: write
id-token: write
steps:
- name: Checkout gh-pages branch
id: checkout-gh-pages
uses: actions/checkout@v4
continue-on-error: true
with:
ref: gh-pages
path: gh-pages-content
token: ${{ secrets.GITHUB_TOKEN }}

- name: Remove PR preview directory
if: steps.checkout-gh-pages.outcome == 'success'
run: |
PR_DIR="gh-pages-content/prs/${{ github.event.pull_request.number }}"
Comment thread
denieler marked this conversation as resolved.
if [ -d "$PR_DIR" ]; then
echo "🗑️ Removing preview directory: $PR_DIR"
rm -rf "$PR_DIR"
echo "✅ Cleanup successful"
else
echo "ℹ️ Preview directory $PR_DIR does not exist"
fi

- name: Deploy cleanup to GitHub Pages
if: steps.checkout-gh-pages.outcome == 'success'
uses: actions/upload-pages-artifact@v3
with:
path: gh-pages-content

- name: Deploy to GitHub Pages
if: steps.checkout-gh-pages.outcome == 'success'
uses: actions/deploy-pages@v4

- name: Comment cleanup notification
if: steps.checkout-gh-pages.outcome == 'success'
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '🗑️ **Storybook preview cleaned up**\n\nThe preview deployment has been automatically removed since this PR was closed.'
});

garbage-collection:
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
name: Garbage Collection
runs-on: ubuntu-latest
permissions:
contents: write
pages: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Checkout gh-pages branch
id: checkout-gh-pages
uses: actions/checkout@v4
continue-on-error: true
with:
ref: gh-pages
path: gh-pages-content
token: ${{ secrets.GITHUB_TOKEN }}

- name: Garbage collect orphaned previews
if: steps.checkout-gh-pages.outcome == 'success'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');

// Get all open PRs
const { data: openPRs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open'
});

const openPRNumbers = new Set(openPRs.map(pr => pr.number.toString()));
console.log(`📊 Found ${openPRNumbers.size} open PRs:`, Array.from(openPRNumbers).join(', '));

// Check prs directory
const prsDir = 'gh-pages-content/prs';
if (!fs.existsSync(prsDir)) {
console.log('ℹ️ No prs directory found');
return;
}

const existingPreviews = fs.readdirSync(prsDir);
console.log(`📁 Found ${existingPreviews.length} preview directories:`, existingPreviews.join(', '));

let cleanedCount = 0;
for (const prDir of existingPreviews) {
const prNumber = prDir;

// Skip if PR is still open (unless force mode)
if (openPRNumbers.has(prNumber) && '${{ github.event.inputs.cleanup_mode }}' !== 'force_all') {
console.log(`⏭️ Skipping ${prNumber} (PR still open)`);
continue;
}

// Remove closed PR preview
const fullPath = path.join(prsDir, prDir);
try {
fs.rmSync(fullPath, { recursive: true, force: true });
console.log(`🗑️ Cleaned up preview: ${prNumber}`);
cleanedCount++;
} catch (error) {
console.error(`❌ Failed to clean ${prNumber}:`, error.message);
}
}

console.log(`✅ Garbage collection complete: ${cleanedCount} previews cleaned`);

// Set output for summary
core.setOutput('cleaned_count', cleanedCount);
core.setOutput('total_found', existingPreviews.length);

- name: Deploy cleanup to GitHub Pages
if: steps.checkout-gh-pages.outcome == 'success'
uses: actions/upload-pages-artifact@v3
with:
path: gh-pages-content

- name: Deploy to GitHub Pages
if: steps.checkout-gh-pages.outcome == 'success'
uses: actions/deploy-pages@v4

- name: Summary
run: |
echo "🧹 **Garbage Collection Summary**" >> $GITHUB_STEP_SUMMARY
echo "- Trigger: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
82 changes: 81 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ jobs:
release:
name: Release
runs-on: ubuntu-latest
concurrency:
group: gh-pages-deployment
cancel-in-progress: false
permissions:
contents: write
issues: write
pull-requests: write
pages: write
id-token: write
steps:
- name: Checkout Repo
uses: actions/checkout@v4
Expand Down Expand Up @@ -136,7 +141,82 @@ jobs:
payload: |
text: 'A new PR was merged to Picasso :parrotspin:'

# Implement Storybook deployment here
- name: Build Storybook for Production
if: ${{ success() && steps.changesets.outputs.published == 'true' }}
run: |
yarn build:storybook
echo "📖 Building Storybook for production deployment..." >> $GITHUB_STEP_SUMMARY

- name: Checkout existing gh-pages content
if: ${{ success() && steps.changesets.outputs.published == 'true' }}
id: checkout-gh-pages-release
uses: actions/checkout@v4
continue-on-error: true
with:
ref: gh-pages
path: gh-pages-content
token: ${{ secrets.GITHUB_TOKEN }}

- name: Create gh-pages directory if branch doesn't exist
if: ${{ success() && steps.changesets.outputs.published == 'true' && steps.checkout-gh-pages-release.outcome == 'failure' }}
run: |
echo "🆕 gh-pages branch doesn't exist, creating organized directory structure"
mkdir -p gh-pages-content/prs
echo "# Picasso Storybook" > gh-pages-content/README.md
echo "This directory contains Storybook deployments for Picasso." >> gh-pages-content/README.md
echo "" >> gh-pages-content/README.md
echo "- Production: Root directory" >> gh-pages-content/README.md
echo "- PR Previews: prs/{number}/" >> gh-pages-content/README.md

- name: Prepare production deployment (preserve PR previews)
if: ${{ success() && steps.changesets.outputs.published == 'true' }}
run: |
# Preserve prs directory if it exists
if [ -d "gh-pages-content/prs" ]; then
echo "📁 Backing up prs directory"
mv gh-pages-content/prs ./prs-backup
fi

# Clear root content but preserve .git and prs
find gh-pages-content -mindepth 1 -maxdepth 1 -not -name ".git" -not -name "prs" -exec rm -rf {} +

# Deploy production to root
echo "📦 Deploying production Storybook to root"
cp -r build/storybook/* gh-pages-content/

# Restore prs
if [ -d "./prs-backup" ]; then
echo "🔄 Restoring prs directory"
mv ./prs-backup gh-pages-content/prs
fi

echo "📂 Production deployment prepared with preserved previews"
echo "Root content:"
ls -la gh-pages-content/
echo "PR previews content:"
ls -la gh-pages-content/prs/ 2>/dev/null || echo "No prs directory"

- name: Setup Pages
if: ${{ success() && steps.changesets.outputs.published == 'true' }}
uses: actions/configure-pages@v4

- name: Upload artifact
if: ${{ success() && steps.changesets.outputs.published == 'true' }}
uses: actions/upload-pages-artifact@v3
with:
path: gh-pages-content

- name: Deploy Production Storybook to GitHub Pages
if: ${{ success() && steps.changesets.outputs.published == 'true' }}
id: storybook-deployment
uses: actions/deploy-pages@v4

- name: Storybook Deployment Success
if: ${{ success() && steps.changesets.outputs.published == 'true' }}
run: |
echo "🚀 Production Storybook deployed to GitHub Pages!" >> $GITHUB_STEP_SUMMARY
echo "📖 Visit: https://toptal.github.io/picasso/" >> $GITHUB_STEP_SUMMARY
echo "✅ PR previews preserved during production deployment" >> $GITHUB_STEP_SUMMARY

integration-tests:
name: Integration Tests
Expand Down
Loading