Skip to main content
Define and execute multi-step automation workflows that combine multiple CLI commands and shell scripts.

Overview

Workflows allow you to:
  • Chain multiple asc commands together
  • Run shell scripts and commands
  • Pass data between steps
  • Handle errors with hooks
  • Execute conditional steps
  • Compose reusable sub-workflows
Workflows intentionally execute arbitrary shell commands. Only run workflow files you trust, especially when using --file. Treat .asc/workflow.json like code: review it before running.

Quick Start

Create a Workflow File

Create .asc/workflow.json in your project:
{
  "env": {
    "APP_ID": "123456789",
    "VERSION": "1.0.0"
  },
  "workflows": {
    "beta": {
      "description": "Distribute latest build to beta testers",
      "steps": [
        {
          "name": "get_latest_build",
          "run": "asc builds list --app $APP_ID --sort -uploadedDate --limit 1 --output json"
        },
        {
          "name": "add_to_group",
          "run": "asc builds add-groups --build $BUILD_ID --group $GROUP_ID"
        }
      ]
    }
  }
}

Run a Workflow

asc workflow run beta BUILD_ID:abc123 GROUP_ID:def456

List Available Workflows

asc workflow list

Validate Workflow File

asc workflow validate

Workflow Structure

Basic Workflow

{
  "env": {
    "GLOBAL_VAR": "value"
  },
  "before_all": "echo Starting workflows",
  "after_all": "echo All workflows complete",
  "error": "echo Error occurred",
  "workflows": {
    "workflow_name": {
      "description": "What this workflow does",
      "env": {
        "WORKFLOW_VAR": "value"
      },
      "steps": [
        {
          "name": "step_1",
          "run": "command to execute"
        }
      ]
    }
  }
}

Environment Variables

Global Environment (env at root):
  • Available to all workflows
  • Merged with system environment
Workflow Environment (env in workflow):
  • Scoped to specific workflow
  • Overrides global variables
Runtime Parameters:
  • Passed via command line: KEY:VALUE
  • Override workflow environment
Precedence (highest to lowest):
  1. Runtime parameters
  2. Workflow env
  3. Global env
  4. System environment

Hooks

before_all: Runs before any workflow
"before_all": "asc auth status"
after_all: Runs after successful workflow
"after_all": "echo Success! && notify-send 'Workflow Complete'"
error: Runs when workflow fails
"error": "echo Failed! && notify-send 'Workflow Failed'"

Step Types

Command Steps

Execute shell commands:
{
  "name": "list_builds",
  "run": "asc builds list --app $APP_ID --limit 5"
}

Conditional Steps

Run only if a variable is set:
{
  "name": "optional_step",
  "if": "BUILD_ID",
  "run": "asc builds get --build $BUILD_ID"
}
Skipped if BUILD_ID is empty or unset.

Sub-Workflow Steps

Call another workflow:
{
  "workflow": "preflight",
  "with": {
    "EXTRA_VAR": "value"
  }
}
The called workflow receives EXTRA_VAR in addition to its own environment.

Complete Workflow Examples

Beta Distribution Workflow

{
  "env": {
    "APP_ID": "123456789"
  },
  "workflows": {
    "beta": {
      "description": "Distribute latest build to TestFlight beta group",
      "env": {
        "GROUP_NAME": "External Beta"
      },
      "steps": [
        {
          "name": "validate_auth",
          "run": "asc auth status"
        },
        {
          "name": "get_latest_build",
          "run": "asc builds list --app $APP_ID --sort -uploadedDate --limit 1 --output json > /tmp/latest_build.json"
        },
        {
          "name": "extract_build_id",
          "run": "BUILD_ID=$(jq -r '.data[0].id' /tmp/latest_build.json) && echo BUILD_ID=$BUILD_ID"
        },
        {
          "name": "get_beta_group",
          "run": "asc testflight beta-groups list --app $APP_ID --output json > /tmp/groups.json"
        },
        {
          "name": "extract_group_id",
          "run": "GROUP_ID=$(jq -r '.data[] | select(.attributes.name == \"'$GROUP_NAME'\") | .id' /tmp/groups.json) && echo GROUP_ID=$GROUP_ID"
        },
        {
          "name": "add_build_to_group",
          "if": "BUILD_ID",
          "run": "asc builds add-groups --build $BUILD_ID --group $GROUP_ID"
        },
        {
          "name": "notify_success",
          "run": "echo Build distributed to $GROUP_NAME"
        }
      ]
    }
  }
}
Run with:
asc workflow run beta

