#!/bin/bash set -e # Deploy Storybook to Cloudflare Pages and comment on PR # Usage: ./pr-storybook-deploy-and-comment.sh [start_time] # Input validation # Validate PR number is numeric case "$1" in ''|*[!0-9]*) echo "Error: PR_NUMBER must be numeric" >&2 exit 1 ;; esac PR_NUMBER="$1" # Sanitize and validate branch name (allow alphanumeric, dots, dashes, underscores, slashes) BRANCH_NAME=$(echo "$2" | sed 's/[^a-zA-Z0-9._/-]//g') if [ -z "$BRANCH_NAME" ]; then echo "Error: Invalid or empty branch name" >&2 exit 1 fi # Validate status parameter STATUS="${3:-completed}" case "$STATUS" in starting|completed) ;; *) echo "Error: STATUS must be 'starting' or 'completed'" >&2 exit 1 ;; esac START_TIME="${4:-$(date -u '+%m/%d/%Y, %I:%M:%S %p')}" # Required environment variables : "${GITHUB_TOKEN:?GITHUB_TOKEN is required}" : "${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}" # Cloudflare variables only required for deployment if [ "$STATUS" = "completed" ]; then : "${CLOUDFLARE_API_TOKEN:?CLOUDFLARE_API_TOKEN is required for deployment}" : "${CLOUDFLARE_ACCOUNT_ID:?CLOUDFLARE_ACCOUNT_ID is required for deployment}" fi # Configuration COMMENT_MARKER="" # Install wrangler if not available (output to stderr for debugging) if ! command -v wrangler > /dev/null 2>&1; then echo "Installing wrangler v4..." >&2 npm install -g wrangler@^4.0.0 >&2 || { echo "Failed to install wrangler" >&2 echo "failed" return } fi # Deploy Storybook report, WARN: ensure inputs are sanitized before calling this function deploy_storybook() { dir="$1" branch="$2" [ ! -d "$dir" ] && echo "failed" && return project="comfy-storybook" echo "Deploying Storybook to project $project on branch $branch..." >&2 # Try deployment up to 3 times i=1 while [ $i -le 3 ]; do echo "Deployment attempt $i of 3..." >&2 # Branch is already sanitized, use it directly if output=$(wrangler pages deploy "$dir" \ --project-name="$project" \ --branch="$branch" 2>&1); then # Extract URL from output (improved regex for valid URL characters) url=$(echo "$output" | grep -oE 'https://[a-zA-Z0-9.-]+\.pages\.dev\S*' | head -1) result="${url:-https://${branch}.${project}.pages.dev}" echo "Success! URL: $result" >&2 echo "$result" # Only this goes to stdout for capture return else echo "Deployment failed on attempt $i: $output" >&2 fi [ $i -lt 3 ] && sleep 10 i=$((i + 1)) done echo "failed" } # Post or update GitHub comment post_comment() { body="$1" temp_file=$(mktemp) echo "$body" > "$temp_file" if command -v gh > /dev/null 2>&1; then # Find existing comment ID existing=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \ --jq ".[] | select(.body | contains(\"$COMMENT_MARKER\")) | .id" | head -1) if [ -n "$existing" ]; then # Update specific comment by ID gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$existing" \ --field body="$(cat "$temp_file")" else gh pr comment "$PR_NUMBER" --body-file "$temp_file" fi else echo "GitHub CLI not available, outputting comment:" cat "$temp_file" fi rm -f "$temp_file" } # Main execution if [ "$STATUS" = "starting" ]; then # Check if this is a version-bump branch IS_VERSION_BUMP="false" if echo "$BRANCH_NAME" | grep -q "^version-bump-"; then IS_VERSION_BUMP="true" fi # Post starting comment with appropriate message if [ "$IS_VERSION_BUMP" = "true" ]; then comment=$(cat < **Build is starting...** ⏰ Started at: $START_TIME UTC ### 🚀 Building Storybook - đŸ“Ļ Installing dependencies... - 🔧 Building Storybook components... - 🎨 Running Chromatic visual tests... --- âąī¸ Please wait while the Storybook build is in progress... EOF ) else comment=$(cat < **Build is starting...** ⏰ Started at: $START_TIME UTC ### 🚀 Building Storybook - đŸ“Ļ Installing dependencies... - 🔧 Building Storybook components... - 🌐 Preparing deployment to Cloudflare Pages... --- âąī¸ Please wait while the Storybook build is in progress... EOF ) fi post_comment "$comment" elif [ "$STATUS" = "completed" ]; then # Deploy and post completion comment # Convert branch name to Cloudflare-compatible format (lowercase, only alphanumeric and dashes) cloudflare_branch=$(echo "$BRANCH_NAME" | tr '[:upper:]' '[:lower:]' | \ sed 's/[^a-z0-9-]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g') echo "Looking for Storybook build in: $(pwd)/storybook-static" # Deploy Storybook if build exists deployment_url="Not deployed" if [ -d "storybook-static" ]; then echo "Found Storybook build, deploying..." url=$(deploy_storybook "storybook-static" "$cloudflare_branch") if [ "$url" != "failed" ] && [ -n "$url" ]; then deployment_url="[View Storybook]($url)" else deployment_url="Deployment failed" fi else echo "Storybook build not found at storybook-static" fi # Get workflow conclusion from environment or default to success WORKFLOW_CONCLUSION="${WORKFLOW_CONCLUSION:-success}" WORKFLOW_URL="${WORKFLOW_URL:-}" # Generate completion comment based on conclusion if [ "$WORKFLOW_CONCLUSION" = "success" ]; then status_icon="✅" status_text="Build completed successfully!" footer_text="🎉 Your Storybook is ready for review!" elif [ "$WORKFLOW_CONCLUSION" = "skipped" ]; then status_icon="â­ī¸" status_text="Build skipped." footer_text="â„šī¸ Chromatic was skipped for this PR." elif [ "$WORKFLOW_CONCLUSION" = "cancelled" ]; then status_icon="đŸšĢ" status_text="Build cancelled." footer_text="â„šī¸ The Chromatic run was cancelled." else status_icon="❌" status_text="Build failed!" footer_text="âš ī¸ Please check the workflow logs for error details." fi comment="$COMMENT_MARKER ## 🎨 Storybook Build Status $status_icon **$status_text** ⏰ Completed at: $(date -u '+%m/%d/%Y, %I:%M:%S %p') UTC ### 🔗 Links - [📊 View Workflow Run]($WORKFLOW_URL)" # Add deployment status if [ "$deployment_url" != "Not deployed" ]; then if [ "$deployment_url" = "Deployment failed" ]; then comment="$comment - ❌ Storybook deployment failed" elif [ "$WORKFLOW_CONCLUSION" = "success" ]; then comment="$comment - 🎨 $deployment_url" else comment="$comment - âš ī¸ Build failed - $deployment_url" fi elif [ "$WORKFLOW_CONCLUSION" != "success" ]; then comment="$comment - â­ī¸ Storybook deployment skipped (build did not succeed)" fi comment="$comment --- $footer_text" post_comment "$comment" fi