asgard 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/docs/task-files.md CHANGED
@@ -1,14 +1,12 @@
1
1
  # Task Files
2
2
 
3
- Asgard uses a convention-based file discovery system. A hidden `.loki` file marks the project root; `*.loki` files in the same directory contain tasks that are loaded on demand via `--auto-load`.
3
+ Asgard uses a convention-based file discovery system. A hidden `.loki` file marks the project root. Everything else — loading sibling files, shared task libraries, monorepo-wide tasks is controlled explicitly from inside your `.loki` file using the `import` and `import_up` Kernel methods.
4
4
 
5
5
  ---
6
6
 
7
7
  ## The `.loki` Root Marker
8
8
 
9
- When you run `asgard`, it searches for a `.loki` file starting in the current working directory and walking upward through parent directories until it finds one or reaches the filesystem root. The first `.loki` file found marks the project root. It may also contain the main task definitions for the project.
10
-
11
- This means you can run `asgard` from any subdirectory of your project and it will find your tasks:
9
+ When you run `asgard`, it searches for a `.loki` file starting in the current working directory and walking upward through parent directories until it finds one or reaches the filesystem root. The first `.loki` file found marks the project root and is the only file Asgard loads automatically.
12
10
 
13
11
  ```
14
12
  myproject/
@@ -18,143 +16,223 @@ myproject/
18
16
  # asgard still works from here
19
17
  ```
20
18
 
21
- !!! note
22
- The `.loki` file can be completely empty. Its presence alone is sufficient to mark the project root. If it is empty and you have `*.loki` files, you must pass `--auto-load` when running `asgard` — otherwise Asgard has nothing to do.
19
+ The `.loki` file can be completely empty — its presence alone marks the project root. It can also contain task definitions, `import` calls, or any valid Ruby.
23
20
 
24
21
  ---
25
22
 
26
- ## Loading `*.loki` Files with `--auto-load`
23
+ ## Loading Files with `import`
24
+
25
+ `import` is a Kernel method available everywhere in Ruby — at the top level of `.loki` files, inside class bodies, and inside task method bodies. It loads `.loki` files with `require`-like idempotency: a file is loaded at most once per process, no matter how many times `import` is called with the same path.
27
26
 
28
- By default, `asgard` only loads `.loki`. To also load `*.loki` files, pass `--auto-load`:
27
+ ### Single file by absolute path
29
28
 
30
- ```bash
31
- asgard --auto-load <task>
29
+ ```ruby
30
+ import "/home/shared/gem_tasks.loki"
32
31
  ```
33
32
 
34
- When `--auto-load` is active, Asgard loads all files matching `*.loki` in the same directory in alphabetical order before loading `.loki`. Each file typically reopens `class Tasks` to add more tasks. The `*.loki` glob specifically excludes `.loki` (note the leading dot) — the entry point is always loaded last.
33
+ ### Single file by relative path
35
34
 
36
- **Load order when `--auto-load` is passed:**
35
+ Relative paths are resolved relative to the **caller's file location**, like `require_relative`:
37
36
 
38
- 1. All `*.loki` files alphabetically (e.g., `build.loki`, `deploy.loki`, `test.loki`)
39
- 2. `.loki` itself (the entry point)
37
+ ```ruby
38
+ # .loki relative to this file's directory
39
+ import "build.loki"
40
+ import "../shared/gem_tasks.loki"
41
+ import "tasks/ci.loki"
42
+ ```
40
43
 
41
- This means any tasks, classes, or variables defined in `*.loki` files are available when `.loki` runs.
44
+ ### All files in the same directory (glob)
42
45
 
43
- ### Task Name Overloading
46
+ ```ruby
47
+ import "*.loki" # all *.loki files in the same directory as the calling file
48
+ ```
44
49
 
45
- Because all `*.loki` files reopen the same `class Tasks`, it is possibleby accident or by design — for two files to define a method with the same name. This is **task overloading**. Ruby's class reopening semantics apply: the last definition loaded wins, silently replacing the earlier one.
50
+ `*.loki` never matches `.loki` (the dotfile entry point) — Ruby's `Dir.glob` excludes dotfiles from `*` patterns by default.
46
51
 
47
- Three things are overwritten when a task name is reused:
52
+ ### All files recursively (recursive glob)
48
53
 
49
- | What | Effect |
50
- |---|---|
51
- | `def method_name` | The Ruby method body — the earlier implementation is gone |
52
- | `desc` metadata | Thor registers the new usage/description string, discarding the old one |
53
- | `depends_on` stages | `method_added` captures the pending deps for the new definition; the earlier dep chain is replaced |
54
+ ```ruby
55
+ import "**/*.loki" # every .loki file in this directory and all subdirectories
56
+ ```
54
57
 
55
- **Accidental overloading** is a silent bug. If `build.loki` and `ci.loki` both define `def build`, only the alphabetically-later file's version runs — with no warning. Keep task names unique across files, or move shared tasks into a dedicated file loaded first.
58
+ ### Specific named files
56
59
 
57
- !!! warning
58
- There is no runtime error when a task is overloaded. If a task is not behaving as expected, check whether another `*.loki` file defines the same method name and loads after it.
60
+ ```ruby
61
+ import "gem_tasks.loki"
62
+ import "ci_tasks.loki"
63
+ ```
59
64
 
60
- **Intentional overloading** lets you extend or wrap a task defined in an earlier file. Use `alias_method` inside a `no_commands` block to preserve the original implementation under a private name, then redefine the task to call it:
65
+ ### Combining patterns
61
66
 
62
67
  ```ruby
