Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 30 additions & 1 deletion docs/dev-tools/prepare.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ run = "npx prisma generate"
| `dir` | string | Working directory for the command |
| `description` | string | Description shown in output |
| `touch_outputs` | bool | Touch output mtimes after a successful run so they appear fresh (default: true) |
| `depends` | string[] | Other provider names that must complete before this one runs |

## Freshness Checking

Expand Down Expand Up @@ -170,10 +171,35 @@ mise prepare --only npm --only codegen
mise prepare --skip npm
```

## Dependencies

Providers can declare dependencies on other providers using the `depends` field. A provider
will wait for all its dependencies to complete successfully before running.

```toml
[prepare.uv]
auto = true

[prepare.ansible-galaxy]
auto = true
depends = ["uv"]
run = "ansible-galaxy install -f requirements.yml"
sources = ["requirements.yml"]
outputs = [".galaxy-installed"]
```

In this example, `ansible-galaxy` will wait for `uv` to finish before starting.

Providers without `depends` run in parallel as before. If a dependency fails, all providers
that depend on it are skipped. Circular dependencies are detected and the affected providers
are skipped with a warning.

## Parallel Execution

Prepare providers run in parallel, respecting the `jobs` setting for concurrency limits.
This speeds up preparation when multiple providers need to run (e.g., both npm and pip).
Providers with `depends` will wait for their dependencies to complete before starting,
while independent providers run concurrently.

```toml
[settings]
Expand All @@ -193,14 +219,17 @@ auto = true

[prepare.prisma]
auto = true
depends = ["npm"] # needs node_modules first
sources = ["prisma/schema.prisma"]
outputs = ["node_modules/.prisma/"]
run = "npx prisma generate"

[prepare.frontend-codegen]
depends = ["npm"] # needs node_modules first
sources = ["schema.graphql", "codegen.ts"]
outputs = ["src/generated/"]
run = "npm run codegen"
```

Running `mise prep` will check all four providers and run any that are stale, in parallel.
Running `mise prep` will install npm and poetry dependencies in parallel, then run prisma
and frontend-codegen (also in parallel, since they only depend on npm, not each other).
84 changes: 84 additions & 0 deletions e2e/cli/test_prepare_depends
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash

# Test prepare dependency ordering
# Verifies that prepare providers with `depends` run in the correct order

# Create source files so providers are stale
touch schema.graphql
touch requirements.txt

cat >mise.toml <<'EOF'
[prepare.step-a]
run = "bash -c 'echo STEP-A >> prepare.log'"
sources = ["schema.graphql"]
outputs = ["gen-a"]
description = "step-a"

[prepare.step-b]
run = "bash -c 'echo STEP-B >> prepare.log'"
sources = ["requirements.txt"]
outputs = ["gen-b"]
depends = ["step-a"]
description = "step-b"

[prepare.step-c]
run = "bash -c 'echo STEP-C >> prepare.log'"
sources = ["requirements.txt"]
outputs = ["gen-c"]
depends = ["step-b"]
description = "step-c"
EOF

# Force run to ensure all steps execute
mise prepare --force 2>&1

# Verify all steps ran and in dependency order (a before b before c)
assert "cat prepare.log" "STEP-A
STEP-B
STEP-C"

# Clean up for next test
rm -f prepare.log

# Test that independent steps can still run in parallel alongside deps
cat >mise.toml <<'EOF'
[prepare.dep-first]
run = "bash -c 'echo DEP-FIRST >> prepare2.log'"
sources = ["schema.graphql"]
outputs = ["gen-dep-first"]
description = "dep-first"

[prepare.dep-second]
run = "bash -c 'echo DEP-SECOND >> prepare2.log'"
sources = ["requirements.txt"]
outputs = ["gen-dep-second"]
depends = ["dep-first"]
description = "dep-second"

[prepare.independent]
run = "bash -c 'echo INDEPENDENT >> prepare2.log'"
sources = ["schema.graphql"]
outputs = ["gen-independent"]
description = "independent"
EOF

mise prepare --force 2>&1

# dep-first must appear before dep-second
output=$(cat prepare2.log)
assert_contains "echo '$output'" "DEP-FIRST"
assert_contains "echo '$output'" "DEP-SECOND"
assert_contains "echo '$output'" "INDEPENDENT"

# Verify dep-first is before dep-second in the log
dep_first_line=$(grep -n "DEP-FIRST" prepare2.log | head -1 | cut -d: -f1)
dep_second_line=$(grep -n "DEP-SECOND" prepare2.log | head -1 | cut -d: -f1)
if [[ $dep_first_line -ge $dep_second_line ]]; then
echo "ERROR: DEP-FIRST (line $dep_first_line) should appear before DEP-SECOND (line $dep_second_line)"
exit 1
fi

rm -f prepare2.log

# Test dry-run with depends shows what would run
assert_contains "mise prepare --dry-run" "dep-first"
7 changes: 7 additions & 0 deletions schema/mise.json
Original file line number Diff line number Diff line change
Expand Up @@ -2221,6 +2221,13 @@
"description": {
"type": "string",
"description": "Description shown in output"
},
"depends": {
"type": "array",
"items": {
"type": "string"
},
"description": "Other prepare providers that must complete before this one runs"
}
},
"additionalProperties": false,
Expand Down
3 changes: 3 additions & 0 deletions src/cli/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ impl Prepare {
PrepareStepResult::Skipped(id) => {
debug!("Skipped: {}", id);
}
PrepareStepResult::Failed(id) => {
miseprintln!("Failed: {}", id);
}
}
}

Expand Down
Loading
Loading