diff --git a/.github/workflows/final-manual-deploy-comment.yml b/.github/workflows/final-manual-deploy-comment.yml index 1b7b7dfdfa8..29d4546eb81 100644 --- a/.github/workflows/final-manual-deploy-comment.yml +++ b/.github/workflows/final-manual-deploy-comment.yml @@ -1,4 +1,4 @@ -name: Final Netlify build manual deploy comment #temp name +name: Final 'Comment' Netlify Build + Build Reportage (Manual Deploy) on: issue_comment: @@ -6,31 +6,39 @@ on: jobs: deploy-preview: - # when a contributor comments 'netlify build', - # but only on pull requests, not issues. - if: | - github.event.comment.body == 'netlify build' - && github.event.issue.pull_request + if: github.event.comment.body == 'netlify build' && github.event.issue.pull_request runs-on: ubuntu-latest steps: - # New step to check if the PR is from a forked repository. - # If the PR is from a fork, doesn't run. Only runs for branches in the main repo. - - name: Check if PR is from a fork + - name: Verify Repository Source env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - PR_URL="${{ github.event.issue.pull_request.url }}" + echo "════════════════════════════════════════════════════════════════" + echo "🔍 Checking if PR is from a forked repository..." + echo "════════════════════════════════════════════════════════════════" + + PR_URL="${{ github.event.issue.pull_request.url || github.event.pull_request.url }}" IS_FORK=$(gh api "$PR_URL" --jq '.head.repo.fork') if [[ "$IS_FORK" == "true" ]]; then - echo "🛑 Halting: This workflow only runs for branches in the main repository, not for forks." + echo "" + echo "🛑 HALTING WORKFLOW" + echo "────────────────────────────────────────────────────────────────" + echo "This workflow only runs for branches in the main repository." + echo "Forks are not supported for security reasons." + echo "════════════════════════════════════════════════════════════════" exit 1 else - echo "✅ PR is not from a fork. Proceeding with the job." + echo "" + echo "✅ Repository source verified" + echo "📍 Source: Main repository (not a fork)" + echo "════════════════════════════════════════════════════════════════" fi - - name: Check for member permission + - name: Validate Team Membership + # Only run this check for issue_comment events (manual triggers) + if: github.event_name == 'issue_comment' env: GH_TOKEN: ${{ secrets.DOCS_ENG_TEAM_MEMBERSHIP_CHECKER }} id: check_permission @@ -42,17 +50,18 @@ jobs: const org = context.repo.owner; const team_slugs = ['doc', 'DOCS-ENG']; // <-- add your team slugs here - console.log(`Organization: ${org}`); - console.log(`Commenter: ${commenter}`); + console.log(`🔐 Checking permissions...`); + console.log(`📋 Organization: ${org}`); + console.log(`👤 Commenter: ${commenter}`); try { const user = await github.rest.users.getAuthenticated(); - console.log('Authenticated as:', user.data.login); + console.log('🔑 Authenticated as:', user.data.login); const scopes = await github.request('GET /user'); - console.log('Token scopes:', scopes.headers['x-oauth-scopes']); + console.log('🎫 Token scopes:', scopes.headers['x-oauth-scopes']); } catch (error) { - console.log('Auth check failed:', error.message); + console.log('❌ Auth check failed:', error.message); } // List all teams where the commenter is a member @@ -61,15 +70,15 @@ jobs: const teams = await github.paginate(github.rest.teams.listForAuthenticatedUser, {}); const userTeams = teams.filter(team => team.organization.login.toLowerCase() === org.toLowerCase()); if (userTeams.length === 0) { - console.log(`${commenter} is not a member of any team in org ${org}.`); + console.log(`⚠️ ${commenter} is not a member of any team in org ${org}.`); } else { - console.log(`${commenter} is a member of the following teams in ${org}:`); + console.log(`👥 ${commenter} is a member of the following teams in ${org}:`); userTeams.forEach(team => { - console.log(`- ${team.slug} (${team.name})`); + console.log(` ✓ ${team.slug} (${team.name})`); }); } } catch (err) { - console.log(`Could not list teams for user ${commenter}: ${err.message}`); + console.log(`❌ Could not list teams for user ${commenter}: ${err.message}`); } } @@ -77,27 +86,27 @@ jobs: async function isMemberOfAnyTeam() { for (const team_slug of team_slugs) { - console.log(`Checking team_slug: ${team_slug}`); + console.log(`🔍 Checking team_slug: ${team_slug}`); try { const response = await github.rest.teams.getMembershipForUserInOrg({ org, team_slug, username: commenter, }); - console.log(`API response for team_slug ${team_slug}:`, JSON.stringify(response.data, null, 2)); + console.log(`📊 API response for team_slug ${team_slug}:`, JSON.stringify(response.data, null, 2)); if (response.data.state === 'active') { - console.log(`${commenter} is an active member of the ${team_slug} team.`); + console.log(`✅ ${commenter} is an active member of the ${team_slug} team.`); return true; } } catch (error) { - console.log(`Error object for team_slug ${team_slug}:`, JSON.stringify(error, Object.getOwnPropertyNames(error))); + console.log(`⚠️ Error object for team_slug ${team_slug}:`, JSON.stringify(error, Object.getOwnPropertyNames(error))); if (error.status !== 404) { - console.log(`Error checking team_slug ${team_slug}: ${error.message}`); + console.log(`❌ Error checking team_slug ${team_slug}: ${error.message}`); core.setFailed(`Could not verify team membership for ${team_slug}: ${error.message}`); return false; } // If 404, user is not in this team, continue to next - console.log(`${commenter} is not a member of ${team_slug} (404)`); + console.log(`ℹ️ ${commenter} is not a member of ${team_slug} (404)`); } } return false; @@ -106,32 +115,195 @@ jobs: // Properly await the async function const isMember = await isMemberOfAnyTeam(); if (!isMember) { - core.setFailed(`${commenter} is not a member of any required team (${team_slugs.join(', ')})`); + core.setFailed(`❌ ${commenter} is not a member of any required team (${team_slugs.join(', ')})`); } else { console.log(`✅ ${commenter} is authorized to trigger builds`); } - # we use `jq` to parse the GH API response - - name: setup jq + - name: Initialize Dependencies uses: dcarbone/install-jq-action@v2 - - name: send request to Netlify build hook + - name: Validate Netlify Credentials + id: validate_netlify_token + env: + NETLIFY_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + run: | + echo "🧪 Validating Netlify API token..." + http_status=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $NETLIFY_TOKEN" https://api.netlify.com/api/v1/user) + + if [ "$http_status" -eq 401 ]; then + echo "::error::Netlify API token is invalid or has expired." + echo "is_invalid=true" >> $GITHUB_OUTPUT + else + echo "✅ Netlify API token is valid." + echo "is_invalid=false" >> $GITHUB_OUTPUT + fi + + - name: Post Comment for Invalid Token + if: steps.validate_netlify_token.outputs.is_invalid == 'true' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prUrl = '${{ github.event.issue.pull_request.url || github.event.pull_request.url }}'; + const prNumber = prUrl.split('/').pop(); + const updateIST = new Date().toLocaleString('en-US', { timeZone: 'Asia/Kolkata', hourCycle: 'h23' }); + + const body = `### 🚨 Action Required: Netlify API Token Expired + (Logged at: ${updateIST} IST) + + Dear *Docs Engineering* team, the \`NETLIFY_AUTH_TOKEN\` used by this workflow is **invalid or has expired**. All Netlify deployment previews are blocked until a new token is provided. + + **To fix this:** + 1. Generate a new Personal Access Token in your Netlify User Settings. + 2. Update the \`NETLIFY_AUTH_TOKEN\` repository secret with the new value. + + This workflow will remain blocked until the credentials are updated.`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: Number(prNumber), + body + }); + + - name: Halt Workflow for Invalid Token + if: steps.validate_netlify_token.outputs.is_invalid == 'true' + run: | + echo "🛑 Halting workflow due to invalid Netlify token." + exit 1 + + - name: Check for Ongoing Deployments + id: check_ongoing_deployment + env: + NETLIFY_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "════════════════════════════════════════════════════════════════" + echo "🔎 Checking for existing Netlify deployments..." + echo "════════════════════════════════════════════════════════════════" + + gh_api_url=$(echo ${{ github.event.issue.pull_request.url || github.event.pull_request.url }} | sed 's/https:\/\/2.zoppoz.workers.dev:443\/https\/api.github.com\///') + gh_api_response=$(gh api $gh_api_url) + branch_name=$(echo $gh_api_response | jq -r .head.ref) + pr_number=$(echo $gh_api_response | jq -r .number) + latest_sha=$(echo $gh_api_response | jq -r .head.sha) + + echo "latest_sha=$latest_sha" >> $GITHUB_OUTPUT + echo " 🔖 Branch: $branch_name" + + DEPLOYS=$(curl -s -H "Authorization: Bearer $NETLIFY_TOKEN" \ + "https://api.netlify.com/api/v1/sites/$NETLIFY_SITE_ID/deploys?branch=$branch_name") + + ONGOING_DEPLOY=$(echo "$DEPLOYS" | jq -r \ + '[.[] | select(.state == "building" or .state == "enqueued" or .state == "processing")] | sort_by(.created_at) | reverse | .[0]') + + if [ -n "$ONGOING_DEPLOY" ] && [ "$ONGOING_DEPLOY" != "null" ]; then + DEPLOY_ID=$(echo "$ONGOING_DEPLOY" | jq -r '.id') + COMMIT_REF=$(echo "$ONGOING_DEPLOY" | jq -r '.commit_ref // .head') + BUILD_URL="https://app.netlify.com/sites/${{ secrets.NETLIFY_SITE_ID }}/deploys/$DEPLOY_ID" + + echo "ongoing_deploy_id=$DEPLOY_ID" >> $GITHUB_OUTPUT + echo "ongoing_commit_ref=$COMMIT_REF" >> $GITHUB_OUTPUT + echo "ongoing_build_url=$BUILD_URL" >> $GITHUB_OUTPUT + echo "pr_number=$pr_number" >> $GITHUB_OUTPUT + + echo "" + echo "🛑 Found an ongoing deployment: $DEPLOY_ID" + echo " Commit: $COMMIT_REF" + echo " Halting workflow." + exit 0 + else + echo "✅ No ongoing deployments found for this branch." + fi + + - name: Post Comment for Ongoing Deployment + if: steps.check_ongoing_deployment.outputs.ongoing_deploy_id + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = '${{ steps.check_ongoing_deployment.outputs.pr_number }}'; + const deployId = '${{ steps.check_ongoing_deployment.outputs.ongoing_deploy_id }}'; + const commitRef = '${{ steps.check_ongoing_deployment.outputs.ongoing_commit_ref }}'; + const buildUrl = '${{ steps.check_ongoing_deployment.outputs.ongoing_build_url }}'; + const shortCommit = commitRef.substring(0, 7); + const repo = '${{ github.repository }}'; + const ongoingCommitLink = `[\`${shortCommit}\`](https://github.com/${repo}/commit/${commitRef})`; + const updateIST = new Date().toLocaleString('en-US', { timeZone: 'Asia/Kolkata', hourCycle: 'h23' }); + + const latestSha = '${{ steps.check_ongoing_deployment.outputs.latest_sha }}'; + const shortLatestSha = latestSha.substring(0, 7); + const latestCommitLink = `[\`${shortLatestSha}\`](https://github.com/${repo}/commit/${latestSha})`; + + const body = `### ⏳ A Netlify Deployment is Already in Progress + (Update logged at: ${updateIST} IST | Commit: ${latestCommitLink}) + + An existing deployment for this branch is currently running. Please wait for it to complete before starting a new one. + + - **Ongoing Deploy ID:** \`${deployId}\` + - **Triggered by Commit:** ${ongoingCommitLink} + - **Monitor Progress:** [View Deployment](${buildUrl}) + + Once the previous deployment is finished, you can trigger a new build by commenting \`netlify build\` on this PR.`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: Number(prNumber), + body + }); + + console.log('✅ Commented on PR about the ongoing deployment.'); + + - name: Stop Workflow if Deployment is Ongoing + if: steps.check_ongoing_deployment.outputs.ongoing_deploy_id + run: | + echo "🛑 Workflow stopped because an existing deployment is in progress." + exit 1 + + - name: Trigger Netlify Deployment + if: ${{ !steps.check_ongoing_deployment.outputs.ongoing_deploy_id }} id: netlify_build env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh_api_url=$(echo ${{ github.event.issue.pull_request.url || github.event.pull_request.url }} | sed 's/https:\/\/2.zoppoz.workers.dev:443\/https\/api.github.com//') + echo "════════════════════════════════════════════════════════════════" + echo "🌐 NETLIFY DEPLOYMENT TRIGGER" + echo "════════════════════════════════════════════════════════════════" + + gh_api_url=$(echo ${{ github.event.issue.pull_request.url || github.event.pull_request.url }} | sed 's/https:\/\/2.zoppoz.workers.dev:443\/https\/api.github.com\///') gh_api_response=$(gh api $gh_api_url) branch_name=$(echo $gh_api_response | jq -r .head.ref) sha=$(echo $gh_api_response | jq -r .head.sha) pr_number=$(echo $gh_api_response | jq -r .number) echo "branch_name=$branch_name" >> $GITHUB_OUTPUT + echo "pr_number=$pr_number" >> $GITHUB_OUTPUT + echo "sha=$sha" >> $GITHUB_OUTPUT + + TRIGGER_TIME=$(date -u +%s) + echo "trigger_time=$TRIGGER_TIME" >> $GITHUB_OUTPUT + + echo "" + echo "📋 Deployment Details:" + echo "────────────────────────────────────────────────────────────────" + echo " 🔖 Branch: $branch_name" + echo " 🔢 PR #: $pr_number" + echo " 📝 Commit: $sha" + echo " 🕐 Time (IST): $(TZ=Asia/Kolkata date '+%Y-%m-%d %H:%M:%S')" + echo "────────────────────────────────────────────────────────────────" + echo "" + echo "🚀 Sending build request to Netlify..." curl -X POST \ "https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_BUILD_HOOK_ID }}?trigger_branch=$branch_name"'&trigger_title=Manual+deploy+preview+for+PR+%23'"$pr_number"'+-+'"$sha" + + echo "" + echo "✅ Build hook triggered successfully" + echo "════════════════════════════════════════════════════════════════" - # This step gets the branch name and replaces any non-alpha-numeric characters with '-' to match Netlify's URL format and capital letters to small case - name: Sanitize branch name for Netlify URL id: sanitize_branch run: | @@ -139,14 +311,499 @@ jobs: SANITIZED_BRANCH_NAME=$(echo "${{ steps.netlify_build.outputs.branch_name }}" | sed 's/[^a-zA-Z0-9]/-/g' | tr 'A-Z' 'a-z') echo "name=$SANITIZED_BRANCH_NAME" >> $GITHUB_OUTPUT - # This step now posts a comment with the dynamically constructed preview URL. - - name: Add PR comment with preview URL - uses: thollander/actions-comment-pull-request@v2 + - name: Create PR Status Comment + id: initial_comment + uses: actions/github-script@v7 with: - message: | - ### Netlify Preview Building! + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + console.log('════════════════════════════════════════════════════════════════'); + console.log('📝 Creating initial PR comment...'); + console.log('════════════════════════════════════════════════════════════════'); + + const triggerEpoch = Number('${{ steps.netlify_build.outputs.trigger_time }}') * 1000; + const triggeredAtIST = new Date(triggerEpoch).toLocaleString('en-US', { timeZone: 'Asia/Kolkata', hourCycle: 'h23' }); + const prNumber = '${{ steps.netlify_build.outputs.pr_number }}'; + const sha = '${{ steps.netlify_build.outputs.sha }}'; + const shortSha = sha.substring(0, 7); + const repo = '${{ github.repository }}'; + const commitLink = `[\`${shortSha}\`](https://github.com/${repo}/commit/${sha})`; + const jobUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`; + const branchName = '${{ steps.netlify_build.outputs.branch_name }}'; + + const body = `### 🚀 Netlify Preview Building... + (Update logged at: ${triggeredAtIST} IST | Commit: ${commitLink}) + + - **PR:** #${prNumber} + - **Branch:** \`${branchName}\` + - **Status:** Build triggered on Netlify. + - **GitHub Actions Job:** [View Job Log](${jobUrl}) + + ⏳ Monitoring deployment status... This usually takes 10-20 minutes.`; - If the build is successful, the preview for this pull request will be available at the following URL (usually takes 10-20 minutes): + const { data: comment } = await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: Number(prNumber), + body + }); + + console.log('✅ Comment created successfully'); + console.log('🆔 Comment ID:', comment.id); + console.log('════════════════════════════════════════════════════════════════'); + core.setOutput('comment_id', comment.id); - **[https://${{ steps.sanitize_branch.outputs.name }}--docs-website-netlify.netlify.app](https://${{ steps.sanitize_branch.outputs.name }}--docs-website-netlify.netlify.app)** - comment_tag: manual-build-comment \ No newline at end of file + - name: Locate Netlify Deployment + id: poll_netlify + env: + NETLIFY_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + BRANCH_NAME="${{ steps.netlify_build.outputs.branch_name }}" + TRIGGER_TIME="${{ steps.netlify_build.outputs.trigger_time }}" + SHA="${{ steps.netlify_build.outputs.sha }}" + PR_NUMBER="${{ steps.netlify_build.outputs.pr_number }}" + DEPLOY_ID="" + DEPLOY_FOUND_VIA="" + + echo "════════════════════════════════════════════════════════════════" + echo "🔎 DEPLOYMENT DISCOVERY PROCESS" + echo "════════════════════════════════════════════════════════════════" + echo "" + echo "📋 Search Parameters:" + echo "────────────────────────────────────────────────────────────────" + echo " 🔖 Branch: $BRANCH_NAME" + echo " 📝 Commit SHA: $SHA" + echo " 🕐 Trigger (UTC): $(date -u -d @$TRIGGER_TIME '+%Y-%m-%d %H:%M:%S')" + echo " 🕐 Trigger (IST): $(TZ=Asia/Kolkata date -d @$TRIGGER_TIME '+%Y-%m-%d %H:%M:%S')" + echo "════════════════════════════════════════════════════════════════" + + echo "" + echo "⏳ Waiting 45 seconds for Netlify to register the deployment..." + sleep 45 + + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ SEARCHING FOR DEPLOYMENT (COMMIT-BASED) ║" + echo "╚════════════════════════════════════════════════════════════════╝" + + for i in {1..10}; do + echo "" + echo "┌────────────────────────────────────────────────────────────────┐" + echo "│ 🔄 Attempt $i/10" + echo "│ 🕐 Time: $(TZ=Asia/Kolkata date '+%Y-%m-%d %H:%M:%S IST')" + echo "└────────────────────────────────────────────────────────────────┘" + + DEPLOYS=$(curl -s -H "Authorization: Bearer $NETLIFY_TOKEN" \ + "https://api.netlify.com/api/v1/sites/$NETLIFY_SITE_ID/deploys") + + DEPLOY=$(echo "$DEPLOYS" | jq -r --arg branch "$BRANCH_NAME" --arg sha "$SHA" \ + '[.[] | select(.branch == $branch and (.commit_ref == $sha or .head == $sha))] | + sort_by(.created_at) | reverse | .[0]') + + if [ "$DEPLOY" != "null" ] && [ -n "$DEPLOY" ]; then + DEPLOY_ID=$(echo "$DEPLOY" | jq -r '.id') + CREATED_AT=$(echo "$DEPLOY" | jq -r '.created_at') + CREATED_TIMESTAMP=$(date -d "$CREATED_AT" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%S" "$CREATED_AT" +%s 2>/dev/null || echo "$TRIGGER_TIME") + + BUILD_URL="https://app.netlify.com/sites/${{ secrets.NETLIFY_SITE_ID }}/deploys/$DEPLOY_ID" + + TIME_DIFF=$((CREATED_TIMESTAMP - TRIGGER_TIME)) + + if [ $TIME_DIFF -ge -5 ]; then + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ ✅ DEPLOYMENT FOUND! ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "📦 Deployment Information:" + echo "────────────────────────────────────────────────────────────────" + echo " 🆔 Deploy ID: $DEPLOY_ID" + echo " 🔗 Build URL: Obfuscated, check updated build status in the PR comment" + echo " 📅 Created (UTC): $CREATED_AT" + echo " 📅 Created (IST): $(TZ=Asia/Kolkata date -d @$CREATED_TIMESTAMP '+%Y-%m-%d %H:%M:%S')" + echo " ✅ Method: Commit-based validation" + echo "════════════════════════════════════════════════════════════════" + DEPLOY_FOUND_VIA="commit" + + echo "deploy_id=$DEPLOY_ID" >> $GITHUB_OUTPUT + echo "build_url=$BUILD_URL" >> $GITHUB_OUTPUT + echo "deploy_found_via=$DEPLOY_FOUND_VIA" >> $GITHUB_OUTPUT + echo "created_at_ist=$(TZ=Asia/Kolkata date -d @$CREATED_TIMESTAMP '+%Y-%m-%d %H:%M:%S IST')" >> $GITHUB_OUTPUT + break + else + echo " ⚠️ Found deployment but it's too old (${TIME_DIFF}s offset)" + fi + fi + + if [ $i -lt 10 ]; then + echo " ⏳ Not found yet, waiting 15 seconds..." + sleep 15 + fi + done + + if [ -z "$DEPLOY_ID" ] || [ "$DEPLOY_ID" = "null" ]; then + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ FALLBACK: SEARCHING BY BRANCH NAME ║" + echo "╚════════════════════════════════════════════════════════════════╝" + + DEPLOY=$(echo "$DEPLOYS" | jq -r --arg branch "$BRANCH_NAME" \ + '[.[] | select(.branch == $branch)] | sort_by(.created_at) | reverse | .[0]') + + if [ "$DEPLOY" != "null" ] && [ -n "$DEPLOY" ]; then + DEPLOY_ID=$(echo "$DEPLOY" | jq -r '.id') + CREATED_AT=$(echo "$DEPLOY" | jq -r '.created_at') + CREATED_TIMESTAMP=$(date -d "$CREATED_AT" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%S" "$CREATED_AT" +%s 2>/dev/null || echo "$TRIGGER_TIME") + BUILD_URL="https://app.netlify.com/sites/${{ secrets.NETLIFY_SITE_ID }}/deploys/$DEPLOY_ID" + + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ ✅ DEPLOYMENT FOUND (BRANCH-BASED) ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "📦 Deployment Information:" + echo "────────────────────────────────────────────────────────────────" + echo " 🆔 Deploy ID: $DEPLOY_ID" + echo " 🔗 Build URL: Obfuscated, check updated build status in the PR comment" + echo " 📅 Created (UTC): $CREATED_AT" + echo " 📅 Created (IST): $(TZ=Asia/Kolkata date -d @$CREATED_TIMESTAMP '+%Y-%m-%d %H:%M:%S')" + echo " ⚠️ Method: Branch-based validation (fallback)" + echo "════════════════════════════════════════════════════════════════" + DEPLOY_FOUND_VIA="branch" + + echo "deploy_id=$DEPLOY_ID" >> $GITHUB_OUTPUT + echo "build_url=$BUILD_URL" >> $GITHUB_OUTPUT + echo "deploy_found_via=$DEPLOY_FOUND_VIA" >> $GITHUB_OUTPUT + echo "created_at_ist=$(TZ=Asia/Kolkata date -d @$CREATED_TIMESTAMP '+%Y-%m-%d %H:%M:%S IST')" >> $GITHUB_OUTPUT + else + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ ❌ DEPLOYMENT NOT FOUND ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "No deployment could be located using either method." + echo "════════════════════════════════════════════════════════════════" + exit 1 + fi + fi + + - name: Update PR Comment (Deployment Found) + if: steps.poll_netlify.outputs.deploy_id != '' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + console.log('════════════════════════════════════════════════════════════════'); + console.log('📝 Updating PR comment with deployment details...'); + console.log('════════════════════════════════════════════════════════════════'); + + const commentId = Number('${{ steps.initial_comment.outputs.comment_id }}'); + const existing = await github.rest.issues.getComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: commentId + }); + + const deployFoundVia = '${{ steps.poll_netlify.outputs.deploy_found_via }}'; + const sha = '${{ steps.netlify_build.outputs.sha }}'; + const shortSha = sha.substring(0, 7); + const repo = '${{ github.repository }}'; + const commitLink = `[\`${shortSha}\`](https://github.com/${repo}/commit/${sha})`; + const jobUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`; + const deployId = '${{ steps.poll_netlify.outputs.deploy_id }}'; + const updateIST = new Date().toLocaleString('en-US', { timeZone: 'Asia/Kolkata', hourCycle: 'h23' }); + + const detailLine = deployFoundVia === 'commit' + ? `- **Validation:** Commit-based (${commitLink})` + : `- **Validation:** Branch-based`; + + const append = `--- + ### ⏳ Netlify Deployment In Progress + (Update logged at: ${updateIST} IST | Commit: ${commitLink}) + + - **Status:** Deployment found on Netlify. + - **Deploy ID:** \`${deployId}\` + ${detailLine} + - **Netlify Build URL:** [View Deployment](${{ steps.poll_netlify.outputs.build_url }}) + - **GitHub Actions Job:** [View Job Log](${jobUrl}) + - **Deployment Created:** ${{ steps.poll_netlify.outputs.created_at_ist }} + + ⏳ Build is currently in progress. Monitoring status...`; + + const body = `${existing.data.body}\n\n${append}`; + await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: commentId, body }); + + console.log('✅ PR comment updated successfully'); + console.log('════════════════════════════════════════════════════════════════'); + + - name: Monitor Deployment Status + id: check_status + env: + NETLIFY_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + run: | + DEPLOY_ID="${{ steps.poll_netlify.outputs.deploy_id }}" + BRANCH_NAME="${{ steps.netlify_build.outputs.branch_name }}" + SHA="${{ steps.netlify_build.outputs.sha }}" + PR_NUMBER="${{ steps.netlify_build.outputs.pr_number }}" + MAX_WAIT_TIME=1800 + POLL_INTERVAL=90 + ELAPSED_TIME=0 + USE_FALLBACK=false + + echo "════════════════════════════════════════════════════════════════" + echo "📊 DEPLOYMENT STATUS MONITORING" + echo "════════════════════════════════════════════════════════════════" + echo "" + echo "📋 Monitoring Configuration:" + echo "────────────────────────────────────────────────────────────────" + echo " 🆔 Deploy ID: $DEPLOY_ID" + echo " 🔖 Branch: $BRANCH_NAME" + echo " ⏱️ Max Wait Time: 30 minutes" + echo " 🔄 Poll Interval: 90 seconds" + echo " 🕐 Started (IST): $(TZ=Asia/Kolkata date -d @${{ steps.netlify_build.outputs.trigger_time }} '+%Y-%m-%d %H:%M:%S')" + echo "════════════════════════════════════════════════════════════════" + + if [ -z "$DEPLOY_ID" ] || [ "$DEPLOY_ID" = "null" ]; then + echo "" + echo "⚠️ WARNING: No Deploy ID found. Using branch-based fallback." + USE_FALLBACK=true + fi + + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ POLLING DEPLOYMENT STATUS ║" + echo "╚════════════════════════════════════════════════════════════════╝" + + while [ $ELAPSED_TIME -lt $MAX_WAIT_TIME ]; do + MINUTES_ELAPSED=$(awk "BEGIN {printf \"%.1f\", $ELAPSED_TIME/60}") + + echo "" + echo "┌────────────────────────────────────────────────────────────────┐" + echo "│ 🔄 Status Check" + echo "│ ⏱️ Elapsed: ${MINUTES_ELAPSED}/30.0 minutes" + echo "│ 🕐 Time (IST): $(TZ=Asia/Kolkata date '+%Y-%m-%d %H:%M:%S')" + echo "└────────────────────────────────────────────────────────────────┘" + + if [ "$USE_FALLBACK" = "true" ]; then + DEPLOYS=$(curl -s -H "Authorization: Bearer $NETLIFY_TOKEN" \ + "https://api.netlify.com/api/v1/sites/$NETLIFY_SITE_ID/deploys") + + DEPLOY=$(echo "$DEPLOYS" | jq -r --arg branch "$BRANCH_NAME" \ + '[.[] | select(.branch == $branch)] | sort_by(.created_at) | reverse | .[0]') + else + DEPLOY=$(curl -s -H "Authorization: Bearer $NETLIFY_TOKEN" \ + "https://api.netlify.com/api/v1/sites/$NETLIFY_SITE_ID/deploys/$DEPLOY_ID") + fi + + if [ "$DEPLOY" != "null" ] && [ -n "$DEPLOY" ]; then + STATE=$(echo "$DEPLOY" | jq -r '.state') + CURRENT_DEPLOY_ID=$(echo "$DEPLOY" | jq -r '.id') + DEPLOY_URL=$(echo "$DEPLOY" | jq -r '.deploy_ssl_url // .ssl_url // .url') + ERROR_MESSAGE=$(echo "$DEPLOY" | jq -r '.error_message // ""') + + BUILD_URL="https://app.netlify.com/sites/$NETLIFY_SITE_ID/deploys/$DEPLOY_ID" + + if [ "$USE_FALLBACK" = "true" ] && [ -z "$DEPLOY_ID" ]; then + DEPLOY_ID=$CURRENT_DEPLOY_ID + echo " 🆔 Deployment ID: $DEPLOY_ID" + fi + + echo " 📊 Current State: $STATE" + + if [ "$STATE" = "ready" ]; then + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ ✅ DEPLOYMENT SUCCESSFUL! ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "🎉 Deployment Details:" + echo "────────────────────────────────────────────────────────────────" + echo " 🌐 Preview URL: $DEPLOY_URL" + echo " 🔗 Build URL: Obfuscated, check updated build status in the PR comment" + echo " 🆔 Deploy ID: $DEPLOY_ID" + echo "════════════════════════════════════════════════════════════════" + + echo "status=success" >> $GITHUB_OUTPUT + echo "deploy_url=$DEPLOY_URL" >> $GITHUB_OUTPUT + echo "deploy_id=$DEPLOY_ID" >> $GITHUB_OUTPUT + exit 0 + elif [ "$STATE" = "error" ]; then + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ ❌ DEPLOYMENT FAILED ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "💥 Error Details:" + echo "────────────────────────────────────────────────────────────────" + echo " 🔗 Build URL: Obfuscated, check updated build status in the PR comment" + echo " 🆔 Deploy ID: $DEPLOY_ID" + echo " 📝 Error: $ERROR_MESSAGE" + echo "════════════════════════════════════════════════════════════════" + + echo "status=failure" >> $GITHUB_OUTPUT + echo "error_message=$ERROR_MESSAGE" >> $GITHUB_OUTPUT + echo "deploy_id=$DEPLOY_ID" >> $GITHUB_OUTPUT + exit 0 + elif [ "$STATE" = "building" ] || [ "$STATE" = "enqueued" ] || [ "$STATE" = "processing" ]; then + echo " ⏳ Status: In Progress ($STATE)" + echo " 🔗 Build URL: Obfuscated, check updated build status in the PR comment" + else + echo " ⚠️ Status: Unknown ($STATE)" + fi + else + echo " ❓ No data returned from Netlify API" + fi + + sleep $POLL_INTERVAL + ELAPSED_TIME=$((ELAPSED_TIME + POLL_INTERVAL)) + done + + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ ⏱️ TIMEOUT REACHED ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "⚠️ The deployment is taking longer than 30 minutes." + echo "Please check the Netlify dashboard for status updates." + echo "════════════════════════════════════════════════════════════════" + + echo "status=timeout" >> $GITHUB_OUTPUT + echo "deploy_id=$DEPLOY_ID" >> $GITHUB_OUTPUT + + - name: Update PR Comment (Success) + if: steps.check_status.outputs.status == 'success' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + console.log('════════════════════════════════════════════════════════════════'); + console.log('✅ Updating PR comment with success status...'); + console.log('════════════════════════════════════════════════════════════════'); + + const commentId = Number('${{ steps.initial_comment.outputs.comment_id }}'); + const existing = await github.rest.issues.getComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: commentId }); + const updateIST = new Date().toLocaleString('en-US', { timeZone: 'Asia/Kolkata', hour12: false }); + const sha = '${{ steps.netlify_build.outputs.sha }}'; + const shortSha = sha.substring(0, 7); + const repo = '${{ github.repository }}'; + const commitLink = `[\`${shortSha}\`](https://github.com/${repo}/commit/${sha})`; + const jobUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`; + + const append = `--- + ### ✅ Netlify Preview Deploy Successful! + (Update logged at: ${updateIST} IST | Commit: ${commitLink}) + + - **Status:** Build Succeeded + - **Preview URL:** [${{ steps.check_status.outputs.deploy_url }}](${{ steps.check_status.outputs.deploy_url }}) + - **Netlify Deployment:** [View Deployment Log](https://app.netlify.com/sites/${{ secrets.NETLIFY_SITE_ID }}/deploys/${{ steps.check_status.outputs.deploy_id }}) + - **GitHub Actions Job:** [View Job Log](${jobUrl}) + + The preview is ready for review. Please test your changes thoroughly before merging.`; + + const body = `${existing.data.body}\n\n${append}`; + await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: commentId, body }); + + console.log('✅ Success comment posted'); + console.log('════════════════════════════════════════════════════════════════'); + + - name: Update PR Comment (Failure) + if: steps.check_status.outputs.status == 'failure' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + console.log('════════════════════════════════════════════════════════════════'); + console.log('❌ Updating PR comment with failure status...'); + console.log('════════════════════════════════════════════════════════════════'); + + const commentId = Number('${{ steps.initial_comment.outputs.comment_id }}'); + const existing = await github.rest.issues.getComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: commentId }); + const updateIST = new Date().toLocaleString('en-US', { timeZone: 'Asia/Kolkata', hour12: false }); + const sha = '${{ steps.netlify_build.outputs.sha }}'; + const shortSha = sha.substring(0, 7); + const repo = '${{ github.repository }}'; + const commitLink = `[\`${shortSha}\`](https://github.com/${repo}/commit/${sha})`; + const jobUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`; + const errorMessage = `${{ steps.check_status.outputs.error_message }}`.trim() || "No error message provided by Netlify. Please check the log."; + + const append = `--- + # 🚨 ⛔ NETLIFY DEPLOYMENT FAILED ⛔ 🚨 + (Update logged at: ${updateIST} IST | Commit: ${commitLink}) + + ## ❌ **DO NOT MERGE THIS PR** ❌ + + The Netlify preview deployment has **FAILED**. This PR contains changes that break the build process. + + ### 🔴 Critical Issue Details: + - **Status:** Build Failed + - **Deploy ID:** \`${{ steps.check_status.outputs.deploy_id }}\` + - **Netlify Deployment:** [View Deployment Log](https://app.netlify.com/sites/${{ secrets.NETLIFY_SITE_ID }}/deploys/${{ steps.check_status.outputs.deploy_id }}) + - **GitHub Actions Job:** [View Job Log](${jobUrl}) + - **Error Message:** + + ${errorMessage} + + ### 🛠️ Required Actions: + 1. **Review the error message and Netlify log carefully.** + 2. **Fix the build issues in your branch.** + 3. **Trigger a new build with \`netlify build\` comment once fixed.** + + ### ⚠️ WARNING TO MAINTAINERS: + **This PR has a failing build and should NOT be merged.** + + --- + **🔴 MERGING THIS PR IN ITS CURRENT STATE WILL BREAK PRODUCTION 🔴**`; + + const body = `${existing.data.body}\n\n${append}`; + await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: commentId, body }); + + console.log('❌ Failure comment posted'); + console.log('════════════════════════════════════════════════════════════════'); + + - name: Update PR Comment (Timeout) + if: steps.check_status.outputs.status == 'timeout' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + console.log('════════════════════════════════════════════════════════════════'); + console.log('⏱️ Updating PR comment with timeout status...'); + console.log('════════════════════════════════════════════════════════════════'); + + const commentId = Number('${{ steps.initial_comment.outputs.comment_id }}'); + const existing = await github.rest.issues.getComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: commentId }); + const updateIST = new Date().toLocaleString('en-US', { timeZone: 'Asia/Kolkata', hourCycle: 'h23' }); + const sha = '${{ steps.netlify_build.outputs.sha }}'; + const shortSha = sha.substring(0, 7); + const repo = '${{ github.repository }}'; + const commitLink = `[\`${shortSha}\`](https://github.com/${repo}/commit/${sha})`; + const jobUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`; + + const append = `--- + ### ⏱️ Netlify Deployment Status: Timeout + (Update logged at: ${updateIST} IST | Commit: ${commitLink}) + + - **Status:** Timeout Reached (>30 minutes) + - **Netlify Deployment:** [View Deployment Log](https://app.netlify.com/sites/${{ secrets.NETLIFY_SITE_ID }}/deploys/${{ steps.check_status.outputs.deploy_id }}) + - **GitHub Actions Job:** [View Job Log](${jobUrl}) + - **Expected Preview URL:** \`https://${{ steps.sanitize_branch.outputs.name }}--docs-website-netlify.netlify.app\` + + **Next steps:** + 1. Check the [Netlify dashboard](https://app.netlify.com/sites/${{ secrets.NETLIFY_SITE_ID }}/deploys/${{ steps.check_status.outputs.deploy_id }}) manually for status. + 2. If the build is still running, please wait. + 3. If needed, re-trigger with a new \`netlify build\` comment.`; + + const body = `${existing.data.body}\n\n${append}`; + await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: commentId, body }); + + console.log('⏱️ Timeout comment posted'); + console.log('════════════════════════════════════════════════════════════════'); + + - name: Fail workflow on deployment failure + if: steps.check_status.outputs.status == 'failure' + run: | + echo "::error::Netlify deployment failed. This PR should not be merged." + exit 1 \ No newline at end of file