63
- # build.loki (loaded first)
68
+ # .loki
69
+ import "*.loki" # load all siblings
70
+ import "../shared/*.loki" # load a parent-level shared library
71
+ ```
72
+
73
+ ### Typical `.loki` entry point
74
+
75
+ ```ruby
76
+ # .loki
77
+ import "*.loki" # load all sibling task files
78
+
64
79
  class Tasks
65
- desc "build", "Compile the project"
66
- def build
67
- sh "rake build"
68
- end
80
+ # any top-level task definitions or overrides
69
81
  end
70
82
  ```
71
83
 
84
+ ### Return value
85
+
86
+ `import` returns `true` if at least one file was newly loaded, `false` if all files were already loaded or no glob pattern matched any file. If a specific (non-glob) file does not exist, `import` raises `LoadError`.
87
+
72
88
  ```ruby
73
- # postbuild.loki (loaded after build.loki, alphabetically)
74
- class Tasks
75
- # Preserve the original under a private name before overwriting it.
76
- no_commands { alias_method :_build_original, :build }
89
+ import("gem_tasks.loki") ? "loaded now" : "already loaded"
90
+ ```
77
91
 
78
- desc "build", "Compile the project and copy assets"
79
- def build
80
- _build_original # runs the original sh "rake build"
81
- sh "cp -r dist/ public/" # adds post-build step
82
- end
92
+ ---
93
+
94
+ ## Finding Files with `loki_up`
95
+
96
+ `loki_up(name = ".loki")` searches `Dir.pwd` and each ancestor directory for a file with the given name, returning its absolute path or `nil`. It does **not** load the file — it only finds it.
97
+
98
+ Despite the name, `loki_up` is not limited to `.loki` files — it will locate any file by name. This makes it useful for finding shared config files, `.env` files, or any other resource that lives somewhere up the directory tree:
99
+
100
+ ```ruby
101
+ loki_up # finds .loki (the project root marker)
102
+ loki_up("gem_tasks.loki") # finds gem_tasks.loki in CWD or any ancestor
103
+ loki_up(".env") # finds the nearest .env file up the tree
104
+ loki_up("VERSION") # finds a VERSION file in CWD or any ancestor
105
+ ```
106
+
107
+ Use `loki_up` when you need the path for other purposes, or to check whether a file exists before deciding to load it:
108
+
109
+ ```ruby
110
+ if (path = loki_up("gem_tasks.loki"))
111
+ import path
83
112
  end