App Store Submission Workflow

{
  "env": {
    "APP_ID": "123456789",
    "PLATFORM": "IOS"
  },
  "before_all": "echo Starting submission workflow",
  "after_all": "echo Submission complete!",
  "error": "echo Submission failed - check logs",
  "workflows": {
    "submit": {
      "description": "Submit latest build for App Store review",
      "steps": [
        {
          "name": "validate_metadata",
          "run": "asc localizations list --version $VERSION_ID --output json | jq -e '.data | length > 0'"
        },
        {
          "name": "get_latest_build",
          "run": "asc builds list --app $APP_ID --sort -uploadedDate --limit 1 --output json > /tmp/build.json"
        },
        {
          "name": "extract_build_id",
          "run": "BUILD_ID=$(jq -r '.data[0].id' /tmp/build.json) && echo BUILD_ID=$BUILD_ID >> $GITHUB_ENV"
        },
        {
          "name": "submit_for_review",
          "if": "BUILD_ID",
          "run": "asc submit create --app $APP_ID --version $VERSION --build $BUILD_ID --platform $PLATFORM --confirm"
        },
        {
          "name": "check_status",
          "run": "asc submit status --version-id $VERSION_ID"
        }
      ]
    }
  }
}
Run with:
asc workflow run submit VERSION:1.0.0 VERSION_ID:abc123

Complete Release Workflow

{
  "env": {
    "APP_ID": "123456789",
    "PLATFORM": "IOS"
  },
  "before_all": "git fetch --tags && git status",
  "after_all": "echo Release workflow complete!",
  "error": "echo ERROR: Release failed",
  "workflows": {
    "release": {
      "description": "Complete release: version creation, metadata, screenshots, submission",
      "steps": [
        {
          "workflow": "create_version"
        },
        {
          "workflow": "update_metadata"
        },
        {
          "workflow": "upload_screenshots"
        },
        {
          "workflow": "submit_for_review"
        }
      ]
    },
    "create_version": {
      "private": true,
      "description": "Create App Store version",
      "steps": [
        {
          "name": "create_version",
          "run": "asc app-versions create --app $APP_ID --version $VERSION --platform $PLATFORM"
        },
        {
          "name": "get_version_id",
          "run": "VERSION_ID=$(asc app-versions list --app $APP_ID --output json | jq -r '.data[] | select(.attributes.versionString == \"'$VERSION'\") | .id') && echo VERSION_ID=$VERSION_ID"
        }
      ]
    },
    "update_metadata": {
      "private": true,
      "description": "Update app metadata",
      "steps": [
        {
          "name": "update_en_US",
          "run": "asc localizations update --version $VERSION_ID --locale en-US --description \"$(cat metadata/en-US/description.txt)\" --keywords \"$(cat metadata/en-US/keywords.txt)\" --whats-new \"$(cat metadata/en-US/whats-new.txt)\""
        },
        {
          "name": "update_es_ES",
          "run": "asc localizations update --version $VERSION_ID --locale es-ES --description \"$(cat metadata/es-ES/description.txt)\" --keywords \"$(cat metadata/es-ES/keywords.txt)\" --whats-new \"$(cat metadata/es-ES/whats-new.txt)\""
        }
      ]
    },
    "upload_screenshots": {
      "private": true,
      "description": "Upload screenshots",
      "steps": [
        {
          "name": "get_en_localization",
          "run": "EN_LOC=$(asc localizations list --version $VERSION_ID --output json | jq -r '.data[] | select(.attributes.locale == \"en-US\") | .id') && echo EN_LOC=$EN_LOC"
        },
        {
          "name": "upload_iphone",
          "run": "asc screenshots upload --version-localization $EN_LOC --path screenshots/en-US/iphone --device-type IPHONE_65"
        },
        {
          "name": "upload_ipad",
          "run": "asc screenshots upload --version-localization $EN_LOC --path screenshots/en-US/ipad --device-type IPAD_PRO_3GEN_129"
        }
      ]
    },
    "submit_for_review": {
      "private": true,
      "description": "Submit for App Store review",
      "steps": [
        {
          "name": "get_build",
          "run": "BUILD_ID=$(asc builds list --app $APP_ID --sort -uploadedDate --limit 1 --output json | jq -r '.data[0].id') && echo BUILD_ID=$BUILD_ID"
        },
        {
          "name": "submit",
          "run": "asc submit create --app $APP_ID --version-id $VERSION_ID --build $BUILD_ID --platform $PLATFORM --confirm"
        }
      ]
    }
  }
}
Run with:
asc workflow run release VERSION:2.0.0

