# Muse Release Process: Building Android (APK/AAB) and iOS Binaries

> Discover the Muse release process for automated Android APK AAB builds with GitHub Actions and manual iOS IPA generation via Xcode or local scripts. Streamline your releases.

- Repository: [Ko Shin/muse](https://github.com/kkoshin/muse)
- Tags: how-to-guide
- Published: 2026-03-05

---

**The Muse release process triggers automated Android builds via GitHub Actions when you publish a new release, while iOS requires running a local shell script for simulator testing or manual Xcode commands to generate production IPAs.**

The [kkoshin/muse](https://github.com/kkoshin/muse) repository is a Kotlin Multiplatform project that packages a **Compose Multiplatform** app for both Android and iOS distribution. Whether you are shipping a signed APK to Google Play or testing an iOS build locally, the release pipeline relies on distinct workflows for each platform.

## Android Release Workflow (APK and AAB)

The Android release process is fully automated through GitHub Actions and produces signed, production-ready artifacts.

### Automated CI/CD Pipeline

The workflow defined in [`.github/workflows/package.yml`](https://github.com/kkoshin/muse/blob/main/.github/workflows/package.yml) listens for `release` events and executes six distinct stages:

1. **Trigger**: Creating a new GitHub release automatically starts the workflow via the `on: release` event trigger.

2. **Build**: The runner executes `./gradlew assembleRelease` to compile the `composeApp` module. This generates an unsigned APK in release mode. For an Android App Bundle (AAB) instead, the command would be `./gradlew bundleRelease`.

3. **Sign**: The `ilharp/sign-android-release` GitHub Action signs the artifact using keystore credentials stored in repository secrets:
   - `KEY_STORE_BASE64` (Base64-encoded keystore)
   - `KEY_STORE_PASSWORD`
   - `ALIAS`
   - `KEY_PASSWORD`

4. **Rename**: The workflow strips the `-signed` suffix from the filename and moves the signed artifact to a `signed/` subdirectory.

5. **Publish**: The signed APK (or AAB) and the ProGuard mapping file are uploaded as a workflow artifact named `release-artifacts`, available for download from the Actions run page.

6. **Metadata**: The `fastlane/metadata/android` directory contains Play Store screenshots, changelogs, and listing metadata that you manually update before distribution.

The release build configuration itself is defined in `composeApp/build.gradle.kts`, which specifies the signing configuration and release build type parameters.

### Local Android Builds

For local development or manual releases, you can generate unsigned artifacts directly:

```bash

# Build APK

./gradlew assembleRelease

# Build Android App Bundle (AAB)

./gradlew bundleRelease

```

These commands output to `composeApp/build/outputs/apk/release/` and `composeApp/build/outputs/bundle/release/` respectively. You must then sign the artifact using `jarsigner` or Android Studio’s **Generate Signed Bundle/APK** wizard, ensuring you use the same keystore referenced in the CI secrets.

## iOS Release Workflow

Unlike Android, the Muse repository does not include a fully automated CI workflow for iOS distribution. Instead, it provides a convenience script for development builds and requires manual Xcode commands for release archiving.

### Development Builds with run_ios.sh

The [`run_ios.sh`](https://github.com/kkoshin/muse/blob/main/run_ios.sh) script automates building, installing, and launching the app on an iOS simulator:

1. **Simulator Selection**: The script searches for a currently booted simulator; if none exists, it automatically boots an `iPhone 15 Pro`.

2. **Build**: It invokes `xcodebuild` using the `swiftApp/swiftApp.xcworkspace` workspace and `swiftApp` scheme in `Debug` configuration, directing derived data to `build/ios_derived_data`.

3. **Installation**: The script locates the compiled `.app` bundle in `DerivedData/Build/Products/Debug-iphonesimulator`, extracts the bundle identifier from `swiftApp/swiftApp/Info.plist` using `PlistBuddy`, and installs the app via `xcrun simctl`.

4. **Launch**: Finally, it launches the installed app on the selected simulator using `xcrun simctl launch`.

### Production IPA Generation

To create a distributable IPA for TestFlight or the App Store, you must run manual `xcodebuild` commands. The repository does not currently automate this step in CI, though you could adapt the following commands into a GitHub Actions workflow:

```bash

# Step 1: Create archive

xcodebuild -workspace swiftApp/swiftApp.xcworkspace \
           -scheme swiftApp \
           -configuration Release \
           -archivePath build/ios.xcarchive \
           archive

# Step 2: Export IPA (requires ExportOptions.plist)

xcodebuild -exportArchive \
           -archivePath build/ios.xcarchive \
           -exportPath build/ios_ipa \
           -exportOptionsPlist ExportOptions.plist

```

The resulting `.ipa` file in `build/ios_ipa/` can then be uploaded to App Store Connect using the Transporter app or `xcrun altool`.

## Key Configuration Files

Understanding the Muse release process requires familiarity with these specific files:

| File Path | Platform | Purpose |
|-----------|----------|---------|
| [`.github/workflows/package.yml`](https://github.com/kkoshin/muse/blob/main/.github/workflows/package.yml) | Android | CI workflow that triggers on releases, builds, signs, and publishes artifacts |
| `composeApp/build.gradle.kts` | Android | Gradle configuration defining the `release` build type and version codes |
| `fastlane/metadata/android/**` | Android | Store listing assets including screenshots and changelogs |
| [`run_ios.sh`](https://github.com/kkoshin/muse/blob/main/run_ios.sh) | iOS | Local development script for simulator builds and testing |
| `swiftApp/swiftApp.xcworkspace` | iOS | Xcode workspace file used by `xcodebuild` |
| `swiftApp/swiftApp/Info.plist` | iOS | Contains bundle identifier used during simulator installation |

## Summary

- **Android releases** are fully automated through GitHub Actions when you publish a new release, producing signed APKs or AABs using secrets stored in the repository.
- **iOS development builds** rely on the [`run_ios.sh`](https://github.com/kkoshin/muse/blob/main/run_ios.sh) convenience script to compile and run the app on a local simulator.
- **iOS production releases** currently require manual `xcodebuild` archiving and exporting, though the commands can be adapted for CI/CD.
- The `fastlane/metadata/android` directory manages Play Store listing content separately from the binary build process.

## Frequently Asked Questions

### How do I trigger an Android release build in Muse?

Create a new GitHub release (or push a new tag) to automatically trigger the [`package.yml`](https://github.com/kkoshin/muse/blob/main/package.yml) workflow. The workflow listens for `release` events and handles the entire build, sign, and publish cycle without manual intervention.

### What secrets are required for Android signing?

You must configure four GitHub Secrets: `KEY_STORE_BASE64` (your keystore file encoded as Base64), `KEY_STORE_PASSWORD`, `ALIAS` (the keystore alias), and `KEY_PASSWORD`. These are consumed by the `ilharp/sign-android-release` action during the CI workflow.

### Can I build iOS release binaries automatically in CI?

The current repository does not include an automated iOS release workflow. While [`run_ios.sh`](https://github.com/kkoshin/muse/blob/main/run_ios.sh) handles debug simulator builds, you must manually run `xcodebuild archive` and `xcodebuild -exportArchive` commands locally to generate IPAs. You can adapt these commands into a GitHub Actions workflow using macOS runners if needed.

### What is the difference between APK and AAB in Muse builds?

The Muse CI workflow generates an APK by default using `./gradlew assembleRelease`. To produce an Android App Bundle (AAB) required for Google Play Store distribution, you would modify the workflow to run `./gradlew bundleRelease` instead, or execute this command locally. Both artifacts are signed using the same keystore configuration.