hecks 2.8.0 → 2.9.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 (404) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +4 -6
  3. data/lib/hecks/adapters/driven/claude_code.rb +4 -104
  4. data/lib/hecks/adapters/driven/d1.rb +44 -237
  5. data/lib/hecks/adapters/driven/folder.rb +17 -76
  6. data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
  7. data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
  8. data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
  9. data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
  10. data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
  11. data/lib/hecks/adapters/driven/heki.rb +13 -42
  12. data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
  15. data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
  16. data/lib/hecks/adapters/driven/lambda.rb +10 -84
  17. data/lib/hecks/adapters/driven/local_storage.rb +5 -40
  18. data/lib/hecks/adapters/driven/memory.rb +17 -98
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres.rb +145 -134
  25. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  26. data/lib/hecks/adapters/driven/prism.rb +7 -30
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  31. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  32. data/lib/hecks/adapters/driven/tenant_provisioner.rb +5 -48
  33. data/lib/hecks/adapters/driven.rb +5 -36
  34. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  35. data/lib/hecks/behaviors/dsl.rb +7 -25
  36. data/lib/hecks/behaviors/expectations.rb +31 -141
  37. data/lib/hecks/behaviors/ir.rb +7 -20
  38. data/lib/hecks/behaviors/rspec.rb +6 -17
  39. data/lib/hecks/behaviors/runner.rb +10 -34
  40. data/lib/hecks/behaviors.rb +4 -8
  41. data/lib/hecks/bluebook/aggregate.rb +9 -75
  42. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  43. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  44. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  45. data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
  46. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  47. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  48. data/lib/hecks/bluebook/assembly.rb +6 -45
  49. data/lib/hecks/bluebook/attribute.rb +12 -61
  50. data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
  51. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  52. data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
  53. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  54. data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
  55. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  56. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  57. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  58. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  59. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  60. data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
  61. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  62. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  63. data/lib/hecks/bluebook/capabilities.rb +17 -49
  64. data/lib/hecks/bluebook/chapter.rb +10 -48
  65. data/lib/hecks/bluebook/command.rb +23 -81
  66. data/lib/hecks/bluebook/domain_port.rb +14 -63
  67. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  68. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  69. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  70. data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
  71. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
  72. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
  73. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
  74. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  75. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  76. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
  77. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  78. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  79. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -194
  80. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  81. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  82. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  83. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  84. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  85. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  86. data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
  87. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  88. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  89. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  90. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  91. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  92. data/lib/hecks/bluebook/dsl/world_builder.rb +19 -111
  93. data/lib/hecks/bluebook/entity.rb +13 -71
  94. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  95. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  96. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  97. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  98. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  99. data/lib/hecks/bluebook/expression/resolver.rb +37 -660
  100. data/lib/hecks/bluebook/hexagon.rb +5 -28
  101. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  102. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  103. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  104. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  105. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  106. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  107. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
  108. data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
  109. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +43 -305
  110. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  111. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  112. data/lib/hecks/bluebook/meta_validator.rb +79 -349
  113. data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
  114. data/lib/hecks/bluebook/model_check.rb +100 -597
  115. data/lib/hecks/bluebook/pattern_subset.rb +11 -69
  116. data/lib/hecks/bluebook/process_manager.rb +13 -83
  117. data/lib/hecks/bluebook/project_loader.rb +3 -39
  118. data/lib/hecks/bluebook/project_register.rb +12 -60
  119. data/lib/hecks/bluebook/query.rb +9 -48
  120. data/lib/hecks/bluebook/read_model.rb +57 -81
  121. data/lib/hecks/bluebook/reference.rb +7 -41
  122. data/lib/hecks/bluebook/smoke_test.rb +8 -98
  123. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  124. data/lib/hecks/bluebook/translation.rb +26 -62
  125. data/lib/hecks/bluebook/value_object.rb +11 -36
  126. data/lib/hecks/bluebook.rb +5 -22
  127. data/lib/hecks/cache_dir.rb +4 -23
  128. data/lib/hecks/cli/document.rb +3 -21
  129. data/lib/hecks/cli/ir.rb +3 -5
  130. data/lib/hecks/cli/mcp.rb +2 -3
  131. data/lib/hecks/cli/mcp_door.rb +19 -114
  132. data/lib/hecks/cli/model_check.rb +18 -85
  133. data/lib/hecks/cli/project_cli.rb +7 -40
  134. data/lib/hecks/cli/project_diagrams.rb +0 -7
  135. data/lib/hecks/cli/run.rb +12 -58
  136. data/lib/hecks/cli/smoke_test.rb +2 -16
  137. data/lib/hecks/cli/stores.rb +1 -2
  138. data/lib/hecks/cli.rb +18 -24
  139. data/lib/hecks/construct.rb +8 -34
  140. data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
  141. data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
  142. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  143. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  144. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  145. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  146. data/lib/hecks/embryonaut_bluebook.rb +4 -71
  147. data/lib/hecks/facade/cli_door.rb +24 -106
  148. data/lib/hecks/facade/cli_runner.rb +28 -172
  149. data/lib/hecks/facade/command_request.rb +13 -33
  150. data/lib/hecks/facade/handle.rb +34 -121
  151. data/lib/hecks/facade/json_door.rb +31 -146
  152. data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
  153. data/lib/hecks/facade/surface/chapter.rb +10 -54
  154. data/lib/hecks/facade/surface.rb +8 -23
  155. data/lib/hecks/facade.rb +10 -25
  156. data/lib/hecks/forms/app.rb +15 -70
  157. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  158. data/lib/hecks/forms/field_renderer.rb +17 -156
  159. data/lib/hecks/forms/field_shape.rb +30 -222
  160. data/lib/hecks/forms/html.rb +12 -47
  161. data/lib/hecks/forms/index_renderer.rb +1 -4
  162. data/lib/hecks/forms/page.rb +3 -9
  163. data/lib/hecks/forms/params.rb +14 -60
  164. data/lib/hecks/forms/port_argument.rb +2 -19
  165. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  166. data/lib/hecks/forms/record_renderer.rb +8 -75
  167. data/lib/hecks/forms/record_table.rb +7 -50
  168. data/lib/hecks/forms/reference_options.rb +3 -30
  169. data/lib/hecks/forms/value_object_shape.rb +8 -35
  170. data/lib/hecks/forms.rb +5 -33
  171. data/lib/hecks/fqn.rb +5 -12
  172. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  173. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  174. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  175. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  176. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  177. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  178. data/lib/hecks/framework.rb +11 -80
  179. data/lib/hecks/freezer.rb +5 -39
  180. data/lib/hecks/grammar/expression.bluebook +15 -40
  181. data/lib/hecks/grammar/translation.bluebook +3 -6
  182. data/lib/hecks/grammar.rb +16 -41
  183. data/lib/hecks/ir.rb +14 -83
  184. data/lib/hecks/language/adapter.bluebook +2 -7
  185. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  186. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  187. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
  188. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  189. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  190. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  191. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  192. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  193. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  194. data/lib/hecks/language/bluebook/query.bluebook +12 -43
  195. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  196. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  197. data/lib/hecks/language/bluebook/vocabulary.bluebook +82 -197
  198. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  199. data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
  200. data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
  201. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  202. data/lib/hecks/language/oidc.json +35 -0
  203. data/lib/hecks/language/port.bluebook +4 -17
  204. data/lib/hecks/language/translation/translation.bluebook +3 -9
  205. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  206. data/lib/hecks/language/world/wiring.bluebook +1 -3
  207. data/lib/hecks/language/world/world.bluebook +5 -7
  208. data/lib/hecks/literal.rb +8 -50
  209. data/lib/hecks/mcp_door_scope.rb +9 -90
  210. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  211. data/lib/hecks/naming.rb +29 -137
  212. data/lib/hecks/ports/access_control.rb +7 -23
  213. data/lib/hecks/ports/agent/answers.rb +5 -23
  214. data/lib/hecks/ports/agent.rb +47 -172
  215. data/lib/hecks/ports/authentication.rb +12 -39
  216. data/lib/hecks/ports/authorization.rb +16 -43
  217. data/lib/hecks/ports/clock.rb +5 -45
  218. data/lib/hecks/ports/extraction.rb +7 -12
  219. data/lib/hecks/ports/identity_assignment.rb +8 -29
  220. data/lib/hecks/ports/identity_generation.rb +6 -29
  221. data/lib/hecks/ports/identity_resolution.rb +2 -10
  222. data/lib/hecks/ports/key_vault.rb +9 -45
  223. data/lib/hecks/ports/persistence/append_only.rb +61 -64
  224. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  225. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  226. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  227. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  228. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
  229. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  230. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
  231. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
  232. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
  233. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
  250. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  251. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  252. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  253. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  254. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  255. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  256. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
  257. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
  258. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  259. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  260. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  261. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  262. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  263. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  264. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  265. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  266. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  267. data/lib/hecks/ports/persistence/state_codec.rb +23 -103
  268. data/lib/hecks/ports/persistence.rb +7 -14
  269. data/lib/hecks/ports/projection.rb +42 -84
  270. data/lib/hecks/ports/query/in_memory.rb +10 -58
  271. data/lib/hecks/ports/query/ordering.rb +11 -32
  272. data/lib/hecks/ports/query.rb +14 -37
  273. data/lib/hecks/ports.rb +2 -8
  274. data/lib/hecks/projections/bootstrap_table.rb +11 -43
  275. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  276. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  277. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  278. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  279. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  280. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  281. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  282. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  283. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  284. data/lib/hecks/projections/deploy/fargate.rb +35 -202
  285. data/lib/hecks/projections/deploy/lambda.rb +150 -678
  286. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  287. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  288. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  289. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  290. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  291. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  292. data/lib/hecks/projections/deploy/preview/template.rb +2 -58
  293. data/lib/hecks/projections/deploy/preview.rb +2 -67
  294. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  295. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  296. data/lib/hecks/projections/deploy/shared.rb +47 -146
  297. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  298. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  299. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  300. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  301. data/lib/hecks/projections/diagrams.rb +20 -582
  302. data/lib/hecks/projections/glossary/html.rb +14 -130
  303. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  304. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  305. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  306. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  307. data/lib/hecks/projections/glossary.rb +30 -222
  308. data/lib/hecks/projections/ir.rb +3 -10
  309. data/lib/hecks/projections/model/deviations.rb +15 -42
  310. data/lib/hecks/projections/model.rb +12 -51
  311. data/lib/hecks/projections/oidc.rb +12 -56
  312. data/lib/hecks/projections/parser_table.rb +9 -28
  313. data/lib/hecks/projections/reference.rb +2 -15
  314. data/lib/hecks/projections/rust_vocabulary.rb +12 -250
  315. data/lib/hecks/projections/shape.rb +3 -29
  316. data/lib/hecks/projections/statements.rb +7 -86
  317. data/lib/hecks/projections/vocabulary.rb +6 -22
  318. data/lib/hecks/projections.rb +2 -16
  319. data/lib/hecks/projector/cli_projector.rb +60 -375
  320. data/lib/hecks/projector/docs_projector.rb +10 -201
  321. data/lib/hecks/projector/exporter.rb +42 -134
  322. data/lib/hecks/projector/ir_projector.rb +2 -9
  323. data/lib/hecks/projector/narrate_projector.rb +15 -189
  324. data/lib/hecks/projector/target.rb +13 -76
  325. data/lib/hecks/projector.rb +30 -121
  326. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  327. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  328. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  329. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  330. data/lib/hecks/query_specification/common/options.rb +2 -6
  331. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  332. data/lib/hecks/query_specification/field_path.rb +37 -77
  333. data/lib/hecks/query_specification/hop_path.rb +12 -76
  334. data/lib/hecks/release/gem_pin.rb +2 -26
  335. data/lib/hecks/release/runner/ci_publisher.rb +3 -6
  336. data/lib/hecks/release/runner/commands.rb +2 -4
  337. data/lib/hecks/release/runner/gem_publisher.rb +2 -3
  338. data/lib/hecks/release/runner/npm_publisher.rb +6 -20
  339. data/lib/hecks/release/runner/preflight.rb +4 -19
  340. data/lib/hecks/release/runner/tagger.rb +2 -9
  341. data/lib/hecks/release/runner.rb +3 -32
  342. data/lib/hecks/rendering.rb +4 -26
  343. data/lib/hecks/router/namespace_installer.rb +12 -36
  344. data/lib/hecks/router.rb +4 -8
  345. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  346. data/lib/hecks/runtime/boot_gates.rb +3 -18
  347. data/lib/hecks/runtime/caller.rb +19 -59
  348. data/lib/hecks/runtime/capability_graph.rb +5 -21
  349. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  350. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
  351. data/lib/hecks/runtime/command_interpreter.rb +124 -306
  352. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  353. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  354. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  355. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  356. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  357. data/lib/hecks/runtime/dependency_planning.rb +30 -102
  358. data/lib/hecks/runtime/dispatcher.rb +82 -241
  359. data/lib/hecks/runtime/entity_element.rb +40 -462
  360. data/lib/hecks/runtime/entity_interpreter.rb +64 -219
  361. data/lib/hecks/runtime/errors.rb +32 -115
  362. data/lib/hecks/runtime/event.rb +3 -25
  363. data/lib/hecks/runtime/identity.rb +18 -154
  364. data/lib/hecks/runtime/instance.rb +18 -150
  365. data/lib/hecks/runtime/interpreting.rb +11 -70
  366. data/lib/hecks/runtime/invocation.rb +28 -164
  367. data/lib/hecks/runtime/loader.rb +48 -186
  368. data/lib/hecks/runtime/outbox.rb +38 -261
  369. data/lib/hecks/runtime/policy_interpreter.rb +38 -200
  370. data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
  371. data/lib/hecks/runtime/query_interpreter.rb +45 -187
  372. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  373. data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
  374. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  375. data/lib/hecks/runtime/reference_hop.rb +8 -81
  376. data/lib/hecks/runtime/refusal_wording.rb +8 -89
  377. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  378. data/lib/hecks/runtime/registry/verification.rb +52 -225
  379. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  380. data/lib/hecks/runtime/registry.rb +99 -244
  381. data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
  382. data/lib/hecks/runtime/routing.rb +4 -15
  383. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  384. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  385. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  386. data/lib/hecks/runtime/tenant_check.rb +6 -68
  387. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  388. data/lib/hecks/runtime/value/admission.rb +16 -67
  389. data/lib/hecks/runtime/value/coercion.rb +155 -641
  390. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  391. data/lib/hecks/runtime/value.rb +12 -84
  392. data/lib/hecks/runtime.rb +17 -65
  393. data/lib/hecks/storehouse.rb +141 -568
  394. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
  395. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  396. data/lib/hecks/vendoring/git_environment.rb +2 -8
  397. data/lib/hecks/vendoring/git_source.rb +4 -10
  398. data/lib/hecks/vendoring.rb +7 -46
  399. data/lib/hecks/version.rb +3 -14
  400. data/lib/hecks.rb +26 -81
  401. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  402. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  403. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  404. metadata +3 -2
