asgard 0.1.2 → 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/.github/workflows/deploy-github-pages.yml +52 -0
- data/.loki +7 -9
- data/.rubocop.yml +157 -0
- data/CHANGELOG.md +93 -2
- data/CLAUDE.md +19 -9
- data/README.md +110 -58
- data/Rakefile +83 -4
- data/docs/api.md +204 -0
- data/docs/assets/css/custom.css +93 -0
- data/docs/assets/images/asgard.jpg +0 -0
- data/docs/changelog.md +104 -0
- data/docs/dependencies.md +221 -0
- data/docs/environment.md +129 -0
- data/docs/examples.md +140 -0
- data/docs/getting-started.md +179 -0
- data/docs/helpers.md +178 -0
- data/docs/index.md +85 -0
- data/docs/options.md +180 -0
- data/docs/shell.md +208 -0
- data/docs/subcommands.md +181 -0
- data/docs/task-files.md +407 -0
- data/docs/tasks.md +286 -0
- data/docs/variables.md +338 -0
- data/examples/.env +4 -0
- data/examples/.loki +24 -2
- data/examples/concurrent.loki +58 -0
- 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 +159 -54
- data/lib/asgard/kernel_methods.rb +77 -0
- data/lib/asgard/shell.rb +9 -6
- data/lib/asgard/tasks.rb +0 -5
- data/lib/asgard/version.rb +1 -1
- data/lib/asgard.rb +7 -18
- data/mkdocs.yml +164 -0
- metadata +32 -4
data/lib/asgard/base.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thor"
|
|
4
|
-
require "set"
|
|
5
4
|
require "dagwood"
|
|
6
5
|
|
|
7
6
|
module Asgard
|
|
@@ -16,23 +15,30 @@ module Asgard
|
|
|
16
15
|
def inherited(subclass)
|
|
17
16
|
super
|
|
18
17
|
Asgard::Base.subclasses << subclass
|
|
19
|
-
subclass.instance_variable_set(:@_deps,
|
|
20
|
-
subclass.instance_variable_set(:@
|
|
21
|
-
subclass.instance_variable_set(:@
|
|
22
|
-
subclass.instance_variable_set(:@
|
|
23
|
-
subclass.instance_variable_set(:@
|
|
18
|
+
subclass.instance_variable_set(:@_deps, {})
|
|
19
|
+
subclass.instance_variable_set(:@_pending_deps, [])
|
|
20
|
+
subclass.instance_variable_set(:@_pending_single_desc, nil)
|
|
21
|
+
subclass.instance_variable_set(:@_pending_single_desc_opts, nil)
|
|
22
|
+
subclass.instance_variable_set(:@_running, Set.new)
|
|
23
|
+
subclass.instance_variable_set(:@_done, Set.new)
|
|
24
|
+
subclass.instance_variable_set(:@_cond, Hash.new { |h, k| h[k] = ConditionVariable.new })
|
|
25
|
+
subclass.instance_variable_set(:@_ran_mutex, Mutex.new)
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def _deps
|
|
27
29
|
@_deps ||= {}
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
def
|
|
31
|
-
@
|
|
32
|
+
def _running
|
|
33
|
+
@_running ||= Set.new
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
def
|
|
35
|
-
@
|
|
36
|
+
def _done
|
|
37
|
+
@_done ||= Set.new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def _cond
|
|
41
|
+
@_cond ||= Hash.new { |h, k| h[k] = ConditionVariable.new }
|
|
36
42
|
end
|
|
37
43
|
|
|
38
44
|
def _ran_mutex
|
|
@@ -41,7 +47,11 @@ module Asgard
|
|
|
41
47
|
|
|
42
48
|
# Reset execution tracking for a fresh asgard invocation.
|
|
43
49
|
def _reset_ran!
|
|
44
|
-
_ran_mutex.synchronize
|
|
50
|
+
_ran_mutex.synchronize do
|
|
51
|
+
@_running = Set.new
|
|
52
|
+
@_done = Set.new
|
|
53
|
+
@_cond = Hash.new { |h, k| h[k] = ConditionVariable.new }
|
|
54
|
+
end
|
|
45
55
|
end
|
|
46
56
|
|
|
47
57
|
# Translate stages into a DependencyGraph-compatible hash.
|
|
@@ -51,7 +61,7 @@ module Asgard
|
|
|
51
61
|
def _build_dep_graph(stages)
|
|
52
62
|
graph = {}
|
|
53
63
|
stages.each_with_index do |stage, i|
|
|
54
|
-
prev_stage = i
|
|
64
|
+
prev_stage = i.positive? ? stages[i - 1] : []
|
|
55
65
|
stage.each { |task| graph[task] = prev_stage.dup }
|
|
56
66
|
end
|
|
57
67
|
graph
|
|
@@ -68,41 +78,100 @@ module Asgard
|
|
|
68
78
|
@_pending_deps = tasks
|
|
69
79
|
end
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
# Allow single-argument desc: desc "Run the tests"
|
|
82
|
+
# The usage string defaults to the method name when the description is the only arg.
|
|
83
|
+
def desc(usage_or_desc, description = nil, options = {})
|
|
84
|
+
if description.nil? || description.is_a?(Hash)
|
|
85
|
+
options = description if description.is_a?(Hash)
|
|
86
|
+
@_pending_single_desc = usage_or_desc
|
|
87
|
+
@_pending_single_desc_opts = options
|
|
88
|
+
else
|
|
89
|
+
@_pending_single_desc = nil
|
|
90
|
+
@_pending_single_desc_opts = nil
|
|
91
|
+
super
|
|
79
92
|
end
|
|
80
93
|
end
|
|
81
94
|
|
|
82
|
-
def import(mod)
|
|
83
|
-
include mod
|
|
84
|
-
end
|
|
85
|
-
|
|
86
95
|
def dotenv(path = ".env")
|
|
87
96
|
require "dotenv"
|
|
88
97
|
Dotenv.load(path) if File.exist?(path)
|
|
89
98
|
end
|
|
90
99
|
|
|
100
|
+
def default_task(meth = nil)
|
|
101
|
+
if meth && meth != :none && @_default_task_location
|
|
102
|
+
here = caller_locations(1, 1).first
|
|
103
|
+
warn "asgard: default_task :#{meth} at #{here.path}:#{here.lineno} " \
|
|
104
|
+
"overrides default_task :#{@_default_task_name} set at " \
|
|
105
|
+
"#{@_default_task_location.path}:#{@_default_task_location.lineno}"
|
|
106
|
+
end
|
|
107
|
+
if meth && meth != :none
|
|
108
|
+
@_default_task_location = caller_locations(1, 1).first
|
|
109
|
+
@_default_task_name = meth
|
|
110
|
+
end
|
|
111
|
+
super
|
|
112
|
+
end
|
|
113
|
+
|
|
91
114
|
# Validate the full dep graph for cycles using Dagwood::DependencyGraph.
|
|
92
115
|
def validate_deps!
|
|
116
|
+
_check_orphaned_deps!
|
|
93
117
|
return if _deps.empty?
|
|
94
118
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
119
|
+
all_task_names = all_commands.keys.map(&:to_sym)
|
|
120
|
+
_check_undefined_deps!(all_task_names)
|
|
121
|
+
_check_dep_arities!
|
|
122
|
+
_build_and_sort_graph(all_task_names)
|
|
123
|
+
rescue TSort::Cyclic => e
|
|
124
|
+
raise Asgard::CircularDependencyError, e.message
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def _check_orphaned_deps!
|
|
130
|
+
pending = Array(@_pending_deps)
|
|
131
|
+
return unless pending.any?
|
|
132
|
+
|
|
133
|
+
raise Asgard::Error,
|
|
134
|
+
"depends_on(#{pending.join(', ')}) declared without a following task definition"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def _check_undefined_deps!(all_task_names)
|
|
138
|
+
undefined = _deps.values.flatten.uniq - all_task_names
|
|
139
|
+
return unless undefined.any?
|
|
140
|
+
|
|
141
|
+
raise Asgard::Error, "undefined task(s) in depends_on: #{undefined.sort.join(', ')}"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def _check_dep_arities!
|
|
145
|
+
_deps.each_value do |stages|
|
|
146
|
+
stages.flatten.each do |dep|
|
|
147
|
+
meth = instance_method(dep.to_s)
|
|
148
|
+
required = meth.parameters.count { |type, _| type == :req }
|
|
149
|
+
next unless required.positive?
|
|
150
|
+
|
|
151
|
+
raise Asgard::Error,
|
|
152
|
+
"task '#{dep}' has #{required} required argument(s) and cannot be used as a dependency"
|
|
153
|
+
end
|
|
98
154
|
end
|
|
155
|
+
end
|
|
99
156
|
|
|
157
|
+
def _build_and_sort_graph(all_task_names)
|
|
158
|
+
full_graph = all_task_names.to_h { |task| [task, _deps.fetch(task, []).flatten] }
|
|
100
159
|
Dagwood::DependencyGraph.new(full_graph).order
|
|
101
|
-
rescue TSort::Cyclic => e
|
|
102
|
-
raise Asgard::CircularDependencyError, e.message
|
|
103
160
|
end
|
|
104
161
|
|
|
162
|
+
public
|
|
163
|
+
|
|
105
164
|
def method_added(method_name)
|
|
165
|
+
if @_pending_single_desc && !no_commands?
|
|
166
|
+
pending_desc = @_pending_single_desc
|
|
167
|
+
pending_opts = @_pending_single_desc_opts || {}
|
|
168
|
+
@_pending_single_desc = nil
|
|
169
|
+
@_pending_single_desc_opts = nil
|
|
170
|
+
desc(method_name.to_s, pending_desc, pending_opts)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
return super unless @usage
|
|
174
|
+
|
|
106
175
|
pending = Array(@_pending_deps).dup
|
|
107
176
|
@_pending_deps = []
|
|
108
177
|
|
|
@@ -117,42 +186,78 @@ module Asgard
|
|
|
117
186
|
|
|
118
187
|
no_commands do
|
|
119
188
|
# Dispatch hook: resolves and runs all deps (in parallel where declared)
|
|
120
|
-
# before executing the target command.
|
|
121
|
-
#
|
|
189
|
+
# before executing the target command.
|
|
190
|
+
#
|
|
191
|
+
# Completion-based deduplication: a task is only marked done after its
|
|
192
|
+
# body finishes. Threads that arrive at an already-running shared dep
|
|
193
|
+
# wait on its ConditionVariable rather than proceeding immediately,
|
|
194
|
+
# preventing the race where parallel tasks start before a shared dep
|
|
195
|
+
# has actually completed.
|
|
122
196
|
def invoke_command(command, *args)
|
|
123
197
|
$DEBUG = true if options[:debug]
|
|
124
198
|
$VERBOSE = true if options[:verbose]
|
|
125
199
|
target = command.name.to_sym
|
|
200
|
+
return unless acquire_run_token(target)
|
|
126
201
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
202
|
+
begin
|
|
203
|
+
run_deps_for(target)
|
|
204
|
+
command.run(self, *args)
|
|
205
|
+
ensure
|
|
206
|
+
signal_done(target)
|
|
131
207
|
end
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
private
|
|
212
|
+
|
|
213
|
+
def acquire_run_token(target)
|
|
214
|
+
self.class._ran_mutex.synchronize do
|
|
215
|
+
if self.class._done.include?(target)
|
|
216
|
+
false
|
|
217
|
+
elsif self.class._running.include?(target)
|
|
218
|
+
self.class._cond[target].wait(self.class._ran_mutex) until self.class._done.include?(target)
|
|
219
|
+
false
|
|
220
|
+
else
|
|
221
|
+
self.class._running.add(target)
|
|
222
|
+
true
|
|
147
223
|
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def run_deps_for(target)
|
|
228
|
+
stages = self.class._deps[target]
|
|
229
|
+
return unless stages&.any?
|
|
148
230
|
|
|
149
|
-
|
|
231
|
+
groups = Dagwood::DependencyGraph.new(self.class._build_dep_graph(stages)).parallel_order
|
|
232
|
+
groups.each { |group| run_dep_group(group) }
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def run_dep_group(group)
|
|
236
|
+
if group.size > 1
|
|
237
|
+
threads = group.map { |task| Thread.new { run_dep(task) } }
|
|
238
|
+
errors = []
|
|
239
|
+
threads.each { |t| begin; t.join; rescue => e; errors << e; end }
|
|
240
|
+
if errors.size == 1
|
|
241
|
+
raise errors.first
|
|
242
|
+
elsif errors.any?
|
|
243
|
+
errors.each { |e| warn "asgard: #{e.message}" }
|
|
244
|
+
raise Asgard::Error, "#{errors.size} parallel dependencies failed"
|
|
245
|
+
end
|
|
246
|
+
else
|
|
247
|
+
run_dep(group.first)
|
|
150
248
|
end
|
|
249
|
+
end
|
|
151
250
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
251
|
+
def signal_done(target)
|
|
252
|
+
self.class._ran_mutex.synchronize do
|
|
253
|
+
self.class._done.add(target)
|
|
254
|
+
self.class._cond[target].broadcast
|
|
155
255
|
end
|
|
156
256
|
end
|
|
257
|
+
|
|
258
|
+
def run_dep(task)
|
|
259
|
+
command = self.class.all_commands[task.to_s]
|
|
260
|
+
invoke_command(command) if command
|
|
261
|
+
end
|
|
157
262
|
end
|
|
158
263
|
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kernel
|
|
4
|
+
def debug? = $DEBUG
|
|
5
|
+
def verbose? = $VERBOSE
|
|
6
|
+
module_function :debug?, :verbose?
|
|
7
|
+
|
|
8
|
+
# Fetch an environment variable by symbol or string name.
|
|
9
|
+
# The name is converted to an uppercase string automatically.
|
|
10
|
+
# Raises KeyError when the variable is missing and no default is given.
|
|
11
|
+
def env(name, default = nil)
|
|
12
|
+
key = name.to_s.upcase
|
|
13
|
+
default.nil? ? ENV.fetch(key) : ENV.fetch(key, default)
|
|
14
|
+
end
|
|
15
|
+
module_function :env
|
|
16
|
+
|
|
17
|
+
def loki_up(name = ".loki")
|
|
18
|
+
dir = Dir.pwd
|
|
19
|
+
loop do
|
|
20
|
+
candidate = File.join(dir, name)
|
|
21
|
+
return candidate if File.exist?(candidate)
|
|
22
|
+
parent = File.dirname(dir)
|
|
23
|
+
break if parent == dir
|
|
24
|
+
dir = parent
|
|
25
|
+
end
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
module_function :loki_up
|
|
29
|
+
|
|
30
|
+
def import(path)
|
|
31
|
+
path = path.to_s
|
|
32
|
+
raise ArgumentError, "import: path must end with .loki (got #{path.inspect})" unless path.end_with?(".loki")
|
|
33
|
+
unless File.absolute_path?(path)
|
|
34
|
+
caller_dir = File.dirname(caller_locations(1, 1).first.absolute_path)
|
|
35
|
+
path = File.expand_path(path, caller_dir)
|
|
36
|
+
end
|
|
37
|
+
paths = path =~ /[*?\[{]/ ? Dir.glob(path) : [path]
|
|
38
|
+
loaded = paths.map do |p|
|
|
39
|
+
if $LOADED_FEATURES.include?(p)
|
|
40
|
+
warn "import: skip #{p} (already loaded)" if debug?
|
|
41
|
+
next false
|
|
42
|
+
end
|
|
43
|
+
warn "import: #{p}" if verbose? || debug?
|
|
44
|
+
load p
|
|
45
|
+
$LOADED_FEATURES << p
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
loaded.any?
|
|
49
|
+
end
|
|
50
|
+
module_function :import
|
|
51
|
+
|
|
52
|
+
def import_up(name = ".loki")
|
|
53
|
+
if name =~ /[*?\[{]/
|
|
54
|
+
dir = Dir.pwd
|
|
55
|
+
loop do
|
|
56
|
+
matches = Dir.glob(File.join(dir, name))
|
|
57
|
+
unless matches.empty?
|
|
58
|
+
warn "import_up: #{name} → #{dir}" if verbose? || debug?
|
|
59
|
+
return matches.map { |p| import(p) }.any?
|
|
60
|
+
end
|
|
61
|
+
parent = File.dirname(dir)
|
|
62
|
+
break if parent == dir
|
|
63
|
+
dir = parent
|
|
64
|
+
end
|
|
65
|
+
warn "import_up: #{name} not found" if debug?
|
|
66
|
+
return false
|
|
67
|
+
end
|
|
68
|
+
path = loki_up(name)
|
|
69
|
+
unless path
|
|
70
|
+
warn "import_up: #{name} not found" if debug?
|
|
71
|
+
return false
|
|
72
|
+
end
|
|
73
|
+
warn "import_up: #{name} → #{path}" if verbose? || debug?
|
|
74
|
+
import path
|
|
75
|
+
end
|
|
76
|
+
module_function :import_up
|
|
77
|
+
end
|
data/lib/asgard/shell.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'English'
|
|
3
4
|
require "tempfile"
|
|
4
5
|
|
|
5
6
|
module Asgard
|
|
@@ -12,12 +13,12 @@ module Asgard
|
|
|
12
13
|
$stdout.puts script unless silent
|
|
13
14
|
|
|
14
15
|
success = if script.include?("\n")
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
system("bash", "-c", script)
|
|
17
|
+
else
|
|
18
|
+
system(script)
|
|
19
|
+
end
|
|
19
20
|
|
|
20
|
-
exit(
|
|
21
|
+
exit($CHILD_STATUS.exitstatus) unless success
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
# Write +script+ to a tempfile and execute it with +interpreter+.
|
|
@@ -32,11 +33,13 @@ module Asgard
|
|
|
32
33
|
}
|
|
33
34
|
ext = extensions.fetch(interpreter.to_sym, ".tmp")
|
|
34
35
|
|
|
36
|
+
$stdout.puts script unless silent
|
|
37
|
+
|
|
35
38
|
Tempfile.create(["asgard_", ext]) do |f|
|
|
36
39
|
f.write(script)
|
|
37
40
|
f.flush
|
|
38
41
|
system(interpreter.to_s, f.path)
|
|
39
|
-
exit(
|
|
42
|
+
exit($CHILD_STATUS.exitstatus) unless $CHILD_STATUS.success?
|
|
40
43
|
end
|
|
41
44
|
end
|
|
42
45
|
end
|
data/lib/asgard/tasks.rb
CHANGED
data/lib/asgard/version.rb
CHANGED
data/lib/asgard.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "asgard/version"
|
|
4
|
+
require_relative "asgard/kernel_methods"
|
|
4
5
|
require_relative "asgard/shell"
|
|
5
6
|
require_relative "asgard/base"
|
|
6
7
|
require_relative "asgard/tasks"
|
|
@@ -12,34 +13,22 @@ module Asgard
|
|
|
12
13
|
# Search the current directory and its ancestors for a .loki task file.
|
|
13
14
|
# Returns the path string, or nil if not found.
|
|
14
15
|
def self.find_task_file
|
|
15
|
-
|
|
16
|
-
loop do
|
|
17
|
-
candidate = File.join(dir, ".loki")
|
|
18
|
-
return candidate if File.exist?(candidate)
|
|
19
|
-
parent = File.dirname(dir)
|
|
20
|
-
break if parent == dir
|
|
21
|
-
dir = parent
|
|
22
|
-
end
|
|
23
|
-
nil
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Load all *.loki files from dir in alphabetical order.
|
|
27
|
-
# Each file typically reopens class Tasks to add tasks.
|
|
28
|
-
# The .loki entry point is excluded — it is loaded separately by run!.
|
|
29
|
-
def self.load_loki(dir)
|
|
30
|
-
Dir.glob(File.join(dir, "*.loki")).sort.each { |f| load f }
|
|
16
|
+
loki_up
|
|
31
17
|
end
|
|
32
18
|
|
|
33
19
|
# Main entry point invoked by the asgard executable.
|
|
34
20
|
def self.run!(argv)
|
|
35
21
|
abort "asgard: unknown command '#{argv.first}'" if argv.first&.start_with?("_")
|
|
36
22
|
task_file = find_task_file or abort "asgard: no .loki file found in #{Dir.pwd}"
|
|
37
|
-
|
|
23
|
+
before = Asgard::Base.subclasses.dup
|
|
38
24
|
load task_file
|
|
39
|
-
|
|
25
|
+
newly_defined = Asgard::Base.subclasses - before
|
|
26
|
+
(newly_defined + [Tasks]).uniq.each(&:validate_deps!)
|
|
40
27
|
Tasks._reset_ran!
|
|
41
28
|
Tasks.start(argv)
|
|
42
29
|
rescue CircularDependencyError => e
|
|
43
30
|
abort "asgard: circular dependency — #{e.message}"
|
|
31
|
+
rescue Error => e
|
|
32
|
+
abort "asgard: #{e.message}"
|
|
44
33
|
end
|
|
45
34
|
end
|
data/mkdocs.yml
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# MkDocs Configuration for Asgard Documentation
|
|
2
|
+
site_name: Asgard
|
|
3
|
+
site_description: Thor-based Ruby task runner with dependency graphs and concurrent execution
|
|
4
|
+
site_author: Dewayne VanHoozer
|
|
5
|
+
site_url: https://madbomber.github.io/asgard
|
|
6
|
+
copyright: Copyright © 2026 Dewayne VanHoozer
|
|
7
|
+
|
|
8
|
+
# Repository information
|
|
9
|
+
repo_name: madbomber/asgard
|
|
10
|
+
repo_url: https://github.com/MadBomber/asgard
|
|
11
|
+
edit_uri: edit/main/docs/
|
|
12
|
+
|
|
13
|
+
# Configuration
|
|
14
|
+
theme:
|
|
15
|
+
name: material
|
|
16
|
+
|
|
17
|
+
# Color scheme
|
|
18
|
+
palette:
|
|
19
|
+
- scheme: default
|
|
20
|
+
primary: indigo
|
|
21
|
+
accent: amber
|
|
22
|
+
toggle:
|
|
23
|
+
icon: material/brightness-7
|
|
24
|
+
name: Switch to dark mode
|
|
25
|
+
|
|
26
|
+
- scheme: slate
|
|
27
|
+
primary: indigo
|
|
28
|
+
accent: amber
|
|
29
|
+
toggle:
|
|
30
|
+
icon: material/brightness-4
|
|
31
|
+
name: Switch to light mode
|
|
32
|
+
|
|
33
|
+
# Typography
|
|
34
|
+
font:
|
|
35
|
+
text: Roboto
|
|
36
|
+
code: Roboto Mono
|
|
37
|
+
|
|
38
|
+
# Logo and icon
|
|
39
|
+
icon:
|
|
40
|
+
repo: fontawesome/brands/github
|
|
41
|
+
logo: material/shield
|
|
42
|
+
|
|
43
|
+
# Theme features
|
|
44
|
+
features:
|
|
45
|
+
- navigation.instant
|
|
46
|
+
- navigation.tracking
|
|
47
|
+
- navigation.tabs
|
|
48
|
+
- navigation.tabs.sticky
|
|
49
|
+
- navigation.path
|
|
50
|
+
- navigation.indexes
|
|
51
|
+
- navigation.top
|
|
52
|
+
- navigation.footer
|
|
53
|
+
- toc.follow
|
|
54
|
+
- search.suggest
|
|
55
|
+
- search.highlight
|
|
56
|
+
- search.share
|
|
57
|
+
- header.autohide
|
|
58
|
+
- content.code.copy
|
|
59
|
+
- content.code.annotate
|
|
60
|
+
- content.tabs.link
|
|
61
|
+
- content.tooltips
|
|
62
|
+
- content.action.edit
|
|
63
|
+
- content.action.view
|
|
64
|
+
|
|
65
|
+
# Plugins
|
|
66
|
+
plugins:
|
|
67
|
+
- search:
|
|
68
|
+
separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])'
|
|
69
|
+
- tags
|
|
70
|
+
|
|
71
|
+
# Extensions
|
|
72
|
+
markdown_extensions:
|
|
73
|
+
- abbr
|
|
74
|
+
- admonition
|
|
75
|
+
- attr_list
|
|
76
|
+
- def_list
|
|
77
|
+
- footnotes
|
|
78
|
+
- md_in_html
|
|
79
|
+
- tables
|
|
80
|
+
- toc:
|
|
81
|
+
permalink: true
|
|
82
|
+
title: On this page
|
|
83
|
+
|
|
84
|
+
- pymdownx.arithmatex:
|
|
85
|
+
generic: true
|
|
86
|
+
- pymdownx.betterem:
|
|
87
|
+
smart_enable: all
|
|
88
|
+
- pymdownx.caret
|
|
89
|
+
- pymdownx.critic
|
|
90
|
+
- pymdownx.details
|
|
91
|
+
- pymdownx.emoji:
|
|
92
|
+
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
|
93
|
+
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
|
94
|
+
- pymdownx.highlight:
|
|
95
|
+
anchor_linenums: true
|
|
96
|
+
line_spans: __span
|
|
97
|
+
pygments_lang_class: true
|
|
98
|
+
- pymdownx.inlinehilite
|
|
99
|
+
- pymdownx.keys
|
|
100
|
+
- pymdownx.magiclink:
|
|
101
|
+
repo_url_shorthand: true
|
|
102
|
+
user: madbomber
|
|
103
|
+
repo: asgard
|
|
104
|
+
normalize_issue_symbols: true
|
|
105
|
+
- pymdownx.mark
|
|
106
|
+
- pymdownx.smartsymbols
|
|
107
|
+
- pymdownx.snippets:
|
|
108
|
+
check_paths: true
|
|
109
|
+
- pymdownx.superfences:
|
|
110
|
+
custom_fences:
|
|
111
|
+
- name: mermaid
|
|
112
|
+
class: mermaid
|
|
113
|
+
format: !!python/name:pymdownx.superfences.fence_code_format
|
|
114
|
+
- pymdownx.tabbed:
|
|
115
|
+
alternate_style: true
|
|
116
|
+
- pymdownx.tasklist:
|
|
117
|
+
custom_checkbox: true
|
|
118
|
+
- pymdownx.tilde
|
|
119
|
+
|
|
120
|
+
# Extra CSS
|
|
121
|
+
extra_css:
|
|
122
|
+
- assets/css/custom.css
|
|
123
|
+
|
|
124
|
+
# Social media and extra configuration
|
|
125
|
+
extra:
|
|
126
|
+
social:
|
|
127
|
+
- icon: fontawesome/brands/github
|
|
128
|
+
link: https://github.com/MadBomber/asgard
|
|
129
|
+
name: Asgard on GitHub
|
|
130
|
+
- icon: fontawesome/solid/gem
|
|
131
|
+
link: https://rubygems.org/gems/asgard
|
|
132
|
+
name: Asgard on RubyGems
|
|
133
|
+
|
|
134
|
+
analytics:
|
|
135
|
+
feedback:
|
|
136
|
+
title: Was this page helpful?
|
|
137
|
+
ratings:
|
|
138
|
+
- icon: material/emoticon-happy-outline
|
|
139
|
+
name: This page was helpful
|
|
140
|
+
data: 1
|
|
141
|
+
note: Thanks for your feedback!
|
|
142
|
+
- icon: material/emoticon-sad-outline
|
|
143
|
+
name: This page could be improved
|
|
144
|
+
data: 0
|
|
145
|
+
note: Thanks for your feedback! Help us improve by creating an issue.
|
|
146
|
+
|
|
147
|
+
# Navigation
|
|
148
|
+
nav:
|
|
149
|
+
- Home: index.md
|
|
150
|
+
- Getting Started: getting-started.md
|
|
151
|
+
- Tasks:
|
|
152
|
+
- Defining Tasks: tasks.md
|
|
153
|
+
- Dependencies: dependencies.md
|
|
154
|
+
- Variables: variables.md
|
|
155
|
+
- Helper Methods: helpers.md
|
|
156
|
+
- CLI:
|
|
157
|
+
- Options & Flags: options.md
|
|
158
|
+
- Subcommands: subcommands.md
|
|
159
|
+
- Shell Helpers: shell.md
|
|
160
|
+
- Environment: environment.md
|
|
161
|
+
- Task Files: task-files.md
|
|
162
|
+
- API Reference: api.md
|
|
163
|
+
- Examples: examples.md
|
|
164
|
+
- Changelog: changelog.md
|