asgard 0.1.0 → 0.1.2
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 +4 -2
- data/CHANGELOG.md +29 -2
- data/CLAUDE.md +117 -0
- data/README.md +415 -136
- data/bin/asgard +1 -20
- data/examples/.loki +2 -0
- data/examples/db_subcommands.loki +74 -0
- data/examples/kitchen_sink.loki +164 -0
- data/examples/server_subcommands.loki +56 -0
- data/lib/asgard/base.rb +10 -8
- data/lib/asgard/tasks.rb +28 -0
- data/lib/asgard/version.rb +1 -1
- data/lib/asgard.rb +26 -13
- metadata +14 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2d904144543326c15257e4e61584c46fa9b0ab4f1ba522680daa930950361e6a
|
|
4
|
+
data.tar.gz: fea08260a5db1fdde268eaf4ca930b1e52a07a39f12eaf2ce20e31736319cdff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9f75f50ba53970998d047e21078b120090ba6c232ac698b0ba705b84a3de542bfef313690e11d2e09989101045323c7cd6549bf51826b316ccf1beeb96ada50d
|
|
7
|
+
data.tar.gz: 497bee99076f476b8fe680962f59b369898381470b27fd59e0f7e4dc7636e620004660cbcf3ceb10a2678e65ddf78a2a7e24e6571362fc7ad3e7929eeb61a614
|
data/.loki
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Asgard gem's own task file.
|
|
3
|
+
# Task is pre-defined by the gem — just reopen it to add tasks.
|
|
2
4
|
|
|
3
|
-
class Tasks
|
|
5
|
+
class Tasks
|
|
4
6
|
var :gem_name, "asgard"
|
|
5
7
|
var :version, -> { Asgard::VERSION }
|
|
6
8
|
|
data/CHANGELOG.md
CHANGED
|
@@ -7,13 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.1] - 2026-05-28
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Parallel dependency execution — wrap deps in an array to run them concurrently:
|
|
14
|
+
`depends_on [:build, :lint]` or `depends_on :setup, [:build, :lint], :deploy`
|
|
15
|
+
- `Asgard.run!(argv)` — single entry point encapsulating find, load, validate, and start
|
|
16
|
+
- `Asgard.load_loki(dir)` — auto-loads all `*.loki` files in a directory alphabetically
|
|
17
|
+
- `Tasks` class pre-defined by the gem (`class Tasks < Asgard::Base`) — task files reopen it without restating the superclass
|
|
18
|
+
- `lib/asgard/tasks.rb` — ships the pre-defined `Tasks` class
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Replaced `SimpleFlow` dependency with `Dagwood` — purpose-built DAG library with no extra dependencies and no Ruby 4 compatibility issues
|
|
23
|
+
- `bin/asgard` simplified to two lines: `require "asgard"` + `Asgard.run!(ARGV)`
|
|
24
|
+
- Task file convention: `.loki` is the project root marker and entry point; `*.loki` files each reopen `class Tasks` and are auto-loaded before `.loki`
|
|
25
|
+
- `Asgard.find_task_files` renamed to `Asgard.find_task_file` (singular — only `.loki` is the entry point)
|
|
26
|
+
- `depends_on` now accepts mixed sequential/parallel stages; bare symbols run sequentially, arrays within the splat run in parallel
|
|
27
|
+
- `run!` handles its own errors — missing `.loki` and circular dependencies produce a clean one-line message and exit 1 rather than a backtrace
|
|
28
|
+
- Thread-safe dep deduplication via class-level `_ran_tasks` Set + Mutex replaces Thor's `@_invocations`
|
|
29
|
+
- Removed `import` macro — task files use Ruby class reopening instead of modules
|
|
30
|
+
|
|
31
|
+
### Removed
|
|
32
|
+
|
|
33
|
+
- `SimpleFlow` dependency (replaced by `Dagwood`)
|
|
34
|
+
- `logger` gem workaround (was only needed for SimpleFlow on Ruby 4)
|
|
35
|
+
- `*.loki` glob fallback in `find_task_file` — only `.loki` is the auto-discovered entry point
|
|
36
|
+
|
|
10
37
|
## [0.1.0] - 2026-05-28
|
|
11
38
|
|
|
12
39
|
### Added
|
|
13
40
|
|
|
14
41
|
- `Asgard::Base` — Thor subclass providing the task DSL
|
|
15
|
-
- `depends_on` — declare
|
|
16
|
-
- `var` — declare static or lazy-evaluated variables available to all
|
|
42
|
+
- `depends_on` — declare task dependencies; dependencies run at most once per invocation
|
|
43
|
+
- `var` — declare static or lazy-evaluated variables available to all tasks
|
|
17
44
|
- `import` — flat-merge a task module into the current class
|
|
18
45
|
- `dotenv` — load a `.env` file into the environment
|
|
19
46
|
- `sh` — run a shell command or multiline heredoc script; exits with the command's status on failure
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What Asgard Is
|
|
6
|
+
|
|
7
|
+
Asgard is a Ruby task runner. Users define tasks in `.loki` files by reopening the pre-defined `Tasks` class. The name is intentional: Thor handles the CLI, Asgard is where tasks live, and Loki (the `.loki` file) holds all the tricks.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bundle install
|
|
13
|
+
bundle exec rake test # run tests (enforces 95% SimpleCov coverage)
|
|
14
|
+
bundle exec rake quality # test + flog complexity check
|
|
15
|
+
bundle exec rake build # build .gem into pkg/
|
|
16
|
+
bundle exec rake install # install locally
|
|
17
|
+
|
|
18
|
+
# or use the gem's own .loki file:
|
|
19
|
+
asgard test
|
|
20
|
+
asgard quality
|
|
21
|
+
asgard release
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Single test: `ruby -Ilib:test test/test_asgard.rb`
|
|
25
|
+
|
|
26
|
+
## Architecture
|
|
27
|
+
|
|
28
|
+
### Entry Point Flow
|
|
29
|
+
|
|
30
|
+
`bin/asgard` → `Asgard.run!(ARGV)` (`lib/asgard.rb`):
|
|
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
|
|
33
|
+
3. Load `.loki` itself last
|
|
34
|
+
4. `Tasks.validate_deps!` — build full dep graph, raise `CircularDependencyError` if cyclic
|
|
35
|
+
5. `Tasks._reset_ran!` — clear execution tracking
|
|
36
|
+
6. `Tasks.start(argv)` — Thor dispatches the command
|
|
37
|
+
|
|
38
|
+
### Core Classes
|
|
39
|
+
|
|
40
|
+
| File | Role |
|
|
41
|
+
|------|------|
|
|
42
|
+
| `lib/asgard/base.rb` | DSL engine; inherits Thor, includes Shell |
|
|
43
|
+
| `lib/asgard/shell.rb` | `sh` / `shebang` helpers |
|
|
44
|
+
| `lib/asgard/tasks.rb` | `class Tasks < Asgard::Base` — the convention class users reopen; also holds gem-owned built-in tasks |
|
|
45
|
+
|
|
46
|
+
### Naming Convention for Gem-Owned Methods
|
|
47
|
+
|
|
48
|
+
Any task or method defined by Asgard itself inside `Tasks` (i.e. not by the user's `.loki` files) must be prefixed with `_`. This distinguishes built-in gem behavior from user-defined tasks and prevents naming collisions.
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
# lib/asgard/tasks.rb — gem-owned built-ins use _ prefix
|
|
52
|
+
class Tasks < Asgard::Base
|
|
53
|
+
desc "--version", "Show version"
|
|
54
|
+
map "--version" => :_version
|
|
55
|
+
def _version
|
|
56
|
+
puts Asgard::VERSION
|
|
57
|
+
exit
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`method_added` in `Base` already skips `_`-prefixed methods when attaching dependency metadata, so built-ins are naturally excluded from the dependency graph.
|
|
63
|
+
|
|
64
|
+
Do not define `_`-prefixed methods in user `.loki` files — that namespace is reserved for the gem.
|
|
65
|
+
|
|
66
|
+
### DSL Mechanics (`lib/asgard/base.rb`)
|
|
67
|
+
|
|
68
|
+
**`depends_on`** stores stages in `@_pending_deps`. On `method_added`, those stages are popped and stored in `@_deps[method_name]`. Bare symbols are sequential stages; arrays within a `depends_on` call are parallel stages:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
depends_on :a, [:b, :c], :d # stages: [[:a], [:b, :c], [:d]]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**`var`** stores values or lambdas in `@_vars` and creates an instance method that evaluates the lambda once on first access.
|
|
75
|
+
|
|
76
|
+
**`invoke_command`** (Thor dispatch hook):
|
|
77
|
+
1. Atomically check `@_ran_tasks` Set (with `@_ran_mutex`); return early if already run
|
|
78
|
+
2. Resolve `@_deps` stages → `_build_dep_graph` → `Dagwood::DependencyGraph#parallel_order`
|
|
79
|
+
3. For each parallel group: spawn one thread per task, join; single-task groups run inline
|
|
80
|
+
4. Execute the target task
|
|
81
|
+
|
|
82
|
+
**`_build_dep_graph(stages)`** converts stages to a DAG hash:
|
|
83
|
+
- `[[:a], [:b, :c], [:d]]` → `{ a: [], b: [:a], c: [:a], d: [:b, :c] }`
|
|
84
|
+
|
|
85
|
+
### Dependency Resolution
|
|
86
|
+
|
|
87
|
+
Dagwood topologically sorts the DAG and returns parallel groups. The thread-safe deduplication (`_ran_tasks` Set + Mutex) ensures each task runs exactly once even when multiple tasks share a common dependency.
|
|
88
|
+
|
|
89
|
+
### Shell Helpers
|
|
90
|
+
|
|
91
|
+
- `sh(script, silent: false)` — single-line strings use `system(script)`; multi-line strings pipe through `bash -c`; exits with the command's status on failure
|
|
92
|
+
- `shebang(interpreter, script)` — writes script to a tempfile and executes with the named interpreter (`:python3`, `:node`, `:ruby`, `:perl`, `:bash`, etc.)
|
|
93
|
+
|
|
94
|
+
## Testing
|
|
95
|
+
|
|
96
|
+
All tests are in `test/test_asgard.rb` (one file, ~11 named classes). SimpleCov minimum is 95%; the Rakefile configures this with a prelude that loads coverage before the library.
|
|
97
|
+
|
|
98
|
+
Key test patterns: tests frequently subclass `Asgard::Base` directly (not `Tasks`) to test the engine in isolation, and use `capture_io` for output assertions.
|
|
99
|
+
|
|
100
|
+
## The `.loki` Format
|
|
101
|
+
|
|
102
|
+
A `.loki` file is plain Ruby that reopens `Tasks`:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
class Tasks
|
|
106
|
+
var :gem_name, "asgard"
|
|
107
|
+
|
|
108
|
+
desc "test", "Run tests"
|
|
109
|
+
def test = sh "bundle exec rake test"
|
|
110
|
+
|
|
111
|
+
depends_on :test
|
|
112
|
+
desc "release", "Build and release"
|
|
113
|
+
def release = sh "bundle exec rake release"
|
|
114
|
+
end
|
|
115
|
+
```
|
|
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.
|