hecks 3.11.0 → 3.13.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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/script_runner.adapter +3 -0
  3. data/lib/hecks/adapters/driven/script_runner.rb +93 -0
  4. data/lib/hecks/adapters/driven.rb +1 -0
  5. data/lib/hecks/adapters/driving/cli_runner/resolution.rb +6 -3
  6. data/lib/hecks/adapters/driving/cli_runner.rb +1 -0
  7. data/lib/hecks/adapters/driving/newcomer.rb +50 -0
  8. data/lib/hecks/bluebook/assembly/contracts/read_models.rb +1 -1
  9. data/lib/hecks/bluebook/assembly/marks.rb +3 -15
  10. data/lib/hecks/bluebook/assembly/scalars.rb +39 -0
  11. data/lib/hecks/bluebook/meta_validator/readings.rb +6 -0
  12. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +6 -3
  13. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +16 -12
  14. data/lib/hecks/bluebook/meta_validator.rb +28 -21
  15. data/lib/hecks/bluebook.rb +1 -0
  16. data/lib/hecks/deploy/bluebook/deploy.bluebook +17 -3
  17. data/lib/hecks/framework/bluebook/change.behaviors +30 -0
  18. data/lib/hecks/framework/bluebook/change.bluebook +162 -0
  19. data/lib/hecks/framework/bluebook/change.hecksagon +21 -0
  20. data/lib/hecks/framework/change.oidc.json +34 -0
  21. data/lib/hecks/framework/console_settings.oidc.json +103 -0
  22. data/lib/hecks/framework/governance.oidc.json +28 -0
  23. data/lib/hecks/framework/identity.oidc.json +18 -0
  24. data/lib/hecks/framework/privacy.oidc.json +30 -0
  25. data/lib/hecks/gate/stages.yml +35 -0
  26. data/lib/hecks/grammar/translation.oidc.json +48 -0
  27. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +12 -1
  28. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +2 -1
  29. data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +5 -1
  30. data/lib/hecks/hecks/hecks.world +3 -0
  31. data/lib/hecks/language/bluebook/bluebook.behaviors +23 -0
  32. data/lib/hecks/language/bluebook/policy.behaviors +19 -0
  33. data/lib/hecks/language/bluebook/vocabulary.bluebook +52 -0
  34. data/lib/hecks/projection_files/expression_tables.rb +7 -2
  35. data/lib/hecks/projections/deploy/box/compose.rb +6 -4
  36. data/lib/hecks/projections/deploy/box/edge.rb +62 -0
  37. data/lib/hecks/projections/deploy/box/logging.rb +99 -0
  38. data/lib/hecks/projections/deploy/box/roll_scripts.rb +16 -8
  39. data/lib/hecks/projections/deploy/box/settings/cdn.rb +40 -0
  40. data/lib/hecks/projections/deploy/box/settings/logs.rb +40 -0
  41. data/lib/hecks/projections/deploy/box/settings/patterns.rb +11 -0
  42. data/lib/hecks/projections/deploy/box/settings/types.rb +16 -1
  43. data/lib/hecks/projections/deploy/box/settings.rb +6 -2
  44. data/lib/hecks/projections/deploy/box/stack_yaml.rb +5 -2
  45. data/lib/hecks/projections/deploy/box/templates/Makefile.tmpl +16 -0
  46. data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +8 -4
  47. data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +3 -0
  48. data/lib/hecks/projections/deploy/box/templates/edge.mk.tmpl +17 -0
  49. data/lib/hecks/projections/deploy/box/templates/edge.yaml.tmpl +65 -0
  50. data/lib/hecks/projections/deploy/box/templates/log-group.yaml.tmpl +12 -0
  51. data/lib/hecks/projections/deploy/box/templates/log-policy.yaml.tmpl +7 -0
  52. data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +5 -4
  53. data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +5 -4
  54. data/lib/hecks/projections/deploy/box.rb +15 -3
  55. data/lib/hecks/projections/deploy/fargate/cdn/lines.rb +12 -1
  56. data/lib/hecks/projections/site/admin_cms/templates/membership.rb +10 -7
  57. data/lib/hecks/projections/site/edge/behaviours.rb +10 -6
  58. data/lib/hecks/projections/site/edge/checks.rb +2 -0
  59. data/lib/hecks/projections/site/edge/function_checks.rb +104 -0
  60. data/lib/hecks/projections/site/edge/function_code.rb +86 -0
  61. data/lib/hecks/projections/site/edge/redirect_host.js.tmpl +65 -0
  62. data/lib/hecks/projections/site/edge.rb +35 -9
  63. data/lib/hecks/projections/site/site_cdn.rb +21 -7
  64. data/lib/hecks/projections/site/site_root/workflow.rb +5 -0
  65. data/lib/hecks/projections/site/table/row_builder.rb +12 -2
  66. data/lib/hecks/projections/site/table.rb +4 -2
  67. data/lib/hecks/projector/cli_audience.rb +20 -0
  68. data/lib/hecks/projector/cli_projector/usage.rb +15 -4
  69. data/lib/hecks/rust_build/host.rb +11 -4
  70. data/lib/hecks/rust_build/prebuilt_binary.rb +101 -0
  71. data/lib/hecks/site/bluebook/site.bluebook +16 -1
  72. data/lib/hecks/tools/ci_gate_decision.rb +35 -0
  73. data/lib/hecks/tools/ci_gates.rb +20 -1
  74. data/lib/hecks/tools/lanes/files.rb +8 -0
  75. data/lib/hecks/tools/lanes/prebuilt_workflow.rb +81 -0
  76. data/lib/hecks/tools/lanes.rb +1 -0
  77. data/lib/hecks/tools/oidc_manifests.rb +44 -6
  78. data/lib/hecks/tools/release_only.rb +111 -0
  79. data/lib/hecks/tools/site_routes/templates.rb +13 -9
  80. data/lib/hecks/version.rb +1 -1
  81. data/lib/hecks/vocabulary.rb +15 -1
  82. data/prebuilt/2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal +0 -0
  83. data/prebuilt/58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal +0 -0
  84. data/prebuilt/{verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json → verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json} +1 -1
  85. data/rust/host/HECKS_RELEASE +1 -1
  86. data/rust/host/src/auth.rs +58 -0
  87. data/rust/host/src/extension.rs +13 -1
  88. data/rust/host/src/lib.rs +1 -0
  89. data/rust/host/src/platform_signin/attack_tests.rs +1100 -0
  90. data/rust/host/src/platform_signin/config.rs +512 -0
  91. data/rust/host/src/platform_signin/cookie.rs +237 -0
  92. data/rust/host/src/platform_signin/extension.rs +190 -0
  93. data/rust/host/src/platform_signin/fetcher.rs +322 -0
  94. data/rust/host/src/platform_signin/handler.rs +251 -0
  95. data/rust/host/src/platform_signin/handler_tests.rs +554 -0
  96. data/rust/host/src/platform_signin/jti.rs +196 -0
  97. data/rust/host/src/platform_signin/jwks.rs +542 -0
  98. data/rust/host/src/platform_signin/jws.rs +249 -0
  99. data/rust/host/src/platform_signin/known_answer_tests.rs +237 -0
  100. data/rust/host/src/platform_signin/mapping_tests.rs +254 -0
  101. data/rust/host/src/platform_signin/mod.rs +75 -0
  102. data/rust/host/src/platform_signin/mode_tests.rs +382 -0
  103. data/rust/host/src/platform_signin/pages.rs +204 -0
  104. data/rust/host/src/platform_signin/query.rs +233 -0
  105. data/rust/host/src/platform_signin/session.rs +151 -0
  106. data/rust/host/src/platform_signin/settings.rs +385 -0
  107. data/rust/host/src/platform_signin/strict_json.rs +351 -0
  108. data/rust/host/src/platform_signin/test_support.rs +235 -0
  109. data/rust/host/src/platform_signin/ttl_tests.rs +94 -0
  110. data/rust/host/src/platform_signin/verify.rs +604 -0
  111. data/rust/host/src/platform_signin/wiring_support.rs +319 -0
  112. data/rust/host/src/web.rs +50 -6
  113. data/rust/parser/src/parse/value_object.rs +22 -10
  114. metadata +59 -7
  115. data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
  116. data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
  117. /data/lib/hecks/framework/{oidc.json → compliance.oidc.json} +0 -0
  118. /data/lib/hecks/grammar/{oidc.json → expression.oidc.json} +0 -0
