hecks 3.2.1 → 3.4.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 +21 -23
- 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/interview_draft.rb +88 -33
- data/lib/hecks/cli/interview_session.rb +77 -26
- data/lib/hecks/cli/interview_shape.rb +198 -0
- data/lib/hecks/corpus.rb +3 -5
- data/lib/hecks/doors/cli_runner.rb +14 -1
- data/lib/hecks/doors/launcher_options.rb +13 -3
- data/lib/hecks/doors/mcp_door_scope.rb +2 -1
- data/lib/hecks/doors.rb +0 -17
- 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/isolated_boot.rb +1 -4
- data/lib/hecks/fuzzing/qa_settings.rb +15 -34
- data/lib/hecks/fuzzing/target_capabilities.rb +1 -1
- data/lib/hecks/gate/stages.yml +113 -8
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +10 -1
- 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/site_toolchain.rb +67 -0
- 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 +213 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +0 -5
- data/lib/hecks/language/oidc.json +5 -0
- 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 +17 -4
- 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/smoke-after-deploy.sh.tmpl +271 -0
- data/lib/hecks/projections/deploy/box.rb +15 -2
- 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/live_edge.rb +162 -0
- 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/loader.rb +6 -7
- 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/runtime.rb +6 -11
- data/lib/hecks/rust_build/host.rb +147 -0
- data/lib/hecks/rust_build/kernel_input.rb +40 -18
- data/lib/hecks/rust_build/project_rust/codegen_run.rb +0 -18
- data/lib/hecks/rust_build/project_rust.rb +3 -10
- data/lib/hecks/rust_build.rb +1 -0
- data/lib/hecks/site/bluebook/site.bluebook +53 -1
- data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
- data/lib/hecks/sme/bluebook/sme.bluebook +128 -1
- data/lib/hecks/tools/gate.rb +1 -1
- data/lib/hecks/tools/rust_comment_style.rb +1 -3
- 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 +151 -0
- data/lib/hecks.rb +6 -12
- data/qa/settings.yml +1 -2
- data/rust/build/src/lineage_pass.rs +30 -15
- data/rust/build/src/main.rs +4 -2
- data/rust/build/src/pipeline.rs +8 -19
- data/rust/codegen/src/bridging.rs +7 -7
- data/rust/codegen/src/json.rs +214 -23
- data/rust/codegen/src/literal.rs +6 -3
- data/rust/codegen/src/main.rs +20 -9
- data/rust/codegen/src/naming.rs +4 -2
- data/rust/codegen/src/optional_pass.rs +226 -0
- data/rust/{build → codegen}/src/sidecars.rs +9 -5
- 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 +99 -2
- 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 -4
- data/rust/parser/src/lex.rs +1 -1
- data/rust/parser/src/main.rs +1 -0
- data/rust/parser/src/parse/command.rs +2 -44
- data/rust/parser/src/parse/hecksagon.rs +15 -24
- 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/kernel/cli.rs +16 -6
- data/rust/src/kernel/needs.rs +104 -9
- metadata +22 -9
- data/lib/hecks/facade.rb +0 -31
- 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
|
@@ -60,6 +60,55 @@ module Hecks
|
|
|
60
60
|
{ report: { value: out.string } }
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
# Checks a project's vendored packages against their `bluebook.lock` files and describes
|
|
64
|
+
# them as the manifest a client image carries, through `Hecks::EmbryonautBluebook::Manifest`.
|
|
65
|
+
#
|
|
66
|
+
# The manifest records the project's own commit and whether its tree has uncommitted changes,
|
|
67
|
+
# read from the repository the project stands in (`unknown` outside one).
|
|
68
|
+
#
|
|
69
|
+
# @param held [Hash] the `Package` query's arguments: `root` (the project; the current
|
|
70
|
+
# directory when absent)
|
|
71
|
+
# @return [Hash{Symbol => String}] `text:` the manifest as JSON
|
|
72
|
+
# @raise [Runtime::NotFound] naming each package whose files disagree with its lock
|
|
73
|
+
def verify(**held)
|
|
74
|
+
root = File.expand_path(plain(held[:root]) || Dir.pwd)
|
|
75
|
+
raise Runtime::NotFound, "#{root} is not a directory" unless File.directory?(root)
|
|
76
|
+
|
|
77
|
+
{ text: EmbryonautBluebook::Manifest.new(root, built_from: built_from(root)).to_json_text }
|
|
78
|
+
rescue EmbryonautBluebook::Manifest::Mismatch => e
|
|
79
|
+
raise Runtime::NotFound, e.message
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Judges every package of a bluebook registry against its latest release, through
|
|
83
|
+
# `Hecks::EmbryonautBluebook::Registry#check`.
|
|
84
|
+
#
|
|
85
|
+
# @param held [Hash] the `Registry` query's arguments: `root` (the registry; the current
|
|
86
|
+
# directory when absent)
|
|
87
|
+
# @return [Hash{Symbol => String}] `text:` the notes and `versions ok`
|
|
88
|
+
# @raise [Runtime::NotFound] naming each package at fault, or when `root` is no repository
|
|
89
|
+
def check(**held)
|
|
90
|
+
report = registry(held).check
|
|
91
|
+
raise Runtime::NotFound, report.to_s unless report.ok?
|
|
92
|
+
|
|
93
|
+
{ text: report.to_s }
|
|
94
|
+
rescue Vendoring::Error => e
|
|
95
|
+
raise Runtime::NotFound, e.message
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Tags a package of a bluebook registry at the version its `bluebook.yml` carries, through
|
|
99
|
+
# `Hecks::EmbryonautBluebook::Registry#release`. The tag is made in the local repository and
|
|
100
|
+
# never pushed.
|
|
101
|
+
#
|
|
102
|
+
# @param held [Hash] the `Registry` record: `package`, and `root` (the current directory when
|
|
103
|
+
# absent)
|
|
104
|
+
# @return [Hash{Symbol => Hash}] `report:` the tag made and the command that publishes it
|
|
105
|
+
# @raise [ConsoleCapture::Failure] when the package breaks a release rule
|
|
106
|
+
def tag(**held)
|
|
107
|
+
{ report: { value: registry(held).release(plain(held[:package])).to_s } }
|
|
108
|
+
rescue Vendoring::Error => e
|
|
109
|
+
raise ConsoleCapture::Failure, e.message
|
|
110
|
+
end
|
|
111
|
+
|
|
63
112
|
# Who the repository at a directory commits as: its configured name and email.
|
|
64
113
|
#
|
|
65
114
|
# @param chdir [String, nil] a directory inside the repository; the current one when nil
|
|
@@ -87,6 +136,16 @@ module Hecks
|
|
|
87
136
|
|
|
88
137
|
private
|
|
89
138
|
|
|
139
|
+
def registry(held)
|
|
140
|
+
EmbryonautBluebook::Registry.new(File.expand_path(plain(held[:root]) || Dir.pwd))
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def built_from(root)
|
|
144
|
+
head = capture("rev-parse", "HEAD", chdir: root)
|
|
145
|
+
status = capture("status", "--porcelain", "--", ".", chdir: root)
|
|
146
|
+
{ "commit" => head.ok? ? head.out.strip : "unknown", "dirty" => status.ok? && !status.out.strip.empty? }
|
|
147
|
+
end
|
|
148
|
+
|
|
90
149
|
def plain(argument) = argument.is_a?(Hash) ? argument[:value] : argument
|
|
91
150
|
|
|
92
151
|
# A source that is not a repository is worded here; the vendoring's own refusal for it is a
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "first_admin"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Adapters
|
|
7
|
+
# What the `DomainRuntime` port's adapter does when an `Operation` asks it to give a domain its
|
|
8
|
+
# first administrator.
|
|
9
|
+
module InProcessAccess
|
|
10
|
+
# Boots the domain and grants the first administrator through its membership chapter.
|
|
11
|
+
#
|
|
12
|
+
# @param held [Hash] the `Operation` record: `subject` (the domain), `email`, and optionally
|
|
13
|
+
# `name` and `role`
|
|
14
|
+
# @return [Hash{Symbol => Hash}] `output:` the sentence saying who now holds which role
|
|
15
|
+
# @raise [Runtime::NotFound] if the domain cannot be found or has no membership chapter, or an
|
|
16
|
+
# administrator already exists
|
|
17
|
+
def bootstrap(**held)
|
|
18
|
+
result = FirstAdmin.new(boot(held[:subject])).call(
|
|
19
|
+
email: plain(held[:email]), name: plain(held[:name]), role: plain(held[:role])
|
|
20
|
+
)
|
|
21
|
+
output(result.to_s)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -9,6 +9,7 @@ require_relative "../../cli/stores"
|
|
|
9
9
|
require_relative "../../cli/shape"
|
|
10
10
|
require_relative "../../cli/history"
|
|
11
11
|
require_relative "in_process_operations"
|
|
12
|
+
require_relative "in_process_access"
|
|
12
13
|
|
|
13
14
|
module Hecks
|
|
14
15
|
module Adapters
|
|
@@ -25,6 +26,7 @@ module Hecks
|
|
|
25
26
|
# a projection refresh, a behaviors run, a smoke test and a bounded follow.
|
|
26
27
|
class InProcessBoot
|
|
27
28
|
include InProcessOperations
|
|
29
|
+
include InProcessAccess
|
|
28
30
|
|
|
29
31
|
# Accepts the arguments every driven adapter is built with and keeps none of them.
|
|
30
32
|
#
|
|
@@ -19,9 +19,9 @@ module Hecks
|
|
|
19
19
|
# minutes, so it starts as a child through `ProcessPool`.
|
|
20
20
|
class RustToolchain
|
|
21
21
|
# The `Hecks::RustBuild` tool each ask runs.
|
|
22
|
-
TOOLS = { generate: "project_rust", wasm: "project_wasm",
|
|
23
|
-
|
|
24
|
-
coverage: "rust_coverage" }.freeze
|
|
22
|
+
TOOLS = { generate: "project_rust", wasm: "project_wasm", host: "project_host",
|
|
23
|
+
browser_wasm: "project_wasm_browser", conform: "rust_conformance",
|
|
24
|
+
replay: "rust_conformance_fuzz", coverage: "rust_coverage" }.freeze
|
|
25
25
|
|
|
26
26
|
# The load path the benchmark child runs with: this checkout's or gem's `lib/`.
|
|
27
27
|
LIB = File.expand_path("../../..", __dir__)
|
|
@@ -73,6 +73,21 @@ module Hecks
|
|
|
73
73
|
child(:wasm, [plain(held[:domain])])
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
# Generates the domain, compiles it to a `wasm32-wasip1` module, builds the host binary from
|
|
77
|
+
# the workspace and stages the three files in a directory an image copies from.
|
|
78
|
+
#
|
|
79
|
+
# @param held [Hash] the `Build` record: `domain`, `target` (the machine's own when absent)
|
|
80
|
+
# and `stage_dir` (`.hecks/host/<target>/` when absent)
|
|
81
|
+
# @return [Hash{Symbol => Hash}] `output:` where the files were staged
|
|
82
|
+
# @raise [ConsoleCapture::Failure] when the toolchain or the target is missing, or a step
|
|
83
|
+
# fails
|
|
84
|
+
def host(**held)
|
|
85
|
+
argv = [plain(held[:domain])]
|
|
86
|
+
argv << "--target=#{plain(held[:target])}" if plain(held[:target])
|
|
87
|
+
argv << "--stage=#{plain(held[:stage_dir])}" if plain(held[:stage_dir])
|
|
88
|
+
child(:host, argv)
|
|
89
|
+
end
|
|
90
|
+
|
|
76
91
|
# Generates the domain, then compiles it to an ES module for a browser through wasm-bindgen.
|
|
77
92
|
#
|
|
78
93
|
# @param held [Hash] the `Build` record: `domain`
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "json"
|
|
3
4
|
require_relative "console_capture"
|
|
5
|
+
require_relative "shell"
|
|
4
6
|
require_relative "codebase/tree"
|
|
5
7
|
require_relative "codebase/ruby_child"
|
|
8
|
+
require "hecks/tools"
|
|
9
|
+
require "hecks/tools/site_routes"
|
|
10
|
+
require "hecks/projections/site/live_edge"
|
|
6
11
|
|
|
7
12
|
module Hecks
|
|
8
13
|
module Adapters
|
|
@@ -44,8 +49,70 @@ module Hecks
|
|
|
44
49
|
{ output: { value: result.out } }
|
|
45
50
|
end
|
|
46
51
|
|
|
52
|
+
# Compares the CloudFront behaviours a project's edge generates with a live distribution's.
|
|
53
|
+
# The live configuration is read from a saved `get-distribution-config` answer (`live`) or
|
|
54
|
+
# fetched with that one read-only call (`distribution`, the distribution's id); nothing is
|
|
55
|
+
# changed on either side.
|
|
56
|
+
#
|
|
57
|
+
# @param held [Hash] the `SiteProjection` record: `domain`, and `live` or `distribution`,
|
|
58
|
+
# with `expect_new` (path patterns, comma separated) and `refs` (`!Ref Name=id` words,
|
|
59
|
+
# comma separated) when set
|
|
60
|
+
# @return [Hash{Symbol => Hash}] `output:` the report of a distribution that matches
|
|
61
|
+
# @raise [ConsoleCapture::Failure] when the project has no edge, the live configuration
|
|
62
|
+
# cannot be read, or the two differ; the message is the report
|
|
63
|
+
def compare(**held)
|
|
64
|
+
edge = edge_of(located(:domain, held[:domain]))
|
|
65
|
+
live = live_configuration(held)
|
|
66
|
+
live_edge = Projections::Site::LiveEdge
|
|
67
|
+
comparison = live_edge.new(edge, live, expect_new: words(held[:expect_new]),
|
|
68
|
+
refs: live_edge.refs_from(plain(held[:refs]))).call
|
|
69
|
+
raise ConsoleCapture::Failure, comparison.to_s unless comparison.clean?
|
|
70
|
+
|
|
71
|
+
{ output: { value: comparison.to_s } }
|
|
72
|
+
end
|
|
73
|
+
|
|
47
74
|
private
|
|
48
75
|
|
|
76
|
+
# The project's checked edge, read the way `project_site` reads it.
|
|
77
|
+
def edge_of(root)
|
|
78
|
+
registry = nil
|
|
79
|
+
outcome = ConsoleCapture.capture { registry = Tools::SiteRoutes.registry_for(root) }
|
|
80
|
+
raise ConsoleCapture::Failure, outcome.output.strip unless outcome.ok?
|
|
81
|
+
|
|
82
|
+
site = Projections::Site
|
|
83
|
+
chapter = site::Table.chapter(registry)
|
|
84
|
+
table = site::Table.read(chapter, registry: registry)
|
|
85
|
+
site::Edge.read(chapter, table: table, vocabulary: site::Table.vocabulary(registry)) ||
|
|
86
|
+
raise(ConsoleCapture::Failure, "#{root} declares no Edge rows")
|
|
87
|
+
rescue Projections::Site::Table::Invalid => e
|
|
88
|
+
raise ConsoleCapture::Failure, e.message
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def live_configuration(held)
|
|
92
|
+
file = plain(held[:live])
|
|
93
|
+
id = plain(held[:distribution])
|
|
94
|
+
text = file ? read_saved(file) : fetch(id)
|
|
95
|
+
JSON.parse(text)
|
|
96
|
+
rescue JSON::ParserError => e
|
|
97
|
+
raise ConsoleCapture::Failure, "the live configuration is not JSON: #{e.message.lines.first.strip}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def read_saved(path)
|
|
101
|
+
File.read(File.expand_path(path))
|
|
102
|
+
rescue SystemCallError => e
|
|
103
|
+
raise ConsoleCapture::Failure, "cannot read the live configuration: #{e.message}"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# The one AWS call: reading a distribution's configuration.
|
|
107
|
+
def fetch(id)
|
|
108
|
+
result = Shell.new.capture("aws", "cloudfront", "get-distribution-config", "--id", id.to_s)
|
|
109
|
+
return result.out if result.ok?
|
|
110
|
+
|
|
111
|
+
raise ConsoleCapture::Failure, "aws cloudfront get-distribution-config failed: #{message_of(result)}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def words(argument) = plain(argument).to_s.split(",").map(&:strip).reject(&:empty?)
|
|
115
|
+
|
|
49
116
|
def message_of(result)
|
|
50
117
|
text = [result.err, result.out].map(&:strip).reject(&:empty?).join("\n")
|
|
51
118
|
text.empty? ? "the tool ended with status #{result.status.exitstatus}" : text
|
|
@@ -85,6 +85,12 @@ Hecks.behaviors "Codebase" do
|
|
|
85
85
|
expect refused: "must match"
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
+
test "ProjectToolsDoc refuses a run that is not named" do
|
|
89
|
+
tests "ProjectToolsDoc", on: "RegenerationRun"
|
|
90
|
+
input run: { value: "" }
|
|
91
|
+
expect refused: "must match"
|
|
92
|
+
end
|
|
93
|
+
|
|
88
94
|
test "DecideCiGate refuses a run that is not named" do
|
|
89
95
|
tests "DecideCiGate", on: "RegenerationRun"
|
|
90
96
|
input run: { value: "" }, gate: { value: "runtime_changed" }
|
|
@@ -1027,6 +1027,22 @@ Hecks.bluebook "Hecks" do
|
|
|
1027
1027
|
emits ProjectCiGatesRequested
|
|
1028
1028
|
end
|
|
1029
1029
|
|
|
1030
|
+
command "ProjectToolsDoc" do
|
|
1031
|
+
role "Maintainer"
|
|
1032
|
+
goal "Write the launcher forms of docs/tools.md from the RetiredScript rows, or with check only compare them"
|
|
1033
|
+
|
|
1034
|
+
attribute :check, Switch, optional: true
|
|
1035
|
+
attribute :run, RunKey
|
|
1036
|
+
attribute :confirm, Switch, optional: true
|
|
1037
|
+
|
|
1038
|
+
sets :check
|
|
1039
|
+
sets :run
|
|
1040
|
+
sets :confirm
|
|
1041
|
+
sets :operation, to: "project_tools_doc"
|
|
1042
|
+
|
|
1043
|
+
emits ProjectToolsDocRequested
|
|
1044
|
+
end
|
|
1045
|
+
|
|
1030
1046
|
command "DecideCiGate" do
|
|
1031
1047
|
role "Maintainer"
|
|
1032
1048
|
goal "Say whether this run's change touches what a CiGate row guards, as the gated job's detector"
|
|
@@ -1105,6 +1121,11 @@ Hecks.bluebook "Hecks" do
|
|
|
1105
1121
|
trigger RegenerationRun::SourceTree::Examine, with: { run: :run }
|
|
1106
1122
|
end
|
|
1107
1123
|
|
|
1124
|
+
policy "ExamineWhenProjectToolsDocRequested" do
|
|
1125
|
+
on "RegenerationRun.ProjectToolsDocRequested"
|
|
1126
|
+
trigger RegenerationRun::SourceTree::Examine, with: { run: :run }
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1108
1129
|
policy "ExamineWhenDecideCiGateRequested" do
|
|
1109
1130
|
on "RegenerationRun.DecideCiGateRequested"
|
|
1110
1131
|
trigger RegenerationRun::SourceTree::Examine, with: { run: :run }
|
|
@@ -176,4 +176,10 @@ Hecks.behaviors "Custodian" do
|
|
|
176
176
|
input domain: { value: "examples/pizzas" }, run: { value: "" }
|
|
177
177
|
expect refused: "must match"
|
|
178
178
|
end
|
|
179
|
+
|
|
180
|
+
test "BuildHost refuses a target that is not a triple" do
|
|
181
|
+
tests "BuildHost", on: "Build"
|
|
182
|
+
input domain: { value: "examples/pizzas" }, run: { value: "host" }, target: { value: "arm64" }
|
|
183
|
+
expect refused: "must match"
|
|
184
|
+
end
|
|
179
185
|
end
|
|
@@ -238,6 +238,9 @@ Hecks.bluebook "Hecks" do
|
|
|
238
238
|
attribute :payload_file, PayloadFile, optional: true
|
|
239
239
|
attribute :health_path, RoutePath, optional: true
|
|
240
240
|
attribute :state_path, RoutePath, optional: true
|
|
241
|
+
attribute :email, Email, optional: true
|
|
242
|
+
attribute :name, PersonName, optional: true
|
|
243
|
+
attribute :role, RoleName, optional: true
|
|
241
244
|
attribute :output, Output, optional: true
|
|
242
245
|
attribute :refusal, Output, optional: true
|
|
243
246
|
identified_by :run
|
|
@@ -294,6 +297,19 @@ Hecks.bluebook "Hecks" do
|
|
|
294
297
|
attribute :value, String
|
|
295
298
|
end
|
|
296
299
|
|
|
300
|
+
# Who the first administrator is: a person of the domain's membership chapter.
|
|
301
|
+
value_object "Email" do
|
|
302
|
+
attribute :value, String, pattern: '^[^ \t\n\r@]+@[^ \t\n\r@]+$'
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
value_object "PersonName" do
|
|
306
|
+
attribute :value, String, pattern: '[^\t\n\r]'
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
value_object "RoleName" do
|
|
310
|
+
attribute :value, String, pattern: '^[^ \t\n\r]+$'
|
|
311
|
+
end
|
|
312
|
+
|
|
297
313
|
lifecycle :status, default: "requested" do
|
|
298
314
|
transition "Succeed" => "succeeded", from: "requested"
|
|
299
315
|
transition "Fail" => "failed", from: "requested"
|
|
@@ -404,6 +420,32 @@ Hecks.bluebook "Hecks" do
|
|
|
404
420
|
emits SmokeHttpRequested
|
|
405
421
|
end
|
|
406
422
|
|
|
423
|
+
# `hecks operation.bootstrap_admin <domain> email=<email> [name=<name>] [role=<role>]`. A
|
|
424
|
+
# domain whose chapter `provides "membership"` has nobody who may grant access until someone is
|
|
425
|
+
# granted it. This admits the person and grants them the role the grant command is gated to
|
|
426
|
+
# (or `role`), as a caller that names that role and binds no `actor_id`: Governance checks that
|
|
427
|
+
# caller by the role it states, which is how the first grant gets made (ADR 0025). It is
|
|
428
|
+
# refused once anyone holds the administrator role (`Admin`, or `Owner`), and the domain's
|
|
429
|
+
# persistence must be the one the service runs on (`DATABASE_URL`), not Memory.
|
|
430
|
+
command "BootstrapAdmin" do
|
|
431
|
+
role "Operator"
|
|
432
|
+
goal "Admit the first administrator of a domain's membership chapter and grant them access"
|
|
433
|
+
|
|
434
|
+
attribute :run, OperationKey
|
|
435
|
+
attribute :subject, Subject
|
|
436
|
+
attribute :email, Email
|
|
437
|
+
attribute :name, PersonName, optional: true
|
|
438
|
+
attribute :role, RoleName, optional: true
|
|
439
|
+
|
|
440
|
+
sets :run
|
|
441
|
+
sets :subject
|
|
442
|
+
sets :email
|
|
443
|
+
sets :name
|
|
444
|
+
sets :role
|
|
445
|
+
|
|
446
|
+
emits AdminBootstrapRequested
|
|
447
|
+
end
|
|
448
|
+
|
|
407
449
|
command "Succeed" do
|
|
408
450
|
role "System"
|
|
409
451
|
goal "Record that the operation ended well, and what it reported"
|
|
@@ -494,6 +536,11 @@ Hecks.bluebook "Hecks" do
|
|
|
494
536
|
trigger Operation::DomainRuntime::Smoke, with: { run: :run }
|
|
495
537
|
end
|
|
496
538
|
|
|
539
|
+
policy "BootstrapWhenRequested" do
|
|
540
|
+
on "Operation.AdminBootstrapRequested"
|
|
541
|
+
trigger Operation::DomainRuntime::Bootstrap, with: { run: :run }
|
|
542
|
+
end
|
|
543
|
+
|
|
497
544
|
policy "OpenWhenRequested" do
|
|
498
545
|
on "Operation.ConsoleRequested"
|
|
499
546
|
trigger Operation::Terminal::Open, with: { run: :run }
|
|
@@ -1173,6 +1220,11 @@ policy "RecordTheEra" do
|
|
|
1173
1220
|
attribute :value, String
|
|
1174
1221
|
end
|
|
1175
1222
|
|
|
1223
|
+
# An answer that is one document of text, exactly as the tool that produced it wrote it.
|
|
1224
|
+
value_object "Document" do
|
|
1225
|
+
attribute :text, String
|
|
1226
|
+
end
|
|
1227
|
+
|
|
1176
1228
|
lifecycle :status, default: "requested" do
|
|
1177
1229
|
transition "Revendor" => "requested", from: %w[vendored refused]
|
|
1178
1230
|
transition "Complete" => "vendored", from: "requested"
|
|
@@ -1248,6 +1300,14 @@ policy "RecordTheEra" do
|
|
|
1248
1300
|
where(status: "refused")
|
|
1249
1301
|
order_by :package
|
|
1250
1302
|
end
|
|
1303
|
+
|
|
1304
|
+
# `hecks package.verify [root=path]`: a read of the files, so it keeps no record. The manifest
|
|
1305
|
+
# is the answer; a package that disagrees with its lock refuses it, naming each one.
|
|
1306
|
+
query "Verify" do
|
|
1307
|
+
description "Check every vendored package against its bluebook.lock, then describe them as the manifest a client image carries."
|
|
1308
|
+
attribute :root, Location, optional: true
|
|
1309
|
+
returns Document
|
|
1310
|
+
end
|
|
1251
1311
|
end
|
|
1252
1312
|
|
|
1253
1313
|
policy "PinWhenVendored" do
|
|
@@ -1270,6 +1330,125 @@ policy "RecordTheEra" do
|
|
|
1270
1330
|
trigger Package::Abandon, with: { refusal: :refusal }
|
|
1271
1331
|
end
|
|
1272
1332
|
|
|
1333
|
+
aggregate "Registry" do
|
|
1334
|
+
description "A bluebook registry repository: packages versioned in it and released as tags."
|
|
1335
|
+
|
|
1336
|
+
attribute :run, RegistryKey
|
|
1337
|
+
attribute :package, PackageName, optional: true
|
|
1338
|
+
attribute :root, Location, optional: true
|
|
1339
|
+
attribute :report, Report, optional: true
|
|
1340
|
+
attribute :refusal, Report, optional: true
|
|
1341
|
+
identified_by :run
|
|
1342
|
+
|
|
1343
|
+
value_object "RegistryKey" do
|
|
1344
|
+
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
1345
|
+
|
|
1346
|
+
invariant("a release is named") { !value.to_s.empty? }
|
|
1347
|
+
end
|
|
1348
|
+
|
|
1349
|
+
value_object "PackageName" do
|
|
1350
|
+
attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
|
|
1351
|
+
end
|
|
1352
|
+
|
|
1353
|
+
# The registry repository: the current directory when absent.
|
|
1354
|
+
value_object "Location" do
|
|
1355
|
+
attribute :value, String, pattern: '[^\t\n\r]'
|
|
1356
|
+
end
|
|
1357
|
+
|
|
1358
|
+
value_object "Report" do
|
|
1359
|
+
attribute :value, String
|
|
1360
|
+
end
|
|
1361
|
+
|
|
1362
|
+
# An answer that is one document of text, exactly as the tool that produced it wrote it.
|
|
1363
|
+
value_object "Document" do
|
|
1364
|
+
attribute :text, String
|
|
1365
|
+
end
|
|
1366
|
+
|
|
1367
|
+
lifecycle :status, default: "requested" do
|
|
1368
|
+
transition "Complete" => "tagged", from: "requested"
|
|
1369
|
+
transition "Abandon" => "refused", from: "requested"
|
|
1370
|
+
end
|
|
1371
|
+
|
|
1372
|
+
# `hecks package.release <package> [root=path]`. Tags the package's current version as
|
|
1373
|
+
# `<package>-vX.Y.Z` in the local repository and never pushes; the report ends in the command
|
|
1374
|
+
# that does. A refusal says which rule the package broke.
|
|
1375
|
+
command "Release" do
|
|
1376
|
+
role "Operator"
|
|
1377
|
+
goal "Tag a package of this registry at the version its bluebook.yml carries"
|
|
1378
|
+
|
|
1379
|
+
attribute :run, RegistryKey
|
|
1380
|
+
attribute :package, PackageName
|
|
1381
|
+
attribute :root, Location, optional: true
|
|
1382
|
+
|
|
1383
|
+
sets :run
|
|
1384
|
+
sets :package
|
|
1385
|
+
sets :root
|
|
1386
|
+
|
|
1387
|
+
emits ReleaseRequested
|
|
1388
|
+
end
|
|
1389
|
+
|
|
1390
|
+
command "Complete" do
|
|
1391
|
+
role "System"
|
|
1392
|
+
goal "Record that the release was tagged, and the command that publishes it"
|
|
1393
|
+
|
|
1394
|
+
reference_to Registry
|
|
1395
|
+
attribute :report, Report
|
|
1396
|
+
|
|
1397
|
+
sets :report
|
|
1398
|
+
|
|
1399
|
+
emits ReleaseRecorded
|
|
1400
|
+
end
|
|
1401
|
+
|
|
1402
|
+
command "Abandon" do
|
|
1403
|
+
role "System"
|
|
1404
|
+
goal "Record that the release was refused, and why"
|
|
1405
|
+
|
|
1406
|
+
reference_to Registry
|
|
1407
|
+
attribute :refusal, Report
|
|
1408
|
+
|
|
1409
|
+
sets :refusal
|
|
1410
|
+
|
|
1411
|
+
emits ReleaseAbandoned
|
|
1412
|
+
end
|
|
1413
|
+
|
|
1414
|
+
# `hecks package.check [root=path]`: a read of the repository's files and tags, so it keeps no
|
|
1415
|
+
# record. A package that changed since its latest release without a version bump and a changelog
|
|
1416
|
+
# entry refuses it, naming each one.
|
|
1417
|
+
query "Check" do
|
|
1418
|
+
description "Judge every package against its latest release: a changed package needs a newer version and a changelog entry."
|
|
1419
|
+
attribute :root, Location, optional: true
|
|
1420
|
+
returns Document
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
query "Releasing" do
|
|
1424
|
+
description "What releasing one run did: its status, and the report or the refusal."
|
|
1425
|
+
attribute :run, RegistryKey
|
|
1426
|
+
where(run: :run)
|
|
1427
|
+
order_by :run
|
|
1428
|
+
end
|
|
1429
|
+
|
|
1430
|
+
query "Refused" do
|
|
1431
|
+
description "Every release that was refused."
|
|
1432
|
+
where(status: "refused")
|
|
1433
|
+
order_by :run
|
|
1434
|
+
end
|
|
1435
|
+
end
|
|
1436
|
+
|
|
1437
|
+
policy "TagWhenReleased" do
|
|
1438
|
+
on "Registry.ReleaseRequested"
|
|
1439
|
+
trigger Registry::Git::Tag, with: { run: :run }
|
|
1440
|
+
end
|
|
1441
|
+
|
|
1442
|
+
policy "RecordTheTag" do
|
|
1443
|
+
on "Registry.ReleaseTagged"
|
|
1444
|
+
trigger Registry::Complete, with: { report: :report }
|
|
1445
|
+
end
|
|
1446
|
+
|
|
1447
|
+
policy "RecordTheRefusedTag" do
|
|
1448
|
+
on "Registry.ReleaseRefused"
|
|
1449
|
+
trigger Registry::Abandon, with: { refusal: :refusal }
|
|
1450
|
+
end
|
|
1451
|
+
|
|
1273
1452
|
# A run record for opening a door onto the framework, not a door: the doors live in `Hecks::Doors`.
|
|
1274
1453
|
aggregate "Door" do
|
|
1275
1454
|
description "A way into the framework opened for a project: its launchers, or an MCP server."
|
|
@@ -1481,6 +1660,8 @@ policy "RecordTheEra" do
|
|
|
1481
1660
|
attribute :domain, DomainPath, optional: true
|
|
1482
1661
|
attribute :script, ScriptPath, optional: true
|
|
1483
1662
|
attribute :artifact, Artifact, optional: true
|
|
1663
|
+
attribute :target, TargetTriple, optional: true
|
|
1664
|
+
attribute :stage_dir, StagePath, optional: true
|
|
1484
1665
|
attribute :seeds, Count, optional: true
|
|
1485
1666
|
attribute :steps, Count, optional: true
|
|
1486
1667
|
attribute :output, Report, optional: true
|
|
@@ -1515,6 +1696,17 @@ policy "RecordTheEra" do
|
|
|
1515
1696
|
attribute :value, String, pattern: '[^ \t\n\r]'
|
|
1516
1697
|
end
|
|
1517
1698
|
|
|
1699
|
+
# A Rust target triple: `<arch>-<vendor>-<os>` with an optional `-<env>`. The toolchain
|
|
1700
|
+
# refuses the triples a host cannot be built for (a wasm target) and the ones not installed.
|
|
1701
|
+
value_object "TargetTriple" do
|
|
1702
|
+
attribute :value, String, pattern: '^[a-z0-9_]+(-[a-z0-9_.]+){2,3}$'
|
|
1703
|
+
end
|
|
1704
|
+
|
|
1705
|
+
# The directory a host build stages its files in.
|
|
1706
|
+
value_object "StagePath" do
|
|
1707
|
+
attribute :value, String, pattern: '[^\t\n\r]'
|
|
1708
|
+
end
|
|
1709
|
+
|
|
1518
1710
|
value_object "Count" do
|
|
1519
1711
|
attribute :value, Integer
|
|
1520
1712
|
|
|
@@ -1563,6 +1755,26 @@ policy "RecordTheEra" do
|
|
|
1563
1755
|
emits WasmBuildRequested
|
|
1564
1756
|
end
|
|
1565
1757
|
|
|
1758
|
+
# Compiles the host binary from the installed gem's workspace for the target and stages it with
|
|
1759
|
+
# the domain's `.wasm` and `.ir.json` (in `.hecks/host/<target>/` unless `stage_dir` names a
|
|
1760
|
+
# directory), the files a container image copies in.
|
|
1761
|
+
command "BuildHost" do
|
|
1762
|
+
role "Operator"
|
|
1763
|
+
goal "Build the Rust host for a domain, with its wasm and IR, ready to copy into an image"
|
|
1764
|
+
|
|
1765
|
+
attribute :domain, DomainPath
|
|
1766
|
+
attribute :run, BuildKey
|
|
1767
|
+
attribute :target, TargetTriple, optional: true
|
|
1768
|
+
attribute :stage_dir, StagePath, optional: true
|
|
1769
|
+
|
|
1770
|
+
sets :domain
|
|
1771
|
+
sets :run
|
|
1772
|
+
sets :target
|
|
1773
|
+
sets :stage_dir
|
|
1774
|
+
|
|
1775
|
+
emits HostBuildRequested
|
|
1776
|
+
end
|
|
1777
|
+
|
|
1566
1778
|
command "BuildBrowserWasm" do
|
|
1567
1779
|
role "Operator"
|
|
1568
1780
|
goal "Compile a domain to an ES module a browser can import"
|
|
@@ -1685,6 +1897,11 @@ policy "RecordTheEra" do
|
|
|
1685
1897
|
trigger Build::RustToolchain::Wasm, with: { run: :run }
|
|
1686
1898
|
end
|
|
1687
1899
|
|
|
1900
|
+
policy "BuildHostWhenRequested" do
|
|
1901
|
+
on "Build.HostBuildRequested"
|
|
1902
|
+
trigger Build::RustToolchain::Host, with: { run: :run }
|
|
1903
|
+
end
|
|
1904
|
+
|
|
1688
1905
|
policy "CompileBrowserWasmWhenRequested" do
|
|
1689
1906
|
on "Build.BrowserWasmBuildRequested"
|
|
1690
1907
|
trigger Build::RustToolchain::BrowserWasm, with: { run: :run }
|
|
@@ -43,6 +43,12 @@ Hecks.hecksagon "Hecks" do
|
|
|
43
43
|
refuses "OperationRefused"
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
asks "Bootstrap", to: Operation do
|
|
47
|
+
attribute :run, OperationKey
|
|
48
|
+
answers "OperationAnswered"
|
|
49
|
+
refuses "OperationRefused"
|
|
50
|
+
end
|
|
51
|
+
|
|
46
52
|
answers_query "Follow"
|
|
47
53
|
end
|
|
48
54
|
|
|
@@ -87,6 +93,18 @@ Hecks.hecksagon "Hecks" do
|
|
|
87
93
|
answers "PackagePinned"
|
|
88
94
|
refuses "PinRefused"
|
|
89
95
|
end
|
|
96
|
+
|
|
97
|
+
answers_query "Verify"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
Hecks::Registry.port "Git" do
|
|
101
|
+
asks "Tag", to: Registry do
|
|
102
|
+
attribute :run, RegistryKey
|
|
103
|
+
answers "ReleaseTagged"
|
|
104
|
+
refuses "ReleaseRefused"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
answers_query "Check"
|
|
90
108
|
end
|
|
91
109
|
|
|
92
110
|
Hecks::Door.port "Workspace" do
|
|
@@ -130,6 +148,12 @@ Hecks.hecksagon "Hecks" do
|
|
|
130
148
|
refuses "BuildRefused"
|
|
131
149
|
end
|
|
132
150
|
|
|
151
|
+
asks "Host", to: Build do
|
|
152
|
+
attribute :run, BuildKey
|
|
153
|
+
answers "BuildAnswered"
|
|
154
|
+
refuses "BuildRefused"
|
|
155
|
+
end
|
|
156
|
+
|
|
133
157
|
asks "BrowserWasm", to: Build do
|
|
134
158
|
attribute :run, BuildKey
|
|
135
159
|
answers "BuildAnswered"
|
data/lib/hecks/hecks/hecks.world
CHANGED
|
@@ -25,8 +25,10 @@
|
|
|
25
25
|
# `Doors::LauncherOptions`): `run_keys` mints a `run` key for a creating command given none,
|
|
26
26
|
# `failure_states` are the lifecycle states `--wait` reports as exit 1 (every failure state the
|
|
27
27
|
# chapter's lifecycles declare), `names` gives a command the launcher name clients type,
|
|
28
|
-
# `
|
|
29
|
-
#
|
|
28
|
+
# `settled` names the commands that always wait for their reactions, as if `--wait` were given, so a
|
|
29
|
+
# refusal is an exit status and a reason on standard error (`package.vendor`, which a script runs
|
|
30
|
+
# for its outcome), `executable` is where `hecks project_cli` writes this chapter's launcher (the
|
|
31
|
+
# gem's `exe/hecks`), `streams` lists the questions `--stream` may tail (`Follow`), `memory_commands` names
|
|
30
32
|
# the commands that launcher runs on the Memory environment unless `HECKS_ENVIRONMENT` is set (so
|
|
31
33
|
# `hecks console` needs no database), and `legacy` names the commands it hands to `Hecks::CLI` in the
|
|
32
34
|
# positional forms ADR 0066 shipped.
|
|
@@ -37,8 +39,11 @@ Hecks.world "Hecks" do
|
|
|
37
39
|
launcher "Launcher", run_keys: true,
|
|
38
40
|
failure_states: %w[flagged failed drifted unreachable refused faulted halted stopped
|
|
39
41
|
abandoned red needs_fix],
|
|
42
|
+
settled: %w[package.vendor package.revendor package.release
|
|
43
|
+
operation.bootstrap_admin],
|
|
40
44
|
names: { "mcp" => "door.serve_mcp", "console" => "operation.open_console", "init" => "door.init",
|
|
41
|
-
"interview" => "door.interview"
|
|
45
|
+
"interview" => "door.interview", "package.check" => "registry.check",
|
|
46
|
+
"package.release" => "registry.release" },
|
|
42
47
|
streams: %w[Follow],
|
|
43
48
|
executable: "exe/hecks",
|
|
44
49
|
memory_commands: %w[console init interview],
|
data/lib/hecks/hecks/site.world
CHANGED
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
# here: hecks.world's `default_adapter` fills the gap, so the memory overlay swaps it with the rest.
|
|
4
4
|
#
|
|
5
5
|
# `run_keys` mints the `run` key of a projection, and `--wait` reports a faulted projection (a
|
|
6
|
-
# routes.ts out of date under `--check`, or a route table the projector refused) as exit 1
|
|
6
|
+
# routes.ts out of date under `--check`, or a route table the projector refused) as exit 1, and
|
|
7
|
+
# `settled` makes `site_projection.check_live` wait for its reactions without the flag, so a live
|
|
8
|
+
# distribution that differs from the project is an exit status.
|
|
7
9
|
Hecks.world "Site" do
|
|
8
10
|
launcher "Launcher", run_keys: true,
|
|
9
|
-
failure_states: %w[faulted refused halted stopped abandoned red needs_fix]
|
|
11
|
+
failure_states: %w[faulted refused halted stopped abandoned red needs_fix],
|
|
12
|
+
settled: %w[site_projection.check_live]
|
|
10
13
|
end
|