Skip to main content

Official Components

GitLab CI integration is provided through reusable components in the asc-ci-components repository:
  • Install component - Set up asc in your pipeline
  • Run component - Install and execute commands in one step
  • Self-managed support - Works with GitLab.com and self-hosted instances
Repository: github.com/rudrankriyam/asc-ci-components

Quick Start

Using the Run Component

The simplest way to use asc in GitLab CI:
.gitlab-ci.yml
include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: deploy
      job_prefix: release
      asc_version: latest
      command: asc apps list
This creates a job named release:run that:
  1. Installs the latest version of asc
  2. Executes asc apps list
  3. Uses CI/CD variables for authentication

Component Reference

Run Component

Installs asc and runs a command in a single job.
include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: deploy           # Pipeline stage
      job_prefix: myapp       # Job name prefix
      asc_version: latest     # Version to install
      command: asc --version  # Command to run
Inputs:
InputDescriptionRequiredDefault
stagePipeline stage nameYes-
job_prefixPrefix for job nameYes-
asc_versionVersion to installNolatest
commandCommand to executeYes-

Install Component

Only installs asc, allowing you to run multiple commands:
include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/install@main
    inputs:
      stage: prepare
      job_prefix: setup
      asc_version: latest

Complete Pipelines

TestFlight Deployment

.gitlab-ci.yml
stages:
  - build
  - deploy

variables:
  ASC_DEFAULT_OUTPUT: json
  ASC_TIMEOUT: 2m
  ASC_UPLOAD_TIMEOUT: 10m

build:ios:
  stage: build
  tags:
    - macos
  script:
    - xcodebuild -workspace MyApp.xcworkspace \
        -scheme MyApp \
        -configuration Release \
        -archivePath $CI_PROJECT_DIR/build/MyApp.xcarchive \
        archive
    - xcodebuild -exportArchive \
        -archivePath $CI_PROJECT_DIR/build/MyApp.xcarchive \
        -exportPath $CI_PROJECT_DIR/build \
        -exportOptionsPlist ExportOptions.plist
  artifacts:
    paths:
      - build/MyApp.ipa
    expire_in: 1 week

include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: deploy
      job_prefix: testflight
      asc_version: latest
      command: |
        asc builds upload \
          --app "$APP_ID" \
          --file build/MyApp.ipa

testflight:run:
  needs:
    - build:ios
  only:
    - main
    - tags

Multi-Stage Release

.gitlab-ci.yml
stages:
  - validate
  - submit
  - notify

# Validate app version
include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: validate
      job_prefix: check
      asc_version: latest
      command: |
        asc validate \
          --app "$APP_ID" \
          --version "$CI_COMMIT_TAG"

check:run:
  only:
    - tags

# Submit for review
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: submit
      job_prefix: release
      asc_version: latest
      command: |
        asc submit \
          --app "$APP_ID" \
          --version "$CI_COMMIT_TAG"

release:run:
  needs:
    - check:run
  only:
    - tags
  when: manual  # Require manual approval

# Get submission status
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: notify
      job_prefix: status
      asc_version: latest
      command: |
        asc review status \
          --app "$APP_ID" \
          --version "$CI_COMMIT_TAG" \
          --output json > review-status.json

status:run:
  needs:
    - release:run
  artifacts:
    reports:
      dotenv: review-status.json

Metadata Sync Pipeline

.gitlab-ci.yml
stages:
  - sync

variables:
  METADATA_PATH: metadata/

include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: sync
      job_prefix: description
      asc_version: latest
      command: |
        asc app-info update \
          --app "$APP_ID" \
          --locale en-US \
          --description "$(cat $METADATA_PATH/en-US/description.txt)"

description:run:
  only:
    changes:
      - metadata/en-US/description.txt

  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: sync
      job_prefix: screenshots
      asc_version: latest
      command: |
        for screenshot in $METADATA_PATH/en-US/screenshots/iphone67/*.png; do
          asc screenshots upload \
            --app "$APP_ID" \
            --version "$VERSION" \
            --locale en-US \
            --display-type APP_IPHONE_67 \
            --file "$screenshot"
        done

screenshots:run:
  only:
    changes:
      - metadata/en-US/screenshots/**/*

Scheduled Monitoring

.gitlab-ci.yml
stages:
  - monitor

include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: monitor
      job_prefix: crashes
      asc_version: latest
      command: |
        asc crashes \
          --app "$APP_ID" \
          --sort -createdDate \
          --limit 20 \
          --output json > crashes.json

crashes:run:
  only:
    - schedules
  artifacts:
    paths:
      - crashes.json
    expire_in: 30 days

  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: monitor
      job_prefix: feedback
      asc_version: latest
      command: |
        asc feedback \
          --app "$APP_ID" \
          --paginate \
          --output json > feedback.json

feedback:run:
  only:
    - schedules
  artifacts:
    paths:
      - feedback.json
    expire_in: 30 days

Authentication Setup

CI/CD Variables

  1. Navigate to Settings > CI/CD > Variables
  2. Click Add variable
  3. Add the required variables:
VariableValueProtectedMasked
ASC_KEY_IDYour Key IDYesYes
ASC_ISSUER_IDYour Issuer IDYesYes
ASC_PRIVATE_KEY_B64Base64-encoded private keyYesYes
APP_IDYour app ID (optional)NoNo
Enable Protected to restrict access to protected branches/tags. Enable Masked to prevent values from appearing in job logs.

Base64 Encoding

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

# Verify encoding
echo "$ENCODED_KEY" | base64 -d | head -1
# Should output: -----BEGIN PRIVATE KEY-----