@@ -0,0 +1,162 @@
1
+ Hecks.bluebook "Change" do
2
+ vision "A change you can preview: it is planned, shown, confirmed against exactly that plan, and only then carried out, whatever script it runs."
3
+ supporting
4
+
5
+ # A framework member like Governance and Identity: attach it with `attaches "Change"` in the
6
+ # consuming domain's own `.hecksagon`. The script is the port's adapter, so what a change
7
+ # does lives behind the `Script` port, and this chapter holds only how any change moves.
8
+ aggregate "Change" do
9
+ description "One change a script makes, previewed before it is carried out and applied only if its plan is unchanged."
10
+
11
+ attribute :run, RunKey
12
+ attribute :script, ScriptName
13
+ attribute :arguments, list_of(Argument), optional: true
14
+ attribute :plan, Report, optional: true
15
+ attribute :planned_digest, Digest, optional: true
16
+ attribute :report, Report, optional: true
17
+ attribute :refusal, Report, optional: true
18
+ identified_by :run
19
+
20
+ value_object "RunKey" do
21
+ attribute :value, String, pattern: '[^ \t\n\r]'
22
+
23
+ invariant("a change is named") { !value.to_s.empty? }
24
+ end
25
+
26
+ # The name the script is registered under with the adapter.
27
+ value_object "ScriptName" do
28
+ attribute :value, String, pattern: '[^ \t\n\r]'
29
+
30
+ invariant("a change names its script") { !value.to_s.empty? }
31
+ end
32
+
33
+ # One argument of the script, spelled as text; the script says what each key means.
34
+ value_object "Argument" do
35
+ attribute :key, String
36
+ attribute :value, String
37
+ end
38
+
39
+ value_object "Report" do
40
+ attribute :value, String
41
+ end
42
+
43
+ # A fingerprint of the plan, the script and its arguments: confirming one digest applies
44
+ # that plan and no other.
45
+ value_object "Digest" do
46
+ attribute :value, String, pattern: '[0-9a-f]+'
47
+
48
+ invariant("a plan has a digest") { !value.to_s.empty? }
49
+ end
50
+
51
+ lifecycle :status, default: "requested" do
52
+ mark :failure, "faulted"
53
+ transition "Plan" => "planned", from: "requested"
54
+ transition "Confirm" => "confirmed", from: "planned"
55
+ transition "Complete" => "applied", from: "confirmed"
56
+ transition "Fault" => "faulted", from: ["requested", "planned", "confirmed"]
57
+ end
58
+
59
+ command "Request" do
60
+ role "Maintainer"
61
+ goal "Ask for a change by naming its script and giving the script's arguments"
62
+
63
+ attribute :run, RunKey
64
+ attribute :script, ScriptName
65
+ attribute :arguments, list_of(Argument), optional: true
66
+
67
+ sets :run
68
+ sets :script
69
+ sets :arguments
70
+
71
+ emits ChangeRequested
72
+ end
73
+
74
+ command "Plan" do
75
+ role "System"
76
+ goal "Record what the script would do, and the digest that stands for it"
77
+
78
+ reference_to Change
79
+ attribute :plan, Report
80
+ attribute :digest, Digest
81
+
82
+ sets :plan
83
+ sets :planned_digest, to: :digest
84
+
85
+ emits ChangePlanned
86
+ end
87
+
88
+ command "Confirm" do
89
+ role "Maintainer"
90
+ goal "Agree to the plan shown, by naming its digest"
91
+
92
+ reference_to Change
93
+ attribute :digest, Digest
94
+
95
+ given("the digest confirmed is the digest planned") { digest.value == planned_digest.value }
96
+
97
+ emits ChangeConfirmed
98
+ end
99
+
100
+ command "Complete" do
101
+ role "System"
102
+ goal "Record that the confirmed plan was carried out, and what the script reported"
103
+
104
+ reference_to Change
105
+ attribute :report, Report
106
+
107
+ sets :report
108
+
109
+ emits ChangeApplied
110
+ end
111
+
112
+ command "Fault" do
113
+ role "System"
114
+ goal "Record that the change was refused or could not be carried out, and why"
115
+
116
+ reference_to Change
117
+ attribute :refusal, Report
118
+
119
+ sets :refusal
120
+
121
+ emits ChangeFaulted
122
+ end
123
+
124
+ query "Outcome" do
125
+ description "How one change stands: its status, its plan, and what it reported or why it failed."
126
+ attribute :run, RunKey
127
+ where(run: :run)
128
+ order_by :run
129
+ end
130
+
131
+ query "Faulted" do
132
+ description "Every change that was refused or could not be carried out."
133
+ where(status: "faulted")
134
+ order_by :run
135
+ end
136
+ end
137
+
138
+ policy "ExamineWhenRequested" do
139
+ on "Change.ChangeRequested"
140
+ trigger Change::Script::Examine, with: { run: :run }
141
+ end
142
+
143
+ policy "RecordThePlanWhenExamined" do
144
+ on "Change.ChangeExamined"
145
+ trigger Change::Plan, with: { plan: :plan, digest: :digest }
146
+ end
147
+
148
+ policy "PerformWhenConfirmed" do
149
+ on "Change.ChangeConfirmed"
150
+ trigger Change::Script::Perform, with: { run: :run }
151
+ end
152
+
153
+ policy "RecordTheOutcomeWhenPerformed" do
154
+ on "Change.ChangeAnswered"
155
+ trigger Change::Complete, with: { report: :report }
156
+ end
157
+
158
+ policy "RecordTheRefusal" do
159
+ on "Change.ChangeRefused"
160
+ trigger Change::Fault, with: { refusal: :refusal }
161
+ end
162
+ end
@@ -0,0 +1,21 @@
1
+ Hecks.hecksagon "Change" do
2
+ # `Change.Request` and `Change.Confirm` declare roles; same reason as Compliance.
3
+ attaches "Governance"
4
+ persisted_by "Memory"
5
+
6
+ # The change moves through two asks: `Examine` plans it, `Perform` carries it out. What a
7
+ # script does is the adapter's, so the chapter never names one.
8
+ Change::Change.port "Script" do
9
+ asks "Examine", to: Change do
10
+ attribute :run, RunKey
11
+ answers "ChangeExamined"
12
+ refuses "ChangeRefused"
13
+ end
14
+
15
+ asks "Perform", to: Change do
16
+ attribute :run, RunKey
17
+ answers "ChangeAnswered"
18
+ refuses "ChangeRefused"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ {
2
+ "audience": "Change",
3
+ "scopes": [
4
+ {
5
+ "scope": "change:change.complete",
6
+ "verb": "Change::Change.Complete",
7
+ "role": "System"
8
+ },
9
+ {
10
+ "scope": "change:change.confirm",
11
+ "verb": "Change::Change.Confirm",
12
+ "role": "Maintainer"
13
+ },
14
+ {
15
+ "scope": "change:change.fault",
16
+ "verb": "Change::Change.Fault",
17
+ "role": "System"
18
+ },
19
+ {
20
+ "scope": "change:change.plan",
21
+ "verb": "Change::Change.Plan",
22
+ "role": "System"
23
+ },
24
+ {
25
+ "scope": "change:change.request",
26
+ "verb": "Change::Change.Request",
27
+ "role": "Maintainer"
28
+ }
29
+ ],
30
+ "roles": [
31
+ "Maintainer",
32
+ "System"
33
+ ]
34
+ }
@@ -0,0 +1,103 @@
1
+ {
2
+ "audience": "ConsoleSettings",
3
+ "scopes": [
4
+ {
5
+ "scope": "console_settings:collection.declare",
6
+ "verb": "ConsoleSettings::Collection.Declare",
7
+ "role": "Console"
8
+ },
9
+ {
10
+ "scope": "console_settings:collection.replace_columns",
11
+ "verb": "ConsoleSettings::Collection.ReplaceColumns",
12
+ "role": "Console"
13
+ },
14
+ {
15
+ "scope": "console_settings:collection.replace_detail_field_columns",
16
+ "verb": "ConsoleSettings::Collection.ReplaceDetailFieldColumns",
17
+ "role": "Console"
18
+ },
19
+ {
20
+ "scope": "console_settings:collection.replace_detail_fields",
21
+ "verb": "ConsoleSettings::Collection.ReplaceDetailFields",
22
+ "role": "Console"
23
+ },
24
+ {
25
+ "scope": "console_settings:collection.replace_field_formats",
26
+ "verb": "ConsoleSettings::Collection.ReplaceFieldFormats",
27
+ "role": "Console"
28
+ },
29
+ {
30
+ "scope": "console_settings:collection.replace_preconditions",
31
+ "verb": "ConsoleSettings::Collection.ReplacePreconditions",
32
+ "role": "Console"
33
+ },
34
+ {
35
+ "scope": "console_settings:collection.set_extra",
36
+ "verb": "ConsoleSettings::Collection.SetExtra",
37
+ "role": "Console"
38
+ },
39
+ {
40
+ "scope": "console_settings:collection.set_identity",
41
+ "verb": "ConsoleSettings::Collection.SetIdentity",
42
+ "role": "Console"
43
+ },
44
+ {
45
+ "scope": "console_settings:collection.set_key",
46
+ "verb": "ConsoleSettings::Collection.SetKey",
47
+ "role": "Console"
48
+ },
49
+ {
50
+ "scope": "console_settings:collection.set_label",
51
+ "verb": "ConsoleSettings::Collection.SetLabel",
52
+ "role": "Console"
53
+ },
54
+ {
55
+ "scope": "console_settings:collection.set_list_query",
56
+ "verb": "ConsoleSettings::Collection.SetListQuery",
57
+ "role": "Console"
58
+ },
59
+ {
60
+ "scope": "console_settings:collection.set_nav_order",
61
+ "verb": "ConsoleSettings::Collection.SetNavOrder",
62
+ "role": "Console"
63
+ },
64
+ {
65
+ "scope": "console_settings:collection.set_primary_field",
66
+ "verb": "ConsoleSettings::Collection.SetPrimaryField",
67
+ "role": "Console"
68
+ },
69
+ {
70
+ "scope": "console_settings:overview.declare",
71
+ "verb": "ConsoleSettings::Overview.Declare",
72
+ "role": "Console"
73
+ },
74
+ {
75
+ "scope": "console_settings:overview.replace_stats",
76
+ "verb": "ConsoleSettings::Overview.ReplaceStats",
77
+ "role": "Console"
78
+ },
79
+ {
80
+ "scope": "console_settings:state_style.declare",
81
+ "verb": "ConsoleSettings::StateStyle.Declare",
82
+ "role": "Console"
83
+ },
84
+ {
85
+ "scope": "console_settings:state_style.set_attention",
86
+ "verb": "ConsoleSettings::StateStyle.SetAttention",
87
+ "role": "Console"
88
+ },
89
+ {
90
+ "scope": "console_settings:state_style.set_extra",
91
+ "verb": "ConsoleSettings::StateStyle.SetExtra",
92
+ "role": "Console"
93
+ },
94
+ {
95
+ "scope": "console_settings:state_style.set_tone",
96
+ "verb": "ConsoleSettings::StateStyle.SetTone",
97
+ "role": "Console"
98
+ }
99
+ ],
100
+ "roles": [
101
+ "Console"
102
+ ]
103
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "audience": "Governance",
3
+ "scopes": [
4
+ {
5
+ "scope": "governance:role_assignment.assign",
6
+ "verb": "Governance::RoleAssignment.Assign",
7
+ "role": "Governance administrator"
8
+ },
9
+ {
10
+ "scope": "governance:role_assignment.revoke",
11
+ "verb": "Governance::RoleAssignment.Revoke",
12
+ "role": "Governance administrator"
13
+ },
14
+ {
15
+ "scope": "governance:role_transition.grant",
16
+ "verb": "Governance::RoleTransition.Grant",
17
+ "role": "Governance administrator"
18
+ },
19
+ {
20
+ "scope": "governance:role_transition.revoke",
21
+ "verb": "Governance::RoleTransition.Revoke",
22
+ "role": "Governance administrator"
23
+ }
24
+ ],
25
+ "roles": [
26
+ "Governance administrator"
27
+ ]
28
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "audience": "Identity",
3
+ "scopes": [
4
+ {
5
+ "scope": "identity:external_identifier.link",
6
+ "verb": "Identity::ExternalIdentifier.Link",
7
+ "role": "Identity registrar"
8
+ },
9
+ {
10
+ "scope": "identity:identity.register",
11
+ "verb": "Identity::Identity.Register",
12
+ "role": "Identity registrar"
13
+ }
14
+ ],
15
+ "roles": [
16
+ "Identity registrar"
17
+ ]
18
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "audience": "Privacy",
3
+ "scopes": [
4
+ {
5
+ "scope": "privacy:marking.mark",
6
+ "verb": "Privacy::Marking.Mark",
7
+ "role": "Deployment"
8
+ },
9
+ {
10
+ "scope": "privacy:marking.unmark",
11
+ "verb": "Privacy::Marking.Unmark",
12
+ "role": "Deployment"
13
+ },
14
+ {
15
+ "scope": "privacy:subject_key.issue",
16
+ "verb": "Privacy::SubjectKey.Issue",
17
+ "role": "System"
18
+ },
19
+ {
20
+ "scope": "privacy:subject_key.shred",
21
+ "verb": "Privacy::SubjectKey.Shred",
22
+ "role": "Privacy officer"
23
+ }
24
+ ],
25
+ "roles": [
26
+ "Deployment",
27
+ "Privacy officer",
28
+ "System"
29
+ ]
30
+ }
@@ -20,8 +20,11 @@
20
20
  # with `only=`: several of them write generated files and compare, so the stage is not meant to