@@ -4,29 +4,16 @@ module Hecks
4
4
  module Projections
5
5
  module Deploy
6
6
  module Preview
7
- # The secrets and IAM roles of a preview stack.
8
- #
9
- # The task role reads the shared database secret and this preview's own
10
- # secrets and nothing owned by the main stack. The execution role also reads
11
- # the database secret, only so the one-shot task can receive the credentials
12
- # as container secrets.
7
+ # The secrets and IAM roles of a preview stack, scoped away from the main stack.
13
8
  module AccessYaml
14
9
  extend YamlText
15
10
 
16
11
  module_function
17
12
 
18
- # Renders the secrets a preview generates: its session secret and the ones
19
- # containers declare.
20
- #
21
- # @param settings [Settings] the resolved preview settings
22
- # @return [Array<String>] one YAML resource block per secret
23
13
  def secrets(settings)
24
14
  [session_secret, *settings.containers.flat_map { |c| container_secrets(c) }]
25
15
  end
26
16
 
27
- # Renders the per-branch session secret the host reads at start.
28
- #
29
- # @return [String] the secret's resource block
30
17
  def session_secret
31
18
  <<~YAML.chomp
32
19
  # No explicit Name: Secrets Manager keeps a deleted secret through its recovery window,
@@ -43,10 +30,6 @@ module Hecks
43
30
  YAML
