Skip to content

[WIP] Add mapbox workflow: install and run multi-step workflows (beta) - #67

Draft
zmofei wants to merge 20 commits into
feat/styles-downloadfrom
feat/workflows
Draft

zmofei wants to merge 20 commits into
feat/styles-downloadfrom
feat/workflows

Conversation

@zmofei

@zmofei zmofei commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Stacked on #66. It uses the src/output/ layout and the --quiet banner flag from that PR, so review it after #66 and retarget to main once #66 merges.

What

mapbox workflow, a command for multi-step recipes of mapbox commands and scripts, defined in a workflow.yaml. It is beta, in development and not recommended for use. Every subcommand says so on stderr, and so do --help, the docs and the changelog:

mapbox workflow list | show <name> | uninstall <name>
mapbox workflow install <name | ./path> [--repo OWNER/REPO] [--ref REF] [--force] [--dry-run]
mapbox workflow run <name> --<input> <value> ... [--dry-run]
  • No workflow ships in the binary. install copies one from a local directory, or from a GitHub repository's workflow/<name>/ (default mapbox/mapbox-cli@main), into ~/.mapbox/workflows/<name>/. run runs only what is installed.
  • Before anything is written, install checks the directory layout, the yaml and every command step against this build's command tree.
  • Each command step runs this binary again with --output json, so credentials, timeouts, path encoding and history behave exactly as for a typed command. Script steps get args, stdin, and MAPBOX_CLI for calling the CLI themselves.
  • run takes each input as a flag (--style-id). The installed workflow is attached as a real subcommand before parsing, so --help, missing inputs and typos behave like any other command. Every other command line sees the tree unchanged. History records workflow run without the workflow's name, and generate-skills leaves workflow out.
  • Expressions are lookups only: ${{ inputs.x }} and ${{ steps.id.output.a[0] }}.
  • The first workflow is workflow/copy-style. It reads a style with one profile, strips the fields the Styles API sets itself, and creates it with another profile. Every sprite, font, tileset and import that belongs to the source account is listed in warnings, because none of them is copied.

The schema and the layout rules are in workflow/README.md, and the commands are documented in docs/commands.md#workflows.

Guards

  • every_published_workflow_is_valid holds workflow/ to the same rules install applies. I checked that it fails on a stray file.
  • workflow/store.rs is added to MAY_DELETE and NO_REQUEST_ID_TO_CARRY, with reasons. The requests go to GitHub, not Mapbox.
  • tests/workflow.rs runs real child processes: values pass between command and script steps, only the result reaches stdout, a failed step stops the run, --dry-run runs nothing, and install, --force and uninstall behave correctly on disk. I checked that the token-forwarding assertion fails when the forwarding is removed.

Not checked

  • copy-style has not been run end to end against the real API. styles get and prepare.py were run on a real style, but styles create was not, because it would create a style on a real account.
  • Cross-account behavior is unknown: whether the Styles API accepts a style whose sprite belongs to another account, and whether the copy can read it.
  • Install from GitHub works end to end against the real API: install copy-style --ref feat/workflows with a token downloaded, checked and installed it. Without a token it returns 404 and the error explains why. The default repository is mapbox/mapbox-cli, because mapbox/cli (the REPO_URL in main.rs) redirects to mapbox/cli-opensource-preview. REPO_URL itself is left alone here.
  • Nothing has been run on Windows. copy-style needs python3 on PATH.

@zmofei zmofei self-assigned this Sep 29, 2026
@zmofei zmofei changed the title Add mapbox workflow: install and run multi-step workflows (beta) [WIP] Add mapbox workflow: install and run multi-step workflows (beta) Sep 29, 2026
A workflow is a workflow.yaml of command and script steps, installed from a
local directory or a GitHub repository's workflow/<stage>/<name>/ into
~/.mapbox/workflows/, then run by name. Command steps run this binary again
with --output json, so they resolve credentials, timeouts and path encoding
exactly as a typed command does.

The first published workflow is workflow/beta/copy-style, which copies a
style between accounts using two credential profiles.
Every workflow subcommand now says on stderr that the feature is beta, in
development and not recommended for use, as do --help, the docs and the
changelog.

