Skip to content

Scan IPSW

Scan IPSW #232

Workflow file for this run

name: Scan IPSW
on:
workflow_dispatch:
inputs:
ipsw_url:
description: "IPSW download URL"
required: true
type: string
release_date:
description: "Release date (ISO 8601, e.g. 2025-04-02)"
required: true
type: string
beta:
description: "Is this a beta release?"
required: false
type: boolean
default: false
beta_number:
description: "Beta number (e.g. 3)"
required: false
type: string
rc:
description: "Is this a Release Candidate?"
required: false
type: boolean
default: false
rc_number:
description: "RC number (e.g. 2). Omit for just RC"
required: false
type: string
device_specific:
description: "Device-specific build (e.g. M3 launch build)"
required: false
type: boolean
default: false
defaults:
run:
shell: bash -xeuo pipefail {0}
concurrency:
group: scan
cancel-in-progress: false
permissions: {}
jobs:
scan:
name: Scan
runs-on: scanner
environment:
name: scan
deployment: false
permissions:
contents: write # required to push branches
pull-requests: write # required to create PRs
steps:
- name: Add Homebrew to PATH
run: echo "/opt/homebrew/bin" >> "${GITHUB_PATH}"
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
fetch-depth: 0
- name: Clean workspace
run: git clean -fdx data/macos/releases/
- name: Set up commit signing
env:
SSH_SIGNING_KEY: ${{ secrets.SSH_SIGNING_KEY }}
run: |
git config user.name "fuyunohoshi"
git config user.email "165941061+fuyunohoshi@users.noreply.github.com"
eval $(ssh-agent)
echo "SSH_AUTH_SOCK=${SSH_AUTH_SOCK}" >> "${GITHUB_ENV}"
echo "SSH_AGENT_PID=${SSH_AGENT_PID}" >> "${GITHUB_ENV}"
pubkey=$(ssh-keygen -y -f /dev/stdin <<< "${SSH_SIGNING_KEY}")
ssh-add -q - <<< "${SSH_SIGNING_KEY}"
allowed_signer="$(git config get user.email) ${pubkey}"
mkdir -p ~/.ssh
echo "${allowed_signer}" >> ~/.ssh/allowed_signers
git config gpg.format ssh
git config commit.gpgsign true
git config user.signingkey "${pubkey}"
git config gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
- name: Set up authenticated remote
env:
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
REPOSITORY: ${{ github.repository }}
run: git remote set-url origin "https://x-access-token:${BOT_TOKEN}@github.com/${REPOSITORY}.git"
- name: Resolve IPSW path
env:
IPSW_URL: ${{ inputs.ipsw_url }}
run: |
ORIG_NAME=$(basename "${IPSW_URL%%\?*}")
VERSION=$(echo "${ORIG_NAME}" | sed -E 's/UniversalMac_([0-9.]+)_.*/\1/')
BUILD=$(echo "${ORIG_NAME}" | sed -E 's/UniversalMac_[0-9.]+_([^_]+)_.*/\1/')
MAJOR=${VERSION%%.*}
IPSW_DIR="/Volumes/macOS-Archive/macOS/${MAJOR}"
mkdir -p "${IPSW_DIR}"
IPSW_FILE="${IPSW_DIR}/macOS-${VERSION}-${BUILD}.ipsw"
echo "IPSW_FILE=${IPSW_FILE}" >> "${GITHUB_ENV}"
- name: Download IPSW
env:
IPSW_URL: ${{ inputs.ipsw_url }}
run: |
set +x
if [[ -f "${IPSW_FILE}" ]]; then
echo "IPSW already cached at ${IPSW_FILE}, skipping download"
else
TOTAL=$(curl -sSLI --retry 5 --retry-connrefused "${IPSW_URL}" | grep -i content-length | tail -1 | tr -dc '0-9')
TOTAL_MB=$(( TOTAL / 1048576 ))
TOTAL_GB=$(( TOTAL_MB * 10 / 1024 ))
echo "Downloading IPSW ($(( TOTAL_GB / 10 )).$(( TOTAL_GB % 10 )) GB)..."
curl -sSL --retry 10 --retry-delay 10 --retry-connrefused --retry-all-errors -C - -o "${IPSW_FILE}.part" "${IPSW_URL}" &
CURL_PID=$!
while kill -0 "${CURL_PID}" 2>/dev/null; do
if [[ -f "${IPSW_FILE}.part" ]]; then
SIZE=$(stat -f%z "${IPSW_FILE}.part" 2>/dev/null || echo 0)
SIZE_MB=$(( SIZE / 1048576 ))
PCT=$(( SIZE_MB * 100 / TOTAL_MB ))
echo "${PCT}% (${SIZE_MB} MB / ${TOTAL_MB} MB)"
fi
sleep 30
done
wait "${CURL_PID}"
mv "${IPSW_FILE}.part" "${IPSW_FILE}"
fi
- name: Set IPSW modification time to release date
env:
RELEASE_DATE: ${{ inputs.release_date }}
run: |
TOUCH_DATE=$(echo "${RELEASE_DATE}" | tr -d '-')0000
touch -t "${TOUCH_DATE}" "${IPSW_FILE}"
- name: Restore CLI build cache
id: cache-cli
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: .build/release/macosdb
key: macosdb-cli-${{ hashFiles('Sources/**/*.swift', 'Package.swift', 'Package.resolved') }}
- name: Build CLI
if: steps.cache-cli.outputs.cache-hit != 'true'
run: swift build -c release --product macosdb
- name: Save CLI build cache
if: steps.cache-cli.outputs.cache-hit != 'true'
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: .build/release/macosdb
key: macosdb-cli-${{ hashFiles('Sources/**/*.swift', 'Package.swift', 'Package.resolved') }}
- name: Create scan branch from latest main
run: |
git fetch origin main
git checkout -f -B scan-wip origin/main
- name: Scan IPSW
env:
IPSW_URL: ${{ inputs.ipsw_url }}
RELEASE_DATE: ${{ inputs.release_date }}
IS_BETA: ${{ inputs.beta }}
BETA_NUMBER: ${{ inputs.beta_number }}
IS_RC: ${{ inputs.rc }}
RC_NUMBER: ${{ inputs.rc_number }}
IS_DEVICE_SPECIFIC: ${{ inputs.device_specific }}
run: |
SCAN_ARGS=(
"${IPSW_FILE}"
--output data/macos/releases
--release-date "${RELEASE_DATE}"
--update-index
--verbose
--ipsw-url "${IPSW_URL}"
)
if [[ "${IS_BETA}" == "true" ]]; then
SCAN_ARGS+=(--beta)
fi
if [[ -n "${BETA_NUMBER}" ]]; then
SCAN_ARGS+=(--beta-number "${BETA_NUMBER}")
fi
if [[ "${IS_RC}" == "true" ]]; then
SCAN_ARGS+=(--rc)
fi
if [[ -n "${RC_NUMBER}" ]]; then
SCAN_ARGS+=(--rc-number "${RC_NUMBER}")
fi
if [[ "${IS_DEVICE_SPECIFIC}" == "true" ]]; then
SCAN_ARGS+=(--device-specific)
fi
.build/release/macosdb scan "${SCAN_ARGS[@]}"
- name: Commit and push
run: |
NEW_FILE=$(git ls-files --others --exclude-standard 'data/macos/releases/')
BASENAME=$(basename "${NEW_FILE}" .json)
BRANCH="feat/data-${BASENAME}"
git branch -m "${BRANCH}"
git add data/
git commit -m "feat(data): add ${BASENAME}"
git push -u origin "${BRANCH}"
echo "SCAN_BRANCH=${BRANCH}" >> "${GITHUB_ENV}"
echo "SCAN_BASENAME=${BASENAME}" >> "${GITHUB_ENV}"
- name: Create pull request
env:
GH_TOKEN: ${{ secrets.BOT_TOKEN }}
IPSW_URL: ${{ inputs.ipsw_url }}
RELEASE_DATE: ${{ inputs.release_date }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
printf -v BODY '## Summary\n- Scanned from IPSW: `%s`\n- Release date: %s\n\nAuto-generated by the [scan workflow](%s).' \
"${IPSW_URL}" "${RELEASE_DATE}" "${RUN_URL}"
gh pr create \
--title "feat(data): add ${SCAN_BASENAME}" \
--body "${BODY}"
- name: Auto-merge pull request
timeout-minutes: 30
env:
GH_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
gh pr merge "${SCAN_BRANCH}" --squash --auto --delete-branch
echo "Waiting for PR to merge..."
while true; do
STATE=$(gh pr view "${SCAN_BRANCH}" --json state -q .state)
if [[ "${STATE}" == "MERGED" ]]; then
echo "PR merged successfully"
break
elif [[ "${STATE}" == "CLOSED" ]]; then
echo "::error::PR was closed without merging"
exit 1
fi
sleep 30
done