113
+
114
+ # Pass the located .env to dotenv — works from any subdirectory
115
+ dotenv loki_up(".env") || ".env"
84
116
  ```
85
117
 
86
- `no_commands` prevents `_build_original` from appearing as a CLI command. The aliased method retains the original's full body including any `sh` calls, `var` access, and private helper calls.
118
+ `loki_up` accepts exact filenames only. Glob patterns are not expanded by `loki_up` use `import_up` for glob-aware ancestor search.
87
119
 
88
- !!! tip
89
- The `_` prefix on the alias name (`_build_original`) follows Asgard's convention for non-user-facing methods and reinforces that it is an implementation detail, not a task to be invoked directly.
120
+ ---
90
121
 
91
- !!! warning "Prefer `depends_on` over intentional overloading"
92
- Using `alias_method` to bolt post-task behaviour onto an existing task is a code smell. It is fragile (load-order dependent), obscures intent, and makes the dependency chain invisible to Asgard's cycle-detection and deduplication logic.
122
+ ## Loading Files Found up the Tree with `import_up`
93
123
 
94
- The idiomatic Asgard solution is to express the relationship explicitly with `depends_on`:
124
+ `import_up(name = ".loki")` combines `loki_up` and `import` into a single call. It finds the file (or files) up the ancestor chain and loads them.
95
125
 
96
- ```ruby
97
- # build.loki
98
- class Tasks
99
- desc "build", "Compile the project"
100
- def build = sh "rake build"
126
+ ### Exact filename
101
127
 
102
- desc "copy_assets", "Copy build output to public/"
103
- def copy_assets = sh "cp -r dist/ public/"
128
+ ```ruby
129
+ import_up "gem_tasks.loki"
130
+ ```
104
131
 
105
- depends_on :build, :copy_assets
106
- desc "build_all", "Compile and copy assets"
107
- def build_all; end
108
- end
109
- ```
132
+ Walks up from `Dir.pwd` until it finds `gem_tasks.loki`, then loads it. Returns `false` if not found anywhere.
133
+
134
+ ### Glob pattern
135
+
136
+ ```ruby
137
+ import_up "*.loki"
138
+ ```
139
+
140
+ Walks up from `Dir.pwd` and stops at the **first ancestor directory** that contains any `*.loki` files, loading all of them. It does not aggregate matches from multiple levels — it loads only the nearest match, then stops.
141
+
142
+ ```
143
+ ~/sandbox/
144
+ gem_tasks.loki ← loaded by import_up("*.loki") from ~/sandbox/myproject/sub/
145
+ ci_tasks.loki ← also loaded — same directory as the first match
146
+ myproject/
147
+ .loki
148
+ sub/
149
+ # Dir.pwd here; import_up("*.loki") finds ~/sandbox/*.loki files
150
+ ```
151
+
152
+ ### Return value
153
+
154
+ Returns `true` if any file was newly loaded, `false` if the file was not found or was already loaded.
155
+
156
+ ### Conditional load
157
+
158
+ Since `import_up` returns `false` when a file is not found (rather than raising), it composes naturally with `||`:
110
159
 
111
- This approach is transparent, testable, and benefits from Asgard's deduplication — `build` will never run twice even if multiple tasks declare it as a dependency.
160
+ ```ruby
161
+ import_up("project_tasks.loki") || import_up("gem_tasks.loki")
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Idempotency
167
+
168
+ Both `import` and `import_up` track loaded files in Ruby's `$LOADED_FEATURES`. A second call with the same path is a no-op and returns `false`. This means:
169
+
170
+ - You can call `import "*.loki"` from both `.loki` and a shared task file without double-loading.
171
+ - `import_up("gem_tasks.loki")` from two different projects in the same process each load their nearest match once.
172
+ - Swapping `require` for `import` in a `.loki` file gives the same once-per-process guarantee.
112
173
 
113
174
  ---
114
175
 
115
- ## Single File Layout
176
+ ## Verbose and Debug Feedback
116
177
 
