asgard 0.2.0 → 0.3.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 -9
- data/.rubocop.yml +157 -0
- data/CHANGELOG.md +49 -11
- data/CLAUDE.md +19 -9
- data/README.md +78 -53
- data/Rakefile +83 -4
- data/docs/api.md +86 -13
- data/docs/changelog.md +4 -0
- data/docs/dependencies.md +25 -25
- data/docs/environment.md +30 -14
- data/docs/examples.md +3 -3
- data/docs/getting-started.md +5 -6
- data/docs/helpers.md +34 -10
- data/docs/index.md +6 -6
- data/docs/options.md +2 -2
- data/docs/shell.md +11 -11
- data/docs/subcommands.md +9 -9
- data/docs/task-files.md +266 -113
- data/docs/tasks.md +17 -15
- data/docs/variables.md +267 -51
- data/examples/.env +4 -0
- data/examples/.loki +24 -2
- data/examples/concurrent.loki +5 -5
- data/examples/db_subcommands.loki +3 -3
- data/examples/env_usage.loki +27 -0
- data/examples/kitchen_sink.loki +48 -15
- data/examples/server_subcommands.loki +3 -3
- data/examples/subdir/.loki +12 -0
- data/examples/subdir/import_demo.loki +14 -0
- data/examples/subdir/import_up_demo.loki +18 -0
- data/lib/asgard/base.rb +125 -83
- data/lib/asgard/kernel_methods.rb +77 -0
- data/lib/asgard/shell.rb +8 -7
- data/lib/asgard/tasks.rb +0 -11
- data/lib/asgard/version.rb +1 -1
- data/lib/asgard.rb +2 -18
- metadata +13 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b6045b69a572bacdc07c1ae149f2978000fb690c180cf536512caf4714345317
|
|
4
|
+
data.tar.gz: defa5899f4e66143fe3118d835c60f128534075d2ce4a41652a0bbf54f61e790
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8d27eb2c214be342a64561c8f2287cd05aa48a30e411676dbd69b20f36ec9b706456d7c805c3bfa3a4b64897b8f79831a0a55b96c38b77e3981de551c8737750
|
|
7
|
+
data.tar.gz: 6bbd01f8c90195fc9b7428dbe7b56721a70d9ffe4b881853497df84c68db7d96400cb279ab9006ff3b6841ab85bd5f54d6838f3d887469331dbe04873bd5dca5
|
data/.loki
CHANGED
|
@@ -3,33 +3,31 @@
|
|
|
3
3
|
# Task is pre-defined by the gem — just reopen it to add tasks.
|
|
4
4
|
|
|
5
5
|
class Tasks
|
|
6
|
-
|
|
7
|
-
var :version, -> { Asgard::VERSION }
|
|
6
|
+
@@gem_name ||= "asgard".freeze
|
|
8
7
|
|
|
9
|
-
desc "
|
|
8
|
+
desc "Run the test suite"
|
|
10
9
|
def test
|
|
11
10
|
sh "bundle exec rake test"
|
|
12
11
|
end
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
desc "quality", "Run tests then flog"
|
|
13
|
+
desc "Run all quality gates (tests, RuboCop, Flog)"
|
|
16
14
|
def quality
|
|
17
|
-
sh "
|
|
15
|
+
sh "bundle exec rake quality"
|
|
18
16
|
end
|
|
19
17
|
|
|
20
|
-
desc "
|
|
18
|
+
desc "Build the gem package"
|
|
21
19
|
def build
|
|
22
20
|
sh "bundle exec rake build"
|
|
23
21
|
end
|
|
24
22
|
|
|
25
23
|
depends_on :test
|
|
26
|
-
desc "
|
|
24
|
+
desc "Build and install gem locally"
|
|
27
25
|
def install
|
|
28
26
|
sh "bundle exec rake install"
|
|
29
27
|
end
|
|
30
28
|
|
|
31
29
|
depends_on :quality
|
|
32
|
-
desc "
|
|
30
|
+
desc "Release to RubyGems"
|
|
33
31
|
def release
|
|
34
32
|
sh "bundle exec rake release"
|
|
35
33
|
end
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
SuggestExtensions: false
|
|
4
|
+
TargetRubyVersion: 3.2
|
|
5
|
+
Exclude:
|
|
6
|
+
- 'examples/**/*'
|
|
7
|
+
- 'vendor/**/*'
|
|
8
|
+
|
|
9
|
+
# ── Style: disabled cops ───────────────────────────────────────────────────
|
|
10
|
+
Style/StringLiterals:
|
|
11
|
+
Enabled: false
|
|
12
|
+
|
|
13
|
+
Style/StringLiteralsInInterpolation:
|
|
14
|
+
Enabled: false
|
|
15
|
+
|
|
16
|
+
Style/Documentation:
|
|
17
|
+
Enabled: false
|
|
18
|
+
|
|
19
|
+
Style/IfUnlessModifier:
|
|
20
|
+
Enabled: false
|
|
21
|
+
|
|
22
|
+
Style/RescueModifier:
|
|
23
|
+
Enabled: false
|
|
24
|
+
|
|
25
|
+
Style/TrivialAccessors:
|
|
26
|
+
Enabled: false
|
|
27
|
+
|
|
28
|
+
Style/MultilineTernaryOperator:
|
|
29
|
+
Enabled: false
|
|
30
|
+
|
|
31
|
+
Style/SafeNavigation:
|
|
32
|
+
Enabled: false
|
|
33
|
+
|
|
34
|
+
Style/ClassAndModuleChildren:
|
|
35
|
+
Enabled: false
|
|
36
|
+
|
|
37
|
+
Style/RescueStandardError:
|
|
38
|
+
Enabled: false
|
|
39
|
+
|
|
40
|
+
# Both % and format/sprintf are acceptable
|
|
41
|
+
Style/FormatString:
|
|
42
|
+
Enabled: false
|
|
43
|
+
|
|
44
|
+
# String concatenation and interpolation are both acceptable
|
|
45
|
+
Style/StringConcatenation:
|
|
46
|
+
Enabled: false
|
|
47
|
+
|
|
48
|
+
# ── Layout ─────────────────────────────────────────────────────────────────
|
|
49
|
+
Layout/LineLength:
|
|
50
|
+
Max: 140
|
|
51
|
+
|
|
52
|
+
Layout/ExtraSpacing:
|
|
53
|
+
Enabled: false
|
|
54
|
+
|
|
55
|
+
Layout/HashAlignment:
|
|
56
|
+
Enabled: false
|
|
57
|
+
|
|
58
|
+
Layout/FirstHashElementIndentation:
|
|
59
|
+
Enabled: false
|
|
60
|
+
|
|
61
|
+
Layout/EmptyLineAfterGuardClause:
|
|
62
|
+
Enabled: false
|
|
63
|
+
|
|
64
|
+
# ── Naming ─────────────────────────────────────────────────────────────────
|
|
65
|
+
# Single-char params (e, t) are acceptable throughout
|
|
66
|
+
Naming/MethodParameterName:
|
|
67
|
+
Enabled: false
|
|
68
|
+
|
|
69
|
+
Naming/VariableNumber:
|
|
70
|
+
Exclude:
|
|
71
|
+
- 'test/**/*'
|
|
72
|
+
|
|
73
|
+
Naming/RescuedExceptionsVariableName:
|
|
74
|
+
Enabled: false
|
|
75
|
+
|
|
76
|
+
Naming/AccessorMethodName:
|
|
77
|
+
Enabled: false
|
|
78
|
+
|
|
79
|
+
Naming/PredicatePrefix:
|
|
80
|
+
Enabled: false
|
|
81
|
+
|
|
82
|
+
Naming/PredicateMethod:
|
|
83
|
+
AllowedMethods:
|
|
84
|
+
- import
|
|
85
|
+
Exclude:
|
|
86
|
+
- 'test/**/*'
|
|
87
|
+
|
|
88
|
+
# ── Lint ───────────────────────────────────────────────────────────────────
|
|
89
|
+
Lint/EmptyBlock:
|
|
90
|
+
Exclude:
|
|
91
|
+
- 'test/**/*'
|
|
92
|
+
|
|
93
|
+
Lint/UnusedMethodArgument:
|
|
94
|
+
Enabled: false
|
|
95
|
+
|
|
96
|
+
Lint/ConstantDefinitionInBlock:
|
|
97
|
+
Exclude:
|
|
98
|
+
- 'Rakefile'
|
|
99
|
+
- 'test/**/*'
|
|
100
|
+
|
|
101
|
+
# ── Gemspec ────────────────────────────────────────────────────────────────
|
|
102
|
+
Gemspec/DevelopmentDependencies:
|
|
103
|
+
EnforcedStyle: Gemfile
|
|
104
|
+
|
|
105
|
+
Gemspec/RequiredRubyVersion:
|
|
106
|
+
Enabled: false
|
|
107
|
+
|
|
108
|
+
Gemspec/OrderedDependencies:
|
|
109
|
+
Enabled: false
|
|
110
|
+
|
|
111
|
+
# ── Metrics ────────────────────────────────────────────────────────────────
|
|
112
|
+
# Flog is the primary complexity gate — these thresholds catch only
|
|
113
|
+
# egregious outliers without false-positiving every dispatch method.
|
|
114
|
+
|
|
115
|
+
Metrics/MethodLength:
|
|
116
|
+
Max: 35
|
|
117
|
+
CountAsOne:
|
|
118
|
+
- heredoc
|
|
119
|
+
- array
|
|
120
|
+
- hash
|
|
121
|
+
Exclude:
|
|
122
|
+
- 'test/**/*'
|
|
123
|
+
|
|
124
|
+
Metrics/AbcSize:
|
|
125
|
+
Max: 40
|
|
126
|
+
Exclude:
|
|
127
|
+
- 'test/**/*'
|
|
128
|
+
|
|
129
|
+
Metrics/ClassLength:
|
|
130
|
+
Max: 300
|
|
131
|
+
Exclude:
|
|
132
|
+
- 'test/**/*'
|
|
133
|
+
|
|
134
|
+
Metrics/ModuleLength:
|
|
135
|
+
Max: 200
|
|
136
|
+
Exclude:
|
|
137
|
+
- 'test/**/*'
|
|
138
|
+
|
|
139
|
+
Metrics/CyclomaticComplexity:
|
|
140
|
+
Max: 20
|
|
141
|
+
Exclude:
|
|
142
|
+
- 'test/**/*'
|
|
143
|
+
|
|
144
|
+
Metrics/PerceivedComplexity:
|
|
145
|
+
Max: 20
|
|
146
|
+
Exclude:
|
|
147
|
+
- 'test/**/*'
|
|
148
|
+
|
|
149
|
+
Metrics/ParameterLists:
|
|
150
|
+
Enabled: false
|
|
151
|
+
|
|
152
|
+
Metrics/BlockLength:
|
|
153
|
+
Max: 60
|
|
154
|
+
Exclude:
|
|
155
|
+
- 'Rakefile'
|
|
156
|
+
- '*.gemspec'
|
|
157
|
+
- 'test/**/*'
|
data/CHANGELOG.md
CHANGED
|
@@ -5,25 +5,63 @@ 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
|
-
## [Unreleased
|
|
8
|
+
## [0.3.0] - Unreleased
|
|
9
9
|
|
|
10
|
-
###
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`loki_up(name = ".loki")` Kernel method** — searches `Dir.pwd` and each ancestor directory for a file with the given name; returns the absolute path of the first match or `nil`. Available everywhere in Ruby (task bodies, `.loki` files, top-level code) as a `module_function` on `Kernel`.
|
|
13
|
+
- **`import(path)` Kernel method** — loads a `.loki` file (or a glob of `.loki` files) with `require`-like idempotency via `$LOADED_FEATURES`. Accepts a `String` or `Pathname`. Relative paths are resolved relative to the caller's file (like `require_relative`). Glob patterns (`*.loki`, `**/*.loki`) expand via `Dir.glob` and load all matches. Returns `true` if any file was newly loaded, `false` if all were already loaded or no glob matches were found. Raises `ArgumentError` if the path does not end with `.loki`.
|
|
14
|
+
- **`import_up(name = ".loki")` Kernel method** — combines `loki_up` and `import`. For exact names, finds the first ancestor directory containing that file and loads it. For glob names, finds the first ancestor directory containing any matching files and loads them all — stopping at that level rather than aggregating across multiple ancestors. Returns `false` if nothing is found.
|
|
15
|
+
- **`debug?` and `verbose?` Kernel module functions** — thin wrappers around `$DEBUG` and `$VERBOSE`, available everywhere in Ruby as `module_function` on `Kernel`. Set via `--debug` / `--verbose` CLI flags or directly via `$DEBUG` / `$VERBOSE`.
|
|
16
|
+
- **`env(name, default = nil)` Kernel method** — fetches a system environment variable by symbol or string name, upcasing the key automatically. `env(:port, "3000")` returns `"3000"` when `PORT` is unset; `env(:api_key)` raises `KeyError` when `API_KEY` is missing and no default is provided. Accepts both `env(:port)` and `env("PORT")` forms. Cleaner than `ENV['PORT']` in task bodies.
|
|
17
|
+
- **Verbose/debug feedback for `import` and `import_up`** — when `verbose?` is true, each file loaded is printed to stderr. When `debug?` is true, already-loaded files are also reported (with an "already loaded" suffix), and `import_up` reports when a file is not found.
|
|
18
|
+
- **RuboCop lint gate** — RuboCop is now a first-class quality gate alongside tests and Flog. Added `rubocop` to the Gemfile, a `.rubocop.yml` tuned for this codebase (Ruby 3.2 target, relaxed `Metrics` thresholds consistent with Flog as the primary complexity gate, `examples/` excluded), and `rake rubocop` / `rake rubocop_fix` tasks backed by a `tmp/rubocop_cache` directory for fast re-runs.
|
|
19
|
+
- **Expanded `rake quality` task** — `quality` now runs three independent gates (tests + coverage, RuboCop, Flog) and prints a formatted pass/fail summary table after all gates complete, so every failure is visible in a single run rather than stopping at the first.
|
|
20
|
+
- **`rake flog_check` task** — replaces the bare `flog lib/` call with a structured task that enforces per-method thresholds (warn ≥20, fail ≥50), lists warnings and failures in separate sections, and exits non-zero only when the failure threshold is breached.
|
|
21
|
+
- **Single-argument `desc` shorthand** — `desc` now accepts one string (the description) with the usage string omitted. The usage defaults to the method name, eliminating the redundant first argument for the common case:
|
|
22
|
+
```ruby
|
|
23
|
+
desc "Run the test suite" # usage defaults to "test"
|
|
24
|
+
def test = sh "bundle exec rake test"
|
|
25
|
+
```
|
|
26
|
+
The two-argument form (`desc "usage", "description"`) still works unchanged and is still required when the usage string differs from the method name (e.g. `desc "build NAME", "Build an artifact"`).
|
|
27
|
+
- **`default_task` override warning** — `Asgard::Base` now overrides Thor's `default_task` to warn to stderr when a second call would silently replace the first. The warning includes the task name, file, and line number for both the original declaration and the override, making accidental cross-file clobbering visible immediately.
|
|
28
|
+
- **`examples/env_usage.loki` and `examples/.env`** — demonstrate the `env()` Kernel helper, including the default-fallback form (`env(:log_level, "info")`) for variables absent from the environment. Uses `loki_up(".env")` so `dotenv` locates the `.env` file correctly regardless of which directory `asgard` is invoked from.
|
|
29
|
+
- **`examples/subdir/`** — three-file demo showing `import` and `import_up` across directory boundaries: `subdir/.loki` imports `import_up_demo.loki` by name; `import_up_demo.loki` calls `import_up "env_usage.loki"` to locate and load a file from an ancestor directory without a hardcoded path.
|
|
30
|
+
- **`status` task in `kitchen_sink.loki`** — demonstrates `debug?` and `verbose?` predicates with conditional output; shows `--debug` printing `$DEBUG`, `$VERBOSE`, and the full options hash.
|
|
31
|
+
- **Computed value methods in `kitchen_sink.loki`** — three private methods (`version`, `sha`, `branch`) demonstrating the idiomatic Ruby replacement for the removed `var` DSL, including memoization via `@ivar ||=`.
|
|
32
|
+
|
|
33
|
+
### Removed
|
|
34
|
+
|
|
35
|
+
- **`var` DSL method** — removed in favour of 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. Breaking change for projects using `var`.
|
|
36
|
+
- **`import` DSL method** — `import(mod)` was a one-line alias for Ruby's built-in `include`. Callers can use `include` directly.
|
|
37
|
+
- **`--auto-load` CLI flag** — sibling `*.loki` loading is now entirely user-controlled: place `import "*.loki"` (or any glob or explicit path) at the top of your `.loki` file to load additional task files. Breaking change for projects that relied on `--auto-load`.
|
|
38
|
+
- **`Asgard.load_loki(dir)`** — replaced by `import` with glob support. Callers can use `import(File.join(dir, "*.loki"))` directly.
|
|
39
|
+
- **`debug?` / `verbose?` private methods on `Tasks`** — removed as redundant. The identical `module_function` versions on `Kernel` are available everywhere, including inside task bodies.
|
|
40
|
+
|
|
41
|
+
### Refactored
|
|
11
42
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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.
|
|
43
|
+
- **`validate_deps!` decomposed into focused private helpers** — the method was performing four unrelated validations in one body (orphaned `depends_on` check, undefined dep name check, dep arity check, cycle detection). Each concern is now a dedicated `_`-prefixed private method (`_check_orphaned_deps!`, `_check_undefined_deps!`, `_check_dep_arities!`, `_build_and_sort_graph`). `validate_deps!` is now a sequencer of ~8 lines. Flog score dropped from 87.3 to 24.4.
|
|
44
|
+
- **`invoke_command` dispatch helpers made private with descriptive names** — the dispatch hook was decomposed into focused helpers. Those helpers (`acquire_run_token`, `run_deps_for`, `run_dep_group`, `signal_done`, `run_dep`) are now declared `private` on `Asgard::Base` rather than wrapped in `no_commands`. The `_` prefix was dropped — the underscore convention is reserved for gem-owned methods on `Tasks`; `private` is sufficient to exclude instance methods from Thor's command registry. `invoke_command` itself stays in `no_commands` so Thor does not warn about an undescribed public method.
|
|
16
45
|
|
|
17
46
|
### Fixed
|
|
18
47
|
|
|
19
|
-
- **
|
|
20
|
-
-
|
|
48
|
+
- **Multiple parallel dep failures now all surfaced** — when two or more parallel deps raised, only the first exception was re-raised; the rest were silently discarded. All errors are now printed to stderr via `warn` before a general `Asgard::Error` is raised. When only one dep fails, its exception is re-raised directly as before.
|
|
49
|
+
- **Subcommand deps not validated at startup** — `run!` only called `Tasks.validate_deps!`, so circular dependencies and undefined dep names in subcommand groups were silently ignored. `run!` now snapshots `Asgard::Base.subclasses` before loading task files and validates every newly defined subclass alongside `Tasks`.
|
|
50
|
+
- **Parallel dep thread orphaned on exception** — when a parallel dep group contained one fast-failing task and one slow task, the join loop re-raised the first thread's exception and abandoned the remaining threads. The join loop now collects all thread exceptions before re-raising, ensuring every thread completes before execution exits the group.
|
|
51
|
+
- **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 arity via `instance_method.parameters` and raises `Asgard::Error` at startup with the task name and argument count.
|
|
52
|
+
- **Orphaned `depends_on` silently discarded** — a `depends_on` declaration at the end of a class body 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.
|
|
53
|
+
- **Single-arg `desc` options silently dropped** — when `desc "description", hide: true` was used, Ruby routed the options hash to the `description` positional parameter. The override now detects a `Hash` in the description position and treats it as options.
|
|
54
|
+
- **Single-arg `desc` stolen by `no_commands` blocks** — if a `no_commands` block appeared between a single-arg `desc` and its method, `method_added` consumed `@_pending_single_desc` for the interstitial helper and leaked a stale `@usage` onto the next real command. The fix: `@_pending_single_desc` is only consumed when `no_commands?` is false.
|
|
55
|
+
- **Parallel dep race condition** — when two parallel tasks shared a common dependency, the second thread could start before the shared dep finished. `_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.
|
|
56
|
+
- **`depends_on` silently dropped before `no_commands` blocks** — the `method_added` guard now checks `@usage` instead of the `no_commands?` counter, so `no_commands` helpers placed between `depends_on` and `def` no longer silently steal the dependency.
|
|
21
57
|
- **`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
58
|
|
|
24
59
|
### Changed
|
|
25
60
|
|
|
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.
|
|
61
|
+
- **`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.
|
|
62
|
+
- **`default_task` behaviour documented** — `docs/tasks.md` now notes that running `asgard` with no arguments displays the help message when `default_task` is not set.
|
|
63
|
+
- **`loki_up` scope clarified in docs** — `docs/task-files.md` and `docs/api.md` now make explicit that `loki_up` locates any file by name, not just `.loki` files, with examples for `.env` and `VERSION`. The `dotenv loki_up(".env") || ".env"` pattern is shown as the canonical way to load a `.env` file from any subdirectory.
|
|
64
|
+
- **`examples/.loki`** — updated to use explicit `import "*.loki"` (sibling files) and `import "subdir/import_demo.loki"` (subdirectory file), with comments explaining `import`, `import_up`, and `loki_up`.
|
|
27
65
|
|
|
28
66
|
## [0.2.0] - 2026-05-29
|
|
29
67
|
|
|
@@ -102,7 +140,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
102
140
|
- 100% test coverage enforced via SimpleCov (95% minimum threshold)
|
|
103
141
|
- Quality task in `.loki` runs flog after tests
|
|
104
142
|
|
|
105
|
-
[
|
|
143
|
+
[0.3.0]: https://github.com/MadBomber/asgard/compare/v0.2.2...HEAD
|
|
106
144
|
[0.2.0]: https://github.com/MadBomber/asgard/compare/v0.1.2...v0.2.0
|
|
107
145
|
[0.1.2]: https://github.com/MadBomber/asgard/compare/v0.1.1...v0.1.2
|
|
108
146
|
[0.1.1]: https://github.com/MadBomber/asgard/compare/v0.1.0...v0.1.1
|
data/CLAUDE.md
CHANGED
|
@@ -29,11 +29,10 @@ 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.
|
|
33
|
-
3.
|
|
34
|
-
4. `Tasks.
|
|
35
|
-
5. `Tasks.
|
|
36
|
-
6. `Tasks.start(argv)` — Thor dispatches the command
|
|
32
|
+
2. Load `.loki` — any sibling `*.loki` files are loaded only if `.loki` calls `import`
|
|
33
|
+
3. `Tasks.validate_deps!` — build full dep graph, raise `CircularDependencyError` if cyclic
|
|
34
|
+
4. `Tasks._reset_ran!` — clear execution tracking
|
|
35
|
+
5. `Tasks.start(argv)` — Thor dispatches the command
|
|
37
36
|
|
|
38
37
|
### Core Classes
|
|
39
38
|
|
|
@@ -71,8 +70,6 @@ Do not define `_`-prefixed methods in user `.loki` files — that namespace is r
|
|
|
71
70
|
depends_on :a, [:b, :c], :d # stages: [[:a], [:b, :c], [:d]]
|
|
72
71
|
```
|
|
73
72
|
|
|
74
|
-
**`var`** stores values or lambdas in `@_vars` and creates an instance method that evaluates the lambda once on first access.
|
|
75
|
-
|
|
76
73
|
**`invoke_command`** (Thor dispatch hook):
|
|
77
74
|
1. Atomically check `@_ran_tasks` Set (with `@_ran_mutex`); return early if already run
|
|
78
75
|
2. Resolve `@_deps` stages → `_build_dep_graph` → `Dagwood::DependencyGraph#parallel_order`
|
|
@@ -91,6 +88,19 @@ Dagwood topologically sorts the DAG and returns parallel groups. The thread-safe
|
|
|
91
88
|
- `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
89
|
- `shebang(interpreter, script)` — writes script to a tempfile and executes with the named interpreter (`:python3`, `:node`, `:ruby`, `:perl`, `:bash`, etc.)
|
|
93
90
|
|
|
91
|
+
### Kernel Methods
|
|
92
|
+
|
|
93
|
+
Asgard adds the following `module_function` methods to `Kernel`, making them available everywhere in `.loki` files without any prefix or require:
|
|
94
|
+
|
|
95
|
+
| Method | Description |
|
|
96
|
+
|--------|-------------|
|
|
97
|
+
| `env(name, default = nil)` | Fetch a system environment variable by symbol or string; name is upcased automatically. Raises `KeyError` when missing and no default given. |
|
|
98
|
+
| `loki_up(name = ".loki")` | Walk CWD and ancestors for a file by name; returns absolute path or `nil`. |
|
|
99
|
+
| `import(path)` | Load a `.loki` file or glob of `.loki` files, idempotently. |
|
|
100
|
+
| `import_up(name = ".loki")` | Combine `loki_up` and `import` — find and load in one call. |
|
|
101
|
+
| `debug?` | Returns `$DEBUG`. |
|
|
102
|
+
| `verbose?` | Returns `$VERBOSE`. |
|
|
103
|
+
|
|
94
104
|
## Testing
|
|
95
105
|
|
|
96
106
|
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.
|
|
@@ -103,7 +113,7 @@ A `.loki` file is plain Ruby that reopens `Tasks`:
|
|
|
103
113
|
|
|
104
114
|
```ruby
|
|
105
115
|
class Tasks
|
|
106
|
-
|
|
116
|
+
@@gem_name ||= "asgard".freeze
|
|
107
117
|
|
|
108
118
|
desc "test", "Run tests"
|
|
109
119
|
def test = sh "bundle exec rake test"
|
|
@@ -114,4 +124,4 @@ class Tasks
|
|
|
114
124
|
end
|
|
115
125
|
```
|
|
116
126
|
|
|
117
|
-
|
|
127
|
+
Only `.loki` is loaded by default. The bare `.loki` file is the project root marker and always controls what else gets loaded — call `import "*.loki"` (or any glob/path) at the top to pull in sibling task files.
|