asgard 0.1.1 → 0.2.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.
@@ -0,0 +1,93 @@
1
+ /* ==========================================================================
2
+ Asgard documentation — custom styles
3
+ Material theme handles the heavy lifting; this file adds targeted polish.
4
+ ========================================================================== */
5
+
6
+ /* --------------------------------------------------------------------------
7
+ .loki filename display
8
+ Used whenever the filename ".loki" or "*.loki" appears inline or in code.
9
+ -------------------------------------------------------------------------- */
10
+
11
+ /* Give inline code that looks like a .loki filename a subtle Norse-gold tint */
12
+ code:is([class*="language-"]) .token.string:has-text(".loki"),
13
+ .md-typeset code.loki-file {
14
+ color: var(--md-accent-fg-color);
15
+ font-weight: 600;
16
+ }
17
+
18
+ /* Highlight .loki filenames in directory tree code blocks */
19
+ .md-typeset .highlight .filename {
20
+ background-color: color-mix(in srgb, var(--md-primary-fg-color) 12%, transparent);
21
+ border-bottom: 2px solid var(--md-accent-fg-color);
22
+ border-radius: 4px 4px 0 0;
23
+ color: var(--md-default-fg-color);
24
+ font-size: 0.75rem;
25
+ font-weight: 600;
26
+ letter-spacing: 0.04em;
27
+ padding: 0.3rem 0.8rem;
28
+ }
29
+
30
+ /* --------------------------------------------------------------------------
31
+ Admonition tweaks — slightly warmer warning border for the "argument"
32
+ class-level scope warning that appears on the tasks page.
33
+ -------------------------------------------------------------------------- */
34
+
35
+ .md-typeset .admonition.warning,
36
+ .md-typeset details.warning {
37
+ border-left-color: #e6a817;
38
+ }
39
+
40
+ .md-typeset .admonition.warning > .admonition-title,
41
+ .md-typeset details.warning > summary {
42
+ background-color: rgba(230, 168, 23, 0.12);
43
+ }
44
+
45
+ /* --------------------------------------------------------------------------
46
+ Table of Contents — emphasise the current section a touch more
47
+ -------------------------------------------------------------------------- */
48
+
49
+ .md-nav__link--active {
50
+ font-weight: 600;
51
+ }
52
+
53
+ /* --------------------------------------------------------------------------
54
+ Home page feature table (HTML table in index.md)
55
+ -------------------------------------------------------------------------- */
56
+
57
+ .md-typeset table:not([class]) td {
58
+ vertical-align: top;
59
+ }
60
+
61
+ /* --------------------------------------------------------------------------
62
+ Execution diagram in dependencies.md — keep it tight and readable
63
+ -------------------------------------------------------------------------- */
64
+
65
+ .md-typeset pre code {
66
+ font-size: 0.85em;
67
+ line-height: 1.5;
68
+ }
69
+
70
+ /* --------------------------------------------------------------------------
71
+ Subtle Norse-shield watermark on the hero block (index page only).
72
+ Relies on the Material "primary: indigo" palette.
73
+ -------------------------------------------------------------------------- */
74
+
75
+ .md-header {
76
+ box-shadow: 0 2px 8px rgba(63, 81, 181, 0.25);
77
+ }
78
+
79
+ /* --------------------------------------------------------------------------
80
+ Code annotations — keep them readable across both light and dark palettes
81
+ -------------------------------------------------------------------------- */
82
+
83
+ .md-typeset .md-annotation__index > * {
84
+ font-size: 0.7rem;
85
+ }
86
+
87
+ /* --------------------------------------------------------------------------
88
+ Task-runner specific: distinguish shell prompt lines from output lines
89
+ in bash code blocks by dimming lines that don't start with '#' or 'asgard'
90
+ -------------------------------------------------------------------------- */
91
+
92
+ /* (Future: add targeted styles once MkDocs Material supports per-line
93
+ highlighting via config; for now this is intentionally minimal.) */
Binary file
data/docs/changelog.md ADDED
@@ -0,0 +1,100 @@
1
+ # Changelog
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
+ ## [0.2.0] — 2026-05-29
12
+
13
+ ### Changed
14
+
15
+ - `*.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.
16
+ - Added `--auto-load` as a built-in CLI flag in `Tasks`, visible in `asgard help`
17
+
18
+ ---
19
+
20
+ ## [0.1.2] — 2026-05-29
21
+
22
+ ### Added
23
+
24
+ - `--version` built-in CLI flag — prints `Asgard::VERSION` and exits; implemented as a `_`-prefixed method in `Tasks` per the gem-owned naming convention
25
+ - `--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`
26
+ - `debug?` and `verbose?` private predicate helpers on `Tasks` — thin wrappers around `$DEBUG` and `$VERBOSE` for use inside task bodies
27
+ - `_` prefix convention for gem-owned methods in `Tasks` — built-in methods use `_` prefix to distinguish them from user-defined tasks
28
+ - `run!` guards against direct invocation of `_`-prefixed commands with a clean error message and exit 1
29
+ - `examples/` directory with working `.loki` files:
30
+ - `kitchen_sink.loki` — demonstrates the full Thor DSL (all option types, `long_desc`, `class_option`, `default_task`, `map`, `depends_on`, `var`, `no_commands`, `private`)
31
+ - `server_subcommands.loki` — subcommand group for server management
32
+ - `db_subcommands.loki` — subcommand group for database management with `depends_on` chaining
33
+ - `concurrent.loki` — demonstrates parallel task execution with interleaved thread output
34
+ - README sections: Helper methods, Subcommands, Thor wrapper callout
35
+
36
+ ### Fixed
37
+
38
+ - 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
39
+
40
+ ### Changed
41
+
42
+ - `--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
43
+ - Removed all references to `just` task runner and `recipe` terminology; Asgard uses "task" throughout
44
+ - `depends_on` parameter renamed from `*recipes` to `*tasks` for consistency
45
+
46
+ ---
47
+
48
+ ## [0.1.1] — 2026-05-28
49
+
50
+ ### Added
51
+
52
+ - Parallel dependency execution — wrap deps in an array to run them concurrently:
53
+ `depends_on [:build, :lint]` or `depends_on :setup, [:build, :lint], :deploy`
54
+ - `Asgard.run!(argv)` — single entry point encapsulating find, load, validate, and start
55
+ - `Asgard.load_loki(dir)` — auto-loads all `*.loki` files in a directory alphabetically
56
+ - `Tasks` class pre-defined by the gem (`class Tasks < Asgard::Base`) — task files reopen it without restating the superclass
57
+ - `lib/asgard/tasks.rb` — ships the pre-defined `Tasks` class
58
+
59
+ ### Changed
60
+
61
+ - Replaced `SimpleFlow` dependency with `Dagwood` — purpose-built DAG library with no extra dependencies and no Ruby 4 compatibility issues
62
+ - `bin/asgard` simplified to two lines: `require "asgard"` + `Asgard.run!(ARGV)`
63
+ - Task file convention: `.loki` is the project root marker and entry point; `*.loki` files each reopen `class Tasks` and are auto-loaded before `.loki`
64
+ - `Asgard.find_task_files` renamed to `Asgard.find_task_file` (singular — only `.loki` is the entry point)
65
+ - `depends_on` now accepts mixed sequential/parallel stages; bare symbols run sequentially, arrays within the splat run in parallel
66
+ - `run!` handles its own errors — missing `.loki` and circular dependencies produce a clean one-line message and exit 1 rather than a backtrace
67
+ - Thread-safe dep deduplication via class-level `_ran_tasks` Set + Mutex replaces Thor's `@_invocations`
68
+ - Removed `import` macro — task files use Ruby class reopening instead of modules
69
+
70
+ ### Removed
71
+
72
+ - `SimpleFlow` dependency (replaced by `Dagwood`)
73
+ - `logger` gem workaround (was only needed for SimpleFlow on Ruby 4)
74
+ - `*.loki` glob fallback in `find_task_file` — only `.loki` is the auto-discovered entry point
75
+
76
+ ---
77
+
78
+ ## [0.1.0] — 2026-05-28
79
+
80
+ ### Added
81
+
82
+ - `Asgard::Base` — Thor subclass providing the task DSL
83
+ - `depends_on` — declare task dependencies; dependencies run at most once per invocation
84
+ - `var` — declare static or lazy-evaluated variables available to all tasks
85
+ - `import` — flat-merge a task module into the current class
86
+ - `dotenv` — load a `.env` file into the environment
87
+ - `sh` — run a shell command or multiline heredoc script; exits with the command's status on failure
88
+ - `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)
89
+ - `Asgard.find_task_files` — search current directory and ancestors for task files
90
+ - Task file resolution: `.loki` takes priority; falls back to all `*.loki` files sorted alphabetically
91
+ - `asgard` executable — finds task files, validates dependency graph, dispatches via Thor
92
+ - Circular dependency detection via `SimpleFlow::DependencyGraph` at startup
93
+ - 100% test coverage enforced via SimpleCov (95% minimum threshold)
94
+ - Quality task in `.loki` runs flog after tests
95
+
96
+ [Unreleased]: https://github.com/MadBomber/asgard/compare/v0.2.0...HEAD
97
+ [0.2.0]: https://github.com/MadBomber/asgard/compare/v0.1.2...v0.2.0
98
+ [0.1.2]: https://github.com/MadBomber/asgard/compare/v0.1.1...v0.1.2
99
+ [0.1.1]: https://github.com/MadBomber/asgard/compare/v0.1.0...v0.1.1
100
+ [0.1.0]: https://github.com/MadBomber/asgard/releases/tag/v0.1.0
@@ -0,0 +1,221 @@
1
+ # Task Dependencies
2
+
3
+ `depends_on` declares what must run before a task. Asgard resolves the dependency graph at startup, validates it for cycles, and executes prerequisites automatically when a task is invoked.
4
+
5
+ !!! note
6
+ `desc` and `depends_on` are independent — either can come first. Both must appear before the `def`.
7
+
8
+ ---
9
+
10
+ ## How It Works
11
+
12
+ When you run `asgard <task>`, Asgard:
13
+
14
+ 1. Validates the full dependency graph for circular references (fails fast with a clear error).
15
+ 2. Resolves the dependency stages for the requested task in order.
16
+ 3. Executes each stage — running parallel groups in native Ruby threads.
17
+ 4. Runs the task itself after all prerequisites complete.
18
+
19
+ **Deduplication:** each task runs at most once per `asgard` invocation, regardless of how many other tasks declare it as a dependency. This is enforced thread-safely via a class-level `Set` and `Mutex`.
20
+
21
+ ---
22
+
23
+ ## Sequential Dependencies
24
+
25
+ Bare symbols run one after another in the order declared:
26
+
27
+ ```ruby
28
+ class Tasks
29
+ desc "build", "Compile the project"
30
+ def build = sh "rake build"
31
+
32
+ depends_on :build
33
+ desc "test", "Run the test suite"
34
+ def test = sh "rake test"
35
+
36
+ depends_on :test
37
+ desc "release", "Publish the gem"
38
+ def release = sh "bundle exec rake release"
39
+ end
40
+ ```
41
+
42
+ ```bash
43
+ asgard release # build → test → release
44
+ ```
45
+
46
+ Multiple sequential dependencies in a single `depends_on` call run left to right:
47
+
48
+ ```ruby
49
+ depends_on :clean, :build, :test
50
+ desc "package", "Clean, build, and test"
51
+ def package = sh "rake package"
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Parallel Dependencies
57
+
58
+ Wrap symbols in an array to declare they can run concurrently. Asgard waits for all tasks in a parallel group to finish before moving to the next stage:
59
+
60
+ ```ruby
61
+ class Tasks
62
+ desc "lint", "Check code style"
63
+ def lint = sh "bundle exec rubocop"
64
+
65
+ desc "typecheck", "Run type checks"
66
+ def typecheck = sh "bundle exec srb tc"
67
+
68
+ depends_on [:lint, :typecheck]
69
+ desc "test", "Run tests (after lint and typecheck)"
70
+ def test = sh "bundle exec rake test"
71
+ end
72
+ ```
73
+
74
+ ```bash
75
+ asgard test # lint ∥ typecheck → test
76
+ ```
77
+
78
+ Parallel groups run in native Ruby threads. For CPU-bound work, keep in mind the GVL; for I/O-bound work (shell commands, network), true concurrency is achieved.
79
+
80
+ ---
81
+
82
+ ## Mixed Sequential and Parallel
83
+
84
+ Mix bare symbols and arrays in a single `depends_on` call. Execution proceeds stage by stage — each stage completes before the next begins:
85
+
86
+ ```ruby
87
+ class Tasks
88
+ desc "setup", "Install dependencies"; def setup = sh "bundle install"
89
+ desc "lint", "Check code style"; def lint = sh "bundle exec rubocop"
90
+ desc "build", "Compile assets"; def build = sh "rake assets:precompile"
91
+ desc "test", "Run tests"; def test = sh "bundle exec rake test"
92
+ desc "notify", "Post to Slack"; def notify = sh "curl $SLACK_WEBHOOK -d '{\"text\":\"done\"}'"
93
+
94
+ # setup first, then lint+build in parallel, then test, then notify
95
+ depends_on :setup, [:lint, :build], :test, :notify
96
+ desc "ci", "Full CI pipeline"
97
+ def ci = sh "echo 'CI complete'"
98
+ end
99
+ ```
100
+
101
+ ```bash
102
+ asgard ci
103
+ ```
104
+
105
+ Execution order:
106
+
107
+ ```
108
+ setup
109
+
110
+ lint ∥ build (concurrent)
111
+
112
+ test
113
+
114
+ notify
115
+
116
+ ci
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Deduplication
122
+
123
+ Each task runs at most once per `asgard` invocation. If multiple tasks declare the same dependency, it executes only on its first encounter:
124
+
125
+ ```ruby
126
+ class Tasks
127
+ desc "setup", "Install gems"
128
+ def setup = sh "bundle install"
129
+
130
+ depends_on :setup
131
+ desc "test", "Run tests"
132
+ def test = sh "rake test"
133
+
134
+ depends_on :setup
135
+ desc "lint", "Check style"
136
+ def lint = sh "rubocop"
137
+
138
+ depends_on [:test, :lint]
139
+ desc "ci", "Test and lint (setup runs once)"
140
+ def ci = puts "done"
141
+ end
142
+ ```
143
+
144
+ When `asgard ci` runs, `setup` executes once even though both `test` and `lint` declare it as a dependency. The deduplication set is managed with a `Mutex` so parallel groups are also safe.
145
+
146
+ ---
147
+
148
+ ## Circular Dependency Detection
149
+
150
+ Asgard validates the full dependency graph using [Dagwood](https://rubygems.org/gems/dagwood) before any task runs. A circular dependency produces a clean error and exits:
151
+
152
+ ```ruby
153
+ class Tasks
154
+ depends_on :b
155
+ desc "a", "Task A"; def a = puts "a"
156
+
157
+ depends_on :a
158
+ desc "b", "Task B"; def b = puts "b"
159
+ end
160
+ ```
161
+
162
+ ```bash
163
+ asgard a
164
+ # asgard: circular dependency — TSort::Cyclic: ...
165
+ ```
166
+
167
+ No backtrace is shown — just a single diagnostic line.
168
+
169
+ ---
170
+
171
+ ## depends_on Across Multiple Files
172
+
173
+ `depends_on` works across `.loki` files because all files reopen the same `class Tasks`. The dependency is recorded when the `def` is encountered, so load order matters:
174
+
175
+ ```ruby
176
+ # build.loki
177
+ class Tasks
178
+ desc "build", "Compile"
179
+ def build = sh "rake build"
180
+ end
181
+
182
+ # test.loki
183
+ class Tasks
184
+ depends_on :build # build.loki must be loaded first
185
+ desc "test", "Test"
186
+ def test = sh "rake test"
187
+ end
188
+ ```
189
+
190
+ When `--auto-load` is used, `*.loki` files are loaded alphabetically, so `build.loki` loads before `test.loki`. If you need to control load order, use explicit `require_relative` from `.loki`.
191
+
192
+ ---
193
+
194
+ ## depends_on Inside Subcommands
195
+
196
+ `depends_on` works within subcommand classes exactly as it does at the top level. Dependency scope is per-class:
197
+
198
+ ```ruby
199
+ class DBCommands < Tasks
200
+ desc "migrate", "Run migrations"
201
+ def migrate = sh "rails db:migrate"
202
+
203
+ desc "seed", "Load seed data"
204
+ def seed = sh "rails db:seed"
205
+
206
+ depends_on :migrate, :seed
207
+ desc "reset", "Migrate then seed"
208
+ def reset = puts "Done."
209
+ end
210
+
211
+ class Tasks
212
+ desc "db SUBCOMMAND", "Manage the database"
213
+ subcommand "db", DBCommands
214
+ end
215
+ ```
216
+
217
+ ```bash
218
+ asgard db reset # migrate → seed → reset
219
+ ```
220
+
221
+ See [Subcommands](subcommands.md) for the full guide.
@@ -0,0 +1,113 @@
1
+ # Environment Variables
2
+
3
+ Asgard provides the `dotenv` class method to load `.env` files into the process environment before tasks run. It is a thin wrapper around the [dotenv gem](https://github.com/bkeepers/dotenv).
4
+
5
+ ---
6
+
7
+ ## Basic Usage
8
+
9
+ Call `dotenv` inside the class body (not inside a task method) to load the default `.env` file:
10
+
11
+ ```ruby
12
+ class Tasks
13
+ dotenv # loads .env from the current working directory
14
+
15
+ desc "check", "Print the app name from .env"
16
+ def check = sh "echo $APP_NAME"
17
+ end
18
+ ```
19
+
20
+ ### Load a Named File
21
+
22
+ Pass a file path string to load a specific file:
23
+
24
+ ```ruby
25
+ class Tasks
26
+ dotenv ".env.local" # load a local override
27
+ dotenv ".env.staging" # load staging-specific vars
28
+ end
29
+ ```
30
+
31
+ ### Multiple Calls
32
+
33
+ Call `dotenv` multiple times to load several files. Each call merges the loaded variables into `ENV`. Later calls do not overwrite variables already set by earlier calls (standard dotenv behavior):
34
+
35
+ ```ruby
36
+ class Tasks
37
+ dotenv # loads .env (base config)
38
+ dotenv ".env.local" # loads .env.local (local overrides)
39
+ end
40
+ ```
41
+
42
+ ---
43
+
44
+ ## When `dotenv` Runs
45
+
46
+ `dotenv` is a **class-level** call — it executes at Ruby class-load time, not when a task is invoked. This means:
47
+
48
+ 1. Variables are available in `ENV` before any task method runs.
49
+ 2. They are also available to lambda variables declared with `var` that reference `ENV`.
50
+ 3. They are available during `depends_on` dependency resolution.
51
+
52
+ ```ruby
53
+ class Tasks
54
+ dotenv
55
+
56
+ var :database_url, -> { ENV.fetch("DATABASE_URL") }
57
+
58
+ desc "migrate", "Run migrations"
59
+ def migrate = sh "DATABASE_URL=#{database_url} rails db:migrate"
60
+ end
61
+ ```
62
+
63
+ !!! warning
64
+ If `.env` does not exist, `dotenv` silently does nothing — it checks `File.exist?` before loading. There is no error for a missing file.
65
+
66
+ ---
67
+
68
+ ## File Not Found
69
+
70
+ Asgard calls `Dotenv.load(path)` only when `File.exist?(path)` is true. If the file is absent, the call is a no-op:
71
+
72
+ ```ruby
73
+ class Tasks
74
+ dotenv ".env.local" # silently skipped if .env.local does not exist
75
+ end
76
+ ```
77
+
78
+ This makes it safe to commit a `.env.local` line to your `.loki` without requiring every developer to create the file.
79
+
80
+ ---
81
+
82
+ ## Environment Variables vs. `var`
83
+
84
+ Use `dotenv` to bring external configuration into `ENV`, and `var` to define task-internal computed values:
85
+
86
+ ```ruby
87
+ class Tasks
88
+ dotenv
89
+
90
+ # Read from ENV (set by dotenv or the shell)
91
+ var :app_name, -> { ENV.fetch("APP_NAME", "myapp") }
92
+ var :port, -> { ENV.fetch("PORT", "3000").to_i }
93
+
94
+ desc "start", "Start the server"
95
+ def start = sh "puma -p #{port}"
96
+ end
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Dotenv File Format
102
+
103
+ Standard dotenv file syntax applies:
104
+
105
+ ```bash
106
+ # .env
107
+ APP_NAME=myapp
108
+ DATABASE_URL=postgres://localhost/myapp_development
109
+ REDIS_URL=redis://localhost:6379/0
110
+ PORT=3000
111
+ ```
112
+
113
+ Multi-line values and quotes are supported per the dotenv gem's own documentation.
data/docs/examples.md ADDED
@@ -0,0 +1,140 @@
1
+ # Examples
2
+
3
+ The `examples/` directory in the Asgard repository contains complete, working `.loki` files that demonstrate every feature of the gem. You can use them as a standalone Asgard project or as copy-paste references.
4
+
5
+ ---
6
+
7
+ ## Using the Examples Directory
8
+
9
+ The `examples/` directory contains its own `.loki` root marker (the gem's project-level `.loki`), which means you can run `asgard` from inside the `examples/` directory and all example files will be loaded:
10
+
11
+ ```bash
12
+ git clone https://github.com/MadBomber/asgard.git
13
+ cd asgard/examples
14
+ asgard help
15
+ ```
16
+
17
+ Alternatively, copy individual example files into your own project's directory.
18
+
19
+ !!! note
20
+ Some examples (notably `concurrent.loki`) produce visible interleaved output to demonstrate real thread concurrency. They are designed to be run, not just read.
21
+
22
+ ---
23
+
24
+ ## `kitchen_sink.loki`
25
+
26
+ **Path:** `examples/kitchen_sink.loki`
27
+
28
+ The most comprehensive example — demonstrates every Thor DSL feature available in Asgard:
29
+
30
+ - `var` with a static value and a lazy lambda
31
+ - `dotenv` (commented out, ready to activate)
32
+ - `class_option` with `:boolean` and `:string` types, including `enum`
33
+ - `default_task` — sets the default command when `asgard` is run with no arguments
34
+ - `map` — short aliases for multiple tasks
35
+ - A basic task with no parameters
36
+ - A task with a positional parameter and default
37
+ - A task with `option` (the `method_option` alias)
38
+ - All five `method_option` types: `:string`, `:boolean`, `:numeric`, `:array`, `:hash`
39
+ - `required` option, `enum` validation, and `banner` customization
40
+ - `long_desc` with `\x5` line-break trick for formatted examples in help text
41
+ - Sequential `depends_on` (`:analyze` before `:spec`)
42
+ - Parallel `depends_on` (`[:analyze, :typecheck]` run concurrently)
43
+ - Mixed sequential + parallel `depends_on` (`:check`, `[:compile, :spec]`, `:pack`)
44
+ - `no_commands` block for a public helper excluded from CLI
45
+ - `private` methods for internal helpers
46
+
47
+ ```bash
48
+ asgard help # see all tasks
49
+ asgard greet # default task
50
+ asgard hello Alice
51
+ asgard compile --jobs 4 --tags debug release --defines VERSION:2 MODE:fast
52
+ asgard deploy --strategy rolling
53
+ asgard report --format html --since 2024-01-01
54
+ asgard pipeline
55
+ ```
56
+
57
+ ---
58
+
59
+ ## `server_subcommands.loki`
60
+
61
+ **Path:** `examples/server_subcommands.loki`
62
+
63
+ Demonstrates Thor subcommands with a server management group. Covers:
64
+
65
+ - Defining a subcommand class (`ServerCommands < Tasks`)
66
+ - Registering it with `subcommand "server", ServerCommands`
67
+ - Per-command options (`--daemon`, `--workers`, `--log`, `--force`, `--wait`)
68
+ - `depends_on` inside a subcommand group (`:stop` and `:start` before `:restart`)
69
+
70
+ ```bash
71
+ asgard server help
72
+ asgard server start
73
+ asgard server start 4000 --workers 4 --daemon
74
+ asgard server stop --force
75
+ asgard server status
76
+ asgard server restart 4000
77
+ ```
78
+
79
+ The `ServerCommands` class inherits from `Tasks`, giving it access to `sh`, `depends_on`, and the built-in `--debug`/`--verbose` flags.
80
+
81
+ ---
82
+
83
+ ## `db_subcommands.loki`
84
+
85
+ **Path:** `examples/db_subcommands.loki`
86
+
87
+ Demonstrates subcommands with more complex `depends_on` chaining within the group. Covers:
88
+
89
+ - `DBCommands < Tasks` with migrate, rollback, seed, reset, console, and status commands
90
+ - Multi-step `depends_on` chain: `rollback → migrate → seed → reset`
91
+ - `long_desc` with formatted examples inside a subcommand class
92
+ - `enum` validation on subcommand options
93
+ - Optional positional parameters (`migrate [VERSION]`, `rollback [STEPS]`, `seed [FILE]`)
94
+
95
+ ```bash
96
+ asgard db help
97
+ asgard db migrate
98
+ asgard db migrate 20240101120000 --dry-run
99
+ asgard db rollback
100
+ asgard db rollback 3
101
+ asgard db seed --env staging
102
+ asgard db reset # rollback → migrate → seed → reset
103
+ asgard db console --env staging
104
+ asgard db status
105
+ ```
106
+
107
+ ---
108
+
109
+ ## `concurrent.loki`
110
+
111
+ **Path:** `examples/concurrent.loki`
112
+
113
+ A focused demonstration of true concurrent execution via parallel `depends_on` groups:
114
+
115
+ - Three worker tasks (`worker_a`, `worker_b`, `worker_c`) each print a character repeatedly with random sleep delays
116
+ - All three run in parallel threads when `asgard finish` is invoked
117
+ - The interleaved output proves that real concurrency is occurring (not sequential batching)
118
+ - Uses `$stdout.sync = true` to ensure thread-safe immediate output flushing
119
+
120
+ ```bash
121
+ asgard finish
122
+ # starting demo of concurrent task execution ...
123
+ # ABCBACBACBABCBACBACB (order varies every run)
124
+ # fini - the end of concurrent task demo
125
+ ```
126
+
127
+ Execution order: `start` → `worker_a ∥ worker_b ∥ worker_c` → `finish`
128
+
129
+ This example is also useful as a test harness for verifying that parallel execution is working correctly on a given system.
130
+
131
+ ---
132
+
133
+ ## Summary
134
+
135
+ | File | Primary Focus |
136
+ |---|---|
137
+ | `kitchen_sink.loki` | Comprehensive Thor DSL reference — options, aliases, long_desc, depends_on |
138
+ | `server_subcommands.loki` | Subcommand groups, per-command options, depends_on in subcommands |
139
+ | `db_subcommands.loki` | Multi-step depends_on chains, enum validation, long_desc in subcommands |
140
+ | `concurrent.loki` | Parallel task execution, thread concurrency demonstration |