21
21
  # run whole in one checkout.
22
22
  pre_push:
23
+ # HECKS_PROCESS_SPECS keeps the examples tagged `process` (they start child processes, so a plain
24
+ # `rspec` leaves them out) in the suite this stage runs.
23
25
  env:
24
26
  HECKS_ENVIRONMENT: memory
27
+ HECKS_PROCESS_SPECS: "1"
25
28
  checks:
26
29
  - id: rspec
27
30
  title: rspec (parallel)
@@ -94,6 +97,38 @@ unguarded_push:
94
97
  - *check_comment_blocks
95
98
  - *check_fuzzing
96
99
 
100
+ # The Rust io specs `ci-rust-io.yml` runs in its `rspec_rust_io` job, for a push that wants their
101
+ # answer first: `hecks gate_run.gate stage=rust_io --wait`. No hook or workflow runs it. It sets CI
102
+ # as the job does, so a skipped example fails here as it fails there. rust/src/generated is not
103
+ # committed, so the check materializes it first, as the job's generated-rust action does. The specs
104
+ # mutate rust/, and a stage's checks run at once, so the job's two legs run one after the other in
105
+ # a single check. spec/gate_rust_io_stage_spec.rb pins the spec lists to the workflow's matrix.
106
+ rust_io:
107
+ env:
108
+ CI: "true"
109
+ HECKS_ENVIRONMENT: memory
110
+ checks:
111
+ - id: rust_io
112
+ title: rust io specs, conformance, coercion, has_many, semantics corpus and feature exclusivity
113
+ run:
114
+ - sh
115
+ - -ec
116
+ - |
117
+ # Building the banking crate makes banking the default feature and rewrites the module
118
+ # list, both tracked. They are put back on the way out, and the check will not start over
119
+ # a person's uncommitted edits to them.
120
+ rewritten="rust/Cargo.toml rust/src/generated/mod.rs"
121
+ if [ -n "$(git status --porcelain -- $rewritten)" ]; then
122
+ echo "uncommitted changes in $rewritten; commit or discard them first" >&2
123
+ exit 1
124
+ fi
125
+ trap 'git checkout -q -- $rewritten' EXIT
126
+ env -u CARGO_TARGET_DIR bundle exec exe/hecks regeneration_run.regenerate_corpus --confirm --wait
127
+ bundle exec exe/hecks build.project_rust domain=examples/banking --wait
128
+ bundle exec rspec --tag io --profile 8 spec/rust_conformance_spec.rb spec/rust_conformance_fuzz_spec.rb spec/rust_numeric_coercion_spec.rb spec/rust_generated/has_many_spec.rb spec/semantics_corpus_spec.rb
129
+ bundle exec rspec --tag io --profile 8 spec/rust_generated/domain_feature_exclusivity_spec.rb
130
+ blocked: a Rust io spec failed, or skipped; the crate it built is under rust/target.
131
+
97
132
  # The checks `.github/workflows/ci-checks.yml` runs. It sets no `env`: the job's journal choice
