birchholt 직접 해보고 남기는 기록

예약 발행이 또 안 떴습니다 — 빈 커밋은 밀기 전에 접었습니다

2026-09-03 오전, 공개일이 된 글이 라이브에 없었습니다. 예약 발행 스케줄은 이틀 전에 한 번 손봐둔 참이었는데도 그랬습니다.

새 책이 놓였어야 할 빈 진열대를 창밖에서 바라보는 사람과 막 넘긴 탁상 달력

미래 날짜 글이 빌드(글 파일을 웹페이지로 찍어내는 과정)에서 빠진다는 것, 날짜가 됐다고 저절로 나타나지 않고 빌드가 한 번 더 돌아야 한다는 것은 먼저 확인해서 적어뒀습니다. 그래서 워크플로에 schedule 트리거를 달아뒀습니다. 워크플로는 GitHub 이 대신 돌려주는 자동 작업이고, 트리거는 그 작업을 언제 시작할지 정하는 조건입니다. 이 글은 그 설계가 또 실패했을 때 어떻게 되살리는지 적은 기록입니다.

빠진 것이 글 하나인지 배포 전체인지

부두에서 화물 목록 전체를 배와 대조하는 사람과 창고 문가에 남은 리본 달린 새 상자

splitting-the-repo 는 date: 2026-09-03 이고 publishDate 가 없어 date 를 대신 쓰는 글입니다. 09:00 KST 에 공개 자격을 얻고 1차 cron 이 10:23 KST 에 도는데, 11:44 KST 에 봤을 때 없었습니다. cron 은 정해둔 시각마다 작업을 자동으로 돌리는 스케줄입니다.

$ curl -o /dev/null -w '%{http_code}' https://birchholt.com/posts/splitting-the-repo/
404

404 하나로는 “배포는 돌았는데 그 글만 빠졌다"와 “배포가 아예 안 돌았다"가 갈리지 않습니다. 그래서 라이브 sitemap.xml 의 <loc> 목록을 같이 봤습니다. sitemap.xml 은 사이트에 있는 주소를 전부 적어 검색엔진에 알려주는 파일입니다. privacy 까지 포함해 9건인데 글 주소가 없었고 올라가 있는 결과물 자체가 그 글을 몰랐습니다. 한 주소만 찍는 것이 왜 확인이 아닌지는 먼저 적어뒀습니다.

9건은 2026-09-03 사고 당시에 센 값입니다. 글이 늘면 달라집니다 — 2026-09-22 에 다시 세니 22건이었습니다.

푸시, 그러니까 GitHub 으로 올리는 일까지는 돼 있었고(git rev-parse HEAD origin/main 두 값이 같았습니다) 제 컴퓨터에서 빌드해봐도 글이 들어 있었습니다. 자격도 커밋도 다 됐는데 라이브만 옛날 결과물이었습니다.

정각을 피해도 건너뜁니다

불이 들어오지 않은 첫 신호등이 걸린 시골 역과 기다리지 않고 손수레를 미는 사람

이게 처음이 아니었습니다. 2026-09-01 에 '0 1 * * *' 가 예정대로 돌지 않아 예약 글이 안 올라갔고 그때는 GitHub 공식 문서의 이 문장을 근거로 삼았습니다.

“The schedule event can be delayed during periods of high loads of GitHub Actions workflow runs. High load times include the start of every hour.”

출처: GitHub Actions · Events that trigger workflows 의 schedule 절입니다(2026-09-22 확인). 같은 문단은 “If the load is sufficiently high enough, some queued jobs may be dropped.” 로 이어집니다.

그래서 정각을 피해 '23 1 * * *' 로 옮겼고 그것만으로는 부족하다고 보고 하루 3회로 늘려뒀습니다.

cronKST역할
23 1 * * *10:231차입니다. 미래 글은 09:00 KST 부터 자격이 생깁니다
23 4 * * *13:231차가 안 떴을 때 잡습니다
23 7 * * *16:232차도 안 떴을 때 잡습니다

그 상태에서 2026-09-03 에 1차가 또 안 떴습니다. cron 이 안 뜬 두 번째 사례이고 정각을 피한 뒤로는 첫 사례입니다. 정각 회피로는 확률만 낮아집니다. GitHub 스케줄은 그 시각에 반드시 돈다는 보장이 없고 여유가 있을 때 돌려주는 쪽에 가깝습니다. 위 문서가 부하가 충분히 높으면 대기 중인 작업이 버려질 수 있다고 적어둔 그대로입니다. 백업 cron 두 개를 둔 판단 자체는 옳았습니다. 다만 이번 복구는 둘 중 어느 쪽도 하지 않았습니다. 13:23 까지 기다릴 생각이 없어서 2차가 돌기 전에 직접 되살렸습니다.

1차가 「건너뛴」 것인지 「돌다가 실패한」 것인지는 구분하지 못했습니다. 저장소가 비공개인 데다 명령줄 도구(gh)가 다른 계정으로 로그인돼 있어 Actions 의 실행 이력을 열어보지 못했습니다.

paths-ignore 가 걸린 워크플로의 복구 경로

내용물이 든 봉투만 통과시키는 분류 게이트 앞에서 빈 봉투를 든 채 멈춘 사람

먼저 떠오른 건 git commit --allow-empty 였습니다. 내용은 안 건드리고 push 만 발생시키면 될 것 같았습니다. 밀기 전에 워크플로를 열어봤고 거기서 접었습니다.

