Skip to main content

CodePush updates with Bitrise CI

You can release your CodePush updates via a Bitrise CI Workflow. The process requires:

  • Setting up CodePush credentials: create Secrets and Environment Variables that hold the deployment ID and the deployment key from Bitrise CodePush.
  • Creating a Workflow that creates the update packages for both iOS and Android, and updates both to the Bitrise CodePush Server.

Configuring CodePush credentials on BitriseClick to copy link

  1. Get your credentials: you need the Release Management app IDs and the CodePush deployment IDs and deployment keys.

  2. Create Environment Variables for the deployment IDs and the connected app IDs:

    • IOS_PROD_DEPLOYMENT_ID: The deployment ID for the iOS app.
    • ANDROID_PROD_DEPLOYMENT_ID: The deployment ID for the Android app.
    • IOS_CONNECTED_APP_ID: The app ID for the iOS app.
    • ANDROID_CONNECTED_APP_ID: The app ID for the iOS app.
  3. Create Secrets for the deployment key and the API token:

    • IOS_PROD_DEPLOYMENT_KEY: The deployment key of the iOS deployment from Bitrise CodePush.
    • ANDROID_PROD_DEPLOYMENT_KEY: The deployment key of the Android deployment from Bitrise CodePush.
    • BITRISE_API_TOKEN: Your personal access token or workspace API token.

Creating a Workflow for CodePush updatesClick to copy link

