Skip to main content

CircleCI Integration

Use the official CircleCI orb to integrate App Store Connect CLI into your pipelines

Official CircleCI Orb

The asc orb provides reusable commands and jobs for CircleCI pipelines:
  • Install command - Set up asc in your jobs
  • Pre-built jobs - Ready-to-use workflows for common tasks
  • Caching support - Automatic binary caching for faster builds
Repository: github.com/rudrankriyam/asc-orb

Quick Start

Basic Configuration

.circleci/config.yml
version: 2.1

orbs:
  asc: rudrankriyam/asc@1.0

workflows:
  deploy:
    jobs:
      - asc/testflight:
          context: app-store-connect
          app-id: "123456789"
          ipa-path: "build/MyApp.ipa"

Orb Commands

Install Command

Installs the asc CLI:
version: 2.1

orbs:
  asc: rudrankriyam/asc@1.0

jobs:
  custom-deploy:
    macos:
      xcode: 15.2.0
    steps:
      - checkout
      
      - asc/install:
          version: latest
      
      - run:
          name: Upload to TestFlight
          command: |
            asc builds upload \
              --app "$APP_ID" \
              --ipa build/MyApp.ipa

workflows:
  deploy:
    jobs:
      - custom-deploy
Parameters:
ParameterDescriptionDefault
versionVersion to installlatest

Run Command

Installs and executes an asc command:
jobs:
  list-apps:
    docker:
      - image: cimg/base:stable
    steps:
      - asc/run:
          command: apps list
Parameters:
ParameterDescriptionRequired
commandCommand to executeYes
versionVersion to installNo

Pre-Built Jobs

TestFlight Upload Job

workflows:
  deploy:
    jobs:
      - build-ipa:
          # Your build job
      
      - asc/testflight:
          requires:
            - build-ipa
          context: app-store-connect
          app-id: "123456789"
          ipa-path: "build/MyApp.ipa"
Parameters:
ParameterDescriptionRequired
app-idApp Store Connect app IDYes
ipa-pathPath to IPA fileYes
versionasc version to useNo

App Store Submit Job

workflows:
  release:
    jobs:
      - asc/submit:
          context: app-store-connect
          app-id: "123456789"
          version: "1.0.0"
          filters:
            tags:
              only: /^v.*/
Parameters:
ParameterDescriptionRequired
app-idApp Store Connect app IDYes
app-versionVersion to submitYes
asc-versionasc CLI version to useNo

Complete Workflows

Full TestFlight Pipeline

.circleci/config.yml
version: 2.1

orbs:
  asc: rudrankriyam/asc@1.0

executors:
  macos-executor:
    macos:
      xcode: 15.2.0
    resource_class: macos.m1.medium.gen1

jobs:
  build:
    executor: macos-executor
    steps:
      - checkout
      
      - restore_cache:
          keys:
            - v1-deps-{{ checksum "Podfile.lock" }}
      
      - run:
          name: Install Dependencies
          command: pod install
      
      - save_cache:
          key: v1-deps-{{ checksum "Podfile.lock" }}
          paths:
            - Pods
      
      - run:
          name: Build Archive
          command: |
            xcodebuild -workspace MyApp.xcworkspace \
              -scheme MyApp \
              -configuration Release \
              -archivePath build/MyApp.xcarchive \
              archive
      
      - run:
          name: Export IPA
          command: |
            xcodebuild -exportArchive \
              -archivePath build/MyApp.xcarchive \
              -exportPath build \
              -exportOptionsPlist ExportOptions.plist
      
      - persist_to_workspace:
          root: build
          paths:
            - MyApp.ipa
  
  deploy:
    executor: macos-executor
    steps:
      - attach_workspace:
          at: build
      
      - asc/install
      
      - run:
          name: Upload to TestFlight
          command: |
            asc builds upload \
              --app "$ASC_APP_ID" \
              --ipa build/MyApp.ipa
      
      - run:
          name: Add to Beta Group
          command: |
            # Wait for processing
            sleep 120
            
            # Get latest build ID
            BUILD_ID=$(asc builds list \
              --app "$ASC_APP_ID" \
              --output json \
              --limit 1 | jq -r '.[0].id')
            
            # Add to external testers
            asc builds add-groups \
              --build-id "$BUILD_ID" \
              --group "External Testers"

workflows:
  build-and-deploy:
    jobs:
      - build:
          filters:
            branches:
              only: main
      
      - deploy:
          requires:
            - build
          context: app-store-connect

App Store Release Pipeline

.circleci/config.yml
version: 2.1