117
- The simplest structure: all tasks in `.loki`, nothing else:
178
+ `import` and `import_up` emit diagnostic messages to stderr when the `verbose?` or `debug?` flags are active (set via `--verbose` or `--debug` on the CLI, or by setting `$VERBOSE`/`$DEBUG` directly):
179
+
180
+ | Flag | `import` output | `import_up` output |
181
+ |---|---|---|
182
+ | `--verbose` | Prints each file path as it is loaded | Prints `name → /full/path` when found |
183
+ | `--debug` | Same as verbose, plus prints a skip message for already-loaded files | Same as verbose, plus prints `name not found` when the search comes up empty |
118
184
 
119
185
  ```
120
- myproject/
121
- .loki
186
+ $ asgard --verbose build
187
+ import: /home/user/myproject/build.loki
188
+ import: /home/user/myproject/test.loki
122
189
  ```
123
190
 
191
+ ---
192
+
193
+ ## Loading Patterns
194
+
195
+ ### Single-file project
196
+
197
+ All tasks in `.loki`, nothing else:
198
+
124
199
  ```ruby
125
200
  # .loki
126
201
  class Tasks
127
- var :app, "myapp"
202
+ @@app ||= "myapp".freeze
128
203
 
129
- desc "build", "Compile the project"
204
+ desc "Compile the project"
130
205
  def build = sh "rake build"
131
206
 
132
- desc "test", "Run the test suite"
207
+ desc "Run the test suite"
133
208
  def test = sh "rake test"
134
209
 
135
- desc "release", "Build and push the gem"
136
- def release = sh "gem push pkg/#{app}-*.gem"
210
+ desc "Build and push the gem"
211
+ def release = sh "gem push pkg/#{@@app}-*.gem"
137
212
  end
138
213
  ```
139
214
 
140
- ---
141
-
142
- ## Multi-File Layout
215
+ ### Multi-file project
143
216
 
144
- Split tasks across files by concern each file reopens `class Tasks`:
217
+ Split tasks across files by concern. Load them all from `.loki` with a glob:
145
218
 
146
219
  ```
147
220
  myproject/
148
- .loki ← entry point (may be empty or contain top-level task)
149
- build.loki ← build-related tasks
150
- deploy.loki ← deployment tasks
221
+ .loki ← entry point; imports siblings
222
+ build.loki ← build tasks
223
+ deploy.loki ← deploy tasks
151
224
  test.loki ← test tasks
152
225
  ```
153
226
 
227
+ ```ruby
228
+ # .loki
229
+ import "*.loki"
230
+ ```
231
+
154
232
  ```ruby
155
233
  # build.loki
156
234
  class Tasks
157
- desc "build", "Compile the project"
235
+ desc "Compile the project"
158
236
  def build = sh "rake build"
159
237
  end
160
238
  ```
@@ -163,7 +241,7 @@ end
163
241
  # test.loki
164
242
  class Tasks
165
243
  depends_on :build
166
- desc "test", "Run the test suite"
244
+ desc "Run the test suite"
167
245
  def test = sh "bundle exec rake test"
168
246
  end
169
247
  ```
@@ -172,83 +250,158 @@ end
172
250
  # deploy.loki
173
251
  class Tasks
174
252
  depends_on :test
175
- desc "deploy", "Deploy to production"
253
+ desc "Deploy to production"
176
254
  def deploy = sh "cap production deploy"
177
255
  end
178
256
  ```
179
257
 
180
- ```ruby
181
- # .loki — can be empty, or can register subcommands, add top-level vars, etc.
182
- ```
258
+ Files loaded via glob are sorted alphabetically by `Dir.glob`, so `build.loki` loads before `test.loki`. Tasks defined in earlier files are available to later files via `depends_on`.
183
259
 
184
- Load order: `build.loki` → `deploy.loki` → `test.loki` → `.loki`.
260
+ ### Controlled load order
185
261
 
186
- !!! tip
187
- When `--auto-load` is used, `*.loki` files are sorted alphabetically, so `build.loki` loads before `test.loki`, which means `depends_on :build` in `test.loki` correctly references a task that already exists.
262
+ When alphabetical order does not match your dependency order, import explicitly:
188
263
 
189
- ---
264
+ ```ruby
265
+ # .loki
266
+ import "infra.loki" # must be first
267
+ import "build.loki" # depends on infra
268
+ import "deploy.loki" # depends on build
269
+ ```
190
270
 
