Skip to content

fly-deploy: pass machine IDs to flyctl restart #10

fly-deploy: pass machine IDs to flyctl restart

fly-deploy: pass machine IDs to flyctl restart #10

Workflow file for this run

# Fly.io deploy pipeline — parallel to the Cloud Run pipeline in build.yml.
#
# Two decoupled paths within one job:
# - Image build + flyctl deploy (code change path; fast)
# - .db build + SFTP upload + swap (data change path)
#
# Both run on every push to the fly-volumes branch, on schedule, and on
# manual dispatch. Once we're confident, we'll merge to main and retire
# build.yml.
name: Fly Deploy
on:
push:
branches:
- fly-volumes
schedule:
- cron: "0 7 * * *" # daily, after upstream data sources update
workflow_dispatch:
env:
FLY_APP: warehouse
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \
/opt/hostedtoolcache/CodeQL || true
df -h
- name: Install build dependencies
run: |
pip install -r requirements.txt
pip install labor-union-parser
- name: Build all databases
run: make
- name: Install flyctl
uses: superfly/flyctl-actions/setup-flyctl@master
# CODE path — push the data-less image. Cheap, fast.
- name: Deploy app to Fly (image only, no DBs)
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: |
flyctl deploy --remote-only --app "$FLY_APP" \
--image-label "build-$GITHUB_RUN_NUMBER"
# DATA path — upload .db files to the volume via SFTP, then swap in.
- name: Ensure machine is running before SFTP
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: |
# Fly may auto-stop the machine between deploy and this step.
# Start every machine for the app; idempotent if already running.
flyctl machine list --app "$FLY_APP" --json \
| jq -r '.[].id' \
| xargs -I{} flyctl machine start {} --app "$FLY_APP" || true
# Give the machine + sshd a few seconds to be reachable.
sleep 10
- name: Upload databases to Fly volume
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: |
# `flyctl ssh console -C` does not interpret shell operators — it
# passes the rest of the line as argv to the first binary. Wrap
# multi-step commands in `sh -c`.
flyctl ssh console --app "$FLY_APP" -C \
"sh -c 'rm -rf /data/incoming && mkdir /data/incoming'"
(
for db in *.db; do
echo "put $db /data/incoming/$db"
done
) | flyctl ssh sftp shell --app "$FLY_APP"
- name: Swap databases atomically
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: |
flyctl ssh console --app "$FLY_APP" -C '/app/scripts/swap-data.sh'
- name: Restart datasette
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: |
# `flyctl machine restart` without an ID is interactive — supply
# every machine ID explicitly.
flyctl machine list --app "$FLY_APP" --json \
| jq -r '.[].id' \
| xargs -I{} flyctl machine restart {} --app "$FLY_APP"
- name: Purge Cloudflare cache
env:
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }}
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
run: |
response=$(curl -fsS -X POST \
"https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/purge_cache" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}')
echo "$response"
echo "$response" | grep -q '"success":true' || { echo "Purge failed"; exit 1; }
- name: Warm cache from sitemap
run: |
curl -fsS https://labordata.bunkum.us/sitemap.xml \
| grep -oE '<loc>[^<]+</loc>' \
| sed 's|<loc>\(.*\)</loc>|\1|' \
| xargs -P 2 -n 1 curl -fsS -o /dev/null