> ## Documentation Index
> Fetch the complete documentation index at: https://patch.codemagic.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish your first release

> Publish your first OTA release after server, CLI, app, and SDK setup.

This is the last step in Setup; it confirms your server, CLI, app, and SDK are wired correctly. After this, see Using Patch for ongoing release workflows.

  <TabItem value="react-native" label="React Native">

From your project root, link the project once:

```bash
cmpatch init
```

It finds or asks for the server, signs you in if needed, creates or selects an
app per platform with a default deployment, writes
`codemagic-patch.config.json` so later commands can omit `--server-url` and
`--app`, and then wires the SDK into the app (see
[Native setup](/docs/setup/native-setup)). Every question has a flag for
scripted setups, for example
`cmpatch init --server-url https://updates.example.com --ios-app MyApp-iOS --android-app MyApp-Android --deployment Staging --yes`;
add `--skip-wire` to link without touching the app.

Complete any remaining steps reported by `init`, then rebuild and install the
native app with the SDK before testing an OTA release. Follow
[Native setup](/docs/setup/native-setup) for CocoaPods or Expo prebuild steps;
an OTA update cannot add the SDK to an existing binary.

Inspect the resolved context:

```bash
cmpatch context
cmpatch context --remote   # also shows server-provided SDK download base URL
```

Dry-run, then publish:

```bash
# Preview without uploading
cmpatch release-react --platform ios --deployment Staging --dry-run
cmpatch release-react --platform android --deployment Staging --dry-run

# Publish
cmpatch release-react --platform ios     --deployment Staging --release-notes "Fix onboarding crash" --yes
cmpatch release-react --platform android --deployment Staging --release-notes "Fix onboarding crash" --yes
```

`release-react` analyzes the project, auto-detects the bundler (Metro or Expo), resolves the target binary version, builds the bundle, and uploads it. If auto-detection can't determine a value, pass it explicitly:

```bash
cmpatch release-react \
  --platform ios \
  --deployment Staging \
  --target-binary-version 1.2.3 \
  --bundler metro \
  --entry-file index.js \
  --yes
```

Watch processing complete:

```bash
cmpatch release list --app MyApp-iOS --deployment Staging --format table
cmpatch release inspect --app MyApp-iOS --deployment Staging --label v1 --wait
```

:::tip
`cmpatch bundle --platform ios` builds a `.cmpatch` artifact without uploading, useful for inspecting or publishing later via `cmpatch release create --bundle-path file.cmpatch`. When the app requires code signing, pass `--private-key-path` to `cmpatch bundle` as well.
:::

  </TabItem>
  <TabItem value="capacitor" label="Capacitor">

Before you start, complete [Native setup](/docs/setup/native-setup-capacitor) and install a native build that includes the SDK on a device or simulator.

Build the web app. `cmpatch-capacitor` uploads the build output directory, which is the `webDir` in your Capacitor config (`www/` for Ionic Angular, `dist/` for most Vite projects). The CLI does not run the build, and you do not need to run `npx cap sync` before publishing.

```bash
ionic build   # or ng build, npx vite build, ...
```

Run a dry run, then publish one release per platform:

```bash
# Validate, archive, and hash the bundle without uploading
cmpatch-capacitor release create --bundle-path www \
  --app MyApp-iOS --deployment Staging --target-binary-version 1.2.3 --dry-run

# Publish
cmpatch-capacitor release create --bundle-path www \
  --app MyApp-iOS --deployment Staging --target-binary-version 1.2.3 \
  --release-notes "Fix onboarding crash"
cmpatch-capacitor release create --bundle-path www \
  --app MyApp-Android --deployment Staging --target-binary-version 1.2.3 \
  --release-notes "Fix onboarding crash"
```

- `--bundle-path` is the `webDir` directory or a ZIP of its contents. `index.html` must be at the top level; otherwise the update installs but the app shows a blank screen. `release create` checks this before uploading.
- `--target-binary-version` is required and must exactly match the installed app version (`CFBundleShortVersionString` on iOS, `versionName` on Android). The CLI does not detect it. See [Binary version compatibility](/docs/using-patch/binary-version-capacitor).
- `release create` asks for confirmation in a terminal. Pass `--yes` to skip the prompt.

Wait for the server to finish processing. Devices receive the release only after processing completes:

```bash
cmpatch-capacitor release list --app MyApp-iOS --deployment Staging
cmpatch-capacitor release inspect --app MyApp-iOS --deployment Staging --label v1 --wait
```

  </TabItem>
</SdkTabs>

## Next steps

- [Verify a test release](/docs/using-patch/verify-test-release): confirm the app on device receives your OTA
- [Releasing updates](/docs/using-patch/releasing-updates): Staging → Production workflow
- [Production control](/docs/using-patch/production-control): rollouts, mandatory updates, rollbacks
- [CI integration](/docs/using-patch/ci-integration): automate in pipelines