on:
  push:
    branches: [main]
    paths-ignore:
      - 'README.md'
      - '.gitignore'

위 조각은 같은 파일에서 push 트리거만 떼어 온 것입니다. 아래 표에 나오는 workflow_dispatch 와 앞에서 본 schedule 은 이 조각 밖, 같은 on: 블록에 함께 있습니다.

paths-ignore 가 있으면 GitHub 은 push 의 변경 파일 목록을 필터에 겁니다. 변경 파일이 0건이면 필터를 통과하는 파일도 0건이라 워크플로가 뜨지 않습니다. 다만 이건 설정을 읽고 내린 판단이고 밀어보고 확인하지는 않았습니다.

그래서 되살리는 경로를 둘로 봤습니다.

방법조건
Actions 탭에서 workflow_dispatch 수동 실행워크플로에 그 트리거가 있어야 합니다
변경이 든 커밋을 pushpaths-ignore 에 없는 파일이 하나는 바뀌어야 합니다

제 워크플로에는 workflow_dispatch 가 이미 들어 있었으니 두 경로 다 열려 있었습니다. 저는 두 번째를 골랐습니다. 마침 이 사건 자체를 워크플로 주석에 남길 참이어서 deploy.yml 에 경위를 적은 커밋이 그대로 트리거가 됐습니다. 푸시하고 라이브를 15초 간격으로 찍었습니다.

[11:49:09] try 3 → 404
[11:49:25] try 4 → 200

푸시부터 반영까지 1분 남짓이었습니다. 그때 돌린 폴링 명령은 남겨두지 않았습니다. 위 출력만 있습니다.

타임존 탓인 줄 알았는데 아니었습니다

시각이 다른 시계 세 개 아래 깃발 꽂힌 화분까지 똑같은 모종 판을 돋보기로 비교하는 사람

저는 제 컴퓨터(KST)와 Actions(UTC)의 판정이 9시간 어긋나서 생긴 일이라고 봤습니다. 제 컴퓨터에서 공개일이 지난 글이 UTC 기준으로는 아직 미래라면 증상이 그대로 설명되니까요. 같은 소스를 타임존만 바꿔 세 번 빌드했습니다(02:47 UTC).

TZ빌드 결과그 타임존이 적용됐다면
UTC7편, 그 글 포함입니다포함이 맞습니다
Asia/Seoul (UTC+9)7편, 그 글 포함입니다포함이 맞습니다
Pacific/Midway (UTC-11)7편, 그 글 포함입니다빠져야 합니다

셋째 줄이 답을 줬습니다. UTC-11 에서 시각 없는 date: 2026-09-03 은 11:00 UTC 가 되어 측정 시점에는 아직 미래입니다. 빠져야 하는데 빠지지 않았습니다.

Hugo 는 TZ 환경변수를 보지 않습니다. 환경변수는 터미널에서 프로그램에 건네주는 설정값입니다. hugo.toml 에 timeZone 이 없으면 시각 없는 날짜를 UTC 로 고정해서 읽습니다. 제 컴퓨터와 GitHub 쪽이 애초에 어긋나지 않으니, 이번 원인은 타임존이 섞인 것이 아니라 순수하게 cron 이 돌지 않은 것이었습니다.

Hugo 설정 문서는 timeZone 을 “시간대 오프셋이 없는 날짜를 파싱할 때 쓰는 시간대"라고만 설명하고, 이 값을 비워뒀을 때 무엇을 기준으로 삼는지는 적어두지 않았습니다 (Hugo · Configure Hugo, 2026-09-22 확인). 그래서 재봤습니다 — KST 로 맞춰둔 컴퓨터에서 같은 트리를 위 세 타임존으로 다시 빌드하니 편수가 셋 다 같았고, 그때 나온 sitemap.xml 의 lastmod 는 세 경우 모두 +00:00 이었습니다(2026-09-22).

판정을 실제로 움직일 수 있는 값은 터미널에서 주는 TZ 가 아니라 hugo.toml 의 timeZone 인데, 넣어보고 재보지는 않았습니다. timeZone 을 추가할 이유가 없다는 결론은 「양쪽이 어긋나지 않는다」까지만 근거가 있습니다.

이 실측을 워크플로 주석에 남길 때는 처음에 Pacific/Midway 한 건만 적었습니다. 나중에 세 건을 다 적어뒀습니다 — 한 건만 남겨두면 이 결론을 나중에 다시 의심할 때 재현 범위를 알 수가 없습니다.

빌드 제외를 결정하는 것은 publishDate 이고 없으면 date 를 대신 봅니다. 시각이 없으면 양쪽 다 00:00 UTC 로 읽히고 KST 로는 09:00 입니다. 그래서 1차 cron 을 10:23 KST 에 뒀습니다. 자격이 생기고 나서 도는 시각이어야 합니다.

Hugo 문서는 publishDate 를 두고 “게시일. 게시일 전에는 --buildFuture 를 주지 않는 한 렌더되지 않는다"고 적습니다(Hugo · Front matter, 2026-09-22 확인). publishDate 가 없을 때 date 를 대신 본다는 것까지는 문서에 없어서 직접 시험했습니다 — date 만 미래로 둔 글을 넣고 빌드하니 결과물에서 빠졌습니다(2026-09-22).

정리

공구 창고 타공판에 손잡이 크랭크와 예비 종을 걸어두는 사람과 덮인 일지