Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
74d0a06
Add mapbox workflow: install and run multi-step workflows (beta)
zmofei Sep 29, 2026
5bf0965
Mark workflow as in development and drop the stage concept
zmofei Sep 29, 2026
1754eb2
Keep workflows directly under workflow/<name>/
zmofei Sep 29, 2026
3e035c1
Install workflows from mapbox/mapbox-cli by default
zmofei Sep 29, 2026
dff980f
Tidy the workflow command's terminal output
zmofei Sep 29, 2026
1a9dfe5
Let uninstall take the directory install was given
zmofei Sep 29, 2026
6362f50
Render a workflow's description in workflow show
zmofei Sep 29, 2026
1026f33
Describe workflow show's layout in the command docs
zmofei Sep 29, 2026
3de6978
Take a workflow's inputs as flags
zmofei Sep 29, 2026
91b849f
Read the retry hint from the JSON error on every platform
zmofei Sep 29, 2026
2dc0609
Show mapbox workflow in the README
zmofei Sep 30, 2026
e355c11
Let a script step run under --dry-run
zmofei Sep 30, 2026
80fb712
Copy a style's fonts and icons in copy-style
zmofei Sep 30, 2026
2337858
Name each profile's account in copy-style's commands
zmofei Sep 30, 2026
037cac2
Create copy-style's copy without the source account's sprite
zmofei Sep 30, 2026
ec8f1a7
Show each workflow step with a spinner, its details and how it ended
zmofei Sep 30, 2026
87d6c5d
Add warnings, quiet details and a result template to workflows
zmofei Sep 30, 2026
add4c5f
Show copy-style's reads as mapbox command steps
zmofei Sep 30, 2026
b3c43b6
Drop the claim that mapbox/mapbox-cli is private
zmofei Sep 30, 2026
2cc6d64
Keep the save test's path out of a script-written JSON string
zmofei Sep 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,34 @@ that may never merge. They are not releases and are not listed here.

## Unreleased

- `mapbox workflow`, beta and in development, not recommended for use:
install and run workflows, which are named, multi-step recipes of
`mapbox` commands and scripts defined in a `workflow.yaml`. `install` copies one from a local directory or from a
GitHub repository's `workflow/<name>/` (by default `mapbox/mapbox-cli`;
`GH_TOKEN`/`GITHUB_TOKEN` for a private one) into `~/.mapbox/workflows/`,
and `list`, `show`, `run` and `uninstall` work on what is installed.
`run` takes each of the workflow's inputs as a flag, as in
`mapbox workflow run copy-style --style-id <id> --from-profile source
--to-profile target`. `run --dry-run` runs only the steps a workflow
marks `dry_run`, which write nothing, so the plan can show real data. At a
terminal each step shows a spinner, the details its script reports on
stderr (a `::progress ` line updates the spinner's text, a `::warn ` line
is a warning, listed again at the end), and `✓` or `✗` with the time it
took; off a terminal, each step is a plain `[n/total]` line followed by
its details. `--quiet` drops the details but keeps warnings. A workflow's
optional `result` template is what text mode prints; `-o json` prints its
outputs. A command step's `save: <file>` keeps its stdout, such as a
style's ZIP, as a file for later steps, in a working directory removed
when the run ends; a command step may be marked `dry_run` when its command
changes nothing. The
generated agent skill leaves `workflow` out. The first one published is
`copy-style`, which copies a style between accounts with its custom fonts
and icons, and on a partial failure lists what it created and the
commands that remove it. The command, the `version: 1` format and the published
workflows may change or be removed without notice, and every `workflow`
subcommand says so on stderr. Nothing changes for a script that does not
use them.

