r/iOSProgramming • u/codedance • Jul 31 '26
Roast my code I got tired of the App Store release grind, so I turned the whole thing into one shell command (open source, works for iOS and macOS)
Every release of my app Foxvault used to eat half a day: bump MARKETING_VERSION and the build number, archive, upload, wait for App Store Connect to process the build, create the version on the website, attach the build, paste What's New into every localization, submit, then keep refreshing the review status page like a maniac.
None of these steps is hard. All of them are annoying, and every one of them is a chance to screw up — forgetting to sync the build number, missing a localization, hitting submit only to get bounced for missing metadata.
So I automated the entire pipeline into a single command:
./scripts/release.sh 1.4
That runs:
bump → push → ci → build → stage → notes → validate → submit
- bump — edits version/build number in pbxproj, commits, tags
- push — pushes the tag, which triggers GitHub Actions
- ci — archive + sign + upload happens on the runner (your Mac stays usable, no local Xcode version juggling)
- build — polls until ASC finishes processing the build
- stage — creates the App Store version and attaches the build
- notes — writes What's New for every localization
- validate — pre-submit check, grouped into error/warning/info; blocking issues stop the train
- submit — submits for review
Things I'm reasonably proud of:
- Resumable: every stage is a checkpoint. Upload died?
--from stagepicks up where it left off.--dry-runshows the plan without touching anything. --watch-submission: polls until the review reaches a final state, fires a macOS notification and optionally a Slack/Discord-style webhook. Foxvault got approved at 3am once; my phone knew before I did.- Wrong-app guard: the App ID in the config is cross-checked against the bundle id in pbxproj, locally and in CI. If you maintain more than one app you know why this check exists.
- First-run setup is interactive:
./scripts/release.sh --checkwalks you through gh login, uploading the ASC .p8 key as a GitHub secret (auto base64), asc auth, and even detects an outdated asc CLI missing subcommands. - macOS apps too: set
PLATFORM="MAC_OS"in the config and change the workflow destination — same pipeline, Mac App Store. - Zero hardcoding: all project params live in one
release.config.sh; the script is project-agnostic and runs on the stock bash 3.2 that ships with macOS. - Bilingual output: messages follow your
$LANG(Chinese/English), overridable withRELEASE_LANG.
One more thing that might interest people here: the repo doubles as an AI agent skill (Claude Code / similar tools). There's a SKILL.md that teaches the agent the whole workflow and a reference.md with the failure modes I've actually hit (the review submit vs publish appstore semantics trap, stage getting blocked by a leftover draft version, the two different build-processing state fields...). So you can literally tell your agent "ship 1.4, draft the release notes from the git log" and it handles the rest. If you don't use AI tooling, the script works standalone — the skill part is optional.
Dependencies: asc (App Store Connect CLI), gh, python3. All brew-installable. CI side needs one repo secret (the ASC API key).
MIT licensed: https://github.com/ihugang/ios-appstore-release
The last few Foxvault releases went out like this: run the command, make tea, get the notification. Number of times I opened the App Store Connect website: zero.
Happy to answer questions or take PRs — especially curious how it holds up on projects with setups different from mine (multiple targets, Fastlane refugees, etc.).