CI/CD Integration

GitHub Actions

name: Release Workflow

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version number (e.g., 1.0.0)'
        required: true

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Install asc CLI
        run: |
          curl -fsSL https://raw.githubusercontent.com/rudrankriyam/App-Store-Connect-CLI/main/install.sh | bash
      
      - name: Configure credentials
        env:
          ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          ASC_PRIVATE_KEY: ${{ secrets.ASC_PRIVATE_KEY }}
          ASC_APP_ID: ${{ secrets.ASC_APP_ID }}
        run: |
          echo "Credentials configured"
      
      - name: Run release workflow
        run: |
          asc workflow run release VERSION:${{ github.event.inputs.version }}
      
      - name: Upload workflow output
        uses: actions/upload-artifact@v3
        with:
          name: workflow-output
          path: /tmp/workflow-*.log

GitLab CI

stages:
  - release

release:production:
  stage: release
  tags:
    - linux
  script:
    - curl -fsSL https://raw.githubusercontent.com/rudrankriyam/App-Store-Connect-CLI/main/install.sh | bash
    - asc workflow run release VERSION:$VERSION
  variables:
    VERSION: "1.0.0"
  only:
    - tags
  when: manual

Advanced Features

Dry Run Mode

Preview workflow execution without running commands:
asc workflow run beta --dry-run
Shows:
  • Steps that would be executed
  • Environment variables
  • Command interpolation
  • Workflow order

Private Workflows

Mark workflows as private (only callable from other workflows):
{
  "workflows": {
    "helper": {
      "private": true,
      "description": "Helper workflow - not directly callable",
      "steps": [
        {"name": "help", "run": "echo Helping..."}
      ]
    },
    "main": {
      "description": "Main workflow that calls helper",
      "steps": [
        {"workflow": "helper"}
      ]
    }
  }
}
# Works
asc workflow run main

# Fails - private workflow
asc workflow run helper
List including private workflows:
asc workflow list --all

Custom Workflow File

Use a different workflow file:
asc workflow run beta --file ./custom-workflows.json

Pretty JSON Output

asc workflow run beta --pretty

Best Practices

  1. Use descriptive names: Name workflows and steps clearly
  2. Add descriptions: Document what each workflow does
  3. Validate before running: Always run asc workflow validate after editing
  4. Use environment variables: Parameterize workflows for reusability
  5. Handle errors: Define error hooks to clean up on failure
  6. Test with dry-run: Use --dry-run to preview execution
  7. Keep workflows focused: Break complex workflows into composable sub-workflows
  8. Version control workflows: Store .asc/workflow.json in git
  9. Document required parameters: Note required KEY:VALUE parameters in descriptions
  10. Use private workflows: Extract common logic into private sub-workflows

Security Considerations

  • Only run workflows from trusted sources
  • Review workflow files before execution
  • Avoid hardcoding secrets—use environment variables
  • In CI/CD, use encrypted secrets for credentials
  • Don’t run untrusted workflows on PRs with access to secrets

Safe Practices

// Good - uses environment variable
"run": "asc builds list --app $APP_ID"

// Bad - hardcoded sensitive data
"run": "asc builds list --app 123456789"

// Good - credentials from environment
"env": {
  "ASC_KEY_ID": "$CI_ASC_KEY_ID"
}

// Bad - credentials in workflow file
"env": {
  "ASC_KEY_ID": "ABC123XYZ"
}

Troubleshooting

Validation Errors

Problem: asc workflow validate reports errors. Solution: Check for:
  • Circular workflow dependencies
  • Missing workflow definitions
  • Invalid JSON syntax
  • Unknown step types

”Workflow not found”

Problem: Workflow name doesn’t exist in file. Solution: List available workflows:
asc workflow list

Variable Not Interpolated

Problem: $VARIABLE appears literally in output. Solution: Ensure the variable is:
  • Defined in env (global or workflow)
  • Passed as runtime parameter: VARIABLE:value
  • Available in system environment

Step Fails Silently

Problem: Step executes but produces no output. Solution:
  • Check step output is written to stderr (workflow stdout is JSON-only)
  • Enable verbose mode (if available)
  • Add explicit echo statements

Workflow Hangs

Problem: Workflow stops and doesn’t complete. Solution:
  • Check for interactive commands (not supported)
  • Verify commands don’t wait for user input
  • Add timeout to long-running commands