- New command: `mapbox styles download <style-id> > style.zip` saves a
style as a ZIP with its sprite icons and custom fonts. `mapbox auth login`
now also asks for the `styles:download` scope it needs, so log in again
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mapbox styles list
- [Diagnostics and settings](#diagnostics-and-settings)
- [Shell completion](#shell-completion)
- [Tileset CLI](#tileset-cli)
- [Workflows (beta)](#workflows-beta)
- [For AI agents](#for-ai-agents)
- [Agent skills](#agent-skills)
- [Generate skills](#generate-skills)
Expand Down Expand Up @@ -289,6 +290,17 @@ apply here; `tilesets` has its own flags, so use `--force`/`-f` for its
prompts. If a tileset command answers for the wrong account,
`mapbox auth whoami` shows which token is in play.

### Workflows (beta)

```sh
mapbox workflow install copy-style
mapbox workflow run copy-style --style-id <STYLE_ID> --from-profile source --to-profile target
```

A workflow is a named, multi-step recipe of `mapbox` commands and scripts.
`mapbox workflow` is in development and not recommended for use yet. See
[Workflows](./docs/commands.md#workflows).

## For AI agents

Two commands, for two different jobs: `agent-skills` installs guidance on
Expand Down
292 changes: 291 additions & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ nests, and is typed `mapbox styles draft get`.
[tilesets.get-mvt](#mapbox-tilesets-get-mvt) ·
[tilesets.query](#mapbox-tilesets-query)

**[Workflows](#workflows)** — [workflow.list](#mapbox-workflow-list) ·
[workflow.show](#mapbox-workflow-show) ·
[workflow.install](#mapbox-workflow-install) ·
[workflow.uninstall](#mapbox-workflow-uninstall) ·
[workflow.run](#mapbox-workflow-run)

**[Tilesets CLI](#tilesets-cli)** — [tilesets-cli](#mapbox-tilesets-cli-args)

Then [Errors](#errors) — the shape a failure takes in each mode.
Expand Down Expand Up @@ -527,7 +533,7 @@ either.
| `--profile <name>` | Which stored credentials to use. |
| `--output`, `-o` | `auto` \| `text` \| `json`. |
| `--id <value>` | On a command that returns a list, print just the row with that `id` or `name`. |
| `--quiet`, `-q` | Don't print the `mapbox · v<version>` banner, which goes to stderr and only when stderr is a terminal. Also `MAPBOX_QUIET`. |
| `--quiet`, `-q` | Don't print the `mapbox · v<version>` banner, which goes to stderr and only when stderr is a terminal, the note after a download, or a workflow step's details. Also `MAPBOX_QUIET`. |
| `--timeout <seconds>` | How long one request may take, connection included. Defaults to 60 seconds, or 900 for a body read from `--file` or from a `--data @<path>`/`@-`. Also `MAPBOX_TIMEOUT`. |

An operation with a request body takes `--data`/`-d` when that body is text
Expand Down Expand Up @@ -3852,6 +3858,290 @@ list drops the `--daily` suggestion once it's already in effect.

---

## Workflows

A workflow is a named, multi-step recipe of `mapbox` commands and scripts,
defined in a `workflow.yaml`. The format and the rules a workflow directory
follows are in [workflow/README.md](../workflow/README.md).

**Beta and in development. Not recommended for use.** The commands, the
`version: 1` format and the published workflows may change or be removed
without notice. Every `mapbox workflow` subcommand opens with this line on
stderr:

```
Beta: `mapbox workflow` is in development and not recommended for use.
```

In text mode, `list`, `show` and `install` end with tips on stderr, naming
the command to run next. `mapbox generate-skills` leaves the `workflow`
commands out of the skill it writes, so that an agent is not taught a command
nobody should rely on yet.

None ships inside the binary. `install` copies one into
`~/.mapbox/workflows/<name>/`, and every other subcommand works on what is
installed there.

---

### `mapbox workflow list`

Lists the installed workflows. One that no longer loads, because a file was
edited by hand or this CLI no longer reads its format, is listed with its
error instead of a summary.

#### Outputs

<table>
<tr><th width="50%"><code>text</code></th><th width="50%"><code>json</code></th></tr>
<tr><td>

```
NAME SUMMARY
copy-style Copy a style from one account to another
```

</td><td>

```json
[
{
"name": "copy-style",
"path": "/Users/me/.mapbox/workflows/copy-style",
"source": "github:mapbox/mapbox-cli@main",
"summary": "Copy a style from one account to another"
}
]
```

</td></tr>
</table>

---

### `mapbox workflow show`

Describes an installed workflow, in the same layout for every one: its name
and summary, its description, its inputs with their types and defaults, its
steps, and where the installed copy came from. When a step names a command or
an argument this build no longer has, the problems are listed after the steps
and under `problems`. How a description is written is in
[workflow/README.md](../workflow/README.md#description).

#### Parameters

| Parameter | Effect |
| --- | --- |
| `NAME` | Name of an installed workflow. |

---

### `mapbox workflow install`

Installs a workflow from GitHub or from a local directory. Everything is read
and checked before anything is written: the layout, the `workflow.yaml`, and
every command step against this build's commands. A workflow that fails any
of these is `invalid_workflow`, with every problem listed at once.

#### Parameters

| Parameter | Effect |
| --- | --- |
| `SOURCE` | A workflow name, looked up in the repository's `workflow/<name>/`, or a path to a local workflow directory: anything with a `/` or starting with `.`. |
| `--repo` | GitHub repository to install from, as `OWNER/REPO`. Defaults to `mapbox/mapbox-cli`. |
| `--ref` | Branch, tag or commit to install from. Defaults to `main`. |
| `--force` | Replace a workflow that is already installed. |
| `--dry-run` | Check the workflow and list the files it would write, then exit. |

The repository is read as one tarball through the GitHub API.
`mapbox/mapbox-cli` is public and needs no token. For a private repository
named with `--repo`, set `GH_TOKEN` or `GITHUB_TOKEN`. It is sent only to
`api.github.com`. Without one, a private repository answers 404,
exactly as a ref that does not exist does, and the error says both.

**An installed workflow stops the install** unless `--force` is given. The new
copy is staged and renamed into place, so an interrupted install leaves the
old copy or the new one. Only regular files are taken. A symlink in a local
directory is refused, and an archive entry whose path would leave the
workflow's directory stops the read.

#### Examples

```sh
mapbox workflow install copy-style

mapbox workflow install copy-style --ref v0.4.0

export GITHUB_TOKEN="$(gh auth token)"
mapbox workflow install sync-tilesets --repo my-org/private-workflows

mapbox workflow install ./workflow/copy-style --force
```

#### Outputs

<table>
<tr><th width="50%"><code>text</code></th><th width="50%"><code>json</code></th></tr>
<tr><td>

```
Installed copy-style

Source ~/dev/cli/workflow/copy-style
Path ~/.mapbox/workflows/copy-style
Files README.md, scripts/copy_style.py, workflow.yaml
```

</td><td>

```json
{
"dry_run": false,
"files": [
"README.md",
"scripts/copy_style.py",
"workflow.yaml"
],
"name": "copy-style",
"path": "/Users/me/.mapbox/workflows/copy-style",
"source": "/Users/me/dev/cli/workflow/copy-style"
}
```

</td></tr>
</table>

---

### `mapbox workflow uninstall`

Removes an installed workflow's directory. At a terminal it asks first, and
`--yes` skips the question. Only a directory `install` wrote under
`~/.mapbox/workflows/` is removed.

#### Parameters

| Parameter | Effect |
| --- | --- |
| `NAME` | Name of an installed workflow, or the directory it was installed from, which names it by its directory name. The directory itself is not touched. |
| `--dry-run` | Say what it would remove, then exit without removing it. |

---

### `mapbox workflow run`

Runs an installed workflow's steps in order. The first step that fails stops
the run with `workflow_failed`, naming the step, and exit code 1.

#### Parameters

| Parameter | Effect |
| --- | --- |
| `NAME` | Name of an installed workflow. |
| `--<input>` | One flag per input the workflow declares, spelled with dashes (`style_id` is `--style-id`). Required, typed and defaulted as its `workflow.yaml` says. `mapbox workflow run <name> --help` lists them. |
| `--dry-run` | Check the workflow and its inputs and print the plan. Runs only the steps marked `dry_run`, which write nothing; skips the rest. |

The flags come from the installed workflow's definition, read before the
command line is parsed, so a missing input, a misspelled flag or a value of the
wrong type is a usage error (exit 2), as on any other command. They are not in
`--schema` or in the shell completion, which describe the command tree without
reading `~/.mapbox/workflows/`. `workflow show` lists them for any installed
workflow.

**stdout holds only the result**: the workflow's `outputs`, or the last
step's output if it declares none, rendered like any other result. Each
step's progress and anything a step writes to stderr go to stderr. At a
terminal, each step shows its title, the details its script reports, and a
spinner that becomes `✓` or `✗` with the time it took; anywhere else, each
step is one `[n/total]` line followed by its details, with no escape codes.
`--quiet` keeps each step's title, how it ended and its warnings, and drops
its details. `workflow/README.md` describes how a script reports progress,
details and warnings.

A workflow that declares `result` prints it in text mode instead of the
outputs as fields; `-o json` always prints the outputs.

Each command step is this binary run again with `--output json`, so it
resolves its token and applies its timeouts as the same command typed by
hand would, and appears in `mapbox history` as its own run. The globals given
to `workflow run` (`--profile`, `--username`, `--use-login`, `--timeout`,
`--yes`, `--debug`, `--token`) reach every command step that does not set its
own. A token typed as `--token` goes to the step's environment, not its
command line.

#### Examples

```sh
mapbox workflow run copy-style \
--style-id cmm28c5rm00bj01qz9hwp69qc \
--from-profile source \
--to-profile target

mapbox workflow run copy-style --style-id cmm28c5rm00bj01qz9hwp69qc \
--from-profile source --to-profile target --dry-run
```

`--dry-run` prints the plan: the inputs as they were read, and each step with
its arguments as written. A step marked `dry_run` in its `workflow.yaml` runs
for real, so the plan can show what the rest would do with real data: a
command step only if its command changes nothing, and a script step with
`MAPBOX_WORKFLOW_DRY_RUN=1` in its environment and the promise to write
nothing. Their outputs are under `results` in `-o json`. Every other step is
skipped. `copy-style` marks all of its steps, so its dry run downloads the
style and lists what it would upload:

```
$ mapbox workflow run copy-style --style-id cmums8rlh000301s96498hnju \
--from-profile default --to-profile default --dry-run
🗺️ mapbox · v0.3.0
────────────────────────────────────────
Beta: `mapbox workflow` is in development and not recommended for use.

▸ Check the source login mapbox auth status
✓ 0.0s

▸ Check the target login mapbox auth status
✓ 0.0s

▸ Download the style mapbox styles download
Saved style.zip (965 KB)
✓ 0.3s

▸ List the target account's fonts mapbox fonts list
✓ 0.1s

▸ Plan the copy
Found 561 icons and 1 custom font
Font Yellow Banana Regular is already in zhuwenlong; it will be skipped
✓ 0.2s

▸ Copy the fonts, the style and its icons
Would copy the style into zhuwenlong as "CLI copy test: Helsinki Evening · CLI Blog Demo":
skip font Yellow Banana Regular (already in zhuwenlong)
create the style
upload 561 icons in 23 batches
point its sprite and glyphs at zhuwenlong
✓ 0.1s

Dry run — only the steps that support it ran, and they wrote nothing. Would run copy-style:

Inputs
from_profile default
name (none)
style_id cmums8rlh000301s96498hnju
to_profile default

Steps
1. Check the source login mapbox auth status (ran in this dry run)
2. Check the target login mapbox auth status (ran in this dry run)
3. Download the style mapbox styles download (ran in this dry run)
4. List the target account's fonts mapbox fonts list (ran in this dry run)
5. Plan the copy python3 scripts/copy_style.py (ran in this dry run)
6. Copy the fonts, the style and its icons python3 scripts/copy_style.py (ran in this dry run)
```

---

## Tilesets CLI

### `mapbox tilesets-cli <args…>`
Expand Down
Loading
Loading