asgard 0.1.2 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d904144543326c15257e4e61584c46fa9b0ab4f1ba522680daa930950361e6a
4
- data.tar.gz: fea08260a5db1fdde268eaf4ca930b1e52a07a39f12eaf2ce20e31736319cdff
3
+ metadata.gz: 839554cd5b16759477245ef561d2769b6d09ea555aa592c8086c2de6bc54450b
4
+ data.tar.gz: 419b813aaccbd3afbe5bc70be2ada0ba4b99ce3f5fdcc0b5b246b461bc3bd76f
5
5
  SHA512:
6
- metadata.gz: 9f75f50ba53970998d047e21078b120090ba6c232ac698b0ba705b84a3de542bfef313690e11d2e09989101045323c7cd6549bf51826b316ccf1beeb96ada50d
7
- data.tar.gz: 497bee99076f476b8fe680962f59b369898381470b27fd59e0f7e4dc7636e620004660cbcf3ceb10a2678e65ddf78a2a7e24e6571362fc7ad3e7929eeb61a614
6
+ metadata.gz: 8eb7270b4a6668ea0e111f739de651753e927347dd91d5a85275a6b2e014b01ba4c3fc84261174d97f221bf36ac23a4868f3b56ba3df80041a96bf9fa9115397
7
+ data.tar.gz: 5fb697c0ac1dd087b1d8b521680a7a3ec277b633dfee7dab612dfce6da6ce4a7d1377b916049913f082c2f233567ef3a311cfe60b6dc1945c891ac122b1add43
@@ -0,0 +1,52 @@
1
+ name: Deploy Documentation to GitHub Pages
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ - develop
7
+ paths:
8
+ - "docs/**"
9
+ - "mkdocs.yml"
10
+ - ".github/workflows/deploy-github-pages.yml"
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: write
15
+ pages: write
16
+ id-token: write
17
+
18
+ jobs:
19
+ deploy:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Checkout code
23
+ uses: actions/checkout@v4
24
+ with:
25
+ fetch-depth: 0
26
+
27
+ - name: Setup Python
28
+ uses: actions/setup-python@v5
29
+ with:
30
+ python-version: 3.x
31
+
32
+ - name: Install dependencies
33
+ run: |
34
+ pip install mkdocs
35
+ pip install mkdocs-material
36
+ pip install mkdocs-macros-plugin
37
+ pip install mike
38
+
39
+ - name: Configure Git
40
+ run: |
41
+ git config --local user.email "action@github.com"
42
+ git config --local user.name "GitHub Action"
43
+
44
+ - name: Build MkDocs site
45
+ run: mkdocs build
46
+
47
+ - name: Deploy to GitHub Pages
48
+ uses: peaceiris/actions-gh-pages@v4
49
+ with:
50
+ github_token: ${{ secrets.GITHUB_TOKEN }}
51
+ publish_dir: ./site
52
+ keep_files: true
data/CHANGELOG.md CHANGED
@@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Fixed (round 2)
11
+
12
+ - **Subcommand deps not validated at startup** — `run!` only called `Tasks.validate_deps!`, so circular dependencies and undefined dep names in subcommand groups (classes that inherit from `Asgard::Base` or `Tasks`) were silently ignored. `run!` now snapshots `Asgard::Base.subclasses` before loading task files and validates every newly defined subclass alongside `Tasks`.
13
+ - **Parallel dep thread orphaned on exception** — when a parallel dep group contained one fast-failing task and one slow task, `threads.each(&:join)` re-raised the first thread's exception and abandoned the remaining threads. The slow thread continued running unsupervised after the caller saw the exception. The join loop now collects all thread exceptions before re-raising the first, ensuring every thread completes before execution exits the group.
14
+ - **Dep with required arguments gave a cryptic runtime error** — `depends_on :build` where `build(name)` has required parameters caused `Thor::InvocationError` at task invocation time with no indication of where the problem was. `validate_deps!` now checks the arity of every dep task via `instance_method.parameters` and raises `Asgard::Error` at startup with the task name and required argument count.
15
+ - **Orphaned `depends_on` silently discarded** — a `depends_on` declaration at the end of a class body (or `.loki` file) with no following `desc`/`def` left `@_pending_deps` non-empty but was ignored by `validate_deps!` due to an early `return if _deps.empty?` guard. `validate_deps!` now checks `@_pending_deps` before that guard and raises `Asgard::Error` naming the orphaned dependencies.
16
+
17
+ ### Fixed
18
+
19
+ - **Parallel dep race condition** — when two parallel tasks shared a common dependency, the second thread could start before the shared dep finished executing. `_ran_tasks` (a single Set) has been replaced with `_running` / `_done` Sets and a per-task `ConditionVariable`. Threads that arrive at an already-running dep now wait for its completion rather than skipping it; the `ensure` block broadcasts completion whether the task succeeds or raises.
20
+ - **`depends_on` silently dropped before `var` or `no_commands`** — Thor uses an integer counter for `@no_commands` that resets to `0` (truthy in Ruby) after any `no_commands` block. The `method_added` guard now checks `@usage` instead: pending deps are only consumed when a command-defining method is added (one preceded by `desc`), so `var` declarations and `no_commands` helpers placed between `depends_on` and `def` no longer silently steal the dependency.
21
+ - **`shebang` ignored its `silent:` keyword argument** — the parameter was accepted but never referenced; the script body is now echoed to stdout unless `silent: true` is passed, matching the behavior of `sh`.
22
+ - **`var` lambdas re-evaluated on every access** — the accessor method now caches its result in a per-instance variable on first call. Lambdas used for computed values (e.g. `` -> { `git describe --tags`.strip } ``) now run exactly once per instance.
23
+
24
+ ### Changed
25
+
26
+ - **`validate_deps!` detects undefined dependency names** — `depends_on :nonexistent` previously passed validation silently and produced no error at runtime. `validate_deps!` now raises `Asgard::Error` listing every dep name that does not correspond to a defined task. `run!` catches this alongside `CircularDependencyError` and exits with a clean message.
27
+
28
+ ## [0.2.0] - 2026-05-29
29
+
30
+ ### Changed
31
+
32
+ - `*.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.
33
+ - Added `--auto-load` as a built-in CLI flag in `Tasks`, visible in `asgard help`
34
+
35
+ ## [0.1.2] - 2026-05-29
36
+
37
+ ### Added
38
+
39
+ - `--version` built-in CLI flag — prints `Asgard::VERSION` and exits; implemented as a `_`-prefixed method in `Tasks` per the gem-owned naming convention
40
+ - `--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`
41
+ - `debug?` and `verbose?` private predicate helpers on `Tasks` — thin wrappers around `$DEBUG` and `$VERBOSE` for use inside task bodies
42
+ - `_` prefix convention for gem-owned methods in `Tasks` — built-in methods use `_` prefix to distinguish them from user-defined tasks
43
+ - `run!` guards against direct invocation of `_`-prefixed commands with a clean error message and exit 1
44
+ - `examples/` directory with working `.loki` files:
45
+ - `kitchen_sink.loki` — demonstrates the full Thor DSL (all option types, `long_desc`, `class_option`, `default_task`, `map`, `depends_on`, `var`, `no_commands`, `private`)
46
+ - `server_subcommands.loki` — subcommand group for server management
47
+ - `db_subcommands.loki` — subcommand group for database management with `depends_on` chaining
48
+ - README sections: Helper methods, Subcommands, Thor wrapper callout
49
+
50
+ ### Fixed
51
+
52
+ - 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
53
+
54
+ ### Changed
55
+
56
+ - `--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
57
+ - Removed all references to `just` task runner and `recipe` terminology; Asgard uses "task" throughout
58
+ - `depends_on` parameter renamed from `*recipes` to `*tasks` for consistency
59
+
10
60
  ## [0.1.1] - 2026-05-28