orbs:
  asc: rudrankriyam/asc@1.0

jobs:
  validate:
    docker:
      - image: cimg/base:stable
    steps:
      - asc/install
      
      - run:
          name: Validate Version
          command: |
            asc validate \
              --app "$ASC_APP_ID" \
              --version "$CIRCLE_TAG"
  
  release:
    docker:
      - image: cimg/base:stable
    steps:
      - asc/install
      
      - run:
          name: Run App Store Publish Command
          command: |
            asc publish appstore \
              --app "$ASC_APP_ID" \
              --ipa "./build/MyApp.ipa" \
              --version "$CIRCLE_TAG" \
              --submit \
              --confirm
  
  monitor:
    docker:
      - image: cimg/base:stable
    steps:
      - asc/install
      
      - run:
          name: Check Review Status
          command: |
            VERSION_ID=$(asc versions list \
              --app "$ASC_APP_ID" \
              --version "$CIRCLE_TAG" \
              --output json | jq -r '.data[0].id')

            asc submit status \
              --version-id "$VERSION_ID" \
              --output json > review-status.json
      
      - store_artifacts:
          path: review-status.json

workflows:
  release:
    jobs:
      - validate:
          context: app-store-connect
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/
      
      - approve-submission:
          type: approval
          requires:
            - validate
          filters:
            tags:
              only: /^v.*/
      
      - release:
          context: app-store-connect
          requires:
            - approve-submission
          filters:
            tags:
              only: /^v.*/
      
      - monitor:
          context: app-store-connect
          requires:
            - release
          filters:
            tags:
              only: /^v.*/

Metadata Sync Workflow

.circleci/config.yml
version: 2.1

orbs:
  asc: rudrankriyam/asc@1.0