191
- ## Explicit Loading
271
+ ### Shared task library in a monorepo
192
272
 
193
- You can explicitly load files from `.loki` using `require_relative`. This gives you control over load order, and lets you load plain Ruby files that are not `.loki` files:
273
+ Place shared tasks in a parent directory and load them from any sub-project:
274
+
275
+ ```
276
+ ~/sandbox/
277
+ gem_tasks.loki ← shared: build, install, release tasks for any gem
278
+ myproject/
279
+ .loki ← loads gem_tasks.loki via import_up
280
+ other_project/
281
+ .loki ← also loads gem_tasks.loki via import_up
282
+ ```
194
283
 
195
284
  ```ruby
196
- # .loki
197
- require_relative "shared/helpers"
198
- require_relative "ci.loki"
285
+ # myproject/.loki
286
+ import_up "gem_tasks.loki" # finds ~/sandbox/gem_tasks.loki
199
287
 
200
288
  class Tasks
201
- include BuildHelpers # defined in shared/helpers.rb
202
-
203
- desc "full-ci", "Complete CI run"
204
- def full_ci = sh "echo 'full CI complete'"
289
+ # project-specific overrides here
205
290
  end
206
291
  ```
207
292
 
208
- Explicitly required files are loaded before `.loki`'s own class body is evaluated. Files loaded via `require_relative` are **not** re-loaded by the alphabetical glob — Ruby's `require_relative` marks them as loaded in `$LOADED_FEATURES`.
293
+ ### Conditional shared library
209
294
 
210
- !!! warning
211
- If you `require_relative "ci.loki"` from `.loki` and also run `asgard --auto-load`, Asgard's glob will also load `ci.loki`. To prevent double-loading, either: (a) put explicitly loaded files in a subdirectory outside the alphabetical sweep, or (b) rely solely on `--auto-load` without `require_relative`.
295
+ ```ruby
296
+ # .loki
297
+ import_up("ci_tasks.loki") || import_up("gem_tasks.loki")
298
+ ```
212
299
 
213
- ---
300
+ Loads `ci_tasks.loki` if found up the tree, otherwise falls back to `gem_tasks.loki`.
214
301
 
215
- ## Subcommands Across Files
302
+ ### Subcommand classes across files
216
303
 
217
- Subcommand classes defined in separate `*.loki` files are available in `.loki` when `--auto-load` is used, because the `*.loki` files load first:
304
+ Define subcommand classes in separate files and register them in `.loki`:
218
305
 
219
306
  ```
220
307
  myproject/
221
- .loki ← registers subcommands
222
- db_subcommands.loki ← defines DBCommands
223
- server_subcommands.loki ← defines ServerCommands
308
+ .loki
309
+ db.loki
310
+ server.loki
224
311
  ```
225
312
 
226
313
  ```ruby
227
- # db_subcommands.loki
314
+ # db.loki
228
315
  class DBCommands < Tasks
229
- desc "migrate", "Run migrations"
316
+ desc "Run migrations"
230
317
  def migrate = sh "rails db:migrate"
231
318
  end
319
+ ```
232
320
 
233
- # server_subcommands.loki
321
+ ```ruby
322
+ # server.loki
234
323
  class ServerCommands < Tasks
235
- desc "start", "Start the server"
324
+ desc "Start the server"
236
325
  def start = sh "rails server"
237
326
  end
327
+ ```
238
328
 
329
+ ```ruby
239
330
  # .loki
331
+ import "*.loki" # db.loki and server.loki load first
332
+
240
333
  class Tasks
241
334
  desc "db SUBCOMMAND", "Manage the database"; subcommand "db", DBCommands
242
335
  desc "server SUBCOMMAND", "Manage the server"; subcommand "server", ServerCommands
243
336
  end
244
337
  ```
245
338
 