44
31
  end
45
32
 
46
- # Renders one generated secret per name a container declares.
47
- #
48
- # @param container [Containers::Entry] the container
49
- # @return [Array<String>] one resource block per declared secret name
50
33
  def container_secrets(container)
51
34
  container.secrets.map do |name|
52
35
  <<~YAML.chomp
@@ -63,10 +46,6 @@ module Hecks
63
46
  end
64
47
  end
65
48
 
66
- # Renders the task execution role and the task role.
67
- #
68
- # @param settings [Settings] the resolved preview settings
69
- # @return [Array<String>] the two role resource blocks
70
49
  def roles(settings)
71
50
  assume = <<~YAML.chomp
72
51
  AssumeRolePolicyDocument:
@@ -79,11 +58,6 @@ module Hecks
79
58
  [execution_role(assume), task_role(assume, settings)]
80
59
  end
81
60
 
82
- # Renders the execution role, which pulls images and hands the one-shot task
83
- # its credentials.
84
- #
85
- # @param assume [String] the shared trust policy block
86
- # @return [String] the role's resource block
87
61
  def execution_role(assume)
88
62
  <<~YAML.chomp
89
63
  # The execution role reads the database secret only so the one-shot task can be given
@@ -105,12 +79,6 @@ module Hecks
105
79
  YAML
