name: Admin Dev Repo # Builds an unsigned dev IPA of FTStore Admin and publishes it together with a # freshly-generated AltStore source (admin-devrepo.json) to a rolling # "admin-dev" prerelease. # https://github.com/FTStuff/FTStore/releases/download/admin-dev/admin-devrepo.json # # Monorepo: the console's Xcode project lives in admin/, so the build steps run # there. The tag is `admin-dev`, NOT `dev` or `manager-dev` — the app and the # Manager own those, and sharing one would make each build overwrite the # other's assets. # # The repo is private, so this source is only reachable by someone who already # has access to it. That is the whole distribution model for the console: the # operator, and nobody else. on: push: branches: [main] paths: - "admin/**" - ".github/workflows/admin-dev-repo.yml" workflow_dispatch: concurrency: group: admin-dev-repo-${{ github.ref }} cancel-in-progress: true permissions: contents: write jobs: build: # macOS label required: the bare `self-hosted` label also matches the org's # Linux runners, which have no xcodebuild and fail the build outright. runs-on: [self-hosted, macOS] env: DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer steps: - name: Checkout # No submodules and no Swift packages here, so neither a recursive # checkout nor a SwiftPM cache buys anything. uses: actions/checkout@v5 with: fetch-depth: 1 - name: Compute dev version run: | BASE=$(grep '^FTSTORE_PROJECT_VERSION' admin/FTStoreAdmin.xcconfig | cut -d= -f2 | tr -d '[:space:]') echo "VERSION=${BASE}-dev.${{ github.run_number }}" >> "$GITHUB_ENV" echo "DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV" - name: Stamp marketing version # NOT agvtool: it writes CFBundleShortVersionString into Info.plist, but # the target sets MARKETING_VERSION = $(FTSTORE_PROJECT_VERSION) and # GENERATE_INFOPLIST_FILE = YES, so Xcode regenerates that key from the # build setting and the edit is thrown away. The IPA then reports the # base version while the source advertises the dev one, and every AltStore # client sees a permanent "update available". Set the variable the build # actually reads, and fail loudly if it ever stops matching. run: | sed -i '' "s|^FTSTORE_PROJECT_VERSION=.*|FTSTORE_PROJECT_VERSION=${VERSION}|" admin/FTStoreAdmin.xcconfig grep -q "^FTSTORE_PROJECT_VERSION=${VERSION}$" admin/FTStoreAdmin.xcconfig - name: Stamp build number (best effort) # Records the source commit in CFBundleVersion. Best effort because # agvtool trips over GENERATE_INFOPLIST_FILE=YES ("Cannot find …/YES") # while still doing the write. continue-on-error: true working-directory: admin run: agvtool new-version -all "${{ github.sha }}" - name: Build (iphoneos, unsigned) working-directory: admin run: make iphoneos - name: Generate admin-devrepo.json run: | IPA=admin/packages/FTStore-Admin.ipa SIZE=$(stat -f%z "$IPA") URL="https://github.com/${{ github.repository }}/releases/download/admin-dev/FTStore-Admin.ipa" jq \ --arg v "$VERSION" --arg d "$DATE" --argjson s "$SIZE" --arg u "$URL" ' .name = "FTStore Admin (Dev)" | .identifier = "app.ftstore.admin.devrepo" | .apps[0].name = "FTStore Admin (Dev)" | .apps[0].subtitle = "Latest commit build — for testing" | .apps[0].version = $v | .apps[0].versionDate = $d | .apps[0].size = $s | .apps[0].downloadURL = $u | .apps[0].versions = [ { "version": $v, "date": $d, "size": $s, "downloadURL": $u } ] | .news = [] ' admin/app-repo.json > admin-devrepo.json echo "Generated admin-devrepo.json for $VERSION (${SIZE} bytes)" - name: Publish dev prerelease uses: softprops/action-gh-release@v3 with: tag_name: admin-dev name: Admin dev build prerelease: true make_latest: false body: "Rolling development build of FTStore Admin from the latest `main` commit (${{ github.sha }}). FTStore Admin is the operator console for the FTStore Admin server — it ships no credential and is a login screen without a dashboard account. Unsigned — sign it on-device at install (e.g. with your own certificate)." files: | admin/packages/FTStore-Admin.ipa admin-devrepo.json