11
61
  ### Added
12
62
 
@@ -52,5 +102,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
52
102
  - 100% test coverage enforced via SimpleCov (95% minimum threshold)
53
103
  - Quality task in `.loki` runs flog after tests
54
104
 
55
- [Unreleased]: https://github.com/MadBomber/asgard/compare/v0.1.0...HEAD
105
+ [Unreleased]: https://github.com/MadBomber/asgard/compare/v0.2.0...HEAD
106
+ [0.2.0]: https://github.com/MadBomber/asgard/compare/v0.1.2...v0.2.0
107
+ [0.1.2]: https://github.com/MadBomber/asgard/compare/v0.1.1...v0.1.2
108
+ [0.1.1]: https://github.com/MadBomber/asgard/compare/v0.1.0...v0.1.1
56
109
  [0.1.0]: https://github.com/MadBomber/asgard/releases/tag/v0.1.0
data/CLAUDE.md CHANGED
@@ -29,7 +29,7 @@ Single test: `ruby -Ilib:test test/test_asgard.rb`
29
29
 
30
30
  `bin/asgard` → `Asgard.run!(ARGV)` (`lib/asgard.rb`):
31
31
  1. Walk CWD + ancestors for `.loki` (marker only, not a task file)
32
- 2. Glob + sort `*.loki` files from that dir, `load` each alphabetically
32
+ 2. If `--auto-load` is in argv: glob + sort `*.loki` files and load each alphabetically
33
33
  3. Load `.loki` itself last
