igniter-extensions 0.5.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 +7 -0
- data/README.md +381 -0
- data/lib/igniter/extensions/contracts/aggregate_pack.rb +103 -0
- data/lib/igniter/extensions/contracts/audit/builder.rb +132 -0
- data/lib/igniter/extensions/contracts/audit/event.rb +34 -0
- data/lib/igniter/extensions/contracts/audit/snapshot.rb +44 -0
- data/lib/igniter/extensions/contracts/audit_pack.rb +60 -0
- data/lib/igniter/extensions/contracts/branch_pack.rb +199 -0
- data/lib/igniter/extensions/contracts/capabilities/declaration.rb +31 -0
- data/lib/igniter/extensions/contracts/capabilities/error.rb +35 -0
- data/lib/igniter/extensions/contracts/capabilities/policy.rb +20 -0
- data/lib/igniter/extensions/contracts/capabilities/report.rb +47 -0
- data/lib/igniter/extensions/contracts/capabilities/violation.rb +30 -0
- data/lib/igniter/extensions/contracts/capabilities_pack.rb +146 -0
- data/lib/igniter/extensions/contracts/collection_pack.rb +212 -0
- data/lib/igniter/extensions/contracts/commerce_pack.rb +91 -0
- data/lib/igniter/extensions/contracts/compose_pack.rb +213 -0
- data/lib/igniter/extensions/contracts/content_addressing/cache.rb +59 -0
- data/lib/igniter/extensions/contracts/content_addressing/content_key.rb +63 -0
- data/lib/igniter/extensions/contracts/content_addressing/declaration.rb +47 -0
- data/lib/igniter/extensions/contracts/content_addressing_pack.rb +90 -0
- data/lib/igniter/extensions/contracts/creator/profile.rb +196 -0
- data/lib/igniter/extensions/contracts/creator/report.rb +85 -0
- data/lib/igniter/extensions/contracts/creator/scaffold.rb +461 -0
- data/lib/igniter/extensions/contracts/creator/scope.rb +79 -0
- data/lib/igniter/extensions/contracts/creator/wizard.rb +269 -0
- data/lib/igniter/extensions/contracts/creator/workflow.rb +189 -0
- data/lib/igniter/extensions/contracts/creator/workflow_step.rb +51 -0
- data/lib/igniter/extensions/contracts/creator/write_result.rb +48 -0
- data/lib/igniter/extensions/contracts/creator/write_step.rb +63 -0
- data/lib/igniter/extensions/contracts/creator/writer.rb +131 -0
- data/lib/igniter/extensions/contracts/creator_pack.rb +128 -0
- data/lib/igniter/extensions/contracts/dataflow/aggregate_operators.rb +119 -0
- data/lib/igniter/extensions/contracts/dataflow/aggregate_state.rb +60 -0
- data/lib/igniter/extensions/contracts/dataflow/builder.rb +66 -0
- data/lib/igniter/extensions/contracts/dataflow/collection_result.rb +70 -0
- data/lib/igniter/extensions/contracts/dataflow/diff.rb +37 -0
- data/lib/igniter/extensions/contracts/dataflow/item_result.rb +44 -0
- data/lib/igniter/extensions/contracts/dataflow/result.rb +58 -0
- data/lib/igniter/extensions/contracts/dataflow/session.rb +173 -0
- data/lib/igniter/extensions/contracts/dataflow/window_filter.rb +49 -0
- data/lib/igniter/extensions/contracts/dataflow_pack.rb +66 -0
- data/lib/igniter/extensions/contracts/debug/pack_audit.rb +181 -0
- data/lib/igniter/extensions/contracts/debug/pack_snapshot.rb +46 -0
- data/lib/igniter/extensions/contracts/debug/profile_snapshot.rb +50 -0
- data/lib/igniter/extensions/contracts/debug/report.rb +50 -0
- data/lib/igniter/extensions/contracts/debug_pack.rb +115 -0
- data/lib/igniter/extensions/contracts/differential/divergence.rb +37 -0
- data/lib/igniter/extensions/contracts/differential/formatter.rb +85 -0
- data/lib/igniter/extensions/contracts/differential/report.rb +83 -0
- data/lib/igniter/extensions/contracts/differential/runner.rb +136 -0
- data/lib/igniter/extensions/contracts/differential_pack.rb +61 -0
- data/lib/igniter/extensions/contracts/execution_report_pack.rb +38 -0
- data/lib/igniter/extensions/contracts/incremental/formatter.rb +60 -0
- data/lib/igniter/extensions/contracts/incremental/node_state.rb +30 -0
- data/lib/igniter/extensions/contracts/incremental/result.rb +65 -0
- data/lib/igniter/extensions/contracts/incremental/session.rb +146 -0
- data/lib/igniter/extensions/contracts/incremental_pack.rb +40 -0
- data/lib/igniter/extensions/contracts/invariants/builder.rb +27 -0
- data/lib/igniter/extensions/contracts/invariants/cases_report.rb +47 -0
- data/lib/igniter/extensions/contracts/invariants/error.rb +34 -0
- data/lib/igniter/extensions/contracts/invariants/invariant.rb +30 -0
- data/lib/igniter/extensions/contracts/invariants/report.rb +45 -0
- data/lib/igniter/extensions/contracts/invariants/suite.rb +36 -0
- data/lib/igniter/extensions/contracts/invariants/violation.rb +39 -0
- data/lib/igniter/extensions/contracts/invariants_pack.rb +88 -0
- data/lib/igniter/extensions/contracts/journal_pack.rb +55 -0
- data/lib/igniter/extensions/contracts/language/formula_pack.rb +185 -0
- data/lib/igniter/extensions/contracts/language/piecewise_pack.rb +166 -0
- data/lib/igniter/extensions/contracts/language/scale_pack.rb +147 -0
- data/lib/igniter/extensions/contracts/lookup_pack.rb +50 -0
- data/lib/igniter/extensions/contracts/mcp/creator_session.rb +105 -0
- data/lib/igniter/extensions/contracts/mcp/tool_argument.rb +35 -0
- data/lib/igniter/extensions/contracts/mcp/tool_definition.rb +33 -0
- data/lib/igniter/extensions/contracts/mcp/tool_result.rb +28 -0
- data/lib/igniter/extensions/contracts/mcp_pack.rb +335 -0
- data/lib/igniter/extensions/contracts/provenance/builder.rb +80 -0
- data/lib/igniter/extensions/contracts/provenance/lineage.rb +59 -0
- data/lib/igniter/extensions/contracts/provenance/node_trace.rb +53 -0
- data/lib/igniter/extensions/contracts/provenance/text_formatter.rb +62 -0
- data/lib/igniter/extensions/contracts/provenance_pack.rb +52 -0
- data/lib/igniter/extensions/contracts/reactive/builder.rb +43 -0
- data/lib/igniter/extensions/contracts/reactive/dispatch_result.rb +59 -0
- data/lib/igniter/extensions/contracts/reactive/engine.rb +79 -0
- data/lib/igniter/extensions/contracts/reactive/event.rb +36 -0
- data/lib/igniter/extensions/contracts/reactive/matcher.rb +20 -0
- data/lib/igniter/extensions/contracts/reactive/plan.rb +58 -0
- data/lib/igniter/extensions/contracts/reactive/subscription.rb +29 -0
- data/lib/igniter/extensions/contracts/reactive_pack.rb +169 -0
- data/lib/igniter/extensions/contracts/saga/compensation.rb +25 -0
- data/lib/igniter/extensions/contracts/saga/compensation_record.rb +28 -0
- data/lib/igniter/extensions/contracts/saga/compensation_set.rb +47 -0
- data/lib/igniter/extensions/contracts/saga/formatter.rb +39 -0
- data/lib/igniter/extensions/contracts/saga/result.rb +56 -0
- data/lib/igniter/extensions/contracts/saga/runner.rb +124 -0
- data/lib/igniter/extensions/contracts/saga_pack.rb +56 -0
- data/lib/igniter/extensions/contracts.rb +445 -0
- data/lib/igniter/extensions.rb +6 -0
- data/lib/igniter-extensions.rb +3 -0
- metadata +152 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Creator
|
|
7
|
+
class Wizard
|
|
8
|
+
PROFILE_EXAMPLES = {
|
|
9
|
+
feature_node: ["examples/contracts/build_your_own_pack.rb"],
|
|
10
|
+
diagnostic_bundle: ["examples/contracts/debug.rb", "examples/contracts/debug_pack_authoring.rb"],
|
|
11
|
+
operational_adapter: ["examples/contracts/build_effect_executor_pack.rb", "examples/contracts/journal.rb"],
|
|
12
|
+
bundle_pack: ["examples/contracts/compose_your_own_packs.rb", "examples/contracts/commerce.rb"]
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
attr_reader :name, :kind, :namespace, :profile, :capabilities, :scope, :root, :mode, :pack, :target_profile
|
|
16
|
+
|
|
17
|
+
def initialize(name: nil, kind: nil, namespace: "MyCompany::IgniterPacks", profile: nil, capabilities: nil,
|
|
18
|
+
scope: nil, root: nil, mode: :skip_existing, pack: nil, target_profile: nil)
|
|
19
|
+
@name = normalize_name(name)
|
|
20
|
+
@kind = kind&.to_sym
|
|
21
|
+
@namespace = normalize_namespace(namespace)
|
|
22
|
+
@profile = profile&.to_sym
|
|
23
|
+
@capabilities = Array(capabilities).map(&:to_sym).uniq.freeze
|
|
24
|
+
@scope = scope&.to_sym
|
|
25
|
+
@root = root&.to_s
|
|
26
|
+
@mode = mode.to_sym
|
|
27
|
+
@pack = pack
|
|
28
|
+
@target_profile = target_profile
|
|
29
|
+
freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def apply(**updates)
|
|
33
|
+
self.class.new(
|
|
34
|
+
name: updates.fetch(:name, name),
|
|
35
|
+
kind: updates.fetch(:kind, kind),
|
|
36
|
+
namespace: updates.fetch(:namespace, namespace),
|
|
37
|
+
profile: updates.fetch(:profile, profile),
|
|
38
|
+
capabilities: updates.fetch(:capabilities, capabilities),
|
|
39
|
+
scope: updates.fetch(:scope, scope),
|
|
40
|
+
root: updates.fetch(:root, root),
|
|
41
|
+
mode: updates.fetch(:mode, mode),
|
|
42
|
+
pack: updates.fetch(:pack, pack),
|
|
43
|
+
target_profile: updates.fetch(:target_profile, target_profile)
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def authoring_profile
|
|
48
|
+
return nil unless profile || kind || !capabilities.empty?
|
|
49
|
+
|
|
50
|
+
Profile.build(
|
|
51
|
+
profile: profile,
|
|
52
|
+
kind: kind,
|
|
53
|
+
capabilities: capabilities
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def target_scope
|
|
58
|
+
return nil unless scope
|
|
59
|
+
|
|
60
|
+
Scope.build(scope)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def ready_for_workflow?
|
|
64
|
+
!name.nil? && !authoring_profile.nil? && !target_scope.nil?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def ready_for_writer?
|
|
68
|
+
ready_for_workflow? && !effective_root.nil?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def effective_root
|
|
72
|
+
return root unless root.nil? || root.empty?
|
|
73
|
+
return nil unless scope
|
|
74
|
+
|
|
75
|
+
suggested_root
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def suggested_root
|
|
79
|
+
case scope
|
|
80
|
+
when :standalone_gem
|
|
81
|
+
name ? "./#{name}" : "./my_pack"
|
|
82
|
+
when :app_local, :monorepo_package
|
|
83
|
+
"."
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def pending_decisions
|
|
88
|
+
decisions = []
|
|
89
|
+
unless name
|
|
90
|
+
decisions << {
|
|
91
|
+
key: :name,
|
|
92
|
+
prompt: "Choose a short pack name",
|
|
93
|
+
options: [],
|
|
94
|
+
hints: ["pick the public noun you want pack users to reach for"]
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
unless authoring_profile
|
|
99
|
+
decisions << {
|
|
100
|
+
key: :profile,
|
|
101
|
+
prompt: "Choose an authoring profile or provide capabilities",
|
|
102
|
+
options: profile_options,
|
|
103
|
+
hints: ["feature packs add graph semantics; operational packs add effect/executor behavior; bundles compose packs without semantic mutation"]
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
unless target_scope
|
|
108
|
+
decisions << {
|
|
109
|
+
key: :scope,
|
|
110
|
+
prompt: "Choose the target scope for the pack",
|
|
111
|
+
options: scope_options,
|
|
112
|
+
hints: branching_hints
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
if ready_for_workflow? && root.nil?
|
|
117
|
+
decisions << {
|
|
118
|
+
key: :root,
|
|
119
|
+
prompt: "Choose the filesystem root for generated files",
|
|
120
|
+
options: [suggested_root].compact,
|
|
121
|
+
hints: ["default root follows the chosen scope; override it only if the host repo layout needs it"]
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
decisions
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def current_decision
|
|
129
|
+
pending_decisions.first
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def recommended_packs
|
|
133
|
+
return { runtime: [], development: [] } unless authoring_profile
|
|
134
|
+
|
|
135
|
+
{
|
|
136
|
+
runtime: authoring_profile.runtime_dependency_hints,
|
|
137
|
+
development: authoring_profile.development_dependency_hints
|
|
138
|
+
}
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def recommended_examples
|
|
142
|
+
return [] unless authoring_profile
|
|
143
|
+
|
|
144
|
+
PROFILE_EXAMPLES.fetch(authoring_profile.name, PROFILE_EXAMPLES.fetch(fallback_profile_name, []))
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def branching_hints
|
|
148
|
+
return [] unless authoring_profile
|
|
149
|
+
|
|
150
|
+
hints = []
|
|
151
|
+
case authoring_profile.kind
|
|
152
|
+
when :operational
|
|
153
|
+
hints << "operational adapters usually want dev-time help from Igniter::Extensions::Contracts::JournalPack"
|
|
154
|
+
hints << "prefer standalone_gem when the adapter will be reused across hosts"
|
|
155
|
+
when :bundle
|
|
156
|
+
if authoring_profile.capability?(:diagnostic)
|
|
157
|
+
hints << "diagnostic bundles usually compose Igniter::Extensions::Contracts::ExecutionReportPack and Igniter::Extensions::Contracts::ProvenancePack"
|
|
158
|
+
hints << "keep DebugPack as a development helper unless the bundle truly needs a runtime-facing debug surface"
|
|
159
|
+
else
|
|
160
|
+
hints << "bundle packs should stay thin and explicit about which packs they compose"
|
|
161
|
+
end
|
|
162
|
+
when :feature
|
|
163
|
+
hints << "feature node packs often start app-local before being promoted into a monorepo package or gem"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
hints.concat(authoring_profile.development_hints)
|
|
167
|
+
hints.uniq
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def workflow
|
|
171
|
+
unless ready_for_workflow?
|
|
172
|
+
raise ArgumentError, "creator wizard is missing decisions: #{pending_decisions.map do |decision|
|
|
173
|
+
decision.fetch(:key)
|
|
174
|
+
end.join(", ")}"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
CreatorPack.workflow(
|
|
178
|
+
name: name,
|
|
179
|
+
kind: kind,
|
|
180
|
+
namespace: namespace,
|
|
181
|
+
profile: profile,
|
|
182
|
+
capabilities: capabilities,
|
|
183
|
+
scope: scope,
|
|
184
|
+
pack: pack,
|
|
185
|
+
target_profile: target_profile
|
|
186
|
+
)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def writer
|
|
190
|
+
raise ArgumentError, "creator wizard needs a root before writing scaffold files" unless ready_for_writer?
|
|
191
|
+
|
|
192
|
+
workflow.writer(root: effective_root, mode: mode)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def to_h
|
|
196
|
+
{
|
|
197
|
+
name: name,
|
|
198
|
+
kind: kind,
|
|
199
|
+
namespace: namespace,
|
|
200
|
+
profile: profile,
|
|
201
|
+
capabilities: capabilities,
|
|
202
|
+
scope: scope,
|
|
203
|
+
root: root,
|
|
204
|
+
suggested_root: suggested_root,
|
|
205
|
+
effective_root: effective_root,
|
|
206
|
+
mode: mode,
|
|
207
|
+
ready_for_workflow: ready_for_workflow?,
|
|
208
|
+
ready_for_writer: ready_for_writer?,
|
|
209
|
+
authoring_profile: authoring_profile&.to_h,
|
|
210
|
+
target_scope: target_scope&.to_h,
|
|
211
|
+
recommended_packs: recommended_packs,
|
|
212
|
+
recommended_examples: recommended_examples,
|
|
213
|
+
branching_hints: branching_hints,
|
|
214
|
+
pending_decisions: pending_decisions
|
|
215
|
+
}
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
private
|
|
219
|
+
|
|
220
|
+
def fallback_profile_name
|
|
221
|
+
case authoring_profile&.kind
|
|
222
|
+
when :operational
|
|
223
|
+
:operational_adapter
|
|
224
|
+
when :bundle
|
|
225
|
+
authoring_profile&.capability?(:diagnostic) ? :diagnostic_bundle : :bundle_pack
|
|
226
|
+
else
|
|
227
|
+
:feature_node
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def profile_options
|
|
232
|
+
Profile.available.map do |available_profile|
|
|
233
|
+
built = Profile.build(profile: available_profile)
|
|
234
|
+
{
|
|
235
|
+
key: available_profile,
|
|
236
|
+
kind: built.kind,
|
|
237
|
+
summary: built.summary,
|
|
238
|
+
capabilities: built.capabilities
|
|
239
|
+
}
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def scope_options
|
|
244
|
+
Scope.available.map do |available_scope|
|
|
245
|
+
built = Scope.build(available_scope)
|
|
246
|
+
{
|
|
247
|
+
key: available_scope,
|
|
248
|
+
root: built.root,
|
|
249
|
+
summary: built.packaging_hints.first
|
|
250
|
+
}
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def normalize_name(value)
|
|
255
|
+
return nil if value.nil?
|
|
256
|
+
|
|
257
|
+
normalized = value.to_s.strip.gsub(/_pack\z/, "").downcase
|
|
258
|
+
normalized.empty? ? nil : normalized
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def normalize_namespace(value)
|
|
262
|
+
normalized = value.to_s.strip
|
|
263
|
+
normalized.empty? ? "MyCompany::IgniterPacks" : normalized
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "workflow_step"
|
|
4
|
+
require_relative "writer"
|
|
5
|
+
|
|
6
|
+
module Igniter
|
|
7
|
+
module Extensions
|
|
8
|
+
module Contracts
|
|
9
|
+
module Creator
|
|
10
|
+
class Workflow
|
|
11
|
+
attr_reader :report
|
|
12
|
+
|
|
13
|
+
def initialize(report:)
|
|
14
|
+
@report = report
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def scaffold
|
|
19
|
+
report.scaffold
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def audit
|
|
23
|
+
report.audit
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def profile
|
|
27
|
+
scaffold.profile
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def scope
|
|
31
|
+
scaffold.scope
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def recommended_packs
|
|
35
|
+
{
|
|
36
|
+
runtime: profile.runtime_dependency_hints,
|
|
37
|
+
development: profile.development_dependency_hints
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def stages
|
|
42
|
+
[
|
|
43
|
+
design_stage,
|
|
44
|
+
scaffold_stage,
|
|
45
|
+
implementation_stage,
|
|
46
|
+
validation_stage,
|
|
47
|
+
packaging_stage
|
|
48
|
+
]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def current_stage
|
|
52
|
+
stages.find { |stage| !stage.complete? } || stages.last
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def ready_for_packaging?
|
|
56
|
+
packaging_stage.status == :ready
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def writer(root:, mode: :skip_existing)
|
|
60
|
+
Writer.new(workflow: self, root: root, mode: mode)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def to_h
|
|
64
|
+
{
|
|
65
|
+
scaffold: scaffold.to_h,
|
|
66
|
+
report: report.to_h,
|
|
67
|
+
recommended_packs: recommended_packs,
|
|
68
|
+
current_stage: current_stage.to_h,
|
|
69
|
+
ready_for_packaging: ready_for_packaging?,
|
|
70
|
+
stages: stages.map(&:to_h)
|
|
71
|
+
}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def design_stage
|
|
77
|
+
hints = []
|
|
78
|
+
hints << "runtime pack recommendations: #{profile.runtime_dependency_hints.join(", ")}" unless profile.runtime_dependency_hints.empty?
|
|
79
|
+
hints << "development pack recommendations: #{profile.development_dependency_hints.join(", ")}" unless profile.development_dependency_hints.empty?
|
|
80
|
+
|
|
81
|
+
WorkflowStep.new(
|
|
82
|
+
key: :select_profile,
|
|
83
|
+
status: :complete,
|
|
84
|
+
title: "Select Authoring Profile",
|
|
85
|
+
summary: "#{profile.name} -> #{profile.summary}; target scope #{scope.name}",
|
|
86
|
+
hints: hints
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def scaffold_stage
|
|
91
|
+
WorkflowStep.new(
|
|
92
|
+
key: :generate_scaffold,
|
|
93
|
+
status: :complete,
|
|
94
|
+
title: "Generate Scaffold",
|
|
95
|
+
summary: "generated #{scaffold.files.size} files rooted at #{scaffold.pack_file_path}",
|
|
96
|
+
hints: scaffold.files.keys
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def implementation_stage
|
|
101
|
+
if audit&.ok?
|
|
102
|
+
WorkflowStep.new(
|
|
103
|
+
key: :implement_pack,
|
|
104
|
+
status: :complete,
|
|
105
|
+
title: "Implement Pack",
|
|
106
|
+
summary: "#{scaffold.pack_constant} satisfies the current install/finalize seam checks"
|
|
107
|
+
)
|
|
108
|
+
elsif audit
|
|
109
|
+
WorkflowStep.new(
|
|
110
|
+
key: :implement_pack,
|
|
111
|
+
status: :needs_attention,
|
|
112
|
+
title: "Implement Pack",
|
|
113
|
+
summary: "fill the missing pack seams in #{scaffold.pack_file_path}",
|
|
114
|
+
hints: implementation_hints
|
|
115
|
+
)
|
|
116
|
+
else
|
|
117
|
+
WorkflowStep.new(
|
|
118
|
+
key: :implement_pack,
|
|
119
|
+
status: :ready,
|
|
120
|
+
title: "Implement Pack",
|
|
121
|
+
summary: "fill in #{scaffold.pack_file_path} and keep the pack on public contracts only",
|
|
122
|
+
hints: report.next_steps
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def validation_stage
|
|
128
|
+
if audit&.ok?
|
|
129
|
+
WorkflowStep.new(
|
|
130
|
+
key: :validate_pack,
|
|
131
|
+
status: :complete,
|
|
132
|
+
title: "Validate Pack",
|
|
133
|
+
summary: "audit passed; the pack finalizes cleanly against the current contracts profile"
|
|
134
|
+
)
|
|
135
|
+
elsif audit
|
|
136
|
+
WorkflowStep.new(
|
|
137
|
+
key: :validate_pack,
|
|
138
|
+
status: :needs_attention,
|
|
139
|
+
title: "Validate Pack",
|
|
140
|
+
summary: "audit_pack found missing seams before finalize",
|
|
141
|
+
hints: [audit.explain]
|
|
142
|
+
)
|
|
143
|
+
else
|
|
144
|
+
WorkflowStep.new(
|
|
145
|
+
key: :validate_pack,
|
|
146
|
+
status: :ready,
|
|
147
|
+
title: "Validate Pack",
|
|
148
|
+
summary: "run audit_pack once the implementation is in place",
|
|
149
|
+
hints: ["Igniter::Extensions::Contracts.audit_pack(#{scaffold.pack_constant}, environment)"]
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def packaging_stage
|
|
155
|
+
if audit&.ok?
|
|
156
|
+
WorkflowStep.new(
|
|
157
|
+
key: :package_pack,
|
|
158
|
+
status: :ready,
|
|
159
|
+
title: "Package Pack",
|
|
160
|
+
summary: "the pack is ready for #{scope.name} packaging review",
|
|
161
|
+
hints: scope.packaging_hints
|
|
162
|
+
)
|
|
163
|
+
else
|
|
164
|
+
WorkflowStep.new(
|
|
165
|
+
key: :package_pack,
|
|
166
|
+
status: :pending,
|
|
167
|
+
title: "Package Pack",
|
|
168
|
+
summary: "finish implementation and validation before packaging for #{scope.name}",
|
|
169
|
+
hints: scope.packaging_hints
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def implementation_hints
|
|
175
|
+
hints = []
|
|
176
|
+
hints << "define node kinds: #{audit.missing_node_definitions.join(", ")}" unless audit.missing_node_definitions.empty?
|
|
177
|
+
hints << "register DSL keywords: #{audit.missing_dsl_keywords.join(", ")}" unless audit.missing_dsl_keywords.empty?
|
|
178
|
+
hints << "register runtime handlers: #{audit.missing_runtime_handlers.join(", ")}" unless audit.missing_runtime_handlers.empty?
|
|
179
|
+
audit.missing_registry_contracts.each do |registry, keys|
|
|
180
|
+
hints << "register #{registry}: #{keys.join(", ")}"
|
|
181
|
+
end
|
|
182
|
+
hints << "resolve finalize error: #{audit.finalize_error}" if audit.finalize_error
|
|
183
|
+
hints
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Creator
|
|
7
|
+
class WorkflowStep
|
|
8
|
+
VALID_STATUSES = %i[complete ready needs_attention pending].freeze
|
|
9
|
+
|
|
10
|
+
attr_reader :key, :status, :title, :summary, :hints
|
|
11
|
+
|
|
12
|
+
def initialize(key:, status:, title:, summary:, hints: [])
|
|
13
|
+
@key = key.to_sym
|
|
14
|
+
@status = normalize_status(status)
|
|
15
|
+
@title = title
|
|
16
|
+
@summary = summary
|
|
17
|
+
@hints = Array(hints).map(&:to_s).uniq.freeze
|
|
18
|
+
freeze
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def complete?
|
|
22
|
+
status == :complete
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def actionable?
|
|
26
|
+
%i[ready needs_attention].include?(status)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_h
|
|
30
|
+
{
|
|
31
|
+
key: key,
|
|
32
|
+
status: status,
|
|
33
|
+
title: title,
|
|
34
|
+
summary: summary,
|
|
35
|
+
hints: hints
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def normalize_status(status)
|
|
42
|
+
value = status.to_sym
|
|
43
|
+
return value if VALID_STATUSES.include?(value)
|
|
44
|
+
|
|
45
|
+
raise ArgumentError, "unsupported creator workflow step status #{status.inspect}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Creator
|
|
7
|
+
class WriteResult
|
|
8
|
+
attr_reader :root, :mode, :steps
|
|
9
|
+
|
|
10
|
+
def initialize(root:, mode:, steps:)
|
|
11
|
+
@root = root
|
|
12
|
+
@mode = mode.to_sym
|
|
13
|
+
@steps = steps.freeze
|
|
14
|
+
freeze
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def files_written
|
|
18
|
+
steps.count { |step| step.kind == :file && %i[written unchanged].include?(step.status) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def files_skipped
|
|
22
|
+
steps.count { |step| step.kind == :file && step.status == :skipped }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def directories_created
|
|
26
|
+
steps.count { |step| step.kind == :directory && step.status == :created }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def success?
|
|
30
|
+
steps.none?(&:actionable?)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_h
|
|
34
|
+
{
|
|
35
|
+
root: root,
|
|
36
|
+
mode: mode,
|
|
37
|
+
files_written: files_written,
|
|
38
|
+
files_skipped: files_skipped,
|
|
39
|
+
directories_created: directories_created,
|
|
40
|
+
success: success?,
|
|
41
|
+
steps: steps.map(&:to_h)
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Creator
|
|
7
|
+
class WriteStep
|
|
8
|
+
VALID_KINDS = %i[directory file].freeze
|
|
9
|
+
VALID_STATUSES = %i[pending created written skipped unchanged].freeze
|
|
10
|
+
|
|
11
|
+
attr_reader :kind, :relative_path, :absolute_path, :status, :reason
|
|
12
|
+
|
|
13
|
+
def initialize(kind:, relative_path:, absolute_path:, status:, reason: nil)
|
|
14
|
+
@kind = normalize_kind(kind)
|
|
15
|
+
@relative_path = relative_path.to_s
|
|
16
|
+
@absolute_path = absolute_path.to_s
|
|
17
|
+
@status = normalize_status(status)
|
|
18
|
+
@reason = reason
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def actionable?
|
|
23
|
+
status == :pending
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def written?
|
|
27
|
+
%i[created written unchanged].include?(status)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def skipped?
|
|
31
|
+
status == :skipped
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_h
|
|
35
|
+
{
|
|
36
|
+
kind: kind,
|
|
37
|
+
relative_path: relative_path,
|
|
38
|
+
absolute_path: absolute_path,
|
|
39
|
+
status: status,
|
|
40
|
+
reason: reason
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def normalize_kind(kind)
|
|
47
|
+
value = kind.to_sym
|
|
48
|
+
return value if VALID_KINDS.include?(value)
|
|
49
|
+
|
|
50
|
+
raise ArgumentError, "unsupported creator write step kind #{kind.inspect}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def normalize_status(status)
|
|
54
|
+
value = status.to_sym
|
|
55
|
+
return value if VALID_STATUSES.include?(value)
|
|
56
|
+
|
|
57
|
+
raise ArgumentError, "unsupported creator write step status #{status.inspect}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|