Using Private Key File

Alternatively, store the raw private key and write it to a file:
before_script:
  - echo "$ASC_PRIVATE_KEY" > /tmp/AuthKey.p8
  - chmod 600 /tmp/AuthKey.p8
  - export ASC_PRIVATE_KEY_PATH=/tmp/AuthKey.p8

after_script:
  - rm -f /tmp/AuthKey.p8

Self-Managed GitLab

For self-hosted GitLab instances, components work identically:
include:
  - component: gitlab.example.com/your-group/asc-ci-components/run@main
    inputs:
      stage: deploy
      job_prefix: release
      asc_version: latest
      command: asc apps list
Clone the components repository to your GitLab instance and reference it with your instance URL.

Advanced Patterns

Parallel Jobs

stages:
  - deploy

.testflight_template:
  stage: deploy
  script:
    - asc builds upload --app "$APP_ID" --file "build/$APP_NAME.ipa"
  only:
    - main

include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/install@main
    inputs:
      stage: deploy
      job_prefix: setup
      asc_version: latest

deploy:app1:
  extends: .testflight_template
  needs:
    - setup:install
  variables:
    APP_ID: "123456789"
    APP_NAME: "MyApp"

deploy:app2:
  extends: .testflight_template
  needs:
    - setup:install
  variables:
    APP_ID: "987654321"
    APP_NAME: "MyAppPro"

Dynamic Versioning

stages:
  - prepare
  - deploy

version:extract:
  stage: prepare
  script:
    - VERSION=$(grep 'MARKETING_VERSION' MyApp.xcodeproj/project.pbxproj | head -1 | sed 's/.*= \(.*\);/\1/')
    - echo "VERSION=$VERSION" > version.env
  artifacts:
    reports:
      dotenv: version.env

include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: deploy
      job_prefix: release
      asc_version: latest
      command: |
        echo "Submitting version $VERSION"
        asc submit --app "$APP_ID" --version "$VERSION"

release:run:
  needs:
    - version:extract

Retry Logic

include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: deploy
      job_prefix: upload
      asc_version: latest
      command: |
        # Retry up to 3 times on failure
        for i in 1 2 3; do
          echo "Upload attempt $i/3"
          asc builds upload --app "$APP_ID" --file build/MyApp.ipa && break
          if [ $i -lt 3 ]; then
            echo "Retrying in 30 seconds..."
            sleep 30
          fi
        done

upload:run:
  retry:
    max: 2
    when:
      - api_failure
      - stuck_or_timeout_failure

Environment-Specific Deployments

stages:
  - deploy:staging
  - deploy:production

# Staging deployment
include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: deploy:staging
      job_prefix: staging
      asc_version: latest
      command: |
        asc builds upload \
          --app "$STAGING_APP_ID" \
          --file build/MyApp-Staging.ipa

staging:run:
  environment:
    name: staging
  only:
    - develop

# Production deployment
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: deploy:production
      job_prefix: production
      asc_version: latest
      command: |
        asc submit \
          --app "$PRODUCTION_APP_ID" \
          --version "$CI_COMMIT_TAG"

production:run:
  environment:
    name: production
  only:
    - tags
  when: manual

Debugging

Enable Debug Output

variables:
  ASC_DEBUG: api

include:
  - component: gitlab.com/rudrankriyam/asc-ci-components/run@main
    inputs:
      stage: deploy
      job_prefix: debug
      asc_version: latest
      command: asc apps list

Test Authentication

test:auth:
  stage: test
  script:
    - |
      echo "Testing App Store Connect authentication..."
      echo "Key ID: ${ASC_KEY_ID:0:8}..."
      echo "Issuer ID: ${ASC_ISSUER_ID:0:8}..."
      
      # Install asc
      curl -fsSL https://asccli.sh/install | bash
      
      # Test command
      asc apps list --limit 1
  only:
    - branches

Verify Variables

verify:env:
  stage: test
  script:
    - |
      echo "Checking required variables..."
      
      if [ -z "$ASC_KEY_ID" ]; then
        echo "ERROR: ASC_KEY_ID not set"
        exit 1
      fi
      
      if [ -z "$ASC_ISSUER_ID" ]; then
        echo "ERROR: ASC_ISSUER_ID not set"
        exit 1
      fi
      
      if [ -z "$ASC_PRIVATE_KEY_B64" ]; then
        echo "ERROR: ASC_PRIVATE_KEY_B64 not set"
        exit 1
      fi
      
      echo "All required variables are set"
  only:
    - branches

Troubleshooting

  • Verify component URL matches your GitLab instance
  • Check repository exists and is accessible
  • Ensure you’re using the correct branch/tag (@main, @v1, etc.)
  • For self-hosted: confirm components are published to the instance
  • Verify all three variables are set in CI/CD settings
  • Check that variables are not expired or masked incorrectly
  • Ensure private key is properly base64-encoded
  • Test credentials locally with the same key
  • Increase job timeout in .gitlab-ci.yml:
    upload:run:
      timeout: 30m
    
  • Set ASC_UPLOAD_TIMEOUT for long uploads
  • Check artifact paths in previous jobs
  • Verify needs dependencies are correct
  • Ensure artifacts haven’t expired

Best Practices

Use protected variables

Mark sensitive variables as Protected and Masked to prevent exposure in logs.

Pin component versions

Use specific tags instead of @main for stability:
component: .../run@v1.0.0

Set timeouts

Configure appropriate timeouts for upload operations:
variables:
  ASC_UPLOAD_TIMEOUT: 15m

Use manual triggers

Require manual approval for production:
when: manual