asgard 0.3.2 → 0.4.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.
- checksums.yaml +4 -4
- data/.loki +7 -1
- data/.reek.yml +42 -0
- data/CHANGELOG.md +60 -4
- data/CLAUDE.md +8 -2
- data/README.md +77 -2
- data/doc_tasks.loki +21 -0
- data/docs/api.md +28 -5
- data/docs/changelog.md +1 -132
- data/docs/dependencies.md +18 -7
- data/docs/index.md +1 -0
- data/docs/schedule.md +101 -0
- 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/errors.rb +7 -0
- data/lib/asgard/schedule/commands.rb +174 -0
- data/lib/asgard/schedule/declaration.rb +176 -0
- data/lib/asgard/schedule/launchd.rb +183 -0
- data/lib/asgard/schedule/systemd.rb +199 -0
- data/lib/asgard/schedule.rb +44 -0
- data/lib/asgard/shell.rb +21 -8
- data/lib/asgard/tasks.rb +12 -0
- data/lib/asgard/version.rb +1 -1
- data/lib/asgard.rb +9 -4
- data/mkdocs.yml +1 -0
- data/quality.loki +7 -2
- data/quality_rails.loki +3 -2
- metadata +14 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0fd4fe5903f93e062c0f515b3f6a49b859671c679b066003c04f2012eaf7176c
|
|
4
|
+
data.tar.gz: 5af1e1b65fc854258a68ec98a220d8260720474d9d84a7644cecec2fc054889f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb251bfc200af2b94dc66e6247475f2a4c0d2a474d0f1d4b165caf05250c8dbb55bd31295b2c0b7e963aeace88f2b8f2c2ecb8751271a522ea5c7a99486bccfe
|
|
7
|
+
data.tar.gz: c2540f86218ca5fe471ca276081d23224b2bf4f5784862915bf69f21c83acf664ef57f051bb4587a7104d0bb0bf82e768f28acc9b5f3e2cc32d15eac902dbf94
|
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
|
@@ -44,6 +44,7 @@ detectors:
|
|
|
44
44
|
Attribute:
|
|
45
45
|
exclude:
|
|
46
46
|
- Asgard::Doctor::ImportTracer#doctor # needs to stay settable/resettable per doctor run
|
|
47
|
+
- Asgard::Schedule#runner # test seam: tests swap in a recording runner, nil restores RUNNER
|
|
47
48
|
|
|
48
49
|
BooleanParameter:
|
|
49
50
|
exclude:
|
|
@@ -55,6 +56,7 @@ detectors:
|
|
|
55
56
|
exclude:
|
|
56
57
|
- Asgard::Shell#sh
|
|
57
58
|
- Asgard::Shell#shebang
|
|
59
|
+
- Asgard::Schedule#entry_name # as: is the user's explicit override of the derived name
|
|
58
60
|
|
|
59
61
|
DuplicateMethodCall:
|
|
60
62
|
exclude:
|
|
@@ -66,6 +68,7 @@ detectors:
|
|
|
66
68
|
- Kernel#import_up # "not found" warned from two independent branches
|
|
67
69
|
- Asgard#self.run! # Base.subclasses snapshot diff; e.message in separate rescues
|
|
68
70
|
- Asgard::Base#tree # say "\n": intentional blank line before and after the tree
|
|
71
|
+
- Asgard::Schedule::Launchd#unload # loaded?(name): check, then poll until bootout finishes
|
|
69
72
|
|
|
70
73
|
FeatureEnvy:
|
|
71
74
|
exclude:
|
|
@@ -75,6 +78,8 @@ detectors:
|
|
|
75
78
|
- Asgard::Base::Registry#inherited # inherited(subclass) configuring subclass is the whole point
|
|
76
79
|
- Asgard::Base::DependencyGraph#_normalize_pending_deps # small transform of its own argument
|
|
77
80
|
- Asgard::Base::DependencyGraph#_call_dep_proc # rescue => e; e.class/e.message is inherent
|
|
81
|
+
- Asgard::Base::DependencyGraph#_validate_dep_stage! # pure shape check of its own argument
|
|
82
|
+
- Asgard::Base::DependencyGraph#_validate_dep_leaf! # pure shape check of its own argument
|
|
78
83
|
- Asgard::Doctor::TaskSections#class_task_file_map
|
|
79
84
|
- Asgard::Doctor::TaskSections#relative_path
|
|
80
85
|
- Asgard::Doctor::TaskSections#task_status
|
|
@@ -84,6 +89,7 @@ detectors:
|
|
|
84
89
|
exclude:
|
|
85
90
|
- Asgard::Base::DependencyGraph#_normalize_pending_deps # respond_to?(:call) distinguishes a Proc from an Array/Symbol — no polymorphic alternative over Ruby's own built-in types
|
|
86
91
|
- Asgard::Base::DependencyGraph#_resolve_lazy_deps! # same check
|
|
92
|
+
- Asgard::Schedule#seconds # duck-types ActiveSupport::Duration (in_seconds) without depending on it
|
|
87
93
|
|
|
88
94
|
InstanceVariableAssumption:
|
|
89
95
|
exclude:
|
|
@@ -98,12 +104,48 @@ detectors:
|
|
|
98
104
|
- Asgard::Doctor::Report#print_task_sections
|
|
99
105
|
- Asgard::Doctor::TaskSections#class_task_file_map
|
|
100
106
|
- Asgard::Doctor::TaskSections#override_count
|
|
107
|
+
- Asgard::Schedule::Commands#preview # specs x files per spec
|
|
108
|
+
- Asgard::Schedule::Launchd#self.calendar_intervals # times x weekdays
|
|
101
109
|
|
|
102
110
|
UtilityFunction:
|
|
103
111
|
exclude:
|
|
104
112
|
- Asgard::Base::DependencyGraph#_build_dep_graph # genuinely pure, but tightly coupled to this graph model
|
|
105
113
|
- Asgard::Base::Dispatch#dep_results # thread-local by design, not self — that's the whole point
|
|
106
114
|
- Asgard::Base::Dispatch#with_dep_results # same: stashes results on Thread.current, not self
|
|
115
|
+
- Asgard::Shell#shell_argv # extracted pure on purpose so it's testable without invoking system/exec
|
|
116
|
+
- Asgard::Schedule::DSL#schedule # class-level DSL delegating to the Schedule registry
|
|
117
|
+
- Asgard::Schedule::Commands#schedules # Thor no_commands helpers: reads of the registry / CWD shared by the subcommands
|
|
118
|
+
- Asgard::Schedule::Commands#schedule_root
|
|
119
|
+
- Asgard::Schedule::Commands#schedule_summary
|
|
120
|
+
|
|
121
|
+
# Asgard::Schedule: the backends implement a fixed API (see
|
|
122
|
+
# lib/asgard/schedule/declaration.rb) plus pure, separately tested helpers,
|
|
123
|
+
# and the keyword parameters below mirror launchd/systemd job fields and the
|
|
124
|
+
# `schedule` DSL's own keywords.
|
|
125
|
+
DataClump:
|
|
126
|
+
exclude:
|
|
127
|
+
- Asgard::Schedule # at:/on: and task/args are the declaration's vocabulary
|
|
128
|
+
|
|
129
|
+
LongParameterList:
|
|
130
|
+
exclude:
|
|
131
|
+
- Asgard::Schedule#normalize # the `schedule` DSL keywords
|
|
132
|
+
- Asgard::Schedule#describe
|
|
133
|
+
- Asgard::Schedule#program_arguments
|
|
134
|
+
- Asgard::Schedule::Launchd#self.plist # one keyword per plist field
|
|
135
|
+
- Asgard::Schedule::Systemd#self.service_unit # one keyword per unit field
|
|
136
|
+
- Asgard::Schedule::Systemd#self.timer_unit
|
|
137
|
+
- Asgard::Schedule::Systemd#initialize # injectable home/runner/env/user for tests
|
|
138
|
+
|
|
139
|
+
RepeatedConditional:
|
|
140
|
+
exclude:
|
|
141
|
+
- Asgard::Schedule::Launchd # loaded?(name) guards distinct launchctl transitions
|
|
142
|
+
|
|
143
|
+
TooManyMethods:
|
|
144
|
+
exclude:
|
|
145
|
+
- Asgard::Schedule::Commands # one method per subcommand plus its helpers
|
|
146
|
+
- Asgard::Schedule::Launchd # backend API + pure helpers
|
|
147
|
+
- Asgard::Schedule::Systemd
|
|
148
|
+
|
|
107
149
|
|
|
108
150
|
exclude_paths:
|
|
109
151
|
- test
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,47 @@ 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.
|
|
8
|
+
## [0.4.0] - 2026-09-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Scheduled tasks, built in** — `schedule :task, at: "17:30", on: :weekdays`
|
|
13
|
+
(or `every: 3600`) at class level in a `.loki` declares a periodic run, and
|
|
14
|
+
`asgard schedule preview|install|list|start|stop|trigger|log|remove`
|
|
15
|
+
manages the entries under launchd (macOS) or systemd user timers (Linux).
|
|
16
|
+
Missed calendar runs fire on wake; a project `.envrc` is loaded at run time
|
|
17
|
+
through `direnv exec`. Promoted from the standalone `dev/schedule.loki`;
|
|
18
|
+
job labels and paths are unchanged, so entries it installed are still
|
|
19
|
+
recognized. ActiveSupport is no longer required: `every:` takes seconds or
|
|
20
|
+
any object with `in_seconds`. The command is registered as the gem-owned
|
|
21
|
+
`_schedule` and mapped to `schedule`. See
|
|
22
|
+
[Scheduled Tasks](https://madbomber.github.io/asgard/schedule/).
|
|
23
|
+
- **`Asgard::Schedule::Error`** (subclass of `Asgard::Error`) — raised when
|
|
24
|
+
`launchctl`/`systemctl` fails; `Asgard.run!` reports it as a one-line
|
|
25
|
+
`asgard: ...` message.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **Upgrading from `dev/schedule.loki`:** remove `import_up "dev/schedule.loki"`
|
|
30
|
+
from your `.loki`. Its `Tasks.schedule` overrides the built-in one and
|
|
31
|
+
records declarations where `asgard schedule` never looks.
|
|
32
|
+
- `Asgard::Error` and `Asgard::CircularDependencyError` now live in
|
|
33
|
+
`lib/asgard/errors.rb`, loaded first. No change to the classes themselves.
|
|
34
|
+
- The gem's own test run (`test_check`, `test_verbose`) loads every
|
|
35
|
+
`test/test_*.rb` in one process, so coverage is measured across the whole
|
|
36
|
+
suite.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- **`asgard <task>` now exits 1 when the task returns `:fail`** — quality-gate
|
|
41
|
+
tasks (`*_check`) signal failure by returning `:fail`; previously that result
|
|
42
|
+
was discarded and asgard always exited 0, so callers relying on the exit
|
|
43
|
+
status (CI, cross-repo runners like `ws_quality.loki`) saw every individual
|
|
44
|
+
gate as passing. The aggregate `quality` task was unaffected (it aborts
|
|
45
|
+
itself). Only a top-level result of exactly `:fail` maps to exit 1; `:warn`
|
|
46
|
+
and `:skip` remain non-blocking.
|
|
47
|
+
|
|
48
|
+
## [0.3.3] - 2026-08-27
|
|
9
49
|
|
|
10
50
|
### Added
|
|
11
51
|
|
|
@@ -105,7 +145,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
105
145
|
|
|
106
146
|
### Added (continued)
|
|
107
147
|
|
|
108
|
-
- **`--doctor` built-in CLI flag** — diagnoses `.loki` resolution, import chains, and task definitions for the current directory, then exits. Handled directly in `Asgard.run!` before the `.loki` file is loaded (same pattern as `--version`), so it keeps working in exactly the situations that would otherwise abort the whole process: a broken `.loki` file, a circular or undefined dependency, or a task silently redefined by a later `def`. Backed by the new `Asgard::Doctor` class. The report includes a "Tasks by file" listing: every command grouped by the file it's defined in, printed as `relative/path:line` so an editor can jump straight to it. A task name defined at more than one location gets every definition annotated inline — the earlier one(s) as `OVERRIDDEN by <file>:<line> — never callable`, the winning (last) one as `active — redefines <file>:<line>` — replacing the old flat "Tasks#x redefined" summary line with an annotation right where the problem is. See [API Reference](
|
|
148
|
+
- **`--doctor` built-in CLI flag** — diagnoses `.loki` resolution, import chains, and task definitions for the current directory, then exits. Handled directly in `Asgard.run!` before the `.loki` file is loaded (same pattern as `--version`), so it keeps working in exactly the situations that would otherwise abort the whole process: a broken `.loki` file, a circular or undefined dependency, or a task silently redefined by a later `def`. Backed by the new `Asgard::Doctor` class. The report includes a "Tasks by file" listing: every command grouped by the file it's defined in, printed as `relative/path:line` so an editor can jump straight to it. A task name defined at more than one location gets every definition annotated inline — the earlier one(s) as `OVERRIDDEN by <file>:<line> — never callable`, the winning (last) one as `active — redefines <file>:<line>` — replacing the old flat "Tasks#x redefined" summary line with an annotation right where the problem is. See [API Reference](https://madbomber.github.io/asgard/api/#asgarddoctor).
|
|
109
149
|
- **Flay and Reek quality gates** — `flay_check` checks for structural code duplication (mass ≥ 150); `reek` checks code smells. Both run as part of `quality` alongside `test`, `rubocop`, and `flog_check`. A `.reek.yml` tunes several detectors to this codebase's conventions (no doc-comment requirement, short variable names, disabled `TooManyStatements`, etc), plus per-method `exclude:` entries (generated with `reek --todo` and hand-curated) that grandfather specific reviewed smells at specific methods — precise enough that a genuinely new smell still fails the gate even at an already-reviewed method, unlike a per-file count.
|
|
110
150
|
- **`test_verbose` task** — runs the test suite with Minitest's verbose (`-v`) output.
|
|
111
151
|
- **Colorized quality gate summary** — `quality`'s final report now prints a green/red PASS/FAIL badge per gate plus a passed/failed tally, via a shared `print_quality_summary` helper.
|
|
@@ -135,7 +175,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
135
175
|
|
|
136
176
|
### Added (continued 3)
|
|
137
177
|
|
|
138
|
-
- **`depends_on` accepts a Proc/lambda in addition to a fixed list** — a sole callable defers resolution to `validate_deps!` (once, right after every `.loki` file has loaded) instead of resolving immediately when `depends_on` itself is evaluated. This solves the "load order matters" problem for a dependency list that can't be known upfront — e.g. "every task whose name ends in `_check`," discovered across several files including ones imported conditionally (`import "quality_rails.loki" if defined?(Rails)`). The Proc must return the same shape the splat form would receive (an array of stages, each a `Symbol` or `Array`); it's written directly in the class body, so it lexically captures that class as `self` and can call `all_commands` bare. A Proc that raises is re-raised as `Asgard::Error` naming the task it was declared for; a Proc that resolves to an undefined task or a cycle is still caught by the existing startup validation, since the resolved result is checked exactly like a plain array. See [Dynamic Dependencies](
|
|
178
|
+
- **`depends_on` accepts a Proc/lambda in addition to a fixed list** — a sole callable defers resolution to `validate_deps!` (once, right after every `.loki` file has loaded) instead of resolving immediately when `depends_on` itself is evaluated. This solves the "load order matters" problem for a dependency list that can't be known upfront — e.g. "every task whose name ends in `_check`," discovered across several files including ones imported conditionally (`import "quality_rails.loki" if defined?(Rails)`). The Proc must return the same shape the splat form would receive (an array of stages, each a `Symbol` or `Array`); it's written directly in the class body, so it lexically captures that class as `self` and can call `all_commands` bare. A Proc that raises is re-raised as `Asgard::Error` naming the task it was declared for; a Proc that resolves to an undefined task or a cycle is still caught by the existing startup validation, since the resolved result is checked exactly like a plain array. See [Dynamic Dependencies](https://madbomber.github.io/asgard/dependencies/#dynamic-dependencies-proc-block-form).
|
|
139
179
|
- **`bundler_audit_check` task** — runs `bundle-audit check --update` against `Gemfile.lock` (new dev dependency `bundler-audit`); reports `FAIL` on any known vulnerability, since this is a security gate, not a suggestion.
|
|
140
180
|
- **`quality_rails.loki`** — imported by `.loki` only when `Rails` is defined; currently ships `brakeman_check`, a Rails security-scan example. Needs no wiring into `quality`'s dependency list — `quality`'s `depends_on` Proc discovers it automatically once it's loaded.
|
|
141
181
|
|
|
@@ -159,6 +199,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
159
199
|
|
|
160
200
|
- **`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
201
|
|
|
202
|
+
### Added (continued 5)
|
|
203
|
+
|
|
204
|
+
- **`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](https://madbomber.github.io/asgard/shell/#handing-off-with-exec).
|
|
205
|
+
- **`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.
|
|
206
|
+
|
|
207
|
+
### Fixed (continued 3)
|
|
208
|
+
|
|
209
|
+
- **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.
|
|
210
|
+
|
|
211
|
+
### Added (continued 6)
|
|
212
|
+
|
|
213
|
+
- **`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](https://madbomber.github.io/asgard/dependencies/#dynamic-dependencies-proc-block-form).
|
|
214
|
+
- **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`.
|
|
215
|
+
- **`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.
|
|
216
|
+
|
|
162
217
|
## [0.2.0] - 2026-05-29
|
|
163
218
|
|
|
164
219
|
### Changed
|
|
@@ -236,7 +291,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
236
291
|
- 100% test coverage enforced via SimpleCov (95% minimum threshold)
|
|
237
292
|
- Quality task in `.loki` runs flog after tests
|
|
238
293
|
|
|
239
|
-
[0.
|
|
294
|
+
[0.4.0]: https://github.com/MadBomber/asgard/compare/v0.3.3...v0.4.0
|
|
295
|
+
[0.3.3]: https://github.com/MadBomber/asgard/compare/v0.2.0...v0.3.3
|
|
240
296
|
[0.2.0]: https://github.com/MadBomber/asgard/compare/v0.1.2...v0.2.0
|
|
241
297
|
[0.1.2]: https://github.com/MadBomber/asgard/compare/v0.1.1...v0.1.2
|
|
242
298
|
[0.1.1]: https://github.com/MadBomber/asgard/compare/v0.1.0...v0.1.1
|
data/CLAUDE.md
CHANGED
|
@@ -21,7 +21,9 @@ asgard quality
|
|
|
21
21
|
asgard release
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Whole suite (one process, so SimpleCov sees everything): `ruby -Ilib:test -e 'Dir["test/test_*.rb"].each { |f| require File.expand_path(f) }'`
|
|
25
|
+
|
|
26
|
+
Single file: `ruby -Ilib:test test/test_asgard.rb` (a single file alone will fall below the coverage minimum)
|
|
25
27
|
|
|
26
28
|
## Architecture
|
|
27
29
|
|
|
@@ -41,6 +43,10 @@ Single test: `ruby -Ilib:test test/test_asgard.rb`
|
|
|
41
43
|
| `lib/asgard/base.rb` | DSL engine; inherits Thor, includes Shell |
|
|
42
44
|
| `lib/asgard/shell.rb` | `sh` / `shebang` helpers |
|
|
43
45
|
| `lib/asgard/tasks.rb` | `class Tasks < Asgard::Base` — the convention class users reopen; also holds gem-owned built-in tasks |
|
|
46
|
+
| `lib/asgard/schedule.rb` | `Asgard::Schedule` — the `schedule` DSL registry; requires the files below |
|
|
47
|
+
| `lib/asgard/schedule/declaration.rb` | Pure declaration validation + the backend API contract |
|
|
48
|
+
| `lib/asgard/schedule/launchd.rb`, `systemd.rb` | Platform backends; shell out through an injectable runner |
|
|
49
|
+
| `lib/asgard/schedule/commands.rb` | `asgard schedule ...` subcommands (registered as `_schedule`, mapped to `schedule`) |
|
|
44
50
|
|
|
45
51
|
### Naming Convention for Gem-Owned Methods
|
|
46
52
|
|
|
@@ -100,7 +106,7 @@ Asgard adds the following `module_function` methods to `Kernel`, making them ava
|
|
|
100
106
|
|
|
101
107
|
## Testing
|
|
102
108
|
|
|
103
|
-
|
|
109
|
+
Engine tests are in `test/test_asgard.rb`; scheduling tests are in `test/test_schedule.rb`, which uses a `FakeRunner` (records launchctl/systemctl argv) so both backends test on any platform. SimpleCov minimum is 95%; the Rakefile configures this with a prelude that loads coverage before the library.
|
|
104
110
|
|
|
105
111
|
Key test patterns: tests frequently subclass `Asgard::Base` directly (not `Tasks`) to test the engine in isolation, and use `capture_io` for output assertions.
|
|
106
112
|
|
data/README.md
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- <strong>Dotenv Support</strong> — load <code>.env</code> files into the environment with <code>dotenv</code><br>
|
|
24
24
|
- <strong>Auto-Discovery</strong> — <code>.loki</code> root marker searched from CWD upward through parent directories<br>
|
|
25
25
|
- <strong>Multi-File Tasks</strong> — split tasks across <code>*.loki</code> files, loaded via <code>import</code> from your <code>.loki</code><br>
|
|
26
|
+
- <strong>Scheduled Tasks</strong> — run any task periodically under launchd (macOS) or systemd timers (Linux) with <code>schedule</code> and <code>asgard schedule install</code><br>
|
|
26
27
|
- <strong>Built-in Flags</strong> — <code>--debug</code>, <code>--verbose</code>, <code>--version</code>, and <code>--doctor</code> built-in class options; header/footer DSL for static help text<br>
|
|
27
28
|
</td>
|
|
28
29
|
</tr>
|
|
@@ -221,14 +222,28 @@ asgard ci executes:
|
|
|
221
222
|
ci
|
|
222
223
|
```
|
|
223
224
|
|
|
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:
|
|
225
|
+
`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
226
|
|
|
226
227
|
```ruby
|
|
227
228
|
depends_on -> { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
228
229
|
def quality = puts "running every *_check task..."
|
|
230
|
+
|
|
231
|
+
depends_on { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
232
|
+
def quality2 = puts "same, as a block..."
|
|
233
|
+
|
|
234
|
+
# do...end for a block spanning multiple statements — braces are for
|
|
235
|
+
# single-line blocks like the two above. The last expression is still what
|
|
236
|
+
# gets returned and validated.
|
|
237
|
+
depends_on do
|
|
238
|
+
checks = all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)
|
|
239
|
+
slow = %i[c_check]
|
|
240
|
+
|
|
241
|
+
[checks - slow]
|
|
242
|
+
end
|
|
243
|
+
def quality3 = puts "same, minus the slow checks, as a multi-line block..."
|
|
229
244
|
```
|
|
230
245
|
|
|
231
|
-
See [Dependencies](https://madbomber.github.io/asgard/dependencies/#dynamic-dependencies-proc-form) for the full explanation.
|
|
246
|
+
See [Dependencies](https://madbomber.github.io/asgard/dependencies/#dynamic-dependencies-proc-block-form) for the full explanation.
|
|
232
247
|
|
|
233
248
|
---
|
|
234
249
|
|
|
@@ -489,6 +504,33 @@ end
|
|
|
489
504
|
|
|
490
505
|
---
|
|
491
506
|
|
|
507
|
+
## Abbreviated command matching
|
|
508
|
+
|
|
509
|
+
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:
|
|
510
|
+
|
|
511
|
+
```ruby
|
|
512
|
+
class Tasks
|
|
513
|
+
desc "Compile the project"
|
|
514
|
+
def build = sh "rake build"
|
|
515
|
+
|
|
516
|
+
desc "Deploy to production"
|
|
517
|
+
def deploy = sh "cap production deploy"
|
|
518
|
+
|
|
519
|
+
desc "Deploy to staging"
|
|
520
|
+
def deploy_staging = sh "cap staging deploy"
|
|
521
|
+
end
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
asgard b # same as: asgard build — only task starting with "b"
|
|
526
|
+
asgard depl # Ambiguous command depl matches [deploy, deploy_staging]
|
|
527
|
+
asgard deploy # runs deploy — an exact match always wins, even over a shorter ambiguous prefix
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
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.
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
492
534
|
## Command aliases
|
|
493
535
|
|
|
494
536
|
`map` creates alternative names for a task:
|
|
@@ -564,6 +606,39 @@ See [`examples/server_subcommands.loki`](examples/server_subcommands.loki) and [
|
|
|
564
606
|
|
|
565
607
|
---
|
|
566
608
|
|
|
609
|
+
## Scheduled tasks
|
|
610
|
+
|
|
611
|
+
Any task can run on a schedule under the platform's own scheduler:
|
|
612
|
+
**launchd** on macOS, **systemd user timers** on Linux. Declare schedules at
|
|
613
|
+
class level in `.loki`:
|
|
614
|
+
|
|
615
|
+
```ruby
|
|
616
|
+
class Tasks
|
|
617
|
+
schedule :daily_summary, at: "17:30", on: :weekdays
|
|
618
|
+
schedule :backup, at: %w[02:00 14:00] # on: defaults to :daily
|
|
619
|
+
schedule :sync, every: 3600 # seconds
|
|
620
|
+
schedule :report, options: "--period week", at: "16:00", on: :friday
|
|
621
|
+
end
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
Then manage them with the built-in `schedule` command:
|
|
625
|
+
|
|
626
|
+
```bash
|
|
627
|
+
asgard schedule preview # job files install would write
|
|
628
|
+
asgard schedule install # load declared entries; drop undeclared ones
|
|
629
|
+
asgard schedule list # installed entries, state, last exit status
|
|
630
|
+
asgard schedule stop NAME # stop one entry (persists across reboots and installs)
|
|
631
|
+
asgard schedule start NAME # start a stopped entry, or install just this one
|
|
632
|
+
asgard schedule trigger NAME # run an installed entry now
|
|
633
|
+
asgard schedule log NAME [-f] # print (or follow) the entry's log
|
|
634
|
+
asgard schedule remove # remove all of this project's entries
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
Jobs run `asgard <task> [options]` from the directory holding `.loki`, with
|
|
638
|
+
the `PATH` captured at install time; a project `.envrc` is loaded at run time
|
|
639
|
+
through `direnv exec`. See [Scheduled Tasks](https://madbomber.github.io/asgard/schedule/)
|
|
640
|
+
for every keyword, entry naming, and file locations.
|
|
641
|
+
|
|
567
642
|
## `method_option` types reference
|
|
568
643
|
|
|
569
644
|
| Type | CLI example | Ruby value |
|
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
|
@@ -27,6 +27,8 @@ abort "asgard: unknown command '#{argv.first}'" if argv.first&.start_with?("_")
|
|
|
27
27
|
|
|
28
28
|
After loading task files, it calls `Tasks.validate_deps!` (circular dependency check) and `Tasks._reset_ran!` (clears per-invocation deduplication state) before starting Thor.
|
|
29
29
|
|
|
30
|
+
If the dispatched task returns exactly `:fail`, `run!` exits with status 1 (the quality-gate convention). Any other return value, including `:warn` and `:skip`, exits 0.
|
|
31
|
+
|
|
30
32
|
---
|
|
31
33
|
|
|
32
34
|
## Kernel Methods
|
|
@@ -111,12 +113,12 @@ import_up "*.loki" # find the nearest ancestor with *.loki files
|
|
|
111
113
|
|
|
112
114
|
| Method | Signature | Description |
|
|
113
115
|
|---|---|---|
|
|
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). |
|
|
116
|
+
| `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
117
|
| `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
118
|
| `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
119
|
| `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
120
|
| `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. |
|
|
121
|
+
| `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
122
|
| `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
123
|
| `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
124
|
| `_reset_ran!` | `Tasks._reset_ran!` | Clear the per-invocation task deduplication set. Called by `run!` before dispatching. Thread-safe via Mutex. |
|
|
@@ -175,12 +177,14 @@ depends_on :clean, :build # two sequential deps
|
|
|
175
177
|
depends_on [:lint, :typecheck] # lint and typecheck run in parallel
|
|
176
178
|
depends_on :setup, [:lint, :build], :test # setup, then lint+build concurrently, then test
|
|
177
179
|
|
|
178
|
-
# A sole Proc/lambda
|
|
179
|
-
# file has loaded — must return the same
|
|
180
|
+
# A sole Proc/lambda (or, equivalently, a block) defers resolution to
|
|
181
|
+
# validate_deps!, after every .loki file has loaded — must return the same
|
|
182
|
+
# shape the splat form above would; the shape is validated when it resolves.
|
|
180
183
|
depends_on -> { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
184
|
+
depends_on { [all_commands.keys.grep(/_check\z/).map(&:to_sym)] }
|
|
181
185
|
```
|
|
182
186
|
|
|
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.
|
|
187
|
+
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
188
|
|
|
185
189
|
---
|
|
186
190
|
|
|
@@ -196,6 +200,8 @@ See [Dynamic Dependencies](dependencies.md#dynamic-dependencies-proc-form) for t
|
|
|
196
200
|
| `class_option :doctor` | class option | `--doctor` flag. Handled by `Asgard.run!` before the `.loki` file is loaded — runs `Asgard::Doctor.new.run` and exits. `no_negate :doctor` suppresses the `[--no-doctor]` / `[--skip-doctor]` help entries. |
|
|
197
201
|
| `debug?` | Kernel module function | Returns `$DEBUG`. Available everywhere via `Kernel`. |
|
|
198
202
|
| `verbose?` | Kernel module function | Returns `$VERBOSE`. Available everywhere via `Kernel`. |
|
|
203
|
+
| `schedule` | class method (DSL) | `schedule :task, at:/every:, ...` — declares a scheduled entry. Extended from `Asgard::Schedule::DSL`; see [`Asgard::Schedule`](#asgardschedule). |
|
|
204
|
+
| `_schedule` | subcommand | `asgard schedule SUBCOMMAND`, backed by `Asgard::Schedule::Commands`. `map "schedule" => :_schedule` keeps the typed name `schedule`, and a user task named `schedule` cannot shadow it. |
|
|
199
205
|
|
|
200
206
|
---
|
|
201
207
|
|
|
@@ -265,6 +271,22 @@ The warning is a shadowed ancestor `.loki` marker one directory further up the t
|
|
|
265
271
|
|
|
266
272
|
---
|
|
267
273
|
|
|
274
|
+
## `Asgard::Schedule`
|
|
275
|
+
|
|
276
|
+
Scheduled tasks. The user-facing guide is [Scheduled Tasks](schedule.md); this section covers the Ruby API.
|
|
277
|
+
|
|
278
|
+
| Method | Signature | Description |
|
|
279
|
+
|---|---|---|
|
|
280
|
+
| `declare` | `Asgard::Schedule.declare(task, **settings) → Hash` | What `Tasks.schedule` calls. Validates via `normalize` and records the entry. Redeclaring an identical entry is a no-op; a different entry under the same name raises `ArgumentError`. |
|
|
281
|
+
| `declarations` | `Asgard::Schedule.declarations → Hash` | Declared entries for this run, keyed by entry name. |
|
|
282
|
+
| `normalize` | `Asgard::Schedule.normalize(task, options: nil, at: nil, on: :daily, every: nil, env: {}, as: nil) → Hash` | Validates one declaration and returns `{ name:, task:, args:, at:, on:, every:, env: }`. Pure. |
|
|
283
|
+
| `backend_class` | `Asgard::Schedule.backend_class(platform = RUBY_PLATFORM)` | `Launchd` on darwin, `Systemd` on linux; raises `Asgard::Schedule::Error` elsewhere. |
|
|
284
|
+
| `runner` / `runner=` | `Asgard::Schedule.runner → #call` | The command runner new backends use: `argv` in, `[output, success?]` out. Defaults to `RUNNER` (`Open3.capture2e`); tests assign a recording runner, and `nil` restores the default. |
|
|
285
|
+
|
|
286
|
+
Both backends, `Asgard::Schedule::Launchd` and `Asgard::Schedule::Systemd`, implement the same instance API: `files`, `install`, `uninstall`, `start`, `stop`, `trigger`, `installed_names`, `status`, `log_path`, `notes`. Each class also exposes the pure helpers that build its job files (`Launchd.plist`, `Systemd.service_unit`, `Systemd.timer_unit`, ...), so they can be tested without a scheduler.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
268
290
|
## `Asgard::Base` Internal Class Methods
|
|
269
291
|
|
|
270
292
|
These are implementation details exposed for extensibility. Prefer the DSL methods above in normal use.
|
|
@@ -296,6 +318,7 @@ These are implementation details exposed for extensibility. Prefer the DSL metho
|
|
|
296
318
|
| Class | Superclass | Description |
|
|
297
319
|
|---|---|---|
|
|
298
320
|
| `Asgard::Error` | `StandardError` | Base error class for all Asgard errors. |
|
|
321
|
+
| `Asgard::Schedule::Error` | `Asgard::Error` | Raised when a `launchctl`/`systemctl` command fails, or on an unsupported platform. `run!` reports it as `asgard: <message>`. |
|
|
299
322
|
| `Asgard::CircularDependencyError` | `Asgard::Error` | Raised by `validate_deps!` when a cycle is detected in the dependency graph. `run!` catches this and calls `abort` with a clean message. |
|
|
300
323
|
|
|
301
324
|
```ruby
|
data/docs/changelog.md
CHANGED
|
@@ -1,132 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
All notable changes to Asgard are documented here.
|
|
4
|
-
|
|
5
|
-
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Asgard adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## [Unreleased]
|
|
10
|
-
|
|
11
|
-
### Added
|
|
12
|
-
|
|
13
|
-
- **`--doctor` built-in CLI flag** — diagnoses `.loki` resolution, import chains, and task definitions for the current directory. Handled directly in `Asgard.run!` (same pattern as `--version`), so it works even when a broken file, a circular/undefined dependency, or a silently redefined task would otherwise abort the whole process. Backed by the new `Asgard::Doctor` class. Includes a "Tasks by file" listing — every command grouped by the file it's defined in, as `file:line`, with any silently-overridden task annotated inline (`OVERRIDDEN by ...` / `active — redefines ...`). See [API Reference](api.md#asgarddoctor).
|
|
14
|
-
- **`helper` DSL method** — defines a method available in both class context (e.g. inside `header`) and instance context (inside task methods) with a single declaration. Eliminates the manual `def self.name` + `no_commands { private def name = self.class.name }` boilerplate. Supports positional arguments, keyword arguments, and block arguments. See [Helper Methods](helpers.md).
|
|
15
|
-
- **Flay and Reek quality gates** — `flay_check` checks for structural duplication (mass ≥ 150); `reek` checks code smells. Reviewed findings are grandfathered precisely, per method and detector, via `reek --todo`-generated `exclude:` entries in `.reek.yml` — a genuinely new smell still fails the gate even at an already-reviewed method.
|
|
16
|
-
- **`test_verbose`, `console` tasks** — verbose test output and an IRB console with the gem loaded.
|
|
17
|
-
- **`git.loki`** — per-repo `push`/`pull`/`fetch` tasks.
|
|
18
|
-
- **`typos_check` / `typos_fix` tasks** — spell-checking via the external `typos` CLI (`brew install typos-cli`). Reports `SKIP` (not a failure) if `typos` isn't installed, with a one-line install hint.
|
|
19
|
-
- **`fasterer_check` task** — performance-idiom suggestions from the `fasterer` gem, reported as `WARN` (non-blocking).
|
|
20
|
-
- **`SKIP` / `WARN` quality-gate statuses** — alongside `PASS`/`FAIL`; only `FAIL` blocks `quality`.
|
|
21
|
-
- Every quality gate now writes full detail to a `<gate>_output.txt` file (gitignored) and prints just a one-line summary to stdout.
|
|
22
|
-
- **`asgard tree`** now shows the project header/footer, matching `asgard help`.
|
|
23
|
-
- **`bundler_audit_check` task** — `bundle-audit check --update` against `Gemfile.lock`; `FAIL` on any known vulnerability.
|
|
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).
|
|
26
|
-
|
|
27
|
-
### Changed
|
|
28
|
-
|
|
29
|
-
- **`quality` task** — discovers every `*_check` task at run time (via a `depends_on` Proc) rather than a fixed list, and runs them all in parallel with a colorized PASS/FAIL/WARN/SKIP summary and tally.
|
|
30
|
-
- **`test`, `rubocop`, `reek` renamed to `test_check`, `rubocop_check`, `reek_check`** — consistency with the other gates is what makes `quality`'s automatic discovery possible.
|
|
31
|
-
- **`release` task** — prompts for confirmation unless `-y`/`--yes` is passed.
|
|
32
|
-
- **`Asgard::Base` and `Asgard::Doctor` split into mixins** — `lib/asgard/base/{registry,dependency_graph,task_dsl,dispatch}.rb` and `lib/asgard/doctor/{task_sections,report}.rb`. No behavior change; drops both classes' Reek `TooManyMethods`/`TooManyInstanceVariables` warnings to zero.
|
|
33
|
-
|
|
34
|
-
### Fixed
|
|
35
|
-
|
|
36
|
-
- **`bin/asgard`** — switched from `require "asgard"` to `require_relative "../lib/asgard"` so the executable always loads the library shipped alongside it, instead of whatever `asgard` gem happens to be installed separately.
|
|
37
|
-
|
|
38
|
-
### Removed
|
|
39
|
-
|
|
40
|
-
- **`reek_baseline` / `ensure_quality_dir` tasks and `.quality/`** — superseded by precise per-method `exclude:` entries in `.reek.yml` (see the Reek gate entry above).
|
|
41
|
-
- **`var` DSL method** — replaced by native Ruby class variables. Use `@@name ||= "value".freeze` in the class body. Class variables are visible in all task instance methods and in subcommand subclasses, making them the correct tool for shared configuration in a Thor-based task runner. See [Variables](variables.md).
|
|
42
|
-
|
|
43
|
-
## [0.2.0] — 2026-05-29
|
|
44
|
-
|
|
45
|
-
### Changed
|
|
46
|
-
|
|
47
|
-
- `*.loki` files are no longer auto-loaded by default. Pass `--auto-load` to `asgard` to load all `*.loki` files from the project root alphabetically before `.loki`. This is a breaking change for projects using the multi-file layout.
|
|
48
|
-
- Added `--auto-load` as a built-in CLI flag in `Tasks`, visible in `asgard help`
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## [0.1.2] — 2026-05-29
|
|
53
|
-
|
|
54
|
-
### Added
|
|
55
|
-
|
|
56
|
-
- `--version` built-in CLI flag — prints `Asgard::VERSION` and exits; implemented as a `_`-prefixed method in `Tasks` per the gem-owned naming convention
|
|
57
|
-
- `--debug` and `--verbose` built-in `class_option` declarations on `Tasks` — set `$DEBUG`/`$VERBOSE` before any task runs via the `invoke_command` hook in `Asgard::Base`
|
|
58
|
-
- `debug?` and `verbose?` private predicate helpers on `Tasks` — thin wrappers around `$DEBUG` and `$VERBOSE` for use inside task bodies
|
|
59
|
-
- `_` prefix convention for gem-owned methods in `Tasks` — built-in methods use `_` prefix to distinguish them from user-defined tasks
|
|
60
|
-
- `run!` guards against direct invocation of `_`-prefixed commands with a clean error message and exit 1
|
|
61
|
-
- `examples/` directory with working `.loki` files:
|
|
62
|
-
- `kitchen_sink.loki` — demonstrates the full Thor DSL (all option types, `long_desc`, `class_option`, `default_task`, `map`, `depends_on`, `var`, `no_commands`, `private`)
|
|
63
|
-
- `server_subcommands.loki` — subcommand group for server management
|
|
64
|
-
- `db_subcommands.loki` — subcommand group for database management with `depends_on` chaining
|
|
65
|
-
- `concurrent.loki` — demonstrates parallel task execution with interleaved thread output
|
|
66
|
-
- README sections: Helper methods, Subcommands, Thor wrapper callout
|
|
67
|
-
|
|
68
|
-
### Fixed
|
|
69
|
-
|
|
70
|
-
- Replaced `warn`/`exit 1` with `abort` throughout `run!` — `Kernel#warn` is silenced when `$VERBOSE = nil`, which is the default in Ruby 4.0; `abort` writes to `$stderr` regardless
|
|
71
|
-
|
|
72
|
-
### Changed
|
|
73
|
-
|
|
74
|
-
- `--debug` and `--verbose` promoted from mapped tasks to `class_option` — they now work as modifiers alongside other commands (e.g. `asgard build --debug`) rather than as standalone commands
|
|
75
|
-
- Removed all references to `just` task runner and `recipe` terminology; Asgard uses "task" throughout
|
|
76
|
-
- `depends_on` parameter renamed from `*recipes` to `*tasks` for consistency
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## [0.1.1] — 2026-05-28
|
|
81
|
-
|
|
82
|
-
### Added
|
|
83
|
-
|
|
84
|
-
- Parallel dependency execution — wrap deps in an array to run them concurrently:
|
|
85
|
-
`depends_on [:build, :lint]` or `depends_on :setup, [:build, :lint], :deploy`
|
|
86
|
-
- `Asgard.run!(argv)` — single entry point encapsulating find, load, validate, and start
|
|
87
|
-
- `Asgard.load_loki(dir)` — auto-loads all `*.loki` files in a directory alphabetically
|
|
88
|
-
- `Tasks` class pre-defined by the gem (`class Tasks < Asgard::Base`) — task files reopen it without restating the superclass
|
|
89
|
-
- `lib/asgard/tasks.rb` — ships the pre-defined `Tasks` class
|
|
90
|
-
|
|
91
|
-
### Changed
|
|
92
|
-
|
|
93
|
-
- Replaced `SimpleFlow` dependency with `Dagwood` — purpose-built DAG library with no extra dependencies and no Ruby 4 compatibility issues
|
|
94
|
-
- `bin/asgard` simplified to two lines: `require "asgard"` + `Asgard.run!(ARGV)`
|
|
95
|
-
- Task file convention: `.loki` is the project root marker and entry point; `*.loki` files each reopen `class Tasks` and are auto-loaded before `.loki`
|
|
96
|
-
- `Asgard.find_task_files` renamed to `Asgard.find_task_file` (singular — only `.loki` is the entry point)
|
|
97
|
-
- `depends_on` now accepts mixed sequential/parallel stages; bare symbols run sequentially, arrays within the splat run in parallel
|
|
98
|
-
- `run!` handles its own errors — missing `.loki` and circular dependencies produce a clean one-line message and exit 1 rather than a backtrace
|
|
99
|
-
- Thread-safe dep deduplication via class-level `_ran_tasks` Set + Mutex replaces Thor's `@_invocations`
|
|
100
|
-
- Removed `import` macro — task files use Ruby class reopening instead of modules
|
|
101
|
-
|
|
102
|
-
### Removed
|
|
103
|
-
|
|
104
|
-
- `SimpleFlow` dependency (replaced by `Dagwood`)
|
|
105
|
-
- `logger` gem workaround (was only needed for SimpleFlow on Ruby 4)
|
|
106
|
-
- `*.loki` glob fallback in `find_task_file` — only `.loki` is the auto-discovered entry point
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## [0.1.0] — 2026-05-28
|
|
111
|
-
|
|
112
|
-
### Added
|
|
113
|
-
|
|
114
|
-
- `Asgard::Base` — Thor subclass providing the task DSL
|
|
115
|
-
- `depends_on` — declare task dependencies; dependencies run at most once per invocation
|
|
116
|
-
- `var` — declare static or lazy-evaluated variables available to all tasks
|
|
117
|
-
- `import` — flat-merge a task module into the current class
|
|
118
|
-
- `dotenv` — load a `.env` file into the environment
|
|
119
|
-
- `sh` — run a shell command or multiline heredoc script; exits with the command's status on failure
|
|
120
|
-
- `shebang` — write a script body to a tempfile and execute it with a given interpreter (`:python3`, `:node`, `:ruby`, `:perl`, `:bash`, `:sh`, or any custom interpreter)
|
|
121
|
-
- `Asgard.find_task_files` — search current directory and ancestors for task files
|
|
122
|
-
- Task file resolution: `.loki` takes priority; falls back to all `*.loki` files sorted alphabetically
|
|
123
|
-
- `asgard` executable — finds task files, validates dependency graph, dispatches via Thor
|
|
124
|
-
- Circular dependency detection via `SimpleFlow::DependencyGraph` at startup
|
|
125
|
-
- 100% test coverage enforced via SimpleCov (95% minimum threshold)
|
|
126
|
-
- Quality task in `.loki` runs flog after tests
|
|
127
|
-
|
|
128
|
-
[Unreleased]: https://github.com/MadBomber/asgard/compare/v0.2.0...HEAD
|
|
129
|
-
[0.2.0]: https://github.com/MadBomber/asgard/compare/v0.1.2...v0.2.0
|
|
130
|
-
[0.1.2]: https://github.com/MadBomber/asgard/compare/v0.1.1...v0.1.2
|
|
131
|
-
[0.1.1]: https://github.com/MadBomber/asgard/compare/v0.1.0...v0.1.1
|
|
132
|
-
[0.1.0]: https://github.com/MadBomber/asgard/releases/tag/v0.1.0
|
|
1
|
+
--8<-- "CHANGELOG.md"
|