339
+ Subcommand classes are available in `.loki` because siblings loaded via `import "*.loki"` execute before `.loki`'s own class body.
340
+
341
+ ---
342
+
343
+ ## Task Name Overloading
344
+
345
+ Because all `*.loki` files reopen the same `class Tasks`, two files can define a method with the same name. Ruby's class reopening semantics apply: the last definition loaded wins, silently replacing the earlier one.
346
+
347
+ Three things are overwritten when a task name is reused:
348
+
349
+ | What | Effect |
350
+ |---|---|
351
+ | `def method_name` | The Ruby method body — the earlier implementation is gone |
352
+ | `desc` metadata | Thor registers the new usage/description string, discarding the old one |
353
+ | `depends_on` stages | `method_added` captures the pending deps for the new definition; the earlier dep chain is replaced |
354
+
355
+ **Accidental overloading** is a silent bug. Keep task names unique across files.
356
+
357
+ !!! warning
358
+ There is no runtime error when a task is overloaded. If a task is not behaving as expected, check whether another `.loki` file defines the same method name and loads after it.
359
+
360
+ **Intentional overloading** lets you extend a task defined in an earlier file using `alias_method`:
361
+
362
+ ```ruby
363
+ # build.loki (loaded first)
364
+ class Tasks
365
+ desc "Compile the project"
366
+ def build = sh "rake build"
367
+ end
368
+
369
+ # postbuild.loki (loaded after build.loki, alphabetically)
370
+ class Tasks
371
+ no_commands { alias_method :_build_original, :build }
372
+
373
+ desc "Compile the project and copy assets"
374
+ def build
375
+ _build_original
376
+ sh "cp -r dist/ public/"
377
+ end
378
+ end
379
+ ```
380
+
381
+ !!! warning "Prefer `depends_on` over intentional overloading"
382
+ Using `alias_method` to bolt post-task behaviour onto an existing task is fragile and load-order dependent. The idiomatic alternative is `depends_on`:
383
+
384
+ ```ruby
385
+ class Tasks
386
+ desc "Compile the project"
387
+ def build = sh "rake build"
388
+
389
+ desc "Copy build output to public/"
390
+ def copy_assets = sh "cp -r dist/ public/"
391
+
392
+ depends_on :build, :copy_assets
393
+ desc "Compile and copy assets"
394
+ def build_all; end
395
+ end
396
+ ```
397
+
246
398
  ---
247
399
 
248
400
  ## Summary of Loading Rules
249
401
 
250
- | File | When loaded | Purpose |
251
- |---|---|---|
252
- | `.loki` | After all `*.loki` | Project root marker; entry point |
253
- | `*.loki` | When `--auto-load` is passed, alphabetically before `.loki` | Task definitions that reopen `class Tasks` |
254
- | `require_relative` targets | At the point of the `require_relative` call | Shared helpers, explicit task files |
402
+ | Method | Finds? | Loads? | Glob? | Ancestor search? |
403
+ |---|---|---|---|---|
404
+ | `loki_up(name)` | Yes | No | No | Yes |
405
+ | `import(path)` | No | Yes | Yes | No |
406
+ | `import_up(name)` | Yes | Yes | Yes | Yes |
407
+ | Asgard's `run!` | Yes | `.loki` only | No | Yes |
data/docs/tasks.md CHANGED
@@ -10,12 +10,12 @@ A task with no parameters and no options:
10
10
 
11
11
  ```ruby
12
12
  class Tasks
13
- desc "hello", "Say hello"
14
- def hello = sh 'echo "Hello, World!"'
13
+ desc "Say hello"
14
+ def hello = puts "Hello, World!"
15
15
  end
16
16
  ```
17
17
 
18
- `desc` takes two arguments: the usage string and the one-line description shown in `asgard help`.
18
+ `desc` accepts either one or two strings. With one argument, the description is shown in `asgard help` and the usage string defaults to the method name. Pass two arguments when the usage string needs to document parameters — `desc "greet NAME", "Greet NAME by name"`.
19
19
 
20
20
  ```bash
21
21
  asgard hello
@@ -31,7 +31,7 @@ Positional parameters are declared directly in the method signature. Document th
31
31
  class Tasks
32
32
  desc "greet NAME", "Greet NAME; omit NAME to greet the world"
33
33
  def greet(name = "World")
34
- sh "echo 'Hello, #{name}!'"
34
+ puts "Hello, #{name}!"
35
35
  end
36
36
  end
37
37
  ```
