Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
eb810d0
chore: restructure codebase into monorepo (#140)
maxwellmattryan Jun 11, 2026
4b0687d
chore: feature flag desktop-only rust code for mobile compilation (#141)
maxwellmattryan Jun 11, 2026
d432a83
chore: mobile CI target for feature flagged compilation (#142)
maxwellmattryan Jun 12, 2026
dd56e5b
chore: mobile database schema and migration strategy (#145)
maxwellmattryan Jun 16, 2026
9b23997
chore: initialize tauri mobile project(s) (#147)
maxwellmattryan Jun 16, 2026
c8f1b72
feat: mobile UI (#149)
maxwellmattryan Jun 24, 2026
7045323
Get ready for testflight
maxwellmattryan Jun 24, 2026
57a5624
chore: release `v0.3.0-staging.1`
maxwellmattryan Jun 24, 2026
926f6d4
chore: release `v0.3.0-staging.2`
maxwellmattryan Jun 24, 2026
8d74119
chore: release `v0.3.0-staging.3`
maxwellmattryan Jun 24, 2026
d6349c9
Cert
maxwellmattryan Jun 24, 2026
8b0927e
chore: release `v0.3.0-staging.4`
maxwellmattryan Jun 24, 2026
548c249
Changes
maxwellmattryan Jun 24, 2026
58b16ff
chore: release `v0.3.0-staging.5`
maxwellmattryan Jun 24, 2026
d43784e
chore: release `v0.3.0-staging.6`
maxwellmattryan Jun 24, 2026
079f5a8
Pin versions
maxwellmattryan Jun 25, 2026
8ecca1a
chore: release `v0.3.0-staging.7`
maxwellmattryan Jun 25, 2026
dc31372
Try again
maxwellmattryan Jun 26, 2026
86b7907
chore: release `v0.3.0-staging.8`
maxwellmattryan Jun 26, 2026
c6d0bfb
Require all platforms to build for successful release
maxwellmattryan Jul 5, 2026
2662d62
Some refreshing UX
maxwellmattryan Jul 6, 2026
dd3a72c
Add onboarding
maxwellmattryan Jul 6, 2026
6fe6035
chore: release `v0.3.0-staging.9`
maxwellmattryan Jul 6, 2026
11d2506
Lots of UI/UX polish
maxwellmattryan Jul 8, 2026
da5fef3
chore: release `v0.3.0-staging.10`
maxwellmattryan Jul 8, 2026
d2c2a8e
Fix versioning stuff
maxwellmattryan Jul 8, 2026
2ccf645
chore: release `v0.3.0-staging.11`
maxwellmattryan Jul 8, 2026
3b7ff08
chore: release `v0.3.0-staging.12`
maxwellmattryan Jul 8, 2026
b30e5ed
Add more observability
maxwellmattryan Jul 9, 2026
631f0bd
chore: release `v0.3.0-staging.13`
maxwellmattryan Jul 9, 2026
b130038
Fix cloud sync
maxwellmattryan Jul 9, 2026
0e0d5f2
chore: release `v0.3.0-staging.14`
maxwellmattryan Jul 9, 2026
d014604
Fix app attest stuff
maxwellmattryan Jul 9, 2026
f93e197
chore: release `v0.3.0-staging.15`
maxwellmattryan Jul 9, 2026
2306e2a
Fix big volume
maxwellmattryan Jul 9, 2026
b82ff18
chore: release `v0.3.0-staging.16`
maxwellmattryan Jul 9, 2026
0cd337d
Restore nav on startup
maxwellmattryan Jul 9, 2026
4f57377
chore: release `v0.3.0-staging.17`
maxwellmattryan Jul 9, 2026
e91b410
Add subtle transition to full screen player
maxwellmattryan Jul 9, 2026
8a71da2
Fix some responsiveness
maxwellmattryan Jul 9, 2026
00af16d
More polish
maxwellmattryan Jul 9, 2026
7d61eb1
chore: release `v0.3.0-staging.18`
maxwellmattryan Jul 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
180 changes: 180 additions & 0 deletions .github/MOBILE_DISTRIBUTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Mobile Distribution

This document covers setting up signed mobile builds for iOS (TestFlight / App Store) and Android (signed APK on GitHub Releases). The CI pipeline in `cd.release.yml` handles building and uploading automatically on `v*` tags — this guide covers the one-time setup and the secrets you need to configure.

## Architecture

Mobile jobs run alongside the desktop build in `cd.release.yml`:

```
validate → build (macOS + Windows) ─┐
→ android (signed APK) ├→ release (draft GitHub Release)
→ ios (TestFlight upload) ─┘
```

Both mobile jobs use `continue-on-error: true` so they can never block a desktop release. If signing secrets are absent, the jobs skip cleanly with a warning.

## Android

### One-time setup

#### 1. Generate a release keystore

```bash
keytool -genkeypair \
-v \
-keystore crate-release.keystore \
-alias crate \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-storepass <STORE_PASSWORD> \
-keypass <KEY_PASSWORD> \
-dname "CN=Crate, OU=bbx-audio, O=bbx-audio, L=, S=, C=US"
```

Keep this keystore safe — losing it means you cannot update the app on any store that uses it for signing.

#### 2. Add GitHub secrets

| Secret | Value |
|--------|-------|
| `ANDROID_KEYSTORE_BASE64` | `base64 -i crate-release.keystore` |
| `ANDROID_KEYSTORE_PASSWORD` | The store password from step 1 |
| `ANDROID_KEY_ALIAS` | `crate` (or whatever alias you used) |
| `ANDROID_KEY_PASSWORD` | The key password from step 1 |

### How it works

The CI job decodes the keystore to `$RUNNER_TEMP`, writes `gen/android/key.properties` (gitignored), and `build.gradle.kts` picks it up via a guarded `signingConfigs.release`. Without `key.properties`, Gradle still configures successfully (unsigned path).

The built APK is renamed to `Crate_<version>_android.apk` with a `.sha256` checksum and attached to the GitHub Release.

### Local signing (optional)

To sign locally, create `src-tauri/gen/android/key.properties`:

```properties
storeFile=/path/to/crate-release.keystore
storePassword=<STORE_PASSWORD>
keyAlias=crate
keyPassword=<KEY_PASSWORD>
```

Then build: `yarn build:android:apk`

## iOS

### One-time setup

#### 1. Apple Developer Program

Ensure your team (`883V548CV2`) has an active Apple Developer Program membership.

#### 2. Create an App Store Distribution certificate

1. Open Keychain Access → Certificate Assistant → Request a Certificate from a Certificate Authority
2. In [Apple Developer → Certificates](https://developer.apple.com/account/resources/certificates), create a new **Apple Distribution** certificate using the CSR
3. Download and install the `.cer` file
4. Export it as `.p12` from Keychain Access (right-click → Export)

#### 3. Register the App ID

In [Apple Developer → Identifiers](https://developer.apple.com/account/resources/identifiers), register:
- Bundle ID: `com.bbx-audio.crate` (production)
- Enable capabilities: none required beyond defaults

#### 4. Create a provisioning profile

In [Apple Developer → Profiles](https://developer.apple.com/account/resources/profiles):
- Type: **App Store Connect**
- App ID: `com.bbx-audio.crate`
- Certificate: the Distribution cert from step 2

Download the `.mobileprovision` file.

#### 5. Create an App Store Connect record

1. Go to [App Store Connect](https://appstoreconnect.apple.com/)
2. Create a new iOS app with bundle ID `com.bbx-audio.crate`
3. Fill in the required metadata (name, description, screenshots, etc.)
4. Enable all territories including EU and Japan

#### 6. Create an App Store Connect API key

1. In App Store Connect → Users and Access → Integrations → App Store Connect API
2. Generate a new key with **App Manager** role
3. Download the `.p8` file (only available once)
4. Note the Key ID and Issuer ID

#### 7. Set up TestFlight

After the first successful upload, the build appears in TestFlight automatically. To share with external testers:
1. Go to App Store Connect → TestFlight → External Testing
2. Create a group and add testers, or generate a **public link** (global — works for EU + Japan)

#### 8. Add GitHub secrets

| Secret | Value |
|--------|-------|
| `IOS_DIST_CERTIFICATE_P12_BASE64` | `base64 -i distribution.p12` |
| `IOS_DIST_CERTIFICATE_PASSWORD` | The .p12 export password |
| `IOS_PROVISIONING_PROFILE_BASE64` | `base64 -i profile.mobileprovision` |
| `IOS_KEYCHAIN_PASSWORD` | Any random password (for the ephemeral CI keychain) |
| `APP_STORE_CONNECT_API_KEY_ID` | Key ID from step 6 |
| `APP_STORE_CONNECT_API_ISSUER_ID` | Issuer ID from step 6 |
| `APP_STORE_CONNECT_API_KEY_P8_BASE64` | `base64 -i AuthKey_XXXX.p8` |

### How it works

The CI job creates an ephemeral keychain, imports the distribution certificate, installs the provisioning profile, builds with `tauri ios build --export-method app-store-connect`, and uploads the IPA via `xcrun altool`. The uploaded build lands in TestFlight automatically.

**Promotion to the public App Store ("Submit for Review") is always a manual action** in App Store Connect.

### Local builds

```bash
yarn build:ios:appstore # App Store Connect (TestFlight / App Store)
yarn build:ios:adhoc # Ad-hoc distribution (direct install)
yarn build:android:apk # Android APK
yarn build:android:aab # Android App Bundle (for Play Store)
```

## Existing secrets (already configured)

These secrets are reused from the desktop release pipeline — no action needed:

| Secret | Purpose |
|--------|---------|
| `GCLOUD_PROJECT_ID` | Cloud sync config (baked into mobile binary) |
| `GCLOUD_WEB_API_KEY` | Cloud sync config |
| `GCLOUD_STORAGE_BUCKET` | Cloud sync config |
| `GCLOUD_OAUTH_CLIENT_ID` | Cloud sync config |
| `GCLOUD_OAUTH_CLIENT_SECRET` | Cloud sync config |
| `GCLOUD_IOS_OAUTH_CLIENT_ID` | iOS OAuth redirect scheme |
| `GCLOUD_ANDROID_OAUTH_CLIENT_ID` | Android OAuth client |
| `APPLE_TEAM_ID` | Apple team identifier |

## Release flow

The mobile release flow is identical to desktop — it's triggered by the same `v*` tag:

1. `./scripts/tag.sh minor` (or `prerelease`, `stage`)
2. Tag push triggers `cd.release.yml`
3. Desktop, Android, and iOS jobs run in parallel
4. Android APK + checksum land on the draft GitHub Release
5. iOS IPA uploads to TestFlight automatically
6. Review and publish the GitHub Release
7. (Optional) Submit the TestFlight build for App Store review in App Store Connect

## App Store compliance note

Crate's preview playback obtains audio streams from third-party services (Bandcamp, SoundCloud, YouTube) by scraping their web pages. Apple Guideline **5.2.3** prohibits in-app playback of third-party content without authorization. The build submitted to the App Store includes this functionality. Be aware of the review risk — an accurate App Store description of in-app third-party streaming is what surfaces the guideline. TestFlight and Android sideload APKs are unaffected by this guideline.

## EU + Japan distribution

- **TestFlight**: global by default — EU and Japan testers use the same public link
- **App Store**: enable all territories in App Store Connect (no code change)
- **Android APK**: sideloading has no regional restrictions
- **AltStore PAL (EU, optional)**: requires accepting Apple's EU alternative distribution terms (Core Technology Fee). Not set up by default.
- **F-Droid**: not possible — Crate's PolyForm Shield license is not FOSS
8 changes: 7 additions & 1 deletion .github/RELEASE_STRATEGY.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,22 @@ The changelog (`CHANGELOG.md`) follows [Keep a Changelog](https://keepachangelog

Prerelease increments (`./scripts/tag.sh prerelease`) skip the changelog entirely — changes continue accumulating under `[Unreleased]` until the next `prepare` or `graduate`.

## Mobile Distribution

iOS (TestFlight / App Store) and Android (signed APK) builds run alongside the desktop build in the same `cd.release.yml` workflow. See [MOBILE_DISTRIBUTION.md](MOBILE_DISTRIBUTION.md) for setup instructions, required GitHub secrets, and the full signing flow.

## Post-Release Checklist

After CI completes:

1. Go to [GitHub Releases](https://github.com/blackboxaudio/crate/releases) and review the draft
2. Verify artifacts are attached (DMG for macOS, MSI/EXE for Windows)
2. Verify artifacts are attached (DMG for macOS, MSI/EXE for Windows, APK + sha256 for Android)
3. Review release notes
4. Publish the release
5. Verify download links work
6. Verify auto-updater picks up the new version (check `latest.json` in GCS)
7. Verify the iOS build appeared in TestFlight (App Store Connect → TestFlight)
8. `sha256sum -c` the Android APK from the release, then sideload to verify

## Utilities

Expand Down
Loading
Loading