Skip to content

fix(ci): 配信先 appdevelopsk.com が main と一致しているか検証する #28

fix(ci): 配信先 appdevelopsk.com が main と一致しているか検証する

fix(ci): 配信先 appdevelopsk.com が main と一致しているか検証する #28

# GitHub Pages の配信元 (pages-blank) を main から自動で組み立てる。
#
# 2026-08-22: 配信元を pages-blank に切り替えた際 privacy-policy/ を持ち込まなかった
# ため、Play Console に申告済みの全 URL が 404 になり、ポリシー違反通知が届いた。
# ファイルは main に残っていたので「存在する=配信されている」と取り違えられた。
# 人手で2ブランチを揃える運用は同じ事故を必ず繰り返すので、機械で揃える。
name: sync pages-blank
on:
push:
branches: [main]
paths: ['privacy-policy/**', 'support/**', '.github/workflows/sync-pages-blank.yml']
schedule:
- cron: '17 3 * * *' # 毎日、配信状態そのものを見張る
workflow_dispatch:
permissions:
contents: write
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { ref: main, fetch-depth: 0 }
- name: pages-blank を組み立て直す
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# 集約先は appdevelopsk.com のままにしたいので、ルートは転送スタブ、
# 法務ページは canonical(.com) + noindex で配信する。
tmp=$(mktemp -d); cp -R privacy-policy support "$tmp/"
git checkout pages-blank
rm -rf privacy-policy support
cp -R "$tmp/privacy-policy" "$tmp/support" .
python3 - <<'PY'
import pathlib
for p in list(pathlib.Path('.').glob('privacy-policy/*/index.html')) + \
list(pathlib.Path('.').glob('support/*/index.html')):
s = p.read_text()
if 'name="robots"' not in s:
p.write_text(s.replace('<head>', '<head>\n<meta name="robots" content="noindex, follow">', 1))
PY
git add privacy-policy support
git diff --cached --quiet || git commit -m "pages: main の privacy/support を配信元へ同期"
git push origin pages-blank
- name: 申告先 URL が実際に 200 か確かめる
run: |
set -euo pipefail
sleep 60 # Pages のビルド待ち
fail=0
for d in privacy-policy/*/ support/*/; do
c=0
# Pages のビルド反映や CDN 側の一時的な 503 で誤検知しないよう、
# 200 が返るか上限に達するまで間隔を空けて再確認する。
for i in 1 2 3 4 5; do
c=$(curl -s -o /dev/null -w '%{http_code}' "https://appdevelopsk.github.io/$d")
[ "$c" = 200 ] && break
sleep 15
done
[ "$c" = 200 ] || { echo "NG $c $d"; fail=$((fail+1)); }
done
echo "404: $fail 件"
[ "$fail" -eq 0 ]
# 2026-09-12: 上の検証は github.io しか見ておらず、**実際の配信先である
# appdevelopsk.com を一度も確認していなかった**。独自ドメインは GitHub Pages では
# なく VPS(nginx, root /var/www/html) が配信しており、そこへの同期機構が無いため、
# 9/9 に直したポリシーが本番では旧版のまま3日間配信され続けた(Play 申告URLは .com 側)。
# ジョブは全て success だったので誰も気づけなかった。
# 鍵を Secrets に置きたくないので同期は手動 rsync のままとし、
# **乖離したらここで落として気づけるようにする**のが役割。
# 復旧手順:
# rsync -av privacy-policy/ pickly-vps:/var/www/html/privacy-policy/ (--delete は付けない)
- name: 配信先 appdevelopsk.com が main と一致しているか確かめる
run: |
set -euo pipefail
UA='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/131.0 Safari/537.36'
miss=0; stale=0
for d in privacy-policy/*/ support/*/; do
url="https://appdevelopsk.com/$d"
# nginx 側に limit_req が入っており、連続アクセスすると弾かれて
# 接続失敗(000)や429になる。2026-09-12 実測で317件の連打中1件が000だったが、
# 単独の再試行では200。CI を落とすのは「本当に落ちている」時だけにしたいので、
# 間隔を空けて3回まで試す。
c=000
for try in 1 2 3; do
c=$(curl -s -A "$UA" -o /tmp/live.html -w '%{http_code}' "$url" || echo 000)
[ "$c" = 200 ] && break
sleep 3
done
if [ "$c" != 200 ]; then echo "NG $c $url"; miss=$((miss+1)); continue; fi
sleep 0.3 # レート制限に当てないための間隔
# 本文(可視テキスト)の一致だけを見る。head 内の meta/link は
# 本番にだけ後から足されたものが在り(canonical・description、2026-09-12 実測9件)、
# 生のHTML比較では全件 STALE になって使い物にならない。
# script/style/head を落とし、タグと空白を潰してから比べる。
if ! python3 - "$url" /tmp/live.html "${d}index.html" <<'PY'; then
import re, sys, html as H
def vis(p):
s = open(p, encoding='utf-8', errors='replace').read()
s = re.sub(r'<head[\s\S]*?</head>|<script[\s\S]*?</script>|<style[\s\S]*?</style>', ' ', s, flags=re.I)
return re.sub(r'\s+', ' ', H.unescape(re.sub(r'<[^>]+>', ' ', s))).strip()
url, live, repo = sys.argv[1], sys.argv[2], sys.argv[3]
if vis(live) != vis(repo):
print(f'STALE {url}')
sys.exit(1)
PY
stale=$((stale+1))
fi
done
echo "到達不能: $miss 件 / 内容が古い: $stale 件"
if [ $((miss+stale)) -ne 0 ]; then
echo "::error::appdevelopsk.com の配信が main と一致していない。VPS へ rsync が必要(詳細はこのステップのコメント)"
exit 1
fi