@@ -51,7 +51,7 @@ Use `method_option` (alias: `option`) for named flags. Access them inside the me
51
51
 
52
52
  ```ruby
53
53
  class Tasks
54
- desc "compile", "Compile the project"
54
+ desc "Compile the project"
55
55
  option :output, aliases: "-o", type: :string, default: "dist/", desc: "Output directory"
56
56
  option :verbose, aliases: "-v", type: :boolean, default: false, desc: "Enable verbose output"
57
57
  option :jobs, aliases: "-j", type: :numeric, default: 1, desc: "Number of parallel jobs"
@@ -117,7 +117,7 @@ asgard deploy production --strategy blue-green
117
117
 
118
118
  ```ruby
119
119
  class Tasks
120
- desc "build", "Build the project"
120
+ desc "Build the project"
121
121
  option :env,
122
122
  type: :string,
123
123
  default: "development",
@@ -139,7 +139,7 @@ Thor validates the value against the enum and shows a helpful error if it doesn'
139
139
 
140
140
  ```ruby
141
141
  class Tasks
142
- desc "wait", "Wait for a service to become available"
142
+ desc "Wait for a service to become available"
143
143
  option :timeout, type: :numeric, default: 30, banner: "SECONDS", desc: "Give up after SECONDS"
144
144
  def wait
145
145
  sh "wait-for-it --timeout #{options[:timeout]}"
@@ -169,7 +169,7 @@ class Tasks
169
169
  asgard report --format json --output report.json\x5
170
170
  asgard report --format text
171
171
  DESC
172
- desc "report", "Generate a project report"
172
+ desc "Generate a project report"
173
173
  option :format, type: :string, default: "text", enum: %w[text html json], desc: "Output format"
174
174
  option :since, type: :string, banner: "DATE", desc: "Limit to changes after DATE"
175
175
  def report
@@ -191,7 +191,7 @@ end
191
191
  class Tasks
192
192
  default_task :greet
193
193
 
194
- desc "greet", "Say hello (runs by default)"
194
+ desc "Say hello (runs by default)"
195
195
  def greet
196
196
  puts "Hello from Asgard!"
197
197
  end
@@ -202,6 +202,8 @@ end
202
202
  asgard # same as: asgard greet
203
203
  ```
204
204
 
205
+ Without `default_task`, running `asgard` with no arguments displays the help message.
206
+
205
207
  ---
206
208
 
207
209
  ## Command Aliases
@@ -215,13 +217,13 @@ class Tasks
215
217
  map "t" => "test"
216
218
  map "b" => "build"
217
219
 
218
- desc "version", "Print the version"
220
+ desc "Print the version"
219
221
  def version = puts Asgard::VERSION
220
222
 
221
- desc "test", "Run tests"
223
+ desc "Run tests"
222
224
  def test = sh "bundle exec rake test"
223
225
 
224
- desc "build", "Build the gem"
226
+ desc "Build the gem"
225
227
  def build = sh "bundle exec rake build"
226
228
  end
227
229
  ```
@@ -249,14 +251,14 @@ class Tasks
249
251
  desc: "Name to greet"
250
252
 
251
253
  desc "hello NAME", "Say hello to NAME"
252
- def hello = sh "echo 'Hello, #{name}!'"
254
+ def hello = puts "Hello, #{name}!"
253
255
  end
254
256
  ```
255
257
 
256
258
  For most multi-task `.loki` files, the simpler positional default pattern is safer:
257
259
 
258
260
  ```ruby
259
- def hello(name = "World") = sh "echo 'Hello, #{name}!'"
261
+ def hello(name = "World") = puts "Hello, #{name}!"
260
262
  ```
261
263
 
262
264
  ---
@@ -267,7 +269,7 @@ def hello(name = "World") = sh "echo 'Hello, #{name}!'"
267
269
 
268
270
  ```ruby
269
271
  class Tasks
270
- desc "build", "Compile the project"
272
+ desc "Compile the project"
271
273
  def build
272
274
  puts "Revision: #{current_sha}"
273
275
  sh "rake build"