98
133
  # (the `hecks-environment` action) reaches every check, as it reached each command before.
99
134
  ci:
@@ -0,0 +1,48 @@
1
+ {
2
+ "audience": "Translation",
3
+ "scopes": [
4
+ {
5
+ "scope": "translation:map.declare",
6
+ "verb": "Translation::Map.Declare",
7
+ "role": "Author"
8
+ },
9
+ {
10
+ "scope": "translation:map.draft",
11
+ "verb": "Translation::Map.Draft",
12
+ "role": "Author"
13
+ },
14
+ {
15
+ "scope": "translation:map.seal",
16
+ "verb": "Translation::Map.Seal",
17
+ "role": "Author"
18
+ },
19
+ {
20
+ "scope": "translation:map.supersede",
21
+ "verb": "Translation::Map.Supersede",
22
+ "role": "Author"
23
+ },
24
+ {
25
+ "scope": "translation:rule.admit",
26
+ "verb": "Translation::Rule.Admit",
27
+ "role": "Author"
28
+ },
29
+ {
30
+ "scope": "translation:rule.execute",
31
+ "verb": "Translation::Rule.Execute",
32
+ "role": "Author"
33
+ },
34
+ {
35
+ "scope": "translation:rule.propose",
36
+ "verb": "Translation::Rule.Propose",
37
+ "role": "Author"
38
+ },
39
+ {
40
+ "scope": "translation:rule.retire",
41
+ "verb": "Translation::Rule.Retire",
42
+ "role": "Author"
43
+ }
44
+ ],
45
+ "roles": [
46
+ "Author"
47
+ ]
48
+ }
@@ -45,12 +45,23 @@ module Hecks
45
45
  # What the runner printed for the example, when it passed.
46
46
  def run_example(path, example)
47
47
  out = StringIO.new
48
- status = Dir.chdir(@tree.root) { runner.call(["--example", example, path], out, out) }
48
+ status = Dir.chdir(@tree.root) { at_reaction_depth_zero { runner.call(["--example", example, path], out, out) } }
49
49
  raise ConsoleCapture::Failure, out.string.strip unless status.zero?
50
50
 
51
51
  out.string.strip
52
52
  end
53
53
 
54
+ # The runner is asked for from inside a command's own reaction cascade, and the dispatcher
55
+ # keeps that cascade's depth on the thread. An example starts at depth zero, as a plain run
56
+ # does, so a spec that cascades four deep is not cut off by the cascade that launched it.
57
+ def at_reaction_depth_zero
58
+ depth = Thread.current[:hecks_reaction_depth]
59
+ Thread.current[:hecks_reaction_depth] = 0
60
+ yield
61
+ ensure
62
+ Thread.current[:hecks_reaction_depth] = depth
63
+ end
64
+
54
65
  def runner
55
66
  return self.class.runner if self.class.runner
56
67
 
@@ -74,7 +74,8 @@ module Hecks
74
74
  { report: { value: child(:lint, paths).dig(:output, :value) } }
75
75
  end
76
76
 
