Files
2026-08-12 19:20:57 +09:00

118 lines
4.5 KiB
YAML

name: Mirror store longevity snapshot
on:
workflow_dispatch:
inputs:
force:
description: Re-publish the current source file ID
required: false
type: boolean
default: false
source_file_id:
description: Optional FILE_<digits> override when automatic discovery is blocked
required: false
type: string
schedule:
- cron: "17 1 * * *"
permissions:
contents: read
concurrency:
group: store-longevity-r2-mirror
cancel-in-progress: false
jobs:
mirror:
runs-on: ubuntu-latest
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
R2_BUCKET: ${{ vars.STORE_LONGEVITY_R2_BUCKET }}
PUBLIC_BASE_URL: ${{ vars.STORE_LONGEVITY_R2_PUBLIC_BASE_URL }}
PREFIX: store-longevity-radar
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Validate mirror configuration
run: |
set -euo pipefail
for name in CLOUDFLARE_ACCOUNT_ID CLOUDFLARE_API_TOKEN AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY R2_BUCKET PUBLIC_BASE_URL; do
if [ -z "${!name}" ]; then
echo "::error::$name is not configured"
exit 1
fi
done
- name: Discover and prepare verified snapshot
id: prepare
run: |
set -euo pipefail
force_arg=()
source_id_arg=()
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force }}" = "true" ]; then
force_arg=(--force)
fi
if [ -n "${{ inputs.source_file_id }}" ]; then
source_id_arg=(--source-file-id "${{ inputs.source_file_id }}")
fi
python3 -m scripts.store_longevity_mirror \
--manifest-url "$PUBLIC_BASE_URL/$PREFIX/latest.json" \
--public-base-url "$PUBLIC_BASE_URL" \
--prefix "$PREFIX" \
--output-dir "$RUNNER_TEMP/store-longevity-radar" \
--github-output "$GITHUB_OUTPUT" \
"${source_id_arg[@]}" \
"${force_arg[@]}"
- name: Upload immutable ZIP
if: steps.prepare.outputs.status == 'prepared'
run: |
aws s3 cp \
"${{ steps.prepare.outputs.zip_path }}" \
"s3://$R2_BUCKET/${{ steps.prepare.outputs.object_key }}" \
--endpoint-url "https://$CLOUDFLARE_ACCOUNT_ID.r2.cloudflarestorage.com" \
--content-type application/zip \
--cache-control "public,max-age=31536000,immutable" \
--only-show-errors
- name: Upload immutable manifest
if: steps.prepare.outputs.status == 'prepared'
run: |
npx -y wrangler@4.118.0 r2 object put \
"$R2_BUCKET/${{ steps.prepare.outputs.version_manifest_key }}" \
--file "${{ steps.prepare.outputs.manifest_path }}" \
--content-type application/json \
--cache-control "public,max-age=31536000,immutable" \
--remote
- name: Publish latest manifest
if: steps.prepare.outputs.status == 'prepared'
run: |
npx -y wrangler@4.118.0 r2 object put \
"$R2_BUCKET/${{ steps.prepare.outputs.latest_manifest_key }}" \
--file "${{ steps.prepare.outputs.manifest_path }}" \
--content-type application/json \
--cache-control "public,max-age=300" \
--remote
- name: Verify public mirror
if: steps.prepare.outputs.status == 'prepared'
env:
EXPECTED_FILE_ID: ${{ steps.prepare.outputs.source_file_id }}
EXPECTED_OBJECT_URL: ${{ steps.prepare.outputs.object_url }}
run: |
set -euo pipefail
curl --fail --location --retry 4 --retry-all-errors \
"$PUBLIC_BASE_URL/$PREFIX/latest.json" \
--output "$RUNNER_TEMP/published-latest.json"
python3 -c 'import json, os; from pathlib import Path; manifest = json.loads(Path(os.environ["RUNNER_TEMP"] + "/published-latest.json").read_text()); assert manifest["source_file_id"] == os.environ["EXPECTED_FILE_ID"]; assert manifest["object_url"] == os.environ["EXPECTED_OBJECT_URL"]'
curl --fail --head --location --retry 4 --retry-all-errors "$EXPECTED_OBJECT_URL"