Tasks
Short commands you run all day: build, lint, reset the database. You keep them in one file, and you run them from the sidebar.
Just the keys and defaults? See the cheat sheet.
- What they are
- The short way to write one
- The long way to write one
- The rest of the file
needs:pulls in what it must, and nothing else- Changing the order you see
- The name in front of the colon is the id
- Where tasks come from
What they are
A task here is the same kind of task a
workflow is made of. The same
parser reads both. These ones live in .local-workflows/tasks.yml:
version: 1
tasks:
setup: echo Restoring dependencies...
They are kept apart from workflows for one reason. A workflow is a pipeline, and you run it as a whole. These are single commands, and you run them one at a time, many times a day.
Every task runs exactly one thing, and that thing is always a
plugin. A plain string like the one above
is sugar for the shell@1 plugin — the task is the wrapper, the plugin
is what executes.
The short way to write one
A plain string is the whole task:
tasks:
setup: echo Restoring dependencies...
lint: npm run lint
The string is short for run:. A run: command uses the shell your OS
already has: cmd on Windows, bash everywhere else. You do not need to
add anything, and nothing is added for you.
If you write PowerShell, say so with shell: pwsh. Without it, a task
using $env:NAME runs in cmd and comes back empty.
The long way to write one
A task here can carry anything a workflow task can carry:
tasks:
setup: echo Restoring dependencies...
build:
name: Build # the row's label; without it, you see the id
desc: Compiles the app # its tooltip
needs: setup # one name, or a list of them
shell: pwsh
env:
BUILD_CONFIGURATION: Debug
run: |
echo one
echo two
The rest of the keys:
| Key | What it does |
|---|---|
uses:, args: |
run a plugin instead of a shell command |
cwd: |
the folder the command runs in |
vars:, dotenv: |
values for this task, next to env: |
trigger: manual |
stop and wait for a person to click Run |
output: |
store what this task printed, for a later task to read |
if: |
run this task only when the condition is true |
timeout:, retries: |
how long it gets, and how many extra tries |
continueOnError: |
let the run carry on when this task fails |
session: |
names an AI conversation; it does nothing for other tasks |
That is the whole list. Any other key is an error, so a typo tells you rather than being quietly ignored.
The rest of the file
Five more keys can sit next to tasks:. They set values that every task
in the file shares:
version: 1 # you can leave this out; it is read as 1
vars: # values you paste into fields below, never exported
dotenv: # .env files, read before env:
plugins: # name a plugin call once, then use it by that name
env: # passed to every task as environment variables
tasks:
You can also write name: at the top of the file, but nothing shows it.
A tasks.yml is a list of commands, not a pipeline with a title.
Here the rules are the other way round. A key that is not one of these is
skipped without a word, so writing taks: gives you an empty list and no
error.
needs: pulls in what it must, and nothing else
You can run any task on its own. When you do, whatever it needs: runs
first, and whatever those tasks need, and so on. Nothing else in the file
runs.
tasks:
setup: echo Restoring...
build:
needs: setup
run: echo Compiling...
Run build, and setup runs first. That is all.
Changing the order you see
Tasks are listed in the order you wrote them in the file. The Tasks row has one icon that changes this:
| The list is in | The icon offers |
|---|---|
| file order | Sort Tasks A-Z |
| A-Z | Sort Tasks in File Order |
You can do the same from the Command Palette, with Local Workflows: Sort Tasks A-Z and Local Workflows: Sort Tasks in File Order.
A-Z sorts by the label you see, which is name:, or the id when there is
no name:. Upper and lower case count as the same letter. Profile tasks
are sorted in with the rest, not pushed to the end. If two labels match,
those two keep their file order.
Your choice is saved for this workspace only, and nothing is written into your repository. Some projects read best in the order you wrote them. Others are thirty unrelated commands, and then you want them A-Z.
The name in front of the colon is the id
In build:, the word build is the task’s id. That is the name needs:
points at, and the name everything else uses too, including the merge
below.
This is the one real difference from a workflow. There, a job’s tasks are a list, and each task is known by its position instead of a name.
Where tasks come from
| Scope | ||
|---|---|---|
| folder | <any>/.local-workflows/tasks.yml |
this project’s |
| workspace | beside an open .code-workspace |
shared by every repository in it |
| profile | ~/.local-workflows/tasks.yml |
yours, in every workspace you open |
The middle one only exists when you opened a .code-workspace file —
see Workspaces. Open a plain
folder and there are two.
On the folder side, every .local-workflows/tasks.yml in the tree
counts, however deep it sits. So a monorepo can keep
packages/api/.local-workflows/tasks.yml next to the code it builds. If
more than one file has tasks, each row is labelled with the project
folder it came from.
You see one list, in that order: folder, then workspace, then
profile. A wider task is left out only when a narrower one has the
same id. So your personal sync sits happily beside the project’s
build, and if the project writes its own sync, the project wins.
When more than one scope has tasks, the rows sit under Folder / Workspace / Profile headings. One scope and the list stays flat.
Tasks that belong to no repository still run in one, not in your home
folder — git pull is only useful where the repo is. In a
.code-workspace window you are asked which one, every time you run it.