From 2b1be188920210e47d837c0861f8af6991142cd2 Mon Sep 17 00:00:00 2001 From: Kim Pohas Date: Thu, 19 Feb 2026 14:59:13 -0800 Subject: [PATCH] Add production verification step to deploy workflow Adds a new `job_verify-production.yml` reusable workflow that runs after the Jenkins pipeline is triggered. It waits for propagation, then curls each changed doc/blog page on sumologic.com/help and fails (triggering the existing Slack alert) if any return a non-200 status. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/job_verify-production.yml | 122 ++++++++++++++++++ .../workflow_deploy-to-pantheon-prod.yml | 7 +- 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/job_verify-production.yml diff --git a/.github/workflows/job_verify-production.yml b/.github/workflows/job_verify-production.yml new file mode 100644 index 0000000000..9cf1861eb7 --- /dev/null +++ b/.github/workflows/job_verify-production.yml @@ -0,0 +1,122 @@ +name: Verify production deployment + +on: + workflow_call: + inputs: + WAIT_SECONDS: + description: Seconds to wait for production to propagate before checking + default: 300 + type: number + BASE_URL: + description: Production base URL to verify against + default: 'https://www.sumologic.com/help' + type: string + GIT_SHA: + description: The commit SHA whose changed files should be verified + required: true + type: string + secrets: + SLACK_URL: + required: false + +jobs: + verify-production: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.GIT_SHA }} + fetch-depth: 2 + + - name: Wait for production propagation + run: | + echo "Waiting ${{ inputs.WAIT_SECONDS }} seconds for production to propagate..." + sleep ${{ inputs.WAIT_SECONDS }} + + - name: Verify changed pages are live on production + run: | + set -euo pipefail + + # Map of source directory prefixes to their production URL prefixes. + # Order matters: more specific prefixes should come first. + declare -A ROUTE_MAP=( + ["docs/"]="/docs/" + ["blog-service/"]="/release-notes-service/" + ["blog-cse/"]="/release-notes-cse/" + ["blog-csoar/"]="/release-notes-csoar/" + ["blog-developer/"]="/release-notes-developer/" + ["blog-collector/"]="/release-notes-collector/" + ) + + BASE_URL="${{ inputs.BASE_URL }}" + FAILED=0 + CHECKED=0 + SKIPPED=0 + + # Get files changed in the deployed commit, scoped to content dirs only + CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD -- \ + 'docs/' \ + 'blog-service/' \ + 'blog-cse/' \ + 'blog-csoar/' \ + 'blog-developer/' \ + 'blog-collector/' \ + | grep -E '\.(md|mdx)$' || true) + + if [ -z "$CHANGED_FILES" ]; then + echo "No doc/blog content files changed in this commit. Nothing to verify." + exit 0 + fi + + echo "Changed content files:" + echo "$CHANGED_FILES" + echo "" + + while IFS= read -r file; do + url_path="" + + for prefix in "${!ROUTE_MAP[@]}"; do + if [[ "$file" == ${prefix}* ]]; then + # Strip source prefix and .md/.mdx extension, apply route prefix + relative="${file#$prefix}" + relative="${relative%.*}" + # Index files (e.g. index.md) collapse to their directory URL + if [[ "$relative" == */index || "$relative" == "index" ]]; then + relative="${relative%/index}" + relative="${relative%index}" + fi + url_path="${ROUTE_MAP[$prefix]}${relative}/" + break + fi + done + + if [ -z "$url_path" ]; then + echo "SKIP (no route mapping): $file" + SKIPPED=$((SKIPPED + 1)) + continue + fi + + full_url="${BASE_URL}${url_path}" + echo -n "Checking: $full_url ... " + + http_status=$(curl --silent --output /dev/null --write-out "%{http_code}" \ + --max-time 15 --retry 2 --retry-delay 5 "$full_url") + + if [ "$http_status" = "200" ]; then + echo "OK (HTTP $http_status)" + CHECKED=$((CHECKED + 1)) + else + echo "FAIL (HTTP $http_status)" + FAILED=$((FAILED + 1)) + fi + done <<< "$CHANGED_FILES" + + echo "" + echo "Results: $CHECKED OK, $FAILED failed, $SKIPPED skipped" + + if [ "$FAILED" -gt 0 ]; then + echo "::error::$FAILED page(s) returned a non-200 status on production. The deploy pipeline reported success but content may not be live." + exit 1 + fi diff --git a/.github/workflows/workflow_deploy-to-pantheon-prod.yml b/.github/workflows/workflow_deploy-to-pantheon-prod.yml index d9289a5647..5d9635e5ac 100644 --- a/.github/workflows/workflow_deploy-to-pantheon-prod.yml +++ b/.github/workflows/workflow_deploy-to-pantheon-prod.yml @@ -41,8 +41,13 @@ jobs: WEBOPS_JENKINS_HOST: ${{ secrets.WEBOPS_JENKINS_HOST }} WEBOPS_AWS_ROLE_JENKINS: ${{ secrets.WEBOPS_AWS_ROLE_JENKINS }} WEBOPS_WEBHOOK_TOKEN: ${{ secrets.WEBOPS_WEBHOOK_TOKEN }} + verify-production: + needs: trigger-jenkins-pipeline + uses: ./.github/workflows/job_verify-production.yml + with: + GIT_SHA: ${{ github.sha }} notify-channel: - needs: [build-site,deploy-to-pantheon,trigger-jenkins-pipeline] + needs: [build-site,deploy-to-pantheon,trigger-jenkins-pipeline,verify-production] if: ${{ failure() }} uses: ./.github/workflows/job_slack-notification.yml with: