Pipedash
Resources

iOS release

Maintainer guide to the Pipedash iOS release. Covers files, versions, workflows, secrets, App Review demo access, signing, disabled features, tests and App Store screenshots.

The iOS app is the crates/pipedash-desktop Tauri app built for iOS. Every build generates the Xcode project, and Git ignores crates/pipedash-desktop/gen. Everything iOS needs lives in the ios directory or in crates/pipedash-desktop/Info.ios.plist. The Mobile page describes the app from the user side.

Files

File paths link to the main branch on GitHub.

PathWhat it is
ios/fastlane/FastfileBuild, simulator capture, screenshot export and upload, and release lanes
ios/xcode/PrivacyInfo.xcprivacyPrivacy manifest copied into the app target
ios/xcode/pipedash.entitlementsApp entitlements without APNs access
ios/fastlane/metadata/en-US/release_notes.txtThe "What's New" text that release sends
ios/fastlane/metadata/en-US/description.txtThe app description that release sends, including the foreground-refresh limits
ios/fastlane/metadata/en-US/support_url.txtThe support URL that release sends. It points to the support page, which lists an email contact
ios/fastlane/review_notes.txtApp Review instructions, with the demo token, repository and workflow filled in at release time
ios/fastlane/app_review_demo.rbRead-only GitHub access checks before release signing or submission
ios/fastlane/ios_project.rbApp version, disabled-feature checks and export profile setup
crates/pipedash-desktop/icons/ios/app-icon-1024.pngFull-bleed App Store icon without alpha
ios/app-store-screenshotsScreenshot editor, saved decks, mock captures and export scripts
ios/fastlane/screenshots/en-US/Generated iPhone and iPad images for upload. Git ignores this directory

Versions

  • App version: the release version with the major number raised by 1, because the App Store already shipped 1.0. Release v0.4.0 ships as 1.4.0. mise run bump:* keeps working, and the Fastfile derives the iOS version.
  • Build number: the latest build in App Store Connect plus 1.
  • Minimum iOS: 15.0. The value bundle.iOS.minimumSystemVersion in tauri.ios.conf.json sets it. Tauri maps it to IPHONEOS_DEPLOYMENT_TARGET. The schema of the installed Tauri CLI also defaults to 15.0.

Workflows

Both workflows run on the macos-26 runner.

  • ios-check.yml: runs on pull requests that touch the app and supports manual runs on a feature branch. The first job runs the Ruby helper tests and publishing guards. It builds an unsigned IPA and checks the disabled background capabilities, the Info.plist keys, export compliance, the app icon and the required-reason APIs. The second job builds for the simulator, starts the app on iPhone and iPad and uploads screenshots as the ios-simulator-screenshots artifact.
  • ios.yml: runs the beta lane (TestFlight) on every v* tag. Manual runs also offer release (App Review submission with screenshots) and upload_screenshots (screenshots only, no submission). A manual beta run accepts a beta_ref branch to build. The other lanes reject it. The workflow uses the app-store environment, which only allows main and v* tags. Screenshot runs keep the generated PNGs and the full bundles in the ios-app-store-screenshots artifact for 14 days.

Secrets and variables

These values belong to the app-store environment unless noted.

NameKindUsed by
MATCH_PASSWORD, MATCH_DEPLOY_KEYSecretmatch reads the certificate and profile from pipedash-certificates-v2
ASC_KEY_ID, ASC_ISSUER_ID, ASC_KEY_CONTENTSecretApp Store Connect API key
APPLE_TEAM_IDRepository secretSigning team
APP_REVIEW_DEMO_TOKENSecretrelease only: GitHub token for the demo repository, filled into the review notes
APP_REVIEW_DEMO_REPOSITORYVariablerelease only: owner/repo of the demo repository
PIPEDASH_PUSH_RELAY_URLOptional repository variableCannot activate remote push or Watch in this build

App Review demo access

release checks the configured GitHub demo before Apple authentication, signing or upload. It sends only authenticated GET requests. The repository must be public, active and writable by the user of the token. Public access lets reviewers open its GitHub page without a repository invitation. GitHub can still require a sign-in to read workflow logs in the browser.

The check finds an active workflow_dispatch workflow on the default branch with defaults for required inputs, a successful manual run and available jobs. It reads the first 100 workflows at most, which matches the workflow list of the app. It fills the name of that workflow into the review instructions.

An HTTP failure stops the release with the endpoint and permission guidance. A network failure omits raw exception details to protect credentials. The lane registers the token for secret masking and rejects --verbose, because the HTTP debug output of Fastlane can include request credentials.

Complete this checklist before each submission:

  1. Use a dedicated public demo repository. Keep production code, credentials, organization secrets, deployments and production network access out of its workflows.
  2. Create a fine-grained token with "Only select repositories" set to that repository. Grant Actions read/write and Contents read only. Do not use a production token or an unrestricted classic token. Set an expiry of at least 90 days, so the token stays valid through re-reviews and appeals. release checks the token only when it runs, so an expired token later leaves App Review unable to add the demo provider.
  3. Keep a harmless workflow_dispatch workflow on the default branch with defaults for any required inputs. Give it enough runtime to test "Stop workflow". Keep a successful manual run with jobs.
  4. Test the review instructions on an iPhone or iPad. Cover provider setup, run details, browser logs, "Run workflow" > "Trigger" and "Stop workflow". The provider export must open the native Files picker and allow any destination.
  5. Answer the App Store Connect export compliance questions for the build. The app links rustls and ring for TLS and uses AES-256-GCM with Argon2id for password-encrypted credential storage. ITSAppUsesNonExemptEncryption is false in Info.ios.plist, and verify_ipa demands that value. Keep it only if the export classification shows that this use is exempt. Otherwise set it to true, update verify_ipa and upload any documentation that App Store Connect requests.

GitHub read endpoints cannot prove the repository restriction of the token, the Actions write permission or that the workflow is harmless. The permissions.push field of the repository reflects the permission of the user. It does not reflect the full permissions of the token. The preflight does not dispatch or cancel a workflow to test write access, so it does not replace the checklist.

Run locally

Run the checks on macOS:

cd ios
bundle install
bundle exec fastlane ios check

The certificate and the profile expire on 2027-10-24. Renewing them runs match without readonly from the ios directory with the App Store Connect API key. The history of pipedash-certificates-v2 shows earlier renewals.

Signing prerequisites

The signed lanes use read-only match for com.henrique.pipedash only. The certificate repository must contain its App Store profile and the private key of the certificate. The app does not request the Push Notifications entitlement, and verify_ipa rejects an APNs entitlement in the signed app.

An authorized maintainer renews the app profile from macOS with writable access to the certificate repository:

cd ios
bundle exec fastlane match appstore --app_identifier com.henrique.pipedash --readonly false

The command uses the existing authentication of the certificate repository and MATCH_PASSWORD. The Apple account or API key needs permission to manage identifiers and distribution profiles. The CI deploy key and the read-only match step do not create missing profiles.

Tauri archives the app with manual signing settings. Fastlane exports the archive with the provisioning profile mapping of the app. The build_app action requires export_method: "app-store". The Tauri CLI and the Xcode export_options[:method] value keep app-store-connect. verify_ipa checks the disabled-feature metadata and rejects bundled app extensions as part of its App Store checks.

Disabled build watching

The release build disables Watch, remote push and Live Activities. PipedashBuildWatchEnabled is false in crates/pipedash-desktop/Info.ios.plist. The native plugin rejects watch starts, skips background task registration and reports Watch as unsupported. The frontend hides the Watch and remote push controls. Existing snapshots stay stored and do not restart automatically.

The app declares no build-watch background task identifiers and no processing or remote-notification background modes. It declares only the com.henrique.pipedash.notifications-refresh task with the fetch mode, and verify_ipa requires exactly that. The app bundles no WidgetKit extension and no Live Activity support. verify_ipa rejects these capabilities and any enabled build-watch flag before upload.

A relay URL in the environment cannot activate either feature. Changing the flag alone is not a supported release configuration.

On upgrade, the native plugin dismisses custom activities that older versions left behind. It keeps their attribute type only for this cleanup. The disabled monitoring code uses iOS 26 APIs behind availability checks, so it does not change the iOS 15.0 minimum.

Remote push

Remote push uses the same PipedashBuildWatchEnabled flag. The app skips token synchronization and rejects the pairing, unpairing and test push commands before it reads saved settings or uses the network. The native plugin unregisters from remote notifications at app start and rejects registration without asking for notification permission.

Saved pairings stay on the device. Local notifications work independently and need no APNs entitlement. The push APIs of the server stay separate from this disabled iOS feature. The push relay page covers the server side.

Tests

Run the Ruby tests from the ios directory:

bundle exec ruby -e 'Dir["fastlane/*_test.rb"].sort.each { |file| require_relative file }'

The ios-check.yml workflow runs this suite before the build. The App Review preflight cases run offline, without GitHub or Apple credentials:

bundle exec ruby fastlane/app_review_demo_test.rb

The screenshot tests use temporary files and replace the external Apple actions. They check the export failure gates, locale isolation, screenshot-only delivery and TestFlight isolation. The delivery checks exercise the Fastlane runner without authentication or upload.

App Store screenshots

The screenshot editor lives in ios/app-store-screenshots. It edits the English iPhone and iPad decks without changing the production app. Commit its project JSON and the source PNGs after the compositions get approval. release exports those saved compositions with their device mockups. It does not recapture the app and does not replace approved source images. The screenshot editor maintenance page covers headline alternatives, dependency upkeep and the editor tests.

Editor

The editor needs Node.js ^22.22.3 || >=24.8.0 and Bun. Run it from ios/app-store-screenshots:

bun install --frozen-lockfile
bun dev --hostname 127.0.0.1 --port 3000

Open http://127.0.0.1:3000. Select iPhone or iPad, edit the copy or drag the device, and click "Export bundle". Each deck has 5 framed product captures and a feature-list closer. Changes save to app-store-screenshots.json. Keep that file and public/screenshots/ together.

Design rules

The customized midnight-glow-pro preset appears as "Pipedash Flat" in the editor. It follows the Pipedash design guide: system typography, solid surfaces, quiet separators and blue emphasis. The decks have no decorative beams, halos, textures or glass cards. The first 2 slides share a thin baseline across the connected canvas. Headlines and product content stay inside their own crops.

Palette values come from src/theme/index.ts and the iOS surface roles in src/App.css. The theme entry in ios/app-store-screenshots/src/lib/constants.ts holds the screenshot palette: the iOS cell background, gray foregrounds and blue shade 3 for text emphasis. Shade 7 is the accent on light backgrounds. Captured UI keeps the status tokens of the app.

The template supplies the physical iPhone and iPad bezels. Apple licenses those assets for Apple-platform app mockups. The Android and Mac decks stay empty.

Captures

The images show the real Pipedash frontend with synthetic repositories, runs, provider incidents and notifications. They are Chromium captures with iOS user agents, not native iOS simulator captures. They contain no real credentials or customer data. The capture script allows only a loopback app URL, blocks external HTTP requests and rejects unexpected API calls or mutations.

From the repository root, start the application frontend:

bun install --frozen-lockfile
bun run dev --host 127.0.0.1 --port 5198 --strictPort

In another terminal, from the repository root, run the capture script:

node ios/app-store-screenshots/scripts/capture-app.mjs

The script uses the Playwright dependency of the editor and /usr/bin/chromium. CHROMIUM_PATH selects another Chromium executable. CAPTURE_APP_URL accepts another loopback frontend URL. The script checks the output dimensions, the populated workflow inputs, unexpected requests and page errors.

Captures live in public/screenshots/apple/{iphone|ipad}/en/01.png through 05.png, in this order:

  1. Unified pipelines across GitHub, GitLab, Jenkins and Argo CD.
  2. Workflow execution with release parameters.
  3. Failed workflow and run history.
  4. Provider health and an incident from its status page.
  5. Failure, outage and recovery notifications.

Reload the editor after a recapture, so it reads the updated images. The source sizes are 1320x2868 for iPhone and 2064x2752 for iPad.

Exports and sizes

The exports/ directory contains the generated bundles and thumbnail previews. Git ignores it, because the editor can regenerate it.

DeckSlidesExport sizesPNGs
iPhone61320x2868, 1284x2778, 1206x2622, 1125x243624
iPad62064x2752, 2048x273212

bun run typecheck and bun run build check the editor. bun run test:export-fastlane runs the export edge tests. The export checks cover exact dimensions, opaque RGB pixels and solid corner pixels.

Browser captures do not show the native system bars or the WebKit rendering. Compare them with the shipped app and check the final files in App Store Connect before submitting.

Publishing with Fastlane

From ios, bundle exec fastlane ios prepare_screenshots builds the editor and exports both saved decks without Apple credentials. It installs Playwright Chromium unless CHROMIUM_PATH points to an existing browser. On Linux, CHROMIUM_PATH=/usr/bin/chromium bundle exec fastlane ios prepare_screenshots uses the installed browser.

The script drives the export through the same editor controls. Autosaves stay in browser memory. The validation checks that the canonical JSON stays unchanged and that both decks are complete before it replaces fastlane/screenshots/en-US/. Supported decks use English, portrait orientation and 1 to 10 slides each. Preparation rejects extra directories in the upload folder instead of sending them.

The upload directory holds one size per device, 1320x2868 for iPhone and 2064x2752 for iPad. Uploading both exported iPad sizes would put 12 images into one Fastlane device slot, which is above the Apple limit of 10 images. The complete multi-size bundles stay in app-store-screenshots/exports/.

To update only the screenshots on the existing editable iOS version, run:

bundle exec fastlane ios upload_screenshots

This lane needs ASC_KEY_ID, ASC_ISSUER_ID and ASC_KEY_CONTENT. It needs no signing or match credentials. It skips the binary, the metadata, the version update and the review submission.

release includes the same images in the App Store submission flow, and beta leaves App Store screenshots alone. Both publishing lanes replace all existing screenshot sets for en-US and leave other locales unchanged. A failed export stops either lane before Apple authentication or upload. The existing screenshots lane remains the native simulator first-start check.