> ## 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.

# Workflows

# Workflows

> Automate multi-step tasks by composing CLI commands and shell scripts in declarative JSON workflows.

Workflows let you define reusable, multi-step automation sequences in a declarative JSON file. Think of workflows as lightweight CI/CD pipelines for App Store Connect tasks.

## Quick Start

Create a workflow file at `.asc/workflow.json`:

```json theme={null} theme={null}
{
  "workflows": {
    "beta": {
      "description": "Distribute latest build to TestFlight beta group",
      "steps": [
        "asc builds list --app $APP_ID --limit 1 --sort -uploadedDate",
        {
          "name": "add_to_group",
          "run": "asc builds add-groups --build-id $BUILD_ID --group $GROUP_ID"
        }
      ]
    }
  }
}
```

Run it:

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

## Workflow File Structure

Workflows are defined in `.asc/workflow.json` (or a custom path with `--file`).

### Top-Level Schema

```json theme={null} theme={null}
{
  "env": {
    "APP_ID": "123456789",
    "VERSION": "1.0.0"
  },
  "before_all": "asc auth status",
  "after_all": "echo workflow_done",
  "error": "echo workflow_failed",
  "workflows": {
    "workflow_name": {
      "description": "...",
      "steps": [...]
    }
  }
}
```

| Field        | Type   | Description                                                     |
| ------------ | ------ | --------------------------------------------------------------- |
| `env`        | object | Global environment variables available to all workflows         |
| `before_all` | string | Shell command run before any workflow                           |
| `after_all`  | string | Shell command run after workflow completes (success or failure) |
| `error`      | string | Shell command run only on workflow failure                      |
| `workflows`  | object | Map of named workflows                                          |

### Workflow Schema

```json theme={null} theme={null}
{
  "description": "Human-readable description",
  "private": false,
  "env": {
    "BUILD_ID": ""
  },
  "steps": [
    "asc apps list",
    {
      "name": "step_name",
      "run": "asc builds list --app $APP_ID",
      "if": "BUILD_ID",
      "with": {
        "NOTE": "custom value"
      },
      "workflow": "other_workflow"
    }
  ]
}
```

| Field         | Type    | Description                                                   |
| ------------- | ------- | ------------------------------------------------------------- |
| `description` | string  | Optional description (shown in `workflow list`)               |
| `private`     | boolean | Hide from `workflow list` (callable only via other workflows) |
| `env`         | object  | Workflow-specific environment variables                       |
| `steps`       | array   | Ordered list of steps to execute                              |

### Step Schema

Steps can be **short form** (bare string) or **long form** (object):

```json theme={null} theme={null}
// Short form
"asc apps list"

// Long form
{
  "name": "list_apps",
  "run": "asc apps list --output json",
  "if": "VARIABLE_NAME",
  "with": {"KEY": "value"},
  "workflow": "sub_workflow_name"
}
```

| Field      | Type   | Required    | Description                                                      |
| ---------- | ------ | ----------- | ---------------------------------------------------------------- |
| `run`      | string | Conditional | Shell command to execute (mutually exclusive with `workflow`)    |
| `workflow` | string | Conditional | Name of another workflow to call (mutually exclusive with `run`) |
| `name`     | string | No          | Step identifier (for debugging and JSON output)                  |
| `if`       | string | No          | Only run if the environment variable exists and is non-empty     |
| `with`     | object | No          | Additional environment variables for this step only              |

<Warning>
  A step must have **either** `run` or `workflow`, but not both.
</Warning>

## Environment Variables

Workflows support environment variable expansion using `$VAR` or `${VAR}` syntax:

```json theme={null} theme={null}
{
  "env": {
    "APP_ID": "123456789",
    "VERSION": "1.0.0"
  },
  "workflows": {
    "example": {
      "steps": [
        "asc apps view --id $APP_ID",
        "asc versions list --app ${APP_ID} --filter[versionString]=$VERSION"
      ]
    }
  }
}
```

### Variable Precedence

