hecks 3.2.0 → 3.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/lib/hecks/bluebook/assembly/contracts.rb +2 -1
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +2 -39
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +3 -3
- data/lib/hecks/bluebook/dsl/need_word.rb +50 -0
- data/lib/hecks/bluebook/dsl/query_builder.rb +6 -1
- data/lib/hecks/bluebook/query.rb +8 -5
- data/lib/hecks/cli/check_era.rb +3 -39
- data/lib/hecks/cli/interview_draft.rb +86 -31
- data/lib/hecks/cli/interview_session.rb +77 -26
- data/lib/hecks/cli/interview_shape.rb +179 -0
- data/lib/hecks/doors/cli_runner.rb +14 -1
- data/lib/hecks/doors/launcher_options.rb +13 -3
- data/lib/hecks/doors/mcp_door.rb +16 -2
- data/lib/hecks/doors/mcp_door_scope.rb +45 -5
- data/lib/hecks/doors/mcp_guide_cache.rb +75 -0
- data/lib/hecks/doors.rb +1 -1
- data/lib/hecks/embryonaut_bluebook/manifest.rb +117 -0
- data/lib/hecks/embryonaut_bluebook/registry.rb +209 -0
- data/lib/hecks/embryonaut_bluebook/vendor.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +2 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +17 -1
- data/lib/hecks/fuzzing/qa_settings.rb +15 -34
- data/lib/hecks/gate/stages.yml +113 -8
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +10 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +10 -2
- data/lib/hecks/hecks/adapters/first_admin.rb +108 -0
- data/lib/hecks/hecks/adapters/git.rb +59 -0
- data/lib/hecks/hecks/adapters/in_process_access.rb +25 -0
- data/lib/hecks/hecks/adapters/in_process_boot.rb +2 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +18 -3
- data/lib/hecks/hecks/adapters/rust_workspace.rb +1 -1
- data/lib/hecks/hecks/adapters/site_toolchain.rb +88 -11
- data/lib/hecks/hecks/codebase.behaviors +6 -0
- data/lib/hecks/hecks/codebase.bluebook +21 -0
- data/lib/hecks/hecks/custodian.behaviors +6 -0
- data/lib/hecks/hecks/custodian.bluebook +217 -0
- data/lib/hecks/hecks/hecks.hecksagon +24 -0
- data/lib/hecks/hecks/hecks.world +8 -3
- data/lib/hecks/hecks/site.world +5 -2
- data/lib/hecks/language/bluebook/query.bluebook +25 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +220 -7
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +1 -1
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -3
- data/lib/hecks/projections/deploy/box/hosting.rb +170 -0
- data/lib/hecks/projections/deploy/box/hosting_settings.rb +123 -0
- data/lib/hecks/projections/deploy/box/settings.rb +40 -6
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +2 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.services-roll.part +33 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +79 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.taskdef-roll.part +77 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.taskdef.part +12 -0
- data/lib/hecks/projections/deploy/box/templates/hosting.mk.tmpl +24 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +17 -0
- data/lib/hecks/projections/deploy/box/templates/smoke-after-deploy.sh.tmpl +271 -0
- data/lib/hecks/projections/deploy/box.rb +28 -4
- data/lib/hecks/projections/deploy/fargate.rb +3 -3
- data/lib/hecks/projections/deploy/lambda.rb +8 -8
- data/lib/hecks/projections/model/deviations.rb +1 -1
- data/lib/hecks/projections/site/edge/behaviours.rb +5 -3
- data/lib/hecks/projections/site/edge/checks.rb +31 -5
- data/lib/hecks/projections/site/edge.rb +25 -11
- data/lib/hecks/projections/site/live_edge.rb +162 -0
- data/lib/hecks/projections/site/routes_ts.rb +50 -27
- data/lib/hecks/projections/site/site_cdn.rb +7 -4
- data/lib/hecks/projections/site/table/checks.rb +53 -7
- data/lib/hecks/projections/site/table/link_builder.rb +64 -0
- data/lib/hecks/projections/site/table/row_builder.rb +21 -6
- data/lib/hecks/projections/site/table.rb +16 -5
- data/lib/hecks/quality_control/adapters/git_pr.rb +0 -14
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +32 -16
- data/lib/hecks/quality_control/cli/qa_seed_angles.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_seed_targets.rb +1 -2
- data/lib/hecks/quality_control/quality_control.bluebook +76 -124
- data/lib/hecks/runtime/interpreting.rb +3 -22
- data/lib/hecks/runtime/needs.rb +48 -0
- data/lib/hecks/runtime/query_interpreter.rb +3 -0
- data/lib/hecks/runtime/registry/verification.rb +3 -3
- data/lib/hecks/rust_build/host.rb +147 -0
- data/lib/hecks/rust_build/kernel_input.rb +51 -17
- data/lib/hecks/rust_build/project_rust/codegen_run.rb +0 -18
- data/lib/hecks/rust_build/project_rust.rb +7 -140
- data/lib/hecks/rust_build.rb +1 -0
- data/lib/hecks/site/bluebook/site.bluebook +80 -14
- data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
- data/lib/hecks/sme/bluebook/sme.bluebook +128 -1
- data/lib/hecks/storehouse.rb +35 -1
- data/lib/hecks/tools/comment_style.rb +4 -2
- data/lib/hecks/tools/gate.rb +1 -1
- data/lib/hecks/tools/rust_comment_style.rb +2 -4
- data/lib/hecks/tools/site_routes.rb +69 -28
- data/lib/hecks/tools/tools_doc.rb +204 -0
- data/lib/hecks/tools.rb +1 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +152 -1
- data/lib/hecks.rb +1 -0
- data/qa/settings.yml +1 -2
- data/rust/build/src/lineage_pass.rs +31 -16
- data/rust/build/src/main.rs +4 -2
- data/rust/build/src/pipeline.rs +9 -20
- data/rust/codegen/src/attr.rs +1 -1
- data/rust/codegen/src/bridging.rs +8 -8
- data/rust/codegen/src/commands.rs +1 -1
- data/rust/codegen/src/constraints.rs +1 -1
- data/rust/codegen/src/dependency_planning.rs +1 -1
- data/rust/codegen/src/domain_generator.rs +2 -2
- data/rust/codegen/src/exemplar.rs +1 -1
- data/rust/codegen/src/expr_emitter.rs +1 -1
- data/rust/codegen/src/fielded.rs +1 -1
- data/rust/codegen/src/json.rs +214 -23
- data/rust/codegen/src/json_codec.rs +1 -1
- data/rust/codegen/src/literal.rs +6 -3
- data/rust/codegen/src/main.rs +23 -13
- data/rust/codegen/src/mutations.rs +1 -1
- data/rust/codegen/src/naming.rs +5 -3
- data/rust/codegen/src/optional_pass.rs +226 -0
- data/rust/codegen/src/ports.rs +1 -1
- data/rust/codegen/src/queries.rs +3 -3
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/read_models.rs +2 -2
- data/rust/codegen/src/reference_specs.rs +1 -1
- data/rust/codegen/src/registry.rs +4 -4
- data/rust/codegen/src/shared.rs +1 -1
- data/rust/{build → codegen}/src/sidecars.rs +9 -5
- data/rust/codegen/src/skip_reason.rs +1 -1
- data/rust/codegen/src/types.rs +4 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +1 -1
- data/rust/host/src/dispatch.rs +5 -0
- data/rust/host/src/needs.rs +193 -29
- data/rust/host/src/resend.rs +4 -4
- data/rust/host/src/ui_schema.rs +1 -1
- data/rust/parser/src/emit.rs +12 -0
- data/rust/parser/src/ir.rs +2 -0
- data/rust/parser/src/keywords.rs +2 -0
- data/rust/parser/src/main.rs +1 -0
- data/rust/parser/src/parse/command.rs +2 -44
- data/rust/parser/src/parse/mod.rs +1 -0
- data/rust/parser/src/parse/needs.rs +60 -0
- data/rust/parser/src/parse/query.rs +3 -0
- data/rust/src/exemplar/commands.rs +1 -1
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/fielded.rs +1 -1
- data/rust/src/exemplar/json.rs +1 -1
- data/rust/src/exemplar/mod.rs +1 -1
- data/rust/src/exemplar/mutations.rs +1 -1
- data/rust/src/exemplar/queries.rs +1 -1
- data/rust/src/exemplar/reactions.rs +1 -1
- data/rust/src/exemplar/read_models.rs +1 -1
- data/rust/src/exemplar/registry.rs +1 -1
- data/rust/src/exemplar/types.rs +1 -1
- data/rust/src/kernel/cli.rs +18 -8
- data/rust/src/kernel/needs.rs +104 -9
- data/rust/src/kernel/read_model.rs +1 -1
- data/rust/src/lib.rs +1 -1
- metadata +24 -30
- data/lib/hecks/rust_build/append_optionals.rb +0 -79
- data/lib/hecks/rust_build/rust_literal.rb +0 -33
- data/lib/hecks/three_zero/forms.yml +0 -127
- data/lib/hecks/three_zero.rb +0 -12
- data/rust/build/src/json.rs +0 -421
- data/rust/build/src/optional_pass.rs +0 -150
- data/rust/project/bridging.rb +0 -257
- data/rust/project/commands.rb +0 -877
- data/rust/project/constraints.rb +0 -86
- data/rust/project/dependency_planning.rb +0 -178
- data/rust/project/domain_generator.rb +0 -983
- data/rust/project/exemplar.rb +0 -148
- data/rust/project/expr_emitter.rb +0 -72
- data/rust/project/fielded.rb +0 -141
- data/rust/project/json_codec.rb +0 -641
- data/rust/project/mutations.rb +0 -695
- data/rust/project/naming.rb +0 -223
- data/rust/project/ports.rb +0 -65
- data/rust/project/queries.rb +0 -493
- data/rust/project/reactions.rb +0 -317
- data/rust/project/read_models.rb +0 -690
- data/rust/project/reference_specs.rb +0 -27
- data/rust/project/registry.rb +0 -396
- data/rust/project/skip_reason.rb +0 -29
- data/rust/project/types.rb +0 -225
- data/rust/project/write_if_changed.rb +0 -7
- data/rust/project.rb +0 -30
- data/rust/project_rust_pipeline.rb +0 -391
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "json"
|
|
5
5
|
require "tmpdir"
|
|
6
|
-
require_relative "../rust_literal"
|
|
7
6
|
require_relative "../write_if_changed"
|
|
8
7
|
|
|
9
8
|
module Hecks
|
|
@@ -78,7 +77,6 @@ module Hecks
|
|
|
78
77
|
def generate_meta(stage, tmp)
|
|
79
78
|
ir_path = write_ir(tmp, "meta", @meta)
|
|
80
79
|
run("full", stage, "meta", META_LABEL, ir_path)
|
|
81
|
-
write_sidecars(File.join(stage, "meta"), @meta, META_LABEL)
|
|
82
80
|
end
|
|
83
81
|
|
|
84
82
|
def generate_target(stage, tmp)
|
|
@@ -87,9 +85,6 @@ module Hecks
|
|
|
87
85
|
args.push(chapter.mod_name, chapter.label, write_ir(tmp, chapter.mod_name, chapter.ir))
|
|
88
86
|
end
|
|
89
87
|
run("full", *args)
|
|
90
|
-
[@target, *@chapters].each do |chapter|
|
|
91
|
-
write_sidecars(File.join(stage, chapter.mod_name), chapter.ir, chapter.label)
|
|
92
|
-
end
|
|
93
88
|
end
|
|
94
89
|
|
|
95
90
|
def write_ir(tmp, name, tree)
|
|
@@ -98,19 +93,6 @@ module Hecks
|
|
|
98
93
|
path
|
|
99
94
|
end
|
|
100
95
|
|
|
101
|
-
# `ir.json` is what `rust/host` reads at runtime, and `metadata.rs` embeds the same text;
|
|
102
|
-
# the codegen binary writes neither.
|
|
103
|
-
def write_sidecars(mod_dir, tree, label)
|
|
104
|
-
text = JSON.pretty_generate(tree)
|
|
105
|
-
File.write(File.join(mod_dir, "ir.json"), text)
|
|
106
|
-
File.write(File.join(mod_dir, "metadata.rs"), <<~RUST)
|
|
107
|
-
// GENERATED by hecks project_rust — #{label}'s own canonical IR,
|
|
108
|
-
// embedded for runtime self-description. Not read by any dispatch
|
|
109
|
-
// function in this module — introspection only.
|
|
110
|
-
pub const IR_JSON: &str = #{RustLiteral.string(text)};
|
|
111
|
-
RUST
|
|
112
|
-
end
|
|
113
|
-
|
|
114
96
|
def run(*args)
|
|
115
97
|
return if system(self.class.binary(@rust_dir), *args, out: $stdout, err: $stderr)
|
|
116
98
|
|
|
@@ -5,7 +5,6 @@ require "json"
|
|
|
5
5
|
require_relative "../../hecks"
|
|
6
6
|
require_relative "../bluebook/meta_validator"
|
|
7
7
|
require_relative "../ports/persistence/plugins/era"
|
|
8
|
-
require_relative "append_optionals"
|
|
9
8
|
require_relative "domain_name"
|
|
10
9
|
require_relative "write_if_changed"
|
|
11
10
|
|
|
@@ -17,9 +16,8 @@ module Hecks
|
|
|
17
16
|
# ProjectRust.call(["path/to/domain"])
|
|
18
17
|
#
|
|
19
18
|
# `hecks-codegen` is the generator (ADR 0086): this class builds the IR from the live registry
|
|
20
|
-
# and `CodegenRun` runs the binary on it.
|
|
21
|
-
#
|
|
22
|
-
# `rust/project_rust_pipeline.rb` instead, with no Ruby load of the domain.
|
|
19
|
+
# and `CodegenRun` runs the binary on it. The Ruby-free path is `hecks-build`, which feeds the
|
|
20
|
+
# same binary from `hecks-parse`.
|
|
23
21
|
#
|
|
24
22
|
# The era plugin is required unconditionally so the generator works for any domain, lineage
|
|
25
23
|
# capable or not; `pg` stays lazy inside it, so nothing here opens a database connection.
|
|
@@ -46,13 +44,7 @@ module Hecks
|
|
|
46
44
|
# @return [Integer] the exit status
|
|
47
45
|
def call
|
|
48
46
|
validate_name!
|
|
49
|
-
|
|
50
|
-
require_relative "../../../rust/project"
|
|
51
|
-
require_relative "../../../rust/project_rust_pipeline"
|
|
52
|
-
RustProjectPipeline.call(@domain)
|
|
53
|
-
else
|
|
54
|
-
generate
|
|
55
|
-
end
|
|
47
|
+
generate
|
|
56
48
|
0
|
|
57
49
|
end
|
|
58
50
|
|
|
@@ -78,18 +70,11 @@ module Hecks
|
|
|
78
70
|
FileUtils.mkdir_p(@out_root)
|
|
79
71
|
build_target_ir
|
|
80
72
|
FileUtils.rm_rf(File.join(@out_root, "active"))
|
|
81
|
-
|
|
73
|
+
generate_with_codegen
|
|
82
74
|
write_root_mod
|
|
83
75
|
sync_cargo_features
|
|
84
76
|
end
|
|
85
77
|
|
|
86
|
-
# The rollback path (ADR 0086): the Ruby generator in `rust/project`, until it is deleted.
|
|
87
|
-
def generate_with_ruby
|
|
88
|
-
require_relative "../../../rust/project"
|
|
89
|
-
write_meta
|
|
90
|
-
write_target
|
|
91
|
-
end
|
|
92
|
-
|
|
93
78
|
# `hecks-codegen` is the generator; this process only builds the IR it reads from the live
|
|
94
79
|
# registry, so a domain's persistence, seams, translations and source text reach `ir.json`.
|
|
95
80
|
def generate_with_codegen
|
|
@@ -97,20 +82,14 @@ module Hecks
|
|
|
97
82
|
vendored = @registry.hecksagons.values.flat_map(&:vendored_packages)
|
|
98
83
|
chapters = (@registry.bluebooks.keys - [@domain_name]).map do |name|
|
|
99
84
|
ir = Hecks::Projector::Exporter.call(@registry).fetch(name)
|
|
100
|
-
CodegenRun::Chapter.new(name.downcase, "#{@domain} (#{attachment(name, vendored)})",
|
|
85
|
+
CodegenRun::Chapter.new(name.downcase, "#{@domain} (#{attachment(name, vendored)})", json_shaped(ir))
|
|
101
86
|
end
|
|
102
87
|
CodegenRun.new(
|
|
103
|
-
rust_dir: @rust_dir, out_root: @out_root, meta:
|
|
104
|
-
target: CodegenRun::Chapter.new(@mod_name, @domain,
|
|
88
|
+
rust_dir: @rust_dir, out_root: @out_root, meta: json_shaped(meta), chapters: chapters,
|
|
89
|
+
target: CodegenRun::Chapter.new(@mod_name, @domain, @ir)
|
|
105
90
|
).call
|
|
106
91
|
end
|
|
107
92
|
|
|
108
|
-
# The IR as `rust/project` hands it to its generators: string-keyed through JSON, with the
|
|
109
|
-
# append-optional fields marked, since `ir.json` records them.
|
|
110
|
-
def prepared_ir(tree, shaped: true)
|
|
111
|
-
AppendOptionals.mark(shaped ? json_shaped(tree) : tree)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
93
|
# Round-trips a value through JSON so generators see the string-keyed shape a real `ir.json`
|
|
115
94
|
# carries, never live Ruby symbols.
|
|
116
95
|
def json_shaped(payload) = JSON.parse(JSON.generate(payload), symbolize_names: true)
|
|
@@ -186,78 +165,6 @@ module Hecks
|
|
|
186
165
|
)
|
|
187
166
|
end
|
|
188
167
|
|
|
189
|
-
# The self-hosted language goes through the same codegen as the target, read from the
|
|
190
|
-
# grammar registry `MetaValidator` boots with.
|
|
191
|
-
def write_meta
|
|
192
|
-
meta_ir = json_shaped(
|
|
193
|
-
Hecks::Projector::Exporter.call(Hecks::Bluebook::MetaValidator.grammar_registry).fetch("Bluebook")
|
|
194
|
-
)
|
|
195
|
-
dir = File.join(@out_root, "meta")
|
|
196
|
-
# Tracked per directory: several domains coexist on disk, and `pop_and_prune` deletes only
|
|
197
|
-
# the files this run never touched. `active` is a set of feature re-exports in the root
|
|
198
|
-
# `mod.rs`, never a directory.
|
|
199
|
-
WriteIfChanged.push_directory(dir)
|
|
200
|
-
result = RustProjection::DomainGenerator.call(
|
|
201
|
-
meta_ir, "the self-hosted language (lib/hecks/language/bluebook)", dir, "meta"
|
|
202
|
-
)
|
|
203
|
-
write_merged(File.join(dir, "merged.rs"), result[:aggregates], result[:queries], result[:read_models],
|
|
204
|
-
policies: RustProjection::Projector.emit_policy_table(meta_ir[:name], meta_ir[:policies],
|
|
205
|
-
meta_ir[:aggregates]),
|
|
206
|
-
cross: RustProjection::Projector.emit_cross_domain_policy_table(meta_ir[:name],
|
|
207
|
-
meta_ir[:policies]),
|
|
208
|
-
managers: meta_ir[:process_managers],
|
|
209
|
-
keys: [[meta_ir[:name], result[:aggregates].map { |a| a[:name] }]])
|
|
210
|
-
append_merged_mod(File.join(dir, "mod.rs"))
|
|
211
|
-
WriteIfChanged.pop_and_prune(dir)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
def write_target
|
|
215
|
-
dir = File.join(@out_root, @mod_name)
|
|
216
|
-
WriteIfChanged.push_directory(dir)
|
|
217
|
-
result = RustProjection::DomainGenerator.call(@ir, @domain, dir, @mod_name)
|
|
218
|
-
chapters = write_chapters
|
|
219
|
-
aggregates = result[:aggregates] + chapters[:aggregates]
|
|
220
|
-
write_merged(File.join(dir, "merged.rs"), aggregates, result[:queries] + chapters[:queries],
|
|
221
|
-
result[:read_models] + chapters[:read_models],
|
|
222
|
-
policies: RustProjection::Projector.emit_merged_policy_table(chapters[:policy_sources]),
|
|
223
|
-
cross: RustProjection::Projector.emit_merged_cross_domain_policy_table(chapters[:policy_sources]),
|
|
224
|
-
managers: @ir[:process_managers] + chapters[:process_managers],
|
|
225
|
-
keys: reference_keys(result[:aggregates], chapters))
|
|
226
|
-
append_merged_mod(File.join(dir, "mod.rs"))
|
|
227
|
-
WriteIfChanged.pop_and_prune(dir)
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
# Every other bluebook the target's `attaches` calls pulled into the registry, each
|
|
231
|
-
# generated into its own module. A framework chapter is a function of its bluebook alone, so
|
|
232
|
-
# regenerating one is safe whichever domain last touched it.
|
|
233
|
-
def write_chapters
|
|
234
|
-
totals = { aggregates: [], queries: [], read_models: [], process_managers: [],
|
|
235
|
-
policy_sources: [{ domain_name: @domain_name, policies: @ir[:policies],
|
|
236
|
-
aggregates: @ir[:aggregates] }],
|
|
237
|
-
mod_names: { @mod_name => @domain_name } }
|
|
238
|
-
vendored = @registry.hecksagons.values.flat_map(&:vendored_packages)
|
|
239
|
-
(@registry.bluebooks.keys - [@domain_name]).each { |chapter| write_chapter(chapter, vendored, totals) }
|
|
240
|
-
totals
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
def write_chapter(chapter_name, vendored, totals)
|
|
244
|
-
mod_name = chapter_name.downcase
|
|
245
|
-
totals[:mod_names][mod_name] = chapter_name
|
|
246
|
-
chapter_ir = json_shaped(Hecks::Projector::Exporter.call(@registry).fetch(chapter_name))
|
|
247
|
-
result = nil
|
|
248
|
-
WriteIfChanged.track_directory(File.join(@out_root, mod_name)) do
|
|
249
|
-
result = RustProjection::DomainGenerator.call(chapter_ir, "#{@domain} (#{attachment(chapter_name, vendored)})",
|
|
250
|
-
File.join(@out_root, mod_name), mod_name,
|
|
251
|
-
merged_module: false)
|
|
252
|
-
end
|
|
253
|
-
totals[:aggregates].concat(result[:aggregates])
|
|
254
|
-
totals[:queries].concat(result[:queries])
|
|
255
|
-
totals[:read_models].concat(result[:read_models])
|
|
256
|
-
totals[:process_managers].concat(chapter_ir[:process_managers])
|
|
257
|
-
totals[:policy_sources] << { domain_name: chapter_name, policies: chapter_ir[:policies],
|
|
258
|
-
aggregates: chapter_ir[:aggregates] }
|
|
259
|
-
end
|
|
260
|
-
|
|
261
168
|
# How the target attached a chapter, across every hecksagon the registry loaded: a chapter a
|
|
262
169
|
# sibling hecksagon vendors in is labelled as vendored.
|
|
263
170
|
def attachment(chapter_name, vendored)
|
|
@@ -265,46 +172,6 @@ module Hecks
|
|
|
265
172
|
name ? "attaches #{name.inspect}, from: :vendor" : "attaches #{chapter_name.inspect}"
|
|
266
173
|
end
|
|
267
174
|
|
|
268
|
-
def reference_keys(target_aggregates, chapters)
|
|
269
|
-
chapters[:mod_names].map do |mod_name, chapter_name|
|
|
270
|
-
own = mod_name == @mod_name ? target_aggregates : chapters[:aggregates].select { |a| a[:chapter_mod] == mod_name }
|
|
271
|
-
[chapter_name, own.map { |a| a[:name] }]
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
# Writes a module's `merged.rs`: the registry and every dispatch table over its aggregates.
|
|
276
|
-
def write_merged(path, aggregates, queries, read_models, policies:, cross:, managers:, keys:)
|
|
277
|
-
projector = RustProjection::Projector
|
|
278
|
-
wrote = WriteIfChanged.block(path) do |file|
|
|
279
|
-
[projector.emit_registry(aggregates), projector.emit_reference_lookup(aggregates), policies, cross,
|
|
280
|
-
projector.emit_process_manager_table(managers), projector.emit_reference_key_table(keys),
|
|
281
|
-
projector.emit_creates_table(aggregates), projector.emit_identity_head_table(aggregates),
|
|
282
|
-
projector.emit_entity_identity_head_table(aggregates),
|
|
283
|
-
projector.emit_command_attributes_table(aggregates), projector.emit_query_table(queries),
|
|
284
|
-
projector.emit_query_arg_check_table(queries)].each do |table|
|
|
285
|
-
file.puts table
|
|
286
|
-
file.puts
|
|
287
|
-
end
|
|
288
|
-
# A read model's `group_by` function is written before the table names it.
|
|
289
|
-
read_models.each do |model|
|
|
290
|
-
next unless model[:group_by_fn_body]
|
|
291
|
-
|
|
292
|
-
file.puts model[:group_by_fn_body]
|
|
293
|
-
file.puts
|
|
294
|
-
end
|
|
295
|
-
file.puts projector.emit_read_model_table(read_models)
|
|
296
|
-
end
|
|
297
|
-
puts(wrote ? "wrote #{path}" : "#{path} unchanged")
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
# Appends the `pub mod merged;` line once; appending it again would grow the file and bump
|
|
301
|
-
# its mtime on every run.
|
|
302
|
-
def append_merged_mod(path)
|
|
303
|
-
return if File.exist?(path) && File.read(path).include?("pub mod merged;")
|
|
304
|
-
|
|
305
|
-
File.open(path, "a") { |file| file.puts "pub mod merged;" }
|
|
306
|
-
end
|
|
307
|
-
|
|
308
175
|
# `src/generated/mod.rs`: every domain ever generated, scanned off disk. A directory is a
|
|
309
176
|
# selectable domain exactly when it carries its own `merged.rs`; a framework chapter has
|
|
310
177
|
# none and is always compiled in.
|
data/lib/hecks/rust_build.rb
CHANGED
|
@@ -31,6 +31,7 @@ module Hecks
|
|
|
31
31
|
TOOLS = {
|
|
32
32
|
"project_rust" => ["rust_build/project_rust", :ProjectRust],
|
|
33
33
|
"project_wasm" => ["rust_build/wasm", :Wasm],
|
|
34
|
+
"project_host" => ["rust_build/host", :Host],
|
|
34
35
|
"project_wasm_browser" => ["rust_build/wasm_browser", :WasmBrowser],
|
|
35
36
|
"rust_conformance" => ["rust_build/conformance", :Conformance],
|
|
36
37
|
"rust_conformance_fuzz" => ["rust_build/conformance_fuzz", :ConformanceFuzz],
|
|
@@ -95,12 +95,18 @@ Hecks.bluebook "Site" do
|
|
|
95
95
|
aggregate "SiteProjection" do
|
|
96
96
|
description "One projection of a site's route table into its routes.ts, or the check that the file on disk is current."
|
|
97
97
|
|
|
98
|
-
attribute :domain,
|
|
99
|
-
attribute :run,
|
|
100
|
-
attribute :out,
|
|
101
|
-
attribute :
|
|
102
|
-
attribute :
|
|
103
|
-
attribute :
|
|
98
|
+
attribute :domain, DomainPath
|
|
99
|
+
attribute :run, RunKey
|
|
100
|
+
attribute :out, Location, optional: true
|
|
101
|
+
attribute :template, Location, optional: true
|
|
102
|
+
attribute :extension, Extension, optional: true
|
|
103
|
+
attribute :check, Switch, optional: true
|
|
104
|
+
attribute :live, Location, optional: true
|
|
105
|
+
attribute :distribution, DistributionId, optional: true
|
|
106
|
+
attribute :expect_new, PathList, optional: true
|
|
107
|
+
attribute :refs, PolicyRefs, optional: true
|
|
108
|
+
attribute :output, Report, optional: true
|
|
109
|
+
attribute :refusal, Report, optional: true
|
|
104
110
|
identified_by :run
|
|
105
111
|
|
|
106
112
|
value_object "RunKey" do
|
|
@@ -116,15 +122,37 @@ Hecks.bluebook "Site" do
|
|
|
116
122
|
invariant("a project is named by its path") { !value.to_s.empty? }
|
|
117
123
|
end
|
|
118
124
|
|
|
119
|
-
#
|
|
125
|
+
# A directory or file anywhere on disk, relative to where the command runs: where routes.ts is
|
|
126
|
+
# written when not in the project's generated/ directory, or the template to rewrite in place.
|
|
120
127
|
value_object "Location" do
|
|
121
128
|
attribute :value, String, pattern: '[^\t\n\r]'
|
|
122
129
|
end
|
|
123
130
|
|
|
131
|
+
# The file extension of the generated module: `ts` (routes.ts), or `mts` (routes.mts), which
|
|
132
|
+
# Node loads as an ES module even in a package whose "type" is "commonjs".
|
|
133
|
+
value_object "Extension" do
|
|
134
|
+
attribute :value, String, one_of: ["ts", "mts"]
|
|
135
|
+
end
|
|
136
|
+
|
|
124
137
|
value_object "Switch" do
|
|
125
138
|
attribute :value, Boolean
|
|
126
139
|
end
|
|
127
140
|
|
|
141
|
+
# A CloudFront distribution's id, whose configuration `check_live` reads.
|
|
142
|
+
value_object "DistributionId" do
|
|
143
|
+
attribute :value, String, pattern: '^[A-Z0-9]+$'
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Path patterns, comma separated: the behaviours a change is about to add to the live distribution.
|
|
147
|
+
value_object "PathList" do
|
|
148
|
+
attribute :value, String, pattern: '[^\t\n\r]'
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Policy references and the ids they stand for live, as `!Ref Name=id`, comma separated.
|
|
152
|
+
value_object "PolicyRefs" do
|
|
153
|
+
attribute :value, String, pattern: '[^\t\n\r]'
|
|
154
|
+
end
|
|
155
|
+
|
|
128
156
|
value_object "Report" do
|
|
129
157
|
attribute :value, String
|
|
130
158
|
end
|
|
@@ -134,26 +162,59 @@ Hecks.bluebook "Site" do
|
|
|
134
162
|
transition "Fault" => "faulted", from: "requested"
|
|
135
163
|
end
|
|
136
164
|
|
|
137
|
-
# `hecks site site_projection.project_site <project> [out=<dir>] [--check]`.
|
|
138
|
-
# routes.ts that differs from the table is faulted, naming the
|
|
165
|
+
# `hecks site site_projection.project_site <project> [out=<dir>] [template=<file>] [extension=ts|mts] [--check]`.
|
|
166
|
+
# With `check` nothing is written: a routes.ts that differs from the table is faulted, naming the
|
|
167
|
+
# file, so a CI job fails on drift.
|
|
139
168
|
command "ProjectSite" do
|
|
140
169
|
goal "Project a site's route table into routes.ts, or with check only compare it with the file on disk"
|
|
141
170
|
|
|
142
|
-
attribute :domain,
|
|
143
|
-
attribute :run,
|
|
144
|
-
attribute :out,
|
|
145
|
-
attribute :
|
|
171
|
+
attribute :domain, DomainPath
|
|
172
|
+
attribute :run, RunKey
|
|
173
|
+
attribute :out, Location, optional: true
|
|
174
|
+
attribute :template, Location, optional: true
|
|
175
|
+
attribute :extension, Extension, optional: true
|
|
176
|
+
attribute :check, Switch, optional: true
|
|
146
177
|
|
|
147
178
|
sets :domain
|
|
148
179
|
sets :run
|
|
149
180
|
sets :out
|
|
181
|
+
sets :template
|
|
182
|
+
sets :extension
|
|
150
183
|
sets :check
|
|
151
184
|
|
|
152
185
|
emits "SiteProjectionRequested"
|
|
153
186
|
end
|
|
154
187
|
|
|
188
|
+
# `hecks site site_projection.check_live <project> live=<saved-config.json> | distribution=<id>
|
|
189
|
+
# [expect_new=<paths>] [refs=<!Ref Name=id,...>]`. Compares the project's generated CloudFront
|
|
190
|
+
# behaviours with a live distribution's, changing neither. `live` is a saved answer of
|
|
191
|
+
# `aws cloudfront get-distribution-config`; `distribution` fetches it with that one read-only call.
|
|
192
|
+
command "CheckLive" do
|
|
193
|
+
goal "Compare a project's generated CloudFront behaviours with a live distribution's"
|
|
194
|
+
|
|
195
|
+
attribute :domain, DomainPath
|
|
196
|
+
attribute :run, RunKey
|
|
197
|
+
attribute :live, Location, optional: true
|
|
198
|
+
attribute :distribution, DistributionId, optional: true
|
|
199
|
+
attribute :expect_new, PathList, optional: true
|
|
200
|
+
attribute :refs, PolicyRefs, optional: true
|
|
201
|
+
|
|
202
|
+
given("exactly one of a saved configuration and a distribution is named: pass live= or distribution=, not both") do
|
|
203
|
+
(live.set? && distribution.unset?) || (live.unset? && distribution.set?)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
sets :domain
|
|
207
|
+
sets :run
|
|
208
|
+
sets :live
|
|
209
|
+
sets :distribution
|
|
210
|
+
sets :expect_new
|
|
211
|
+
sets :refs
|
|
212
|
+
|
|
213
|
+
emits "LiveCheckRequested"
|
|
214
|
+
end
|
|
215
|
+
|
|
155
216
|
command "Complete" do
|
|
156
|
-
goal "Record that routes.ts was written, or found current, and
|
|
217
|
+
goal "Record that routes.ts was written, or found current, or the live edge matched, and what it said"
|
|
157
218
|
|
|
158
219
|
reference_to SiteProjection
|
|
159
220
|
attribute :output, Report
|
|
@@ -193,6 +254,11 @@ Hecks.bluebook "Site" do
|
|
|
193
254
|
trigger SiteProjection::SiteToolchain::Project, with: { run: :run }
|
|
194
255
|
end
|
|
195
256
|
|
|
257
|
+
policy "CheckWhenRequested" do
|
|
258
|
+
on "SiteProjection.LiveCheckRequested"
|
|
259
|
+
trigger SiteProjection::SiteToolchain::Compare, with: { run: :run }
|
|
260
|
+
end
|
|
261
|
+
|
|
196
262
|
policy "RecordTheProjection" do
|
|
197
263
|
on "SiteProjection.SiteAnswered"
|
|
198
264
|
trigger SiteProjection::Complete, with: { output: :output }
|
|
@@ -21,6 +21,8 @@ Hecks.bluebook "SME" do
|
|
|
21
21
|
attribute :thing_findings, list_of(ThingFinding)
|
|
22
22
|
attribute :action_findings, list_of(ActionFinding)
|
|
23
23
|
attribute :rule_findings, list_of(RuleFinding)
|
|
24
|
+
attribute :field_findings, list_of(FieldFinding)
|
|
25
|
+
attribute :transition_findings, list_of(TransitionFinding)
|
|
24
26
|
|
|
25
27
|
# Held as counts because a rule cannot look inside a list: `Conclude` needs to know that at least
|
|
26
28
|
# one thing and one action were accepted, and each acceptance adds one here (see the policies).
|
|
@@ -92,6 +94,17 @@ Hecks.bluebook "SME" do
|
|
|
92
94
|
attribute :value, Boolean
|
|
93
95
|
end
|
|
94
96
|
|
|
97
|
+
# A list the expert gave in a breath, comma separated: the values a field may take, or the
|
|
98
|
+
# fields an action needs to be told. Absent when the expert gave none.
|
|
99
|
+
value_object "Listing" do
|
|
100
|
+
attribute :value, String, pattern: '[^\t\n\r]'
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Who may do an action, in the expert's words ("a maintainer", "QA engineer").
|
|
104
|
+
value_object "Actor" do
|
|
105
|
+
attribute :value, String, pattern: '[^\t\n\r]'
|
|
106
|
+
end
|
|
107
|
+
|
|
95
108
|
# A thing the domain keeps track of, and the field that identifies one: an aggregate to be.
|
|
96
109
|
entity "ThingFinding" do
|
|
97
110
|
description "A thing the expert described, proposed as an aggregate, with the field that identifies one."
|
|
@@ -136,6 +149,8 @@ Hecks.bluebook "SME" do
|
|
|
136
149
|
attribute :thing, Name
|
|
137
150
|
attribute :event, Name
|
|
138
151
|
attribute :creates, Creates, optional: true
|
|
152
|
+
attribute :takes, Listing, optional: true
|
|
153
|
+
attribute :by, Actor, optional: true
|
|
139
154
|
attribute :source, Source
|
|
140
155
|
|
|
141
156
|
lifecycle :status, default: "proposed" do
|
|
@@ -192,6 +207,77 @@ Hecks.bluebook "SME" do
|
|
|
192
207
|
end
|
|
193
208
|
end
|
|
194
209
|
|
|
210
|
+
# Something the expert said a thing has, and the values it may take when they listed them: an
|
|
211
|
+
# attribute to be, with a closed set when the expert gave one.
|
|
212
|
+
entity "FieldFinding" do
|
|
213
|
+
description "A field the expert said a thing has, with the values it may take when they listed them."
|
|
214
|
+
|
|
215
|
+
identified_by :number
|
|
216
|
+
|
|
217
|
+
attribute :number, FindingNumber
|
|
218
|
+
attribute :thing, Name
|
|
219
|
+
attribute :name, Name
|
|
220
|
+
attribute :values, Listing, optional: true
|
|
221
|
+
attribute :source, Source
|
|
222
|
+
|
|
223
|
+
lifecycle :status, default: "proposed" do
|
|
224
|
+
transition "AcceptField" => "accepted", from: "proposed"
|
|
225
|
+
transition "RejectField" => "rejected", from: "proposed"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
command "AcceptField", from: "proposed" do
|
|
229
|
+
goal "Establish that the field is real"
|
|
230
|
+
|
|
231
|
+
attribute :number, FindingNumber
|
|
232
|
+
|
|
233
|
+
emits "FieldAccepted"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
command "RejectField", from: "proposed" do
|
|
237
|
+
goal "Set the proposed field aside"
|
|
238
|
+
|
|
239
|
+
attribute :number, FindingNumber
|
|
240
|
+
|
|
241
|
+
emits "FieldRejected"
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# The state a thing is in after an action, and the state it had to be in before: one step of a
|
|
246
|
+
# lifecycle to be. The step an action that creates the thing makes names the state it starts in.
|
|
247
|
+
entity "TransitionFinding" do
|
|
248
|
+
description "A change of state an action makes to a thing, and the state it must be in before."
|
|
249
|
+
|
|
250
|
+
identified_by :number
|
|
251
|
+
|
|
252
|
+
attribute :number, FindingNumber
|
|
253
|
+
attribute :thing, Name
|
|
254
|
+
attribute :action, Name
|
|
255
|
+
attribute :to, Name
|
|
256
|
+
attribute :from, Name, optional: true
|
|
257
|
+
attribute :source, Source
|
|
258
|
+
|
|
259
|
+
lifecycle :status, default: "proposed" do
|
|
260
|
+
transition "AcceptTransition" => "accepted", from: "proposed"
|
|
261
|
+
transition "RejectTransition" => "rejected", from: "proposed"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
command "AcceptTransition", from: "proposed" do
|
|
265
|
+
goal "Establish that the change of state is real"
|
|
266
|
+
|
|
267
|
+
attribute :number, FindingNumber
|
|
268
|
+
|
|
269
|
+
emits "TransitionAccepted"
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
command "RejectTransition", from: "proposed" do
|
|
273
|
+
goal "Set the proposed change of state aside"
|
|
274
|
+
|
|
275
|
+
attribute :number, FindingNumber
|
|
276
|
+
|
|
277
|
+
emits "TransitionRejected"
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
195
281
|
lifecycle :status, default: "planned" do
|
|
196
282
|
transition "Begin" => "underway", from: "planned"
|
|
197
283
|
transition "Conclude" => "concluded", from: "underway"
|
|
@@ -257,16 +343,57 @@ Hecks.bluebook "SME" do
|
|
|
257
343
|
attribute :thing, Name
|
|
258
344
|
attribute :event, Name
|
|
259
345
|
attribute :creates, Creates, optional: true
|
|
346
|
+
attribute :takes, Listing, optional: true
|
|
347
|
+
attribute :by, Actor, optional: true
|
|
260
348
|
attribute :source, Source
|
|
261
349
|
|
|
262
350
|
given("findings are proposed only while the interview is underway") { status == "underway" }
|
|
263
351
|
given("a finding cites an exchange that was recorded") { source.value <= exchanges.size }
|
|
264
352
|
|
|
265
|
-
sets :action_findings, append: { number: :number, name: :name, thing: :thing, event: :event, creates: :creates,
|
|
353
|
+
sets :action_findings, append: { number: :number, name: :name, thing: :thing, event: :event, creates: :creates,
|
|
354
|
+
takes: :takes, by: :by, source: :source }
|
|
266
355
|
|
|
267
356
|
emits "ActionProposed"
|
|
268
357
|
end
|
|
269
358
|
|
|
359
|
+
command "ProposeField" do
|
|
360
|
+
goal "Propose a field the expert said a thing has, and the values it may take"
|
|
361
|
+
|
|
362
|
+
reference_to Interview
|
|
363
|
+
attribute :number, FindingNumber
|
|
364
|
+
attribute :thing, Name
|
|
365
|
+
attribute :name, Name
|
|
366
|
+
attribute :values, Listing, optional: true
|
|
367
|
+
attribute :source, Source
|
|
368
|
+
|
|
369
|
+
given("findings are proposed only while the interview is underway") { status == "underway" }
|
|
370
|
+
given("a finding cites an exchange that was recorded") { source.value <= exchanges.size }
|
|
371
|
+
|
|
372
|
+
sets :field_findings, append: { number: :number, thing: :thing, name: :name, values: :values, source: :source }
|
|
373
|
+
|
|
374
|
+
emits "FieldProposed"
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
command "ProposeTransition" do
|
|
378
|
+
goal "Propose the change of state an action makes to a thing"
|
|
379
|
+
|
|
380
|
+
reference_to Interview
|
|
381
|
+
attribute :number, FindingNumber
|
|
382
|
+
attribute :thing, Name
|
|
383
|
+
attribute :action, Name
|
|
384
|
+
attribute :to, Name
|
|
385
|
+
attribute :from, Name, optional: true
|
|
386
|
+
attribute :source, Source
|
|
387
|
+
|
|
388
|
+
given("findings are proposed only while the interview is underway") { status == "underway" }
|
|
389
|
+
given("a finding cites an exchange that was recorded") { source.value <= exchanges.size }
|
|
390
|
+
|
|
391
|
+
sets :transition_findings, append: { number: :number, thing: :thing, action: :action, to: :to, from: :from,
|
|
392
|
+
source: :source }
|
|
393
|
+
|
|
394
|
+
emits "TransitionProposed"
|
|
395
|
+
end
|
|
396
|
+
|
|
270
397
|
command "ProposeRule" do
|
|
271
398
|
goal "Keep a rule in the expert's words"
|
|
272
399
|
|
data/lib/hecks/storehouse.rb
CHANGED
|
@@ -230,10 +230,44 @@ module Hecks
|
|
|
230
230
|
result = runtime.dispatch_flat(spec[:command], envelope)
|
|
231
231
|
ok(summary: summary,
|
|
232
232
|
id: result.id,
|
|
233
|
-
state:
|
|
233
|
+
state: settled_state(runtime, spec, result),
|
|
234
234
|
events: result.events.map { |event| { name: event.name, payload: Doors::JsonDoor.materialize(event.payload) } })
|
|
235
235
|
end
|
|
236
236
|
|
|
237
|
+
# The record as its repository holds it once every reaction has run, which is the state the
|
|
238
|
+
# dispatch resulted in; the handle's own state is the record as the command left it, so a run
|
|
239
|
+
# record that a reaction completes would otherwise read as `requested`. Falls back to the
|
|
240
|
+
# handle's state for a command that belongs to no top-level aggregate or whose record cannot be
|
|
241
|
+
# read.
|
|
242
|
+
# :nodoc:
|
|
243
|
+
def settled_state(runtime, spec, result)
|
|
244
|
+
return if result.state.nil?
|
|
245
|
+
|
|
246
|
+
bluebook = bluebook_for(runtime)
|
|
247
|
+
head = spec[:command].split("::", 2).last
|
|
248
|
+
aggregate = bluebook.aggregates.find { |candidate| candidate.hecks_name == head.split(".").first } if
|
|
249
|
+
head.count(".") == 1
|
|
250
|
+
record = aggregate && runtime.registry.repository(bluebook.name, aggregate)&.find(result.id)&.to_h
|
|
251
|
+
Doors::JsonDoor.materialize(record || result.state)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# What a caller needs to call each of the named commands: its qualified name, the role it
|
|
255
|
+
# declares, what it does, and its argument names, a trailing `*` marking a required one.
|
|
256
|
+
#
|
|
257
|
+
# @param runtime [Runtime::Dispatcher] the booted domain
|
|
258
|
+
# @param names [Array<String>] short or qualified command names
|
|
259
|
+
# @return [Array<String>] one line per name that resolves, in order
|
|
260
|
+
def command_guide(runtime, names)
|
|
261
|
+
cli = Projector.call(:cli, bluebook: bluebook_for(runtime), options: { program: "mcp" })
|
|
262
|
+
names.filter_map do |name|
|
|
263
|
+
spec = resolve!(cli, name.to_s, asking: false)
|
|
264
|
+
arguments = spec[:arguments].map { |arg| arg[:path].split(".").first + (arg[:required] ? "*" : "") }
|
|
265
|
+
"#{name} (role #{spec[:role] || 'none'}): #{spec[:summary]}. Arguments: #{arguments.uniq.join(', ')}"
|
|
266
|
+
rescue Runtime::NotFound
|
|
267
|
+
nil
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
237
271
|
# :nodoc:
|
|
238
272
|
def dry_run_outcome(runtime, spec, envelope, summary:)
|
|
239
273
|
flat = flatten_legacy(envelope, spec[:receiver], spec[:legacy_receiver])
|
|
@@ -946,10 +946,12 @@ module Hecks
|
|
|
946
946
|
$CHILD_STATUS.success? ? out : nil
|
|
947
947
|
end
|
|
948
948
|
|
|
949
|
-
# `.rb` files, plus extensionless scripts with a Ruby shebang.
|
|
949
|
+
# `.rb` files, plus extensionless scripts with a Ruby shebang. The first line is read as
|
|
950
|
+
# bytes, so an extensionless binary (a compiled test fixture's output) is passed over
|
|
951
|
+
# rather than refused for not being UTF-8.
|
|
950
952
|
def ruby_files(dir)
|
|
951
953
|
scripts = Dir[File.join(dir, "**", "*")].select do |path|
|
|
952
|
-
File.file?(path) && File.extname(path).empty? && File.open(path, &:gets).to_s.match?(/\A#!.*ruby/)
|
|
954
|
+
File.file?(path) && File.extname(path).empty? && File.open(path, "rb", &:gets).to_s.match?(/\A#!.*ruby/n)
|
|
953
955
|
end
|
|
954
956
|
Dir[File.join(dir, "**", "*.rb")] + scripts
|
|
955
957
|
end
|
data/lib/hecks/tools/gate.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Hecks
|
|
|
25
25
|
# @param root [String] the checkout the checks run in
|
|
26
26
|
# @return [Integer] 0 when every check passed, 1 when one failed, 2 on bad usage
|
|
27
27
|
def main(argv, root: Tools::ROOT, **)
|
|
28
|
-
stages = YAML.load_file(STAGES_FILE)
|
|
28
|
+
stages = YAML.load_file(STAGES_FILE, aliases: true)
|
|
29
29
|
return list(stages) if argv == ["--list"]
|
|
30
30
|
|
|
31
31
|
name = argv.find { |arg| !arg.include?("=") && !arg.start_with?("--") }
|