34
34
  4. `Tasks.validate_deps!` — build full dep graph, raise `CircularDependencyError` if cyclic
35
35
  5. `Tasks._reset_ran!` — clear execution tracking
@@ -114,4 +114,4 @@ class Tasks
114
114
  end
115
115
  ```
116
116
 
117
- Multiple `*.loki` files in the same directory are all loaded (alphabetically). The bare `.loki` file serves only as the project root marker — its content is loaded last.
117
+ By default, only `.loki` is loaded. Pass `--auto-load` to `asgard` to also load all `*.loki` files in the same directory alphabetically before `.loki` is loaded. The bare `.loki` file serves as the project root marker — its content is always loaded last.
data/README.md CHANGED
@@ -1,10 +1,33 @@
1
1
  # Asgard
2
2
 
3
- A Ruby task runner built on [Thor](https://github.com/rails/thor) for argument handling and [Dagwood](https://github.com/rewindio/dagwood) for dependency ordering.
4
-
5
- The name comes from Norse mythology: **Thor** is the CLI framework, **Asgard** is the realm where tasks live, and the task file is named **loki** — because Loki holds all the tricks.
6
-
7
- > **Asgard is a wrapper around [Thor](https://github.com/rails/thor).** Anything Thor can do — subcommands, typed options, argument validation, shell completion — is available inside a `.loki` file. Familiarity with Thor's DSL will make you immediately productive with Asgard.
3
+ > [!INFO]
4
+ > See the [CHANGELOG](CHANGELOG.md) for the latest changes. The [examples directory](examples/) contains working `.loki` files demonstrating the full feature set.
5
+
6
+ <br>
7
+ <table>
8
+ <tr>
9
+ <td width="40%" align="center" valign="top">
10
+ <img src="docs/assets/images/asgard.jpg" alt="Asgard"><br>
11
+ <em>"Loki writes the tricks. Asgard runs them."</em>
12
+ </td>
13
+ <td width="60%" valign="top">
14
+ <strong>Key Features</strong><br>
15
+
16
+ - <strong>Thor-Powered CLI</strong> — every Thor DSL feature available inside <code>.loki</code> task files<br>
17
+ - <strong>Task Dependencies</strong> — sequential, parallel, and mixed dependency graphs via <code>depends_on</code><br>
18
+ - <strong>Concurrent Execution</strong> — parallel task groups run in native Ruby threads<br>
19
+ - <strong>Subcommands</strong> — group related tasks under a named namespace<br>
20
+ - <strong>Variables</strong> — static values and lazy-evaluated lambdas via <code>var</code><br>
21
+ - <strong>Shell Helpers</strong> — <code>sh</code> for any shell command or heredoc; <code>shebang</code> for polyglot scripts<br>
22
+ - <strong>Dotenv Support</strong> — load <code>.env</code> files into the environment with <code>dotenv</code><br>
23
+ - <strong>Auto-Discovery</strong> — <code>.loki</code> root marker searched from CWD upward through parent directories<br>
24
+ - <strong>Multi-File Tasks</strong> — split tasks across <code>*.loki</code> files, loaded on demand with <code>--auto-load</code><br>
25
+ - <strong>Built-in Flags</strong> — <code>--version</code>, <code>--debug</code>, and <code>--verbose</code> available on every task<br>
26
+ </td>
27
+ </tr>
28
+ </table>
29
+
30
+ <p>Asgard is a <a href="https://github.com/rails/thor">Thor</a>-based task runner for Ruby projects. Define tasks in <code>.loki</code> files, declare dependencies between them, and let Asgard handle ordering and concurrent execution. Anything Thor can do — subcommands, typed options, argument validation — is available inside a <code>.loki</code> file.</p>
8
31
 
9
32
  ## Installation
10
33
 
@@ -55,7 +78,7 @@ asgard hello Alice
55
78
 
56
79
  ### A task with a formal argument declaration
57
80
 
58
- Use `argument` for richer metadata — type checking, enums, and help text:
81
+ Use `argument` for richer metadata — type checking, enums, and help text. **Warning: `argument` is a class-level declaration that applies to every task in the class**, not just the one below it. It is best suited for single-command CLIs or when every task genuinely shares the same positional input. In multi-task files, prefer method signature parameters instead.
59
82
 
60
83
  ```ruby