This section shows you how to create a CI Workflow that will:

  • Create an update package for iOS.
  • Upload the iOS update package to Bitrise CodePush Server.
  • Create an update package for Android.
  • Upload the Android update package to Bitrise CodePush Server.
  1. Create a Workflow and after the git-clone Step, add an npm Step with the install command:

    ---
    format_version: '13'
    default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
    project_type: ios
    workflows:
    codepush_update_to_server:
    description: The workflow will generate and push your update to CodePush Server.
    steps:
    - git-clone@8: {}
    - npm@1:
    inputs:
    - command: install
  2. Add a script Step that extracts the app version using a JSON parser and set version in an Environment Variable using envman:

    - script@1:
    title: Extract App Version
    inputs:
    - content: |-
    #!/bin/bash
    set -e

    # Extract version from app.json using jq (JSON parser)
    VERSION=$(jq -r '.version' package.json)

    # Verify version extraction
    if [ -z "$VERSION" ]; then
    echo "Error: Could not extract version from package.json"
    exit 1
    fi

    echo "Extracted version from package.json: $VERSION"

    # Set the environment variable using envman
    envman add --key APP_VERSION --value "$VERSION"

    echo "Successfully set APP_VERSION=$VERSION"
  3. Add another script Step that clones the release-management-recipes repository from GitHub. This contains the upload script we'll use later.

    - script@1:
    title: Get Release Management Recipes
    inputs:
    - content: >-
    #!/usr/bin/env bash
    # fail if any commands fails
    set -e
    # make pipelines' return status equal the last command to exit with a
    non-zero status, or zero if all commands exit successfully
    set -o pipefail
    # debug log
    set -x

    # write your script here
    git clone https://github.com/bitrise-io/release-management-recipes
  4. Generate your iOS update bundle and create a zip archive from it:

    - script@1:
    title: Generate iOS Update Bundle
    inputs:
    - content: >-
    #!/usr/bin/env bash
    # fail if any commands fails
    set -e
    # make pipelines' return status equal the last command to exit with a
    non-zero status, or zero if all commands exit successfully
    set -o pipefail
    # debug log
    set -x

    npx react-native bundle \
    --platform ios \
    --dev false \
    --entry-file index.js \
    --bundle-output ./build/main.jsbundle \
    --assets-dest ./build

    # Create zip archive
    zip -r update.zip ./build
  5. Upload the iOS update bundle to the CodePush Server with upload_code_push_package.sh:

    - script@1:
    title: Upload iOS Update Bundle to Codepush Server
    inputs:
    - content: >
    #!/usr/bin/env bash
    # fail if any commands fails
    set -e
    # make pipelines' return status equal the last command to exit with a non-zero status, or zero if all
    commands exit successfully
    set -o pipefail
    # debug log
    set -x

    cd release-management-recipes

    UPLOAD_RESPONSE=$(PACKAGE_PATH=../update.zip \
    AUTHORIZATION_TOKEN=$BITRISE_API_TOKEN \
    CONNECTED_APP_ID=$IOS_CONNECTED_APP_ID \
    DEPLOYMENT_ID=$IOS_PROD_DEPLOYMENT_ID \
    APP_VERSION=$APP_VERSION /bin/bash ./api/upload_code_push_package.sh 2>&1)
    EXIT_CODE=$?

    if [ $EXIT_CODE -ne 0 ]; then
    \ echo \"❌ upload_code_push_package.sh failed with exit code
    $EXIT_CODE\"
    \ echo \"$UPLOAD_RESPONSE\"
    \ exit $EXIT_CODE
    fi

    # Take only the last line of the response (final/latest status JSON
    object) FINAL_RESPONSE=$(echo "$UPLOAD_RESPONSE" | tail -n1)

    # Check explicitly for ERR_INTERNAL or other internal error indicators
    if echo \"$UPLOAD_RESPONSE\" | grep -q \"ERR_INTERNAL\"; then
    \ ERROR_MESSAGE=$(echo \"$FINAL_RESPONSE\" | jq -r '.message' ||
    echo \"Unknown error\")
    \ echo \"❌ Server returned internal error: $ERROR_MESSAGE\"
    \ exit 1
    fi


    # Now safely parse 'status' and 'status_reason' from the final
    response line PACKAGE_STATUS=$(echo "$FINAL_RESPONSE" | jq -r
    '.status' || echo "null") STATUS_REASON=$(echo "$FINAL_RESPONSE" | jq
    -r '.status_reason' || echo "")

    if [ "$PACKAGE_STATUS" = "processed_valid" ]; then echo "✅ Package
    uploaded and processed successfully." rm -rf ../build.zip rm -rf
    ../build else echo "⚠️ Package upload unexpected status:
    $PACKAGE_STATUS - Reason: $STATUS_REASON" exit 1 fi cd ..
  6. Generate the Android update bundle:

    - script@1:
    title: Generate Android Update Bundle
    inputs:
    - content: |
    #!/usr/bin/env bash
    # fail if any commands fails
    set -e
    # make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
    set -o pipefail
    # debug log
    set -x

    npx react-native bundle \
    --platform android \
    --dev false \
    --entry-file index.js \
    --bundle-output ./build/index.android.bundle \
    --assets-dest ./build

    # Create zip archive
    zip -r update.zip ./build
  7. Upload the Android update bundle to the CodePush Server:

    - script@1:
    title: Upload Android Update Bundle to Codepush Server
    inputs:
    - content: |
    #!/usr/bin/env bash
    # fail if any commands fails
    set -e
    # make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
    set -o pipefail
    # debug log
    set -x

    cd release-management-recipes

    UPLOAD_RESPONSE=$(PACKAGE_PATH=../update.zip \
    AUTHORIZATION_TOKEN=$BITRISE_API_TOKEN \
    CONNECTED_APP_ID=$ANDROID_CONNECTED_APP_ID \
    DEPLOYMENT_ID=$ANDROID_PROD_DEPLOYMENT_ID \
    APP_VERSION=$APP_VERSION /bin/bash ./api/upload_code_push_package.sh 2>&1)
    EXIT_CODE=$?

    if [ $EXIT_CODE -ne 0 ]; then
    echo "❌ upload_code_push_package.sh failed with exit code $EXIT_CODE"
    echo "$UPLOAD_RESPONSE"
    exit $EXIT_CODE
    fi

    # Take only the last line of the response (final/latest status JSON object)
    FINAL_RESPONSE=$(echo "$UPLOAD_RESPONSE" | tail -n1)

    # Check explicitly for ERR_INTERNAL or other internal error indicators
    if echo "$UPLOAD_RESPONSE" | grep -q "ERR_INTERNAL"; then
    ERROR_MESSAGE=$(echo "$FINAL_RESPONSE" | jq -r '.message' || echo "Unknown error")
    echo "❌ Server returned internal error: $ERROR_MESSAGE"
    exit 1
    fi

    # Now safely parse 'status' and 'status_reason' from the final response line
    PACKAGE_STATUS=$(echo "$FINAL_RESPONSE" | jq -r '.status' || echo "null")
    STATUS_REASON=$(echo "$FINAL_RESPONSE" | jq -r '.status_reason' || echo "")

    if [ "$PACKAGE_STATUS" = "processed_valid" ]; then
    echo "✅ Package uploaded and processed successfully."
    else
    echo "⚠️ Package upload unexpected status: $PACKAGE_STATUS - Reason: $STATUS_REASON"
    exit 1
    fi

    cd ..

Automating the CodePush update releaseClick to copy link

After creating a Workflow to generate and upload your CodePush updates, you have two ways of running it:

You can set up several different types of triggers to automate the process. Our example configuration uses a pull request trigger with a label:

Release a CodePush update when a pull request is opened to a specific branch

In this example, we're setting up a trigger where:

  • Bitrise looks for pull requests opened with updates as the target branch.
  • The codepush_update_deploy Workflow is triggered when a pull request to the branch receives the release-update label to the PR.
  1. Create the trigger: set up a pull request trigger with two conditions, target_branch and label:

    • Set target_branch to updates.
    • Set label to release-update.
    GUI config

    You can set these up on the GUI of the Workflow Editor, too. Here we're showing you the YAML configuration.

    format_version: "13"
    default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
    project_type: react-native
    workflows:
    codepush_update_deploy:
    status_report_name: 'Executing <target_id> for <project_title>'
    description: |
    Uploads Update Bundle to Bitrise CodePush Server
    steps:
    [...]
    triggers:
    pull_request:
    - target_branch: updates
    label: release-update
  2. When you want to release CodePush updates, open a pull request to the updates branch.

  3. After the PR has been reviewed and approved, add the release-update label to it.