77
- # Projects each domain's OIDC client and scope manifest into its `oidc.json`, through
77
+ # Projects each domain's OIDC client and scope manifest into its `oidc.json`, or into one
78
+ # `<chapter>.oidc.json` for each chapter of a directory that holds several, through
78
79
  # `hecks deploy oidc_manifest.project_oidc`.
79
80
  #
80
81
  # @param held [Hash] the `OidcManifest` record: `domains` (comma separated directories; every
@@ -119,9 +119,13 @@ module Hecks
119
119
  ["pg_restore", "--no-owner", "--no-privileges", "-d", database_name, file]
120
120
  end
121
121
 
122
+ # What the URL names, and nothing it leaves out: `postgres:///postgres` parses with an empty
123
+ # host, which would override a `PGHOST` the environment sets and send the restore to the
124
+ # local socket while the rest of the rehearsal reached the server by TCP.
122
125
  def restore_env
123
126
  uri = URI.parse(server_url)
124
- { "PGHOST" => uri.host, "PGPORT" => uri.port&.to_s, "PGUSER" => uri.user, "PGPASSWORD" => uri.password }.compact
127
+ { "PGHOST" => uri.host, "PGPORT" => uri.port&.to_s, "PGUSER" => uri.user, "PGPASSWORD" => uri.password }
128
+ .reject { |_name, value| value.to_s.empty? }
125
129
  end
