hecks 3.0.4 → 3.1.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/exe/hecks +25 -7
- data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +1 -1
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +2 -2
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +59 -32
- data/lib/hecks/bluebook/hexagon.rb +45 -25
- data/lib/hecks/bluebook/model_check.rb +7 -7
- data/lib/hecks/chapters.rb +31 -3
- data/lib/hecks/cli/check_era.rb +1 -1
- data/lib/hecks/cli/console.rb +24 -8
- data/lib/hecks/cli/deploy_template_diff.rb +1 -1
- data/lib/hecks/cli/doc_coverage.rb +1 -1
- data/lib/hecks/cli/domain_stub.rb +128 -0
- data/lib/hecks/cli/interview_draft.rb +246 -0
- data/lib/hecks/cli/model_check.rb +1 -1
- data/lib/hecks/cli/project_cli.rb +69 -18
- data/lib/hecks/cli/reference.rb +2 -1
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +1 -1
- data/lib/hecks/cli/seed_semantics_corpus.rb +38 -9
- data/lib/hecks/cli/spec_example.rb +1 -1
- data/lib/hecks/cli/stress_concurrency_specs.rb +14 -5
- data/lib/hecks/corpus.rb +11 -8
- data/lib/hecks/deploy/bluebook/deploy.bluebook +85 -12
- data/lib/hecks/deploy/oidc.json +5 -0
- data/lib/hecks/doc/reference.rb +4 -3
- data/lib/hecks/doors/cli_door.rb +13 -13
- data/lib/hecks/doors/cli_runner.rb +52 -32
- data/lib/hecks/doors/launcher_options.rb +23 -5
- data/lib/hecks/doors/mcp_door.rb +40 -18
- data/lib/hecks/doors/mcp_door_scope.rb +244 -24
- data/lib/hecks/doors.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +2 -2
- data/lib/hecks/forms/banking_presentation.rb +1 -1
- data/lib/hecks/framework/bluebook/compliance.bluebook +1 -1
- data/lib/hecks/framework/bluebook/console_settings.bluebook +1 -1
- data/lib/hecks/framework/bluebook/framework.hecksagon +4 -4
- data/lib/hecks/framework.rb +1 -1
- data/lib/hecks/fuzzing/combination_miner.rb +1 -1
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +1 -1
- data/lib/hecks/fuzzing/differential.rb +4 -2
- data/lib/hecks/fuzzing/era_boundary.rb +1 -1
- data/lib/hecks/fuzzing/form_census.rb +2 -2
- data/lib/hecks/fuzzing/generated_domain_check.rb +1 -1
- data/lib/hecks/fuzzing/isolated_boot.rb +10 -3
- data/lib/hecks/fuzzing/persistence_parity.rb +2 -2
- data/lib/hecks/fuzzing/target_capabilities.rb +3 -3
- data/lib/hecks/gate/stages.yml +8 -4
- data/lib/hecks/grammar/grammar.hecksagon +2 -2
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +4 -4
- data/lib/hecks/hecks/adapters/host_http.rb +4 -3
- data/lib/hecks/hecks/adapters/local_files.rb +59 -0
- data/lib/hecks/hecks/adapters/site_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +54 -0
- data/lib/hecks/hecks/codebase.bluebook +4 -3
- data/lib/hecks/hecks/context_map.hecksagon +2 -2
- data/lib/hecks/hecks/custodian.bluebook +45 -3
- data/lib/hecks/hecks/hecks.hecksagon +10 -4
- data/lib/hecks/hecks/hecks.world +7 -4
- data/lib/hecks/hecks/site.behaviors +17 -0
- data/lib/hecks/hecks/site.world +10 -0
- data/lib/hecks/language/bluebook/bluebook.hecksagon +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +24 -47
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +4 -4
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +2 -1
- data/lib/hecks/projections/deploy/box/settings.rb +248 -0
- data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +267 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +70 -0
- data/lib/hecks/projections/deploy/box/templates/fetch-secrets.sh +21 -0
- data/lib/hecks/projections/deploy/box/templates/rds.yaml.tmpl +165 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +43 -0
- data/lib/hecks/projections/deploy/box.rb +387 -0
- data/lib/hecks/projections/deploy/fargate/cdn.rb +13 -1
- data/lib/hecks/projections/deploy/fargate.rb +1 -1
- data/lib/hecks/projections/deploy/lambda.rb +11 -10
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +1 -1
- data/lib/hecks/projections/diagrams.rb +2 -2
- data/lib/hecks/projections/site/edge/behaviours.rb +107 -0
- data/lib/hecks/projections/site/edge/checks.rb +137 -0
- data/lib/hecks/projections/site/edge/pattern.rb +50 -0
- data/lib/hecks/projections/site/edge/rules.rb +128 -0
- data/lib/hecks/projections/site/edge.rb +136 -0
- data/lib/hecks/projections/site/regions.rb +44 -0
- data/lib/hecks/projections/site/routes_ts.rb +284 -0
- data/lib/hecks/projections/site/site_cdn.rb +78 -0
- data/lib/hecks/projections/site/table/checks.rb +124 -0
- data/lib/hecks/projections/site/table/row_builder.rb +155 -0
- data/lib/hecks/projections/site/table.rb +98 -0
- data/lib/hecks/projections.rb +2 -0
- data/lib/hecks/projector/cli_projector.rb +172 -95
- data/lib/hecks/quality_control/adapters/agent.adapter +1 -1
- data/lib/hecks/quality_control/adapters/agent.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +8 -8
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +4 -4
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +6 -5
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +5 -5
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep.rb +10 -9
- data/lib/hecks/quality_control/cli/qa_tick.rb +3 -3
- data/lib/hecks/quality_control/quality_control.bluebook +18 -18
- data/lib/hecks/quality_control/quality_control.hecksagon +1 -1
- data/lib/hecks/runtime/command_interpreter.rb +2 -0
- data/lib/hecks/runtime/interpreting.rb +21 -0
- data/lib/hecks/runtime/invocation.rb +5 -3
- data/lib/hecks/runtime/registry/verification.rb +15 -29
- data/lib/hecks/runtime/registry.rb +14 -20
- data/lib/hecks/rust_build/append_optionals.rb +79 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +2 -1
- data/lib/hecks/rust_build/domain_name.rb +26 -0
- data/lib/hecks/rust_build/kernel_input.rb +58 -0
- data/lib/hecks/rust_build/project_rust/codegen_run.rb +143 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +1 -1
- data/lib/hecks/rust_build/project_rust.rb +57 -24
- data/lib/hecks/rust_build/rust_literal.rb +33 -0
- data/lib/hecks/rust_build/write_if_changed.rb +86 -0
- data/lib/hecks/site/bluebook/site.bluebook +205 -0
- data/lib/hecks/site/bluebook/site.hecksagon +5 -0
- data/lib/hecks/site/bluebook/site.ports.hecksagon +15 -0
- data/lib/hecks/sme/bluebook/sme.bluebook +336 -0
- data/lib/hecks/sme/bluebook/sme.hecksagon +5 -0
- data/lib/hecks/storehouse.rb +37 -7
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +1 -1
- data/lib/hecks/three_zero/forms.yml +88 -88
- data/lib/hecks/tools/ci_gates.rb +1 -1
- data/lib/hecks/tools/deploy_recipe.rb +14 -6
- data/lib/hecks/tools/oidc_manifests.rb +2 -1
- data/lib/hecks/tools/sequence_script.rb +2 -2
- data/lib/hecks/tools/site_routes.rb +152 -0
- data/lib/hecks/tools/tenant_provisioning.rb +1 -1
- data/lib/hecks/tools.rb +1 -0
- data/lib/hecks/version.rb +1 -1
- data/rust/build/src/cargo_sync.rs +1 -1
- data/rust/build/src/pipeline.rs +23 -16
- data/rust/build/src/resolve.rs +39 -6
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +1 -1
- data/rust/host/src/dispatch.rs +87 -7
- data/rust/host/src/needs.rs +111 -0
- data/rust/host/src/web/newsletter/template.rs +69 -0
- data/rust/host/src/web/newsletter/tests.rs +43 -0
- data/rust/host/src/web/newsletter.rs +6 -1
- data/rust/host/src/web/newsletter_send.rs +1 -1
- data/rust/parser/src/keywords.rs +7 -6
- data/rust/parser/src/main.rs +4 -3
- data/rust/parser/src/parse/chapter.rs +1 -1
- data/rust/parser/src/parse/hecksagon.rs +25 -10
- data/rust/project/mutations.rb +4 -30
- data/rust/project/naming.rb +8 -28
- data/rust/project/write_if_changed.rb +4 -80
- data/rust/project_rust_pipeline.rb +16 -12
- data/rust/src/kernel/cli.rs +2 -0
- data/rust/src/kernel/needs.rs +74 -1
- metadata +39 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 29694d3639f1ad69a3d0a81bf4e6494c5a6d60a88ca9897af3a01224944d654e
|
|
4
|
+
data.tar.gz: b9fb1376266fe98b2d09c6127bbbbe3a954c11e9441c3f01976e54f44b63eaf7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6eea28ed2426948aeca13df88967f06a5d64acf29dbb376a16b53d82367e90383f3646928ee4940ff9d93969ea7721ed29fb4467096eb8a2b88bc14f7f7ede47
|
|
7
|
+
data.tar.gz: 52d5f117ce1b1bae946f939dad3c73567a44c5c466257c37cbaf5bb7bcecb0cb3a3ea92e9c41b3edde78efbdb674669d08f56305287f46313c1cb4ce813d3042
|
data/exe/hecks
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
# Hecks's command line — GENERATED by hecks project_cli. Do not edit.
|
|
4
4
|
#
|
|
5
|
-
# A launcher, not a CLI: the
|
|
5
|
+
# A launcher, not a CLI: the commands, their arguments and their refusals are
|
|
6
6
|
# projected from the bluebook beside it every time this runs, so a change
|
|
7
7
|
# to the chapter shows up here without re-minting anything.
|
|
8
8
|
#
|
|
9
|
-
# exe/hecks every
|
|
10
|
-
# exe/hecks <
|
|
9
|
+
# exe/hecks every command, and every question
|
|
10
|
+
# exe/hecks <command> --help what it wants, and how it refuses
|
|
11
11
|
|
|
12
12
|
Encoding.default_external = Encoding::UTF_8
|
|
13
13
|
Encoding.default_internal = Encoding::UTF_8
|
|
@@ -21,10 +21,17 @@ if LEGACY.include?(ARGV.first)
|
|
|
21
21
|
exit Hecks::CLI.start(ARGV) unless Hecks::CLI.launcher_form?(ARGV)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
# These commands keep nothing worth a database, so they run on Memory unless told otherwise.
|
|
25
|
+
# A person is at them: they wait for their result, say why when they are refused, and print
|
|
26
|
+
# no record when they end well.
|
|
27
|
+
MEMORY_COMMANDS = %w[console init].freeze
|
|
28
|
+
ENV["HECKS_ENVIRONMENT"] ||= "memory" if MEMORY_COMMANDS.include?(ARGV.first.to_s.chomp("!"))
|
|
29
|
+
ARGV << "--wait" if MEMORY_COMMANDS.include?(ARGV.first.to_s.chomp("!")) && !ARGV.include?("--wait")
|
|
30
|
+
|
|
24
31
|
require "hecks"
|
|
25
32
|
|
|
26
33
|
# Usage is answered from the projected chapter alone: no adapter is bound and no
|
|
27
|
-
# database is opened. Only a line that runs a
|
|
34
|
+
# database is opened. Only a line that runs a command boots the domain,
|
|
28
35
|
# and it boots from what the usage check already loaded.
|
|
29
36
|
domain = File.expand_path("../lib/hecks/hecks", __dir__)
|
|
30
37
|
program = "hecks"
|
|
@@ -39,7 +46,7 @@ text, status, reason = Hecks::Doors::CliRunner.usage(
|
|
|
39
46
|
)
|
|
40
47
|
unless text
|
|
41
48
|
runtime = begin
|
|
42
|
-
Hecks.boot_described(described, install_doors: false)
|
|
49
|
+
Hecks::Doors::LauncherOptions.quietly(hold: MEMORY_COMMANDS.include?(ARGV.first.to_s.chomp("!"))) { Hecks.boot_described(described, install_doors: false) }
|
|
43
50
|
rescue StandardError => e
|
|
44
51
|
abort "cannot open Hecks: #{e.message.lines.first.strip}"
|
|
45
52
|
end
|
|
@@ -53,8 +60,19 @@ end
|
|
|
53
60
|
# A `--wait` run that failed answers its settled record on stdout, so `| jq` reads it
|
|
54
61
|
# either way, and says why on stderr.
|
|
55
62
|
if reason
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
if MEMORY_COMMANDS.include?(ARGV.first.to_s.chomp("!"))
|
|
64
|
+
why = begin
|
|
65
|
+
require "json"
|
|
66
|
+
JSON.parse(text).dig("state", "refusal", "value")
|
|
67
|
+
rescue JSON::ParserError
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
warn(why ? why.sub(/\A(\w+::)+\w+: /, "") : reason)
|
|
71
|
+
else
|
|
72
|
+
puts text
|
|
73
|
+
warn reason
|
|
74
|
+
end
|
|
58
75
|
exit status
|
|
59
76
|
end
|
|
77
|
+
exit 0 if status.zero? && MEMORY_COMMANDS.include?(ARGV.first.to_s.chomp("!"))
|
|
60
78
|
status.zero? ? puts(text) : abort(text)
|
|
@@ -7,7 +7,7 @@ module Hecks
|
|
|
7
7
|
# Runtime::RemoteDispatcher's writes: one Lambda invoke, one JSON round trip.
|
|
8
8
|
#
|
|
9
9
|
# The function name defaults to `"hecks-#{domain.downcase}"`, matching
|
|
10
|
-
# `hecks deploy project`'s `stack_name` computation. A domain whose stack
|
|
10
|
+
# `hecks deploy recipe.project`'s `stack_name` computation. A domain whose stack
|
|
11
11
|
# name doesn't follow that pattern must pass `function` explicitly, via
|
|
12
12
|
# a `.world`'s `persisted_by("Lambda")`/`dispatched_by("Lambda")` block.
|
|
13
13
|
class Client
|
|
@@ -38,7 +38,7 @@ module Hecks
|
|
|
38
38
|
function = setting(settings, :function, nil)
|
|
39
39
|
# `domain` only prefixes the instances lookup; the function to call is named by
|
|
40
40
|
# DOMAIN_NAME (set in a deployed Lambda, where root is always /var/task), else by
|
|
41
|
-
# root's basename, which matches hecks deploy project's stack name on a local boot.
|
|
41
|
+
# root's basename, which matches hecks deploy recipe.project's stack name on a local boot.
|
|
42
42
|
function_domain = ENV["DOMAIN_NAME"] || (root ? File.basename(root) : domain)
|
|
43
43
|
@client = Client.new(domain: function_domain, region: region, function: function)
|
|
44
44
|
@prefix = "#{domain}::#{aggregate.hecks_name}#"
|
|
@@ -5,8 +5,8 @@ require "fileutils"
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Adapters
|
|
7
7
|
# The imperative half of tenant provisioning, behind `Deploy::Tenant.port
|
|
8
|
-
# "TenantProvisioning"`, for `Tenant.Provision` and for hecks deploy provision; booting
|
|
9
|
-
# domain and its capability gate stay in hecks deploy provision.
|
|
8
|
+
# "TenantProvisioning"`, for `Tenant.Provision` and for hecks deploy tenant.provision; booting
|
|
9
|
+
# the domain and its capability gate stay in hecks deploy tenant.provision.
|
|
10
10
|
class TenantProvisioner
|
|
11
11
|
# Raised when a value is outside its allow-list or the overlay path leaves `environments/`.
|
|
12
12
|
class Refused < ArgumentError; end
|
|
@@ -3,7 +3,7 @@ module Hecks
|
|
|
3
3
|
module Bluebook
|
|
4
4
|
module DSL
|
|
5
5
|
# Parses a `.hecksagon` file's DSL block into a Hecksagon: a domain's adapter bindings,
|
|
6
|
-
#
|
|
6
|
+
# attachments and subscriptions — wiring, not part of the bluebook.
|
|
7
7
|
class HecksagonBuilder
|
|
8
8
|
GRAMMAR_CONTEXT = "Hecksagon".freeze
|
|
9
9
|
|
|
@@ -57,20 +57,22 @@ module Hecks
|
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
# The release that drops the deprecated spellings of `attaches`; kept in step with
|
|
61
|
+
# `Hecks::Doors::REMOVAL`, which this file cannot require.
|
|
62
|
+
REMOVAL = "3.2.0".freeze
|
|
63
|
+
|
|
64
|
+
attr_reader :binds, :subscriptions, :attachments
|
|
61
65
|
|
|
62
66
|
def initialize(domain)
|
|
63
67
|
@domain = domain
|
|
64
68
|
@binds = []
|
|
65
69
|
@subscriptions = []
|
|
66
|
-
@
|
|
67
|
-
@vendored_bluebooks = []
|
|
68
|
-
@attached_chapters = []
|
|
70
|
+
@attachments = []
|
|
69
71
|
@bounded = false
|
|
70
72
|
@translates = []
|
|
71
73
|
end
|
|
72
74
|
|
|
73
|
-
# Marks this chapter as a bounded context;
|
|
75
|
+
# Marks this chapter as a bounded context; an attachment marks its chapter bounded too.
|
|
74
76
|
# Must declare at least one `translates` ACL or boot refuses.
|
|
75
77
|
def bounded
|
|
76
78
|
@bounded = true
|
|
@@ -79,35 +81,37 @@ module Hecks
|
|
|
79
81
|
# Subscribes to an event from outside this domain's own bluebook.
|
|
80
82
|
def subscribe(event) = @subscriptions << event.to_s
|
|
81
83
|
|
|
82
|
-
# Attaches a
|
|
84
|
+
# Attaches a chapter by name and loads it into the current registry, marked bounded.
|
|
85
|
+
#
|
|
86
|
+
# Without `from:` the name is a chapter the gem carries (a framework member such as
|
|
87
|
+
# Governance, or a language chapter such as Deploy) with the ports and adapters it
|
|
88
|
+
# ships. `from: :vendor` loads `vendor/embryonaut_bluebooks/<name>/bluebook` instead,
|
|
89
|
+
# and is never a fallback for a misspelt gem name.
|
|
90
|
+
#
|
|
91
|
+
# @param name [String, Symbol] a gem chapter's name or a vendored package's directory name
|
|
92
|
+
# @param from [Symbol, nil] `:vendor` for a vendored package, nil for a gem chapter
|
|
93
|
+
# @raise [Runtime::WiringError] if the name is unknown, or `from:` names another source
|
|
94
|
+
def attaches(name, from: nil)
|
|
95
|
+
unless [nil, :vendor].include?(from)
|
|
96
|
+
raise Runtime::WiringError,
|
|
97
|
+
"attaches #{name.to_s.inspect}, from: #{from.inspect} — `from:` takes only :vendor"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
from == :vendor ? attach_vendored(name) : attach_gem(name)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# The deprecated spelling of `attaches "Name"` for a framework member.
|
|
83
104
|
def uses_framework(name)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
# anti-corruption layer.
|
|
105
|
+
deprecated_word("uses_framework", "attaches #{name.to_s.inspect}")
|
|
106
|
+
@attachments << Attachment.new(name: name.to_s, source: :gem)
|
|
107
|
+
self.class.with_real_modules { Hecks::Framework.load!(name) }
|
|
88
108
|
Hecks.current_registry&.mark_bounded(name.to_s)
|
|
89
109
|
end
|
|
90
110
|
|
|
91
|
-
#
|
|
92
|
-
# registry. Recorded separately from @framework_members, which is load-bearing for
|
|
93
|
-
# governance checks.
|
|
111
|
+
# The deprecated spelling of `attaches "name", from: :vendor`.
|
|
94
112
|
def uses_embryonaut_bluebook(name)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
# Marks the chapter bounded, same as `uses_framework`; the directory name Pascal-cases
|
|
98
|
-
# to the chapter name (`"membership"` → `Membership`).
|
|
99
|
-
Hecks.current_registry&.mark_bounded(Hecks::Naming.pascal(name.to_s))
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# Attaches a chapter the gem carries (the language, Expression, Tenancy, Deploy,
|
|
103
|
-
# QualityControl) by name and loads its files into the registry, with the ports and
|
|
104
|
-
# adapters it ships. Marked bounded like a framework member, since this hecksagon is its
|
|
105
|
-
# anti-corruption layer.
|
|
106
|
-
def attaches(name)
|
|
107
|
-
require_relative "../../chapters"
|
|
108
|
-
@attached_chapters << name.to_s
|
|
109
|
-
self.class.with_real_modules { Hecks::Chapters.load!(name) }
|
|
110
|
-
Hecks.current_registry&.mark_bounded(name.to_s)
|
|
113
|
+
deprecated_word("uses_embryonaut_bluebook", "attaches #{name.to_s.inspect}, from: :vendor")
|
|
114
|
+
attach_vendored(name)
|
|
111
115
|
end
|
|
112
116
|
|
|
113
117
|
# Declares a port at the hecksagon's root — the chapter as a whole, not one aggregate.
|
|
@@ -145,9 +149,32 @@ module Hecks
|
|
|
145
149
|
# No ungoverned-role check here — it runs once on the merged result at verify! time.
|
|
146
150
|
def build
|
|
147
151
|
Hecksagon.new(domain: @domain, binds: @binds, subscriptions: @subscriptions,
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
attachments: @attachments, bounded: @bounded, translates: @translates)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Loads a chapter the gem carries and records it as attached from the gem.
|
|
156
|
+
def attach_gem(name)
|
|
157
|
+
require_relative "../../chapters"
|
|
158
|
+
@attachments << Attachment.new(name: name.to_s, source: :gem)
|
|
159
|
+
self.class.with_real_modules { Hecks::Chapters.attach!(name) }
|
|
160
|
+
Hecks.current_registry&.mark_bounded(name.to_s)
|
|
161
|
+
end
|
|
162
|
+
private :attach_gem
|
|
163
|
+
|
|
164
|
+
# Loads a vendored package and records it as attached from `vendor/`. The directory name
|
|
165
|
+
# Pascal-cases to the chapter name (`"membership"` becomes `Membership`).
|
|
166
|
+
def attach_vendored(name)
|
|
167
|
+
@attachments << Attachment.new(name: name.to_s, source: :vendor)
|
|
168
|
+
Hecks::EmbryonautBluebook.load!(name)
|
|
169
|
+
Hecks.current_registry&.mark_bounded(Hecks::Naming.pascal(name.to_s))
|
|
170
|
+
end
|
|
171
|
+
private :attach_vendored
|
|
172
|
+
|
|
173
|
+
# Tells the author that a word is going away.
|
|
174
|
+
def deprecated_word(word, instead)
|
|
175
|
+
warn "[hecks] `#{word}` is deprecated and is removed in #{REMOVAL}; use `#{instead}`"
|
|
150
176
|
end
|
|
177
|
+
private :deprecated_word
|
|
151
178
|
|
|
152
179
|
# Records any verb the grammar doesn't own as a domain-wide default bind, e.g. bare
|
|
153
180
|
# `persisted_by "Heki"` at the top of a block, applied unless an aggregate overrides it.
|
|
@@ -20,6 +20,23 @@ module Hecks
|
|
|
20
20
|
def aggregate_name = Naming.demodulise(aggregate)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# One chapter a hecksagon attaches, with where it was found.
|
|
24
|
+
#
|
|
25
|
+
# `source` is `:gem` for a chapter the gem carries (a framework member such as Governance,
|
|
26
|
+
# or a language chapter such as Deploy) and `:vendor` for a package vendored into the
|
|
27
|
+
# consuming project, whose `name` is the package's directory name (`"membership"`).
|
|
28
|
+
Attachment = Struct.new(:name, :source, keyword_init: true) do
|
|
29
|
+
# @return [Boolean] whether the chapter is a vendored package
|
|
30
|
+
def vendor? = source == :vendor
|
|
31
|
+
|
|
32
|
+
# @return [String] the chapter's name in the registry: a gem chapter's own name, a
|
|
33
|
+
# vendored package's directory name in Pascal case
|
|
34
|
+
def chapter_name = vendor? ? Naming.pascal(name) : name
|
|
35
|
+
|
|
36
|
+
# @return [Hash{Symbol => String}] the attachment as plain data
|
|
37
|
+
def to_h = { name: name.to_s, source: source.to_s }
|
|
38
|
+
end
|
|
39
|
+
|
|
23
40
|
# The built form of a `.hecksagon` file, produced by `DSL::HecksagonBuilder`.
|
|
24
41
|
# `Behaviour::Hecksagon` supplies the bind lookups; this class holds the declared data.
|
|
25
42
|
class Hecksagon
|
|
@@ -27,39 +44,31 @@ module Hecks
|
|
|
27
44
|
include Behaviour::Hecksagon
|
|
28
45
|
|
|
29
46
|
emits_ir(
|
|
30
|
-
domain:
|
|
31
|
-
binds:
|
|
32
|
-
subscriptions:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
bounded: :bounded?,
|
|
37
|
-
translates: -> { translates.map(&:to_s) }
|
|
47
|
+
domain: :domain,
|
|
48
|
+
binds: many(:binds),
|
|
49
|
+
subscriptions: -> { subscriptions.map(&:to_s) },
|
|
50
|
+
attachments: -> { attachments.map(&:to_h) },
|
|
51
|
+
bounded: :bounded?,
|
|
52
|
+
translates: -> { translates.map(&:to_s) }
|
|
38
53
|
)
|
|
39
54
|
|
|
40
|
-
attr_reader :domain, :binds, :subscriptions, :
|
|
41
|
-
:attached_chapters, :translates
|
|
55
|
+
attr_reader :domain, :binds, :subscriptions, :attachments, :translates
|
|
42
56
|
|
|
43
57
|
# @param domain [String, Symbol] the domain this hecksagon wires
|
|
44
58
|
# @param binds [Array<Bluebook::Bind>] the declared adapter binds
|
|
45
59
|
# @param subscriptions [Array<String, Symbol>] the external events this domain
|
|
46
60
|
# subscribes to
|
|
47
|
-
# @param
|
|
48
|
-
#
|
|
49
|
-
# bluebook package names this domain attaches
|
|
50
|
-
# @param attached_chapters [Array<String, Symbol>] chapters the gem carries, attached by name
|
|
61
|
+
# @param attachments [Array<Bluebook::Attachment>] every chapter this hecksagon attaches,
|
|
62
|
+
# each with its source (`:gem` or `:vendor`)
|
|
51
63
|
# @param bounded [Boolean] whether this chapter is an explicit bounded context
|
|
52
|
-
# (consumer-owned; `
|
|
53
|
-
# attached chapters bounded on the registry instead)
|
|
64
|
+
# (consumer-owned; `attaches` marks attached chapters bounded on the registry instead)
|
|
54
65
|
# @param translates [Array<String>] names of `translates` ACL blocks declared here
|
|
55
|
-
def initialize(domain:, binds: [], subscriptions: [],
|
|
56
|
-
|
|
66
|
+
def initialize(domain:, binds: [], subscriptions: [], attachments: [], bounded: false,
|
|
67
|
+
translates: [])
|
|
57
68
|
@domain = domain.to_s
|
|
58
69
|
@binds = binds
|
|
59
70
|
@subscriptions = subscriptions
|
|
60
|
-
@
|
|
61
|
-
@vendored_bluebooks = vendored_bluebooks
|
|
62
|
-
@attached_chapters = attached_chapters
|
|
71
|
+
@attachments = attachments
|
|
63
72
|
@bounded = bounded ? true : false
|
|
64
73
|
@translates = Array(translates).map(&:to_s)
|
|
65
74
|
end
|
|
@@ -69,11 +78,22 @@ module Hecks
|
|
|
69
78
|
# @return [Boolean] whether `bounded` was declared on this block
|
|
70
79
|
def bounded? = @bounded
|
|
71
80
|
|
|
72
|
-
# Every chapter this hecksagon brings into its domain's registry
|
|
73
|
-
#
|
|
81
|
+
# Every chapter this hecksagon brings into its domain's registry, gem and vendored alike.
|
|
82
|
+
#
|
|
83
|
+
# @return [Array<String>] the chapters' names as the registry holds them, in the order
|
|
84
|
+
# they were attached
|
|
85
|
+
def member_chapters = attachments.map(&:chapter_name)
|
|
86
|
+
|
|
87
|
+
# Says whether this hecksagon attaches a chapter, whatever its source.
|
|
88
|
+
#
|
|
89
|
+
# @param chapter [String, Symbol] a chapter's name as the registry holds it
|
|
90
|
+
# @return [Boolean] whether that chapter is attached here
|
|
91
|
+
def attaches?(chapter) = member_chapters.include?(chapter.to_s)
|
|
92
|
+
|
|
93
|
+
# The vendored packages this hecksagon attaches.
|
|
74
94
|
#
|
|
75
|
-
# @return [Array<String>]
|
|
76
|
-
def
|
|
95
|
+
# @return [Array<String>] their directory names, in the order they were attached
|
|
96
|
+
def vendored_packages = attachments.select(&:vendor?).map { |attachment| attachment.name.to_s }
|
|
77
97
|
end
|
|
78
98
|
|
|
79
99
|
# The built form of a `.world` file, produced by `DSL::WorldBuilder`.
|
|
@@ -27,7 +27,7 @@ module Hecks
|
|
|
27
27
|
# @param hecksagon [Bluebook::Hecksagon, nil] this bluebook's own sibling wiring
|
|
28
28
|
# file, if the caller loaded one
|
|
29
29
|
# @param known_domains [Set<String>, nil] every bluebook/hecksagon name booted
|
|
30
|
-
# anywhere in this corpus scan, checked to catch a typo'd across/
|
|
30
|
+
# anywhere in this corpus scan, checked to catch a typo'd across/attaches target
|
|
31
31
|
# @param global_emitted_events [Set<String>, nil] every event emitted anywhere in
|
|
32
32
|
# the corpus, checked as a fallback so a translates reaction isn't flagged deaf
|
|
33
33
|
# @param profile [Symbol, nil] :client adds ClientProfile's error findings
|
|
@@ -371,7 +371,7 @@ module Hecks
|
|
|
371
371
|
findings
|
|
372
372
|
end
|
|
373
373
|
|
|
374
|
-
# `
|
|
374
|
+
# `attaches` is a Shared Kernel relationship (X merges in, no boundary);
|
|
375
375
|
# `across` is Customer/Supplier (real cross-Lambda RPC). This checks the choice
|
|
376
376
|
# between them instead of leaving it as an unenforced comment. (ADR 0025)
|
|
377
377
|
def cross_domain_policy_findings(policy, hecksagon, known_domains)
|
|
@@ -382,13 +382,13 @@ module Hecks
|
|
|
382
382
|
target = policy.target_domain
|
|
383
383
|
findings = []
|
|
384
384
|
|
|
385
|
-
if hecksagon.
|
|
385
|
+
if hecksagon.attaches?(target)
|
|
386
386
|
# Shared Kernel and Customer/Supplier are mutually exclusive claims about
|
|
387
387
|
# the same target — declaring both means either a pointless RPC to a
|
|
388
|
-
# domain already local, or
|
|
388
|
+
# domain already local, or an attaches that isn't doing its job.
|
|
389
389
|
findings << Finding.new(kind: :contradictory_relationship, severity: :error, subject: policy.name,
|
|
390
390
|
message: "across #{target.inspect} dispatches over RPC (Customer/Supplier), " \
|
|
391
|
-
"but this hecksagon also
|
|
391
|
+
"but this hecksagon also attaches #{target.inspect} (Shared " \
|
|
392
392
|
"Kernel) — #{target} is already loaded in-process here, so the two " \
|
|
393
393
|
"relationship declarations contradict each other for the same " \
|
|
394
394
|
"target domain")
|
|
@@ -399,7 +399,7 @@ module Hecks
|
|
|
399
399
|
message: "across #{target.inspect} declares a Customer/Supplier " \
|
|
400
400
|
"relationship, but nothing in this hecksagon records the " \
|
|
401
401
|
"expectation — add subscribe \"#{target}.SomeEvent\" for what " \
|
|
402
|
-
"you expect back from it, or
|
|
402
|
+
"you expect back from it, or attaches #{target.inspect} to " \
|
|
403
403
|
"attach it in-process instead")
|
|
404
404
|
end
|
|
405
405
|
|
|
@@ -426,7 +426,7 @@ module Hecks
|
|
|
426
426
|
reached = []
|
|
427
427
|
reached << "#{target} is a domain this corpus boots" if known_domains&.include?(target)
|
|
428
428
|
if hecksagon
|
|
429
|
-
reached << "this hecksagon
|
|
429
|
+
reached << "this hecksagon attaches #{target.inspect}" if hecksagon.attaches?(target)
|
|
430
430
|
if hecksagon.subscriptions.any? { |subscribed| Naming.qualifier(subscribed) == target }
|
|
431
431
|
reached << "this hecksagon subscribes to #{target}"
|
|
432
432
|
end
|
data/lib/hecks/chapters.rb
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
require_relative "naming"
|
|
2
|
+
require_relative "framework"
|
|
2
3
|
require_relative "runtime/registry"
|
|
3
4
|
require_relative "bluebook/meta_validator"
|
|
4
5
|
|
|
5
6
|
module Hecks
|
|
6
7
|
# The chapters the gem carries that a hecksagon can attach by name (ADR 0080): the language
|
|
7
|
-
# declared in itself, Expression, Tenancy, Deploy and QualityControl. Framework members stay
|
|
8
|
-
# `Framework
|
|
8
|
+
# declared in itself, Expression, Tenancy, Deploy, Site and QualityControl. Framework members stay
|
|
9
|
+
# in `Framework`; `table` and `attach!` find a name across both.
|
|
9
10
|
#
|
|
10
11
|
# A chapter is named by the `Hecks.bluebook "Name"` header of its files, and may span several.
|
|
11
12
|
# Beside its bluebook a chapter may carry what every hecksagon attaching it needs, whatever
|
|
@@ -14,7 +15,7 @@ module Hecks
|
|
|
14
15
|
module Chapters
|
|
15
16
|
# Where attachable chapters live, relative to `lib/hecks/`.
|
|
16
17
|
GLOBS = %w[language/**/*.bluebook grammar/expression.bluebook tenancy/bluebook/*.bluebook
|
|
17
|
-
deploy/bluebook/*.bluebook quality_control/*.bluebook].freeze
|
|
18
|
+
deploy/bluebook/*.bluebook site/bluebook/*.bluebook quality_control/*.bluebook].freeze
|
|
18
19
|
|
|
19
20
|
# Every attachable chapter, by name, with the files that declare it.
|
|
20
21
|
#
|
|
@@ -26,6 +27,33 @@ module Hecks
|
|
|
26
27
|
.freeze
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
# Every chapter the gem carries, whichever module loads it: the one lookup table `attaches`
|
|
31
|
+
# finds a name in.
|
|
32
|
+
#
|
|
33
|
+
# @return [Hash{String => Array<String>}] chapter name to its files' absolute paths, the
|
|
34
|
+
# framework members (`Framework.members`) and the attachable chapters (`index`) together
|
|
35
|
+
def self.table
|
|
36
|
+
Framework.members.transform_values { |path| [path] }.merge(index)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Loads one gem chapter by name, found in `table`: a framework member through
|
|
40
|
+
# `Framework.load!`, any other chapter through `load!`.
|
|
41
|
+
#
|
|
42
|
+
# @param name [String, Symbol] the chapter's name, such as `"Governance"` or `"Deploy"`
|
|
43
|
+
# @return [Boolean, nil] true when this call loaded it, nil when it was already held
|
|
44
|
+
# @raise [Runtime::WiringError] if the gem carries no chapter of that name; the message
|
|
45
|
+
# lists the known names and says how to attach a vendored package
|
|
46
|
+
def self.attach!(name)
|
|
47
|
+
key = name.to_s
|
|
48
|
+
return Framework.load!(key) if Framework.members.key?(key)
|
|
49
|
+
return load!(key) if index.key?(key)
|
|
50
|
+
|
|
51
|
+
raise Runtime::WiringError,
|
|
52
|
+
"attaches #{name.inspect}: the gem carries no chapter of that name — known: " \
|
|
53
|
+
"#{table.keys.sort.join(', ')}. To attach a vendored package, write " \
|
|
54
|
+
"`attaches #{name.to_s.inspect}, from: :vendor`"
|
|
55
|
+
end
|
|
56
|
+
|
|
29
57
|
# Loads a chapter's files into the current registry, unless it already holds the chapter.
|
|
30
58
|
#
|
|
31
59
|
# The files load together inside `MetaValidator.defer`, so a chapter spread over several files
|
data/lib/hecks/cli/check_era.rb
CHANGED
|
@@ -28,7 +28,7 @@ module Hecks
|
|
|
28
28
|
# @raise [Errno::ENOENT, Errno::EACCES] if the file cannot be read
|
|
29
29
|
# @raise [Runtime::EraCheck::ExpectedEra::Unreachable] if the host cannot be reached
|
|
30
30
|
# @raise [Runtime::EraCheck::ExpectedEra::BadResponse] if it answers no era
|
|
31
|
-
def assess(url, file, timeout:
|
|
31
|
+
def assess(url, file, timeout:)
|
|
32
32
|
expected = Runtime::EraCheck::ExpectedEra
|
|
33
33
|
allowed = expected.parse(File.read(file))
|
|
34
34
|
document = expected.fetch_version(url, timeout: timeout)
|
data/lib/hecks/cli/console.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Hecks
|
|
|
9
9
|
# The command behind `hecks console` and Custodian's `Operation.OpenConsole`: boots a domain
|
|
10
10
|
# (pizzas on Memory by default) and drops into IRB with its door installed, for dispatching a
|
|
11
11
|
# real command by hand. No argument needs no database (ADR 0073); a domain directory boots as
|
|
12
|
-
# it is wired, which may bind PostgresEra.
|
|
12
|
+
# it is wired, which may bind PostgresEra (examples/directory does).
|
|
13
13
|
module Console
|
|
14
14
|
PIZZAS = File.expand_path("../../../examples/pizzas", __dir__)
|
|
15
15
|
|
|
@@ -25,14 +25,27 @@ module Hecks
|
|
|
25
25
|
# @param domain [String, nil] a domain directory; the bundled pizzas domain when nil
|
|
26
26
|
# @param launcher [#call] starts the interactive session; IRB unless a caller stands in
|
|
27
27
|
# @return [Runtime] the runtime the session ran against
|
|
28
|
-
def call(domain = nil, launcher: -> {
|
|
28
|
+
def call(domain = nil, launcher: -> { irb_session })
|
|
29
29
|
runtime = domain ? Hecks.boot(domain) : Hecks.boot_files(DEFAULT_FILES)
|
|
30
30
|
puts overview(runtime)
|
|
31
|
-
puts banner
|
|
31
|
+
puts banner(domain)
|
|
32
32
|
launcher.call
|
|
33
33
|
runtime
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Starts IRB with an empty command line. IRB reads `ARGV` for a script to run, and a
|
|
37
|
+
# launcher's `ARGV` holds its own words (`console`, `subject=<domain>`), which IRB would
|
|
38
|
+
# open as a file and then exit without a prompt.
|
|
39
|
+
#
|
|
40
|
+
# @return [void] when the session ends; `ARGV` is restored
|
|
41
|
+
def irb_session
|
|
42
|
+
words = ARGV.dup
|
|
43
|
+
ARGV.clear
|
|
44
|
+
IRB.start(__FILE__)
|
|
45
|
+
ensure
|
|
46
|
+
ARGV.replace(words)
|
|
47
|
+
end
|
|
48
|
+
|
|
36
49
|
# @param runtime [Runtime] a booted domain
|
|
37
50
|
# @return [String] each chapter's vision and its aggregates' commands
|
|
38
51
|
def overview(runtime)
|
|
@@ -46,16 +59,19 @@ module Hecks
|
|
|
46
59
|
end.join
|
|
47
60
|
end
|
|
48
61
|
|
|
49
|
-
# @
|
|
50
|
-
|
|
62
|
+
# @param domain [String, nil] a domain directory, or nil for the bundled pizzas domain
|
|
63
|
+
# @return [String] the version, and for the pizzas domain a first session to try
|
|
64
|
+
def banner(domain = nil)
|
|
65
|
+
return "\nhecks #{Hecks::VERSION}\n\n" if domain
|
|
66
|
+
|
|
51
67
|
<<~BANNER
|
|
52
68
|
|
|
53
69
|
hecks #{Hecks::VERSION}
|
|
54
70
|
|
|
55
71
|
try:
|
|
56
|
-
order = Order.create_pizza!(name:
|
|
57
|
-
order.add_topping!(topping:
|
|
58
|
-
order.purchase!(customer_name:
|
|
72
|
+
order = Order.create_pizza!(name: "Margherita", pizza: { price_cents: { cents: 1200 }, size: "large" })
|
|
73
|
+
order.add_topping!(topping: "Basil", amount: 3)
|
|
74
|
+
order.purchase!(customer_name: "Chris", amount: { cents: 1200 })
|
|
59
75
|
order.status
|
|
60
76
|
order.events.last
|
|
61
77
|
|
|
@@ -5,7 +5,7 @@ require_relative "../projections/deploy/template_diff"
|
|
|
5
5
|
|
|
6
6
|
module Hecks
|
|
7
7
|
module CLI
|
|
8
|
-
# The command behind `hecks deploy diff
|
|
8
|
+
# The command behind `hecks deploy template_comparison.diff`: compares two
|
|
9
9
|
# CloudFormation templates offline and reports differences by logical id; `--strict` also
|
|
10
10
|
# counts cosmetic differences (comments, key order).
|
|
11
11
|
module DeployTemplateDiff
|
|
@@ -24,7 +24,7 @@ module Hecks
|
|
|
24
24
|
return 0 if clean
|
|
25
25
|
|
|
26
26
|
err.puts "docs/implemented/reference/ is behind the language. " \
|
|
27
|
-
"Regenerate with hecks project_reference to see the words in place."
|
|
27
|
+
"Regenerate with hecks language_run.project_reference to see the words in place."
|
|
28
28
|
1
|
|
29
29
|
end
|
|
30
30
|
end
|