1. **Runtime parameters** (highest priority): `asc workflow run example VAR:value`
2. **Step-level `with`**: `{"with": {"VAR": "value"}}`
3. **Workflow-level `env`**: `{"env": {"VAR": "value"}}`
4. **Global `env`**: Top-level `env` object
5. **Process environment** (lowest priority): Inherited from shell

### Runtime Parameters

Pass parameters when running a workflow:

```bash theme={null} theme={null}
asc workflow run beta \
  BUILD_ID:abc123 \
  GROUP_ID:xyz789 \
  APP_ID:123456789
```

Parameters follow the format `KEY:VALUE`. They override all other variable sources.

## Conditional Steps

Use the `if` field to skip steps when a variable is unset or empty:

```json theme={null} theme={null}
{
  "steps": [
    {
      "name": "upload_build",
      "run": "asc builds upload --path app.ipa"
    },
    {
      "name": "publish_app_store_release",
      "if": "SUBMIT_FOR_REVIEW",
      "run": "asc publish appstore --app $APP_ID --ipa ./build/MyApp.ipa --version $VERSION --submit --confirm"
    }
  ]
}
```

Skip the submission step:

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

Include the submission step:

```bash theme={null} theme={null}
asc workflow run release SUBMIT_FOR_REVIEW:true
```

## Calling Other Workflows

Workflows can call other workflows using the `workflow` field:

```json theme={null} theme={null}
{
  "workflows": {
    "preflight": {
      "private": true,
      "description": "Pre-deployment checks",
      "steps": [
        "asc auth status",
        "asc apps view --id $APP_ID"
      ]
    },
    "deploy": {
      "description": "Deploy to TestFlight",
      "steps": [
        {
          "workflow": "preflight",
          "with": {"NOTE": "Running preflight checks"}
        },
        "asc builds upload --path app.ipa"
      ]
    }
  }
}
```

Run the parent workflow:

```bash theme={null} theme={null}
asc workflow run deploy APP_ID:123456789
```

The `preflight` workflow is called first, with access to the `NOTE` variable.

<Note>
  Private workflows (`"private": true`) are hidden from `asc workflow list` but can be called by other workflows.
</Note>

## Hooks

Workflows support lifecycle hooks at the definition level:

### before\_all

Runs **once** before any workflow:

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

Use for authentication checks, environment validation, or setup tasks.

### after\_all

Runs **once** after the workflow completes (success or failure):

```json theme={null} theme={null}
{
  "after_all": "echo workflow_done",
  "workflows": {...}
}
```

Use for cleanup, notifications, or logging.

### error

Runs **only** on workflow failure:

```json theme={null} theme={null}
{
  "error": "echo workflow_failed && curl -X POST https://hooks.slack.com/...",
  "workflows": {...}
}
```

Use for error notifications or rollback logic.

<Warning>
  Hooks run via `bash` (with `pipefail`) or `sh`. At least one shell must be in `PATH`.
</Warning>

## Output Format

Workflow output is **JSON-only** on stdout:

```bash theme={null} theme={null}
asc workflow run beta BUILD_ID:abc123 GROUP_ID:xyz789 --pretty
```

```json theme={null} theme={null}
{
  "workflow": "beta",
  "steps": [
    {
      "name": "step_1",
      "command": "asc builds list --app $APP_ID --limit 1 --sort -uploadedDate",
      "exit_code": 0
    },
    {
      "name": "add_to_group",
      "command": "asc builds add-groups --build-id abc123 --group xyz789",
      "exit_code": 0
    }
  ],
  "before_all": {
    "command": "asc auth status",
    "exit_code": 0
  },
  "after_all": {
    "command": "echo workflow_done",
    "exit_code": 0
  },
  "success": true
}
```

Step and hook command output streams to **stderr** to keep stdout machine-parseable.

## Commands

### workflow run

Run a named workflow:

```bash theme={null} theme={null}
asc workflow run <name> [KEY:VALUE ...] [flags]
```

Flags:

* `--file` - Path to workflow\.json (default: `.asc/workflow.json`)
* `--dry-run` - Preview steps without executing
* `--pretty` - Pretty-print JSON output

Example:

