> ## Documentation Index
> Fetch the complete documentation index at: https://docs.asccli.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Automation

# Workflow Automation

> Automate complex App Store Connect workflows with multi-step sequences

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

<Warning>
  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.
</Warning>

## Quick Start

### Create a Workflow File

Create `.asc/workflow.json` in your project:

```json theme={null} theme={null}
{
  "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-id $BUILD_ID --group $GROUP_ID"
        }
      ]
    }
  }
}
```

### Run a Workflow

```bash theme={null} theme={null}
asc workflow run beta BUILD_ID:abc123 GROUP_ID:def456
```

### List Available Workflows

```bash theme={null} theme={null}
asc workflow list
```

### Validate Workflow File

```bash theme={null} theme={null}
asc workflow validate
```

## Workflow Structure

### Basic Workflow

```json theme={null} theme={null}
{
  "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

```json theme={null} theme={null}
"before_all": "asc auth status"
```

**after\_all**: Runs after successful workflow

```json theme={null} theme={null}
"after_all": "echo Success! && notify-send 'Workflow Complete'"
```

**error**: Runs when workflow fails

```json theme={null} theme={null}
"error": "echo Failed! && notify-send 'Workflow Failed'"
```

## Step Types

### Command Steps

Execute shell commands:

```json theme={null} theme={null}
{
  "name": "list_builds",
  "run": "asc builds list --app $APP_ID --limit 5"
}
```

### Conditional Steps

Run only if a variable is set:

```json theme={null} theme={null}
{
  "name": "optional_step",
  "if": "BUILD_ID",
  "run": "asc builds info --build-id $BUILD_ID"
}
```

Skipped if `BUILD_ID` is empty or unset.

### Sub-Workflow Steps

Call another workflow:

```json theme={null} theme={null}
{
  "workflow": "preflight",
  "with": {
    "EXTRA_VAR": "value"
  }
}
```

The called workflow receives `EXTRA_VAR` in addition to its own environment.

## Complete Workflow Examples

### Beta Distribution Workflow

```json theme={null} theme={null}
{
  "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 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-id $BUILD_ID --group $GROUP_ID"
        },
        {
          "name": "notify_success",
          "run": "echo Build distributed to $GROUP_NAME"
        }
      ]
    }
  }
}
```

Run with:

```bash theme={null} theme={null}
asc workflow run beta
```

### App Store Submission Workflow

```json theme={null} theme={null}
{
  "env": {
    "APP_ID": "123456789",
    "IPA_PATH": "./build/MyApp.ipa",
    "PLATFORM": "IOS"
  },
  "before_all": "echo Starting submission workflow",
  "after_all": "echo Submission complete!",
  "error": "echo Submission failed - check logs",
  "workflows": {
    "release": {
      "description": "Publish latest build to the App Store",
      "steps": [
        {
          "name": "validate_metadata",
          "run": "asc localizations list --version $VERSION_ID --output json | jq -e '.data | length > 0'"
        },
        {
          "name": "publish_release",
          "if": "IPA_PATH",
          "run": "asc publish appstore --app $APP_ID --ipa $IPA_PATH --version $VERSION --submit --confirm"
        },
        {
          "name": "check_status",
          "run": "asc submit status --version-id $VERSION_ID"
        }
      ]
    }
  }
}
```

Run with:

```bash theme={null} theme={null}
asc workflow run release VERSION:1.0.0 VERSION_ID:abc123
```

### Complete Release Workflow

```json theme={null} theme={null}
{
  "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 versions create --app $APP_ID --version $VERSION --platform $PLATFORM"
        },
        {
          "name": "get_version_id",
          "run": "VERSION_ID=$(asc 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": "Run the App Store release pipeline",
      "steps": [
        {
          "name": "ensure_ipa",
          "run": "test -f \"$IPA_PATH\""
        },
        {
          "name": "publish_appstore",
          "run": "asc publish appstore --app $APP_ID --ipa $IPA_PATH --version $VERSION --submit --confirm"
        }
      ]
    }
  }
}
```

Run with:

```bash theme={null} theme={null}
asc workflow run release VERSION:2.0.0
```

## CI/CD Integration

### GitHub Actions

```yaml theme={null} theme={null}
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/rorkai/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

```yaml theme={null} theme={null}
stages:
  - release

release:production:
  stage: release
  tags:
    - linux
  script:
    - curl -fsSL https://raw.githubusercontent.com/rorkai/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:

```bash theme={null} theme={null}
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):

```json theme={null} theme={null}
{
  "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"}
      ]
    }
  }
}
```

```bash theme={null} theme={null}
# Works
asc workflow run main

# Fails - private workflow
asc workflow run helper
```

List including private workflows:

```bash theme={null} theme={null}
asc workflow list --all
```

### Custom Workflow File

Use a different workflow file:

```bash theme={null} theme={null}
asc workflow run beta --file ./custom-workflows.json
```

### Pretty JSON Output

```bash theme={null} theme={null}
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

<Warning>
  * 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
</Warning>

### Safe Practices

```json theme={null} theme={null}
// 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:

```bash theme={null} theme={null}
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

## Related Topics

* [TestFlight Distribution](/guides/testflight) - Automate beta distribution
* [App Store Submission](/guides/app-store-submission) - Automate submissions
* [Metadata Management](/guides/metadata-management) - Automate metadata updates
* [Screenshots](/guides/screenshots) - Automate screenshot workflows
