hecks 3.10.0 → 3.12.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/examples/pizzas/bluebook/pizzas.bluebook +29 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
- data/exe/hecks +4 -4
- data/lib/hecks/adapters/driven/d1.rb +2 -10
- data/lib/hecks/adapters/driven/heki.adapter +1 -1
- data/lib/hecks/adapters/driven/heki.rb +2 -12
- data/lib/hecks/adapters/driven/lambda.adapter +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +3 -18
- data/lib/hecks/adapters/driven/postgres.rb +4 -11
- data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
- data/lib/hecks/adapters/driven/sqlite.rb +2 -10
- data/lib/hecks/adapters/driving/admission.rb +46 -0
- data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
- data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
- data/lib/hecks/adapters/driving/cli.rb +127 -0
- data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +158 -0
- data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
- data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
- data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +70 -0
- data/lib/hecks/adapters/driving/command_request.rb +121 -0
- data/lib/hecks/adapters/driving/handle.rb +214 -0
- data/lib/hecks/adapters/driving/json.rb +136 -0
- data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
- data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
- data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
- data/lib/hecks/adapters/driving/mcp.rb +159 -0
- data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
- data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
- data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
- data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
- data/lib/hecks/adapters/driving/newcomer.rb +50 -0
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
- data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
- data/lib/hecks/adapters/driving/ruby.rb +83 -0
- data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
- data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
- data/lib/hecks/behaviors/expectations/boot.rb +1 -1
- data/lib/hecks/bench/ruby_runner.rb +1 -1
- data/lib/hecks/bluebook/ask_resolution.rb +130 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
- data/lib/hecks/bluebook/assembly/contracts/read_models.rb +1 -1
- data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
- data/lib/hecks/bluebook/assembly/marks.rb +4 -15
- data/lib/hecks/bluebook/assembly/scalars.rb +39 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/command.rb +13 -8
- data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
- data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
- data/lib/hecks/bluebook/capabilities.rb +91 -9
- data/lib/hecks/bluebook/domain_port.rb +16 -4
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
- data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
- data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
- data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
- data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
- data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
- data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
- data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
- data/lib/hecks/bluebook/expression/projection.json +36 -0
- data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
- data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
- data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
- data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
- data/lib/hecks/bluebook/expression/resolver.rb +20 -1
- data/lib/hecks/bluebook/hecksagon.rb +23 -3
- data/lib/hecks/bluebook/lifecycle.rb +22 -3
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
- data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/readings.rb +24 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +6 -3
- data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
- data/lib/hecks/bluebook/meta_validator.rb +12 -12
- data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
- data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
- data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
- data/lib/hecks/bluebook/model_check.rb +8 -2
- data/lib/hecks/bluebook/policy.rb +4 -2
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/bluebook/synthesizer.rb +2 -0
- data/lib/hecks/bluebook.rb +2 -0
- data/lib/hecks/cli/command_table.rb +1 -1
- data/lib/hecks/cli/console.rb +2 -1
- data/lib/hecks/cli/document.rb +1 -1
- data/lib/hecks/cli/launcher_words.rb +4 -4
- data/lib/hecks/cli/mcp.rb +4 -4
- data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
- data/lib/hecks/cli/project_cli/templates.rb +5 -5
- data/lib/hecks/cli/project_cli.rb +9 -6
- data/lib/hecks/cli/run.rb +2 -2
- data/lib/hecks/cli/smoke_http/checks.rb +4 -1
- data/lib/hecks/cli/smoke_http/transport.rb +3 -3
- data/lib/hecks/cli/smoke_http.rb +3 -0
- data/lib/hecks/codemod.rb +5 -2
- data/lib/hecks/corpus/routes.rb +7 -0
- data/lib/hecks/corpus/rust.rb +3 -2
- data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -4
- data/lib/hecks/doc/reference/rendering.rb +6 -3
- data/lib/hecks/forms/app/commands.rb +8 -7
- data/lib/hecks/forms/app/queries.rb +2 -1
- data/lib/hecks/forms/app.rb +1 -1
- data/lib/hecks/forms/record_renderer.rb +1 -1
- data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
- data/lib/hecks/fuzzing/failure_triage.rb +2 -1
- data/lib/hecks/fuzzing/mutation/run.rb +11 -4
- data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
- data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
- data/lib/hecks/fuzzing/properties/guards.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency.rb +6 -2
- data/lib/hecks/fuzzing/shrinker.rb +3 -1
- data/lib/hecks/fuzzing/value_generator.rb +19 -5
- data/lib/hecks/gate/stages.yml +33 -7
- data/lib/hecks/grammar/evolve.rb +1 -1
- data/lib/hecks/grammar/expression_operators.json +372 -0
- data/lib/hecks/grammar/operators.rb +3 -1
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
- data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
- data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
- data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
- data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
- data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
- data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
- data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
- data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
- data/lib/hecks/hecks/adapters/local_files.rb +2 -2
- data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
- data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
- data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
- data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +133 -0
- data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
- data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
- data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
- data/lib/hecks/hecks/adapters/terminal.rb +13 -11
- data/lib/hecks/hecks/codebase.behaviors +13 -1
- data/lib/hecks/hecks/codebase.bluebook +239 -5
- data/lib/hecks/hecks/custodian.behaviors +24 -6
- data/lib/hecks/hecks/custodian.bluebook +6 -1227
- data/lib/hecks/hecks/deploy.behaviors +1 -1
- data/lib/hecks/hecks/deploy.world +1 -3
- data/lib/hecks/hecks/hecks.behaviors +1 -1
- data/lib/hecks/hecks/hecks.hecksagon +53 -18
- data/lib/hecks/hecks/hecks.world +13 -10
- data/lib/hecks/hecks/rehearsal.bluebook +477 -0
- data/lib/hecks/hecks/site.behaviors +1 -1
- data/lib/hecks/hecks/site.world +0 -1
- data/lib/hecks/hecks/tooling.bluebook +1238 -0
- data/lib/hecks/indifferent_key.rb +20 -0
- data/lib/hecks/language/adapter.bluebook +13 -3
- data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
- data/lib/hecks/language/bluebook/bluebook.behaviors +23 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
- data/lib/hecks/language/bluebook/entity.bluebook +18 -0
- data/lib/hecks/language/bluebook/policy.behaviors +19 -0
- data/lib/hecks/language/bluebook/policy.bluebook +9 -1
- data/lib/hecks/language/bluebook/query.bluebook +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +63 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
- data/lib/hecks/language/oidc.json +10 -0
- data/lib/hecks/language/port.bluebook +4 -4
- data/lib/hecks/literal.rb +12 -3
- data/lib/hecks/ports/key_vault.rb +7 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
- data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
- data/lib/hecks/projection_files/expression_tables.rb +7 -2
- data/lib/hecks/projections/deploy/box/compose.rb +6 -4
- data/lib/hecks/projections/deploy/box/edge.rb +62 -0
- data/lib/hecks/projections/deploy/box/logging.rb +99 -0
- data/lib/hecks/projections/deploy/box/roll_scripts.rb +16 -8
- data/lib/hecks/projections/deploy/box/settings/cdn.rb +40 -0
- data/lib/hecks/projections/deploy/box/settings/logs.rb +40 -0
- data/lib/hecks/projections/deploy/box/settings/patterns.rb +11 -0
- data/lib/hecks/projections/deploy/box/settings/types.rb +16 -1
- data/lib/hecks/projections/deploy/box/settings.rb +6 -2
- data/lib/hecks/projections/deploy/box/stack_yaml.rb +5 -2
- data/lib/hecks/projections/deploy/box/templates/Makefile.tmpl +16 -0
- data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +8 -4
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +32 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/box/templates/edge.mk.tmpl +17 -0
- data/lib/hecks/projections/deploy/box/templates/edge.yaml.tmpl +65 -0
- data/lib/hecks/projections/deploy/box/templates/log-group.yaml.tmpl +12 -0
- data/lib/hecks/projections/deploy/box/templates/log-policy.yaml.tmpl +7 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +5 -4
- data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +5 -4
- data/lib/hecks/projections/deploy/box.rb +15 -3
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/shared.rb +5 -2
- data/lib/hecks/projections/model/deviations.rb +2 -2
- data/lib/hecks/projections/model.rb +2 -2
- data/lib/hecks/projections/oidc.rb +13 -1
- data/lib/hecks/projections/shape.rb +5 -2
- data/lib/hecks/projections/site/cms_editor/attributes.rb +26 -6
- data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
- data/lib/hecks/projections/site/cms_editor/checks.rb +14 -1
- data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
- data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
- data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
- data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
- data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
- data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
- data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
- data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
- data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
- data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
- data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
- data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
- data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
- data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
- data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
- data/lib/hecks/projections/site/cms_editor/schema.rb +96 -19
- data/lib/hecks/projections/site/cms_editor/setting.rb +52 -9
- data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +18 -1
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +251 -57
- data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +32 -24
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +78 -24
- data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +6 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +19 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +31 -8
- data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +131 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +8 -5
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +229 -34
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +40 -81
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +29 -12
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +53 -17
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +178 -88
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
- data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +2 -1
- data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
- data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
- data/lib/hecks/projections/site/cms_editor.rb +49 -23
- data/lib/hecks/projections/site/role_probe.rb +2 -0
- data/lib/hecks/projections/site/site_root/workflow.rb +5 -0
- data/lib/hecks/projector/cli_audience.rb +22 -1
- data/lib/hecks/projector/cli_projector/listing.rb +1 -1
- data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
- data/lib/hecks/projector/cli_projector/usage.rb +15 -4
- data/lib/hecks/projector/docs_projector/closing.rb +1 -1
- data/lib/hecks/projector/docs_projector.rb +1 -1
- data/lib/hecks/projector/exporter/bindings.rb +40 -10
- data/lib/hecks/projector/exporter/capability_values.rb +55 -0
- data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
- data/lib/hecks/projector/target.rb +11 -0
- data/lib/hecks/projector.rb +32 -5
- data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
- data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
- data/lib/hecks/quality_control/quality_control.bluebook +6 -2
- data/lib/hecks/release/approval.rb +64 -0
- data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
- data/lib/hecks/router/namespace_installer.rb +16 -16
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/best_effort.rb +22 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
- data/lib/hecks/runtime/command_interpreter.rb +3 -2
- data/lib/hecks/runtime/command_rules/emission.rb +1 -1
- data/lib/hecks/runtime/dispatcher.rb +4 -4
- data/lib/hecks/runtime/entity_element/location.rb +1 -1
- data/lib/hecks/runtime/event.rb +5 -0
- data/lib/hecks/runtime/identity.rb +7 -6
- data/lib/hecks/runtime/interpreting.rb +2 -1
- data/lib/hecks/runtime/invocation_builder.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
- data/lib/hecks/runtime/loader.rb +10 -10
- data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
- data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
- data/lib/hecks/runtime/policy_interpreter.rb +6 -6
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
- data/lib/hecks/runtime/reaction_invocation.rb +1 -1
- data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
- data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
- data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
- data/lib/hecks/runtime/registry/merging.rb +1 -1
- data/lib/hecks/runtime/registry/providers.rb +16 -0
- data/lib/hecks/runtime/registry/read_repository.rb +6 -4
- data/lib/hecks/runtime/registry/verification.rb +4 -1
- data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
- data/lib/hecks/runtime/registry.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
- data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter.rb +3 -3
- data/lib/hecks/runtime/tenant_check.rb +5 -4
- data/lib/hecks/runtime/value/coercion.rb +2 -2
- data/lib/hecks/runtime/value/validation.rb +16 -4
- data/lib/hecks/runtime.rb +9 -9
- data/lib/hecks/rust_build/host.rb +11 -4
- data/lib/hecks/rust_build/prebuilt_binary.rb +101 -0
- data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
- data/lib/hecks/rust_build/wasm.rb +1 -1
- data/lib/hecks/rust_build/wasm_browser.rb +1 -1
- data/lib/hecks/rust_build.rb +11 -0
- data/lib/hecks/served_options.rb +53 -0
- data/lib/hecks/site/bluebook/site.bluebook +1 -0
- data/lib/hecks/storehouse/audit.rb +1 -1
- data/lib/hecks/storehouse/dispatching.rb +8 -6
- data/lib/hecks/storehouse/introspection.rb +2 -2
- data/lib/hecks/storehouse/querying.rb +6 -4
- data/lib/hecks/storehouse.rb +4 -4
- data/lib/hecks/tools/ci_gate_decision.rb +35 -0
- data/lib/hecks/tools/ci_gates.rb +20 -1
- data/lib/hecks/tools/comment_style/source_file.rb +4 -1
- data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
- data/lib/hecks/tools/lanes/files.rb +8 -0
- data/lib/hecks/tools/lanes/prebuilt_workflow.rb +81 -0
- data/lib/hecks/tools/lanes/rows.rb +6 -0
- data/lib/hecks/tools/lanes.rb +1 -0
- data/lib/hecks/tools/oidc_manifests.rb +4 -5
- data/lib/hecks/tools/regeneration_run.rb +11 -4
- data/lib/hecks/tools/release_only.rb +111 -0
- data/lib/hecks/tools/site_routes/editor_files.rb +12 -5
- data/lib/hecks/tools/tenant_provisioning.rb +2 -2
- data/lib/hecks/tools/tools_doc.rb +4 -4
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +19 -5
- data/lib/hecks.rb +15 -15
- data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
- data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
- data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
- data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
- data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
- data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
- data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
- data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
- data/prebuilt/9ee54e2f0d4d6d3fe046827779b221e02de665fba911a048ae5dd8b8fbfb7d90.marshal +0 -0
- data/prebuilt/d74a00e996222c1d8b676d5959092effc46b4dc59e0555f3bf9554ac58e3094a.marshal +0 -0
- data/prebuilt/verdicts-df19f35868d3284a70f55e42dcef87ef4e65e6a678e5135f72e1f6cff4c1357b.json +1 -0
- data/rust/.gitignore +6 -0
- data/rust/Cargo.toml +6 -0
- data/rust/build/src/binding_pass.rs +103 -0
- data/rust/build/src/cargo_sync.rs +6 -0
- data/rust/build/src/lineage_pass.rs +3 -3
- data/rust/build/src/main.rs +17 -5
- data/rust/build/src/pipeline.rs +48 -5
- data/rust/build/src/seams_pass.rs +123 -0
- data/rust/build/src/translation_pass.rs +167 -0
- data/rust/codegen/src/asks.rs +103 -0
- data/rust/codegen/src/commands.rs +13 -8
- data/rust/codegen/src/constraints.rs +25 -0
- data/rust/codegen/src/domain_generator.rs +2 -0
- data/rust/codegen/src/expr_emitter.rs +13 -0
- data/rust/codegen/src/json_codec.rs +54 -15
- data/rust/codegen/src/main.rs +1 -0
- data/rust/codegen/src/mutations.rs +128 -3
- data/rust/codegen/src/queries.rs +11 -2
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/types.rs +166 -8
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +4 -4
- data/rust/host/src/bin/validate_harness.rs +43 -0
- data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
- data/rust/host/src/boundary_fuzz/ir.rs +1 -0
- data/rust/host/src/boundary_fuzz/server.rs +1 -1
- data/rust/host/src/checkout.rs +35 -19
- data/rust/host/src/commerce_ir.rs +408 -2
- data/rust/host/src/expr_json.rs +118 -0
- data/rust/host/src/extension.rs +13 -1
- data/rust/host/src/lib.rs +1 -0
- data/rust/host/src/mint.rs +1 -1
- data/rust/host/src/payments/tests.rs +27 -1
- data/rust/host/src/payments.rs +12 -4
- data/rust/host/src/platform_signin/attack_tests.rs +1100 -0
- data/rust/host/src/platform_signin/config.rs +512 -0
- data/rust/host/src/platform_signin/cookie.rs +237 -0
- data/rust/host/src/platform_signin/extension.rs +190 -0
- data/rust/host/src/platform_signin/fetcher.rs +322 -0
- data/rust/host/src/platform_signin/handler.rs +251 -0
- data/rust/host/src/platform_signin/handler_tests.rs +554 -0
- data/rust/host/src/platform_signin/jti.rs +196 -0
- data/rust/host/src/platform_signin/jwks.rs +542 -0
- data/rust/host/src/platform_signin/jws.rs +249 -0
- data/rust/host/src/platform_signin/known_answer_tests.rs +237 -0
- data/rust/host/src/platform_signin/mapping_tests.rs +254 -0
- data/rust/host/src/platform_signin/mod.rs +75 -0
- data/rust/host/src/platform_signin/mode_tests.rs +382 -0
- data/rust/host/src/platform_signin/pages.rs +204 -0
- data/rust/host/src/platform_signin/query.rs +233 -0
- data/rust/host/src/platform_signin/session.rs +151 -0
- data/rust/host/src/platform_signin/settings.rs +385 -0
- data/rust/host/src/platform_signin/strict_json.rs +351 -0
- data/rust/host/src/platform_signin/test_support.rs +235 -0
- data/rust/host/src/platform_signin/ttl_tests.rs +94 -0
- data/rust/host/src/platform_signin/verify.rs +604 -0
- data/rust/host/src/platform_signin/wiring_support.rs +319 -0
- data/rust/host/src/query_step.rs +22 -4
- data/rust/host/src/reference_validate.rs +653 -108
- data/rust/host/src/resend.rs +2 -0
- data/rust/host/src/server.rs +19 -6
- data/rust/host/src/ui_schema.rs +132 -5
- data/rust/host/src/web/newsletter.rs +7 -8
- data/rust/host/src/web/newsletter_send.rs +38 -13
- data/rust/host/src/web/registrations.rs +229 -26
- data/rust/host/src/web.rs +53 -9
- data/rust/parser/src/emit.rs +38 -6
- data/rust/parser/src/expr/ast_json.rs +7 -1
- data/rust/parser/src/expr/resolver.rs +171 -6
- data/rust/parser/src/ir.rs +7 -0
- data/rust/parser/src/keywords.rs +24 -0
- data/rust/parser/src/main.rs +3 -0
- data/rust/parser/src/parse/aggregate.rs +2 -1
- data/rust/parser/src/parse/chapter.rs +2 -2
- data/rust/parser/src/parse/command.rs +2 -0
- data/rust/parser/src/parse/hecksagon.rs +5 -2
- data/rust/parser/src/parse/lifecycle.rs +81 -0
- data/rust/parser/src/parse/mod.rs +15 -1
- data/rust/parser/src/parse/policy.rs +13 -2
- data/rust/parser/src/parse/value_object.rs +34 -17
- data/rust/parser/src/ruby_value.rs +1 -1
- data/rust/src/exemplar/commands.rs +4 -4
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/json.rs +43 -1
- data/rust/src/kernel/cli.rs +7 -5
- data/rust/src/kernel/dispatch.rs +2 -2
- data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
- data/rust/src/kernel/expr.rs +66 -3
- data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
- data/rust/src/kernel/expression_operators/text.rs +26 -2
- data/rust/src/kernel/json.rs +15 -0
- data/rust/src/kernel/mod.rs +1 -1
- data/rust/src/kernel/orchestrate.rs +54 -5
- data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
- data/rust/src/kernel/pattern.rs +60 -0
- data/rust/src/kernel/reference_lookup.rs +90 -7
- metadata +178 -38
- data/lib/hecks/doors/cli_door/nesting.rb +0 -34
- data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
- data/lib/hecks/doors/cli_door.rb +0 -124
- data/lib/hecks/doors/cli_runner/answers.rb +0 -138
- data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
- data/lib/hecks/doors/cli_runner/settling.rb +0 -115
- data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
- data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
- data/lib/hecks/doors/cli_runner.rb +0 -66
- data/lib/hecks/doors/command_request.rb +0 -119
- data/lib/hecks/doors/handle.rb +0 -208
- data/lib/hecks/doors/json_door.rb +0 -132
- data/lib/hecks/doors/launcher_options.rb +0 -247
- data/lib/hecks/doors/mcp_door/properties.rb +0 -47
- data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
- data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
- data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
- data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
- data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
- data/lib/hecks/doors/mcp_door.rb +0 -156
- data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
- data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
- data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
- data/lib/hecks/doors/mcp_door_scope.rb +0 -201
- data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
- data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
- data/lib/hecks/doors/ruby_door.rb +0 -80
- data/lib/hecks/doors/usage_cache.rb +0 -139
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_widget.js.tmpl +0 -282
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/media_picker.js.tmpl +0 -109
- data/prebuilt/c28d6d9db14eea3e17161e9d9c262efee30b256cf97693a7cfd552bce24b2f7f.marshal +0 -0
- data/prebuilt/cb375cba61ca0dbe8ddb9739c306510fe05e5f4b7a9b6955caff8224c63ea0e0.marshal +0 -0
- data/prebuilt/verdicts-d6d584ad43b5f3cebf88aafbbdbf024c38574acf18e06adffbc555f7786b1c2f.json +0 -1
|
@@ -126,7 +126,7 @@ module Hecks
|
|
|
126
126
|
# skips the vendored lookup
|
|
127
127
|
# @return [Array<String>] file paths to read, in load order; `[]` when none applies
|
|
128
128
|
def fallback_source_files(bluebook, directory, domain_files, registry)
|
|
129
|
-
vendored_name = registry && vendored_bluebook_name_for(registry, bluebook.name)
|
|
129
|
+
vendored_name = registry && vendored_bluebook_name_for(registry, bluebook.name, directory)
|
|
130
130
|
framework_path = Framework.members[bluebook.name]
|
|
131
131
|
return [framework_path] if framework_path
|
|
132
132
|
return vendored_source_for(directory, vendored_name) if vendored_name
|
|
@@ -139,19 +139,30 @@ module Hecks
|
|
|
139
139
|
|
|
140
140
|
# Recovers the vendored package name behind a bluebook, if some hecksagon vendored it.
|
|
141
141
|
#
|
|
142
|
-
# Matched by Pascal-casing
|
|
143
|
-
#
|
|
142
|
+
# Matched first by Pascal-casing (the package name Pascal-cases to the bluebook's declared
|
|
143
|
+
# name), then by the package's own files: a package whose chapter is named otherwise (the
|
|
144
|
+
# package `gallery` declaring `Galleries`) is the one whose files declare that name.
|
|
144
145
|
#
|
|
145
146
|
# @param registry [Runtime::Registry] the registry whose hecksagons are searched
|
|
146
147
|
# @param bluebook_name [String] the bluebook's declared Pascal-case name
|
|
148
|
+
# @param directory [String] path of the domain's own bluebook directory, whose parent
|
|
149
|
+
# holds `vendor/embryonaut_bluebooks`
|
|
147
150
|
# @return [String, nil] the package name as written in `attaches ... from: :vendor`; nil
|
|
148
|
-
# when no
|
|
149
|
-
def vendored_bluebook_name_for(registry, bluebook_name)
|
|
150
|
-
registry.hecksagons.each_value
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
# when no vendored package matches `bluebook_name` by name or by declaration
|
|
152
|
+
def vendored_bluebook_name_for(registry, bluebook_name, directory)
|
|
153
|
+
names = registry.hecksagons.each_value.flat_map(&:vendored_packages).uniq
|
|
154
|
+
names.find { |name| Naming.pascal(name) == bluebook_name } ||
|
|
155
|
+
names.find { |name| declared_by_package?(directory, name, bluebook_name) }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Asks whether any file of a vendored package opens the named bluebook.
|
|
159
|
+
#
|
|
160
|
+
# @param directory [String] path of the domain's own bluebook directory
|
|
161
|
+
# @param package [String] the vendored package name
|
|
162
|
+
# @param bluebook_name [String] the declared bluebook name looked for
|
|
163
|
+
# @return [Boolean] true when some `.bluebook` file of the package declares `bluebook_name`
|
|
164
|
+
def declared_by_package?(directory, package, bluebook_name)
|
|
165
|
+
vendored_source_for(directory, package).any? { |path| declares_bluebook?(path, bluebook_name) }
|
|
155
166
|
end
|
|
156
167
|
|
|
157
168
|
# Lists the `.bluebook` files a vendored embryonaut package ships.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require_relative "../../../../../indifferent_key"
|
|
2
|
+
|
|
1
3
|
module Hecks
|
|
2
4
|
module Adapters
|
|
3
5
|
class PostgresEra
|
|
@@ -17,11 +19,11 @@ module Hecks
|
|
|
17
19
|
# Coalesce instead of `setting` for the era: the factory always passes `era:`, nil until
|
|
18
20
|
# the boot gate resolves it, and nil can never be a real override.
|
|
19
21
|
def resolve_era!(settings)
|
|
20
|
-
@era =
|
|
22
|
+
@era = IndifferentKey.read(settings, :era)
|
|
21
23
|
@era ||= @lineage.current_era
|
|
22
24
|
# Non-nil only for a held-but-superseded boot. `append` and `atomic_put` refuse on it
|
|
23
25
|
# because a superuser walks through the RLS fence.
|
|
24
|
-
@superseded_by =
|
|
26
|
+
@superseded_by = IndifferentKey.read(settings, :superseded_by)
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def provision!
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require_relative "../lineage"
|
|
2
2
|
require_relative "../../storage_shape"
|
|
3
3
|
require_relative "context"
|
|
4
|
+
require_relative "../../../../../../indifferent_key"
|
|
4
5
|
|
|
5
6
|
module Hecks
|
|
6
7
|
module Adapters
|
|
@@ -22,7 +23,7 @@ module Hecks
|
|
|
22
23
|
lineage.check_fence_applies!(allow_superuser: PostgresEra.setting(settings, :allow_superuser, default: false))
|
|
23
24
|
lineage.ensure_base!
|
|
24
25
|
# key? first, never `||`: a stored `false` must not read as an absent key.
|
|
25
|
-
role =
|
|
26
|
+
role = IndifferentKey.read(settings, :role)
|
|
26
27
|
resolve_era(Context.new(registry: registry, bluebook: bluebook, current_text: current_text,
|
|
27
28
|
lineage: lineage, role: role, directory: directory))
|
|
28
29
|
ensure
|
|
@@ -26,7 +26,7 @@ module Hecks
|
|
|
26
26
|
# @raise [ArgumentError] on an unknown option
|
|
27
27
|
def build(registry, domain, aggregate, bind, **)
|
|
28
28
|
opts = BuildOptions.new(**)
|
|
29
|
-
settings = adapter_settings(registry, domain, bind, opts.settings_verb)
|
|
29
|
+
settings = adapter_settings(registry, domain, aggregate, bind, opts.settings_verb)
|
|
30
30
|
adapter = registry.adapter_class(bind.adapter).new(aggregate: aggregate, settings: settings, root: registry.root)
|
|
31
31
|
repository = AppendOnly.new(CodecBoundary.guard!(adapter))
|
|
32
32
|
# `:atomic_append` (PostgresEra) means `append` already committed the
|
|
@@ -39,14 +39,17 @@ module Hecks
|
|
|
39
39
|
#
|
|
40
40
|
# @param registry [Runtime::Registry] the booted registry
|
|
41
41
|
# @param domain [String, Symbol] the domain the aggregate belongs to
|
|
42
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate the adapter is built for
|
|
42
43
|
# @param bind [Bluebook::Bind] the bind naming the adapter
|
|
43
44
|
# @param settings_verb [String] the verb whose settings configure the adapter
|
|
44
|
-
# @return [Hash] the declared settings
|
|
45
|
-
|
|
45
|
+
# @return [Hash] the declared settings, under the adapter's own declared defaults, plus
|
|
46
|
+
# the domain, resolved era and superseding era
|
|
47
|
+
def adapter_settings(registry, domain, aggregate, bind, settings_verb)
|
|
46
48
|
registry.check_verb(bind)
|
|
47
49
|
settings = registry.binding_settings(domain, settings_verb, bind.adapter)
|
|
48
50
|
.reject { |key, _| key.to_sym == :role }
|
|
49
51
|
registry.check_settings(bind, settings)
|
|
52
|
+
settings = registry.with_adapter_defaults(bind.adapter, aggregate, settings)
|
|
50
53
|
# domain, resolved era and superseding era ride along outside the declared
|
|
51
54
|
# settings: a lineage adapter needs them to journal per domain, per-era.
|
|
52
55
|
settings.merge(domain: domain.to_s, era: registry.resolved_eras[domain.to_s],
|
|
@@ -47,10 +47,15 @@ module Hecks
|
|
|
47
47
|
declared = algebra.fetch(operator[:symbol]) do
|
|
48
48
|
raise Refused, "#{operator[:symbol]} is admitted but Vocabulary::Comparison declares no algebra for it"
|
|
49
49
|
end
|
|
50
|
-
row.merge(compares_less_than: declared[:compares_less_than],
|
|
51
|
-
compares_equal:
|
|
50
|
+
row.merge(compares_less_than: spelled_true?(declared[:compares_less_than]),
|
|
51
|
+
compares_equal: spelled_true?(declared[:compares_equal]),
|
|
52
|
+
negated: spelled_true?(declared[:negated]))
|
|
52
53
|
end
|
|
53
54
|
|
|
55
|
+
# @param cell [Object] a `Comparison` cell, declared as the text `"true"` or `"false"`
|
|
56
|
+
# @return [Boolean] whether the cell spells true
|
|
57
|
+
def spelled_true?(cell) = cell.to_s == "true"
|
|
58
|
+
|
|
54
59
|
# @param dropped [Hash{String => Array<String>}] operator to the guards evaluating through it
|
|
55
60
|
# @return [String] one line for each operator a projection would strand
|
|
56
61
|
def dropped_message(dropped)
|
|
@@ -13,11 +13,13 @@ module Hecks
|
|
|
13
13
|
# an ECS task definition when the world names one
|
|
14
14
|
def render_compose_sh(plan, region)
|
|
15
15
|
if plan.task_definition
|
|
16
|
-
template("render-compose-taskdef.sh.tmpl",
|
|
17
|
-
|
|
16
|
+
template("render-compose-taskdef.sh.tmpl",
|
|
17
|
+
{ "STACK" => plan.infra_name, "FAMILY" => plan.task_definition, "PROXY_IMAGE" => plan.proxy_image }
|
|
18
|
+
.merge(compose_log_values(plan, region, "$s.name")))
|
|
18
19
|
else
|
|
19
|
-
template("render-compose.sh.tmpl",
|
|
20
|
-
|
|
20
|
+
template("render-compose.sh.tmpl",
|
|
21
|
+
{ "STACK" => plan.infra_name, "REGION" => region, "DB_NAME" => plan.database_name,
|
|
22
|
+
"PROXY_IMAGE" => plan.proxy_image }.merge(compose_log_values(plan, region, ".name")))
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Projections
|
|
3
|
+
module Deploy
|
|
4
|
+
module Box
|
|
5
|
+
# The CloudFront edge of a box deploy, for a world that declares `cdn`: the `edge.yaml`
|
|
6
|
+
# stack, the `edge.mk` that deploys it, and the two pieces `make stacks` gains so the box
|
|
7
|
+
# admits the CDN's addresses. Mixed into `Box`, which supplies `template`.
|
|
8
|
+
module Edge
|
|
9
|
+
# The AWS-managed prefix list of CloudFront's origin-facing addresses. Its id differs in
|
|
10
|
+
# every region, so it is looked up by this name rather than written down.
|
|
11
|
+
PREFIX_LIST_NAME = "com.amazonaws.global.cloudfront.origin-facing".freeze
|
|
12
|
+
|
|
13
|
+
# @param plan [Settings::Plan] the resolved settings
|
|
14
|
+
# @param region [String] the validated region
|
|
15
|
+
# @return [Hash{String => String}] `edge.yaml` and `edge.mk`, or nothing without a `cdn`
|
|
16
|
+
def edge_files(plan, region)
|
|
17
|
+
return {} unless plan.cdn
|
|
18
|
+
|
|
19
|
+
{ "edge.yaml" => edge_yaml(plan),
|
|
20
|
+
"edge.mk" => template("edge.mk.tmpl", "EDGE_STACK" => plan.edge_stack, "REGION" => region) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @param plan [Settings::Plan] the resolved settings, with a `cdn`
|
|
24
|
+
# @return [String] the CloudFront stack
|
|
25
|
+
def edge_yaml(plan)
|
|
26
|
+
values = { "STACK" => plan.infra_name, "ALIASES" => plan.cdn.aliases.join(","),
|
|
27
|
+
"ORIGIN_HEADER" => plan.origin_header, "ORIGIN_SECRET" => plan.origin_secret }
|
|
28
|
+
template("edge.yaml.tmpl", values)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @param plan [Settings::Plan] the resolved settings
|
|
32
|
+
# @param region [String] the validated region
|
|
33
|
+
# @return [Hash{String => String}] the Makefile markers `CDN_CHECK` and `CDN_PARAM`:
|
|
34
|
+
# empty without a `cdn`, so a world that does not opt in renders the same Makefile
|
|
35
|
+
def edge_makefile_values(plan, region)
|
|
36
|
+
return { "CDN_CHECK" => "", "CDN_PARAM" => "" } unless plan.cdn
|
|
37
|
+
|
|
38
|
+
{ "CDN_CHECK" => prefix_list_check(region), "CDN_PARAM" => "\t\tCdnPrefixListId=#{prefix_list_lookup(region)} \\" }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def prefix_list_query(region)
|
|
44
|
+
"aws ec2 describe-managed-prefix-lists --region #{region} --filters " \
|
|
45
|
+
"Name=prefix-list-name,Values=#{PREFIX_LIST_NAME} --query \"PrefixLists[0].PrefixListId\" --output text"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def prefix_list_lookup(region)
|
|
49
|
+
"$$(#{prefix_list_query(region)})"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Stops `make stacks` before it changes anything when the prefix list cannot be found,
|
|
53
|
+
# because a box deployed without it has no inbound rule and the CDN gets a 504.
|
|
54
|
+
def prefix_list_check(region)
|
|
55
|
+
"\t@#{prefix_list_query(region)} | grep -q '^pl-' || { echo \"no #{PREFIX_LIST_NAME} " \
|
|
56
|
+
"prefix list in #{region}: the box would admit no CDN and answer 504\" >&2; exit 1; }"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Projections
|
|
3
|
+
module Deploy
|
|
4
|
+
module Box
|
|
5
|
+
# Where the box's containers write their logs. With `logs "cloudwatch"` (the default) every
|
|
6
|
+
# service uses Docker's `awslogs` driver into one log group the box stack creates, one
|
|
7
|
+
# stream per service, and the box role may write to that group only. With `logs "local"`
|
|
8
|
+
# the services keep Docker's rotating `json-file` logs, which a roll deletes with the
|
|
9
|
+
# container.
|
|
10
|
+
#
|
|
11
|
+
# `docker logs` keeps working with `awslogs`: Docker's dual-logging cache (Docker 20.10 and
|
|
12
|
+
# later, which the Amazon Linux 2023 package installs) keeps a local copy for reads.
|
|
13
|
+
# Mixed into `Box`, which supplies `template`.
|
|
14
|
+
module Logging
|
|
15
|
+
LOG_EXIT_CODE = "# 44 the box stack has no log group yet: update the stack (make stacks) before rolling\n".freeze
|
|
16
|
+
LOG_NOTE = <<~NOTE.freeze
|
|
17
|
+
# Every container logs to the CloudWatch group the box stack creates. A container whose log
|
|
18
|
+
# stream cannot be written does not start, so the roll first checks that the stack has the
|
|
19
|
+
# group (and the role's right to write it) and exits 44 if not. SKIP_LOG_GROUP_CHECK=1 skips it.
|
|
20
|
+
# `docker logs` and the log capture below still read the box's local copy (Docker's dual-logging cache).
|
|
21
|
+
#
|
|
22
|
+
NOTE
|
|
23
|
+
LOG_GROUP_CHECK = <<~CHECK.freeze
|
|
24
|
+
if [ "${SKIP_LOG_GROUP_CHECK:-}" = 1 ]; then
|
|
25
|
+
echo "==> skipping the log group check (SKIP_LOG_GROUP_CHECK=1)"
|
|
26
|
+
elif ! aws cloudformation describe-stack-resource --stack-name "$BOX_STACK" --logical-resource-id BoxLogGroup >/dev/null 2>&1; then
|
|
27
|
+
echo "==> $BOX_STACK has no BoxLogGroup: its containers would fail to start on the awslogs driver." >&2
|
|
28
|
+
echo " Run make stacks to update box.yaml (log group and the role's logs policy) first, then roll again." >&2
|
|
29
|
+
exit 44
|
|
30
|
+
fi
|
|
31
|
+
CHECK
|
|
32
|
+
COMPOSE_LOG_NOTE = <<~NOTE.freeze
|
|
33
|
+
#
|
|
34
|
+
# Every service logs to the CloudWatch group the box stack creates (one stream per service). A
|
|
35
|
+
# container whose stream cannot be written does not start, so update the box stack (make stacks:
|
|
36
|
+
# the group and the role's logs policy) before the first roll that uses this file. `docker logs`
|
|
37
|
+
# still reads the box's local copy.
|
|
38
|
+
NOTE
|
|
39
|
+
JSON_FILE = '{driver: "json-file", options: {"max-size": "10m", "max-file": "3"}}'.freeze
|
|
40
|
+
|
|
41
|
+
# @param plan [Settings::Plan] the resolved settings
|
|
42
|
+
# @return [String] the log group resource spliced into `box.yaml`, or nothing for `local`
|
|
43
|
+
def log_group_yaml(plan)
|
|
44
|
+
return "" unless plan.cloudwatch_logs?
|
|
45
|
+
|
|
46
|
+
template("log-group.yaml.tmpl", "LOG_GROUP" => plan.log_group,
|
|
47
|
+
"RETENTION_DAYS" => plan.log_retention_days.to_s)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @param plan [Settings::Plan] the resolved settings
|
|
51
|
+
# @return [String] the role policy that lets the box write to its log group only, or
|
|
52
|
+
# nothing for `local`
|
|
53
|
+
def log_policy_yaml(plan)
|
|
54
|
+
plan.cloudwatch_logs? ? template("log-policy.yaml.tmpl", {}) : ""
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The values `render-compose.sh` splices in: the jq definition of `log` and the call that
|
|
58
|
+
# names each service's stream. `local` renders the `json-file` definition and the bare
|
|
59
|
+
# `log`, so its output does not change.
|
|
60
|
+
#
|
|
61
|
+
# @param plan [Settings::Plan] the resolved settings
|
|
62
|
+
# @param region [String] the validated region
|
|
63
|
+
# @param service [String] the jq expression for a service's name in the template
|
|
64
|
+
# @return [Hash{String => String}] the template values
|
|
65
|
+
def compose_log_values(plan, region, service)
|
|
66
|
+
return { "COMPOSE_LOG_NOTE" => "", **local_log_values } unless plan.cloudwatch_logs?
|
|
67
|
+
|
|
68
|
+
{ "COMPOSE_LOG_NOTE" => COMPOSE_LOG_NOTE, "LOG_DEF" => awslogs_definition(plan, region),
|
|
69
|
+
"LOG_SERVICE" => "log(#{service})", "LOG_CADDY" => 'log("caddy")', "LOG_TUNNEL" => 'log("cloudflared")' }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# The `deploy-box.sh` pieces that stop a roll onto a stack without the log group.
|
|
73
|
+
#
|
|
74
|
+
# @param plan [Settings::Plan] the resolved settings
|
|
75
|
+
# @return [Hash{String => String}] the exit-code line, the comment and the check, or empty
|
|
76
|
+
# strings for `local`
|
|
77
|
+
def log_roll_values(plan)
|
|
78
|
+
return { "LOG_EXIT_CODE" => "", "LOG_NOTE" => "", "LOG_GROUP_CHECK" => "" } unless plan.cloudwatch_logs?
|
|
79
|
+
|
|
80
|
+
{ "LOG_EXIT_CODE" => LOG_EXIT_CODE, "LOG_NOTE" => LOG_NOTE, "LOG_GROUP_CHECK" => LOG_GROUP_CHECK }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def awslogs_definition(plan, region)
|
|
86
|
+
options = %({"awslogs-region": "#{region}", "awslogs-group": "#{plan.log_group}", \
|
|
87
|
+
"awslogs-create-group": "false", tag: $service})
|
|
88
|
+
%(def log($service): {driver: "awslogs", options: #{options}};)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def local_log_values
|
|
92
|
+
{ "LOG_DEF" => "def log: #{JSON_FILE};", "LOG_SERVICE" => "log", "LOG_CADDY" => "log",
|
|
93
|
+
"LOG_TUNNEL" => "log" }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -45,7 +45,8 @@ module Hecks
|
|
|
45
45
|
"RDS_STACK" => plan.rds_stack, "REGION" => region,
|
|
46
46
|
"DIR" => "/opt/#{plan.infra_name}", "HEALTH_CHECKS" => health_checks(plan),
|
|
47
47
|
"SMOKE_HEADER" => smoke_header(plan), "USAGE" => deploy_usage(plan),
|
|
48
|
-
"DB_SECRET_LINE" => database_secret_line(plan)
|
|
48
|
+
"DB_SECRET_LINE" => database_secret_line(plan),
|
|
49
|
+
**log_roll_values(plan))
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
# @param plan [Settings::Plan] the resolved settings
|
|
@@ -94,18 +95,25 @@ module Hecks
|
|
|
94
95
|
end
|
|
95
96
|
|
|
96
97
|
# @param plan [Settings::Plan] the resolved settings
|
|
98
|
+
# @param region [String] the validated region
|
|
97
99
|
# @return [String] a Makefile whose targets create the two stacks and roll the box
|
|
98
|
-
def makefile(plan)
|
|
99
|
-
|
|
100
|
-
base = template("Makefile.tmpl",
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
.merge(database_makefile_values(plan)))
|
|
104
|
-
plan.hosting ? "#{base}\ninclude hosting.mk\n" : base
|
|
100
|
+
def makefile(plan, region)
|
|
101
|
+
values = makefile_values(plan, region)
|
|
102
|
+
base = template("Makefile.tmpl", values)
|
|
103
|
+
includes = [("hosting.mk" if plan.hosting), ("edge.mk" if plan.cdn)].compact
|
|
104
|
+
includes.empty? ? base : "#{base}\n#{includes.map { |file| "include #{file}\n" }.join}"
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
private
|
|
108
108
|
|
|
109
|
+
def makefile_values(plan, region)
|
|
110
|
+
hint = plan.task_definition ? "[TASKDEF=family:revision]" : '[TAGS="web=20260101 worker=20260101"]'
|
|
111
|
+
{ "STACK" => plan.infra_name, "DEPLOY_HINT" => hint, "RDS_STACK" => plan.rds_stack,
|
|
112
|
+
"BOX_STACK" => plan.box_stack, "DEPLOY_RECIPE" => RollRecipe.deploy(plan),
|
|
113
|
+
"AMI_PARAMETER" => ami_parameter(plan.instance_type) }
|
|
114
|
+
.merge(database_makefile_values(plan), edge_makefile_values(plan, region))
|
|
115
|
+
end
|
|
116
|
+
|
|
109
117
|
def schema_list(migration)
|
|
110
118
|
migration.schemas.map { |name| "`#{name}`" }.join(", ")
|
|
111
119
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative "patterns"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
module Deploy
|
|
6
|
+
module Box
|
|
7
|
+
module Settings
|
|
8
|
+
# Reads the `cdn` setting: the CloudFront edge in front of the box. Extended onto
|
|
9
|
+
# `Settings`. The alias names themselves are checked by the Deploy bluebook's
|
|
10
|
+
# `BoxTarget.Declare` before this runs; this checks the shape and the guard.
|
|
11
|
+
module CdnReaders
|
|
12
|
+
include Patterns
|
|
13
|
+
|
|
14
|
+
# @param value [Hash{Symbol => Object}, nil] the world's `cdn` setting
|
|
15
|
+
# @param origin_header [String, nil] the declared origin header
|
|
16
|
+
# @return [Cdn, nil] the edge to generate, or nil when the world declares none
|
|
17
|
+
# @raise [ArgumentError] when the shape is wrong or there is no origin guard
|
|
18
|
+
def read_cdn(value, origin_header)
|
|
19
|
+
return nil if value.nil?
|
|
20
|
+
|
|
21
|
+
aliases = cdn_aliases(value)
|
|
22
|
+
raise ArgumentError, CDN_NEEDS_ORIGIN if origin_header.nil?
|
|
23
|
+
|
|
24
|
+
Settings::Cdn.new(aliases: aliases.uniq)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def cdn_aliases(value)
|
|
30
|
+
listed = value.is_a?(Hash) && value.keys == [:aliases] ? value[:aliases] : nil
|
|
31
|
+
raise ArgumentError, CDN_SHAPE unless listed.is_a?(Array) && !listed.empty?
|
|
32
|
+
|
|
33
|
+
listed.map { |name| check(:cdn_aliases, name, CDN_ALIAS) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative "patterns"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
module Deploy
|
|
6
|
+
module Box
|
|
7
|
+
module Settings
|
|
8
|
+
# Reads the `logs` and `log_retention_days` settings: where the containers' logs go and
|
|
9
|
+
# how long CloudWatch keeps them. Extended onto `Settings`.
|
|
10
|
+
#
|
|
11
|
+
# Retention is one of CloudWatch's own values and never "never expire", so a box's log
|
|
12
|
+
# bill is bounded by a declaration.
|
|
13
|
+
module LogReaders
|
|
14
|
+
include Patterns
|
|
15
|
+
|
|
16
|
+
# @param settings [Hash{Symbol => Object}] the world's `AwsBox` settings
|
|
17
|
+
# @return [Hash{Symbol => Object}] the plan's `logs` and `log_retention_days`
|
|
18
|
+
# @raise [ArgumentError] when `logs` is not a known destination, or the retention is not
|
|
19
|
+
# a value CloudWatch accepts
|
|
20
|
+
def read_logs(settings)
|
|
21
|
+
destination = settings.fetch(:logs, "cloudwatch").to_s
|
|
22
|
+
raise ArgumentError, LOGS_SHAPE unless LOG_DESTINATIONS.include?(destination)
|
|
23
|
+
|
|
24
|
+
days = settings.fetch(:log_retention_days, DEFAULT_LOG_RETENTION_DAYS)
|
|
25
|
+
raise ArgumentError, retention_refusal(days) unless LOG_RETENTION_DAYS.include?(days)
|
|
26
|
+
|
|
27
|
+
{ logs: destination, log_retention_days: days }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def retention_refusal(days)
|
|
33
|
+
"log_retention_days: #{days.inspect} is not a CloudWatch retention; use one of #{LOG_RETENTION_DAYS.join(", ")}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -23,6 +23,13 @@ module Hecks
|
|
|
23
23
|
S3_BUCKET = /\A[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]\z/
|
|
24
24
|
S3_SHAPE = "s3_access: a list of `{ bucket: \"name\", write: true }` hashes (write is optional)".freeze
|
|
25
25
|
FROM_TASKDEF = %i[env secrets repository].freeze
|
|
26
|
+
LOG_DESTINATIONS = %w[cloudwatch local].freeze
|
|
27
|
+
DEFAULT_LOG_RETENTION_DAYS = 30
|
|
28
|
+
# The retention periods CloudWatch Logs accepts, in days, without "never expire".
|
|
29
|
+
LOG_RETENTION_DAYS = [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1096, 1827, 2192, 2557,
|
|
30
|
+
2922, 3288, 3653].freeze
|
|
31
|
+
LOGS_SHAPE = "logs: \"cloudwatch\" ships every container's log to CloudWatch Logs; " \
|
|
32
|
+
"\"local\" keeps Docker's rotating json-file logs on the box".freeze
|
|
26
33
|
|
|
27
34
|
# The settings that size an RDS instance: with `shared_database` they are the shared
|
|
28
35
|
# instance's.
|
|
@@ -37,6 +44,10 @@ module Hecks
|
|
|
37
44
|
"(the secret holding the tunnel token)".freeze
|
|
38
45
|
ORIGIN_ENV_SHAPE = "origin_env: the container environment variable names that hold the origin secret " \
|
|
39
46
|
"need an origin_secret and a task_definition to compare against".freeze
|
|
47
|
+
CDN_ALIAS = /\A[a-z0-9.-]{4,253}\z/
|
|
48
|
+
CDN_SHAPE = "cdn: a hash with `aliases`, the non-empty list of host names the CloudFront edge answers on".freeze
|
|
49
|
+
CDN_NEEDS_ORIGIN = "cdn: the box admits CloudFront's addresses on port 80, so it needs the origin guard " \
|
|
50
|
+
"that refuses every other caller; declare origin_header and origin_secret".freeze
|
|
40
51
|
ORIGIN_PAIR = "origin_header and origin_secret go together: the header a CDN sends, " \
|
|
41
52
|
"and the secret that holds its value".freeze
|
|
42
53
|
end
|
|
@@ -45,18 +45,33 @@ module Hecks
|
|
|
45
45
|
# @!attribute [r] write [Boolean] whether a production box may also write and delete
|
|
46
46
|
Bucket = Struct.new(:name, :write, keyword_init: true)
|
|
47
47
|
|
|
48
|
+
# The CloudFront edge a world asks for with `cdn`.
|
|
49
|
+
#
|
|
50
|
+
# @!attribute [r] aliases [Array<String>] the host names the distribution answers on
|
|
51
|
+
Cdn = Struct.new(:aliases, keyword_init: true)
|
|
52
|
+
|
|
48
53
|
# Everything the generator reads, checked.
|
|
49
54
|
Plan = Struct.new(
|
|
50
55
|
:infra_name, :stack_prefix, :instance_type, :volume_gb, :swap_gb, :database_class,
|
|
51
56
|
:storage_gb, :backup_days, :snapshots_keep, :database_name, :engine_version,
|
|
52
57
|
:containers, :routes, :default_container, :origin_header, :origin_secret,
|
|
53
58
|
:secret_prefixes, :writable_secrets, :origin_env, :tunnel, :tunnel_service, :proxy_image,
|
|
54
|
-
:task_definition, :migration, :s3_buckets, :hosting, :shared_database,
|
|
59
|
+
:task_definition, :migration, :s3_buckets, :hosting, :shared_database, :cdn,
|
|
60
|
+
:logs, :log_retention_days, keyword_init: true
|
|
55
61
|
) do
|
|
56
62
|
# @return [String] the CloudFormation stack that holds the database: the shared
|
|
57
63
|
# instance's when the world names one, else this site's own
|
|
58
64
|
def rds_stack = shared_database || "#{stack_prefix}-#{infra_name}-rds"
|
|
59
65
|
|
|
66
|
+
# @return [String] the CloudFormation stack that holds the CloudFront edge
|
|
67
|
+
def edge_stack = "#{stack_prefix}-#{infra_name}-edge"
|
|
68
|
+
|
|
69
|
+
# @return [String] the CloudWatch log group the box's containers write to
|
|
70
|
+
def log_group = "/#{box_stack}"
|
|
71
|
+
|
|
72
|
+
# @return [Boolean] whether the containers' logs ship to CloudWatch Logs
|
|
73
|
+
def cloudwatch_logs? = logs == "cloudwatch"
|
|
74
|
+
|
|
60
75
|
# @return [Boolean] whether the database lives on an instance shared with other sites
|
|
61
76
|
def shared? = !shared_database.nil?
|
|
62
77
|
|
|
@@ -4,6 +4,8 @@ require_relative "settings/types"
|
|
|
4
4
|
require_relative "settings/checks"
|
|
5
5
|
require_relative "settings/containers"
|
|
6
6
|
require_relative "settings/origin"
|
|
7
|
+
require_relative "settings/cdn"
|
|
8
|
+
require_relative "settings/logs"
|
|
7
9
|
|
|
8
10
|
module Hecks
|
|
9
11
|
module Projections
|
|
@@ -21,6 +23,8 @@ module Hecks
|
|
|
21
23
|
extend Checks
|
|
22
24
|
extend ContainerReaders
|
|
23
25
|
extend OriginReaders
|
|
26
|
+
extend CdnReaders
|
|
27
|
+
extend LogReaders
|
|
24
28
|
|
|
25
29
|
module_function
|
|
26
30
|
|
|
@@ -50,7 +54,7 @@ module Hecks
|
|
|
50
54
|
shared_database: read_shared_database(settings),
|
|
51
55
|
s3_buckets: read_s3_access(settings.fetch(:s3_access, [])),
|
|
52
56
|
hosting: HostingSettings.read(settings, task_definition)
|
|
53
|
-
}
|
|
57
|
+
}.merge(read_logs(settings))
|
|
54
58
|
end
|
|
55
59
|
|
|
56
60
|
# @param settings [Hash{Symbol => Object}] the world's `AwsBox` settings
|
|
@@ -62,7 +66,7 @@ module Hecks
|
|
|
62
66
|
origin_env = read_origin_env(settings, secret, task_definition)
|
|
63
67
|
tunnel, tunnel_service = read_tunnel(settings.fetch(:tunnel, false), containers)
|
|
64
68
|
{ origin_header: header, origin_secret: secret, origin_env: origin_env,
|
|
65
|
-
tunnel: tunnel, tunnel_service: tunnel_service }
|
|
69
|
+
tunnel: tunnel, tunnel_service: tunnel_service, cdn: read_cdn(settings[:cdn], header) }
|
|
66
70
|
end
|
|
67
71
|
|
|
68
72
|
# @return [Hash{Symbol => Object}] the plan's names, sizes, routes and secret scopes
|
|
@@ -20,16 +20,19 @@ module Hecks
|
|
|
20
20
|
template("box.yaml.tmpl",
|
|
21
21
|
"STACK" => plan.infra_name, "INSTANCE_TYPE" => plan.instance_type,
|
|
22
22
|
"VOLUME_GB" => plan.volume_gb.to_s, "SNAPSHOTS_KEEP" => plan.snapshots_keep.to_s,
|
|
23
|
-
"AMI_PARAMETER" => ami_parameter(plan.instance_type),
|
|
24
23
|
"COMPUTE_DOMAIN" => compute_domain(region),
|
|
25
24
|
"SECRET_RESOURCES" => secret_resources(plan), "WRITABLE_SECRETS" => writable_secrets(plan),
|
|
26
25
|
"TUNNEL_EGRESS" => tunnel_egress(plan),
|
|
27
26
|
"SWAP_COMMANDS" => swap_commands(plan), "ECR_REPOSITORIES" => ecr_repositories(plan),
|
|
28
|
-
"ECR_OUTPUTS" => ecr_outputs(plan), "S3_POLICY" => s3_policy(plan)
|
|
27
|
+
"ECR_OUTPUTS" => ecr_outputs(plan), "S3_POLICY" => s3_policy(plan),
|
|
28
|
+
"LOG_GROUP" => log_group_yaml(plan), "LOG_POLICY" => log_policy_yaml(plan))
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# Graviton families end their generation digit with `g` (`t4g`, `m7gd`, `c6gn`).
|
|
32
32
|
#
|
|
33
|
+
# Only a first create reads it, from `make stacks`: a live box keeps the image it runs,
|
|
34
|
+
# because a newer one would replace the instance.
|
|
35
|
+
#
|
|
33
36
|
# @param instance_type [String] an EC2 instance type
|
|
34
37
|
# @return [String] the SSM parameter of the matching Amazon Linux image
|
|
35
38
|
def ami_parameter(instance_type)
|
|
@@ -1,21 +1,37 @@
|
|
|
1
1
|
# @@STACK@@: @@WHAT@@.
|
|
2
2
|
# make stacks VPC=vpc-... PRIVATE_SUBNETS=subnet-a,subnet-b PUBLIC_SUBNET=subnet-c [REHEARSAL=true]
|
|
3
3
|
# make deploy @@DEPLOY_HINT@@
|
|
4
|
+
# `make stacks` keeps a live box on the image it runs, so an update never replaces the instance.
|
|
5
|
+
# To move to a newer image on purpose: make stacks ... AMI_ID=ami-...
|
|
4
6
|
RDS_STACK = @@RDS_STACK@@
|
|
5
7
|
BOX_STACK = @@BOX_STACK@@
|
|
6
8
|
@@REHEARSAL_NOTE@@
|
|
7
9
|
REHEARSAL ?= false
|
|
10
|
+
AMI_ID ?=
|
|
8
11
|
|
|
9
12
|
HECKS ?= hecks
|
|
10
13
|
|
|
11
14
|
@@PHONY@@
|
|
12
15
|
stacks:
|
|
16
|
+
@@CDN_CHECK@@
|
|
13
17
|
@@RDS_DEPLOY@@
|
|
18
|
+
@AMI="$(AMI_ID)"; SRC="given as AMI_ID"; \
|
|
19
|
+
if [ -z "$$AMI" ]; then \
|
|
20
|
+
BOX=$$(aws cloudformation describe-stack-resource --stack-name $(BOX_STACK) --logical-resource-id Box --query StackResourceDetail.PhysicalResourceId --output text 2>/dev/null); \
|
|
21
|
+
case "$$BOX" in \
|
|
22
|
+
i-*) AMI=$$(aws ec2 describe-instances --instance-ids "$$BOX" --query "Reservations[0].Instances[0].ImageId" --output text); SRC="pinned: the image $$BOX runs" ;; \
|
|
23
|
+
*) AMI=$$(aws ssm get-parameter --name @@AMI_PARAMETER@@ --query Parameter.Value --output text); SRC="new: no $(BOX_STACK) stack yet, so the latest image" ;; \
|
|
24
|
+
esac; \
|
|
25
|
+
fi; \
|
|
26
|
+
case "$$AMI" in ami-*) ;; *) echo "no ami- image found for $(BOX_STACK): pass one with AMI_ID=ami-..." >&2; exit 1 ;; esac; \
|
|
27
|
+
echo "==> AmiId $$AMI ($$SRC)"; \
|
|
14
28
|
aws cloudformation deploy --template-file box.yaml --stack-name $(BOX_STACK) --capabilities CAPABILITY_IAM \
|
|
15
29
|
--parameter-overrides VpcId=$(VPC) SubnetId=$(PUBLIC_SUBNET) \
|
|
16
30
|
DbSecurityGroupId=$$(aws cloudformation describe-stacks --stack-name $(RDS_STACK) --query "Stacks[0].Outputs[?OutputKey=='DbSecurityGroupId'].OutputValue" --output text) \
|
|
17
31
|
@@DB_SECRET_PARAM@@
|
|
32
|
+
AmiId=$$AMI \
|
|
18
33
|
AlertTopicArn=$$(aws cloudformation describe-stacks --stack-name $(RDS_STACK) --query "Stacks[0].Outputs[?OutputKey=='AlertTopicArn'].OutputValue" --output text) \
|
|
34
|
+
@@CDN_PARAM@@
|
|
19
35
|
Rehearsal=$(REHEARSAL)
|
|
20
36
|
|
|
21
37
|
@@PROVISION@@
|
|
@@ -20,9 +20,12 @@ Parameters:
|
|
|
20
20
|
Type: String
|
|
21
21
|
Default: @@INSTANCE_TYPE@@
|
|
22
22
|
AmiId:
|
|
23
|
-
Type: AWS::
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
Type: AWS::EC2::Image::Id
|
|
24
|
+
Description: >-
|
|
25
|
+
The image the box runs, always given and never resolved here. Resolving the public image
|
|
26
|
+
key again on a live box finds the newest image, and a changed ImageId replaces the instance
|
|
27
|
+
and its Elastic IP association. make stacks passes the running box's own image, and takes a
|
|
28
|
+
newer one only when AMI_ID is given.
|
|
26
29
|
RootVolumeGb:
|
|
27
30
|
Type: Number
|
|
28
31
|
Default: @@VOLUME_GB@@
|
|
@@ -82,7 +85,7 @@ Resources:
|
|
|
82
85
|
SourceSecurityGroupId: !Ref BoxSecurityGroup
|
|
83
86
|
Description: App box
|
|
84
87
|
|
|
85
|
-
BoxRole:
|
|
88
|
+
@@LOG_GROUP@@ BoxRole:
|
|
86
89
|
Type: AWS::IAM::Role
|
|
87
90
|
Properties:
|
|
88
91
|
AssumeRolePolicyDocument:
|
|
@@ -106,6 +109,7 @@ Resources:
|
|
|
106
109
|
@@SECRET_RESOURCES@@
|
|
107
110
|
@@WRITABLE_SECRETS@@
|
|
108
111
|
@@S3_POLICY@@
|
|
112
|
+
@@LOG_POLICY@@
|
|
109
113
|
|
|
110
114
|
BoxProfile:
|
|
111
115
|
Type: AWS::IAM::InstanceProfile
|