Skip to content

Add "What Is Immutable Infrastructure?" explainer #3680

Add "What Is Immutable Infrastructure?" explainer

Add "What Is Immutable Infrastructure?" explainer #3680

name: Social Media Review
on:
pull_request:
types: [opened, reopened, ready_for_review, synchronize]
paths:
- 'content/blog/*/index.md'
issue_comment:
types: [created]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to review'
required: true
type: string
jobs:
social-review:
concurrency:
group: >-
social-review-${{
github.event.pull_request.number
|| github.event.inputs.pr_number
|| (github.event_name == 'issue_comment'
&& contains(github.event.comment.body, '/social-review')
&& github.event.issue.number)
|| github.run_id
}}
cancel-in-progress: true
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(github.event.comment.body, '/social-review') &&
(github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'COLLABORATOR')) ||
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login != 'pulumi-bot' &&
github.event.pull_request.user.login != 'dependabot[bot]' &&
!(github.event.action == 'synchronize' && github.actor == 'claude[bot]'))
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
pull-requests: write
id-token: write
actions: read
steps:
- name: Resolve PR info
id: pr-info
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
PR_NUMBER="${{ github.event.inputs.pr_number }}"
elif [ "${{ github.event_name }}" = "issue_comment" ]; then
PR_NUMBER="${{ github.event.issue.number }}"
else
PR_NUMBER="${{ github.event.pull_request.number }}"
fi
if [ "${{ github.event_name }}" = "pull_request" ]; then
HEAD_REF="${{ github.event.pull_request.head.ref }}"
BASE_REF="${{ github.event.pull_request.base.ref }}"
else
HEAD_REF=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json headRefName -q .headRefName)
BASE_REF=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json baseRefName -q .baseRefName)
fi
echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
echo "head_ref=$HEAD_REF" >> "$GITHUB_OUTPUT"
echo "base_ref=$BASE_REF" >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@v7
with:
ref: ${{ steps.pr-info.outputs.head_ref }}
fetch-depth: 0
- uses: astral-sh/setup-uv@v7
- name: Skip if social copy unchanged since prior review
id: gate
if: github.event_name == 'pull_request' && github.event.action == 'synchronize'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_REF: ${{ steps.pr-info.outputs.head_ref }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ steps.pr-info.outputs.number }}
REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
SERVER_URL: ${{ github.server_url }}
run: |
set -e
# The review verdict lives in the PR comment, not the run conclusion —
# if the social copy hasn't changed since the comment was last updated,
# the verdict still applies. Re-running risks flapping (PASS<->FAIL on
# identical copy) and wastes Claude calls on PRs stuck on a char-limit
# fail (where every push to the same over-limit copy would otherwise
# trigger a fresh review).
#
# Baseline preference order:
# 1. Our edit-in-place comment's footer SHA (most accurate — atomic
# with the comment write, unaffected by later step failures).
# 2. Last successful workflow run on this branch (legacy fallback
# for PRs that pre-date the marker comment).
PRIOR_SHA=""
PRIOR_RUN_ID=""
BASELINE_SOURCE=""
# (1) Try the marker comment first.
# `|| true` on the grep pipeline so a missing footer doesn't kill the
# script under `set -e` — fall through to the run-based baseline instead.
COMMENT_BODY=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" \
--jq '[.[] | select(.body | startswith("<!-- social-review -->"))] | .[-1].body // empty')
if [ -n "$COMMENT_BODY" ]; then
COMMENT_SHA=$(echo "$COMMENT_BODY" | grep -oE 'commit `[a-f0-9]{7,40}`' | head -1 | grep -oE '[a-f0-9]{7,40}' || true)
if [ -n "$COMMENT_SHA" ]; then
PRIOR_SHA="$COMMENT_SHA"
BASELINE_SOURCE="comment"
echo "Baseline: social-review comment footer SHA $PRIOR_SHA"
fi
fi
# (2) Fall back to last successful run on this branch.
# `gh run list --branch` (and the API's ?branch= query) is unreliable
# for slash-containing branch names — silently returns empty for some
# branches that do have runs. Query the workflow's runs unfiltered and
# filter by head_branch in jq client-side, which always works.
if [ -z "$PRIOR_SHA" ]; then
PRIOR=$(gh api \
"repos/$REPO/actions/workflows/claude-social-review.yml/runs?per_page=30" \
--jq "[.workflow_runs[] | select(.head_branch == \"$HEAD_REF\" and .event == \"pull_request\" and .conclusion == \"success\" and .id != $RUN_ID)] | .[0]")
PRIOR_SHA=$(echo "$PRIOR" | jq -r '.head_sha // ""')
PRIOR_RUN_ID=$(echo "$PRIOR" | jq -r '.id // ""')
if [ -n "$PRIOR_SHA" ]; then
BASELINE_SOURCE="run"
echo "Baseline: prior successful run $PRIOR_RUN_ID at SHA $PRIOR_SHA"
fi
fi
if [ -z "$PRIOR_SHA" ]; then
echo "No prior baseline (no marker comment, no successful run) — running review."
echo "should_skip=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# Force-push / rebase resilience: if the prior SHA is unreachable in
# the local clone, try to fetch it; if still missing, fall through to
# running the review (degrades to current behaviour, never worse).
if ! git cat-file -e "$PRIOR_SHA" 2>/dev/null; then
git fetch origin "$PRIOR_SHA" --depth=1 2>/dev/null || true
fi
if ! git cat-file -e "$PRIOR_SHA" 2>/dev/null; then
echo "Prior baseline $PRIOR_SHA unreachable (force-push/rebase?) — running review."
echo "should_skip=false" >> "$GITHUB_OUTPUT"
exit 0
fi
uv run scripts/social/social-review-gate.py \
--repo . --prior "$PRIOR_SHA" --head "$HEAD_SHA" \
| tee /tmp/gate.json
SHOULD_SKIP=$(jq -r .should_skip /tmp/gate.json)
REASON=$(jq -r .reason /tmp/gate.json)
if [ "$SHOULD_SKIP" = "true" ]; then
echo "should_skip=true" >> "$GITHUB_OUTPUT"
# Log to the action run instead of posting a PR comment — the comment
# was noisy on active PRs (one per push that didn't touch the social
# block). Anyone wanting a fresh review can still comment `/social-review`.
if [ "$BASELINE_SOURCE" = "comment" ]; then
VERDICT_LINK="The verdict in the existing social-review PR comment still applies."
NOTICE_TAIL="The PR comment from commit $PRIOR_SHA still applies."
else
VERDICT_LINK="The verdict from the [prior run](${SERVER_URL}/${REPO}/actions/runs/${PRIOR_RUN_ID}) still applies."
NOTICE_TAIL="Prior run: ${SERVER_URL}/${REPO}/actions/runs/${PRIOR_RUN_ID}."
fi
echo "::notice::Skipping social media review — $REASON. $NOTICE_TAIL Comment /social-review to force a fresh review."
{
echo "## 🟰 Skipping social media review"
echo ""
echo "**Reason:** $REASON"
echo ""
echo "$VERDICT_LINK"
echo ""
echo "Comment \`/social-review\` on the PR to force a fresh review."
} >> "$GITHUB_STEP_SUMMARY"
else
echo "should_skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Fetch secrets from ESC
if: steps.gate.outputs.should_skip != 'true'
id: esc-secrets
uses: pulumi/esc-action@v3
- name: Configure AWS Credentials
if: steps.gate.outputs.should_skip != 'true'
id: aws-creds
continue-on-error: true
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::388588623842:role/ContinuousDelivery
role-session-name: social-review
aws-region: us-west-2
- if: steps.gate.outputs.should_skip != 'true'
uses: pulumi/actions@v7
- name: Get social state bucket name
if: steps.gate.outputs.should_skip != 'true'
id: bucket
continue-on-error: true
env:
PULUMI_ACCESS_TOKEN: ${{ steps.esc-secrets.outputs.PULUMI_ACCESS_TOKEN }}
run: |
set -eo pipefail
BUCKET=$(pulumi -C infrastructure stack output socialStateBucketName --stack ${{ vars.PULUMI_STACK_NAME }})
echo "name=$BUCKET" >> "$GITHUB_OUTPUT"
- name: Configure git auth for private social_core git-dep
if: steps.gate.outputs.should_skip != 'true'
env:
PULUMI_BOT_TOKEN: ${{ steps.esc-secrets.outputs.PULUMI_BOT_TOKEN }}
run: |
git config --global url."https://x-access-token:${PULUMI_BOT_TOKEN}@github.com/".insteadOf "https://github.com/"
- name: Check which posts need social review
if: steps.gate.outputs.should_skip != 'true'
id: check
env:
SOCIAL_STATE_BUCKET: ${{ steps.bucket.outputs.name }}
BASE_REF: origin/${{ steps.pr-info.outputs.base_ref }}
run: |
set +e
uv run scripts/social/schedule-posts.py --check > .social-check-output.txt
EXIT=$?
set -e
if [ -s .social-check-output.txt ]; then
cat .social-check-output.txt
echo "needs_review=true" >> "$GITHUB_OUTPUT"
else
echo "No blog posts need social media review."
echo "needs_review=false" >> "$GITHUB_OUTPUT"
fi
if [ $EXIT -ne 0 ]; then
echo "has_errors=true" >> "$GITHUB_OUTPUT"
fi
- name: Run Claude Social Media Review
if: steps.gate.outputs.should_skip != 'true' && steps.check.outputs.needs_review == 'true'
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: |
You are running in a CI environment.
Review the social media copy for pull request #${{ steps.pr-info.outputs.number }}.
The social copy and status for each blog post is in `.social-check-output.txt`. Read that file — it contains the actual post text for each platform, plus metadata (character counts, already-posted status). Posts that are already posted or too old have been filtered out.
If you want more context about the blog post itself (to judge whether the social copy accurately represents it), the file path is listed in the output — you can read it.
Review each platform's copy using the rules in `.claude/commands/social-media-review/SKILL.md`.
IMPORTANT: Keep the review short and scannable. Use `#### Platform — PASS` or `#### Platform — FAIL` headings with bullet-pointed reasons. No analysis paragraphs, no rubric citations. A blogger should read it in 30 seconds.
Write the review to `.social-review.md`. Do NOT post a PR comment — a subsequent workflow step handles that. The file's body must:
1. Start with the literal line `<!-- social-review -->` (HTML comment marker; lets future runs find this comment to update it in place).
2. Then `## Social Media Review` and the per-platform sections.
3. End with a footer line: `_Updated for commit \`${{ github.event.pull_request.head.sha || github.event.workflow_run.head_sha }}\` (short: \`$(echo "${{ github.event.pull_request.head.sha || github.event.workflow_run.head_sha }}" | cut -c1-7)\`) at $(date -u '+%Y-%m-%d %H:%M UTC')._`
Use the Write tool. Do not run `gh pr comment`.
claude_args: '--model claude-sonnet-5 --allowed-tools "Read,Glob,Grep,Agent,Write,Bash(python3:*),Bash(gh pr view:*),Bash(gh pr diff:*),Bash(date:*)"'
- name: Find existing social review comment
if: steps.gate.outputs.should_skip != 'true' && steps.check.outputs.needs_review == 'true'
id: find-social-comment
uses: peter-evans/find-comment@v4
with:
issue-number: ${{ steps.pr-info.outputs.number }}
body-includes: '<!-- social-review -->'
direction: last
- name: Post or update social review comment
if: steps.gate.outputs.should_skip != 'true' && steps.check.outputs.needs_review == 'true'
uses: peter-evans/create-or-update-comment@v5
with:
issue-number: ${{ steps.pr-info.outputs.number }}
comment-id: ${{ steps.find-social-comment.outputs.comment-id }}
body-path: .social-review.md
edit-mode: replace
- name: Fail if copy exceeds character limits
if: steps.gate.outputs.should_skip != 'true' && steps.check.outputs.has_errors == 'true'
run: |
echo "::error::Social copy exceeds platform character limits. See review comment for details."
exit 1
env:
ESC_ACTION_OIDC_AUTH: true
ESC_ACTION_OIDC_ORGANIZATION: pulumi
ESC_ACTION_OIDC_REQUESTED_TOKEN_TYPE: urn:pulumi:token-type:access_token:organization
ESC_ACTION_ENVIRONMENT: github-secrets/pulumi-docs
ESC_ACTION_EXPORT_ENVIRONMENT_VARIABLES: false