Workflows live at a fixed workflow/beta/<name>/ rather than under a
variable stage directory, so the stage list, the stage recorded at install
and the Stage shown by list and show are gone.
The beta/ level no longer means anything now that the stage concept is
gone, so copy-style moves up to workflow/copy-style and install looks for
workflow/<name>/ in a repository.
mapbox/cli redirects to a different repository on GitHub, so the default
source never held this repository's workflows.
The notice is one short line with a bold label, paths under the home
directory read as ~/..., list and show lay their fields out in aligned
columns, and the next command to run is a tip on stderr rather than part
of the result. Step progress is colored at a terminal. JSON is unchanged.
install takes a path, so uninstall ./workflow/copy-style is the natural
undo; it now names the workflow by that directory's name. Only an
installed workflow under ~/.mapbox/workflows/ is ever removed, as before.
The already-installed error now gives the install line to retry with
--force.
show lays every workflow out the same way: name and summary, the
description, inputs, steps, then where the installed copy came from. The
description is read as a small Markdown subset: paragraphs are reflowed,
indented lines are highlighted commands, '- ' lines are a list, and code
spans are highlighted. copy-style's description uses it.
mapbox workflow run copy-style --style-id <id> --from-profile source
--to-profile target, in place of repeated --input KEY=VALUE. Before the
parse, the named installed workflow is attached under `run` as a real
subcommand whose inputs are typed, required flags, so --help, a missing
input and a misspelled flag behave as on every other command. Every other
command line sees the tree unchanged.

An input may not take a global option's name. The run record keeps the path
as `workflow run`, without the workflow's name, and the generated agent
skill leaves `workflow` out, since it is not recommended for use.
The test matched the install line in stderr's raw text, but the error is
JSON there and JSON escapes a Windows path's backslashes, so it failed on
windows-2022 only. It now parses the error and compares next_actions.
Every top-level command needs an example line in the README, which the
docs contract now checks.
@zmofei
zmofei changed the base branch from feat/terminal-styling to feat/styles-download September 30, 2026 10:17
A step marked dry_run runs during a dry run with MAPBOX_WORKFLOW_DRY_RUN=1
and the promise to write nothing, so the plan can show real data. Only a
script can be marked, and a marked step reads only marked steps.
Download the style's ZIP as the source account, then upload new fonts,
create the style, upload its icons in batches of 25 and point its sprite
and glyphs at the target account. Both steps support --dry-run. A partial
failure lists what was created and the commands that remove it.
--use-login keeps a MAPBOX_ACCESS_TOKEN from outranking the profile, but a
MAPBOX_USERNAME still named the account, so a request could carry one
account's token to another's path and be refused. Pass --username with
the login's own account. Also check the target before downloading, and
show a failed command's message and fix rather than its JSON.
The Styles API refuses a new style whose sprite the target account cannot
read, which a private sprite of another account is. Create the copy with
no sprite and its glyphs already on the target, then set its own sprite
after the icons are uploaded.
At a terminal a step is its title, the details its script reports on
stderr, and a status line: a spinner beside the latest ::progress line
while it runs, then a check or a cross and the time it took. Off a
terminal it stays one [n/total] line, details follow, and progress lines
are dropped. Any workflow's script steps get this; copy-style now reports
each font, the created style and the icon batches.

A workflow's result also stops claiming its JSON is what an API sent.
A script's ::warn line is shown under its step and listed again after the
last one. --quiet keeps each step's title, outcome and warnings and drops
its details. A workflow's optional result template is what text mode
prints instead of the outputs as fields; -o json still prints the
outputs. copy-style uses all three, and every workflow can.
A command step can now keep its stdout as a file with save, in a working
directory removed when the run ends, which is how a style's ZIP reaches a
later step; and it can be marked dry_run when its command changes nothing.
copy-style checks both logins, downloads the style and lists the target's
fonts as command steps, and keeps a script only for the copy's loops.
It is public, so installing from it needs no token. Show the token with
a private --repo instead.
On Windows the working directory's path is full of backslashes, which the
script wrote into its own JSON unescaped, so its output parsed as text.
Take the path through outputs instead, which the runner encodes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant