Blog draft #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Blog draft | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| pr_numbers: | |
| description: 'PR numbers (comma-separated, e.g. 42,57,63)' | |
| required: true | |
| blog_theme: | |
| description: 'Blog theme/angle (optional)' | |
| required: false | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| draft: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Kiro CLI | |
| run: curl -fsSL https://cli.kiro.dev/install | bash | |
| - name: Collect PR data | |
| id: collect | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBERS: ${{ inputs.pr_numbers }} | |
| run: | | |
| mkdir -p /tmp/pr-data | |
| BRANCH_PARTS="" | |
| for pr in $(echo "$PR_NUMBERS" | tr ',' ' '); do | |
| echo "--- Collecting PR #${pr} ---" | |
| gh pr view "$pr" --json title,body,number --jq '{title,body,number}' > "/tmp/pr-data/pr-${pr}-meta.json" | |
| gh pr diff "$pr" > "/tmp/pr-data/pr-${pr}.diff" | |
| BRANCH_PARTS="${BRANCH_PARTS}${pr}-" | |
| done | |
| FIRST_PR=$(echo "$PR_NUMBERS" | cut -d',' -f1) | |
| SLUG=$(gh pr view "$FIRST_PR" --json title --jq '.title' | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | head -c 40) | |
| BRANCH_NAME="sdpm/pr${BRANCH_PARTS}${SLUG}" | |
| echo "branch_name=${BRANCH_NAME}" >> "$GITHUB_OUTPUT" | |
| - name: Clone blog repo | |
| env: | |
| BLOG_REPO_TOKEN: ${{ secrets.BLOG_REPO_TOKEN }} | |
| BLOG_REPO: ${{ secrets.BLOG_REPO }} | |
| run: | | |
| git clone "https://x-access-token:${BLOG_REPO_TOKEN}@github.com/${BLOG_REPO}.git" /tmp/zenn-blog | |
| - name: Generate blog draft with Kiro CLI | |
| env: | |
| KIRO_API_KEY: ${{ secrets.KIRO_API_KEY }} | |
| PR_NUMBERS: ${{ inputs.pr_numbers }} | |
| BLOG_THEME: ${{ inputs.blog_theme }} | |
| NO_COLOR: "1" | |
| TERM: dumb | |
| run: | | |
| INSTRUCTION="" | |
| if [ -f /tmp/zenn-blog/instruction.md ]; then | |
| INSTRUCTION=$(cat /tmp/zenn-blog/instruction.md) | |
| fi | |
| PR_DATA="" | |
| for pr in $(echo "$PR_NUMBERS" | tr ',' ' '); do | |
| META=$(cat "/tmp/pr-data/pr-${pr}-meta.json") | |
| DIFF=$(head -c 50000 "/tmp/pr-data/pr-${pr}.diff") | |
| PR_DATA="${PR_DATA} | |
| === PR #${pr} === | |
| ${META} | |
| Diff (truncated): | |
| ${DIFF} | |
| " | |
| done | |
| THEME_INSTRUCTION="" | |
| if [ -n "$BLOG_THEME" ]; then | |
| THEME_INSTRUCTION="テーマ/切り口: ${BLOG_THEME}" | |
| fi | |
| PROMPT="以下の instruction.md の指示を最優先で遵守してください。 | |
| ${INSTRUCTION} | |
| ${THEME_INSTRUCTION} | |
| PR データ: | |
| ${PR_DATA} | |
| Zenn frontmatter (---で始まる) から始まるMarkdownのみを出力してください。余計な説明は不要です。" | |
| kiro-cli chat --no-interactive --trust-all-tools "$PROMPT" > /tmp/blog-draft-raw.txt 2>/dev/null | |
| echo "Raw output: $(wc -c < /tmp/blog-draft-raw.txt) bytes" | |
| # Extract markdown starting from Zenn frontmatter (first ---) | |
| sed -n '/^---$/,$ p' /tmp/blog-draft-raw.txt > /tmp/blog-draft.md | |
| echo "Extracted: $(wc -c < /tmp/blog-draft.md) bytes" | |
| if [ ! -s /tmp/blog-draft.md ]; then | |
| echo "::error::Failed to extract blog draft from Kiro CLI output" | |
| exit 1 | |
| fi | |
| # Generate filename from frontmatter title or fallback | |
| TITLE=$(grep '^title:' /tmp/blog-draft.md | head -1 | sed 's/^title:\s*"//;s/"$//') | |
| if [ -n "$TITLE" ]; then | |
| FILENAME=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//' | head -c 50) | |
| FILENAME="sdpm-${FILENAME}.md" | |
| else | |
| FILENAME="sdpm-draft-$(date +%Y%m%d).md" | |
| fi | |
| echo "$FILENAME" > /tmp/blog-filename.txt | |
| echo "Filename: $FILENAME" | |
| - name: Push to blog repo and create PR | |
| env: | |
| BLOG_REPO_TOKEN: ${{ secrets.BLOG_REPO_TOKEN }} | |
| GH_TOKEN: ${{ secrets.BLOG_REPO_TOKEN }} | |
| BLOG_REPO: ${{ secrets.BLOG_REPO }} | |
| BRANCH_NAME: ${{ steps.collect.outputs.branch_name }} | |
| PR_NUMBERS: ${{ inputs.pr_numbers }} | |
| BLOG_THEME: ${{ inputs.blog_theme }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| run: | | |
| FILENAME=$(cat /tmp/blog-filename.txt) | |
| cd /tmp/zenn-blog | |
| git checkout publish | |
| git checkout -b "$BRANCH_NAME" | |
| cp /tmp/blog-draft.md "articles/${FILENAME}" | |
| git add "articles/${FILENAME}" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| PR_LIST=$(echo "$PR_NUMBERS" | tr ',' ' ' | xargs -I{} echo "- https://github.com/${GITHUB_REPOSITORY}/pull/{}") | |
| git commit -m "📝 Blog draft: ${FILENAME} | |
| Source PRs: | |
| ${PR_LIST}" | |
| git push origin "$BRANCH_NAME" | |
| PR_URL=$(gh pr create \ | |
| --repo "$BLOG_REPO" \ | |
| --base publish \ | |
| --head "$BRANCH_NAME" \ | |
| --title "📝 Blog draft: ${FILENAME}" \ | |
| --body "Auto-generated blog draft from SDPM CI. | |
| **Source PRs:** | |
| ${PR_LIST} | |
| **Theme:** ${BLOG_THEME} | |
| --- | |
| Review, edit, and merge to publish." \ | |
| 2>&1) | |
| echo "$PR_URL" > /tmp/blog-pr-url.txt | |
| - name: Label source PRs as blog:done | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBERS: ${{ inputs.pr_numbers }} | |
| run: | | |
| for pr in $(echo "$PR_NUMBERS" | tr ',' ' '); do | |
| gh pr edit "$pr" --remove-label "blog:pending" --add-label "blog:done" 2>/dev/null || true | |
| done | |
| - name: Notify Slack | |
| if: always() | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| PR_NUMBERS: ${{ inputs.pr_numbers }} | |
| run: | | |
| if [ -z "$SLACK_WEBHOOK_URL" ]; then exit 0; fi | |
| PR_URL=$(cat /tmp/blog-pr-url.txt 2>/dev/null || echo "N/A") | |
| SUBJECT="📝 Blog draft ready: PR ${PR_NUMBERS}" | |
| curl -s -X POST "$SLACK_WEBHOOK_URL" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"subject\":\"${SUBJECT}\",\"url\":\"${PR_URL}\"}" |