61
84
  class Tasks
@@ -116,7 +139,7 @@ end
116
139
 
117
140
  `depends_on` declares what must run before a task. Each dependency runs at most once per `asgard` invocation regardless of how many tasks declare it. Circular dependencies are caught at startup.
118
141
 
119
- `desc` and `depends_on` are independent — either can come first, both must appear before `def`.
142
+ `desc` and `depends_on` are independent — either can come first, both must appear before `def`. `var` declarations between `depends_on` and `def` are safe and do not consume the pending dependency.
120
143
 
121
144
  ### Sequential dependencies
122
145
 
@@ -328,10 +351,14 @@ end
328
351
 
329
352
  Supported interpreters: `:python3`, `:python`, `:node`, `:ruby`, `:perl`, `:bash`, `:sh`. Any other symbol is passed directly to `system` with a `.tmp` extension.
330
353
 
331
- Pass `silent: true` to suppress the command echo:
354
+ Pass `silent: true` to both `sh` and `shebang` to suppress the script echo:
332
355
 
333
356
  ```ruby
334
- def build = sh "rake build", silent: true
357
+ def build = sh "rake build", silent: true
358
+ def analyze = shebang :python3, <<~PY, silent: true
359
+ import json
360
+ print(json.load(open("data.json")))
361
+ PY
335
362
  ```
336
363
 
337
364
  ---