106
80
  end
107
81
 
108
- # Renders the task role: read access to the shared database secret and this
109
- # preview's own secrets.
110
- #
111
- # @param assume [String] the shared trust policy block
112
- # @param settings [Settings] the resolved preview settings
113
- # @return [String] the role's resource block
114
82
  def task_role(assume, settings)
115
83
  readable = ["SessionSecret", *settings.containers.flat_map { |c| c.secrets.map { |n| secret_id(c, n) } }]
116
84
  <<~YAML.chomp
@@ -2,54 +2,15 @@ module Hecks
2
2
  module Projections
3
3
  module Deploy
4
4
  module Preview
5
- # The containers one preview task runs, in one normalized shape.
6
- #
7
- # A preview task runs the same containers as the stack it previews.
8
- # This module is the single place that decides which containers those
9
- # are, so the rest of `Preview` never asks where the list came from.
10
- #
11
- # ## One adapter to the shared setting
12
- #
13
- # The main Fargate stack is growing a multi-container setting.
14
- # `shared_setting` is the only method that reads it; every other
15
- # method here works on the normalized entries described below. When
16
- # that setting lands with a different shape, translate it inside
17
- # `shared_setting` and nothing else changes.
18
- #
19
- # ## Entry keys
20
- #
21
- # Each entry is a Hash with Symbol keys:
22
- #
23
- # - `name` (required): lowercase letters, digits and hyphens; also the
24
- # container name and the suffix of its ECR repository.
25
- # - `port` (required): the port the container listens on. Unique
26
- # across the task, since all containers share one network namespace.
27
- # - `health_check_path`: target group health check path, default `"/"`.
28
- # - `paths`: load balancer path patterns forwarded to this container.
29
- # - `default`: `true` sends every unmatched request here. When no
30
- # entry says so, the `host` entry is the default, else the first.
31
- # - `host`: `true` marks the Hecks host container. It gets the domain,
32
- # session and database environment, and the signup path.
33
- # - `database`: `true` gives a non-host container the database
34
- # environment as well. The host always has it.
35
- # - `environment`: extra environment, name to value. `{{preview_url}}`
36
- # inside a value becomes the preview's public https URL.
37
- # - `secrets`: environment variable names. Each becomes a per-branch
38
- # generated secret whose ARN is that variable's value.
39
- # - `image`: the local docker image `preview.sh` pushes for this
40
- # container, default `"<name>:latest"`.
5
+ # The containers one preview task runs, normalized via `resolve`; `shared_setting`
6
+ # is the one hook into the main stack's own (still-growing) multi-container setting.
41
7
  module Containers
42
8
  # One normalized container of the preview task.
43
9
  Entry = Data.define(:name, :port, :health_check_path, :paths, :default, :host, :database,
44
10
  :environment, :secrets, :image) do
45
- # Answers the CloudFormation logical-id fragment for this container.
46
- #
47
- # @return [String] `name` in CamelCase, such as `"MySite"` for `"my-site"`
11
+ # CamelCases `name`, e.g. `"my-site"` becomes `"MySite"`.
48
12
  def logical = name.split("-").map(&:capitalize).join
49
13
 
50
- # Answers whether the container is reachable through the load balancer.
51
- #
52
- # @return [Boolean] true when it is the default or owns any path
53
14
  def routed? = default || !paths.empty?
54
15
  end
55
16
 
@@ -60,17 +21,8 @@ module Hecks
60
21
 
61
22
  module_function
62
23
 
