Skip to content

Claude Post Inventory #97

Claude Post Inventory

Claude Post Inventory #97

name: Claude Post Inventory
# 블로그 배포가 끝날 때마다 초안(draft)·예약(scheduled) 글 현황을 이슈 하나에
# 갱신합니다. 사실 나열만 하며, 발행 권유·재촉은 하지 않습니다.
#
# 역할을 둘로 나눠 둔 이유:
# - 수집(post-inventory-collect.py): frontmatter·git 이력·sitemap 대조
# - 서술(Claude): 표 렌더링과 이슈 갱신
# 2026-08-04 수동 실행이 7턴 27초 만에 permission denial 4건으로 끝났는데,
# exit 0이라 잡은 초록이었고 이슈 #163은 7/30 상태 그대로였다. 세는 일을
# 스크립트로 내리면 그 실패 모드가 사라지고, 수집 결과가 Step Summary에 남는다.
#
# 월간 cron 대신 deploy-blog.yml 완료(workflow_run)를 트리거로 쓴다 — 배포마다
# sitemap이 갱신되므로 그 직후에 봐야 in_sitemap 판정이 그 시점 기준으로 맞다.
# deploy-blog는 push·매일 cron·수동 실행 전부에서 돌므로 이 워크플로도 그때마다
# 같이 갱신된다. 배포가 실패하면 sitemap이 그대로이므로 건너뛴다(아래 if).
on:
workflow_run:
# workflow_run은 파일 경로가 아니라 워크플로의 `name`으로 매칭한다 —
# deploy-blog.yml의 name을 고치면 여기도 함께 고쳐야 한다. 어긋나면
# 실패가 아니라 **아예 실행되지 않아서** Actions 탭에 흔적도 남지 않는다.
workflows: ['Deploy Blog to Cloudflare Workers']
types: [completed]
workflow_dispatch:
concurrency:
group: claude-post-inventory
cancel-in-progress: false
permissions:
contents: read
issues: write
id-token: write
jobs:
inventory:
# workflow_run은 deploy-blog가 실패해도 완료되면 일단 트리거된다.
# 배포 실패 시 sitemap이 갱신되지 않으므로 다시 볼 이유가 없어 건너뛴다.
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# 초안이 언제 생겼는지 git 이력으로 계산하기 위해 전체 이력 필요
fetch-depth: 0
# workflow_run은 기본 checkout이 default 브랜치 최신 커밋을 받아오지
# 않으므로, 트리거한 deploy-blog 런의 커밋을 명시한다. workflow_dispatch로
# 수동 실행할 때는 head_sha가 없으므로 일반 checkout으로 폴백한다.
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
# frontmatter·git 이력·sitemap 대조는 세면 끝나는 일이라 스크립트가 맡는다.
# 2026-08-04 수동 실행이 7턴 27초 만에 denial 4건으로 끝나 이슈가 갱신되지
# 않았는데, exit 0이라 로그만으로는 정상처럼 보였다. 그 실패 모드를 없앤다.
- name: Collect draft/scheduled facts
run: |
python3 .github/scripts/post-inventory-collect.py \
--out "${GITHUB_WORKSPACE}/post-inventory-facts.json"
- name: Run Claude post inventory
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# 예약 실행 잡은 "아무것도 만들지 않음"이 정상 종료라 성공 로그만
# 남는다. 실행 리포트를 Step Summary에 찍어 두면 무엇을 확인하고
# 왜 만들지 않았는지를 raw 로그를 펼치지 않고 볼 수 있다.
display_report: true
prompt: |
당신은 블로그 초안·예약글 인벤토리 담당입니다. 블로그가 배포될 때마다
현황을 사실만 기록합니다. 모든 산출물은 한글로 작성하세요.
<중요 — 어조 규칙>
발행을 권유·재촉·평가하는 문구를 절대 쓰지 마세요.
("슬슬 발행하면 좋겠다", "오래 방치되었다", "완성도가 높아 보인다"
전부 금지) 날짜와 상태의 사실 나열만 합니다. 이 저장소 주인의 원칙:
글은 내용을 완전히 숙지하기 전에는 발행하지 않으며, 그 판단은
본인만 합니다.
</중요>
<입력>
`post-inventory-facts.json` (저장소 루트)에 이번 실행의 수집 결과가
있습니다. 이 파일을 Read 하세요. 저장소를 다시 훑거나 git·curl을
돌릴 필요가 없습니다 — 그런 도구는 주어지지 않습니다.
- `drafts` / `scheduled`: status별 글 목록. 각 항목에 title, series,
date, scheduledDate, publish_at(= scheduledDate ?? date),
first_commit, last_commit, in_sitemap이 들어 있습니다
- `today_kst`: 오늘 날짜
- `shallow_clone`: true면 first_commit이 잘린 값이니 표에 "-"로
적고 각주를 다세요 (정상 실행에서는 false입니다)
- `sitemap.status`가 200이 아니면 in_sitemap을 신뢰할 수 없습니다.
그때는 scheduled 표를 그대로 싣되 비고에 "sitemap 조회 실패로
배포 여부 미확인"이라고 적으세요
</입력>
<표에 넣을 것>
- draft: 전부
- scheduled: `in_sitemap`이 false인 글만. true면 이미 사이트에
올라간 글이라 제외합니다. `publish_at`이 `today_kst`보다 과거인데
in_sitemap이 false면 비고에 "예정일 경과, 배포 대기"라고 적으세요
</표에 넣을 것>
<산출 — 이슈 하나를 계속 갱신>
제목이 "📋 블로그 초안·예약글 인벤토리"인 열린 이슈를 찾으세요.
- 있으면 `gh issue edit <번호> --body-file <파일>`로 본문 전체를
이번 결과로 교체
- 없으면 같은 제목으로 새로 생성
본문 구성: 갱신 일자 + draft 표 + scheduled 표 (마크다운 표).
본문 파일은 **하위 디렉터리를 만들지 말고** 작업 디렉터리 루트에
바로 쓰세요 (디렉터리 생성은 차단됩니다).
초안·예약글이 모두 0개인 경우: 기존 이슈가 있으면 "현재 없음"으로
갱신하고, 이슈 자체가 없다면 새로 만들지 말고 종료하세요.
</산출>
# 수집이 끝난 뒤라 git·curl이 필요 없다. Bash는 이슈 조작 하나뿐.
claude_args: '--model claude-sonnet-5 --allowed-tools "Read,Write,Grep,Glob,Bash(gh issue:*)"'