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
2 changes: 1 addition & 1 deletion TASK_CACHE_PARITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ outside this tracker.
- [x] Cache useful results for tasks with no filesystem outputs
- [x] Support reusable and global input groups
- [x] Support global environment inputs and pass-through environment variables
- [ ] Support negative input and output patterns
- [x] Support negative input and output patterns
- [ ] Support runtime-command inputs
- [ ] Include external dependency and lockfile state through explicit input configuration
- [ ] Add per-run cache read/write controls, including local-only and cache-disabled modes
Expand Down
19 changes: 17 additions & 2 deletions docs/tasks/task-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,21 @@ otherwise `sources` on the dependent task would be effectively useless.
The counterpart to `sources`, these are the files or directories that the task will create/modify after
it executes.

Entries prefixed with `!` exclude matching outputs. As with `sources`, entries
are evaluated in order, a later entry can re-include a path, and `\!` escapes a
literal leading bang.

```mise-toml
[tasks.build]
run = "npm run build"
sources = ["src/**"]
outputs = ["dist", "!dist/**/*.map", "!dist/.vite/**"]
```

Excluded files do not participate in output freshness checks and are not
stored in task-cache artifacts. If excluded files already exist beneath an
output directory when a cached artifact is restored, mise preserves them.

`auto = true` is an alternative to specifying output files manually. In that case, mise will touch
an internally tracked file based on the hash of the task definition (stored in `~/.local/state/mise/task-outputs/<hash>` if you're curious).
This is useful if you want `mise run` to execute when sources change but don't want to have to manually `touch`
Expand All @@ -498,8 +513,8 @@ to reproduce.

Artifact caching requires [`experimental`](/configuration/settings.html#experimental), at least one
matching `source`, and either explicit output paths or `outputs = []`.
`outputs = { auto = true }`, absolute outputs, and outputs that escape the task directory are not
supported.
`outputs = { auto = true }`, absolute outputs, and output patterns (including
the body of an exclusion) that escape the task directory are not supported.

```mise-toml
[settings]
Expand Down
53 changes: 53 additions & 0 deletions e2e/tasks/test_task_artifact_cache_negative_patterns
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash

cat <<'EOF' >mise.toml
[settings]
experimental = true
task.source_freshness_hash_contents = true

[tasks.build]
run = '''
mkdir -p dist/private
cp src/input.txt dist/result.txt
printf 'generated map\n' >dist/result.map
printf 'generated token\n' >dist/private/token
printf 'ran\n' >>runs.txt
'''
sources = ["src/**/*", "!src/ignored.txt"]
outputs = ["dist", "!dist/**/*.map", "!dist/private/**"]
cache = { enabled = true }
EOF

mkdir -p src
printf 'one\n' >src/input.txt
printf 'ignored one\n' >src/ignored.txt
mise cache clear

mise run build
assert "wc -l <runs.txt | tr -d ' '" "1"

# Excluded inputs do not invalidate the key. Excluded outputs remain local
# when a selected sibling is restored into the same output directory.
printf 'ignored two\n' >src/ignored.txt
printf 'local map\n' >dist/result.map
printf 'local token\n' >dist/private/token
rm dist/result.txt
assert_contains "mise run build 2>&1" "restored outputs from cache"
assert "wc -l <runs.txt | tr -d ' '" "1"
assert "cat dist/result.txt" "one"
assert "cat dist/result.map" "local map"
assert "cat dist/private/token" "local token"

# Excluded outputs are not stored in the artifact.
rm -rf dist
assert_contains "mise run build 2>&1" "restored outputs from cache"
assert "cat dist/result.txt" "one"
assert_fail "test -e dist/result.map"
assert_fail "test -e dist/private/token"

# Included inputs still invalidate the cache.
printf 'two changed\n' >src/input.txt
rm -rf dist
mise run build
assert "wc -l <runs.txt | tr -d ' '" "2"
assert "cat dist/result.txt" "two changed"
26 changes: 26 additions & 0 deletions e2e/tasks/test_task_artifact_cache_validation
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@ EOF

assert_fail_contains "mise run build" "cache output must stay within"

cat <<'EOF' >mise.toml
[settings]
experimental = true

[tasks.build]
run = "mkdir -p dist"
sources = ["input"]
outputs = ["dist", "!../out"]
cache = { enabled = true }
EOF

assert_fail_contains "mise run build" "cache output must stay within"

cat <<'EOF' >mise.toml
[settings]
experimental = true

[tasks.build]
run = "mkdir -p dist"
sources = ["input"]
outputs = ["!dist/**"]
cache = { enabled = true }
EOF

assert_fail_contains "mise run build" "outputs require at least one non-excluded pattern"

mkdir outside
ln -s outside build
cat <<'EOF' >mise.toml
Expand Down
6 changes: 3 additions & 3 deletions schema/mise-task.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions schema/mise.json
Original file line number Diff line number Diff line change
Expand Up @@ -2927,15 +2927,15 @@
"outputs": {
"oneOf": [
{
"description": "files created by this task",
"description": "files created by this task (entries starting with `!` are excluded; use `\\!` to escape a literal `!` prefix)",
"items": {
"description": "glob pattern or path to files created by this task",
"description": "glob pattern or path to files created by this task (entries starting with `!` are excluded)",
"type": "string"
},
"type": "array"
},
{
"description": "glob pattern or path to files created by this task",
"description": "glob pattern or path to files created by this task (entries starting with `!` are excluded)",
"type": "string"
},
{
Expand Down
Loading