63
- # Answers the preview task's containers, normalized and validated.
64
- #
65
- # @param preview [Hash{Symbol => Object}] the preview settings; `:containers` wins
66
- # @param deploy_settings [Hash{Symbol => Object}] the `deployed_to` settings, read
67
- # by `shared_setting`
68
- # @param main [Hash{Symbol => Object}] the main stack's own facts; `:name`, `:port`,
69
- # `:image` describe the single container used when neither list is given
70
- # @param signup_path [String, nil] path forwarded to the host container, or nil
71
- # @return [Array<Entry>] at least one entry, exactly one of them the default
72
- # @raise [ArgumentError] if an entry is malformed, a name or port repeats, or two
73
- # entries claim to be the default or the host
24
+ # `preview[:containers]` wins over the main stack's own multi-container setting,
25
+ # which wins over a single entry built from the main stack's own container.
74
26
  def resolve(preview:, deploy_settings:, main:, signup_path: nil)
75
27
  raw = preview[:containers] || shared_setting(deploy_settings) || [single(main)]
76
28
  entries = Array(raw).map { |item| normalize(item) }
@@ -78,31 +30,15 @@ module Hecks
78
30
  with_default(with_signup(entries, signup_path))
79
31
  end
80
32
 
81
- # Reads the main stack's own container list, when it declares one.
82
- #
83
- # This is the one hook to the multi-container setting shared with the
84
- # main template; it answers entries in the shape documented above.
85
- #
86
- # @param deploy_settings [Hash{Symbol => Object}] the `deployed_to` settings
87
- # @return [Array<Hash>, nil] the list, or nil when the stack runs one container
88
33
  def shared_setting(deploy_settings)
89
34
  deploy_settings[:containers]
90
35
  end
91
36
 
92
- # Builds the single-container entry a one-container stack previews as.
93
- #
94
- # @param main [Hash{Symbol => Object}] the main stack's facts, see `resolve`
95
- # @return [Hash{Symbol => Object}] the entry, always the host and the default
96
37
  def single(main)
97
38
  name = main.fetch(:name).to_s.downcase.gsub(/[^a-z0-9]+/, "-").sub(/\A[^a-z]+/, "")[0, 30].sub(/-+\z/, "")
98
39
  { name: name, port: main.fetch(:port), host: true, default: true, image: main[:image] }
99
40
  end
100
41
 
101
- # Turns one raw entry into an `Entry`, filling every default.
102
- #
103
- # @param item [Hash{Symbol => Object}] the raw entry, string keys accepted
104
- # @return [Entry] the normalized entry
105
- # @raise [ArgumentError] if the entry is not a Hash or lacks a valid `name` or `port`
106
42
  def normalize(item)
107
43
  raise ArgumentError, "preview container #{item.inspect} must be a Hash" unless item.is_a?(Hash)
108
44
 
@@ -117,13 +53,6 @@ module Hecks
117
53
  )
118
54
  end
119
55
 
120
- # Answers a value as a String, refusing one that does not match a pattern.
121
- #
122
- # @param value [Object] the raw value
123
- # @param pattern [Regexp] what the string must match
124
- # @param label [String] what the value is, for the message
125
- # @return [String] the value as a String
126
- # @raise [ArgumentError] if the string does not match
127
56
  def matching(value, pattern, label)
128
57
  text = value.to_s
129
58
  raise ArgumentError, "#{label} #{text.inspect} must match #{pattern.inspect}" unless text.match?(pattern)
@@ -131,19 +60,8 @@ module Hecks
131
60
  text
132
61
  end
133
62
 
134
- # Answers one path pattern, refusing one the template could not quote safely.
135
- #
136
- # @param path [Object] the raw path
137
- # @return [String] the path
138
- # @raise [ArgumentError] if it does not start with `/` or holds an unexpected character
139
63
  def path_of(path) = matching(path, PATH_PATTERN, "preview container path")
140
64
 
141
- # Reads and range-checks one entry's port.
142
- #
143
- # @param item [Hash{Symbol => Object}] the raw entry
144
- # @param name [String] the entry's name, for the message
145
- # @return [Integer] the port
146
- # @raise [ArgumentError] if the port is missing or outside 1..65535
147
65
  def port_of(item, name)
148
66
  port = Integer(item[:port], exception: false)
149
67
  raise ArgumentError, "preview container #{name} needs a port between 1 and 65535" unless port&.between?(1, 65_535)
@@ -151,20 +69,10 @@ module Hecks
151
69
  port
152
70
  end
153
71
 
154
- # Stringifies an entry's environment, keeping declaration order.
155
- #
156
- # @param environment [Hash{Symbol, String => Object}, nil] the raw environment
157
- # @return [Hash{String => String}] name to value
158
72
  def environment_of(environment)
159
73
  (environment || {}).to_h { |name, value| [name.to_s, value.to_s] }
160
74
  end
161
75
 
162
- # Refuses a list that cannot be one task.
163
- #
164
- # @param entries [Array<Entry>] the normalized entries
165
- # @return [void]
166
- # @raise [ArgumentError] on an empty list, a repeated name or port, more than one
167
- # default or host, or a secret name that is not an environment variable name
168
76
  def check!(entries)
169
77
  raise ArgumentError, "preview containers must not be empty" if entries.empty?
170
78
 
@@ -175,11 +83,6 @@ module Hecks
175
83
  check_roles!(entries)
176
84
  end
177
85
 
178
- # Refuses a second default or host, and a secret that is not an environment variable name.
179
- #
180
- # @param entries [Array<Entry>] the normalized entries
181
- # @return [void]
182
- # @raise [ArgumentError] on a second default or host, or a malformed secret name
183
86
  def check_roles!(entries)
184
87
  raise ArgumentError, "at most one preview container may be the default" if entries.count(&:default) > 1