```bash theme={null} theme={null}
asc workflow run beta \
  BUILD_ID:abc123 \
  GROUP_ID:xyz789 \
  --dry-run \
  --pretty
```

### workflow list

List available workflows:

```bash theme={null} theme={null}
asc workflow list [flags]
```

Flags:

* `--file` - Path to workflow\.json
* `--all` - Include private workflows
* `--pretty` - Pretty-print JSON output

Output:

```json theme={null} theme={null}
[
  {
    "name": "beta",
    "description": "Distribute latest build to TestFlight beta group",
    "private": false,
    "step_count": 2
  },
  {
    "name": "release",
    "description": "Publish a version to the App Store",
    "private": false,
    "step_count": 3
  }
]
```

### workflow validate

Validate workflow file for errors and cycles:

```bash theme={null} theme={null}
asc workflow validate [flags]
```

Flags:

* `--file` - Path to workflow\.json
* `--pretty` - Pretty-print JSON output

Output:

```json theme={null} theme={null}
{
  "valid": true,
  "errors": []
}
```

Or on error:

```json theme={null} theme={null}
{
  "valid": false,
  "errors": [
    {
      "workflow": "beta",
      "message": "step 2: missing 'run' or 'workflow' field"
    },
    {
      "workflow": "release",
      "message": "circular dependency detected: release -> preflight -> release"
    }
  ]
}
```

## Security Considerations

<Warning>
  Workflows intentionally execute arbitrary shell commands. Only run workflow files you trust.
</Warning>

### Best Practices

* **Review workflow files** before running them, especially with `--file`
* **Avoid running workflows from untrusted sources** (e.g., unreviewed PRs)
* **Be careful with secrets** - steps inherit your process environment
* **In CI, use secret masking** - workflows may log environment variables
* **Use `workflow validate`** to check structure, but **validation does not check safety**

### CI/CD Considerations

```yaml theme={null} theme={null}
# GitHub Actions example
- name: Run workflow
  run: asc workflow run beta BUILD_ID:${{ secrets.BUILD_ID }}
  env:
    ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
    ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
    ASC_PRIVATE_KEY: ${{ secrets.ASC_PRIVATE_KEY }}
```

<Note>
  Avoid running workflows on untrusted pull requests with access to secrets or tokens.
</Note>

## Examples

### TestFlight Distribution

```json theme={null} theme={null}
{
  "env": {
    "APP_ID": "123456789"
  },
  "workflows": {
    "beta": {
      "description": "Distribute build to TestFlight beta group",
      "env": {
        "BUILD_ID": "",
        "GROUP_ID": ""
      },
      "steps": [
        {
          "name": "list_builds",
          "run": "asc builds list --app $APP_ID --sort -uploadedDate --limit 5"
        },
        {
          "name": "list_groups",
          "run": "asc testflight groups list --app $APP_ID --limit 20"
        },
        {
          "name": "add_build_to_group",
          "if": "BUILD_ID",
          "run": "asc builds add-groups --build-id $BUILD_ID --group $GROUP_ID"
        }
      ]
    }
  }
}
```

Run:

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

### App Store Submission

```json theme={null} theme={null}
{
  "workflows": {
    "release": {
      "description": "Submit version for App Store review",
      "steps": [
        {
          "workflow": "preflight",
          "with": {"NOTE": "running preflight checks"}
        },
        {
          "name": "publish",
          "run": "asc publish appstore --app $APP_ID --ipa ./build/MyApp.ipa --version $VERSION --submit --confirm"
        }
      ]
    },
    "preflight": {
      "private": true,
      "description": "Pre-submission checks",
      "steps": [
        "asc auth status",
        "asc apps view --id $APP_ID",
        "asc builds info --build-id $BUILD_ID"
      ]
    }
  }
}
```

Run:

```bash theme={null} theme={null}
asc workflow run release \
  APP_ID:123456789 \
  VERSION:1.0.0 \
  BUILD_ID:abc123
```

## Related

* [Error Handling](/concepts/error-handling) - How workflows handle step failures
* [Output Formats](/concepts/output-formats) - Workflow output is JSON-only
* [Authentication](/authentication) - Workflows inherit authentication