@@ -443,7 +470,7 @@ Common `method_option` keys: `aliases`, `type`, `default`, `required`, `desc`, `
443
470
 
444
471
  ## Task files
445
472
 
446
- Asgard searches the current directory and its ancestors for a `.loki` file. That file marks the project root. All `*.loki` files in the same directory are auto-loaded alphabetically before `.loki` is loaded.
473
+ Asgard searches the current directory and its ancestors for a `.loki` file. That file marks the project root. `*.loki` files in the same directory are loaded only when `asgard` is invoked with `--auto-load`.
447
474
 
448
475
  ### Single file
449
476
 
@@ -514,9 +541,9 @@ end
514
541
  |---|---|
515
542
  | `Asgard.run!(argv)` | Entry point — finds `.loki`, loads task files, starts CLI |
516
543
  | `Asgard.find_task_file` | Returns path to `.loki` searching from CWD upward, or nil |
517
- | `Asgard.load_loki(dir)` | Loads all `*.loki` files in dir alphabetically |
544
+ | `Asgard.load_loki(dir)` | Loads all `*.loki` files in dir alphabetically — called by `run!` only when `--auto-load` is passed |
518
545
 
519
- `run!` handles its own errors — a missing `.loki` or a circular dependency both produce a clean one-line message and exit 1.
546
+ `run!` handles its own errors — a missing `.loki`, a circular dependency, or a `depends_on` that names a task that doesn't exist all produce a clean one-line message and exit 1.
520
547
 
521
548
  ---
522
549
 
data/docs/api.md ADDED
@@ -0,0 +1,131 @@
1
+ # API Reference
2
+
3
+ This page documents the public Ruby API for the Asgard gem. Most users interact with Asgard through the CLI and the task DSL — this page is primarily useful when integrating Asgard into tooling or extending it programmatically.
4
+
5
+ ---
6
+
7
+ ## `Asgard` Module Methods
8
+
9
+ These class methods are defined on the `Asgard` module itself.
10
+
11
+ | Method | Signature | Description |
12
+ |---|---|---|
13
+ | `run!` | `Asgard.run!(argv)` | Main entry point. Finds `.loki`, loads all task files, validates the dependency graph, and dispatches via Thor. Handles its own errors: missing `.loki` and circular dependencies both produce a clean one-line message and `exit 1`. |
14
+ | `find_task_file` | `Asgard.find_task_file → String, nil` | Searches `Dir.pwd` and each ancestor directory for a `.loki` file. Returns the absolute path string of the first match, or `nil` if none is found. |
15
+ | `load_loki` | `Asgard.load_loki(dir)` | Loads all `*.loki` files in `dir` alphabetically, excluding `.loki` itself. Called by `run!` only when `--auto-load` is present in `argv`. |
16
+
17
+ ### `run!` Details
18
+
19
+ ```ruby
20
+ Asgard.run!(ARGV)
21
+ ```
22
+
23
+ `run!` guards against direct invocation of `_`-prefixed commands before any files are loaded:
24
+
25
+ ```ruby
26
+ abort "asgard: unknown command '#{argv.first}'" if argv.first&.start_with?("_")
27
+ ```
28
+
29
+ After loading task files, it calls `Tasks.validate_deps!` (circular dependency check) and `Tasks._reset_ran!` (clears per-invocation deduplication state) before starting Thor.
30
+
31
+ ---
32
+
33
+ ## `Asgard::Base` DSL Class Methods
34
+
35
+ `Asgard::Base` is a `Thor` subclass that provides the task DSL. It is the superclass of `Tasks`. All DSL methods are class methods (called in the class body).
36
+
37
+ | Method | Signature | Description |
38
+ |---|---|---|
39
+ | `depends_on` | `depends_on(*tasks)` | Declare prerequisites for the next `def`. Bare symbols run sequentially; arrays within the splat run as a parallel group. |
40
+ | `var` | `var(name, value = nil, &block)` | Declare a named variable. If `value` responds to `call` (lambda/proc) or a block is given, the value is computed lazily on first access. Accessible in task bodies as a method. |
41
+ | `import` | `import(mod)` | Include a module into the current class (thin alias for `include`). |
42
+ | `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. |
43
+ | `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. |
44
+ | `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. |
45
+ | `validate_deps!` | `Tasks.validate_deps!` | Build and topologically sort the full dependency graph using Dagwood. Raises `Asgard::CircularDependencyError` on cycles. Called by `run!` at startup. |
46
+ | `_reset_ran!` | `Tasks._reset_ran!` | Clear the per-invocation task deduplication set. Called by `run!` before dispatching. Thread-safe via Mutex. |
47
+
48
+ ### `depends_on` Argument Shapes
49
+
50
+ ```ruby
51
+ depends_on :build # single sequential dep
52
+ depends_on :clean, :build # two sequential deps
53
+ depends_on [:lint, :typecheck] # lint and typecheck run in parallel
54
+ depends_on :setup, [:lint, :build], :test # setup, then lint+build concurrently, then test
55
+ ```
56
+
57
+ ---
58
+
59
+ ## `Tasks` Built-ins
60
+
61
+ `Tasks` is pre-defined by the gem as `class Tasks < Asgard::Base`. It adds the following:
62
+
63
+ | Item | Type | Description |
64
+ |---|---|---|
65
+ | `class_option :debug` | class option | `--debug` flag. Sets `$DEBUG = true` before any task runs. Boolean, default `false`. |
66
+ | `class_option :verbose` | class option | `--verbose` flag. Sets `$VERBOSE = true` before any task runs. Boolean, default `false`. |
67
+ | `_version` | private task method | Implements `--version`. Prints `Asgard::VERSION` and exits. Registered via `map "--version" => :_version`. Uses `_` prefix convention. |
68
+ | `debug?` | private instance method | Returns `$DEBUG`. Available in all task bodies and subcommand classes that inherit from `Tasks`. |
69
+ | `verbose?` | private instance method | Returns `$VERBOSE`. Available in all task bodies and subcommand classes that inherit from `Tasks`. |
70
+ | `--auto-load` | CLI flag (consumed by `run!`) | Triggers loading of all `*.loki` files before the main `.loki` and the requested task. Consumed by `run!` before Thor dispatch. |
71
+
72
+ ---
73
+
74
+ ## `Asgard::Base` Internal Class Methods
75
+
76
+ These are implementation details exposed for extensibility. Prefer the DSL methods above in normal use.
77
+
78
+ | Method | Description |
79
+ |---|---|
80
+ | `_deps` | Hash mapping task name symbols to their stage arrays. Set by `depends_on` + `method_added`. |
81
+ | `_vars` | Hash mapping var name symbols to their static values or callables. |
82
+ | `_ran_tasks` | `Set` of task name symbols that have already run in the current invocation. |
83
+ | `_ran_mutex` | `Mutex` protecting `_ran_tasks` for thread-safe deduplication. |
84
+ | `_build_dep_graph(stages)` | Translates the stage array (from `_deps`) into a Dagwood-compatible hash. |
85
+
86
+ ---
87
+
88
+ ## `invoke_command` Hook
89
+
90
+ `Asgard::Base` overrides Thor's `invoke_command` to implement dependency resolution and deduplication:
91
+
92
+ 1. Sets `$DEBUG` / `$VERBOSE` from `options` if the corresponding flags are present.
93
+ 2. Checks `_ran_tasks` — skips if this task has already run.
94
+ 3. Marks the task as ran.
95
+ 4. Resolves dependency stages from `_deps`, builds the Dagwood graph, and executes groups (parallel groups in threads, sequential groups one at a time).
96
+ 5. Calls `command.run(self, *args)` to execute the task itself.
97
+
98
+ ---
99
+
100
+ ## Error Classes
101
+
102
+ | Class | Superclass | Description |
103
+ |---|---|---|
104
+ | `Asgard::Error` | `StandardError` | Base error class for all Asgard errors. |
105
+ | `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. |
106
+
107
+ ```ruby
108
+ begin
109
+ Asgard.run!(ARGV)
110
+ rescue Asgard::CircularDependencyError => e
111
+ # This is already handled inside run! — you only need this
112
+ # if you call validate_deps! directly in your own tooling.
113
+ abort "circular dependency: #{e.message}"
114
+ end
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Dependencies
120
+
121
+ | Gem | Version | Purpose |
122
+ |---|---|---|
123
+ | [thor](https://github.com/rails/thor) | `~> 1.0` | CLI framework; provides the full task DSL |
124
+ | [dagwood](https://rubygems.org/gems/dagwood) | `~> 1.0` | DAG library for dependency graph resolution and topological sort |
125
+ | [dotenv](https://github.com/bkeepers/dotenv) | `~> 3.0` | `.env` file loading |
126
+
127
+ ---
128
+
129
+ ## Ruby Version Requirement
130
+
131
+ Asgard requires **Ruby >= 3.2.0**.
@@ -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