185
88
  raise ArgumentError, "at most one preview container may be the host" if entries.count(&:host) > 1
@@ -188,11 +91,7 @@ module Hecks
188
91
  raise ArgumentError, "preview secret name #{bad.inspect} must be an environment variable name" if bad
189
92
  end
190
93
 
191
- # Adds the signup path to the host container's own paths.
192
- #
193
- # @param entries [Array<Entry>] the normalized entries
194
- # @param signup_path [String, nil] the path, or nil for none
195
- # @return [Array<Entry>] the entries, the host's paths extended when it needs a rule
94
+ # Skipped when the host is already the default: it gets unmatched traffic anyway.
196
95
  def with_signup(entries, signup_path)
197
96
  return entries unless signup_path
198
97
 
@@ -201,10 +100,6 @@ module Hecks
201
100
  end
202
101
  end
203
102
 
204
- # Marks the default container when no entry does.
205
- #
206
- # @param entries [Array<Entry>] the normalized entries
207
- # @return [Array<Entry>] the entries with exactly one default
208
103
  def with_default(entries)
209
104
  return entries if entries.any?(&:default)
210
105
 
@@ -5,11 +5,8 @@ module Hecks
5
5
  module Projections
6
6
  module Deploy
7
7
  module Preview
8
- # The network edge of a preview stack: security groups, target groups, the
9
- # load balancer, its listener rules and the service that registers the containers.
10
- #
11
- # A container is reachable when it is the default or owns a path. Paths are
12
- # split into rules of at most five, the most a load balancer condition holds.
8
+ # The network edge of a preview stack: security groups, load balancer and service.
9
+ # Paths are sliced into rules of at most five, the most a condition holds.
13
10
  module RoutingYaml
14
11
  CLOUDFRONT_PREFIX_LIST = "pl-3b927c52".freeze
15
12
  PATHS_PER_RULE = 5
@@ -22,10 +19,6 @@ module Hecks
22
19
 
23
20
  module_function
24
21
 
25
- # Renders the load balancer's security group and one ingress rule per routed container.
26
- #
27
- # @param settings [Settings] the resolved preview settings
28
- # @return [Array<String>] the resource blocks
29
22
  def networking(settings)
30
23
  ingress = settings.containers.select(&:routed?).map do |c|
31
24
  <<~YAML.chomp
@@ -42,10 +35,6 @@ module Hecks
42
35
  [alb_security_group(settings), "#{INGRESS_NOTE}\n#{ingress.join("\n\n")}"]
43
36
  end
44
37
 
45
- # Renders the load balancer's security group, open to CloudFront's prefix list only.
46
- #
47
- # @param settings [Settings] the resolved preview settings
48
- # @return [String] the resource block
49
38
  def alb_security_group(settings)
50
39
  <<~YAML.chomp
51
40
  AlbSecurityGroup:
@@ -62,19 +51,11 @@ module Hecks
62
51
  YAML
63
52
  end
64
53
 
65
- # Renders the target groups, the load balancer, its listener and the path rules.
66
- #
67
- # @param settings [Settings] the resolved preview settings
68
- # @return [Array<String>] the resource blocks
69
54
  def load_balancing(settings)
70
55
  routed = settings.containers.select(&:routed?)
71
56
  [*routed.map { |c| target_group(c) }, alb(settings), listener(settings), *listener_rules(settings)]
72
57
  end
73
58
 
74
- # Renders the target group of one routed container.
75
- #
76
- # @param container [Containers::Entry] the container
77
- # @return [String] the resource block
78
59
  def target_group(container)
79
60
  <<~YAML.chomp
80
61
  #{container.logical}TargetGroup:
@@ -89,10 +70,6 @@ module Hecks
89
70
  YAML
90
71
  end
91
72
 
92
- # Renders the internet-facing application load balancer.
93
- #
94
- # @param settings [Settings] the resolved preview settings
95
- # @return [String] the resource block
96
73
  def alb(settings)
97
74
  <<~YAML.chomp
98
75
  Alb:
@@ -106,10 +83,7 @@ module Hecks
106
83
  YAML
107
84
  end
108
85
 
109
- # Renders the port 80 listener, whose default action goes to the default container.
110
- #
111
- # @param settings [Settings] the resolved preview settings
112
- # @return [String] the resource block
86
+ # The default action goes to the default container.
113
87
  def listener(settings)
114
88
  <<~YAML.chomp
115
89
  Listener:
@@ -124,11 +98,6 @@ module Hecks
124
98
  YAML
125
99
  end
126
100
 
127
- # Lists the listener rules to render: a non-default container's paths, sliced to
128
- # fit one condition.
129
- #
130
- # @param settings [Settings] the resolved preview settings
131
- # @return [Array<Hash{Symbol => Object}>] entries with `:container`, `:paths` and `:id`
132
101
  def rule_specs(settings)
133
102
  settings.containers.reject(&:default).select(&:routed?).flat_map do |c|
134
103
  c.paths.each_slice(PATHS_PER_RULE).with_index.map do |slice, n|
@@ -137,19 +106,11 @@ module Hecks
137
106
  end
138
107
  end
139
108
 
140
- # Renders every listener rule, with priorities counting up in tens.
141
- #
142
- # @param settings [Settings] the resolved preview settings
143
- # @return [Array<String>] the resource blocks
109
+ # Priorities count up in tens.
144
110
  def listener_rules(settings)
145
111
  rule_specs(settings).each_with_index.map { |spec, i| listener_rule(spec, (i + 1) * 10) }
