task-keeper 0.31.0

Task keeper to manage tasks from different task runners
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
Task Keeper
=================
tk(Task Keeper) is a tool to manage tasks from different task files,
such as `Makefile`,`justfile`, `package.json` , `deno.json`, `.fleet/run.json` etc.,
and call tasks from different project management tools,
such as `Apache Maven`, `Gradle`, `Cargo` and `npm` etc.

![Task Keeper](./screenshot.png)

**Bonus**: sq(Squirrel) is a command-line snippets keeper to manage cli snippets.

# Features

* List tasks from different task files: `tk --list`
* Invoke task: `tk task_name`
* Invoke task from a runner: `tk --runner=npm start`
* Sync tasks between sources: `tk --from=npm --to=fleet task1 task2`
* .env and [dotenvx]https://dotenvx.com/ support by default: `tk --no-dotenv` to disable
* `--doctor` support: check your system for potential problems to run tasks
* Execute command line: `tk -- node hello.js` with a correct language version and PATH
* Notification: save task output to OSS or send notification to NATS server

# How to get started?

* Install by cargo: `cargo install task-keeper` (or `cargo binstall task-keeper`)
* Mac: `brew tap linux-china/tap; brew install --no-quarantine task-keeper`
* Using [nix]https://nix.dev/:
    * try without installing: `nix shell github:linux-china/task-keeper`
    * install: e.g. `nix profile install github:linux-china/task-keeper`
* Download pre-built binary from [GitHub Releases]https://github.com/linux-china/task-keeper/releases

After installation, execute `tk --help` for usage. Some commands as following:

* list tasks: `tk --list`
* execute task: `tk task_name`

# Why task keeper?

Sorry, I got lost in different task files and management tools, and sometimes I even cannot remember how to run them.

* Find tasks: `Makefile`,`justfile`, `package.json`, `deno.json`, `Taskfile.yml`, `tasks.py`, `README.md` etc., and
  too many task files to check tasks.
* Run task: `just taskName`, `npm run task_name`, `deno task task_name`, `composer run-script task_name` etc
* Check outdated deps: `mvn versions:display-dependency-updates`, `./gradlew dependencyUpdates`, `npm outdated` etc
* Build project: `mvn -DskipTests package`, `./gradlew assemble`, `cargo build` etc

Too many differences, I want to save my brain and keyboard, and you know MacBook keyboard's price tag is $400+.

# Task runners support

* make(Makefile): https://www.gnu.org/software/make/manual/make.html, [Modern Make]https://github.com/tj/mmake support
* ant(build.xml): https://ant.apache.org/
* npm(package.json): https://docs.npmjs.com/cli/v8/using-npm/scripts
* jake(jakefile.js): https://jakejs.com
* deno(deno.json): https://deno.land/manual/tools/task_runner
* composer(composer.json): https://getcomposer.org/doc/articles/scripts.md
* just(justfile): https://github.com/casey/just
* Rakefile(rake): https://ruby.github.io/rake/
* invoke(tasks.py): https://www.pyinvoke.org/
* task(Taskfile.yml): https://github.com/go-task/task  https://www.sobyte.net/post/2022-04/taskfile/
* cargo-make(Makefile.toml):  https://github.com/sagiegurari/cargo-make
* JBang(jbang-catalog.json): https://www.jbang.dev/documentation/guide/latest/alias_catalogs.html
* proc(Procfile): https://devcenter.heroku.com/articles/procfile
* Bun Shell(Taskfile.ts): https://bun.sh/docs/runtime/shell
* usql(queries.sql): https://github.com/xo/usql/
* markdown(README.md): shell code block support
* task.sh: vanilla shell script
* fleet(fleet/run.json): https://www.jetbrains.com/help/fleet/run-configurations.html#reference
* VS Code Tasks: https://code.visualstudio.com/docs/editor/tasks
* zed Tasks: https://zed.dev/docs/tasks
* poe: batteries included task runner that works well with pyproject.toml https://github.com/nat-n/poethepoet
* argc: a Bash-based command runner https://github.com/sigoden/argc
* nur: a task runner based on nu shell https://github.com/ddanier/nur
* cargo-xtask: https://github.com/linux-china/xtask-demo
* go-xtask: https://github.com/linux-china/xtask-go-demo
* grunt: https://gruntjs.com/
* gulp: https://gulpjs.com/

