DeployPulse

Automating CodePush Releases with DeployPulse Using GitHub Actions

Managing over-the-air (OTA) updates for React Native apps can be time-consuming, but DeployPulse and GitHub Actions make it seamless. In this guide, we'll walk through a GitHub Action workflow that automates the release process of a CodePush update to DeployPulse.


The easiest way to integrate DeployPulse into your workflow is with the official setup-dpctl GitHub Action. It installs dpctl in a single step — no cloning or building required.

- uses: deploypulseio/setup-dpctl@v1
  with:
    node-version: '22'     # pin Node.js version (optional, default: "22")
    version: 'v1.0.0'      # pin dpctl version (recommended for production)

A complete workflow using the action:

name: Deploy CodePush Update via DeployPulse

on:
  push:
    branches:
      - main
      - dev
    paths:
      - 'app/**'

jobs:
  release:
    environment: ${{github.ref == 'refs/heads/main' && 'production' || 'dev'}}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Install dpctl
        uses: deploypulseio/setup-dpctl@v1

      - name: Authenticate with DeployPulse
        run: dpctl login --key ${{ secrets.DEPLOYPULSE_ACCESS_TOKEN }}

      - name: Read Current Version
        working-directory: ./app
        run: |
          TARGET_VERSION=$(node -p "require('./package.json').version")
          echo "TARGET_VERSION=$TARGET_VERSION" >> $GITHUB_ENV

      - name: Install Dependencies
        working-directory: ./app
        run: npm install

      - name: Release Update via dpctl
        working-directory: ./app
        env:
          DEPLOYMENT: ${{ vars.DEPLOYMENT }}
          APP_NAME: ${{ vars.APP_NAME }}
        run: |
          dpctl release-react \
          "$APP_NAME" ios \
          -d "$DEPLOYMENT" \
          --mandatory \
          --targetBinaryVersion "$TARGET_VERSION" \
          --description "CodePush Release for Version $TARGET_VERSION"

For production environments, pin dpctl to a specific version via the version input to avoid unexpected behavior from new releases.

To release in a disabled state (e.g. to stage a release before enabling it in the dashboard), add --disabled true to the release command.


What This Workflow Does

  1. Runs on push to main or dev branches when changes occur inside the app/ directory.
  2. Installs dpctl via the setup-dpctl action — no manual clone or build needed.
  3. Authenticates with DeployPulse using a secret API key stored in GitHub Secrets.
  4. Reads the current app version from package.json and passes it as targetBinaryVersion.
  5. Deploys a CodePush release targeting either the production or development environment.

Step-by-Step Breakdown

Step 1 — Trigger the Workflow on Code Changes

on:
  push:
    branches:
      - main
      - dev
    paths:
      - 'app/**'

Only runs when changes are pushed to main or dev inside the app/ directory. Update paths if your app code lives elsewhere.


Step 2 — Set the Deployment Environment

environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'dev' }}

Dynamically sets the deployment environment — production on main, dev otherwise.


Step 3 — Checkout the Repository

- name: Checkout repository
  uses: actions/checkout@v6

Step 4 — Install dpctl

- name: Install dpctl
  uses: deploypulseio/setup-dpctl@v1

Installs Node.js and dpctl in a single step.


Step 5 — Authenticate with DeployPulse

- name: Authenticate with DeployPulse
  run: dpctl login --key ${{ secrets.DEPLOYPULSE_ACCESS_TOKEN }}

DEPLOYPULSE_ACCESS_TOKEN should be stored as a GitHub Secret.


Step 6 — Read the Current App Version

- name: Read Current Version
  working-directory: ./app
  run: |
    TARGET_VERSION=$(node -p "require('./package.json').version")
    echo "TARGET_VERSION=$TARGET_VERSION" >> $GITHUB_ENV

Reads the version from package.json and exposes it as TARGET_VERSION for subsequent steps.


Step 7 — Install App Dependencies

- name: Install Dependencies
  working-directory: ./app
  run: npm install

Step 8 — Release the Update

- name: Release Update via dpctl
  working-directory: ./app
  env:
    DEPLOYMENT: ${{ vars.DEPLOYMENT }}
    APP_NAME: ${{ vars.APP_NAME }}
  run: |
    dpctl release-react \
    "$APP_NAME" ios \
    -d "$DEPLOYMENT" \
    --mandatory \
    --targetBinaryVersion "$TARGET_VERSION" \
    --description "CodePush Release for Version $TARGET_VERSION"

APP_NAME and DEPLOYMENT are GitHub Environment Variables set per environment (dev/production).


Why This GitHub Action is Useful

  • Automates OTA updates to DeployPulse with zero manual effort.
  • Ensures updates are version-controlled via package.json.
  • Uses secure authentication with GitHub Secrets.
  • Handles multiple environments (dev and production) dynamically.
  • Only triggers when necessary (app/ directory changes).

This GitHub Action streamlines CodePush updates by integrating directly with DeployPulse. For more details, see the rest of the DeployPulse documentation.

To sign your releases with a private key so devices can verify bundle integrity before applying an update, see the Code Signing guide — it includes a GitHub Actions snippet.


Manual Installation (Alternative)

If you prefer not to use the setup-dpctl action, you can clone and build the CLI directly in your workflow:

- name: Set up Node.js
  uses: actions/setup-node@v6
  with:
    node-version: 23

- name: Clone dpctl CLI
  run: |
    git clone https://github.com/deploypulseio/dpctl dpctl
    cd dpctl
    npm install
    npm run build
    npm install -g

- name: Verify dpctl CLI
  run: dpctl --version

These three steps replace the single setup-dpctl step above. The rest of the workflow (authenticate, read version, install deps, release) remains identical.

Full workflow using manual installation:

name: Deploy CodePush Update via DeployPulse

on:
  push:
    branches:
      - main
      - dev
    paths:
      - 'app/**'

jobs:
  release:
    environment: ${{github.ref == 'refs/heads/main' && 'production' || 'dev'}}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Set up Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 23

      - name: Clone dpctl CLI
        run: |
          git clone https://github.com/deploypulseio/dpctl dpctl
          cd dpctl
          npm install
          npm run build
          npm install -g

      - name: Verify dpctl CLI
        run: dpctl --version

      - name: Authenticate with DeployPulse
        run: |
          dpctl login --key ${{ secrets.DEPLOYPULSE_ACCESS_TOKEN }}

      - name: Read Current Version
        working-directory: ./app
        run: |
          TARGET_VERSION=$(node -p "require('./package.json').version")
          echo "TARGET_VERSION=$TARGET_VERSION" >> $GITHUB_ENV
          echo "Deploying CodePush for version: $TARGET_VERSION"

      - name: Install Dependencies
        working-directory: ./app
        run: npm install

      - name: Release Update via dpctl
        working-directory: ./app
        env:
          DEPLOYMENT: ${{ vars.DEPLOYMENT }}
          APP_NAME: ${{ vars.APP_NAME }}
        run: |
            dpctl release-react \
            "$APP_NAME" ios \
            -d "$DEPLOYMENT" \
            --mandatory \
            --targetBinaryVersion "$TARGET_VERSION" \
            --description "CodePush Release for Version $TARGET_VERSION"