146
112
  end
147
113
 
148
- # Renders one listener rule.
149
- #
150
- # @param spec [Hash{Symbol => Object}] one entry of `rule_specs`
151
- # @param priority [Integer] the rule's listener priority
152
- # @return [String] the resource block
153
114
  def listener_rule(spec, priority)
154
115
  <<~YAML.chomp
155
116
  #{spec[:id]}:
@@ -166,17 +127,8 @@ module Hecks
166
127
  YAML
167
128
  end
168
129
 
169
- # Names one listener rule's logical id.
170
- #
171
- # @param container [Containers::Entry] the container the rule forwards to
172
- # @param slice [Integer] the zero-based index of the rule among that container's rules
173
- # @return [String] the logical id
174
130
  def rule_id(container, slice) = "ListenerRule#{container.logical}#{slice + 1}"
175
131
 
176
- # Renders the service, which registers every routed container with its target group.
177
- #
178
- # @param settings [Settings] the resolved preview settings
179
- # @return [String] the resource block
180
132
  def service(settings)
181
133
  rule_ids = rule_specs(settings).map { |spec| spec[:id] }
182
134
  <<~YAML.chomp
@@ -200,10 +152,6 @@ module Hecks
200
152
  YAML
201
153
  end
202
154
 
203
- # Renders the service's load balancer registrations.
204
- #
205
- # @param settings [Settings] the resolved preview settings
206
- # @return [String] one list entry per routed container
207
155
  def service_load_balancers(settings)
208
156
  settings.containers.select(&:routed?).map do |c|
209
157
  "- ContainerName: #{c.name}\n ContainerPort: #{c.port}\n TargetGroupArn: !Ref #{c.logical}TargetGroup"
@@ -4,22 +4,13 @@ module Hecks
4
4
  module Projections
5
5
  module Deploy
6
6
  module Preview
7
- # Renders `preview.sh`, the script that creates, updates and deletes one
8
- # branch's preview stack.
9
- #
10
- # The script text lives in `preview.sh.erb` beside this file because it
11
- # is a shell program, not Ruby: keeping it as a file lets `bash -n` and
12
- # `shellcheck` read it as one. Every value baked into it has already
13
- # passed `Settings`' patterns, so the template never quotes defensively.
7
+ # Renders `preview.sh` from `preview.sh.erb`, kept as a separate file so `bash -n`
8
+ # and `shellcheck` can read it; every baked-in value already passed `Settings`.
14
9
  module Script
15
10
  TEMPLATE = File.join(__dir__, "preview.sh.erb").freeze
16
11
 
17
12
  module_function
18
13
 
19
- # Renders the script.
20
- #
21
- # @param settings [Settings] the resolved preview settings
22
- # @return [String] the bash script, ending in one newline
23
14
  def render(settings)
