hecks 2.8.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/hecks +50 -7
- data/lib/hecks/adapters/driven/claude_code.rb +4 -104
- data/lib/hecks/adapters/driven/d1.rb +44 -237
- data/lib/hecks/adapters/driven/folder.rb +17 -76
- data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
- data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
- data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
- data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
- data/lib/hecks/adapters/driven/heki.rb +13 -42
- data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
- data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
- data/lib/hecks/adapters/driven/lambda.rb +10 -84
- data/lib/hecks/adapters/driven/local_storage.rb +5 -40
- data/lib/hecks/adapters/driven/memory.rb +32 -100
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
- data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +10 -32
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
- data/lib/hecks/adapters/driven/postgres.rb +149 -136
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +48 -38
- data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
- data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
- data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
- data/lib/hecks/adapters/driven/sqlite.rb +128 -65
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +76 -62
- data/lib/hecks/adapters/driven.rb +5 -36
- data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
- data/lib/hecks/behaviors/dsl.rb +7 -25
- data/lib/hecks/behaviors/expectations.rb +32 -142
- data/lib/hecks/behaviors/ir.rb +7 -20
- data/lib/hecks/behaviors/rspec.rb +6 -17
- data/lib/hecks/behaviors/runner.rb +10 -34
- data/lib/hecks/behaviors.rb +4 -8
- data/lib/hecks/bench/cli.rb +62 -0
- data/lib/hecks/bench/environment.rb +71 -0
- data/lib/hecks/bench/postgres_probe.rb +30 -0
- data/lib/hecks/bench/report.rb +76 -0
- data/lib/hecks/bench/ruby_runner.rb +93 -0
- data/lib/hecks/bench/run.rb +55 -0
- data/lib/hecks/bench/rust_runner.rb +101 -0
- data/lib/hecks/bench/stats.rb +46 -0
- data/lib/hecks/bench/suite.rb +98 -0
- data/lib/hecks/bench/workload.rb +89 -0
- data/lib/hecks/bench.rb +17 -0
- data/lib/hecks/bluebook/aggregate.rb +9 -75
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
- data/lib/hecks/bluebook/assembly/build.rb +4 -15
- data/lib/hecks/bluebook/assembly/contract.rb +15 -136
- data/lib/hecks/bluebook/assembly/contracts.rb +56 -174
- data/lib/hecks/bluebook/assembly/marks.rb +31 -223
- data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
- data/lib/hecks/bluebook/assembly.rb +6 -45
- data/lib/hecks/bluebook/attribute.rb +12 -61
- data/lib/hecks/bluebook/behaviour/aggregate.rb +39 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +11 -20
- data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
- data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
- data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
- data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
- data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
- data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
- data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
- data/lib/hecks/bluebook/capabilities.rb +17 -49
- data/lib/hecks/bluebook/chapter.rb +16 -50
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +49 -67
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +96 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +63 -169
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +3 -1
- data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
- data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +42 -89
- data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +148 -197
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
- data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
- data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
- data/lib/hecks/bluebook/dsl/query_builder.rb +38 -43
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
- data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
- data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
- data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
- data/lib/hecks/bluebook/dsl/world_builder.rb +46 -111
- data/lib/hecks/bluebook/entity.rb +13 -71
- data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
- data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
- data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
- data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
- data/lib/hecks/bluebook/expression/resolver.rb +39 -661
- data/lib/hecks/bluebook/hexagon.rb +18 -33
- data/lib/hecks/bluebook/lifecycle.rb +5 -16
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
- data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
- data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +25 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -306
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +87 -352
- data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
- data/lib/hecks/bluebook/model_check.rb +122 -596
- data/lib/hecks/bluebook/pattern_subset.rb +49 -69
- data/lib/hecks/bluebook/process_manager.rb +13 -83
- data/lib/hecks/bluebook/project_loader.rb +3 -39
- data/lib/hecks/bluebook/project_register.rb +12 -60
- data/lib/hecks/bluebook/query.rb +27 -51
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +9 -99
- data/lib/hecks/bluebook/synthesizer.rb +13 -62
- data/lib/hecks/bluebook/translation.rb +26 -62
- data/lib/hecks/bluebook/value_object.rb +11 -36
- data/lib/hecks/bluebook.rb +5 -22
- data/lib/hecks/cache_dir.rb +4 -23
- data/lib/hecks/canonical_json.rb +30 -0
- data/lib/hecks/chapters.rb +102 -0
- data/lib/hecks/cli/behaviors.rb +61 -0
- data/lib/hecks/cli/canonicalise.rb +25 -0
- data/lib/hecks/cli/check_engine_agreement.rb +32 -0
- data/lib/hecks/cli/check_era.rb +88 -0
- data/lib/hecks/cli/console.rb +66 -0
- data/lib/hecks/cli/deploy_template_diff.rb +40 -0
- data/lib/hecks/cli/doc_coverage.rb +32 -0
- data/lib/hecks/cli/document.rb +5 -23
- data/lib/hecks/cli/expression_projection.rb +41 -0
- data/lib/hecks/cli/follow.rb +88 -0
- data/lib/hecks/cli/history.rb +44 -0
- data/lib/hecks/cli/ir.rb +4 -6
- data/lib/hecks/cli/mcp.rb +5 -6
- data/lib/hecks/cli/model_check.rb +24 -88
- data/lib/hecks/cli/pattern_cases.rb +47 -0
- data/lib/hecks/cli/present.rb +39 -0
- data/lib/hecks/cli/project_cli.rb +223 -64
- data/lib/hecks/cli/project_diagrams.rb +4 -10
- data/lib/hecks/cli/project_glossary.rb +44 -0
- data/lib/hecks/cli/reference.rb +46 -0
- data/lib/hecks/cli/refresh_projections.rb +44 -0
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
- data/lib/hecks/cli/release.rb +70 -0
- data/lib/hecks/cli/release_gem.rb +59 -0
- data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
- data/lib/hecks/cli/rspec_shard_files.rb +66 -0
- data/lib/hecks/cli/run.rb +15 -61
- data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
- data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
- data/lib/hecks/cli/shape.rb +70 -0
- data/lib/hecks/cli/smoke_http.rb +230 -0
- data/lib/hecks/cli/smoke_test.rb +7 -19
- data/lib/hecks/cli/spec_example.rb +27 -0
- data/lib/hecks/cli/statements.rb +32 -0
- data/lib/hecks/cli/stores.rb +4 -6
- data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
- data/lib/hecks/cli.rb +216 -73
- data/lib/hecks/codemod.rb +324 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/corpus.rb +423 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +617 -158
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -11
- data/lib/hecks/deploy/oidc.json +82 -2
- data/lib/hecks/doc/reference.rb +351 -0
- data/lib/hecks/doors/cli_door.rb +196 -0
- data/lib/hecks/doors/cli_runner.rb +297 -0
- data/lib/hecks/{facade → doors}/command_request.rb +14 -34
- data/lib/hecks/doors/handle.rb +206 -0
- data/lib/hecks/doors/json_door.rb +132 -0
- data/lib/hecks/doors/launcher_options.rb +129 -0
- data/lib/hecks/{cli → doors}/mcp_door.rb +25 -120
- data/lib/hecks/doors/mcp_door_scope.rb +97 -0
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +162 -0
- data/lib/hecks/doors/ruby_door/chapter.rb +72 -0
- data/lib/hecks/doors/ruby_door.rb +77 -0
- data/lib/hecks/doors.rb +61 -0
- data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
- data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
- data/lib/hecks/embryonaut_bluebook.rb +5 -72
- data/lib/hecks/engine_agreement.rb +156 -0
- data/lib/hecks/facade.rb +23 -51
- data/lib/hecks/forms/app.rb +21 -76
- data/lib/hecks/forms/banking_presentation.rb +50 -0
- data/lib/hecks/forms/command_form_renderer.rb +10 -29
- data/lib/hecks/forms/field_renderer.rb +17 -156
- data/lib/hecks/forms/field_shape.rb +31 -222
- data/lib/hecks/forms/html.rb +12 -47
- data/lib/hecks/forms/index_renderer.rb +1 -4
- data/lib/hecks/forms/page.rb +3 -9
- data/lib/hecks/forms/params.rb +14 -60
- data/lib/hecks/forms/port_argument.rb +2 -19
- data/lib/hecks/forms/query_form_renderer.rb +4 -79
- data/lib/hecks/forms/record_renderer.rb +8 -75
- data/lib/hecks/forms/record_table.rb +7 -50
- data/lib/hecks/forms/reference_options.rb +3 -30
- data/lib/hecks/forms/value_object_shape.rb +8 -35
- data/lib/hecks/forms.rb +5 -33
- data/lib/hecks/fqn.rb +5 -12
- data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
- data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
- data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
- data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
- data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
- data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
- data/lib/hecks/framework.rb +11 -80
- data/lib/hecks/freezer.rb +5 -39
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
- data/lib/hecks/fuzzing/combination_miner.rb +86 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
- data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
- data/lib/hecks/fuzzing/differential.rb +156 -0
- data/lib/hecks/fuzzing/domain_generator.rb +615 -0
- data/lib/hecks/fuzzing/era_boundary.rb +71 -0
- data/lib/hecks/fuzzing/form_census.rb +136 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
- data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
- data/lib/hecks/fuzzing/outside_world.rb +45 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
- data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
- data/lib/hecks/fuzzing/properties/guards.rb +190 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
- data/lib/hecks/fuzzing/properties/querying.rb +175 -0
- data/lib/hecks/fuzzing/properties.rb +162 -0
- data/lib/hecks/fuzzing/qa_settings.rb +123 -0
- data/lib/hecks/fuzzing/replay.rb +505 -0
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
- data/lib/hecks/fuzzing/self_consistency.rb +339 -0
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
- data/lib/hecks/fuzzing/shrinker.rb +166 -0
- data/lib/hecks/fuzzing/structural_skips.rb +33 -0
- data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
- data/lib/hecks/fuzzing/value_generator.rb +216 -0
- data/lib/hecks/fuzzing.rb +23 -0
- data/lib/hecks/grammar/evolve.rb +336 -0
- data/lib/hecks/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
- data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
- data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
- data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
- data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
- data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
- data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
- data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
- data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
- data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
- data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
- data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
- data/lib/hecks/hecks/adapters/git.adapter +7 -0
- data/lib/hecks/hecks/adapters/git.rb +102 -0
- data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
- data/lib/hecks/hecks/adapters/host_http.rb +65 -0
- data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
- data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
- data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
- data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
- data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
- data/lib/hecks/hecks/adapters/local_files.rb +46 -0
- data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
- data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
- data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
- data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
- data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
- data/lib/hecks/hecks/adapters/shell.adapter +7 -0
- data/lib/hecks/hecks/adapters/shell.rb +49 -0
- data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
- data/lib/hecks/hecks/adapters/terminal.rb +74 -0
- data/lib/hecks/hecks/codebase.behaviors +172 -0
- data/lib/hecks/hecks/codebase.bluebook +2453 -0
- data/lib/hecks/hecks/context_map.hecksagon +13 -0
- data/lib/hecks/hecks/custodian.behaviors +179 -0
- data/lib/hecks/hecks/custodian.bluebook +1847 -0
- data/lib/hecks/hecks/deploy.behaviors +74 -0
- data/lib/hecks/hecks/deploy.world +12 -0
- data/lib/hecks/hecks/environments/memory.world +10 -0
- data/lib/hecks/hecks/governance.world +7 -0
- data/lib/hecks/hecks/hecks.behaviors +62 -0
- data/lib/hecks/hecks/hecks.bluebook +124 -0
- data/lib/hecks/hecks/hecks.hecksagon +390 -0
- data/lib/hecks/hecks/hecks.world +42 -0
- data/lib/hecks/ir.rb +14 -83
- data/lib/hecks/kernel_capabilities.rb +194 -0
- data/lib/hecks/language/adapter.bluebook +2 -7
- data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
- data/lib/hecks/language/bluebook/bluebook.bluebook +26 -83
- data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
- data/lib/hecks/language/bluebook/command.bluebook +27 -144
- data/lib/hecks/language/bluebook/entity.bluebook +24 -97
- data/lib/hecks/language/bluebook/policy.bluebook +16 -67
- data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
- data/lib/hecks/language/bluebook/projection.bluebook +126 -60
- data/lib/hecks/language/bluebook/query.bluebook +19 -43
- data/lib/hecks/language/bluebook/shape.bluebook +23 -73
- data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
- data/lib/hecks/language/bluebook/vocabulary.bluebook +83 -198
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +14 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +26 -25
- data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
- data/lib/hecks/language/oidc.json +35 -0
- data/lib/hecks/language/port.bluebook +4 -17
- data/lib/hecks/language/translation/translation.bluebook +3 -9
- data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
- data/lib/hecks/language/world/wiring.bluebook +1 -3
- data/lib/hecks/language/world/world.bluebook +5 -7
- data/lib/hecks/literal.rb +8 -50
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
- data/lib/hecks/persistence_legacy_fixture.rb +113 -0
- data/lib/hecks/ports/access_control.rb +7 -23
- data/lib/hecks/ports/agent/answers.rb +5 -23
- data/lib/hecks/ports/agent.rb +47 -172
- data/lib/hecks/ports/authentication.rb +12 -39
- data/lib/hecks/ports/authorization.rb +16 -43
- data/lib/hecks/ports/clock.rb +5 -45
- data/lib/hecks/ports/extraction.rb +7 -12
- data/lib/hecks/ports/identity_assignment.rb +8 -29
- data/lib/hecks/ports/identity_generation.rb +6 -29
- data/lib/hecks/ports/identity_resolution.rb +2 -10
- data/lib/hecks/ports/key_vault.rb +9 -45
- data/lib/hecks/ports/persistence/append_only.rb +61 -64
- data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
- data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
- data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
- data/lib/hecks/ports/persistence/plugin.rb +13 -42
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +44 -144
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +20 -85
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +18 -24
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +19 -76
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +43 -127
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +131 -555
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +50 -47
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
- data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
- data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
- data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
- data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
- data/lib/hecks/ports/persistence/state_codec.rb +47 -108
- data/lib/hecks/ports/persistence.rb +7 -14
- data/lib/hecks/ports/projection.rb +42 -84
- data/lib/hecks/ports/query/in_memory.rb +10 -58
- data/lib/hecks/ports/query/ordering.rb +11 -32
- data/lib/hecks/ports/query.rb +14 -37
- data/lib/hecks/ports.rb +2 -8
- data/lib/hecks/projection_files.rb +285 -0
- data/lib/hecks/projections/bootstrap_table.rb +12 -44
- data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
- data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
- data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
- data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
- data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
- data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
- data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
- data/lib/hecks/projections/deploy/fargate.rb +40 -207
- data/lib/hecks/projections/deploy/lambda.rb +191 -708
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
- data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
- data/lib/hecks/projections/deploy/preview/script.rb +2 -14
- data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
- data/lib/hecks/projections/deploy/preview/template.rb +3 -59
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +48 -147
- data/lib/hecks/projections/deploy/smoke.rb +4 -61
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
- data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
- data/lib/hecks/projections/deploy/template_diff.rb +5 -57
- data/lib/hecks/projections/diagrams.rb +22 -584
- data/lib/hecks/projections/glossary/html.rb +14 -130
- data/lib/hecks/projections/glossary/markdown.rb +8 -69
- data/lib/hecks/projections/glossary/mermaid.rb +9 -66
- data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
- data/lib/hecks/projections/glossary/sentences.rb +14 -159
- data/lib/hecks/projections/glossary.rb +30 -222
- data/lib/hecks/projections/ir.rb +3 -10
- data/lib/hecks/projections/model/deviations.rb +16 -43
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +13 -32
- data/lib/hecks/projections/reference.rb +4 -18
- data/lib/hecks/projections/rust_vocabulary.rb +13 -251
- data/lib/hecks/projections/shape.rb +3 -29
- data/lib/hecks/projections/statements.rb +7 -86
- data/lib/hecks/projections/vocabulary.rb +6 -22
- data/lib/hecks/projections.rb +2 -16
- data/lib/hecks/projector/cli_projector.rb +154 -400
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +42 -134
- data/lib/hecks/projector/ir_projector.rb +2 -9
- data/lib/hecks/projector/narrate_projector.rb +15 -189
- data/lib/hecks/projector/target.rb +13 -76
- data/lib/hecks/projector.rb +30 -121
- data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
- data/lib/hecks/quality_control/adapters/agent.rb +58 -0
- data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
- data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
- data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
- data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
- data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
- data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
- data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
- data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
- data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
- data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
- data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
- data/lib/hecks/quality_control/cli/child.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
- data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
- data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
- data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
- data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
- data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
- data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
- data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
- data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
- data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
- data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
- data/lib/hecks/query_ir.rb +346 -0
- data/lib/hecks/query_ir_mcp.rb +198 -0
- data/lib/hecks/query_specification/common/comparators.rb +5 -26
- data/lib/hecks/query_specification/common/comparison.rb +35 -143
- data/lib/hecks/query_specification/common/dsl.rb +8 -24
- data/lib/hecks/query_specification/common/null_policy.rb +20 -70
- data/lib/hecks/query_specification/common/options.rb +2 -6
- data/lib/hecks/query_specification/common/where_clause.rb +2 -10
- data/lib/hecks/query_specification/field_path.rb +37 -77
- data/lib/hecks/query_specification/hop_path.rb +12 -76
- data/lib/hecks/release/gem_pin.rb +2 -26
- data/lib/hecks/release/runner/ci_publisher.rb +5 -8
- data/lib/hecks/release/runner/clean_tree.rb +42 -0
- data/lib/hecks/release/runner/commands.rb +2 -4
- data/lib/hecks/release/runner/console.rb +6 -0
- data/lib/hecks/release/runner/gem_publisher.rb +15 -9
- data/lib/hecks/release/runner/npm_publisher.rb +9 -53
- data/lib/hecks/release/runner/preflight.rb +29 -20
- data/lib/hecks/release/runner/published.rb +8 -26
- data/lib/hecks/release/runner/tagger.rb +31 -23
- data/lib/hecks/release/runner.rb +60 -38
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +15 -39
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/adapter_lookup.rb +124 -0
- data/lib/hecks/runtime/aggregate_lock.rb +8 -33
- data/lib/hecks/runtime/boot_gates.rb +3 -18
- data/lib/hecks/runtime/caller.rb +19 -59
- data/lib/hecks/runtime/capability_graph.rb +5 -21
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +27 -160
- data/lib/hecks/runtime/command_interpreter.rb +124 -306
- data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
- data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
- data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
- data/lib/hecks/runtime/command_rules/emission.rb +8 -21
- data/lib/hecks/runtime/command_rules/references.rb +23 -202
- data/lib/hecks/runtime/dependency_planning.rb +50 -104
- data/lib/hecks/runtime/dispatcher.rb +134 -243
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +64 -219
- data/lib/hecks/runtime/errors.rb +32 -115
- data/lib/hecks/runtime/event.rb +3 -25
- data/lib/hecks/runtime/identity.rb +18 -154
- data/lib/hecks/runtime/instance.rb +18 -150
- data/lib/hecks/runtime/interpreting.rb +14 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +109 -194
- data/lib/hecks/runtime/outbox.rb +39 -262
- data/lib/hecks/runtime/policy_interpreter.rb +43 -202
- data/lib/hecks/runtime/port_operation_interpreter.rb +28 -97
- data/lib/hecks/runtime/query_interpreter.rb +122 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/reaction_outcome.rb +89 -0
- data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
- data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
- data/lib/hecks/runtime/reference_hop.rb +8 -81
- data/lib/hecks/runtime/refusal_wording.rb +28 -87
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +213 -227
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +156 -247
- data/lib/hecks/runtime/remote_dispatcher.rb +68 -154
- data/lib/hecks/runtime/routing.rb +4 -15
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
- data/lib/hecks/runtime/saga_interpreter.rb +79 -263
- data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
- data/lib/hecks/runtime/tenant_check.rb +6 -68
- data/lib/hecks/runtime/tenant_scope.rb +3 -28
- data/lib/hecks/runtime/value/admission.rb +16 -67
- data/lib/hecks/runtime/value/coercion.rb +162 -760
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value/field_checks.rb +172 -0
- data/lib/hecks/runtime/value.rb +15 -84
- data/lib/hecks/runtime.rb +35 -69
- data/lib/hecks/rust_build/conformance.rb +158 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
- data/lib/hecks/rust_build/coverage.rb +218 -0
- data/lib/hecks/rust_build/native_build.rb +124 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
- data/lib/hecks/rust_build/project_rust.rb +332 -0
- data/lib/hecks/rust_build/wasm.rb +105 -0
- data/lib/hecks/rust_build/wasm_browser.rb +113 -0
- data/lib/hecks/rust_build.rb +144 -0
- data/lib/hecks/storehouse.rb +155 -582
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +10 -34
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- data/lib/hecks/three_zero/forms.yml +127 -0
- data/lib/hecks/three_zero.rb +12 -0
- data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
- data/lib/hecks/tools/comment_style.rb +1070 -0
- data/lib/hecks/tools/corpus_report.rb +87 -0
- data/lib/hecks/tools/deploy_recipe.rb +255 -0
- data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
- data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
- data/lib/hecks/tools/era_projection_backfill.rb +94 -0
- data/lib/hecks/tools/era_reattest.rb +146 -0
- data/lib/hecks/tools/evolve_run.rb +189 -0
- data/lib/hecks/tools/fuzz_sweep.rb +373 -0
- data/lib/hecks/tools/heki_compaction.rb +100 -0
- data/lib/hecks/tools/hoist_local_givens.rb +246 -0
- data/lib/hecks/tools/journal_compaction.rb +89 -0
- data/lib/hecks/tools/oidc_manifests.rb +70 -0
- data/lib/hecks/tools/query_ir_run.rb +81 -0
- data/lib/hecks/tools/regeneration_run.rb +166 -0
- data/lib/hecks/tools/rust_comment_style.rb +642 -0
- data/lib/hecks/tools/sequence_script.rb +62 -0
- data/lib/hecks/tools/tail_merge.rb +117 -0
- data/lib/hecks/tools/tenant_provisioning.rb +142 -0
- data/lib/hecks/tools/translation_audit.rb +208 -0
- data/lib/hecks/tools/translation_scaffold.rb +131 -0
- data/lib/hecks/tools.rb +86 -0
- data/lib/hecks/vendoring/git_environment.rb +2 -8
- data/lib/hecks/vendoring/git_source.rb +4 -10
- data/lib/hecks/vendoring.rb +7 -46
- data/lib/hecks/version.rb +3 -14
- data/lib/hecks.rb +52 -90
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
- data/qa/settings.yml +66 -0
- data/rust/.cargo/config.toml +6 -0
- data/rust/.gitignore +2 -0
- data/rust/Cargo.lock +54 -0
- data/rust/Cargo.toml +61 -0
- data/rust/build/.cargo/config.toml +3 -0
- data/rust/build/.gitignore +1 -0
- data/rust/build/Cargo.lock +7 -0
- data/rust/build/Cargo.toml +19 -0
- data/rust/build/src/build_artifact.rs +47 -0
- data/rust/build/src/cargo_sync.rs +248 -0
- data/rust/build/src/json.rs +421 -0
- data/rust/build/src/lineage_pass.rs +285 -0
- data/rust/build/src/main.rs +51 -0
- data/rust/build/src/optional_pass.rs +150 -0
- data/rust/build/src/pipeline.rs +264 -0
- data/rust/build/src/reserved_names.rs +76 -0
- data/rust/build/src/resolve.rs +124 -0
- data/rust/build/src/root.rs +25 -0
- data/rust/build/src/sidecars.rs +73 -0
- data/rust/build/src/subprocess.rs +51 -0
- data/rust/build/src/tmp.rs +31 -0
- data/rust/codegen/.cargo/config.toml +3 -0
- data/rust/codegen/.gitignore +1 -0
- data/rust/codegen/Cargo.lock +7 -0
- data/rust/codegen/Cargo.toml +20 -0
- data/rust/codegen/src/attr.rs +32 -0
- data/rust/codegen/src/bridging.rs +481 -0
- data/rust/codegen/src/commands.rs +1226 -0
- data/rust/codegen/src/constraints.rs +109 -0
- data/rust/codegen/src/dependency_planning.rs +209 -0
- data/rust/codegen/src/domain_generator.rs +1288 -0
- data/rust/codegen/src/exemplar.rs +230 -0
- data/rust/codegen/src/expr_emitter.rs +87 -0
- data/rust/codegen/src/fielded.rs +192 -0
- data/rust/codegen/src/hecks_naming.rs +33 -0
- data/rust/codegen/src/json.rs +335 -0
- data/rust/codegen/src/json_codec.rs +697 -0
- data/rust/codegen/src/literal.rs +207 -0
- data/rust/codegen/src/main.rs +290 -0
- data/rust/codegen/src/manifest.rs +227 -0
- data/rust/codegen/src/mutations.rs +784 -0
- data/rust/codegen/src/naming.rs +286 -0
- data/rust/codegen/src/ports.rs +208 -0
- data/rust/codegen/src/prelude.rs +116 -0
- data/rust/codegen/src/queries.rs +786 -0
- data/rust/codegen/src/reactions.rs +532 -0
- data/rust/codegen/src/read_models.rs +697 -0
- data/rust/codegen/src/reference_specs.rs +40 -0
- data/rust/codegen/src/registry.rs +966 -0
- data/rust/codegen/src/reserved_names.rs +76 -0
- data/rust/codegen/src/shared.rs +93 -0
- data/rust/codegen/src/skip_reason.rs +25 -0
- data/rust/codegen/src/types.rs +362 -0
- data/rust/host/.cargo/config.toml +3 -0
- data/rust/host/.gitignore +1 -0
- data/rust/host/Cargo.lock +4297 -0
- data/rust/host/Cargo.toml +61 -0
- data/rust/host/HECKS_RELEASE +1 -0
- data/rust/host/rds-ca-bundle.pem +2736 -0
- data/rust/host/src/api.rs +1596 -0
- data/rust/host/src/approval.rs +652 -0
- data/rust/host/src/auth.rs +1287 -0
- data/rust/host/src/bin/expr_harness.rs +46 -0
- data/rust/host/src/bin/lineage_harness.rs +119 -0
- data/rust/host/src/bin/mint_harness.rs +135 -0
- data/rust/host/src/checkout.rs +265 -0
- data/rust/host/src/dispatch.rs +1443 -0
- data/rust/host/src/expr_json.rs +1117 -0
- data/rust/host/src/field_hints.rs +52 -0
- data/rust/host/src/ir.rs +733 -0
- data/rust/host/src/journal.rs +874 -0
- data/rust/host/src/lambda_client.rs +378 -0
- data/rust/host/src/log.rs +147 -0
- data/rust/host/src/main.rs +474 -0
- data/rust/host/src/mint.rs +1865 -0
- data/rust/host/src/payments/keystore.rs +330 -0
- data/rust/host/src/payments/tests.rs +1400 -0
- data/rust/host/src/payments.rs +767 -0
- data/rust/host/src/presentation.rs +698 -0
- data/rust/host/src/presentation_write.rs +1431 -0
- data/rust/host/src/query_step.rs +149 -0
- data/rust/host/src/rate_limit.rs +904 -0
- data/rust/host/src/reference_transform.rs +241 -0
- data/rust/host/src/reference_validate.rs +338 -0
- data/rust/host/src/resend.rs +296 -0
- data/rust/host/src/secrets.rs +106 -0
- data/rust/host/src/server.rs +663 -0
- data/rust/host/src/storage_shape.rs +175 -0
- data/rust/host/src/test_pg.rs +40 -0
- data/rust/host/src/ui_schema.rs +1351 -0
- data/rust/host/src/wasm_runner.rs +191 -0
- data/rust/host/src/web/newsletter/cooldown.rs +126 -0
- data/rust/host/src/web/newsletter/tests.rs +104 -0
- data/rust/host/src/web/newsletter.rs +310 -0
- data/rust/host/src/web/newsletter_send.rs +357 -0
- data/rust/host/src/web/registrations.rs +1505 -0
- data/rust/host/src/web.rs +2937 -0
- data/rust/lsp/.gitignore +1 -0
- data/rust/lsp/Cargo.lock +7 -0
- data/rust/lsp/Cargo.toml +16 -0
- data/rust/lsp/README.md +133 -0
- data/rust/lsp/src/diagnostics.rs +165 -0
- data/rust/lsp/src/json.rs +385 -0
- data/rust/lsp/src/main.rs +399 -0
- data/rust/lsp/src/outline.rs +177 -0
- data/rust/lsp/src/rpc.rs +67 -0
- data/rust/parser/.cargo/config.toml +5 -0
- data/rust/parser/.gitignore +1 -0
- data/rust/parser/Cargo.lock +7 -0
- data/rust/parser/Cargo.toml +21 -0
- data/rust/parser/src/build/closed_sets.rs +27 -0
- data/rust/parser/src/build/identity.rs +276 -0
- data/rust/parser/src/build/mod.rs +12 -0
- data/rust/parser/src/build/naming.rs +141 -0
- data/rust/parser/src/build/pattern_subset.rs +161 -0
- data/rust/parser/src/build/query_derive.rs +72 -0
- data/rust/parser/src/build/query_inference.rs +269 -0
- data/rust/parser/src/build/query_options.rs +100 -0
- data/rust/parser/src/build/read_model.rs +94 -0
- data/rust/parser/src/build/references.rs +32 -0
- data/rust/parser/src/canonical.rs +114 -0
- data/rust/parser/src/diag.rs +55 -0
- data/rust/parser/src/emit.rs +953 -0
- data/rust/parser/src/expr/ast_json.rs +147 -0
- data/rust/parser/src/expr/evaluator.rs +81 -0
- data/rust/parser/src/expr/mod.rs +84 -0
- data/rust/parser/src/expr/resolver.rs +497 -0
- data/rust/parser/src/ir.rs +387 -0
- data/rust/parser/src/keywords.rs +585 -0
- data/rust/parser/src/lex.rs +662 -0
- data/rust/parser/src/main.rs +252 -0
- data/rust/parser/src/parse/aggregate.rs +612 -0
- data/rust/parser/src/parse/chapter.rs +517 -0
- data/rust/parser/src/parse/command.rs +637 -0
- data/rust/parser/src/parse/domain_port.rs +127 -0
- data/rust/parser/src/parse/entity.rs +527 -0
- data/rust/parser/src/parse/file.rs +67 -0
- data/rust/parser/src/parse/hecksagon.rs +231 -0
- data/rust/parser/src/parse/lifecycle.rs +162 -0
- data/rust/parser/src/parse/mod.rs +1262 -0
- data/rust/parser/src/parse/policy.rs +87 -0
- data/rust/parser/src/parse/process_manager.rs +305 -0
- data/rust/parser/src/parse/query.rs +100 -0
- data/rust/parser/src/parse/read_model.rs +214 -0
- data/rust/parser/src/parse/value_object.rs +250 -0
- data/rust/parser/src/ruby_value.rs +422 -0
- data/rust/project/bridging.rb +257 -0
- data/rust/project/commands.rb +877 -0
- data/rust/project/constraints.rb +86 -0
- data/rust/project/dependency_planning.rb +178 -0
- data/rust/project/domain_generator.rb +980 -0
- data/rust/project/exemplar.rb +148 -0
- data/rust/project/expr_emitter.rb +72 -0
- data/rust/project/fielded.rb +141 -0
- data/rust/project/json_codec.rb +641 -0
- data/rust/project/mutations.rb +721 -0
- data/rust/project/naming.rb +205 -0
- data/rust/project/ports.rb +65 -0
- data/rust/project/queries.rb +493 -0
- data/rust/project/reactions.rb +317 -0
- data/rust/project/read_models.rb +690 -0
- data/rust/project/reference_specs.rb +27 -0
- data/rust/project/registry.rb +396 -0
- data/rust/project/skip_reason.rb +29 -0
- data/rust/project/types.rb +225 -0
- data/rust/project/write_if_changed.rb +83 -0
- data/rust/project.rb +30 -0
- data/rust/project_rust_pipeline.rb +383 -0
- data/rust/src/exemplar/commands.rs +279 -0
- data/rust/src/exemplar/constraints.rs +18 -0
- data/rust/src/exemplar/fielded.rs +280 -0
- data/rust/src/exemplar/json.rs +277 -0
- data/rust/src/exemplar/mod.rs +17 -0
- data/rust/src/exemplar/mutations.rs +80 -0
- data/rust/src/exemplar/queries.rs +33 -0
- data/rust/src/exemplar/reactions.rs +88 -0
- data/rust/src/exemplar/read_models.rs +48 -0
- data/rust/src/exemplar/registry.rs +166 -0
- data/rust/src/exemplar/types.rs +44 -0
- data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
- data/rust/src/kernel/attribute_shapes/list.rs +18 -0
- data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
- data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
- data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
- data/rust/src/kernel/cli.rs +585 -0
- data/rust/src/kernel/dispatch.rs +1090 -0
- data/rust/src/kernel/expr.rs +458 -0
- data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
- data/rust/src/kernel/expression_operators/comparison.rs +48 -0
- data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
- data/rust/src/kernel/expression_operators/logical.rs +13 -0
- data/rust/src/kernel/expression_operators/membership.rs +18 -0
- data/rust/src/kernel/expression_operators/mod.rs +68 -0
- data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
- data/rust/src/kernel/expression_operators/positional.rs +126 -0
- data/rust/src/kernel/expression_operators/presence.rs +101 -0
- data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
- data/rust/src/kernel/expression_operators/sized.rs +35 -0
- data/rust/src/kernel/expression_operators/text.rs +79 -0
- data/rust/src/kernel/expression_operators/to_string.rs +18 -0
- data/rust/src/kernel/json.rs +779 -0
- data/rust/src/kernel/mod.rs +133 -0
- data/rust/src/kernel/named_query.rs +219 -0
- data/rust/src/kernel/orchestrate.rs +1810 -0
- data/rust/src/kernel/pattern.rs +307 -0
- data/rust/src/kernel/query_comparators.rs +206 -0
- data/rust/src/kernel/query_ordering.rs +103 -0
- data/rust/src/kernel/read_model.rs +914 -0
- data/rust/src/kernel/reference_lookup.rs +149 -0
- data/rust/src/kernel/repository.rs +468 -0
- data/rust/src/kernel/routing.rs +612 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
- data/rust/src/kernel/vocab/field_hint.rs +70 -0
- data/rust/src/kernel/vocab/mod.rs +20 -0
- data/rust/src/kernel/vocab/query_comparator.rs +65 -0
- data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
- data/rust/src/lib.rs +8 -0
- data/rust/src/main.rs +16 -0
- data/rust/web/.gitignore +1 -0
- data/rust/web/Cargo.lock +119 -0
- data/rust/web/Cargo.toml +18 -0
- data/rust/web/src/lib.rs +8 -0
- metadata +478 -12
- data/lib/hecks/facade/cli_door.rb +0 -178
- data/lib/hecks/facade/cli_runner.rb +0 -271
- data/lib/hecks/facade/handle.rb +0 -293
- data/lib/hecks/facade/json_door.rb +0 -247
- data/lib/hecks/facade/surface/aggregate_door.rb +0 -222
- data/lib/hecks/facade/surface/chapter.rb +0 -116
- data/lib/hecks/facade/surface.rb +0 -61
- data/lib/hecks/mcp_door_scope.rb +0 -176
|
@@ -4,46 +4,8 @@ require_relative "shared"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Projections
|
|
6
6
|
module Deploy
|
|
7
|
-
# The AWS Lambda deploy target
|
|
8
|
-
#
|
|
9
|
-
# not an ordinary projection: it reads a domain's own
|
|
10
|
-
# `deployed_to("AwsLambda")` `.world` settings, not only its
|
|
11
|
-
# declaration, because a running system has to know how it is wired.
|
|
12
|
-
#
|
|
13
|
-
# `bin/project_deploy` finds and boots the domain's own chapter and
|
|
14
|
-
# its `.world`/`.hecksagon` bindings, calls this through
|
|
15
|
-
# `Projector.call(:aws_lambda, bluebook:, options:, world:)`, and
|
|
16
|
-
# writes the returned tree — see that script for the CLI-facing parts
|
|
17
|
-
# (ARGV parsing, `--tenant`/`--schema`, finding the `.world` file) this
|
|
18
|
-
# target never needs to know about.
|
|
19
|
-
#
|
|
20
|
-
# ## What this generates
|
|
21
|
-
#
|
|
22
|
-
# The SAM template and build Makefile for `rust/host` (the
|
|
23
|
-
# wasmtime+Postgres Lambda entry point) from a domain's own `.world`
|
|
24
|
-
# file, the same way `bin/project_wasm` generates the `.wasm` artifact
|
|
25
|
-
# from the domain's own `.bluebook` — no hand-authored deployment
|
|
26
|
-
# config for any given domain, only a generator run against whatever it
|
|
27
|
-
# declares:
|
|
28
|
-
#
|
|
29
|
-
# deployed_to("AwsLambda") { region "..."; memory 512; timeout 10 }
|
|
30
|
-
#
|
|
31
|
-
# is a verb in a domain's `.world` file, read through `Hecks.world`'s
|
|
32
|
-
# own generic settings bag
|
|
33
|
-
# (`lib/hecks/bluebook/dsl/world_builder.rb#method_missing`).
|
|
34
|
-
#
|
|
35
|
-
# Self-contained: the returned `template.yaml` owns its own private
|
|
36
|
-
# VPC, subnets, and RDS Postgres instance, not just the Lambda — no
|
|
37
|
-
# externally-supplied database, no secret anyone has to type (RDS's
|
|
38
|
-
# own `ManageMasterUserPassword` plus a CloudFormation dynamic
|
|
39
|
-
# reference compose `DATABASE_URL` at deploy time). `PackageType:
|
|
40
|
-
# Zip`, `Runtime: provided.al2023` — no container, no Docker, no ECR.
|
|
41
|
-
#
|
|
42
|
-
# `Shared` (`lib/hecks/projections/deploy/shared.rb`) carries the parts
|
|
43
|
-
# of this generator `Fargate` needs too — VPC/subnet/security-group
|
|
44
|
-
# resources, RDS/Aurora, `bastion.yaml`, and the era-minting/
|
|
45
|
-
# translation Make recipes — plain functions, not a registered target
|
|
46
|
-
# of their own.
|
|
7
|
+
# The AWS Lambda deploy target (ADR 0018): renders `template.yaml`,
|
|
8
|
+
# `Makefile`, `samconfig.toml`, and `bastion.yaml` from a domain's `.world`.
|
|
47
9
|
module Lambda
|
|
48
10
|
extend Projector::Target
|
|
49
11
|
|
|
@@ -51,37 +13,14 @@ module Hecks
|
|
|
51
13
|
|
|
52
14
|
module_function
|
|
53
15
|
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
# `bastion.yaml` for one domain's `deployed_to("AwsLambda")` deploy
|
|
57
|
-
# target.
|
|
16
|
+
# Renders `template.yaml`, `Makefile`, `samconfig.toml`, and (unless
|
|
17
|
+
# this domain shares another domain's RDS instance) `bastion.yaml`.
|
|
58
18
|
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
# @param bluebook [Bluebook::Behaviour::Chapter] the domain's own booted chapter
|
|
65
|
-
# @param options [Hash] generation options
|
|
66
|
-
# @option options [Bluebook::World] :world the domain's `.world` settings,
|
|
67
|
-
# required — `Projector.call`'s own `world:` keyword merges this in
|
|
68
|
-
# @option options [String] :domain_dir the domain directory's path
|
|
69
|
-
# @option options [String] :root the hecks project root, for `rust/host`,
|
|
70
|
-
# `rust/dist`, and this project's own `Gemfile.lock`
|
|
71
|
-
# @option options [String] :world_file the `.world` file's own path, quoted in
|
|
72
|
-
# refusal messages
|
|
73
|
-
# @option options [Runtime::Registry] :cross_domain_registry every chapter this
|
|
74
|
-
# domain loads (its own, plus any framework attachments), for resolving
|
|
75
|
-
# cross-domain policy invoke targets
|
|
76
|
-
# @option options [Hash] :tenant `--tenant`/`--schema` overrides
|
|
77
|
-
# (`{tenant: "acme", schema: "acme"}`), or omitted for an ordinary deploy
|
|
78
|
-
# @return [Hash{String => String}] `"template.yaml"`, `"Makefile"`,
|
|
79
|
-
# `"samconfig.toml"`, and — unless this domain declares `database "Shared"` —
|
|
80
|
-
# `"bastion.yaml"`, each mapped to its rendered content
|
|
81
|
-
# @raise [ArgumentError] if the domain's own deploy settings conflict (both
|
|
82
|
-
# `web "Rust"` and a `lambda_handler.rb`, `database "Shared"` with no
|
|
83
|
-
# `owner`, and the other refusals `deploy.bluebook`'s own
|
|
84
|
-
# `LambdaTarget.Declare` and this generator raise)
|
|
19
|
+
# @param bluebook [Bluebook::Behaviour::Chapter] the domain's booted chapter
|
|
20
|
+
# @param options [Hash] :world, :domain_dir, :root, :world_file, and
|
|
21
|
+
# :cross_domain_registry are required; :tenant is optional
|
|
22
|
+
# @return [Hash{String => String}] generated file contents by filename
|
|
23
|
+
# @raise [ArgumentError] if `deployed_to("AwsLambda")` is invalid
|
|
85
24
|
def call(bluebook:, options: {})
|
|
86
25
|
world = options.fetch(:world)
|
|
87
26
|
domain = options.fetch(:domain_dir)
|
|
@@ -95,19 +34,9 @@ module Hecks
|
|
|
95
34
|
|
|
96
35
|
deploy_settings = world.for_verb("deployed_to")
|
|
97
36
|
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
# lib/hecks/runtime/registry.rb) the same way `Runtime::Loader.boot`'s
|
|
102
|
-
# own first phase does, without that method's later `run_boot_gates!`/
|
|
103
|
-
# `dispatcher_for` steps — those need a live persistence adapter (a real
|
|
104
|
-
# Postgres connection for a PostgresEra-bound domain), which a static
|
|
105
|
-
# template generator must never require. A registry of its own, not
|
|
106
|
-
# `cross_domain_registry` — that one boots this domain's bluebook too,
|
|
107
|
-
# but through a different, hand-rolled port/adapter load (persistence
|
|
108
|
-
# + extraction + memory + prism only) than the one `pending_privacy_
|
|
109
|
-
# markings` was verified against; kept separate rather than assumed
|
|
110
|
-
# equivalent.
|
|
37
|
+
# Structural-only load (no `run_boot_gates!`/live persistence adapter)
|
|
38
|
+
# populates `registry.pending_privacy_markings` for pii detection below
|
|
39
|
+
# without requiring a real Postgres connection to generate a template.
|
|
111
40
|
pii_registry = Hecks::Runtime::Registry.new(root: File.expand_path(domain))
|
|
112
41
|
Hecks.with_registry(pii_registry) do
|
|
113
42
|
bootstrap = Hecks::Ports::Loading.bootstrap
|
|
@@ -118,21 +47,12 @@ module Hecks
|
|
|
118
47
|
Dir.glob(File.join(bluebook_dir, "*.hecksagon")).each { |file| Kernel.load(file) }
|
|
119
48
|
end
|
|
120
49
|
|
|
121
|
-
# Only `category: "pii"`
|
|
122
|
-
#
|
|
123
|
-
# redaction concern (Privacy::Marking's own mechanism) without also
|
|
124
|
-
# provisioning CloudFront/WAF, which this generator has no way to know
|
|
125
|
-
# is warranted for, say, health data under a different compliance
|
|
126
|
-
# regime entirely. A domain wanting the same protection for a
|
|
127
|
-
# non-"pii" category marks it "pii" too — the category string is
|
|
128
|
-
# open-ended by design (privacy.bluebook's own header), not a closed
|
|
129
|
-
# enum this generator could instead enumerate.
|
|
50
|
+
# Only `category: "pii"` triggers CloudFront/WAF provisioning; other
|
|
51
|
+
# marking categories (e.g. "phi") stay a Governance/redaction concern.
|
|
130
52
|
pii_detected = pii_registry.pending_privacy_markings.any? { |marking| marking[:category].to_s == "pii" }
|
|
131
53
|
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
# `infra_name`/`hecks_schema` are computed below, so both read it the
|
|
135
|
-
# same way they already read any other `deployed_to` setting.
|
|
54
|
+
# Applied before infra_name/hecks_schema below, so both read the
|
|
55
|
+
# override the same way they read any other `deployed_to` setting.
|
|
136
56
|
if tenant_options[:tenant]
|
|
137
57
|
base_stack_name = deploy_settings[:stack_name] || domain_name
|
|
138
58
|
deploy_settings = deploy_settings.merge(
|
|
@@ -141,57 +61,20 @@ module Hecks
|
|
|
141
61
|
)
|
|
142
62
|
end
|
|
143
63
|
|
|
144
|
-
#
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
#
|
|
148
|
-
# `stack_name` at all. They diverge on purpose for a domain whose
|
|
149
|
-
# declared identity (`Hecks.bluebook`, hence `world.domain`, hence
|
|
150
|
-
# `.world`'s own filename — `domain_name` above, needed just to find
|
|
151
|
-
# that file) does not match its live AWS stack's own name: a
|
|
152
|
-
# CloudFormation stack cannot be renamed in place, and this domain's
|
|
153
|
-
# logical ids are already load-bearing on a real RDS instance other
|
|
154
|
-
# domains borrow from in Shared mode (see `owner_stack_name` below) —
|
|
155
|
-
# regenerating the template under a new stack/logical-id would not
|
|
156
|
-
# rename anything, it would stand up a second, empty, disconnected
|
|
157
|
-
# set of AWS resources next to the real one. `stack_name
|
|
158
|
-
# "embryonaut"` in `deployed_to("AwsLambda")` pins the original name
|
|
159
|
-
# forever, independent of any later `formerly_known_as`-style rename
|
|
160
|
-
# to the domain's own declared identity.
|
|
64
|
+
# Every AWS-facing name (stack, logical ids, S3 prefixes, secrets)
|
|
65
|
+
# reads `infra_name`, not `domain_name`, so a `stack_name` override
|
|
66
|
+
# survives a later `formerly_known_as` rename of the declared identity
|
|
67
|
+
# without renaming (and disconnecting from) the live AWS stack.
|
|
161
68
|
infra_name = deploy_settings[:stack_name] || domain_name
|
|
162
69
|
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
# already a valid database name and nothing ever needed a second
|
|
167
|
-
# variable. `stack_name "quality-control-webhook"` (a real, live,
|
|
168
|
-
# multi-word choice — the first one) broke that assumption for real:
|
|
169
|
-
# RDS's own DBName/DatabaseName parameter refuses it outright
|
|
170
|
-
# ("DBName must begin with a letter and contain only alphanumeric
|
|
171
|
-
# characters" — confirmed live, a genuine CREATE_FAILED before this
|
|
172
|
-
# existed), and `infra_name` unsanitized is also what every downstream
|
|
173
|
-
# consumer (WebFunction/#{logical_id}'s own DB_NAME Environment
|
|
174
|
-
# variable, every Makefile shell command that connects `-d
|
|
175
|
-
# #{infra_name}`) reads as the database to actually connect to — so
|
|
176
|
-
# every one of those has to agree on the same sanitized spelling, not
|
|
177
|
-
# just the one CloudFormation property that happened to refuse loudly.
|
|
178
|
-
# Stack names, logical ids, and S3 prefixes are unchanged (still read
|
|
179
|
-
# `infra_name` directly) — CloudFormation/S3 both allow hyphens fine;
|
|
180
|
-
# only the actual database identifier needed this at all.
|
|
70
|
+
# RDS's own DBName/DatabaseName parameter refuses non-alphanumeric
|
|
71
|
+
# characters; every downstream consumer (Environment vars, Makefile
|
|
72
|
+
# shell commands) has to read the same sanitized spelling.
|
|
181
73
|
db_name = infra_name.gsub(/[^a-zA-Z0-9]/, "")
|
|
182
74
|
|
|
183
|
-
#
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
# replacing a `fetch(:region) { abort ... }` chain with
|
|
187
|
-
# real, named, corpus-testable refusals: memory/timeout are actual
|
|
188
|
-
# Integers here (never silently stringified) and checked against
|
|
189
|
-
# Lambda's own real 128-10240 MB / 900s ceilings, not just "present or
|
|
190
|
-
# not." Only the default values stay Ruby's own concern —
|
|
191
|
-
# deploy.bluebook's own header explains why: a command-level `default:`
|
|
192
|
-
# is honored only at the JSON/Rust-codegen boundary, never by Ruby's
|
|
193
|
-
# own direct dispatch — so this validates a fully-resolved target,
|
|
194
|
-
# after defaulting, not instead of it.
|
|
75
|
+
# Dispatches into LambdaTarget.Declare's given/invariant machinery for
|
|
76
|
+
# named, corpus-testable refusals (memory/timeout checked as real
|
|
77
|
+
# Integers against Lambda's 128-10240 MB / 900s ceilings).
|
|
195
78
|
deploy_dispatcher = Hecks.boot(File.expand_path("../../deploy", __dir__))
|
|
196
79
|
begin
|
|
197
80
|
target = deploy_dispatcher.dispatch(
|
|
@@ -201,14 +84,7 @@ module Hecks
|
|
|
201
84
|
region: { value: deploy_settings[:region] },
|
|
202
85
|
memory: { value: deploy_settings.fetch(:memory, 512) },
|
|
203
86
|
timeout: { value: deploy_settings.fetch(:timeout, 10) },
|
|
204
|
-
# "Postgres" — Embryonaut's own live choice, and every domain
|
|
205
|
-
# declared before this setting existed — unless a domain's own
|
|
206
|
-
# deployed_to("AwsLambda") names "Aurora" explicitly.
|
|
207
87
|
database: { value: deploy_settings.fetch(:database, "Postgres") },
|
|
208
|
-
# "None" — every domain declared before this setting existed,
|
|
209
|
-
# Embryonaut included (its own public web UI is the separate,
|
|
210
|
-
# pre-existing lambda_handler.rb-file-presence mechanism, not this
|
|
211
|
-
# attribute) — unless a domain names "Rust" explicitly.
|
|
212
88
|
web: { value: deploy_settings.fetch(:web, "None") }
|
|
213
89
|
}
|
|
214
90
|
).instance
|
|
@@ -216,68 +92,26 @@ module Hecks
|
|
|
216
92
|
raise ArgumentError, "#{world_file}'s deployed_to(\"AwsLambda\") is invalid: #{e.message}"
|
|
217
93
|
end
|
|
218
94
|
|
|
219
|
-
# `dispatch "None"`
|
|
220
|
-
#
|
|
221
|
-
#
|
|
222
|
-
#
|
|
223
|
-
# comment on `infra_name`, above, has the full reasoning for why those
|
|
224
|
-
# stay Ruby-level rather than validated attributes).
|
|
225
|
-
#
|
|
226
|
-
# `dispatch "None"` decides whether the primary rust/host dispatch
|
|
227
|
-
# Lambda (`#{logical_id}`, computed further below) gets generated at
|
|
228
|
-
# all. Default (unset, every domain declared
|
|
229
|
-
# before this setting existed) keeps generating the rust/host wasmtime
|
|
230
|
-
# dispatch Lambda exactly as always — "None" is the new, opt-in case: no
|
|
231
|
-
# rust/host dispatch Lambda for this domain at all, because a domain
|
|
232
|
-
# with no `.bluebook`-shaped command surface of its own to dispatch
|
|
233
|
-
# through (QualityControl's own real first user: a driving GitHub
|
|
234
|
-
# webhook adapter, `lib/hecks/adapters/driving/github_webhook.rb`,
|
|
235
|
-
# calling straight into `QualityControl::Clearance` through Ruby, never
|
|
236
|
-
# through rust/host's wasmtime dispatch at all) has nothing for that
|
|
237
|
-
# Lambda to ever serve. Reuses the existing `lambda_handler.rb`-file-
|
|
238
|
-
# presence WebFunction mechanism wholesale (Runtime: ruby3.2, Function
|
|
239
|
-
# URL AuthType `NONE`, container build + patch-pg-native for `pg`'s native
|
|
240
|
-
# extension) rather than inventing a second Ruby-Lambda shape — the
|
|
241
|
-
# WebFunction path already proved itself for real (Embryonaut's own live
|
|
242
|
-
# stack) before this ever needed a second consumer; deploy.bluebook's
|
|
243
|
-
# own generator, "one generator reads what a domain declares," extends
|
|
244
|
-
# rather than forks.
|
|
95
|
+
# `dispatch "None"` skips generating a rust/host dispatch Lambda for a
|
|
96
|
+
# domain with no `.bluebook` command surface to dispatch through (e.g.
|
|
97
|
+
# QualityControl's GitHub webhook, dispatching straight into Ruby);
|
|
98
|
+
# such a domain's WebFunction becomes its only Lambda.
|
|
245
99
|
dispatch_none = deploy_settings[:dispatch].to_s == "None"
|
|
246
100
|
|
|
247
|
-
#
|
|
248
|
-
#
|
|
249
|
-
# real plaintext under, once it resolves `#{secret_env}_ARN` (below,
|
|
250
|
-
# WebFunction's own Environment) via the AWS SDK the same cold-start
|
|
251
|
-
# pattern DATABASE_URL/SESSION_SECRET/GOOGLE_CLIENT_ID already use
|
|
252
|
-
# (WebFunction's own header comment: "fetch at runtime, over the SDK,
|
|
253
|
-
# never let CloudFormation/Lambda configuration see it at all"). Wired
|
|
254
|
-
# generically — any WebFunction-shaped domain declaring one gets its own
|
|
255
|
-
# auto-generated, never-typed-or-seen SecretsManager secret + least-
|
|
256
|
-
# privilege grant, not something specific to GitHub webhooks; this
|
|
257
|
-
# domain's own `qa/lambda_handler.rb` is simply the first real consumer.
|
|
101
|
+
# Names the env var WebFunction's `lambda_handler.rb` finds the fetched
|
|
102
|
+
# secret's plaintext under, once it resolves `#{secret_env}_ARN`.
|
|
258
103
|
webhook_secret_env = deploy_settings[:secret_env]
|
|
259
104
|
|
|
260
|
-
#
|
|
261
|
-
# `
|
|
262
|
-
# than assumed, since `dispatch "None"` is a generic mechanism (any
|
|
263
|
-
# future domain could adopt it, each with its own wrapper module name)
|
|
264
|
-
# — "WebLambdaHandler" stays the fixed, historical default for every
|
|
265
|
-
# domain that has never set this (Embryonaut's own, unchanged).
|
|
105
|
+
# The module name `lambda_handler.rb` actually defines; named here
|
|
106
|
+
# since `dispatch "None"` domains may each pick their own.
|
|
266
107
|
webhook_handler_module = deploy_settings.fetch(:handler_module, "WebLambdaHandler")
|
|
267
108
|
if dispatch_none && !deploy_settings.key?(:handler_module)
|
|
268
109
|
raise ArgumentError, "#{world_file}'s deployed_to(\"AwsLambda\") declares dispatch \"None\" but no handler_module — add handler_module \"YourModuleName\" naming the module #{domain}/lambda_handler.rb defines."
|
|
269
110
|
end
|
|
270
111
|
|
|
271
|
-
#
|
|
272
|
-
#
|
|
273
|
-
#
|
|
274
|
-
# that itself declares a cross-domain policy, and a policy nested inside
|
|
275
|
-
# `aggregate "X" do ... end` is exactly as real a cross-domain trigger as
|
|
276
|
-
# one declared at the bluebook's own top level (docs/implemented/guides/policies-
|
|
277
|
-
# and-process-managers.md: "A policy does not care where it was
|
|
278
|
-
# written"). `to_h`'s own `policies` already flattens aggregate-scoped
|
|
279
|
-
# and top-level policies into one list per bluebook — read the same way
|
|
280
|
-
# `rust/project/reactions.rb`'s `emit_cross_domain_policy_table` does.
|
|
112
|
+
# Scans every loaded chapter, not just this domain's own — a
|
|
113
|
+
# `uses_framework`-attached chapter can itself declare a cross-domain
|
|
114
|
+
# policy, including one nested inside `aggregate "X" do ... end`.
|
|
281
115
|
cross_domain_lambda_targets = cross_domain_registry.bluebooks.flat_map { |chapter_name, bluebook|
|
|
282
116
|
bluebook.policies.select(&:target_domain).map(&:target_domain)
|
|
283
117
|
}.uniq.sort
|
|
@@ -291,15 +125,8 @@ module Hecks
|
|
|
291
125
|
shared = database == "Shared"
|
|
292
126
|
rust_web = target.state[:web].value == "Rust"
|
|
293
127
|
|
|
294
|
-
#
|
|
295
|
-
#
|
|
296
|
-
# refused by CloudFormation outside us-east-1, independent of which
|
|
297
|
-
# region the distribution itself, being global, would otherwise
|
|
298
|
-
# suggest). Refused here, before writing a single file, the same
|
|
299
|
-
# discipline this generator already holds every other one of its own
|
|
300
|
-
# refusals to — a generated template that would only fail at deploy
|
|
301
|
-
# time, in a region a caller may not think to suspect, is a worse
|
|
302
|
-
# failure mode than refusing now with the fix named.
|
|
128
|
+
# AWS::WAFv2::WebACL with Scope: CLOUDFRONT is refused by
|
|
129
|
+
# CloudFormation outside us-east-1; refused here before writing a file.
|
|
303
130
|
if pii_detected && region != "us-east-1"
|
|
304
131
|
raise ArgumentError, "#{world_file} marks a field \"pii\" but deployed_to(\"AwsLambda\") sets region " \
|
|
305
132
|
"#{region.inspect} — a CloudFront-scoped WAFv2 WebACL can only be created in " \
|
|
@@ -307,25 +134,13 @@ module Hecks
|
|
|
307
134
|
"genuinely holds none."
|
|
308
135
|
end
|
|
309
136
|
|
|
310
|
-
# Read
|
|
311
|
-
#
|
|
312
|
-
# `target.state[...]` field above, which `LambdaTarget` validates as
|
|
313
|
-
# always-present for every `AwsLambda` deployment regardless of pii.
|
|
314
|
-
# "none" (`RestrictionType: none`, `Locations: []`) is CloudFront's own
|
|
315
|
-
# default shape for "no restriction configured" — declaring one later
|
|
316
|
-
# is a `deployed_to` edit, not a template rewrite.
|
|
137
|
+
# Read off `deploy_settings` directly, not `target` — these two are
|
|
138
|
+
# optional and pii-only, unlike every always-validated `target.state`.
|
|
317
139
|
geo_restriction_type = deploy_settings[:geo_restriction] || "none"
|
|
318
140
|
geo_restriction_countries = deploy_settings[:geo_restriction_countries] || []
|
|
319
141
|
|
|
320
|
-
#
|
|
321
|
-
#
|
|
322
|
-
# isolated by a native Postgres schema (this domain's own lowercase
|
|
323
|
-
# name) rather than a separate database. `owner` is read the same way
|
|
324
|
-
# `pg_version`/`google_oauth_present` below are — a Ruby-level fact off
|
|
325
|
-
# `deploy_settings` directly, not one of `Deploy::LambdaTarget.Declare`'s
|
|
326
|
-
# own validated attributes (deploy.bluebook's own comment on why: which
|
|
327
|
-
# domain owns the shared instance is a deploy-time wiring fact, not a
|
|
328
|
-
# business invariant).
|
|
142
|
+
# Shared mode provisions no RDS/VPC of its own; it borrows another
|
|
143
|
+
# already-deployed domain's instance, isolated by Postgres schema.
|
|
329
144
|
if shared
|
|
330
145
|
owner_domain_name = deploy_settings[:owner] or raise ArgumentError, <<~MSG
|
|
331
146
|
#{world_file}'s deployed_to("AwsLambda") declares database "Shared" but no owner. Add one, e.g.:
|
|
@@ -338,182 +153,83 @@ module Hecks
|
|
|
338
153
|
|
|
339
154
|
naming the already-deployed domain whose Postgres instance this one borrows.
|
|
340
155
|
MSG
|
|
341
|
-
# `
|
|
342
|
-
#
|
|
343
|
-
# commit to for this domain, and the default for the owner too. Not
|
|
344
|
-
# always the owner's real stack name, though — a domain generated
|
|
345
|
-
# before this convention existed keeps whatever it was actually
|
|
346
|
-
# deployed as (real, live example: Embryonaut's own stack is
|
|
347
|
-
# "hecksagain-embryonaut", a legacy prefix, not "hecks-embryonaut" --
|
|
348
|
-
# `deploy:`'s own owner-outputs lookup below would look for a stack
|
|
349
|
-
# that has never existed and fail before this domain's own `sam
|
|
350
|
-
# deploy` ever ran). `owner_stack "hecksagain-embryonaut"` is the
|
|
351
|
-
# escape hatch, read the same deploy_settings-direct way `owner`
|
|
352
|
-
# itself is (deploy.bluebook's own comment on why neither lives in
|
|
353
|
-
# the validated LambdaTarget aggregate) — optional, defaults to the
|
|
354
|
-
# ordinary convention for every domain that doesn't need it.
|
|
156
|
+
# `owner_stack` is the escape hatch for an owner whose live stack
|
|
157
|
+
# name predates the "hecks-<lowercase name>" convention below.
|
|
355
158
|
owner_stack_name = deploy_settings[:owner_stack] || "hecks-#{owner_domain_name.downcase}"
|
|
356
159
|
owner_db_name = owner_domain_name.downcase
|
|
357
160
|
end
|
|
358
161
|
|
|
359
|
-
#
|
|
360
|
-
#
|
|
361
|
-
#
|
|
362
|
-
# schema the real migration creates for it, `CREATE SCHEMA
|
|
363
|
-
# #{infra_name}`, and needs no separate declaration since Shared mode
|
|
364
|
-
# already implies it). Optional and explicit otherwise
|
|
365
|
-
# (`deployed_to("AwsLambda") { schema "embryonaut" }`) — for an owning
|
|
366
|
-
# domain that later migrates itself onto a shared instance's own schema
|
|
367
|
-
# too (Embryonaut, in the real migration this exists for), without
|
|
368
|
-
# switching its own `database` setting away from where its real RDS
|
|
369
|
-
# instance already lives.
|
|
162
|
+
# rust/host's optional counterpart to Ruby's `settings[:schema]`.
|
|
163
|
+
# Automatic (this domain's lowercase name) in Shared mode; otherwise
|
|
164
|
+
# explicit via `deployed_to("AwsLambda") { schema "..." }`.
|
|
370
165
|
hecks_schema = shared ? infra_name : deploy_settings[:schema]
|
|
371
166
|
|
|
372
167
|
logical_id = "#{infra_name.split(/[_-]/).map(&:capitalize).join}Function"
|
|
373
|
-
#
|
|
374
|
-
#
|
|
375
|
-
# (
|
|
376
|
-
#
|
|
377
|
-
# generated by the pre-rename hecksagain fork). Same shape and
|
|
378
|
-
# reasoning as `owner_stack` above — read `deploy_settings`-direct, not
|
|
379
|
-
# validated by LambdaTarget, optional — but for this domain rather than
|
|
380
|
-
# the owner it borrows from. Everything downstream (stack, FunctionName
|
|
381
|
-
# for both Lambdas, the OAuth secret name and its IAM policy, the
|
|
382
|
-
# bastion stack, samconfig's stack_name/s3_prefix) reads `stack_name`,
|
|
383
|
-
# so one setting moves them all together; `infra_name` (logical ids,
|
|
384
|
-
# deploy/<dir>, DB name) is untouched — that is `stack_name`'s job.
|
|
385
|
-
# Defaults to "hecks", so every domain that doesn't set it regenerates
|
|
386
|
-
# byte-identically.
|
|
168
|
+
# The "hecks-" half of this domain's own stack name; overridable for a
|
|
169
|
+
# live stack that predates the convention. Everything downstream
|
|
170
|
+
# (FunctionNames, OAuth secret, bastion stack, samconfig) reads
|
|
171
|
+
# `stack_name`; `infra_name` (logical ids, DB name) is untouched.
|
|
387
172
|
stack_prefix = deploy_settings[:stack_prefix] || "hecks"
|
|
388
173
|
stack_name = "#{stack_prefix}-#{infra_name}"
|
|
389
174
|
|
|
390
175
|
db_id = "#{logical_id.sub(/Function\z/, '')}Db"
|
|
391
|
-
#
|
|
392
|
-
#
|
|
393
|
-
#
|
|
394
|
-
# reference below has to point at the cluster, not `db_id` itself,
|
|
395
|
-
# when Aurora is chosen. The plain-RDS path is unaffected: `db_ref_id`
|
|
396
|
-
# equals `db_id` exactly, so every one of those references renders
|
|
397
|
-
# byte-identical to before this existed.
|
|
176
|
+
# Aurora splits the database into a DBCluster plus DBInstance(s), so
|
|
177
|
+
# every `.Endpoint.Address`/`.MasterUserSecret` reference below has to
|
|
178
|
+
# point at the cluster, not `db_id`, when Aurora is chosen.
|
|
398
179
|
db_ref_id = aurora ? "#{db_id}Cluster" : db_id
|
|
399
180
|
|
|
400
|
-
#
|
|
401
|
-
#
|
|
402
|
-
#
|
|
403
|
-
# `
|
|
404
|
-
# managed-password feature at all). `secret_sub` is the identifier for
|
|
405
|
-
# use inside a `!Sub` "${...}" interpolation (dot-free for a plain
|
|
406
|
-
# `!Ref`, dotted for a `!GetAtt` attribute — CloudFormation's `${}`
|
|
407
|
-
# auto-detects either shape); `secret_intrinsic` is the same fact
|
|
408
|
-
# spelled as a bare YAML value, for the one site (stack_outputs) that
|
|
409
|
-
# isn't inside a `!Sub` string at all.
|
|
181
|
+
# Aurora uses a self-managed secret (ManageMasterUserPassword rotation
|
|
182
|
+
# would break a Lambda's static Environment); plain RDS uses its own
|
|
183
|
+
# MasterUserSecret attribute. `secret_sub` is for `!Sub "${...}"`
|
|
184
|
+
# interpolation; `secret_intrinsic` is the same fact as a bare value.
|
|
410
185
|
secret_sub = aurora ? "#{db_id}Secret" : "#{db_ref_id}.MasterUserSecret.SecretArn"
|
|
411
186
|
secret_intrinsic = aurora ? "!Ref #{db_id}Secret" : "!GetAtt #{db_ref_id}.MasterUserSecret.SecretArn"
|
|
412
|
-
#
|
|
413
|
-
#
|
|
414
|
-
# deploy-time `{{resolve:secretsmanager:...}}` dynamic reference (see
|
|
415
|
-
# that Environment.Variables comment below for why DATABASE_URL moved
|
|
416
|
-
# off that mechanism entirely). Shared mode has no `secret_sub` of its
|
|
417
|
-
# own to reach for -- `OwningDatabaseSecretArn` (this template's own
|
|
418
|
-
# Parameter, filled from the owning stack's Output) already is the bare
|
|
419
|
-
# ARN string, not a Ref/GetAtt target inside this stack.
|
|
187
|
+
# Shared mode has no `secret_sub` of its own; `OwningDatabaseSecretArn`
|
|
188
|
+
# (this template's Parameter) is already a bare ARN string.
|
|
420
189
|
db_secret_ref = shared ? "OwningDatabaseSecretArn" : secret_sub
|
|
421
190
|
|
|
422
|
-
#
|
|
423
|
-
#
|
|
424
|
-
#
|
|
425
|
-
# adapter Sinatra runs through. No new .world verb for this: the file
|
|
426
|
-
# itself is the declaration, the same way a `.hecksagon` file's mere
|
|
427
|
-
# existence is what makes a domain framework-attached.
|
|
191
|
+
# Opt-in by file presence: a domain wanting its own web app on Lambda
|
|
192
|
+
# drops a `lambda_handler.rb` (the Rack-to-Function-URL-event adapter
|
|
193
|
+
# Sinatra runs through) at its own root. No separate .world verb.
|
|
428
194
|
web_handler_present = File.exist?(File.join(domain, "lambda_handler.rb"))
|
|
429
195
|
|
|
430
|
-
# A domain declares one web story, not both
|
|
431
|
-
# serves its own web UI in-process) and a `lambda_handler.rb` (a
|
|
432
|
-
# separate Ruby WebFunction) are two different mechanisms for the same
|
|
433
|
-
# job; picking one silently when both are present would be exactly the
|
|
434
|
-
# kind of ambiguity this generator's own `deploy.bluebook` gate exists
|
|
435
|
-
# to refuse instead of guess at.
|
|
196
|
+
# A domain declares one web story, not both.
|
|
436
197
|
if rust_web && web_handler_present
|
|
437
198
|
raise ArgumentError, "#{domain} declares both web \"Rust\" (#{world_file}) and a lambda_handler.rb (#{File.join(domain, 'lambda_handler.rb')}) — pick one."
|
|
438
199
|
end
|
|
439
200
|
|
|
440
|
-
# Read
|
|
441
|
-
# `patch-pg-native`
|
|
442
|
-
#
|
|
443
|
-
# gem's own Ruby wrapper (lib/pg.rb, autoloads, etc.) can drift apart.
|
|
444
|
-
# The plain, platform-less lock line ("pg (1.6.3)") is what this
|
|
445
|
-
# matches — platform-suffixed siblings ("pg (1.6.3-aarch64-linux)")
|
|
446
|
-
# fail the all-digits-and-dots capture on purpose.
|
|
447
|
-
#
|
|
448
|
-
# Optional, not required whenever a web app exists — Embryonaut's own
|
|
449
|
-
# Sinatra app talks to Postgres directly (hence `pg` in its
|
|
450
|
-
# Gemfile.lock) but that's that app's own choice, not a rule every
|
|
451
|
-
# WebFunction follows. A web app that only ever dispatches through a
|
|
452
|
-
# Lambda-routed domain (RemoteDispatcher/Adapters::Lambda, never a
|
|
453
|
-
# local Postgres connection — hecks_on_web's own apps, e.g.) has no
|
|
454
|
-
# `pg` gem and needs none of the native-extension patching below; the
|
|
455
|
-
# generated Makefile's own `deploy:` target branches on whether this
|
|
456
|
-
# is present, same convention `google_oauth_present` already uses for
|
|
457
|
-
# its own opt-in machinery.
|
|
201
|
+
# Read from the domain's own Gemfile.lock, not hardcoded — the
|
|
202
|
+
# `patch-pg-native` build recipe below must fetch the same pg version
|
|
203
|
+
# Bundler resolved, or the from-source extension can drift from it.
|
|
458
204
|
pg_version = nil
|
|
459
205
|
if web_handler_present
|
|
460
206
|
# `dispatch "None"` reads this project's own root Gemfile.lock, not
|
|
461
|
-
# #{domain}'s
|
|
462
|
-
#
|
|
463
|
-
# #{domain} (a bluebook directory inside this very repo, e.g. "qa")
|
|
464
|
-
# has no Gemfile of its own to bundle at all; it needs `lib/hecks`
|
|
465
|
-
# and this project's own Gemfile.lock, sitting one level up. Every
|
|
466
|
-
# other WebFunction (Embryonaut's own self-contained app checkout,
|
|
467
|
-
# vendoring its own copy of hecks) keeps reading its own, unchanged.
|
|
207
|
+
# #{domain}'s: #{domain} has no Gemfile of its own to bundle (it
|
|
208
|
+
# needs `lib/hecks` from one level up), so CodeUri is `root` there.
|
|
468
209
|
pg_version_path = File.join(dispatch_none ? root : domain, "Gemfile.lock")
|
|
469
210
|
pg_version = File.read(pg_version_path)[/^\s+pg \(([\d.]+)\)/, 1]
|
|
470
211
|
end
|
|
471
212
|
web_logical_id = "WebFunction"
|
|
472
213
|
|
|
473
|
-
# `dispatch "None"`
|
|
474
|
-
#
|
|
475
|
-
# of its own to zip, so WebFunction's CodeUri has to be this whole
|
|
476
|
-
# project's own root instead (the identical absolute-path shape
|
|
477
|
-
# Embryonaut's own real, live deploy already uses for a checkout that
|
|
478
|
-
# isn't colocated with `deploy/<stack>/` either — bin/project_deploy's
|
|
479
|
-
# own header: "Self-contained... no hand-authored deployment config").
|
|
480
|
-
# `web_handler_relpath` follows the same split: #{domain}/lambda_handler
|
|
481
|
-
# is only the right relative path once CodeUri stops being #{domain}
|
|
482
|
-
# itself — every existing WebFunction (CodeUri: #{domain}) still finds
|
|
483
|
-
# its own lambda_handler.rb at that directory's own root, unchanged.
|
|
214
|
+
# Same `dispatch "None"` split as `pg_version_path` above: #{domain}
|
|
215
|
+
# alone has no Gemfile/lib/hecks to zip, so CodeUri is `root` instead.
|
|
484
216
|
web_code_uri = dispatch_none ? root : domain
|
|
485
217
|
web_handler_relpath = dispatch_none ? "#{domain}/lambda_handler" : "lambda_handler"
|
|
486
218
|
|
|
487
|
-
#
|
|
488
|
-
#
|
|
489
|
-
#
|
|
490
|
-
# this tool for anything but detecting the key is there); the real
|
|
491
|
-
# value is synced into Secrets Manager by `make sync-google-oauth`
|
|
492
|
-
# below, straight from that same file, at deploy time — never baked
|
|
493
|
-
# into this generated, git-tracked template.yaml as plaintext.
|
|
494
|
-
# Also gates the NAT Gateway/public subnet template.yaml's own "no NAT
|
|
495
|
-
# gateway" comment describes — computed here, once, before
|
|
496
|
-
# stack_outputs/bastion_parameters below need to know whether
|
|
497
|
-
# #{db_id}PublicSubnet exists at all to pass along to bastion.yaml.
|
|
219
|
+
# `.env.local` is #{domain}'s gitignored local-dev secrets file, read
|
|
220
|
+
# only to detect the key is present; `make sync-google-oauth` syncs
|
|
221
|
+
# the real value into Secrets Manager at deploy time.
|
|
498
222
|
google_oauth_present = (web_handler_present || rust_web) &&
|
|
499
223
|
File.exist?(File.join(domain, ".env.local")) &&
|
|
500
224
|
File.read(File.join(domain, ".env.local")).match?(/^GOOGLE_CLIENT_ID=\S/)
|
|
501
225
|
|
|
502
|
-
#
|
|
503
|
-
#
|
|
504
|
-
# build yet (web_handler_present's own Member-style direct-Postgres
|
|
505
|
-
# binding), real, unbuilt design, not a small addition. This check
|
|
506
|
-
# alone is why the google_oauth_present check below can assume
|
|
507
|
-
# web_handler_present is already false by the time it runs — it always
|
|
508
|
-
# aborts first when both are true.
|
|
226
|
+
# A shared-instance Ruby WebFunction needs cross-stack DATABASE_URL
|
|
227
|
+
# wiring this generator doesn't build yet.
|
|
509
228
|
if shared && web_handler_present
|
|
510
229
|
raise ArgumentError, "#{domain} declares both database \"Shared\" and a lambda_handler.rb — a shared-instance Ruby WebFunction isn't supported yet."
|
|
511
230
|
end
|
|
512
231
|
|
|
513
|
-
# `dispatch "None"` means WebFunction
|
|
514
|
-
# there is nothing left to reach through a Function URL at all without a
|
|
515
|
-
# `lambda_handler.rb` to serve it, and (below) no rust_web dispatch
|
|
516
|
-
# Lambda whose own Function URL that role could fall back to either.
|
|
232
|
+
# `dispatch "None"` means WebFunction is the domain's only Lambda.
|
|
517
233
|
if dispatch_none && !web_handler_present
|
|
518
234
|
raise ArgumentError, "#{domain} declares dispatch \"None\" but has no lambda_handler.rb — dispatch \"None\" means no rust/host dispatch Lambda at all, so a WebFunction (lambda_handler.rb) has to exist to be the domain's only Lambda."
|
|
519
235
|
end
|
|
@@ -521,33 +237,12 @@ module Hecks
|
|
|
521
237
|
raise ArgumentError, "#{domain} declares both dispatch \"None\" and web \"Rust\" — dispatch \"None\" already means there is no rust/host Lambda for rust_web's own in-process web UI to run inside."
|
|
522
238
|
end
|
|
523
239
|
|
|
524
|
-
#
|
|
525
|
-
#
|
|
526
|
-
#
|
|
527
|
-
|
|
528
|
-
#
|
|
529
|
-
#
|
|
530
|
-
# template already routes those subnets through its NAT Gateway and
|
|
531
|
-
# already permits 443-to-internet egress on that security group (added
|
|
532
|
-
# there for the owner's own WebFunction's real, live OAuth
|
|
533
|
-
# token-exchange bug — see that stack's own
|
|
534
|
-
# `#{owner_domain_name}FunctionEgressToInternet` resource). Nothing new
|
|
535
|
-
# to provision for this combination; the Parameters section below just
|
|
536
|
-
# has to declare both the Owning* set (Shared mode) and
|
|
537
|
-
# WebRedirectBaseUrl (OAuth) together, not as alternatives — see its
|
|
538
|
-
# own comment on why an if/elsif there would be wrong.
|
|
539
|
-
#
|
|
540
|
-
# Still a real gap, left refused: rust_web isn't checked here
|
|
541
|
-
# explicitly because shared && web_handler_present already aborted
|
|
542
|
-
# above whenever web_handler_present is true — so by construction, if
|
|
543
|
-
# `shared && google_oauth_present` is ever true at this point,
|
|
544
|
-
# web_handler_present must be false, meaning google_oauth_present's own
|
|
545
|
-
# `(web_handler_present || rust_web)` can only have been satisfied by
|
|
546
|
-
# rust_web. Nothing left to refuse here.
|
|
547
|
-
|
|
548
|
-
# The stack↔bastion contract, and the least-privilege cross-domain
|
|
549
|
-
# invoke grant — `Shared`'s own header explains why both are
|
|
550
|
-
# `Fargate`'s problem too, not only this target's.
|
|
240
|
+
# A Shared-mode rust_web domain with OAuth needs no new NAT Gateway:
|
|
241
|
+
# it already runs inside the owner's borrowed private subnets and
|
|
242
|
+
# security group, which already route to the internet for OAuth.
|
|
243
|
+
|
|
244
|
+
# The stack-bastion contract and the least-privilege cross-domain
|
|
245
|
+
# invoke grant are shared with `Fargate` — see `Shared`'s header.
|
|
551
246
|
stack_outputs = Shared.stack_outputs(
|
|
552
247
|
shared: shared, db_id: db_id, db_ref_id: db_ref_id, secret_intrinsic: secret_intrinsic,
|
|
553
248
|
compute_security_group_ref: "!Ref #{logical_id}SecurityGroup", google_oauth_present: google_oauth_present
|
|
@@ -555,14 +250,9 @@ module Hecks
|
|
|
555
250
|
bastion_parameters = Shared.bastion_parameters(shared: shared, google_oauth_present: google_oauth_present)
|
|
556
251
|
Shared.check_bastion_parameters!(bastion_parameters, stack_outputs)
|
|
557
252
|
|
|
558
|
-
#
|
|
559
|
-
#
|
|
560
|
-
#
|
|
561
|
-
# the line after it opens, so two on one line only stay
|
|
562
|
-
# unambiguous as long as neither branch is ever edited to span
|
|
563
|
-
# the other's own territory. An ordinary `if`/`else`, each
|
|
564
|
-
# heredoc entirely inside its own branch, cannot develop that
|
|
565
|
-
# failure mode at all.
|
|
253
|
+
# A plain local, not two heredocs opened side by side inside the
|
|
254
|
+
# template heredoc's own interpolation — a heredoc body starts on the
|
|
255
|
+
# line after it opens, so an if/else here keeps each one unambiguous.
|
|
566
256
|
web_google_oauth_env_yaml =
|
|
567
257
|
if google_oauth_present
|
|
568
258
|
<<~GOOGLE.each_line.with_index.map { |l, i| i.zero? ? l : " " + l }.join.rstrip
|
|
@@ -592,7 +282,7 @@ module Hecks
|
|
|
592
282
|
end
|
|
593
283
|
|
|
594
284
|
template_yaml = <<~YAML
|
|
595
|
-
# GENERATED by
|
|
285
|
+
# GENERATED by hecks deploy project #{domain} — re-run it to refresh
|
|
596
286
|
# this file rather than hand-editing. Source: #{world_file}'s own
|
|
597
287
|
# deployed_to("AwsLambda") block.
|
|
598
288
|
#
|
|
@@ -619,22 +309,10 @@ module Hecks
|
|
|
619
309
|
journal (docs/decisions/0018), backed by its own private RDS
|
|
620
310
|
Postgres instance. PackageType Zip, no container.
|
|
621
311
|
#{
|
|
622
|
-
#
|
|
623
|
-
#
|
|
624
|
-
#
|
|
625
|
-
#
|
|
626
|
-
# one), rather than treated as mutually exclusive alternatives via
|
|
627
|
-
# an if/elsif/else here. That shape breaks for the one combination
|
|
628
|
-
# both can be true at once (a Shared-mode rust_web domain with
|
|
629
|
-
# real Google OAuth): the elsif branch would never run, so
|
|
630
|
-
# OwningSubnetAId/OwningSecurityGroupId/etc. would never get declared as
|
|
631
|
-
# Parameters at all, even though VpcConfig below (`shared ?
|
|
632
|
-
# "SubnetIds: [!Ref OwningSubnetAId, ...]" : ...`) already
|
|
633
|
-
# references them unconditionally whenever `shared` is true --
|
|
634
|
-
# exactly the CloudFormation-references-an-undeclared-Parameter
|
|
635
|
-
# break bastion_parameters' own generation-time assertion (above)
|
|
636
|
-
# exists to catch for a different table. This fixes it for both
|
|
637
|
-
# the "either" and the "both" case.
|
|
312
|
+
# google_oauth_present and shared are independent and collected
|
|
313
|
+
# into one array under a single `Parameters:` header, not an
|
|
314
|
+
# if/elsif/else — both can be true at once (a Shared-mode
|
|
315
|
+
# rust_web domain with real Google OAuth).
|
|
638
316
|
param_blocks = []
|
|
639
317
|
if google_oauth_present
|
|
640
318
|
param_blocks << <<~OAUTHPARAMS.rstrip
|
|
@@ -660,8 +338,8 @@ module Hecks
|
|
|
660
338
|
# targets, below) via the SAME `aws cloudformation describe-stacks`
|
|
661
339
|
# pattern `mint-era` already used for a bastion, one stack over
|
|
662
340
|
# instead of one sibling stack over. Never a CloudFormation
|
|
663
|
-
# Export/ImportValue -- this codebase uses none anywhere (
|
|
664
|
-
#
|
|
341
|
+
# Export/ImportValue -- this codebase uses none anywhere (hecks
|
|
342
|
+
# deploy project's own header on why: a live shell lookup keeps
|
|
665
343
|
# this stack independently deployable/deletable, never coupled to
|
|
666
344
|
# #{owner_domain_name}'s own stack through CloudFormation itself).
|
|
667
345
|
OwningVpcId:
|
|
@@ -693,17 +371,8 @@ module Hecks
|
|
|
693
371
|
if param_blocks.empty?
|
|
694
372
|
""
|
|
695
373
|
else
|
|
696
|
-
#
|
|
697
|
-
#
|
|
698
|
-
# least-indented line, matching every other heredoc's own
|
|
699
|
-
# convention in this file), but these are Parameters: children,
|
|
700
|
-
# which YAML requires indented under it. Confirmed the hard way:
|
|
701
|
-
# without this, `ruby -ryaml` parses the file without raising
|
|
702
|
-
# (it's still syntactically valid YAML) but
|
|
703
|
-
# `doc["Parameters"]["OwningVpcId"]` is nil — WebRedirectBaseUrl/
|
|
704
|
-
# OwningVpcId/etc. land as their own top-level document keys,
|
|
705
|
-
# siblings of Parameters/Resources, not children of Parameters
|
|
706
|
-
# at all.
|
|
374
|
+
# Indented 2: the heredocs above dedent to column 0, but these
|
|
375
|
+
# are Parameters: children, which YAML requires indented under it.
|
|
707
376
|
"Parameters:\n" + param_blocks.join("\n").each_line.map { |l| " #{l}" }.join
|
|
708
377
|
end
|
|
709
378
|
}
|
|
@@ -805,13 +474,8 @@ module Hecks
|
|
|
805
474
|
# sibling of VpcConfig) is the one runtime IAM permission
|
|
806
475
|
# that fetch needs, scoped to this one secret ARN.
|
|
807
476
|
#{if shared
|
|
808
|
-
#
|
|
809
|
-
#
|
|
810
|
-
# OwningDatabaseEndpoint/OwningDatabaseSecretArn (this
|
|
811
|
-
# template's own Parameters, above), not a resource in
|
|
812
|
-
# this stack. Same dbname as #{owner_domain_name}'s own
|
|
813
|
-
# DBName -- one database, isolated by schema
|
|
814
|
-
# (HECKS_SCHEMA below), not a second database.
|
|
477
|
+
# Owner's live Endpoint/Secret, looked up into
|
|
478
|
+
# OwningDatabaseEndpoint/OwningDatabaseSecretArn.
|
|
815
479
|
"DB_HOST: !Ref OwningDatabaseEndpoint\n" \
|
|
816
480
|
" DB_NAME: #{owner_db_name}\n" \
|
|
817
481
|
" DB_SECRET_ARN: !Sub \"${OwningDatabaseSecretArn}\""
|
|
@@ -821,13 +485,13 @@ module Hecks
|
|
|
821
485
|
" DB_SECRET_ARN: !Sub \"${#{secret_sub}}\""
|
|
822
486
|
end}
|
|
823
487
|
# `domain_name`, NOT `infra_name` — this names the actual
|
|
824
|
-
# .wasm FILE
|
|
488
|
+
# .wasm FILE `hecks build_wasm` writes (rust/dist/#{domain_name}.wasm,
|
|
825
489
|
# its own target_mod_name convention, a Rust-build-pipeline
|
|
826
490
|
# concern unrelated to AWS resource identity) and what the
|
|
827
491
|
# Makefile below actually bundles into the Lambda package
|
|
828
492
|
# under. Confirmed the hard way: pinning this to infra_name
|
|
829
493
|
# alongside the real AWS-identity fields left it looking for
|
|
830
|
-
# a file
|
|
494
|
+
# a file `hecks build_wasm` never produces.
|
|
831
495
|
HECKS_WASM_PATH: !Sub "/var/task/#{domain_name}.wasm"
|
|
832
496
|
# STATIC, not a deploy-time parameter — HECKS_DOMAIN is this
|
|
833
497
|
# domain's own declared name, known at generation time the
|
|
@@ -972,7 +636,7 @@ module Hecks
|
|
|
972
636
|
PasswordLength: 64
|
|
973
637
|
ExcludePunctuation: true
|
|
974
638
|
|
|
975
|
-
#{webhook_secret_env ?
|
|
639
|
+
#{webhook_secret_env ? <<~WEBHOOKSECRET.rstrip : ""}
|
|
976
640
|
# Auto-generated, never typed or seen — the SAME
|
|
977
641
|
# ManageMasterUserPassword/SessionSecret pattern above, applied to
|
|
978
642
|
# `secret_env`'s own webhook secret instead (deploy_settings' own
|
|
@@ -1158,7 +822,7 @@ module Hecks
|
|
|
1158
822
|
Outputs:
|
|
1159
823
|
FunctionUrl:
|
|
1160
824
|
Value: !GetAtt #{logical_id}Url.FunctionUrl#{web_handler_present ? "\n WebFunctionUrl:\n Value: !GetAtt #{web_logical_id}Url.FunctionUrl" : ""}
|
|
1161
|
-
# stack_outputs (
|
|
825
|
+
# stack_outputs (hecks deploy project, above the heredocs) is the ONE
|
|
1162
826
|
# place these four names live — DatabaseEndpoint/DatabaseSecretArn
|
|
1163
827
|
# feed `make mint-era`'s own boot step directly; VpcId/
|
|
1164
828
|
# DbSecurityGroupId ALSO feed bastion.yaml's own Parameters (a
|
|
@@ -1167,7 +831,7 @@ module Hecks
|
|
|
1167
831
|
# network without this main template ever knowing a bastion
|
|
1168
832
|
# exists) and the Makefile's own --parameter-overrides. Rename a
|
|
1169
833
|
# key in that table and every consumer follows; reference one that
|
|
1170
|
-
# doesn't exist there and
|
|
834
|
+
# doesn't exist there and hecks deploy project refuses before
|
|
1171
835
|
# writing a single file, instead of a bastion.yaml whose Parameter
|
|
1172
836
|
# nothing could ever fill. (`PrivateSubnetId` used to sit here too
|
|
1173
837
|
# — dropped, nothing ever consumed it; bastion.yaml mints its own
|
|
@@ -1176,31 +840,13 @@ module Hecks
|
|
|
1176
840
|
YAML
|
|
1177
841
|
|
|
1178
842
|
# Spliced in after the heredoc renders, not interpolated inside it —
|
|
1179
|
-
#
|
|
1180
|
-
#
|
|
1181
|
-
# before any `#{...}` interpolation runs. `cross_domain_lambda_policies`
|
|
1182
|
-
# is "" for the overwhelmingly common case (no cross-domain policies at
|
|
1183
|
-
# all), and a `#{...}` marker written at column 0 in the source (so it
|
|
1184
|
-
# renders flush when empty) is itself a zero-indent line by that same
|
|
1185
|
-
# raw-text measure — dragging the whole heredoc's computed dedent down to
|
|
1186
|
-
# zero and leaving every other line's original leading whitespace
|
|
1187
|
-
# un-stripped, a real, generated-then-caught-by-spec bug (`bin/
|
|
1188
|
-
# project_deploy`'s own contract spec: "Outputs only declares []" — not
|
|
1189
|
-
# actually empty, just re-indented into structural nonsense by this).
|
|
1190
|
-
# A plain, unconditional `String#sub` after the fact has no such
|
|
1191
|
-
# interaction with the text it's replacing into.
|
|
843
|
+
# a `#{...}` marker at column 0 would drag the heredoc's own dedent
|
|
844
|
+
# computation to zero and leave every other line un-stripped.
|
|
1192
845
|
template_yaml = template_yaml.sub(/^([ \t]*)# TMPL:cross_domain_lambda_policies\n/) { Shared.cross_domain_invoke_policy_yaml(cross_domain_lambda_targets, $1) }
|
|
1193
846
|
|
|
1194
|
-
#
|
|
1195
|
-
#
|
|
1196
|
-
#
|
|
1197
|
-
# above: `pii_cloudfront_yaml` builds its own already-correctly-indented
|
|
1198
|
-
# text from scratch (its own `reindent` lambda), so there is nothing here
|
|
1199
|
-
# for a heredoc dedent computation to interact with badly.
|
|
1200
|
-
# `fronted_logical_id`/`use_oac` are resolved here, not earlier, because
|
|
1201
|
-
# both need `web_handler_present`/`web_logical_id`/`rust_web`/`logical_id`
|
|
1202
|
-
# — every one of which is computed after this generator's own pii
|
|
1203
|
-
# detection, above.
|
|
847
|
+
# Operates on the already-rendered string — `pii_cloudfront_yaml`
|
|
848
|
+
# builds its own correctly-indented text, so nothing here interacts
|
|
849
|
+
# with a heredoc's own dedent computation.
|
|
1204
850
|
if pii_detected
|
|
1205
851
|
fronted_logical_id = web_handler_present ? web_logical_id : logical_id
|
|
1206
852
|
use_oac = !web_handler_present && !rust_web
|
|
@@ -1212,46 +858,22 @@ module Hecks
|
|
|
1212
858
|
template_yaml = template_yaml.sub(/^Outputs:\n/) { "#{pii_resources}Outputs:\n PiiDistributionDomainName:\n Value: !GetAtt PiiDistribution.DomainName\n" }
|
|
1213
859
|
end
|
|
1214
860
|
|
|
1215
|
-
#
|
|
1216
|
-
#
|
|
1217
|
-
#
|
|
1218
|
-
#
|
|
1219
|
-
# established `<<~TAG.each_line.with_index.map { ... }` convention)
|
|
1220
|
-
# looked right, but broke several pre-existing embedded multi-line
|
|
1221
|
-
# `#{shared ? "a\nb" : "c"}`-shaped strings already living inside that
|
|
1222
|
-
# block (the DB_HOST/DB_NAME/DB_SECRET_ARN Environment lines among
|
|
1223
|
-
# them) — those assume they sit at exactly one reindentation layer deep
|
|
1224
|
-
# (the outer template heredoc's own margin), and adding a second layer
|
|
1225
|
-
# on top shifted every line their own hardcoded embedded-newline
|
|
1226
|
-
# indentation didn't already account for. Confirmed live, diffing a
|
|
1227
|
-
# real regenerated examples/banking (`dispatch` unset — the ordinary,
|
|
1228
|
-
# far more common path) against its own git-tracked output: real
|
|
1229
|
-
# indentation corruption, not a false alarm. Operating on the already-
|
|
1230
|
-
# fully-rendered, already-correctly-indented string instead sidesteps
|
|
1231
|
-
# that whole class of interaction — no new heredoc layer, so nothing
|
|
1232
|
-
# already living inside the old one has to change its own assumptions.
|
|
861
|
+
# Surgical removal, post-render, not a nested heredoc — a conditional
|
|
862
|
+
# nested heredoc broke embedded multi-line `#{shared ? ... : ...}`
|
|
863
|
+
# strings inside the resource block, which assume exactly one
|
|
864
|
+
# reindentation layer (confirmed regenerating examples/banking).
|
|
1233
865
|
if dispatch_none
|
|
1234
|
-
#
|
|
1235
|
-
#
|
|
1236
|
-
# property this resource ever renders, confirmed above), so a second
|
|
1237
|
-
# occurrence of "AuthType:" later in the document (WebFunction's own,
|
|
1238
|
-
# always AuthType: `NONE`) can never be matched instead.
|
|
866
|
+
# Non-greedy through the resource's own FunctionUrlConfig/AuthType
|
|
867
|
+
# closing pair, so WebFunction's own later "AuthType:" can't match.
|
|
1239
868
|
template_yaml = template_yaml.sub(
|
|
1240
869
|
/^ #{Regexp.escape(logical_id)}:\n.*?\n AuthType: (?:NONE|AWS_IAM)\n/m, ""
|
|
1241
870
|
)
|
|
1242
|
-
#
|
|
1243
|
-
#
|
|
1244
|
-
# removed from this template entirely; SAM refuses a `!Ref` to an
|
|
1245
|
-
# undeclared resource at package time, not silently.
|
|
871
|
+
# Removes the now-dangling `!Ref` to the resource just deleted above
|
|
872
|
+
# — SAM refuses an unresolved Ref at package time, not silently.
|
|
1246
873
|
template_yaml = template_yaml.sub(
|
|
1247
874
|
/^ - LambdaInvokePolicy:\n FunctionName: !Ref #{Regexp.escape(logical_id)}\n/, ""
|
|
1248
875
|
)
|
|
1249
|
-
#
|
|
1250
|
-
# `!GetAtt`; WebFunction's own URL becomes the stack's one and only
|
|
1251
|
-
# "FunctionUrl" (never "WebFunctionUrl" — that name is reserved for
|
|
1252
|
-
# the two-URL case, a domain that also has a #{logical_id} of its
|
|
1253
|
-
# own to disambiguate from, which this one, by construction, never
|
|
1254
|
-
# does).
|
|
876
|
+
# WebFunction's own URL becomes the stack's only "FunctionUrl".
|
|
1255
877
|
template_yaml = template_yaml.sub(
|
|
1256
878
|
/^ FunctionUrl:\n Value: !GetAtt #{Regexp.escape(logical_id)}Url\.FunctionUrl\n WebFunctionUrl:\n Value: !GetAtt #{Regexp.escape(web_logical_id)}Url\.FunctionUrl\n/,
|
|
1257
879
|
" FunctionUrl:\n Value: !GetAtt #{web_logical_id}Url.FunctionUrl\n"
|
|
@@ -1260,48 +882,24 @@ module Hecks
|
|
|
1260
882
|
|
|
1261
883
|
|
|
1262
884
|
|
|
1263
|
-
#
|
|
1264
|
-
#
|
|
1265
|
-
#
|
|
1266
|
-
#
|
|
1267
|
-
# by default) — this template only ever exists for the few minutes
|
|
1268
|
-
# `make mint-era` (below) needs it, then gets deleted. SSM Session
|
|
1269
|
-
# Manager only, never SSH: no key pair, no inbound security group rule
|
|
1270
|
-
# at all (the instance's own SG declares zero Ingress) — the only way
|
|
1271
|
-
# in is `aws ssm start-session`, itself gated by the caller's own IAM
|
|
1272
|
-
# permissions, nothing this template opens to the internet.
|
|
1273
|
-
# Skipped entirely when `shared` — this domain provisions no RDS/VPC
|
|
1274
|
-
# of its own for a bastion to reach (bastion_parameters is empty for
|
|
1275
|
-
# the same reason, above); a bare `!Ref VpcId` with no declared
|
|
1276
|
-
# Parameter would just fail at deploy time. Era-minting for a
|
|
1277
|
-
# Shared-mode domain reuses its owner's own already-standing
|
|
1278
|
-
# infrastructure instead — see the Makefile's own `mint-era` comment
|
|
1279
|
-
# for the manual path this leaves until that's automated too.
|
|
885
|
+
# A separate, sibling stack for the few minutes `make mint-era`
|
|
886
|
+
# needs it, then deleted — SSM Session Manager only, never SSH, no
|
|
887
|
+
# inbound rule at all. Skipped when `shared`: era-minting reuses the
|
|
888
|
+
# owner's own standing infrastructure instead.
|
|
1280
889
|
bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
1281
890
|
domain: domain, infra_name: infra_name, stack_name: stack_name, db_id: db_id,
|
|
1282
891
|
google_oauth_present: google_oauth_present, bastion_parameters: bastion_parameters
|
|
1283
892
|
)
|
|
1284
893
|
|
|
1285
|
-
#
|
|
1286
|
-
#
|
|
1287
|
-
#
|
|
1288
|
-
# leading spaces at all), and nesting this heredoc a second level
|
|
1289
|
-
# deeper inside another `#{...}` interpolation (the way `OWNDB`/params
|
|
1290
|
-
# above handle conditional template.yaml content) would need its own
|
|
1291
|
-
# per-line re-indent pass — one that adds spaces before every line
|
|
1292
|
-
# including the already-tab-prefixed recipe ones, corrupting Make's
|
|
1293
|
-
# own recipe-line detection. A single top-level heredoc, each line
|
|
1294
|
-
# already at its own final indentation (recipe lines as " \t..." —
|
|
1295
|
-
# the same two-space-before-the-tab convention every other recipe
|
|
1296
|
-
# line in this Makefile already uses, stripped by the encolosing
|
|
1297
|
-
# `<<~MAKE` heredoc's own squiggly stripping below, same as them),
|
|
1298
|
-
# sidesteps that: nothing re-touches an already-correct line twice.
|
|
894
|
+
# A top-level variable, not inlined in the Makefile heredoc below —
|
|
895
|
+
# Make recipe lines need a literal leading tab, and a nested
|
|
896
|
+
# interpolation's re-indent pass would corrupt that detection.
|
|
1299
897
|
mint_era_recipe =
|
|
1300
898
|
if shared
|
|
1301
899
|
<<~SHAREDMINT.rstrip
|
|
1302
900
|
# NOT AUTOMATED YET for a Shared-mode domain — it has no RDS/VPC
|
|
1303
901
|
# of its own to stand a temporary bastion next to (bastion.yaml
|
|
1304
|
-
# itself isn't even generated here —
|
|
902
|
+
# itself isn't even generated here — hecks deploy project's own
|
|
1305
903
|
# comment on why). Minting era 1 for #{infra_name}'s own schema
|
|
1306
904
|
# needs a tunnel to #{owner_domain_name}'s ALREADY-EXISTING RDS
|
|
1307
905
|
# instance instead -- reuse #{owner_domain_name}'s own deploy
|
|
@@ -1350,7 +948,7 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1350
948
|
else
|
|
1351
949
|
<<~OWNMINT.rstrip
|
|
1352
950
|
\t@echo "Looking up $(STACK)'s VPC/security group..."
|
|
1353
|
-
# stack_outputs/bastion_parameters (
|
|
951
|
+
# stack_outputs/bastion_parameters (hecks deploy project) are the ONE
|
|
1354
952
|
# place these OutputKey strings and parameter names live — this eval
|
|
1355
953
|
# chain and the --parameter-overrides line just below are both
|
|
1356
954
|
# generated from the SAME table template.yaml's Outputs and
|
|
@@ -1469,48 +1067,22 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1469
1067
|
\techo "Tearing down the temporary bastion stack..."; \\
|
|
1470
1068
|
\taws cloudformation delete-stack --stack-name $(BASTION_STACK); \\
|
|
1471
1069
|
\taws cloudformation wait stack-delete-complete --stack-name $(BASTION_STACK); \\
|
|
1472
|
-
\techo "$(BASTION_STACK) deleted. Era 1 should now be held if BOOT_STATUS was 0 — verify with
|
|
1070
|
+
\techo "$(BASTION_STACK) deleted. Era 1 should now be held if BOOT_STATUS was 0 — verify with hecks console or a Postgres query against hecks_eras."; \\
|
|
1473
1071
|
\texit $$BOOT_STATUS
|
|
1474
1072
|
OWNMINT
|
|
1475
1073
|
end
|
|
1476
1074
|
|
|
1477
|
-
#
|
|
1478
|
-
#
|
|
1479
|
-
# the edge, check it with bin/translation_audit, then boot again") when
|
|
1480
|
-
# a deploy's own pre-flight boot check refuses a shape change with no
|
|
1481
|
-
# translation edge covering it. Same bastion/tunnel/retry/teardown
|
|
1482
|
-
# chain mint_era_recipe already uses — only the one thing done over the
|
|
1483
|
-
# tunnel differs: these run the scaffold/audit scripts (hecks's
|
|
1484
|
-
# own bin/, not a Ruby -e one-liner) instead of a boot check, since
|
|
1485
|
-
# both scripts already do their own registry-loading internally.
|
|
1075
|
+
# Shares mint_era_recipe's bastion/tunnel/retry/teardown chain but
|
|
1076
|
+
# runs `hecks ask scaffold_translation` or `hecks ask audit_translation` over it.
|
|
1486
1077
|
#
|
|
1487
|
-
#
|
|
1488
|
-
#
|
|
1489
|
-
# `
|
|
1490
|
-
#
|
|
1491
|
-
#
|
|
1492
|
-
# `
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
# `.world` file declares. Without `db_env_blind:` below, this whole
|
|
1496
|
-
# recipe would stand up a real bastion, punch a live 5432 ingress rule
|
|
1497
|
-
# into production's security group, open a real SSM tunnel to
|
|
1498
|
-
# #{stack_name}'s RDS instance, tear it all down again — and then
|
|
1499
|
-
# silently scaffold/audit the developer's local dev database the
|
|
1500
|
-
# entire time, reporting success. `db_env_blind:`
|
|
1501
|
-
# (below) is exactly this: true for scaffold-translation/
|
|
1502
|
-
# translation-audit (confirmed env-blind), left false for
|
|
1503
|
-
# migrate-console-settings (an app-owned script this generator doesn't
|
|
1504
|
-
# control the internals of — it may honor these vars; not asserting
|
|
1505
|
-
# either way here). A `db_env_blind` recipe refuses before ever
|
|
1506
|
-
# touching AWS unless `ALLOW_LOCAL_DB=1` is set, rather than silently
|
|
1507
|
-
# doing the wrong (but locally successful-looking) thing.
|
|
1508
|
-
# `cwd:`/`run_prefix:` — every existing caller runs a hecks-owned
|
|
1509
|
-
# script from hecks's own $(root) with `-Ilib` (source, not the
|
|
1510
|
-
# installed gem); `migrate_console_settings_recipe` below is the one
|
|
1511
|
-
# exception, an app-owned script that needs the app's own Gemfile
|
|
1512
|
-
# context instead — `cd $(DOMAIN) && bundle exec ruby`, not `-Ilib`.
|
|
1513
|
-
translation_recipe = lambda do |verb, script, extra_args = "", cwd: "$(ROOT)", run_prefix: "ruby -Ilib", db_env_blind: false|
|
|
1078
|
+
# Neither script actually reads DATABASE_URL/HECKS_SCHEMA (both call
|
|
1079
|
+
# `registry.binding_settings`, a lookup against the literal `database
|
|
1080
|
+
# "..."` in #{domain}'s `.world`) — without `db_env_blind:`, this
|
|
1081
|
+
# recipe would open a real tunnel to production and silently
|
|
1082
|
+
# scaffold/audit the local dev database instead, reporting success.
|
|
1083
|
+
# `db_env_blind` refuses unless `ALLOW_LOCAL_DB=1` is set.
|
|
1084
|
+
translation_recipe = lambda do |verb, script, extra_args = "", cwd: "$(ROOT)", run_prefix: "ruby -Ilib", db_env_blind: false,
|
|
1085
|
+
domain_arg: "$(DOMAIN) "|
|
|
1514
1086
|
if shared
|
|
1515
1087
|
<<~SHAREDTRANSLATION.rstrip
|
|
1516
1088
|
\t@echo "#{verb} isn't automated yet for a Shared-mode domain (database \\"Shared\\") -- see mint-era's own comment in the generated Makefile for the manual path through #{owner_domain_name}'s own tunnel."; \\
|
|
@@ -1559,7 +1131,7 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1559
1131
|
\t\t\tnc -z localhost 15432 2>/dev/null && break; \\
|
|
1560
1132
|
\t\t\tsleep 1; \\
|
|
1561
1133
|
\t\tdone; \\
|
|
1562
|
-
\t\tcd #{cwd} && DATABASE_URL="postgres://postgres:$$DB_PASS_URLENC@localhost:15432/#{db_name}" HECKS_SCHEMA="#{hecks_schema}" #{run_prefix} #{script} #{cwd == "$(ROOT)" ?
|
|
1134
|
+
\t\tcd #{cwd} && DATABASE_URL="postgres://postgres:$$DB_PASS_URLENC@localhost:15432/#{db_name}" HECKS_SCHEMA="#{hecks_schema}" #{run_prefix} #{script} #{cwd == "$(ROOT)" ? domain_arg : ""}#{extra_args}&& { RUN_STATUS=0; break; }; \\
|
|
1563
1135
|
\t\tRUN_STATUS=$$?; \\
|
|
1564
1136
|
\t\techo "#{verb} attempt $$attempt/5 failed (exit $$RUN_STATUS) -- restarting the tunnel and retrying in 3s..."; \\
|
|
1565
1137
|
\t\tsleep 3; \\
|
|
@@ -1574,41 +1146,31 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1574
1146
|
end
|
|
1575
1147
|
end
|
|
1576
1148
|
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
#
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
#
|
|
1149
|
+
# `hecks ask` answers the scaffold as text, and refuses (exit 1) where the script aborted.
|
|
1150
|
+
scaffold_translation_recipe = translation_recipe.call(
|
|
1151
|
+
"scaffold-translation", "exe/hecks ask scaffold_translation", db_env_blind: true,
|
|
1152
|
+
run_prefix: "HECKS_ENVIRONMENT=memory ruby", domain_arg: "domain=$(DOMAIN) "
|
|
1153
|
+
)
|
|
1154
|
+
# A refused audit is a refused query: `hecks ask` exits 1, so the target fails too.
|
|
1155
|
+
translation_audit_recipe = translation_recipe.call(
|
|
1156
|
+
"translation-audit", "exe/hecks ask audit_translation", db_env_blind: true,
|
|
1157
|
+
run_prefix: "HECKS_ENVIRONMENT=memory ruby", domain_arg: "domain=$(DOMAIN) "
|
|
1158
|
+
)
|
|
1159
|
+
|
|
1160
|
+
# Same chain, running an app-owned one-time migration script instead
|
|
1161
|
+
# of a hecks tool. `bin/migrate_console_settings` belongs to the domain's own
|
|
1162
|
+
# application, not to hecks, so it stays a `bin/` path when hecks drops its own.
|
|
1163
|
+
# Harmless when the domain has none: `bundle exec ruby` on a missing file just fails
|
|
1164
|
+
# loudly.
|
|
1589
1165
|
migrate_console_settings_recipe = translation_recipe.call(
|
|
1590
1166
|
"migrate-console-settings", "bin/migrate_console_settings", "",
|
|
1591
1167
|
cwd: "$(DOMAIN)", run_prefix: "bundle exec ruby"
|
|
1592
1168
|
)
|
|
1593
1169
|
|
|
1594
|
-
# `
|
|
1595
|
-
#
|
|
1596
|
-
#
|
|
1597
|
-
#
|
|
1598
|
-
# BASTION_STACK Make variables mint-era already defines, and the same
|
|
1599
|
-
# stand-up/tunnel/teardown-no-matter-what shell chain — only the one
|
|
1600
|
-
# thing done over the tunnel differs: a schema rename instead of a
|
|
1601
|
-
# Ruby boot. `OLD`/`NEW` are Make command-line variables
|
|
1602
|
-
# (`make rename-schema OLD=old NEW=new`), not baked in here, so this
|
|
1603
|
-
# is genuinely reusable — the domain whose own schema this renames is
|
|
1604
|
-
# whichever one owns this stack's RDS instance (this generator's own
|
|
1605
|
-
# `#{infra_name}` database on it), not necessarily this domain forever.
|
|
1606
|
-
#
|
|
1607
|
-
# Idempotent by inspection, not by catching a Postgres error: checks
|
|
1608
|
-
# which of old/new actually exists as a schema before touching
|
|
1609
|
-
# anything, so a second run (or a run after a partial failure) reads
|
|
1610
|
-
# as a clear no-op message rather than a bare "schema already exists"
|
|
1611
|
-
# error with no context.
|
|
1170
|
+
# `OLD`/`NEW` are Make command-line variables (`make rename-schema
|
|
1171
|
+
# OLD=old NEW=new`), reusable across whichever domain owns this
|
|
1172
|
+
# stack's RDS instance. Idempotent by inspection (checks which schema
|
|
1173
|
+
# exists first), not by catching a Postgres error.
|
|
1612
1174
|
rename_schema_recipe =
|
|
1613
1175
|
if shared
|
|
1614
1176
|
<<~SHAREDRENAME.rstrip
|
|
@@ -1718,27 +1280,11 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1718
1280
|
OWNRENAME
|
|
1719
1281
|
end
|
|
1720
1282
|
|
|
1721
|
-
# `
|
|
1722
|
-
#
|
|
1723
|
-
#
|
|
1724
|
-
#
|
|
1725
|
-
#
|
|
1726
|
-
# Owning* overrides whenever OAuth is also present. Caught live
|
|
1727
|
-
# the first time a Shared-mode domain with real Google OAuth
|
|
1728
|
-
# actually ran `make deploy`: `sam deploy` refused
|
|
1729
|
-
# with "Parameters: [OwningVpcId, ...] must have values" because
|
|
1730
|
-
# nothing had ever passed them. WebRedirectBaseUrl stays a conditional
|
|
1731
|
-
# override (empty on a genuine first deploy, before the Function Url
|
|
1732
|
-
# exists — see that Parameter's own comment); Owning* is unconditional
|
|
1733
|
-
# whenever `shared`, in every branch that reaches `sam deploy` at all.
|
|
1734
|
-
# One continuous `\`-joined shell chain, however many pieces
|
|
1735
|
-
# contribute to it — `@` (Make's own "don't echo this line" prefix)
|
|
1736
|
-
# is only meaningful on the true first line of that chain; written on
|
|
1737
|
-
# any later line it stops being a Make directive at all and becomes
|
|
1738
|
-
# literal shell text (`@WEB_URL=...` parses as a bogus command, not
|
|
1739
|
-
# an assignment) the moment two independently-`@`-prefixed pieces
|
|
1740
|
-
# get concatenated. Built unprefixed here; `@` is added once, to
|
|
1741
|
-
# whichever piece actually ends up first, right before joining.
|
|
1283
|
+
# `google_oauth_present` and `shared` are independent facts, not
|
|
1284
|
+
# alternatives — an elsif would silently drop Owning* overrides
|
|
1285
|
+
# whenever OAuth is also present. `@` (Make's "don't echo" prefix)
|
|
1286
|
+
# is only meaningful on the chain's true first line, so lines are
|
|
1287
|
+
# built unprefixed and `@` is added once, right before joining.
|
|
1742
1288
|
owner_lookup_lines = shared ? [
|
|
1743
1289
|
%(OWNER_VPC_ID=$$(aws cloudformation describe-stacks --stack-name #{owner_stack_name} --query "Stacks[0].Outputs[?OutputKey=='VpcId'].OutputValue" --output text); \\),
|
|
1744
1290
|
%(OWNER_SUBNET_A_ID=$$(aws cloudformation describe-stacks --stack-name #{owner_stack_name} --query "Stacks[0].Outputs[?OutputKey=='PrivateSubnetAId'].OutputValue" --output text); \\),
|
|
@@ -1777,36 +1323,13 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1777
1323
|
deploy_shell_chain = owner_lookup_lines + sam_deploy_lines
|
|
1778
1324
|
deploy_shell_chain = ["@#{deploy_shell_chain.first}"] + deploy_shell_chain.drop(1) if deploy_shell_chain.first&.match?(/=\$\$\(/)
|
|
1779
1325
|
|
|
1780
|
-
#
|
|
1781
|
-
#
|
|
1782
|
-
#
|
|
1783
|
-
#
|
|
1784
|
-
#
|
|
1785
|
-
#
|
|
1786
|
-
#
|
|
1787
|
-
# Google OAuth newly added to an existing stack deadlocks this bridge —
|
|
1788
|
-
# confirmed live: stack_outputs (above) only gains PublicSubnetId/
|
|
1789
|
-
# BastionSubnetId entries once `google_oauth_present` is true, but those
|
|
1790
|
-
# two CloudFormation resources (#{db_id}PublicSubnet/
|
|
1791
|
-
# #{db_id}BastionPublicSubnet) don't exist on a stack that predates
|
|
1792
|
-
# turning OAuth on — only the upcoming `sam deploy` (further below,
|
|
1793
|
-
# still ahead of us here) creates them. This pre-check's own `mint-era`
|
|
1794
|
-
# call evaluates stack_outputs against the currently live stack (an
|
|
1795
|
-
# `aws cloudformation describe-stacks` eval chain, same one mint-era's
|
|
1796
|
-
# own recipe uses), gets an empty string back for both, and hands
|
|
1797
|
-
# bastion.yaml an empty `AWS::EC2::Subnet::Id` — CloudFormation refuses
|
|
1798
|
-
# that outright. The pre-check fails before `sam deploy` ever runs, so
|
|
1799
|
-
# the one deploy that would actually create those outputs never gets
|
|
1800
|
-
# the chance to: a hard deadlock, `make deploy` can never get OAuth
|
|
1801
|
-
# provisioned on a stack that didn't have it already. Detected the same
|
|
1802
|
-
# way a genuine first deploy is detected just below (describe-stacks
|
|
1803
|
-
# succeeding or not) — here, describe-stacks succeeds (the stack
|
|
1804
|
-
# itself exists) but the specific output this deploy is newly adding
|
|
1805
|
-
# does not, yet. Skip the pre-deploy bridge in exactly that one case;
|
|
1806
|
-
# the unconditional `mint-era` call at the very end of `deploy:` still
|
|
1807
|
-
# covers it once `sam deploy` has actually created PublicSubnetId/
|
|
1808
|
-
# BastionSubnetId — the exact same "runs once, after the stack exists"
|
|
1809
|
-
# path a domain's true first deploy already takes below.
|
|
1326
|
+
# Google OAuth newly added to an existing stack would deadlock this
|
|
1327
|
+
# bridge: PublicSubnetId/BastionSubnetId don't exist on the live stack
|
|
1328
|
+
# until the upcoming `sam deploy` creates them, so the pre-check's own
|
|
1329
|
+
# `mint-era` call would hand bastion.yaml an empty Subnet::Id and
|
|
1330
|
+
# CloudFormation would refuse it before `sam deploy` ever runs. Skip
|
|
1331
|
+
# the bridge in exactly that case; the unconditional `mint-era` call
|
|
1332
|
+
# at the end of `deploy:` still covers it afterward.
|
|
1810
1333
|
predeploy_bridge_shell =
|
|
1811
1334
|
if google_oauth_present
|
|
1812
1335
|
<<~OAUTHBRIDGE.rstrip
|
|
@@ -1842,7 +1365,7 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1842
1365
|
).map { |l| "\t#{l}" }.join("\n")
|
|
1843
1366
|
|
|
1844
1367
|
makefile_content = <<~MAKE
|
|
1845
|
-
# GENERATED by
|
|
1368
|
+
# GENERATED by hecks deploy project #{domain} — re-run it to refresh
|
|
1846
1369
|
# this file rather than hand-editing.
|
|
1847
1370
|
#
|
|
1848
1371
|
# `sam build` invokes the build-<LogicalId> target below (SAM's own
|
|
@@ -1853,7 +1376,7 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1853
1376
|
# `brew install cargo-lambda`.
|
|
1854
1377
|
|
|
1855
1378
|
HOST_DIR := #{File.join(root, "rust", "host")}
|
|
1856
|
-
# `domain_name`, matching
|
|
1379
|
+
# `domain_name`, matching `hecks build_wasm`'s own target_mod_name
|
|
1857
1380
|
# (a Rust-build-pipeline naming convention, not AWS resource
|
|
1858
1381
|
# identity — see HECKS_WASM_PATH's own comment above).
|
|
1859
1382
|
WASM := #{File.join(root, "rust", "dist", "#{domain_name}.wasm")}
|
|
@@ -1868,13 +1391,13 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1868
1391
|
# start of a work session silently kept getting deployed through
|
|
1869
1392
|
# every later kernel change (a real, live bug: this cost a stale
|
|
1870
1393
|
# deploy against a wasm that predated the kernel's own "mutations"
|
|
1871
|
-
# field). `
|
|
1394
|
+
# field). `hecks build_wasm` recompiles fast enough that "always
|
|
1872
1395
|
# rebuild" is the safe default, matching rust/host's own bootstrap
|
|
1873
1396
|
# build just below, which was never guarded this way to begin with.
|
|
1874
|
-
\tcd #{root} &&
|
|
1397
|
+
\tcd #{root} && HECKS_ENVIRONMENT=memory ruby exe/hecks build_wasm domain=#{domain} --wait
|
|
1875
1398
|
\t@rustup target list --installed 2>/dev/null | grep -qx aarch64-unknown-linux-gnu || rustup target add aarch64-unknown-linux-gnu
|
|
1876
1399
|
# `rustup run stable`, not a bare `cargo lambda` — same reasoning
|
|
1877
|
-
#
|
|
1400
|
+
# `hecks build_wasm`'s own Makefile-equivalent line holds itself to: a
|
|
1878
1401
|
# `cargo`/`rustc` earlier on PATH than rustup's own shims (Homebrew
|
|
1879
1402
|
# installs one; common on this kind of machine) is a DIFFERENT
|
|
1880
1403
|
# toolchain that never saw `rustup target add`, and fails with
|
|
@@ -1940,26 +1463,26 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1940
1463
|
# `make verify-parity-#{logical_id}` — closes the exact gap the
|
|
1941
1464
|
# equivalence-gap plan's own Phase 8 named: parity was checked ONLY
|
|
1942
1465
|
# against CI's fixed test corpus, completely decoupled from what a
|
|
1943
|
-
# real `sam deploy` actually ships — `
|
|
1466
|
+
# real `sam deploy` actually ships — `hecks check_conformance` (the
|
|
1944
1467
|
# differential harness, docs/decisions/0010-ruby-is-the-reference-
|
|
1945
1468
|
# implementation.md) had never once been run against a specific
|
|
1946
1469
|
# compiled deploy artifact. Runs it here, for real, against
|
|
1947
1470
|
# $(WASM) -- the EXACT file `build-#{logical_id}` (above) just built
|
|
1948
1471
|
# and `deploy:` (below) is about to ship -- not a corpus-wide `cargo
|
|
1949
|
-
# build`'s own separate binary. `
|
|
1472
|
+
# build`'s own separate binary. `hecks check_conformance --wait` exits non-zero
|
|
1950
1473
|
# on any mismatch, which this target lets propagate uncaught: a real
|
|
1951
1474
|
# divergence between THIS artifact and Ruby's own reading of the
|
|
1952
1475
|
# identical source blocks `make deploy` before `sam deploy` ever
|
|
1953
1476
|
# runs, the same way a failing build already would.
|
|
1954
1477
|
#
|
|
1955
1478
|
# `spec/corpus/#{domain_name}.json` is this domain's OWN pinned
|
|
1956
|
-
# fuzzer-replay script (the same shape `
|
|
1479
|
+
# fuzzer-replay script (the same shape `hecks fuzz`/`hecks run` already
|
|
1957
1480
|
# use) -- if a domain doesn't have one yet, this warns LOUDLY and
|
|
1958
1481
|
# continues rather than either silently skipping (this project's own
|
|
1959
1482
|
# standing rule against a silent gap reading as full coverage) or
|
|
1960
1483
|
# blocking every deploy of a domain nobody has written one for yet.
|
|
1961
1484
|
#
|
|
1962
|
-
# SECOND HALF, BELOW — `
|
|
1485
|
+
# SECOND HALF, BELOW — `hecks fuzz_conformance`, ADR 0037's own fuzz
|
|
1963
1486
|
# bridge pointed at this exact $(WASM). A pinned script only proves
|
|
1964
1487
|
# "matches Ruby on the cases we thought to write down" (ADR 0039's own
|
|
1965
1488
|
# honest framing); the fuzz bridge is what actually FOUND Findings 1-6.
|
|
@@ -1973,12 +1496,12 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
1973
1496
|
.PHONY: verify-parity-#{logical_id}
|
|
1974
1497
|
verify-parity-#{logical_id}:
|
|
1975
1498
|
\t@if [ -f #{root}/spec/corpus/#{domain_name}.json ]; then \\
|
|
1976
|
-
\t\tcd #{root} &&
|
|
1499
|
+
\t\tcd #{root} && HECKS_ENVIRONMENT=memory ruby exe/hecks check_conformance domain=#{domain} script=spec/corpus/#{domain_name}.json artifact=$(WASM) --wait; \\
|
|
1977
1500
|
\telse \\
|
|
1978
|
-
\t\techo "verify-parity-#{logical_id}: no spec/corpus/#{domain_name}.json -- SKIPPING the pre-deploy Ruby/Rust parity check, nothing to compare $(WASM) against. Write one (
|
|
1501
|
+
\t\techo "verify-parity-#{logical_id}: no spec/corpus/#{domain_name}.json -- SKIPPING the pre-deploy Ruby/Rust parity check, nothing to compare $(WASM) against. Write one (hecks fuzz/hecks run's own script shape) before this domain's next deploy."; \\
|
|
1979
1502
|
\tfi
|
|
1980
1503
|
\t@echo "verify-parity-#{logical_id}: fuzzing $(WASM) against generated sequences (ADR 0037's bridge, WARN-ONLY -- see this target's own comment)..."
|
|
1981
|
-
\t-@cd #{root} &&
|
|
1504
|
+
\t-@cd #{root} && HECKS_ENVIRONMENT=memory ruby exe/hecks fuzz_conformance domain=#{domain} artifact=$(WASM) --wait
|
|
1982
1505
|
|
|
1983
1506
|
# `make mint-era` — takes #{stack_name}'s RDS instance from freshly
|
|
1984
1507
|
# created to "era 1 minted, ready for HECKS_DOMAIN/HECKS_ERA to
|
|
@@ -2201,7 +1724,7 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
2201
1724
|
# safe either way, since mint-era's own quiet-reboot path is a
|
|
2202
1725
|
# no-op the moment nothing has actually changed.
|
|
2203
1726
|
#
|
|
2204
|
-
# See predeploy_bridge_shell's own comment (
|
|
1727
|
+
# See predeploy_bridge_shell's own comment (hecks deploy project,
|
|
2205
1728
|
# above, right before deploy_recipe_lines) for why this branches
|
|
2206
1729
|
# on google_oauth_present at all -- the short version: adding
|
|
2207
1730
|
# Google OAuth to an EXISTING stack would otherwise deadlock this
|
|
@@ -2214,15 +1737,10 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
2214
1737
|
\t$(MAKE) mint-era
|
|
2215
1738
|
MAKE
|
|
2216
1739
|
|
|
2217
|
-
# Everything `sam deploy --guided`
|
|
2218
|
-
#
|
|
2219
|
-
# domain and its deployed_to("AwsLambda") block are: none of it needs
|
|
2220
|
-
# a human typing answers on every deploy. There's no secret to carry
|
|
2221
|
-
# here either now: DATABASE_URL is composed inside the template itself
|
|
2222
|
-
# from RDS's own auto-generated Secrets Manager password, so nothing
|
|
2223
|
-
# gets typed, saved, or passed as a --parameter-overrides flag at all.
|
|
1740
|
+
# Everything `sam deploy --guided` would ask interactively is already
|
|
1741
|
+
# known from the domain's own `deployed_to("AwsLambda")` block.
|
|
2224
1742
|
samconfig_toml = <<~TOML
|
|
2225
|
-
# GENERATED by
|
|
1743
|
+
# GENERATED by hecks deploy project #{domain} — re-run it to refresh
|
|
2226
1744
|
# this file rather than hand-editing.
|
|
2227
1745
|
version = 0.1
|
|
2228
1746
|
|
|
@@ -2243,46 +1761,11 @@ bastion_yaml = shared ? nil : Shared.bastion_yaml(
|
|
|
2243
1761
|
files
|
|
2244
1762
|
end
|
|
2245
1763
|
|
|
2246
|
-
# Builds the CloudFront/WAFv2/logging resources a
|
|
2247
|
-
#
|
|
2248
|
-
#
|
|
2249
|
-
#
|
|
2250
|
-
#
|
|
2251
|
-
# fronted by a distribution carrying a WAFv2 WebACL (AWS managed rule
|
|
2252
|
-
# groups), security response headers, geo-restriction, and access
|
|
2253
|
-
# logging — every other domain's own template is untouched
|
|
2254
|
-
# (`pii_detected` false means `call` never invokes this at all).
|
|
2255
|
-
#
|
|
2256
|
-
# **`OAC` only for the AWS_IAM case** (`use_oac`) — the already-public
|
|
2257
|
-
# `WebFunction`/`rust_web` shape (`AuthType: NONE`) is left exactly as
|
|
2258
|
-
# reachable as it already was; CloudFront adds WAF/headers/geo/logging
|
|
2259
|
-
# on top of that, it does not change who could already call the
|
|
2260
|
-
# Function URL directly. `#{logical_id}` itself (the AWS_IAM,
|
|
2261
|
-
# internal-dispatch default) is the opposite: OAC lets CloudFront sign
|
|
2262
|
-
# requests to it via SigV4 while `PiiLambdaInvokePermission`'s own
|
|
2263
|
-
# `SourceArn` admits only this one distribution — direct, unsigned
|
|
2264
|
-
# access to the Function URL stays refused exactly as it was before
|
|
2265
|
-
# this ran.
|
|
2266
|
-
#
|
|
2267
|
-
# **Managed cache/origin-request policy ids** — not custom resources.
|
|
2268
|
-
# `4135ea2d-6df8-44a3-9df3-4b5a84be39ad`/`216adef6-5c7f-47e4-b989-
|
|
2269
|
-
# 5492eafa07d3` are AWS's own permanent, account-independent
|
|
2270
|
-
# `Managed-CachingDisabled`/`Managed-AllViewer` ids (the same ones the
|
|
2271
|
-
# console's own dropdown offers) — this fronts a Lambda dispatch
|
|
2272
|
-
# endpoint, not a static site; caching a response meant for exactly
|
|
2273
|
-
# one caller would be a real correctness bug, not a performance choice
|
|
2274
|
-
# made once here.
|
|
2275
|
-
#
|
|
2276
|
-
# @param fronted_logical_id [String] the Lambda resource this distribution fronts
|
|
2277
|
-
# @param use_oac [Boolean] true only when `fronted_logical_id`'s own FunctionUrlConfig is
|
|
2278
|
-
# AWS_IAM (never true for WebFunction/rust_web, both always `NONE`)
|
|
2279
|
-
# @param geo_restriction_type ["none", "allowlist", "blocklist"] `deployed_to`'s own
|
|
2280
|
-
# `geo_restriction` setting; "none" (no restriction, structurally present so a later
|
|
2281
|
-
# change is a one-line `deployed_to` edit, not a template rewrite) when unset
|
|
2282
|
-
# @param geo_restriction_countries [Array<String>] ISO 3166-1 alpha-2 codes; ignored when
|
|
2283
|
-
# `geo_restriction_type` is "none"
|
|
2284
|
-
# @return [String] the Resources entries to splice in before Outputs:, absolutely
|
|
2285
|
-
# indented to 2 spaces (this stack's own top-level Resources entry column)
|
|
1764
|
+
# Builds the CloudFront/WAFv2/logging resources a pii-marked domain is
|
|
1765
|
+
# fronted by. `use_oac` signs requests only for the AWS_IAM case; the
|
|
1766
|
+
# already-public WebFunction/rust_web Function URL stays as reachable
|
|
1767
|
+
# as before. Managed cache/origin-request policy ids are AWS's own
|
|
1768
|
+
# permanent `Managed-CachingDisabled`/`Managed-AllViewer` ids.
|
|
2286
1769
|
def pii_cloudfront_yaml(fronted_logical_id:, use_oac:, geo_restriction_type:, geo_restriction_countries:)
|
|
2287
1770
|
reindent = ->(text) { text.each_line.map { |line| line.strip.empty? ? line : " #{line}" }.join }
|
|
2288
1771
|
|