jobs:
  sync-description:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      
      - asc/install
      
      - run:
          name: Update Description
          command: |
            asc apps info edit \
              --app "$ASC_APP_ID" \
              --locale en-US \
              --description "$(cat metadata/en-US/description.txt)"
  
  sync-screenshots:
    macos:
      xcode: 15.2.0
    steps:
      - checkout
      
      - asc/install
      
      - run:
          name: Upload Screenshots
          command: |
            for screenshot in metadata/en-US/screenshots/iphone67/*.png; do
              asc screenshots upload \
                --version-localization "$VERSION_LOCALIZATION_ID" \
                --device-type "IPHONE_65" \
                --path "$screenshot"
            done

workflows:
  metadata-update:
    jobs:
      - sync-description:
          context: app-store-connect
          filters:
            branches:
              only: main
      
      - sync-screenshots:
          context: app-store-connect
          filters:
            branches:
              only: main

Scheduled Monitoring

.circleci/config.yml
version: 2.1

orbs:
  asc: rudrankriyam/asc@1.0

jobs:
  monitor-crashes:
    docker:
      - image: cimg/base:stable
    steps:
      - asc/install
      
      - run:
          name: Fetch Crashes
          command: |
            asc testflight crashes list \
              --app "$ASC_APP_ID" \
              --sort -createdDate \
              --limit 20 \
              --output json > crashes.json
      
      - run:
          name: Analyze Crash Count
          command: |
            CRASH_COUNT=$(jq 'length' crashes.json)
            echo "Found $CRASH_COUNT recent crashes"
            
            if [ "$CRASH_COUNT" -gt 10 ]; then
              echo "WARNING: High crash count detected!"
            fi
      
      - store_artifacts:
          path: crashes.json
  
  monitor-feedback:
    docker:
      - image: cimg/base:stable
    steps:
      - asc/install
      
      - run:
          name: Fetch Feedback
          command: |
            asc testflight feedback list \
              --app "$ASC_APP_ID" \
              --paginate \
              --output json > feedback.json
      
      - store_artifacts:
          path: feedback.json

workflows:
  daily-monitoring:
    triggers:
      - schedule:
          cron: "0 9 * * *"  # Daily at 9 AM UTC
          filters:
            branches:
              only: main
    jobs:
      - monitor-crashes:
          context: app-store-connect
      
      - monitor-feedback:
          context: app-store-connect

Authentication Setup

CircleCI Contexts

  1. Navigate to Organization Settings > Contexts
  2. Create a new context: app-store-connect
  3. Add environment variables:
VariableDescription
ASC_KEY_IDApp Store Connect Key ID
ASC_ISSUER_IDApp Store Connect Issuer ID
ASC_PRIVATE_KEY_B64Base64-encoded private key
ASC_APP_IDYour app ID (optional)

Base64 Encoding

# Encode private key
base64 -i AuthKey_ABC123.p8 | tr -d '\n'

# Verify
echo "$ENCODED" | base64 -d | head -1
# Should show: -----BEGIN PRIVATE KEY-----

Using Contexts in Workflows

workflows:
  deploy:
    jobs:
      - deploy-job:
          context: app-store-connect  # References the context

Project-Level Variables

Alternatively, set variables at the project level:
  1. Go to Project Settings > Environment Variables
  2. Add the same variables as above

Advanced Patterns

Matrix Builds

version: 2.1

orbs:
  asc: rudrankriyam/asc@1.0

parameters:
  apps:
    type: string
    default: |
      [
        {"name": "MyApp", "id": "123456789", "scheme": "MyApp"},
        {"name": "MyAppPro", "id": "987654321", "scheme": "MyAppPro"}
      ]

jobs:
  deploy-app:
    parameters:
      app-name:
        type: string
      app-id:
        type: string
      scheme:
        type: string
    macos:
      xcode: 15.2.0
    steps:
      - checkout
      
      - run:
          name: Build << parameters.app-name >>
          command: |
            xcodebuild -scheme << parameters.scheme >> \
              -archivePath build/<< parameters.app-name >>.xcarchive \
              archive
            
            xcodebuild -exportArchive \
              -archivePath build/<< parameters.app-name >>.xcarchive \
              -exportPath build \
              -exportOptionsPlist ExportOptions.plist
      
      - asc/install
      
      - run:
          name: Upload << parameters.app-name >>
          command: |
            asc builds upload \
              --app "<< parameters.app-id >>" \
              --ipa build/<< parameters.app-name >>.ipa

workflows:
  deploy-all:
    jobs:
      - deploy-app:
          name: deploy-myapp
          app-name: MyApp
          app-id: "123456789"
          scheme: MyApp
          context: app-store-connect
      
      - deploy-app:
          name: deploy-myapp-pro
          app-name: MyAppPro
          app-id: "987654321"
          scheme: MyAppPro
          context: app-store-connect

Conditional Execution

jobs:
  conditional-deploy:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      
      - asc/install
      
      - when:
          condition:
            equal: [ main, << pipeline.git.branch >> ]
          steps:
            - run:
                name: Deploy to Production
                command: |
                  asc publish appstore --app "$PROD_APP_ID" --ipa "build/MyApp.ipa" --version "$VERSION" --submit --confirm
      
      - when:
          condition:
            equal: [ develop, << pipeline.git.branch >> ]
          steps:
            - run:
                name: Deploy to Staging
                command: |
                  asc builds upload --app "$STAGING_APP_ID" --ipa "build/MyApp.ipa"

Executors

macOS Executor

For build operations:
executors:
  macos-executor:
    macos:
      xcode: 15.2.0
    resource_class: macos.m1.medium.gen1
    environment:
      ASC_DEFAULT_OUTPUT: json
      ASC_TIMEOUT: 2m

Docker Executor

For API-only operations:
executors:
  docker-executor:
    docker:
      - image: cimg/base:stable
    environment:
      ASC_DEFAULT_OUTPUT: json

Caching

Cache dependencies and build artifacts:
jobs:
  build:
    steps:
      - checkout
      
      - restore_cache:
          keys:
            - v1-asc-{{ arch }}-{{ checksum "go.sum" }}
            - v1-asc-{{ arch }}
      
      - asc/install
      
      - save_cache:
          key: v1-asc-{{ arch }}-{{ checksum "go.sum" }}
          paths:
            - ~/.cache/asc

Troubleshooting

  • Verify orb is published: circleci orb list rudrankriyam
  • Check CircleCI version is 2.1+
  • Ensure organization allows third-party orbs
  • Verify context variables are set correctly
  • Check job references the correct context
  • Ensure private key is base64-encoded without newlines
  • Test credentials locally
  • Check available macOS resource classes for your plan
  • Use macos.m1.medium.gen1 for M1 builds
  • Consider using macos.x86.medium.gen2 for Intel
  • Increase job timeout:
    jobs:
      deploy:
        no_output_timeout: 30m
    
  • Set environment variables:
    environment:
      ASC_UPLOAD_TIMEOUT: 15m
    

Best Practices

Use contexts for secrets

Centralize credentials in contexts instead of project variables.

Cache dependencies

Speed up builds with caching:
- restore_cache
- save_cache

Pin orb versions

Use specific versions for stability:
orbs:
  asc: rudrankriyam/asc@1.0.0

Use approval jobs

Require manual approval for production:
- approve:
    type: approval