24
15
  ERB.new(File.read(TEMPLATE), trim_mode: "-").result_with_hash(
25
16
  s: settings, image_hint: image_hint(settings)
@@ -27,9 +18,6 @@ module Hecks
27
18
  end
28
19
 
29
20
  # Names the local images `deploy` expects, for the script's header.
30
- #
31
- # @param settings [Settings] the resolved preview settings
32
- # @return [String] the image references, comma separated
33
21
  def image_hint(settings)
34
22
  settings.containers.map(&:image).join(", ")
35
23
  end
@@ -4,18 +4,8 @@ module Hecks
4
4
  module Projections
5
5
  module Deploy
6
6
  module Preview
7
- # The resolved, validated settings of one preview stack family.
8
- #
9
- # `Settings.build` is the only place a raw `preview` block meets the
10
- # main stack's own facts, so every default lives here and the two
11
- # renderers (`Template`, `Script`) read plain values.
12
- #
13
- # ## Why validation is this strict
14
- #
15
- # Every value ends up in a CloudFormation name, a Postgres database
16
- # name that `CREATE DATABASE` cannot parameterize, or a shell script
17
- # baked with the value. Patterns are checked once here, so neither
18
- # renderer has to quote defensively.
7
+ # The resolved, validated settings of one preview stack, built once by `build`.
8
+ # Checked strictly: values feed CloudFormation names and a baked shell script.
19
9
  class Settings
20
10
  KEYS = %i[prefix alb_prefix owner_stack database_stack database_endpoint_output database_secret_output
21
11
  db_prefix protected_databases protected_branches cpu memory log_retention_days session_cookie
@@ -34,18 +24,7 @@ module Hecks
34
24
 
35
25
  attr_reader(*KEYS, :region, :stack_name, :infra_name, :main)
36
26
 
37
- # Builds the settings for one domain's previews.
38
- #
39
- # @param raw [Hash{Symbol => Object}, true] the `preview` block; `true` accepts
40
- # every default
41
- # @param deploy_settings [Hash{Symbol => Object}] the `deployed_to` settings
42
- # @param main [Hash{Symbol => Object}] the main stack's facts: `:infra_name`,
43
- # `:stack_name`,
44
- # `:stack_prefix`, `:region`, `:cpu`, `:memory`, `:db_name`, `:owner_stack` (nil unless
45
- # the main stack borrows a database), `:name`, `:port`, `:image`, `:domain`, `:web`,
46
- # `:wasm_path`, `:ir_path`, `:schema`
47
- # @return [Settings] the validated settings
48
- # @raise [ArgumentError] if a key is unknown or a value breaks its pattern
27
+ # `raw` may be `true`, accepting every default.
49
28
  def self.build(raw, deploy_settings:, main:)
50
29
  raw = raw.is_a?(Hash) ? raw.transform_keys(&:to_sym) : {}
51
30
  unknown = raw.keys - KEYS
@@ -56,9 +35,6 @@ module Hecks
56
35
  new(raw, deploy_settings, main)
57
36
  end
58
37
 
59
- # @param raw [Hash{Symbol => Object}] the symbolized `preview` settings
60
- # @param deploy_settings [Hash{Symbol => Object}] the `deployed_to` settings
61
- # @param main [Hash{Symbol => Object}] the main stack's facts, see `build`
62
38
  def initialize(raw, deploy_settings, main)
63
39
  @main = main
64
40
  @region = main.fetch(:region)
@@ -72,14 +48,8 @@ module Hecks
72
48
  signup_path: (signup_path if first_admin))
73
49
  end
74
50
 
75
- # Answers the host container, when the task has one.
76
- #
77
- # @return [Containers::Entry, nil] the entry flagged `host`
78
51
  def host = containers.find(&:host)
79
52
 
80
- # Answers the container that receives unmatched requests.
81
- #
82
- # @return [Containers::Entry] the default entry
83
53
  def default_container = containers.find(&:default)
84
54
 
85
55
  private
@@ -7,9 +7,6 @@ module Hecks
7
7
  module Preview
8
8
  # The task definitions of a preview stack: the containers it runs and the
9
9
  # one-shot task that creates its database.
10
- #
11
- # The host container gets the domain, session and database environment; a
12
- # container's own `environment` wins over a generated entry of the same name.
13
10
  module TaskYaml
14
11
  # The one-shot task's script. Idempotent, and refuses names the preview must never touch.
15
12
  DB_INIT_SCRIPT = <<~SH.freeze
@@ -52,10 +49,7 @@ module Hecks
52
49
 
53
50
  module_function
54
51
 
55
- # Renders the one-shot task that creates (or drops) this preview's database.
56
- #
57
- # @param settings [Settings] the resolved preview settings
58
- # @return [String] the task definition's resource block
52
+ # Also renders the drop path, taken when `ACTION` is `drop`.
59
53
  def db_init_task(settings)
60
54
  <<~YAML.chomp
61
55
  # Run by preview.sh (`aws ecs run-task`) after the images are pushed and before the
@@ -100,10 +94,6 @@ module Hecks
100
94
  YAML
101
95
  end
102
96
 
103
- # Renders the preview's task definition, one container per entry of the container list.
104
- #
105
- # @param settings [Settings] the resolved preview settings
106
- # @return [String] the task definition's resource block
107
97
  def task_definition(settings)
108
98
  <<~YAML.chomp
109
99
  TaskDefinition:
@@ -124,11 +114,6 @@ module Hecks
124
114
  YAML
125
115
  end
126
116
 
127
- # Renders one container of the task definition.
128
- #
129
- # @param settings [Settings] the resolved preview settings
130
- # @param container [Containers::Entry] the container
131
- # @return [String] the list entry
132
117
  def container_definition(settings, container)
133
118
  lines = [
134
119
  "- Name: #{container.name}",
@@ -141,10 +126,6 @@ module Hecks
141
126
  lines.join("\n")
142
127
  end
143
128
 
144
- # Renders the log driver settings of one container.
145
- #
146
- # @param container [Containers::Entry] the container
147
- # @return [String] the settings, ending in a newline
148
129
  def log_options(container)
149
130
  <<~YAML
150
131
  LogDriver: awslogs
@@ -155,23 +136,13 @@ module Hecks
155
136
  YAML
156
137
  end
157
138
 
158
- # Renders one container's environment as a list.
159
- #
160
- # @param settings [Settings] the resolved preview settings
161
- # @param container [Containers::Entry] the container
162
- # @return [String] the list entries
163
139
  def environment_yaml(settings, container)
164
140
  environment_pairs(settings, container).map do |name, value|
165
141
  "- Name: #{name}\n Value: #{value}"
166
142
  end.join("\n")
167
143
  end
168
144
 
169
- # Orders one container's environment as `[name, rendered value]` pairs; the container's
170
- # `environment` wins over a generated entry of the same name.
171
- #
172
- # @param settings [Settings] the resolved preview settings
173
- # @param container [Containers::Entry] the container
174
- # @return [Array<Array(String, String)>] the pairs, each value already rendered as YAML
145
+ # The container's own `environment` wins over a generated entry of the same name.
175
146
  def environment_pairs(settings, container)
176
147
  pairs = { "PORT" => scalar(container.port.to_s) }
177
148
  pairs.merge!(host_environment(settings)) if container.host
@@ -181,10 +152,6 @@ module Hecks
181
152
  pairs.to_a
182
153
  end
183
154
 
184
- # Answers the environment only the Hecks host container gets.
185
- #
186
- # @param settings [Settings] the resolved preview settings
187
- # @return [Hash{String => String}] name to rendered value
188
155
  def host_environment(settings)
189
156
  main = settings.main
190
157
  pairs = {
@@ -199,9 +166,6 @@ module Hecks
199
166
  pairs
200
167
  end
201
168
 
202
- # Answers the environment that points a container at this preview's database.
203
- #
204
- # @return [Hash{String => String}] name to rendered value
205
169
  def database_environment
206
170
  {
207
171
  "DB_HOST" => "!Ref OwningDatabaseEndpoint", "DB_NAME" => "!Ref DbName",