126
130
  end
127
131
  end
@@ -36,6 +36,8 @@
36
36
  # checkout runs (Codebase, and releasing hecks itself): the help leaves them out anywhere else and
37
37
  # `--maintainer` lists them; `chapters` names the attached chapters the help points at, one line each
38
38
  # (`hecks deploy`), and `maintainer_chapters` the language's own, added inside a checkout.
39
+ # `newcomer` names the only commands the help lists in a folder holding no bluebook yet
40
+ # (`hecks --all` lists the rest), so a first run shows how to start and nothing else.
39
41
  Hecks.world "Hecks" do
40
42
  realm "Hecks"
41
43
  default_adapter "PostgresEra"
@@ -58,6 +60,7 @@ Hecks.world "Hecks" do
58
60
  maintainer: %w[LanguageRun KernelRun ConformanceRun RegenerationRun GateRun StyleRun InspectionRun
59
61
  CodemodRun TestSuiteRun CorpusRun PublishingRun PromotionRun Release
60
62
  SyntaxBootCache],
63
+ newcomer: %w[launch.init launch.interview operation.open_console],
61
64
  chapters: %w[Deploy Governance Tenancy Site Tickets QualityControl],
62
65
  maintainer_chapters: %w[Bluebook Hecksagon World Adapter Port Translation Expression],