### Bun Shell - Taskfile.ts

[Bun Shell](https://bun.sh/docs/runtime/shell) is a nice feature from Bun to make shell scripting with JavaScript &
TypeScript fun.

Now Task Keeper support `Taskfile.ts` with Bun Shell, and you can use the following code to run tasks:

```typescript
import {$} from "bun";

export async function hello() {
    await $`echo Hello World!`;
}

export async function list_js() {
    await $`ls *.js`;
}
```

Then execute `tk hello` to run the task with Bun Shell.

### Fleet Run configurations

command type support now:

* command
* maven/gradle
* cargo
* go
* maven-run
* cargo-run
* docker-run
* python
* flask
* fastapi
* node
* php

For details, please refer https://www.jetbrains.com/help/fleet/run-configs.html

# Language version detection and PATH

Task Keeper uses `.java-version`, `.node-version`, `.python-version`  files to detect a language version and bound with
local installed SDK.

To make task runner run tasks smoothly, Task Keeper will append the following directories to `PATH` automatically:

* `node-modules/.bin`
* `venv/bin` or `.venv/bin`
* `vendor/bin`
* `bin`
* `.bin`

For example, if you use Python virtual env to manage your project, Task Keeper will add `venv/bin` to `PATH`
automatically, and you don't need to do anything.

```
hello:
  python hello.py
```

**Tips**: you can use double dash to run command with language detection and correct `PATH`,
such as `tk -- mvn spring-boot:run`. To make life easy, and you can use `alias e='tk --'` to create an alias,
then you can run `e mvn spring-boot:run` to run your project.

# Package manager support

### Common tasks for all package managers:

* init: create a project by manager `mvn archetype:generate`, `npm init`, `cargo new xxx` etc
* install: install all dependencies `npm istall`, `dependency:resolve`
* compile: compile source code, not available for some script languages
* build: cargo: `tk build -- --release`, maven: `mvn -DskipTests clean package`, npm: `npm run build`
* start: start project `go run main.go`
* test: run test npm: `npm test`, maven: `mvn test`, cargo: `cargo test`
* doc: generate documentation `mvn javadoc:javadoc`
* deps: list all dependencies
* clean: clean build artifacts, maven: `mvn clean`, cargo: `cargo clean`
* outdated: display outdated dependencies `go list -u -m all`
* update: update outdated dependencies `go get -u`
* add dependency: `tk add dependency` or `tk -D add dependency` or `tk --runner=npm add dependency`

**Attention**: if package manager's task name is in a task runner, and task keeper will not execute package manager's
command.

### Apache Maven

Available

### [Amper]https://amper.org/

Available

### Gradle

Please set up [gradle-versions-plugin](https://github.com/ben-manes/gradle-versions-plugin) for dependency version
management.
You can transparently add the plugin to every Gradle project that you run via a Gradle init script.
`$HOME/.gradle/init.d/plugins.gradle` with the following code:

```
initscript {
  repositories {
     gradlePluginPortal()
  }

  dependencies {
     classpath 'com.github.ben-manes:gradle-versions-plugin:0.53.0'
     classpath 'org.cyclonedx.bom:org.cyclonedx.bom.gradle.plugin:3.0.1'
  }
}

allprojects {
  apply plugin: com.github.benmanes.gradle.versions.VersionsPlugin
  apply plugin: org.cyclonedx.gradle.CyclonedxPlugin

  tasks.named("dependencyUpdates").configure {
    // configure the task, for example wrt. resolution strategies
  }
  
  // https://github.com/CycloneDX/cyclonedx-gradle-plugin/tree/master?tab=readme-ov-file#advanced-configuration
  tasks.named("cyclonedxDirectBom").configure {
     jsonOutput.set(file("build/application.cdx.json"))
     projectType = "application"
  }
}
```

### Sbt

Please add [sbt-updates](https://github.com/rtimush/sbt-updates) and DependencyTreePlugin as global plugins.
`$HOME/.sbt/1.0/plugins/plugins.sbt` with the following code:

```
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.3")
addDependencyTreePlugin
```

### bld

Available.

[bld](https://rife2.com/bld) is a new build system that allows you to write your build logic in pure Java.

### npm

Available

* If `npm-check` command available, `npm-check -u` will be used as `outdated` task.
* `yarn`, `pnpm`, `bun` support

### Cargo

Available. Please install `cargo-tree`, `cargo-outdated`.

### Composer

Available

### Bundler

Available

### Go Module

Available

### CMake

Only support [cmake-conan](https://github.com/conan-io/cmake-conan), and conanfile.txt required.

Default build directory is `cmake-build-debug`, and you override it by `CMAKE_BINARY_DIR=_build tk build`.

### Meson

Available

### Swift

Available. Please install [swift-outdated](https://github.com/kiliankoe/swift-outdated) for `outdated` operation.

### Bazel

Available.

### Python

Available with the following tools:

* [poe]https://github.com/nat-n/poethepoet: `[tool.poe]` detected in `pyproject.toml`
* [uv]https://github.com/astral-sh/uv: if `uv.lock` or `[tool.uv]` detected in `pyproject.toml`
* [Poetry]https://python-poetry.org/: Python packaging and dependency management made easy
* [Invoke]https://www.pyinvoke.org/index.html: a Python library for managing shell-oriented subprocesses and
  organizing executable Python code into CLI-invokable tasks.
* [pipenv]https://pipenv.pypa.io/en/latest/
* requirements.txt

You can use `uv` to install these tools:

- `uv tool install poethepoet`
- `uv tool install poetry`
- `uv tool install invoke`

### Lein

Available.

Please set up antq for outdated dependencies. `$HOME/.lein/profiles.clj`

```clojure
{
 :user
 {:dependencies [[com.github.liquidz/antq "RELEASE"]]
  :aliases {"outdated" ["run" "-m" "antq.core"]}
 }
}
```

### Mix package manager

Available.

### Rebar3 package manager

Available.

### Dart package manager

Available.

### Zig Build System

Available.

### task - Taskfile.yml

if you use JetBrains IDE to edit Taskfile.yml, please add `$schema` comment on top of Taskfile.yml for completion.

```yaml
# $schema: https://taskfile.dev/schema.json
version: '3'

tasks:
  hello:
    cmds:
      - echo 'Hello World from Task!'
    silent: true
```

### xtask for Rust and Golang

* cargo-xtask: https://github.com/linux-china/xtask-demo
* go-xtask: https://github.com/linux-china/xtask-go-demo

### usql with  queries.sql

You can run [usql](https://github.com/xo/usql/) with `queries.sql`:

```sql
-- DSN_URL=sqlite://demo.sqlite3

-- QUERY all_users
SELECT * FROM users;

-- QUERY get_user :one
SELECT * FROM users WHERE id = 1;
```

SQL Types support now:

- QUERY: query data
- EXEC: update data
- MIGRATE: schema migration

### Tasks from README.md

Task keeper will parse README.md and extract tasks with the following code block format:

~~~markdown
```shell {#task_name}
curl https://httpbin.org/ip
```
~~~

Now only `shell`, `sh`, `javascript` and `typescript` are supported.

Run JavaScript/TypeScript by `node` or `deno`:

~~~markdown
```javascript {#task_name .deno}
console.log("hello world");
```
~~~

# Task options/params and global options

* Task options: `tk hello --name linux_china`
* Task params: `tk hello - Jackie`, use `-` to separate task params
* Global options for command:  `tk hello --name Jack -- --verbose`, use `--` double dash to separate global options

# Todo

## Task Runners

* mask(maskfile.md): https://github.com/jacobdeichert/mask
* Pants Build System: https://github.com/pantsbuild/pants

# Package Managers

* realize(.realize.yaml): https://github.com/oxequa/realize

# Version detection

Task Keeper will detect a version configuration file and adjust the environment variables to run tasks.

### Python

`.python-version` is used for version management for [pyenv](https://github.com/pyenv/pyenv).

### Java

`.java-version` is used for version management, and values as following:

* 17: OpenJDK distributed by https://adoptium.net/
* 22.2.r17-grl: GraalVM

Task Keeper will try to find Java from `$HOME/.jbang/cache/jdks` or `$HOME/.sdkman/candidates/java/`.

`.sdkmanrc` support, and set HOME and PATH environment variables automatically. Please refer https://sdkman.io/usage#env
for detail.

### Node.js

`.node-version` is used for Node.js version management, and values as following:

* 18: match a major version of Node.js
* 16.16.0: match version of Node.js

Task Keeper will try to find Node from `$HOME/.nvm/versions/node` or `$HOME/.volta/tools/image/node`.

# sq(Squirrel)

sq is a command-line snippets keeper to manage cli snippets, and it's based on Just command runner.

- List snippets: `sq list`
- Add snippet: `sq add snippet_name`
- Edit snippet: `sq edit snippet_name`. sq uses `EDITOR` environment variable to open snippets justfile.
- Run snippet: `sq snippet_name`

Snippets justfile: `$HOME/.sk/snippets.just`.

# Notification

Please set `TK_TASK_ID` for task unique name, and Task Keeper will send notification when the task finished.

* NATS: `NATS_URL` environment variable to set NATS server URL, and `task-keeper` as subject.
* Output saved on OSS: `TK_TASK_ID` environment variable to set OSS object name, such as `tasks/random-uuid`.
  to set OSS object name.
    - `S3_ENDPOINT_URL`: such as `http://minio.example.com:9000`
    - `S3_ACCESS_KEY`: access key for S3
    - `S3_SECRET_KEY`: access secret for S3
    - `S3_BUCKET`: bucket name for S3
    - `S3_REGION`: region for S3, such as `us-east-1`
    - `S3_VIRTUAL_STYLE`: for MinIO, you don't need to set this, but for Aliyun OSS, you need to set this to `true` to
      use
      virtual-hosted-style requests.

**Tips**: Notification is useful for CI/CD.

# How to add a new task runner?

* Create a new rust file in `src/runners` directory, such as `myrunner.rs`
* Add metadata for the runner in `src/runners/mod.rs`
* Add runner's tasks in `src/keeper.rs`
* Add runner's diagnose in `src/main.rs`

# References

* The Ultimate Guide to Gemfile and
  Gemfile.lock: https://blog.saeloun.com/2022/08/16/understanding_gemfile_and_gemfile_lock
* Your Makefiles are wrong: https://tech.davis-hansson.com/p/make/
* Learn Makefiles With the tastiest examples: https://makefiletutorial.com/
* Taskfile: a modern alternative to Makefile - https://itnext.io/taskfile-a-modern-alternative-to-makefile-6b3f545f77bd
* Task Runner Census 2025: https://aleyan.com/blog/2025-task-runners-census/
* [vstask]https://lib.rs/crates/vstask: A command-line tool to run VS Code tasks from the terminal.

# Task scripts demo in Markdown

```shell {#demo}
$ curl https://httpbin.org/get
$ curl -X POST https://httpbin.org/post
```

```shell {#myip desc="get my internet ip address"}
curl --silent https://httpbin.org/ip | jq '.origin'
```

```shell {#demo2}
curl https://httpbin.org/ip \
    --user-agent "Task Keeper/0.1.0" \
    --silent
curl https://httpbin.org/headers
```

```typescript {#js2 .deno}
let name: string = "linux_china";
console.log(name);
```