asgard 0.3.2 → 0.3.3
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.
- checksums.yaml +4 -4
- data/.loki +7 -1
- data/.reek.yml +3 -0
- data/CHANGELOG.md +16 -1
- data/README.md +43 -2
- data/doc_tasks.loki +21 -0
- data/docs/api.md +7 -5
- data/docs/changelog.md +3 -1
- data/docs/dependencies.md +18 -7
- data/docs/shell.md +30 -1
- data/docs/tasks.md +40 -0
- data/examples/depends_on_block/bad/.loki +51 -0
- data/examples/depends_on_block/good/.loki +83 -0
- data/examples/kitchen_sink.loki +13 -0
- data/lib/asgard/base/dependency_graph.rb +43 -7
- data/lib/asgard/base/task_dsl.rb +1 -2
- data/lib/asgard/shell.rb +21 -8
- data/lib/asgard/version.rb +1 -1
- data/lib/asgard.rb +2 -0
- data/quality_rails.loki +3 -2
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f88b550d323b26b075a45f6a7d6e8781b7d54d062c1f724de25b2d253d2f3e4
|
|
4
|
+
data.tar.gz: 5c2a05803003793e14ad08f0453d0a549cb6f2f0202b31c38c600303a539d66b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d9f2d214246e0960b2a2a4ca790818a8f57edfd45c17f0fca6edf1bcf486cc096d0331818d899c5ca929f9fcec404329744403a0f03aa2fe0faafb70b13ae172
|
|
7
|
+
data.tar.gz: e35a52164438b5734cdf86ee4f7210e2169edb391e8478cddd1548698691d3bddea5d703ef113e34bcdd1caeb0041d9851ec94b8ee7840b9115a0ca821f03f49
|
data/.loki
CHANGED
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
# Task is pre-defined by the gem — just reopen it to add tasks.
|
|
4
4
|
|
|
5
5
|
import "quality.loki"
|
|
6
|
-
|
|
6
|
+
# asgard runs as a separate process outside the app it's checking, so
|
|
7
|
+
# `defined?(Rails)` never sees the app's Rails constant. RAILS_ROOT — set in
|
|
8
|
+
# a Rails repo's own .envrc (`export RAILS_ROOT=$RR`, after RR is defined)
|
|
9
|
+
# — is the signal instead.
|
|
10
|
+
import "quality_rails.loki" if ENV["RAILS_ROOT"]
|
|
7
11
|
|
|
8
12
|
import "gem_tasks.loki"
|
|
9
13
|
import "git.loki"
|
|
14
|
+
import "doc_tasks.loki"
|
|
15
|
+
|
|
10
16
|
import "xyzzy.loki" # An example for the --doctor flag
|
|
11
17
|
|
|
12
18
|
class Tasks
|
data/.reek.yml
CHANGED
|
@@ -75,6 +75,8 @@ detectors:
|
|
|
75
75
|
- Asgard::Base::Registry#inherited # inherited(subclass) configuring subclass is the whole point
|
|
76
76
|
- Asgard::Base::DependencyGraph#_normalize_pending_deps # small transform of its own argument
|
|
77
77
|
- Asgard::Base::DependencyGraph#_call_dep_proc # rescue => e; e.class/e.message is inherent
|
|
78
|
+
- Asgard::Base::DependencyGraph#_validate_dep_stage! # pure shape check of its own argument
|
|
79
|
+
- Asgard::Base::DependencyGraph#_validate_dep_leaf! # pure shape check of its own argument
|
|
78
80
|
- Asgard::Doctor::TaskSections#class_task_file_map
|
|
79
81
|
- Asgard::Doctor::TaskSections#relative_path
|
|
80
82
|
- Asgard::Doctor::TaskSections#task_status
|
|
@@ -104,6 +106,7 @@ detectors:
|
|
|
104
106
|
- Asgard::Base::DependencyGraph#_build_dep_graph # genuinely pure, but tightly coupled to this graph model
|
|
105
107
|
- Asgard::Base::Dispatch#dep_results # thread-local by design, not self — that's the whole point
|
|
106
108
|
- Asgard::Base::Dispatch#with_dep_results # same: stashes results on Thread.current, not self
|
|
109
|
+
- Asgard::Shell#shell_argv # extracted pure on purpose so it's testable without invoking system/exec
|
|
107
110
|
|
|
108
111
|
exclude_paths:
|
|
109
112
|
- test
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [0.3.
|
|
8
|
+
## [0.3.3] - 2026-08-27
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
@@ -159,6 +159,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
159
159
|
|
|
160
160
|
- **`examples/bad.loki`** — a worked demonstration of the race the fix above addresses: 4 parallel workers read-modify-write a shared `@hits` counter directly (the anti-pattern `quality.loki` used to have), reliably losing updates. Kept as a contrast example for what `dep_result`/`dep_results` is for.
|
|
161
161
|
|
|
162
|
+
### Added (continued 5)
|
|
163
|
+
|
|
164
|
+
- **`sh(script, exec: true)`** — hands the command the asgard process itself via `Kernel.exec` instead of forking. For a task's final, long-running command (a dev server, a REPL) this replaces the ruby process outright, so nothing sits resident in memory behind it and Ctrl-C is handled directly by the command instead of unwinding back through asgard. `doc_tasks.loki`'s `doc_server` task (`sh "mkdocs serve", exec: true`) is the motivating example. See [Shell Helpers](docs/shell.md#handing-off-with-exec).
|
|
165
|
+
- **`bootstrap` and `env_info` tasks in `kitchen_sink.loki`** — demonstrate `sh` with a multi-line heredoc (routed through `bash -c`) and a single-line command, respectively.
|
|
166
|
+
|
|
167
|
+
### Fixed (continued 3)
|
|
168
|
+
|
|
169
|
+
- **Ctrl-C during a running `sh` command printed a raw `Interrupt` backtrace** — SIGINT hits the whole foreground process group, so asgard's own ruby process raised `Interrupt` independently of whatever the shelled-out command did with the signal, and it went uncaught, unwinding through Thor and printing a stack trace before exiting. `Asgard.run!` now rescues `Interrupt` and exits with the conventional 130 status.
|
|
170
|
+
|
|
171
|
+
### Added (continued 6)
|
|
172
|
+
|
|
173
|
+
- **`depends_on` accepts a block in addition to a Proc/lambda** — `depends_on { ... }` (or `depends_on do ... end` for a block spanning multiple statements) defers resolution to `validate_deps!` exactly like the existing sole-Proc/lambda form; the two are interchangeable. `depends_on` still accepts task arguments *or* a block, never both — combining them raises `Asgard::Error`. See [Dynamic Dependencies](docs/dependencies.md#dynamic-dependencies-proc-block-form).
|
|
174
|
+
- **The resolved Proc/lambda/block result is now shape-validated** — once `validate_deps!` calls it, the return value must be an `Array` of stages, each a `Symbol`/`String` (sequential) or an `Array` of `Symbol`/`String` (parallel group), nested no deeper than that. A bad shape (wrong type, an invalid stage, a non-Symbol/String leaf, or nesting more than one level deep) now raises `Asgard::Error` naming the task and the offending value, instead of failing later with an opaque `NoMethodError`.
|
|
175
|
+
- **`examples/depends_on_block/good/` and `examples/depends_on_block/bad/`** — two self-contained example projects (each its own `.loki` root, isolated from the main `examples/` tree) demonstrating the block form: `good/` covers single-line `{ ... }`, `do...end`, and a mixed sequential+parallel shape; `bad/` demonstrates the double-wrapped-array mistake that the new shape validation catches, with the exact `Asgard::Error` message it produces.
|
|
176
|
+
|
|
162
177
|
## [0.2.0] - 2026-05-29
|
|
163
178
|
|
|
164
179
|
### Changed
|
data/README.md
CHANGED
|
@@ -221,14 +221,28 @@ asgard ci executes:
|
|
|
221
221
|
ci
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
`depends_on` also accepts a `Proc`/lambda instead of a fixed list, resolved once every `.loki` file has finished loading rather than immediately — useful when the list can't be known upfront, e.g. "every task whose name ends in `_check`," discovered across several files:
|
|
224
|
+
`depends_on` also accepts a `Proc`/lambda (or, equivalently, a block) instead of a fixed list, resolved once every `.loki` file has finished loading rather than immediately — useful when the list can't be known upfront, e.g. "every task whose name ends in `_check`," discovered across several files:
|
|
225
225
|
|
|
226
226
|
```ruby
|
|
227
227
|
depends_on -> { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
228
228
|
def quality = puts "running every *_check task..."
|
|
229
|
+
|
|
230
|
+
depends_on { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
231
|
+
def quality2 = puts "same, as a block..."
|
|
232
|
+
|
|
233
|
+
# do...end for a block spanning multiple statements — braces are for
|
|
234
|
+
# single-line blocks like the two above. The last expression is still what
|
|
235
|
+
# gets returned and validated.
|
|
236
|
+
depends_on do
|
|
237
|
+
checks = all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)
|
|
238
|
+
slow = %i[c_check]
|
|
239
|
+
|
|
240
|
+
[checks - slow]
|
|
241
|
+
end
|
|
242
|
+
def quality3 = puts "same, minus the slow checks, as a multi-line block..."
|
|
229
243
|
```
|
|
230
244
|
|
|
231
|
-
See [Dependencies](https://madbomber.github.io/asgard/dependencies/#dynamic-dependencies-proc-form) for the full explanation.
|
|
245
|
+
See [Dependencies](https://madbomber.github.io/asgard/dependencies/#dynamic-dependencies-proc-block-form) for the full explanation.
|
|
232
246
|
|
|
233
247
|
---
|
|
234
248
|
|
|
@@ -489,6 +503,33 @@ end
|
|
|
489
503
|
|
|
490
504
|
---
|
|
491
505
|
|
|
506
|
+
## Abbreviated command matching
|
|
507
|
+
|
|
508
|
+
Every task is a Thor command, so you don't have to type the full name — Thor resolves any unambiguous prefix automatically, with no extra code:
|
|
509
|
+
|
|
510
|
+
```ruby
|
|
511
|
+
class Tasks
|
|
512
|
+
desc "Compile the project"
|
|
513
|
+
def build = sh "rake build"
|
|
514
|
+
|
|
515
|
+
desc "Deploy to production"
|
|
516
|
+
def deploy = sh "cap production deploy"
|
|
517
|
+
|
|
518
|
+
desc "Deploy to staging"
|
|
519
|
+
def deploy_staging = sh "cap staging deploy"
|
|
520
|
+
end
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
```bash
|
|
524
|
+
asgard b # same as: asgard build — only task starting with "b"
|
|
525
|
+
asgard depl # Ambiguous command depl matches [deploy, deploy_staging]
|
|
526
|
+
asgard deploy # runs deploy — an exact match always wins, even over a shorter ambiguous prefix
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
This is Thor's own dispatch behavior, not an Asgard feature — it applies to every task in every `.loki` file automatically. When a prefix matches more than one task, Thor lists the candidates instead of guessing; type enough of the name to disambiguate, or use `map` (below) to pin a short name that stays stable even if a later-added task would otherwise make it ambiguous.
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
492
533
|
## Command aliases
|
|
493
534
|
|
|
494
535
|
`map` creates alternative names for a task:
|
data/doc_tasks.loki
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# dev/doc_tasks.loki
|
|
2
|
+
|
|
3
|
+
# TODO: create common tasks for documentation management
|
|
4
|
+
class Tasks
|
|
5
|
+
|
|
6
|
+
if File.exist?(ENV['RR']+'/.config/tocer/configuration.yml')
|
|
7
|
+
desc "Management table of contents"
|
|
8
|
+
def tocer = sh "tocer help"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if File.exist?(ENV['RR']+'/mkdocs.yml')
|
|
13
|
+
desc "Documentation builder"
|
|
14
|
+
def doc_builder = sh "mkdocs build"
|
|
15
|
+
|
|
16
|
+
desc "Documentation server startup"
|
|
17
|
+
depends_on :doc_builder
|
|
18
|
+
def doc_server = sh "mkdocs serve", exec: true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
data/docs/api.md
CHANGED
|
@@ -111,12 +111,12 @@ import_up "*.loki" # find the nearest ancestor with *.loki files
|
|
|
111
111
|
|
|
112
112
|
| Method | Signature | Description |
|
|
113
113
|
|---|---|---|
|
|
114
|
-
| `depends_on` | `depends_on(*tasks)` | Declare prerequisites for the next `def`. Bare symbols run sequentially; arrays within the splat run as a parallel group. A sole `Proc`/lambda defers resolution to `validate_deps!` (after every `.loki` file has loaded) instead of resolving immediately — see [Dynamic Dependencies](dependencies.md#dynamic-dependencies-proc-form). |
|
|
114
|
+
| `depends_on` | `depends_on(*tasks, &block)` | Declare prerequisites for the next `def`. Bare symbols run sequentially; arrays within the splat run as a parallel group. A sole `Proc`/lambda, or a block in place of the splat args, defers resolution to `validate_deps!` (after every `.loki` file has loaded) instead of resolving immediately — see [Dynamic Dependencies](dependencies.md#dynamic-dependencies-proc-block-form). Passing both task arguments and a block raises `Asgard::Error`. |
|
|
115
115
|
| `dotenv` | `dotenv(path = ".env")` | Load the specified `.env` file into `ENV` using the dotenv gem. Silently skipped if the file does not exist. Called at class-load time. |
|
|
116
116
|
| `header` | `header(text)` | Append a line of text shown above the commands list in `asgard help`. Each call adds another line. No-op for per-command help. |
|
|
117
117
|
| `footer` | `footer(text)` | Prepend a line of text shown below the options block in `asgard help`. Each call inserts above the previous lines. No-op for per-command help. |
|
|
118
118
|
| `no_negate` | `no_negate(*names)` | Suppress `[--no-name]` / `[--skip-name]` help entries for one or more boolean class options. Call after the `class_option` declaration. |
|
|
119
|
-
| `sh` | `sh(script, silent: false)` | Instance method. Run a shell command or multiline heredoc. Single-line → `system(script)`; multiline → `system("bash", "-c", script)`. Exits with the command's status on failure. |
|
|
119
|
+
| `sh` | `sh(script, silent: false, exec: false)` | Instance method. Run a shell command or multiline heredoc. Single-line → `system(script)`; multiline → `system("bash", "-c", script)`. Exits with the command's status on failure. With `exec: true`, replaces the asgard process via `Kernel.exec` instead of forking — see [Shell Helpers](shell.md#handing-off-with-exec). |
|
|
120
120
|
| `shebang` | `shebang(interpreter, script, silent: false)` | Instance method. Write `script` to a tempfile and execute it with `interpreter`. See the [Shell Helpers](shell.md) page for the full interpreter table. |
|
|
121
121
|
| `validate_deps!` | `Tasks.validate_deps!` | Build and topologically sort the full dependency graph using stdlib `TSort`. Raises `Asgard::CircularDependencyError` on cycles. Called by `run!` at startup. |
|
|
122
122
|
| `_reset_ran!` | `Tasks._reset_ran!` | Clear the per-invocation task deduplication set. Called by `run!` before dispatching. Thread-safe via Mutex. |
|
|
@@ -175,12 +175,14 @@ depends_on :clean, :build # two sequential deps
|
|
|
175
175
|
depends_on [:lint, :typecheck] # lint and typecheck run in parallel
|
|
176
176
|
depends_on :setup, [:lint, :build], :test # setup, then lint+build concurrently, then test
|
|
177
177
|
|
|
178
|
-
# A sole Proc/lambda
|
|
179
|
-
# file has loaded — must return the same
|
|
178
|
+
# A sole Proc/lambda (or, equivalently, a block) defers resolution to
|
|
179
|
+
# validate_deps!, after every .loki file has loaded — must return the same
|
|
180
|
+
# shape the splat form above would; the shape is validated when it resolves.
|
|
180
181
|
depends_on -> { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
182
|
+
depends_on { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
181
183
|
```
|
|
182
184
|
|
|
183
|
-
See [Dynamic Dependencies](dependencies.md#dynamic-dependencies-proc-form) for the full explanation of the Proc form — why it exists, when it runs, and how errors are reported.
|
|
185
|
+
See [Dynamic Dependencies](dependencies.md#dynamic-dependencies-proc-block-form) for the full explanation of the Proc/block form — why it exists, when it runs, and how errors are reported.
|
|
184
186
|
|
|
185
187
|
---
|
|
186
188
|
|
data/docs/changelog.md
CHANGED
|
@@ -22,7 +22,9 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Asg
|
|
|
22
22
|
- **`asgard tree`** now shows the project header/footer, matching `asgard help`.
|
|
23
23
|
- **`bundler_audit_check` task** — `bundle-audit check --update` against `Gemfile.lock`; `FAIL` on any known vulnerability.
|
|
24
24
|
- **`quality_rails.loki`** — imported only when `Rails` is defined; ships `brakeman_check` as a Rails security-scan example. No wiring needed — `quality` discovers it automatically (see `depends_on` below).
|
|
25
|
-
- **`depends_on` accepts a Proc/lambda**, not just a fixed list — resolved once, in `validate_deps!`, after every `.loki` file has loaded, instead of immediately. Solves "load order matters" for a dependency list that can't be known upfront, e.g. every task ending in `_check` across several files. See [Dynamic Dependencies](dependencies.md#dynamic-dependencies-proc-form).
|
|
25
|
+
- **`depends_on` accepts a Proc/lambda**, not just a fixed list — resolved once, in `validate_deps!`, after every `.loki` file has loaded, instead of immediately. Solves "load order matters" for a dependency list that can't be known upfront, e.g. every task ending in `_check` across several files. See [Dynamic Dependencies](dependencies.md#dynamic-dependencies-proc-block-form).
|
|
26
|
+
- **`depends_on` also accepts a block** — `depends_on { ... }` or `depends_on do ... end`, interchangeable with the Proc/lambda form above. Task arguments and a block can't be combined; doing so raises `Asgard::Error`.
|
|
27
|
+
- **The Proc/lambda/block result is now shape-validated** once resolved — it must be an `Array` of `Symbol`/`String` (sequential) or `Array` of `Symbol`/`String` (parallel group) stages, nested no deeper. A bad shape raises a clear `Asgard::Error` naming the task and the offending value instead of a raw `NoMethodError`. See `examples/depends_on_block/{good,bad}/`.
|
|
26
28
|
|
|
27
29
|
### Changed
|
|
28
30
|
|
data/docs/dependencies.md
CHANGED
|
@@ -236,9 +236,9 @@ Because `*.loki` files are loaded alphabetically when `import "*.loki"` is used,
|
|
|
236
236
|
|
|
237
237
|
---
|
|
238
238
|
|
|
239
|
-
## Dynamic Dependencies (Proc Form)
|
|
239
|
+
## Dynamic Dependencies (Proc / Block Form)
|
|
240
240
|
|
|
241
|
-
`depends_on` normally takes a fixed list, recorded the moment the `def` right after it is encountered — which is why load order matters, as above. Pass a `Proc` or lambda instead, and that list is computed *later*, after every `.loki` file has finished loading, rather than at the point `depends_on` itself is evaluated:
|
|
241
|
+
`depends_on` normally takes a fixed list, recorded the moment the `def` right after it is encountered — which is why load order matters, as above. Pass a `Proc` or lambda instead — or, equivalently, a block — and that list is computed *later*, after every `.loki` file has finished loading, rather than at the point `depends_on` itself is evaluated:
|
|
242
242
|
|
|
243
243
|
```ruby
|
|
244
244
|
depends_on -> { [all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)] }
|
|
@@ -246,22 +246,33 @@ desc "Run every *_check quality gate task in parallel"
|
|
|
246
246
|
def quality
|
|
247
247
|
# ...
|
|
248
248
|
end
|
|
249
|
+
|
|
250
|
+
depends_on { [all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)] }
|
|
251
|
+
desc "Same thing, written as a block"
|
|
252
|
+
def quality2
|
|
253
|
+
# ...
|
|
254
|
+
end
|
|
249
255
|
```
|
|
250
256
|
|
|
251
|
-
This solves exactly the "load order matters" problem from the previous section: a plain array can only name tasks that already exist in `.loki` files loaded *before* this one. A Proc is resolved once every file has loaded, so it can safely reference a task defined in a file that hasn't been imported yet at the point `depends_on` is written — including one that only exists conditionally, e.g. a Rails-specific task file imported with `import "quality_rails.loki" if defined?(Rails)`.
|
|
257
|
+
This solves exactly the "load order matters" problem from the previous section: a plain array can only name tasks that already exist in `.loki` files loaded *before* this one. A Proc/block is resolved once every file has loaded, so it can safely reference a task defined in a file that hasn't been imported yet at the point `depends_on` is written — including one that only exists conditionally, e.g. a Rails-specific task file imported with `import "quality_rails.loki" if defined?(Rails)`. `depends_on` accepts task arguments *or* a block, never both — combining them raises `Asgard::Error`.
|
|
258
|
+
|
|
259
|
+
**Shape:** the Proc/block must return exactly what the plain-array form would receive as its splat arguments — an array of stages, each a `Symbol`/`String` (sequential) or an `Array` of `Symbol`/`String` (parallel group), nested no deeper than that. The example above returns `[[:a_check, :b_check, :c_check]]`: one stage, containing every matching task, all running in parallel — the same shape as `depends_on [:a_check, :b_check, :c_check]`. This shape is validated once the result comes back — a bad return value (wrong type, a stage that isn't a Symbol/String/Array, a leaf inside a parallel group that isn't a Symbol/String, or nesting more than one level deep) raises `Asgard::Error` naming the task and the offending value, instead of failing later with an opaque `NoMethodError`:
|
|
252
260
|
|
|
253
|
-
|
|
261
|
+
```bash
|
|
262
|
+
asgard quality
|
|
263
|
+
# asgard: depends_on proc/block for 'quality' returned invalid stage 123 (Integer); expected a Symbol, String, or Array of them
|
|
264
|
+
```
|
|
254
265
|
|
|
255
|
-
**When it runs:** once, when `validate_deps!` runs (right after the `.loki` chain finishes loading, before any task dispatches). The resolved result replaces the Proc in the dependency table, so cycle detection, undefined-task checks, and arity checks all run against the *resolved* list — a Proc that references an undefined task, or that itself introduces a cycle, is caught at startup exactly like a plain array would be:
|
|
266
|
+
**When it runs:** once, when `validate_deps!` runs (right after the `.loki` chain finishes loading, before any task dispatches). The resolved result replaces the Proc/block in the dependency table, so cycle detection, undefined-task checks, and arity checks all run against the *resolved* list — a Proc/block that references an undefined task, or that itself introduces a cycle, is caught at startup exactly like a plain array would be:
|
|
256
267
|
|
|
257
268
|
```bash
|
|
258
269
|
asgard quality
|
|
259
270
|
# asgard: undefined task(s) in depends_on: ghost_check
|
|
260
271
|
```
|
|
261
272
|
|
|
262
|
-
**`self` inside the Proc:** since
|
|
273
|
+
**`self` inside the Proc/block:** since it's written directly in a `class Tasks` body, it lexically captures that class as `self` — so it can call `all_commands`, `_deps`, or any other class-level method bare, without a `self.class.` prefix, even though it's actually invoked later from inside `validate_deps!`.
|
|
263
274
|
|
|
264
|
-
**If the Proc raises**, the error is caught and re-raised as `Asgard::Error` naming the task it was declared for:
|
|
275
|
+
**If the Proc/block raises**, the error is caught and re-raised as `Asgard::Error` naming the task it was declared for:
|
|
265
276
|
|
|
266
277
|
```
|
|
267
278
|
asgard: depends_on proc for 'quality' raised RuntimeError: boom
|
data/docs/shell.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Asgard provides two methods for running shell commands and scripts from within task bodies: `sh` for shell commands and heredocs, and `shebang` for polyglot scripts. Both are provided by `Asgard::Shell` and mixed into every `Tasks` instance.
|
|
4
4
|
|
|
5
|
-
Both methods exit with the command's status code on failure — they do not raise Ruby exceptions.
|
|
5
|
+
Both methods exit with the command's status code on failure — they do not raise Ruby exceptions. `sh` also accepts `exec: true` to replace the asgard process outright instead of forking — see [Handing Off with `exec`](#handing-off-with-exec).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -80,6 +80,35 @@ class Tasks
|
|
|
80
80
|
end
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
### Handing Off with `exec`
|
|
84
|
+
|
|
85
|
+
Pass `exec: true` to hand the command the asgard process itself instead of forking a child. Under the hood this calls `Kernel.exec` rather than `system`, which replaces the running ruby process image with the command — asgard doesn't stick around waiting on it.
|
|
86
|
+
|
|
87
|
+
Use this for a task's final, long-running command — a dev server, a REPL, anything meant to take over the terminal — so there's no idle ruby process sitting in memory alongside it, and Ctrl-C is handled directly by the command instead of unwinding back through asgard:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
class Tasks
|
|
91
|
+
desc "Documentation server startup"
|
|
92
|
+
depends_on :doc_builder
|
|
93
|
+
def doc_server = sh "mkdocs serve", exec: true
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Since the process is replaced, nothing after the `sh` call ever runs, and `depends_on` chains upstream of it must already have finished (they have — dependencies run before the task body).
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
class Tasks
|
|
101
|
+
desc "Start a REPL — never returns to asgard"
|
|
102
|
+
def console
|
|
103
|
+
sh "bundle exec pry", exec: true
|
|
104
|
+
puts "unreachable"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
!!! note
|
|
110
|
+
`exec: true` only makes sense for a command meant to run for the lifetime of the process. Don't use it for a step with more work queued after it in the same task.
|
|
111
|
+
|
|
83
112
|
---
|
|
84
113
|
|
|
85
114
|
## `shebang` — Polyglot Scripts
|
data/docs/tasks.md
CHANGED
|
@@ -206,6 +206,46 @@ Without `default_task`, running `asgard` with no arguments displays the help mes
|
|
|
206
206
|
|
|
207
207
|
---
|
|
208
208
|
|
|
209
|
+
## Abbreviated Command Matching
|
|
210
|
+
|
|
211
|
+
Every task is a Thor command, and Thor resolves any unambiguous prefix of a command name to that command automatically — no Asgard code involved, and nothing to declare. Given:
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
class Tasks
|
|
215
|
+
desc "Check code style with RuboCop"
|
|
216
|
+
def rubocop_check = sh "bundle exec rubocop"
|
|
217
|
+
|
|
218
|
+
desc "Run the test suite"
|
|
219
|
+
def test_check = sh "bundle exec rake test"
|
|
220
|
+
|
|
221
|
+
desc "Run the test suite with verbose output"
|
|
222
|
+
def test_verbose = sh "bundle exec rake test -v"
|
|
223
|
+
|
|
224
|
+
desc "Deploy to production"
|
|
225
|
+
def deploy = sh "cap production deploy"
|
|
226
|
+
|
|
227
|
+
desc "Deploy to staging"
|
|
228
|
+
def deploy_staging = sh "cap staging deploy"
|
|
229
|
+
end
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
asgard r # same as: asgard rubocop_check — the only task starting with "r"
|
|
234
|
+
asgard test_c # same as: asgard test_check — enough of the name to be unique
|
|
235
|
+
asgard test # Ambiguous command test matches [test_check, test_verbose]
|
|
236
|
+
asgard deploy # runs deploy, not deploy_staging — see below
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Thor matches on a plain prefix (`command_name.start_with?(typed_string)`), so the shortest string that is still unique for your task set works. Two details worth knowing:
|
|
240
|
+
|
|
241
|
+
- **An exact full name always wins**, even if it's also a prefix of another task. `asgard deploy` runs `deploy` itself, never the ambiguous-prefix error, because `deploy` is a defined command — not merely a prefix of `deploy_staging`.
|
|
242
|
+
- **An ambiguous prefix produces a clean error listing every candidate** (`Ambiguous command X matches [...]`) rather than guessing or running the alphabetically-first match. Type enough of the name to disambiguate.
|
|
243
|
+
|
|
244
|
+
!!! tip
|
|
245
|
+
Because matching depends on every task name currently defined, a short prefix that's unique today can become ambiguous tomorrow when a new task with the same stem is added — e.g. adding `rubocop_fix` alongside `rubocop_check` turns `asgard r` from a clean match into `Ambiguous command r matches [rubocop_check, rubocop_fix]`. Use [`map`](#command-aliases) below for a short name you want to guarantee stays stable regardless of what other tasks get added later.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
209
249
|
## Command Aliases
|
|
210
250
|
|
|
211
251
|
`map` creates short aliases for existing tasks:
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# examples/depends_on_block/bad/.loki
|
|
2
|
+
# BAD example: depends_on block returning a mis-shaped result.
|
|
3
|
+
#
|
|
4
|
+
# Compare with ../good/.loki, which does this correctly:
|
|
5
|
+
#
|
|
6
|
+
# depends_on { [all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)] }
|
|
7
|
+
# ^ ^
|
|
8
|
+
# one wrapping [ ] — this is "one stage, containing every matching
|
|
9
|
+
# task, all running in parallel."
|
|
10
|
+
#
|
|
11
|
+
# It's an easy typo to add a SECOND wrapping array, thinking the outer
|
|
12
|
+
# array is "the list of stages" and the inner one is "this stage's tasks"
|
|
13
|
+
# as two separate concerns:
|
|
14
|
+
#
|
|
15
|
+
# depends_on { [[all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)]] }
|
|
16
|
+
# ^ ^
|
|
17
|
+
# that's one stage too many — the middle array ends up
|
|
18
|
+
# *inside* a stage as a dependency, not as the stage itself.
|
|
19
|
+
#
|
|
20
|
+
# depends_on's block/Proc result is validated the moment it resolves
|
|
21
|
+
# (during validate_deps!, before any task runs): every stage must be a
|
|
22
|
+
# Symbol/String (sequential) or an Array of Symbol/String (parallel group)
|
|
23
|
+
# — nesting one level deeper than that is rejected with Asgard::Error
|
|
24
|
+
# instead of failing later with a confusing NoMethodError.
|
|
25
|
+
#
|
|
26
|
+
# Run with (from this directory):
|
|
27
|
+
# asgard quality
|
|
28
|
+
#
|
|
29
|
+
# Expected output (validate_deps! aborts before quality — or any other
|
|
30
|
+
# task — ever runs):
|
|
31
|
+
# asgard: depends_on proc/block for 'quality' returned invalid dependency
|
|
32
|
+
# [:a_check, :b_check, :c_check] (Array) in a parallel group; expected a
|
|
33
|
+
# Symbol or String
|
|
34
|
+
|
|
35
|
+
class Tasks
|
|
36
|
+
desc "Run the first check"
|
|
37
|
+
def a_check = puts "running a_check ..."
|
|
38
|
+
|
|
39
|
+
desc "Run the second check"
|
|
40
|
+
def b_check = puts "running b_check ..."
|
|
41
|
+
|
|
42
|
+
desc "Run the third check"
|
|
43
|
+
def c_check = puts "running c_check ..."
|
|
44
|
+
|
|
45
|
+
# BUG: double-wrapped — [[ ... ]] instead of [ ... ]. See comment above.
|
|
46
|
+
depends_on { [[all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)]] }
|
|
47
|
+
desc "Run every *_check task in parallel, then report"
|
|
48
|
+
def quality
|
|
49
|
+
puts "quality: all *_check tasks passed"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# examples/depends_on_block/good/.loki
|
|
2
|
+
# GOOD example: depends_on with a block instead of a Proc/lambda.
|
|
3
|
+
#
|
|
4
|
+
# A block behaves exactly like `-> { ... }` — it defers resolution to
|
|
5
|
+
# validate_deps! (once, after every .loki file has loaded) instead of
|
|
6
|
+
# resolving the moment depends_on is evaluated. This is the real pattern
|
|
7
|
+
# from this gem's own quality.loki: gather every task whose name ends in
|
|
8
|
+
# `_check`, without having to know their names or load order upfront.
|
|
9
|
+
#
|
|
10
|
+
# The block's return value is validated once it resolves: it must be an
|
|
11
|
+
# Array of stages, each a Symbol/String (sequential) or an Array of
|
|
12
|
+
# Symbol/String (parallel group) — see ../bad/.loki for what happens when
|
|
13
|
+
# that shape is wrong.
|
|
14
|
+
#
|
|
15
|
+
# `{ ... }` braces are used below for the short, single-line blocks (quality,
|
|
16
|
+
# full_cycle) — standard Ruby style reserves `do ... end` for a block that
|
|
17
|
+
# spans multiple statements, shown in smoke further down.
|
|
18
|
+
#
|
|
19
|
+
# Run with (from this directory):
|
|
20
|
+
# asgard quality
|
|
21
|
+
#
|
|
22
|
+
# Expected output:
|
|
23
|
+
# running a_check ...
|
|
24
|
+
# running b_check ...
|
|
25
|
+
# running c_check ...
|
|
26
|
+
# quality: all *_check tasks passed
|
|
27
|
+
|
|
28
|
+
class Tasks
|
|
29
|
+
desc "Run the first check"
|
|
30
|
+
def a_check = puts "running a_check ..."
|
|
31
|
+
|
|
32
|
+
desc "Run the second check"
|
|
33
|
+
def b_check = puts "running b_check ..."
|
|
34
|
+
|
|
35
|
+
desc "Run the third check"
|
|
36
|
+
def c_check = puts "running c_check ..."
|
|
37
|
+
|
|
38
|
+
# self inside the block is the Tasks class (it's written directly in the
|
|
39
|
+
# class body), so all_commands can be called bare, with no self.class.
|
|
40
|
+
# prefix — even though the block itself isn't invoked until later.
|
|
41
|
+
depends_on { [all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)] }
|
|
42
|
+
desc "Run every *_check task in parallel, then report"
|
|
43
|
+
def quality
|
|
44
|
+
puts "quality: all *_check tasks passed"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# A block can also return a mix of sequential Symbols and parallel Arrays,
|
|
48
|
+
# exactly like the plain splat form: depends_on :setup, [:a_check, :b_check], :teardown
|
|
49
|
+
desc "Prepare fixtures before the checks run"
|
|
50
|
+
def setup = puts "setup: fixtures ready"
|
|
51
|
+
|
|
52
|
+
desc "Clean up fixtures after the checks run"
|
|
53
|
+
def teardown = puts "teardown: fixtures removed"
|
|
54
|
+
|
|
55
|
+
depends_on { [:setup, %i[a_check b_check], :teardown] }
|
|
56
|
+
desc "setup, then a_check+b_check in parallel, then teardown"
|
|
57
|
+
def full_cycle
|
|
58
|
+
puts "full_cycle: done"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# A block spanning multiple statements — local variables, some computation
|
|
62
|
+
# — reads more naturally as do...end than as one long `{ ... }` line.
|
|
63
|
+
# Whatever it does along the way, the last expression is still what gets
|
|
64
|
+
# returned and validated: here, [:setup, Array, :teardown], the same shape
|
|
65
|
+
# as full_cycle above.
|
|
66
|
+
#
|
|
67
|
+
# Gotcha: this block runs during validate_deps!, which happens *before*
|
|
68
|
+
# Thor parses this invocation's CLI options (see the Entry Point Flow in
|
|
69
|
+
# CLAUDE.md) — so checking `--verbose`/`--debug` in here would always see
|
|
70
|
+
# the pre-flag default, never what was actually passed on this run. Keep
|
|
71
|
+
# depends_on blocks limited to shaping the dependency graph itself.
|
|
72
|
+
depends_on do
|
|
73
|
+
all_checks = all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)
|
|
74
|
+
slow_checks = %i[c_check]
|
|
75
|
+
fast_checks = all_checks - slow_checks
|
|
76
|
+
|
|
77
|
+
[:setup, fast_checks, :teardown]
|
|
78
|
+
end
|
|
79
|
+
desc "setup, then every *_check except the slow ones, then teardown"
|
|
80
|
+
def smoke
|
|
81
|
+
puts "smoke: done"
|
|
82
|
+
end
|
|
83
|
+
end
|
data/examples/kitchen_sink.loki
CHANGED
|
@@ -181,6 +181,19 @@ class Tasks
|
|
|
181
181
|
end
|
|
182
182
|
end
|
|
183
183
|
|
|
184
|
+
# ── Asgard: sh — run shell commands, single-line and multi-line heredoc ────
|
|
185
|
+
desc "Show environment info (single-line sh)"
|
|
186
|
+
def env_info = sh "echo Running #{@@app_name} on $(uname -s)"
|
|
187
|
+
|
|
188
|
+
desc "Bootstrap the local environment (multi-line sh / heredoc)"
|
|
189
|
+
def bootstrap
|
|
190
|
+
sh <<~SHELL
|
|
191
|
+
echo "App: #{@@app_name}"
|
|
192
|
+
echo "Ruby: $(ruby -v)"
|
|
193
|
+
echo "Date: $(date)"
|
|
194
|
+
SHELL
|
|
195
|
+
end
|
|
196
|
+
|
|
184
197
|
# ── Thor: no_commands — public helper excluded from CLI and --help ──────────
|
|
185
198
|
no_commands do
|
|
186
199
|
def current_sha
|
|
@@ -17,13 +17,21 @@ module Asgard
|
|
|
17
17
|
# depends_on [:build, :lint] # build and lint in parallel
|
|
18
18
|
# depends_on :setup, [:build, :lint], :test # setup, then build+lint, then test
|
|
19
19
|
#
|
|
20
|
-
# A sole Proc/lambda
|
|
21
|
-
# .loki file has loaded),
|
|
22
|
-
#
|
|
23
|
-
# (sequential) or Array
|
|
20
|
+
# A sole Proc/lambda, or a block in place of the splat args, defers
|
|
21
|
+
# resolution to validate_deps! (after every .loki file has loaded),
|
|
22
|
+
# instead of now. It must return the same shape the splat form above
|
|
23
|
+
# would: an array of stages, each a Symbol (sequential) or Array
|
|
24
|
+
# (parallel group).
|
|
24
25
|
#
|
|
25
26
|
# depends_on -> { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
26
|
-
|
|
27
|
+
# depends_on { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
28
|
+
def depends_on(*tasks, &block)
|
|
29
|
+
if block
|
|
30
|
+
raise Asgard::Error, "depends_on accepts either task arguments or a block, not both" if tasks.any?
|
|
31
|
+
|
|
32
|
+
tasks = [block]
|
|
33
|
+
end
|
|
34
|
+
|
|
27
35
|
@_pending_deps = tasks
|
|
28
36
|
end
|
|
29
37
|
|
|
@@ -65,7 +73,9 @@ module Asgard
|
|
|
65
73
|
_deps.each do |task, stages_or_proc|
|
|
66
74
|
next unless stages_or_proc.respond_to?(:call)
|
|
67
75
|
|
|
68
|
-
|
|
76
|
+
result = Array(_call_dep_proc(task, stages_or_proc))
|
|
77
|
+
_validate_dep_shape!(task, result)
|
|
78
|
+
_deps[task] = _stages_from(result)
|
|
69
79
|
end
|
|
70
80
|
end
|
|
71
81
|
|
|
@@ -75,6 +85,32 @@ module Asgard
|
|
|
75
85
|
raise Asgard::Error, "depends_on proc for '#{task}' raised #{e.class}: #{e.message}"
|
|
76
86
|
end
|
|
77
87
|
|
|
88
|
+
# A resolved proc/lambda/block result must be an Array of stages, each
|
|
89
|
+
# either a Symbol/String (sequential) or an Array of Symbol/String
|
|
90
|
+
# (parallel group) — no deeper nesting, no other leaf types.
|
|
91
|
+
def _validate_dep_shape!(task, result)
|
|
92
|
+
result.each { |stage| _validate_dep_stage!(task, stage) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def _validate_dep_stage!(task, stage)
|
|
96
|
+
case stage
|
|
97
|
+
when Symbol, String then nil
|
|
98
|
+
when Array then stage.each { |leaf| _validate_dep_leaf!(task, leaf) }
|
|
99
|
+
else
|
|
100
|
+
raise Asgard::Error,
|
|
101
|
+
"depends_on proc/block for '#{task}' returned invalid stage #{stage.inspect} " \
|
|
102
|
+
"(#{stage.class}); expected a Symbol, String, or Array of them"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def _validate_dep_leaf!(task, leaf)
|
|
107
|
+
return if leaf.is_a?(Symbol) || leaf.is_a?(String)
|
|
108
|
+
|
|
109
|
+
raise Asgard::Error,
|
|
110
|
+
"depends_on proc/block for '#{task}' returned invalid dependency #{leaf.inspect} " \
|
|
111
|
+
"(#{leaf.class}) in a parallel group; expected a Symbol or String"
|
|
112
|
+
end
|
|
113
|
+
|
|
78
114
|
def _check_orphaned_deps!
|
|
79
115
|
pending = Array(@_pending_deps)
|
|
80
116
|
return unless pending.any?
|
|
@@ -107,7 +143,7 @@ module Asgard
|
|
|
107
143
|
# the order itself isn't otherwise used (execution order comes from the
|
|
108
144
|
# stage groups each task's own depends_on declared).
|
|
109
145
|
def _build_and_sort_graph(all_task_names)
|
|
110
|
-
full_graph = all_task_names.to_h { |task| [task, _deps.fetch(task
|
|
146
|
+
full_graph = all_task_names.to_h { |task| [task, _deps.fetch(task) { [] }.flatten] }
|
|
111
147
|
Graph.new(full_graph).tsort
|
|
112
148
|
end
|
|
113
149
|
|
data/lib/asgard/base/task_dsl.rb
CHANGED
|
@@ -48,11 +48,10 @@ module Asgard
|
|
|
48
48
|
here = caller_locations(1, 1).first if active
|
|
49
49
|
|
|
50
50
|
if active && @_default_task_location
|
|
51
|
-
# rubocop:disable Style/StderrPuts -- warn bypasses $stderr in Ruby 4.0, breaking capture_io in tests
|
|
51
|
+
# rubocop:disable-next Style/StderrPuts -- warn bypasses $stderr in Ruby 4.0, breaking capture_io in tests
|
|
52
52
|
$stderr.puts "asgard: default_task :#{meth} at #{here.path}:#{here.lineno} " \
|
|
53
53
|
"overrides default_task :#{@_default_task_name} set at " \
|
|
54
54
|
"#{@_default_task_location.path}:#{@_default_task_location.lineno}"
|
|
55
|
-
# rubocop:enable Style/StderrPuts
|
|
56
55
|
end
|
|
57
56
|
if active
|
|
58
57
|
@_default_task_location = here
|
data/lib/asgard/shell.rb
CHANGED
|
@@ -8,17 +8,21 @@ module Asgard
|
|
|
8
8
|
# Run a shell script. Multiline strings are passed to bash -c; single-line
|
|
9
9
|
# strings are passed to system directly. Exits with the command's status
|
|
10
10
|
# code on failure.
|
|
11
|
-
|
|
11
|
+
#
|
|
12
|
+
# Pass exec: true to replace the current process instead of forking —
|
|
13
|
+
# useful for a task's final, long-running command (e.g. a dev server)
|
|
14
|
+
# so the asgard/ruby process doesn't sit resident in memory alongside it.
|
|
15
|
+
def sh(script, silent: false, exec: false)
|
|
12
16
|
script = script.strip
|
|
13
17
|
$stdout.puts script unless silent
|
|
18
|
+
argv = shell_argv(script)
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
exit($CHILD_STATUS.exitstatus) unless success
|
|
20
|
+
if exec
|
|
21
|
+
$stdout.flush
|
|
22
|
+
Kernel.exec(*argv)
|
|
23
|
+
else
|
|
24
|
+
exit($CHILD_STATUS.exitstatus) unless system(*argv)
|
|
25
|
+
end
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
# Write +script+ to a tempfile and execute it with +interpreter+.
|
|
@@ -42,5 +46,14 @@ module Asgard
|
|
|
42
46
|
exit($CHILD_STATUS.exitstatus) unless $CHILD_STATUS.success?
|
|
43
47
|
end
|
|
44
48
|
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# The argv passed to system/exec for +script+: multi-line scripts run
|
|
53
|
+
# through `bash -c` so assignments carry across lines; single-line
|
|
54
|
+
# scripts run directly.
|
|
55
|
+
def shell_argv(script)
|
|
56
|
+
script.include?("\n") ? ["bash", "-c", script] : [script]
|
|
57
|
+
end
|
|
45
58
|
end
|
|
46
59
|
end
|
data/lib/asgard/version.rb
CHANGED
data/lib/asgard.rb
CHANGED
data/quality_rails.loki
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# Rails-specific quality gate tasks — imported by .loki only when
|
|
3
|
-
#
|
|
2
|
+
# Rails-specific quality gate tasks — imported by .loki only when RAILS_ROOT
|
|
3
|
+
# is set. asgard runs as a separate process outside the app it's checking,
|
|
4
|
+
# so `defined?(Rails)` never sees the app's Rails constant.
|
|
4
5
|
#
|
|
5
6
|
# `quality` (in quality.loki) discovers every *_check task at run time by
|
|
6
7
|
# introspecting Tasks.all_commands, rather than a fixed depends_on list — so
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asgard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dewayne VanHoozer
|
|
@@ -62,6 +62,7 @@ files:
|
|
|
62
62
|
- bin/asgard
|
|
63
63
|
- bin/console
|
|
64
64
|
- bin/setup
|
|
65
|
+
- doc_tasks.loki
|
|
65
66
|
- docs/api.md
|
|
66
67
|
- docs/assets/css/custom.css
|
|
67
68
|
- docs/assets/images/asgard.jpg
|
|
@@ -83,6 +84,8 @@ files:
|
|
|
83
84
|
- examples/bad.loki
|
|
84
85
|
- examples/concurrent.loki
|
|
85
86
|
- examples/db_subcommands.loki
|
|
87
|
+
- examples/depends_on_block/bad/.loki
|
|
88
|
+
- examples/depends_on_block/good/.loki
|
|
86
89
|
- examples/env_usage.loki
|
|
87
90
|
- examples/kitchen_sink.loki
|
|
88
91
|
- examples/server_subcommands.loki
|