Skip to content

Update FCS Keys

Update FCS Keys #371

name: Update FCS Keys
on:
schedule:
- cron: "0 19 * * *" # daily at 11:00 PST (19:00 UTC)
workflow_dispatch:
permissions:
contents: write
jobs:
update-all-fcs-keys:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.26"
- name: Build ipsw CLI
run: |
go build -o ipsw ./cmd/ipsw
- name: Get all latest builds
id: get-builds
run: |
set -euo pipefail
echo "Getting all latest build numbers..."
get_latest_build() {
platform="$1"
device="$2"
track="$3"
if [ "$track" = "beta" ]; then
build=$(./ipsw download ota --platform "$platform" --beta --show-latest-build --device "$device" --insecure)
else
build=$(./ipsw download ota --platform "$platform" --show-latest-build --device "$device" --insecure)
fi
build=$(printf '%s' "$build" | tr -d '\r\n')
if [ -z "$build" ]; then
echo "::error::No build returned for platform=$platform device=$device track=$track" >&2
exit 1
fi
if ! printf '%s' "$build" | grep -Eq '^[0-9A-Za-z]+$'; then
echo "::error::Invalid build identifier '$build' for platform=$platform device=$device track=$track" >&2
exit 1
fi
printf '%s' "$build"
}
# Get stable builds (use same probe devices as Makefile defaults)
STABLE_IOS=$(get_latest_build ios iPhone17,1 stable)
STABLE_MACOS=$(get_latest_build macos Mac14,7 stable)
STABLE_VISIONOS=$(get_latest_build visionos RealityDevice14,1 stable)
# Get beta builds
BETA_IOS=$(get_latest_build ios iPhone17,1 beta)
BETA_MACOS=$(get_latest_build macos Mac14,7 beta)
BETA_VISIONOS=$(get_latest_build visionos RealityDevice14,1 beta)
# RC builds use same endpoint as stable for now
RC_IOS="$STABLE_IOS"
RC_MACOS="$STABLE_MACOS"
RC_VISIONOS="$STABLE_VISIONOS"
# Create fingerprints
STABLE_FP="${STABLE_IOS}_${STABLE_MACOS}_${STABLE_VISIONOS}"
RC_FP="${RC_IOS}_${RC_MACOS}_${RC_VISIONOS}"
BETA_FP="${BETA_IOS}_${BETA_MACOS}_${BETA_VISIONOS}"
# Output all builds
echo "stable_ios=$STABLE_IOS" >> $GITHUB_OUTPUT
echo "stable_macos=$STABLE_MACOS" >> $GITHUB_OUTPUT
echo "stable_visionos=$STABLE_VISIONOS" >> $GITHUB_OUTPUT
echo "stable_fingerprint=$STABLE_FP" >> $GITHUB_OUTPUT
echo "rc_ios=$RC_IOS" >> $GITHUB_OUTPUT
echo "rc_macos=$RC_MACOS" >> $GITHUB_OUTPUT
echo "rc_visionos=$RC_VISIONOS" >> $GITHUB_OUTPUT
echo "rc_fingerprint=$RC_FP" >> $GITHUB_OUTPUT
echo "beta_ios=$BETA_IOS" >> $GITHUB_OUTPUT
echo "beta_macos=$BETA_MACOS" >> $GITHUB_OUTPUT
echo "beta_visionos=$BETA_VISIONOS" >> $GITHUB_OUTPUT
echo "beta_fingerprint=$BETA_FP" >> $GITHUB_OUTPUT
echo "Current fingerprints:"
echo " Stable: $STABLE_FP"
echo " RC: $RC_FP"
echo " Beta: $BETA_FP"
- name: Check for new builds
id: check-builds
run: |
set -euo pipefail
[ -n "${{ steps.get-builds.outputs.stable_ios }}" ] || { echo "::error::stable_ios output is empty" >&2; exit 1; }
[ -n "${{ steps.get-builds.outputs.stable_macos }}" ] || { echo "::error::stable_macos output is empty" >&2; exit 1; }
[ -n "${{ steps.get-builds.outputs.stable_visionos }}" ] || { echo "::error::stable_visionos output is empty" >&2; exit 1; }
[ -n "${{ steps.get-builds.outputs.beta_ios }}" ] || { echo "::error::beta_ios output is empty" >&2; exit 1; }
[ -n "${{ steps.get-builds.outputs.beta_macos }}" ] || { echo "::error::beta_macos output is empty" >&2; exit 1; }
[ -n "${{ steps.get-builds.outputs.beta_visionos }}" ] || { echo "::error::beta_visionos output is empty" >&2; exit 1; }
# Get cached fingerprints from the nested FcsKeys structure
LAST_STABLE=$(jq -r '.fcs_keys_stable.fcs_keys.release.fingerprint // ""' hack/.watch_cache)
LAST_RC=$(jq -r '.fcs_keys_rc.fcs_keys.rc.fingerprint // ""' hack/.watch_cache)
LAST_BETA=$(jq -r '.fcs_keys_beta.fcs_keys.beta.fingerprint // ""' hack/.watch_cache)
# Check what needs updating
UPDATE_STABLE=false
UPDATE_RC=false
UPDATE_BETA=false
UPDATE_ANY=false
if [ "$LAST_STABLE" != "${{ steps.get-builds.outputs.stable_fingerprint }}" ]; then
echo "New stable builds found"
UPDATE_STABLE=true
UPDATE_ANY=true
fi
if [ "$LAST_RC" != "${{ steps.get-builds.outputs.rc_fingerprint }}" ]; then
echo "New RC builds found"
UPDATE_RC=true
UPDATE_ANY=true
fi
if [ "$LAST_BETA" != "${{ steps.get-builds.outputs.beta_fingerprint }}" ]; then
echo "New beta builds found"
UPDATE_BETA=true
UPDATE_ANY=true
fi
echo "update_stable=$UPDATE_STABLE" >> $GITHUB_OUTPUT
echo "update_rc=$UPDATE_RC" >> $GITHUB_OUTPUT
echo "update_beta=$UPDATE_BETA" >> $GITHUB_OUTPUT
echo "update_any=$UPDATE_ANY" >> $GITHUB_OUTPUT
- name: Update stable FCS keys
if: steps.check-builds.outputs.update_stable == 'true'
run: |
echo "Running FCS keys update for stable..."
make update_fcs_keys_release \
FCS_IOS_BUILD="${{ steps.get-builds.outputs.stable_ios }}" \
FCS_MOS_BUILD="${{ steps.get-builds.outputs.stable_macos }}" \
FCS_VOS_BUILD="${{ steps.get-builds.outputs.stable_visionos }}"
- name: Update RC FCS keys
if: steps.check-builds.outputs.update_rc == 'true'
run: |
echo "Running FCS keys update for RC..."
make update_fcs_keys_rc
- name: Update beta FCS keys
if: steps.check-builds.outputs.update_beta == 'true'
run: |
echo "Running FCS keys update for beta..."
make update_fcs_keys_beta
- name: Check for changes and commit
if: steps.check-builds.outputs.update_any == 'true'
run: |
set -euo pipefail
# Debug: Show file timestamps and git status
echo "=== File timestamps ==="
ls -la pkg/aea/data/fcs-keys*
echo "=== Git status ==="
git status pkg/aea/data/fcs-keys*
echo "=== Git diff ==="
git diff pkg/aea/data/fcs-keys.json pkg/aea/data/fcs-keys.gz || echo "No diff output"
# Check if any files actually changed
if git diff --quiet pkg/aea/data/fcs-keys.json pkg/aea/data/fcs-keys.gz; then
echo "No changes to FCS keys files despite new builds"
exit 0
fi
# Update the cache file with all new fingerprints
cp hack/.watch_cache hack/.watch_cache.tmp
# Update stable if needed
if [ "${{ steps.check-builds.outputs.update_stable }}" = "true" ]; then
jq -c --sort-keys --arg fp "${{ steps.get-builds.outputs.stable_fingerprint }}" \
--arg ios "${{ steps.get-builds.outputs.stable_ios }}" \
--arg macos "${{ steps.get-builds.outputs.stable_macos }}" \
--arg visionos "${{ steps.get-builds.outputs.stable_visionos }}" \
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'.fcs_keys_stable = {
"fcs_keys": {
"release": {
"fingerprint": $fp,
"ios_build": $ios,
"macos_build": $macos,
"visionos_build": $visionos,
"updated_at": $timestamp
}
}
}' hack/.watch_cache.tmp > hack/.watch_cache.tmp2
mv hack/.watch_cache.tmp2 hack/.watch_cache.tmp
fi
# Update RC if needed
if [ "${{ steps.check-builds.outputs.update_rc }}" = "true" ]; then
jq -c --sort-keys --arg fp "${{ steps.get-builds.outputs.rc_fingerprint }}" \
--arg ios "${{ steps.get-builds.outputs.rc_ios }}" \
--arg macos "${{ steps.get-builds.outputs.rc_macos }}" \
--arg visionos "${{ steps.get-builds.outputs.rc_visionos }}" \
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'.fcs_keys_rc = {
"fcs_keys": {
"rc": {
"fingerprint": $fp,
"ios_build": $ios,
"macos_build": $macos,
"visionos_build": $visionos,
"updated_at": $timestamp
}
}
}' hack/.watch_cache.tmp > hack/.watch_cache.tmp2
mv hack/.watch_cache.tmp2 hack/.watch_cache.tmp
fi
# Update beta if needed
if [ "${{ steps.check-builds.outputs.update_beta }}" = "true" ]; then
jq -c --sort-keys --arg fp "${{ steps.get-builds.outputs.beta_fingerprint }}" \
--arg ios "${{ steps.get-builds.outputs.beta_ios }}" \
--arg macos "${{ steps.get-builds.outputs.beta_macos }}" \
--arg visionos "${{ steps.get-builds.outputs.beta_visionos }}" \
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'.fcs_keys_beta = {
"fcs_keys": {
"beta": {
"fingerprint": $fp,
"ios_build": $ios,
"macos_build": $macos,
"visionos_build": $visionos,
"updated_at": $timestamp
}
}
}' hack/.watch_cache.tmp > hack/.watch_cache.tmp2
mv hack/.watch_cache.tmp2 hack/.watch_cache.tmp
fi
# Final sort of the cache file for idempotency
jq --sort-keys . hack/.watch_cache.tmp > hack/.watch_cache
rm hack/.watch_cache.tmp
# Also sort the main FCS keys JSON file before commit (if it exists)
if [ -f pkg/aea/data/fcs-keys.json ]; then
jq --sort-keys . pkg/aea/data/fcs-keys.json > pkg/aea/data/fcs-keys.json.tmp && mv pkg/aea/data/fcs-keys.json.tmp pkg/aea/data/fcs-keys.json
jq -e 'type == "object" and length > 0' pkg/aea/data/fcs-keys.json >/dev/null
gzip -cn pkg/aea/data/fcs-keys.json > pkg/aea/data/fcs-keys.gz
fi
# Configure git
git config --local user.name "github-actions[bot]"
git config --local user.email "github-actions[bot]@users.noreply.github.com"
# Build commit message
COMMIT_MSG="chore(fcs-keys): update FCS keys"
UPDATES=()
if [ "${{ steps.check-builds.outputs.update_stable }}" = "true" ]; then
UPDATES+=("stable")
fi
if [ "${{ steps.check-builds.outputs.update_rc }}" = "true" ]; then
UPDATES+=("RC")
fi
if [ "${{ steps.check-builds.outputs.update_beta }}" = "true" ]; then
UPDATES+=("beta")
fi
# Join updates with commas
UPDATE_STR=$(IFS=', '; echo "${UPDATES[*]}")
COMMIT_MSG="$COMMIT_MSG ($UPDATE_STR) [skip ci]"
# Commit all changes at once
git add pkg/aea/data/fcs-keys.json pkg/aea/data/fcs-keys.gz hack/.watch_cache
git commit -m "$COMMIT_MSG"
git push