63
66
  memory_commands: %w[console init interview],
@@ -0,0 +1,23 @@
1
+ Hecks.behaviors "Bluebook" do
2
+ vision "A chapter is opened with a name, a vision and a classification, and refuses a classification the language does not admit."
3
+
4
+ loads "bluebook.bluebook", "bluebook.hecksagon"
5
+
6
+ test "Declare opens a chapter" do
7
+ tests "Declare", on: "Bluebook"
8
+ input name: "Pizzas", vision: "Pizzas, built and sold.", classification: "core"
9
+ expect emits: ["ChapterDeclared"]
10
+ end
11
+
12
+ test "Declare refuses a classification that is not core, supporting or generic" do
13
+ tests "Declare", on: "Bluebook"
14
+ input name: "Pizzas", vision: "Pizzas, built and sold.", classification: "huge"
15
+ expect refused: "a chapter is core, supporting, or generic"
16
+ end
17
+
18
+ test "Declare refuses a chapter with no name" do
19
+ tests "Declare", on: "Bluebook"
20
+ input name: "", vision: "Pizzas, built and sold.", classification: "core"
21
+ expect refused: "a chapter is named"
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ Hecks.behaviors "Bluebook" do
2
+ vision "A policy hangs off a chapter and names the event it watches; an unnamed policy is refused."
3
+
4
+ loads "bluebook.bluebook", "policy.bluebook", "bluebook.hecksagon"
5
+
6
+ test "Declare puts a policy on a chapter" do
7
+ tests "Declare", on: "Policy"
8
+ setup "Bluebook::Bluebook.Declare", name: "Pizzas", vision: "Pizzas, built and sold.", classification: "core"
9
+ input bluebook: "Pizzas", name: "NotifyKitchen", on_event: "OrderPlaced"
10
+ expect emits: ["ReactionDeclared"]
11
+ end
12
+
13
+ test "Declare refuses a policy with no name" do
14
+ tests "Declare", on: "Policy"
15
+ setup "Bluebook::Bluebook.Declare", name: "Pizzas", vision: "Pizzas, built and sold.", classification: "core"
16
+ input bluebook: "Pizzas", name: "", on_event: "OrderPlaced"
17
+ expect refused: "a policy is named"
18
+ end
19
+ end