hecks 3.9.1 → 3.11.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 +155 -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 +69 -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/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/rules.rb +4 -2
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
- data/lib/hecks/bluebook/assembly/marks.rb +1 -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 +18 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
- 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 +1 -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 +124 -2
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +8 -0
- data/lib/hecks/deploy/oidc.json +16 -3
- 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 +4 -10
- 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/clearing.rb +51 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +2 -0
- 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 +129 -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/site_toolchain/role_prober.rb +78 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +29 -4
- data/lib/hecks/hecks/adapters/terminal.rb +13 -11
- data/lib/hecks/hecks/adapters/world_clearing.rb +65 -0
- 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 +61 -18
- data/lib/hecks/hecks/hecks.world +11 -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.bluebook +15 -0
- data/lib/hecks/language/bluebook/entity.bluebook +18 -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 +11 -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/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +15 -0
- data/lib/hecks/projections/deploy/fargate/settings.rb +3 -1
- data/lib/hecks/projections/deploy/fargate/stack/readers.rb +1 -0
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/shared.rb +5 -2
- data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +7 -4
- 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 +95 -0
- data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
- data/lib/hecks/projections/site/cms_editor/checks.rb +77 -0
- 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 +85 -0
- 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/pretty.rb +40 -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 +161 -0
- data/lib/hecks/projections/site/cms_editor/setting.rb +130 -0
- data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +370 -0
- 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 +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/session.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/sso.ts.tmpl +20 -0
- 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 +133 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +29 -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 +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +177 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/multipart.ts.tmpl +53 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/sniff.ts.tmpl +94 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/storage.ts.tmpl +50 -0
- 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 +214 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/server.ts.tmpl +65 -0
- 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 +123 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_parse.js.tmpl +320 -0
- 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 +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +61 -0
- 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 +92 -0
- 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 +63 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +213 -0
- 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 +17 -0
- 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 +133 -0
- data/lib/hecks/projections/site/role_probe.rb +74 -0
- data/lib/hecks/projector/cli_audience.rb +2 -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/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/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 +29 -1
- data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -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/comment_style/source_file.rb +4 -1
- data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
- data/lib/hecks/tools/lanes/rows.rb +6 -0
- data/lib/hecks/tools/oidc_manifests.rb +4 -5
- data/lib/hecks/tools/regeneration_run.rb +11 -4
- data/lib/hecks/tools/site_routes/cli.rb +1 -1
- data/lib/hecks/tools/site_routes/editor_files.rb +45 -0
- data/lib/hecks/tools/site_routes.rb +14 -7
- 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 +4 -4
- 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/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
- data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
- data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
- data/rust/.gitignore +6 -0
- data/rust/Cargo.toml +9 -1
- 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/bridging.rs +1 -1
- data/rust/codegen/src/commands.rs +13 -8
- data/rust/codegen/src/constraints.rs +25 -0
- data/rust/codegen/src/domain_generator.rs +11 -9
- data/rust/codegen/src/expr_emitter.rs +13 -0
- data/rust/codegen/src/json_codec.rs +54 -15
- data/rust/codegen/src/main.rs +2 -1
- data/rust/codegen/src/mutations.rs +174 -4
- data/rust/codegen/src/naming.rs +30 -0
- 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/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/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 +3 -3
- 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 +13 -8
- 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 +73 -4
- data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
- data/rust/src/kernel/expression_operators/presence.rs +41 -3
- 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 +174 -36
- 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/prebuilt/0ee7f83cba59ce7d1c45c65ba166b071779f70143449f584f33ff7989f64eccb.marshal +0 -0
- data/prebuilt/6e4a6bcf0aa880e2e6b056e1846b70e5a768090b9dc998bbadeaf5d6bcfd3db8.marshal +0 -0
- data/prebuilt/verdicts-91aaf93ce8981bd9a754948f075d95650dc2ec6435015376f5c60992b36a24b1.json +0 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "uri"
|
|
6
|
+
require_relative "declaration"
|
|
7
|
+
require_relative "postgres_reading"
|
|
8
|
+
|
|
9
|
+
module Hecks
|
|
10
|
+
module Adapters
|
|
11
|
+
module Rehearsal
|
|
12
|
+
# The data copy as a throwaway database on the server the world's `connection_env` names.
|
|
13
|
+
#
|
|
14
|
+
# `empty` and `dump` create a database of this run's own (`hecks_rehearsal_<token>`) and
|
|
15
|
+
# restore the dump into it; `readonly_copy` reads a database somebody else made, as it is,
|
|
16
|
+
# in a read-only session, and never creates or drops one. A database is isolated, not a
|
|
17
|
+
# schema, because a dump carries its own schema names. The name derives from the run key, so
|
|
18
|
+
# every ask of one rehearsal reaches the same database and a dropped one is dropped once.
|
|
19
|
+
class PostgresStore
|
|
20
|
+
include PostgresReading
|
|
21
|
+
|
|
22
|
+
PREFIX = "hecks_rehearsal_"
|
|
23
|
+
NAME = /\A#{PREFIX}[0-9a-f]{16}\z/
|
|
24
|
+
|
|
25
|
+
# @param declaration [Declaration] a `postgres` declaration
|
|
26
|
+
# @param run [String] the rehearsal's key
|
|
27
|
+
# @param protected_databases [Array<String>] databases the domain's own world names
|
|
28
|
+
def initialize(declaration, run, protected_databases: [])
|
|
29
|
+
@declaration = declaration
|
|
30
|
+
@run = run
|
|
31
|
+
@protected = protected_databases
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [String] the database this rehearsal reads
|
|
35
|
+
def database_name
|
|
36
|
+
return URI.parse(server_url).path.delete_prefix("/") if @declaration.mode == "readonly_copy"
|
|
37
|
+
|
|
38
|
+
"#{PREFIX}#{Digest::SHA256.hexdigest(@run)[0, 16]}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Creates the database and restores the dump, or checks the copy is reachable. A failure
|
|
42
|
+
# leaves nothing behind.
|
|
43
|
+
#
|
|
44
|
+
# @return [void]
|
|
45
|
+
# @raise [Refused] if the server refuses, or the restore does
|
|
46
|
+
def prepare!
|
|
47
|
+
return reach_copy! if @declaration.mode == "readonly_copy"
|
|
48
|
+
|
|
49
|
+
discard!
|
|
50
|
+
admin { |db| db.exec("CREATE DATABASE #{db.quote_ident(database_name)}") }
|
|
51
|
+
restore! if @declaration.mode == "dump"
|
|
52
|
+
rescue Refused, PG::Error => e
|
|
53
|
+
discard!
|
|
54
|
+
raise e.is_a?(Refused) ? e : Refused.new(e.message.strip)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Drops the throwaway database. A copy somebody else made is never dropped, and one the
|
|
58
|
+
# world asked to keep is refused here, so the run records why it stands.
|
|
59
|
+
#
|
|
60
|
+
# @return [void]
|
|
61
|
+
# @raise [Refused] if the world's `drop_after` is false and a database stands
|
|
62
|
+
def drop!
|
|
63
|
+
return if @declaration.mode == "readonly_copy" || !NAME.match?(database_name)
|
|
64
|
+
raise Refused, "kept #{database_name}: the world's drop_after is false" unless @declaration.drop_after
|
|
65
|
+
|
|
66
|
+
discard!
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [String] a URL for the database this rehearsal reads
|
|
70
|
+
def url
|
|
71
|
+
uri = URI.parse(server_url)
|
|
72
|
+
uri.path = "/#{database_name}"
|
|
73
|
+
uri.to_s
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def discard!
|
|
79
|
+
return unless NAME.match?(database_name)
|
|
80
|
+
|
|
81
|
+
admin { |db| db.exec("DROP DATABASE IF EXISTS #{db.quote_ident(database_name)} WITH (FORCE)") }
|
|
82
|
+
rescue Refused, PG::Error
|
|
83
|
+
nil
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def server_url
|
|
87
|
+
value = @declaration.connection
|
|
88
|
+
return value if value.match?(%r{\Apostgres(ql)?://})
|
|
89
|
+
|
|
90
|
+
raise Refused, "#{@declaration.connection_env} must hold a postgres:// URL"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def admin
|
|
94
|
+
require "pg"
|
|
95
|
+
db = PG.connect(server_url)
|
|
96
|
+
yield db
|
|
97
|
+
ensure
|
|
98
|
+
db&.close
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def reach_copy!
|
|
102
|
+
raise Refused, "readonly_copy names a database the domain's own world persists to" if @protected.include?(url)
|
|
103
|
+
|
|
104
|
+
admin { |db| db.exec("SELECT 1") }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# argv arrays and PG* variables: no shell, and no password on a command line.
|
|
108
|
+
def restore!
|
|
109
|
+
file = @declaration.dump
|
|
110
|
+
raise Refused, "no such dump #{file.inspect}" unless File.file?(file)
|
|
111
|
+
|
|
112
|
+
_out, err, status = Open3.capture3(restore_env, *restore_argv(file))
|
|
113
|
+
raise Refused, "restoring the dump failed: #{err.strip.lines.first}" unless status.success?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def restore_argv(file)
|
|
117
|
+
return ["psql", "--no-psqlrc", "-v", "ON_ERROR_STOP=1", "-d", database_name, "-f", file] if file.end_with?(".sql")
|
|
118
|
+
|
|
119
|
+
["pg_restore", "--no-owner", "--no-privileges", "-d", database_name, file]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def restore_env
|
|
123
|
+
uri = URI.parse(server_url)
|
|
124
|
+
{ "PGHOST" => uri.host, "PGPORT" => uri.port&.to_s, "PGUSER" => uri.user, "PGPASSWORD" => uri.password }.compact
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Adapters
|
|
5
|
+
module Rehearsal
|
|
6
|
+
# The mint audit's refusals as report rows: `Aggregate#id`, the rule that refused, and the
|
|
7
|
+
# audit's own wording, capped, with the total of them all.
|
|
8
|
+
module Rows
|
|
9
|
+
# How many rows a report holds; the total counts the rest.
|
|
10
|
+
CAP = 50
|
|
11
|
+
|
|
12
|
+
ROW = /\A([^#:]+)#([^:]+): (.*)\z/m
|
|
13
|
+
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
# @param violations [Array<String>] the audit's refusals, `Aggregate#id: wording`
|
|
17
|
+
# @return [Array(Array<Hash>, Integer)] the first `CAP` rows, and how many there were
|
|
18
|
+
def from(violations)
|
|
19
|
+
[violations.first(CAP).map { |text| row(text) }, violations.size]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @param text [String] one refusal
|
|
23
|
+
# @return [Hash] `aggregate`, `id`, `rule`, `wording`
|
|
24
|
+
def row(text)
|
|
25
|
+
match = ROW.match(text)
|
|
26
|
+
aggregate, id, wording = match ? match.captures : ["(edge)", "-", text]
|
|
27
|
+
{ aggregate: aggregate, id: id, rule: rule(wording), wording: wording }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @param wording [String] a refusal's words
|
|
31
|
+
# @return [String] `lifecycle`, `reference` or `value`
|
|
32
|
+
def rule(wording)
|
|
33
|
+
return "lifecycle" if wording.include?("lifecycle never reaches")
|
|
34
|
+
return "reference" if wording.include?("reference transform") || wording.include?("id set changed")
|
|
35
|
+
|
|
36
|
+
"value"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "hecks/projector/exporter"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module Adapters
|
|
9
|
+
module Rehearsal
|
|
10
|
+
# The Rust host's mint audit (`reference_validate`), run over the translated rows through the
|
|
11
|
+
# `validate_harness` binary, as the second engine of a rehearsal.
|
|
12
|
+
#
|
|
13
|
+
# The binary is found by `HECKS_VALIDATE_HARNESS`, else under `rust/host/target` of the
|
|
14
|
+
# checkout this gem runs from (`cargo build --bin validate_harness`); a rehearsal never
|
|
15
|
+
# builds it. Without one the engine is reported as not run.
|
|
16
|
+
class RustAudit
|
|
17
|
+
# @!attribute [r] ran
|
|
18
|
+
# @return [Boolean] whether the harness answered
|
|
19
|
+
# @!attribute [r] violations
|
|
20
|
+
# @return [Array<String>] its refusals, `Aggregate#id: wording`
|
|
21
|
+
# @!attribute [r] note
|
|
22
|
+
# @return [String] why it did not run, or what it ran over
|
|
23
|
+
# @!attribute [r] rows
|
|
24
|
+
# @return [Integer] how many rows it was given (zero when it did not run)
|
|
25
|
+
Result = Struct.new(:ran, :violations, :note, :rows, keyword_init: true)
|
|
26
|
+
|
|
27
|
+
# @return [String, nil] the harness binary, when one stands
|
|
28
|
+
def self.binary
|
|
29
|
+
root = File.expand_path("../../../../..", __dir__)
|
|
30
|
+
built = %w[debug release].map { |profile| "#{root}/rust/host/target/#{profile}/validate_harness" }
|
|
31
|
+
[ENV.fetch("HECKS_VALIDATE_HARNESS", nil), *built].compact.find { |path| File.executable?(path) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param subject [Subject] the loaded domain, whose IR the audit is held to
|
|
35
|
+
# @param states [Hash{String => Hash{String => Hash}}] aggregate name, then id, then state
|
|
36
|
+
# @return [Result] what the harness refused, or why it did not run
|
|
37
|
+
def self.call(subject, states)
|
|
38
|
+
path = binary
|
|
39
|
+
return new(nil, subject).stood_down("no validate_harness built: cargo build --bin validate_harness") unless path
|
|
40
|
+
|
|
41
|
+
new(path, subject).call(states)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @param path [String, nil] the harness binary
|
|
45
|
+
# @param subject [Subject] the loaded domain
|
|
46
|
+
def initialize(path, subject)
|
|
47
|
+
@path = path
|
|
48
|
+
@subject = subject
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @param states [Hash] aggregate name, id, state
|
|
52
|
+
# @return [Result] the harness's answer
|
|
53
|
+
def call(states)
|
|
54
|
+
cases = states.flat_map { |name, rows| rows.map { |id, state| case_for(name, id, state) } }
|
|
55
|
+
out, err, status = Open3.capture3(@path, stdin_data: JSON.generate("ir" => ir, "cases" => cases))
|
|
56
|
+
return stood_down("validate_harness failed: #{err.lines.first&.strip}") unless status.success?
|
|
57
|
+
|
|
58
|
+
answered(out, cases.size)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @param note [String] why the engine did not run
|
|
62
|
+
# @return [Result] an answer that refused nothing
|
|
63
|
+
def stood_down(note) = Result.new(ran: false, violations: [], note: note, rows: 0)
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def answered(out, rows)
|
|
68
|
+
Result.new(ran: true, violations: refusals(out), note: "ran over #{rows} rows", rows: rows)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def ir = Projector::Exporter.call(@subject.held.registry).fetch(@subject.bluebook.name)
|
|
72
|
+
|
|
73
|
+
def case_for(name, id, state)
|
|
74
|
+
{ "aggregate" => name, "id" => id, "state" => JSON.parse(JSON.generate(state)) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def refusals(out)
|
|
78
|
+
JSON.parse(out).fetch("results").flat_map { |result| result["violations"] || [result["error"]].compact }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "declaration"
|
|
4
|
+
require_relative "environment"
|
|
5
|
+
require_relative "../journal_store/edge_audit"
|
|
6
|
+
require_relative "../journal_store/held_domain"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module Adapters
|
|
10
|
+
module Rehearsal
|
|
11
|
+
# The domain a rehearsal is run on: loaded as the era tools load it, with nothing bound or
|
|
12
|
+
# booted, so no persistence binding of the domain's own is reached.
|
|
13
|
+
class Subject
|
|
14
|
+
# The domain did not load: the answer a rehearsal gives is `cannot_boot`, with the reason.
|
|
15
|
+
class CannotBoot < StandardError; end
|
|
16
|
+
|
|
17
|
+
attr_reader :held, :declaration, :environment
|
|
18
|
+
|
|
19
|
+
# @param path [String] the domain directory
|
|
20
|
+
# @return [Subject] the loaded subject, with its world's declaration checked
|
|
21
|
+
# @raise [CannotBoot] if the directory is absent, declares no bluebook or does not load
|
|
22
|
+
# @raise [Refused] if the world's `rehearsed_by` is absent or wrong
|
|
23
|
+
def self.open(path)
|
|
24
|
+
environment = choose_environment(path)
|
|
25
|
+
held = load(path, environment)
|
|
26
|
+
new(held, Declaration.from_world(held.registry.world(held.bluebook.name), held.directory), environment)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Whatever stops a domain from loading is a reason it cannot boot, a script error included.
|
|
30
|
+
# A missing bind names the environment that was loaded and the file it came from.
|
|
31
|
+
#
|
|
32
|
+
# @api private
|
|
33
|
+
def self.load(path, environment)
|
|
34
|
+
JournalStore::HeldDomain.open(path, environment: environment.overlay)
|
|
35
|
+
rescue Runtime::WiringError => e
|
|
36
|
+
raise CannotBoot, "#{e.message} The rehearsal loaded #{environment}; overlays this domain " \
|
|
37
|
+
"holds: #{environment.available_words}. " \
|
|
38
|
+
"Name one with `environment \"...\"` in rehearsed_by, or HECKS_PROJECT_ENVIRONMENT."
|
|
39
|
+
rescue StandardError, ScriptError => e
|
|
40
|
+
raise CannotBoot, e.message
|
|
41
|
+
end
|
|
42
|
+
private_class_method :load
|
|
43
|
+
|
|
44
|
+
# The overlay the world names, read before the domain loads so the load can use it.
|
|
45
|
+
#
|
|
46
|
+
# @api private
|
|
47
|
+
def self.choose_environment(path)
|
|
48
|
+
loading = Ports::Loading.bootstrap
|
|
49
|
+
directory = loading.bluebook_directory(path)
|
|
50
|
+
Environment.resolve(declared_environment(loading, directory), directory)
|
|
51
|
+
rescue StandardError, ScriptError
|
|
52
|
+
Environment.resolve(nil, path.to_s)
|
|
53
|
+
end
|
|
54
|
+
private_class_method :choose_environment
|
|
55
|
+
|
|
56
|
+
# @api private
|
|
57
|
+
def self.declared_environment(loading, directory)
|
|
58
|
+
registry = Runtime::Registry.new(root: File.dirname(directory))
|
|
59
|
+
Hecks.with_registry(registry) do
|
|
60
|
+
loading.load_library
|
|
61
|
+
loading.load_each(directory, %w[*.world])
|
|
62
|
+
end
|
|
63
|
+
registry.worlds.values.filter_map { |world| world.for_verb("rehearsed_by")[:environment] }.first
|
|
64
|
+
rescue StandardError, ScriptError
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
private_class_method :declared_environment
|
|
68
|
+
|
|
69
|
+
# @param held [JournalStore::HeldDomain] the loaded domain
|
|
70
|
+
# @param declaration [Declaration] what its world says about the data copy
|
|
71
|
+
# @param environment [Environment] the overlay the domain was loaded under
|
|
72
|
+
def initialize(held, declaration, environment)
|
|
73
|
+
@held = held
|
|
74
|
+
@declaration = declaration
|
|
75
|
+
@environment = environment
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @return [Bluebook::Chapter] the domain's bluebook as it stands
|
|
79
|
+
def bluebook = held.bluebook
|
|
80
|
+
|
|
81
|
+
# @return [String] the label the shape a mint would hold has
|
|
82
|
+
def label = Runtime::StorageShape.mint_label(bluebook)
|
|
83
|
+
|
|
84
|
+
# @return [String] the domain's bluebook directory
|
|
85
|
+
def directory = held.directory
|
|
86
|
+
|
|
87
|
+
# The edge that leads one era's label to the shape's label.
|
|
88
|
+
#
|
|
89
|
+
# @param from [String] the held era's label
|
|
90
|
+
# @return [Bluebook::Translation, nil] the edge; nil when none is declared
|
|
91
|
+
def edge_from(from)
|
|
92
|
+
held.registry.translations.find { |t| t.domain == bluebook.name && t.from == from && t.to == label }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @return [Array<String>] the persisted databases the domain's own world names, which a
|
|
96
|
+
# rehearsal must never be pointed at
|
|
97
|
+
def declared_databases
|
|
98
|
+
blocks = held.registry.world(bluebook.name)&.settings || {}
|
|
99
|
+
blocks.select { |key, _| key.to_s.start_with?("persisted_by") }.values.filter_map { |block| block[:database] }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require_relative "rehearsal/declaration"
|
|
5
|
+
require_relative "rehearsal/rows"
|
|
6
|
+
require_relative "rehearsal/subject"
|
|
7
|
+
require_relative "rehearsal/memory_store"
|
|
8
|
+
require_relative "rehearsal/postgres_store"
|
|
9
|
+
require_relative "rehearsal/inquiry"
|
|
10
|
+
|
|
11
|
+
module Hecks
|
|
12
|
+
module Adapters
|
|
13
|
+
# Helpers the two rehearsal port adapters (`RehearsalStore`, `RehearsalSubject`) share.
|
|
14
|
+
module Rehearsal
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
# The Hecks this process runs, as `release+commit` when a checkout can name its commit.
|
|
18
|
+
#
|
|
19
|
+
# @return [String] `3.9.0`, or `3.9.0+bb8e41d`
|
|
20
|
+
def host_version
|
|
21
|
+
commit = checkout_commit
|
|
22
|
+
commit ? "#{Hecks::VERSION}+#{commit[0, 7]}" : Hecks::VERSION.to_s
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param pin [String, nil] a release or a commit prefix the run must be on
|
|
26
|
+
# @return [void]
|
|
27
|
+
# @raise [Refused] if this process is not on the pin
|
|
28
|
+
def check_pin!(pin)
|
|
29
|
+
return if pin.nil? || pin.empty? || pin == Hecks::VERSION.to_s || checkout_commit.to_s.start_with?(pin)
|
|
30
|
+
|
|
31
|
+
raise Refused, "this is Hecks #{host_version}, not the pin #{pin}: run the rehearsal from the pinned install"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param subject [Subject] the loaded domain
|
|
35
|
+
# @param run [String] the rehearsal's key
|
|
36
|
+
# @return [MemoryStore, PostgresStore] the store its world declares
|
|
37
|
+
def store_for(subject, run)
|
|
38
|
+
declaration = subject.declaration
|
|
39
|
+
return MemoryStore.new(declaration) if declaration.kind == "memory"
|
|
40
|
+
|
|
41
|
+
PostgresStore.new(declaration, run, protected_databases: subject.declared_databases)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [String, nil] the commit of the checkout this gem runs from
|
|
45
|
+
def checkout_commit
|
|
46
|
+
root = File.expand_path("../../../..", __dir__)
|
|
47
|
+
out, status = Open3.capture2("git", "-C", root, "rev-parse", "HEAD", err: File::NULL)
|
|
48
|
+
status.success? ? out.strip : nil
|
|
49
|
+
rescue SystemCallError
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Required by name, not relative to this file: a fuzz or model check boots a copy of this directory,
|
|
2
|
+
# and the implementation must load once, from `lib/`, not from the copy.
|
|
3
|
+
require "hecks/hecks/adapters/rehearsal_store"
|
|
4
|
+
|
|
5
|
+
Hecks.adapter "RehearsalStore" do
|
|
6
|
+
port "RehearsalStore"
|
|
7
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rehearsal"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Adapters
|
|
7
|
+
# The `RehearsalStore` port's adapter: the copy of a domain's data its own world names, which
|
|
8
|
+
# prepares itself when surveyed, runs the mint's audit over itself when examined, and is
|
|
9
|
+
# dropped when it has answered, whichever way it answers.
|
|
10
|
+
#
|
|
11
|
+
# The strategy (`memory` or `postgres`) is the one the subject's `.world` declares with
|
|
12
|
+
# `rehearsed_by`, never an argument. Nothing is written to the subject's own databases: no era
|
|
13
|
+
# is held or named, no table provisioned, no command dispatched. The copy is dropped here, in
|
|
14
|
+
# the same ask that last needs it, so no reaction that fails to arrive can leave one behind.
|
|
15
|
+
class RehearsalStore
|
|
16
|
+
# Accepts the arguments every driven adapter is built with and keeps none of them.
|
|
17
|
+
#
|
|
18
|
+
# @param aggregate [Object, nil] unused
|
|
19
|
+
# @param settings [Hash] unused
|
|
20
|
+
# @param root [String, nil] unused
|
|
21
|
+
def initialize(aggregate: nil, settings: {}, root: nil); end
|
|
22
|
+
|
|
23
|
+
# Prepares the copy, loads the domain against it, and reads where its shape stands.
|
|
24
|
+
#
|
|
25
|
+
# @param held [Hash] the record: `domain`, `run` and `pin`
|
|
26
|
+
# @return [Hash] the shape facts and what the copy holds, or `booted: false` with why the
|
|
27
|
+
# domain did not load
|
|
28
|
+
# @raise [Rehearsal::Refused] if the pin, the world's declaration or the copy is refused
|
|
29
|
+
def survey(**held)
|
|
30
|
+
Rehearsal.check_pin!(plain(held[:pin]))
|
|
31
|
+
subject = Rehearsal::Subject.open(plain(held[:domain]))
|
|
32
|
+
store = Rehearsal.store_for(subject, plain(held[:run]))
|
|
33
|
+
store.prepare!
|
|
34
|
+
guarded(store) { surveyed(subject, store) }
|
|
35
|
+
rescue Rehearsal::Subject::CannotBoot => e
|
|
36
|
+
{ booted: { value: false }, boot_error: { value: e.message } }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Runs the mint's audit over the copy, then drops it.
|
|
40
|
+
#
|
|
41
|
+
# @param held [Hash] the record: `domain` and `run`
|
|
42
|
+
# @return [Hash] the edge facts, every refused row, what was not checked, and whether the
|
|
43
|
+
# copy was dropped
|
|
44
|
+
def examine(**held)
|
|
45
|
+
subject = Rehearsal::Subject.open(plain(held[:domain]))
|
|
46
|
+
store = Rehearsal.store_for(subject, plain(held[:run]))
|
|
47
|
+
facts = guarded(store) { Rehearsal::Inquiry.new(subject, store).examine }
|
|
48
|
+
facts.merge(release(store))
|
|
49
|
+
rescue Rehearsal::Subject::CannotBoot => e
|
|
50
|
+
raise Rehearsal::Refused, e.message
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# A copy that holds nothing is answered at once, so it is dropped here: no examination
|
|
56
|
+
# follows to drop it. Any other copy stands until it is examined.
|
|
57
|
+
def surveyed(subject, store)
|
|
58
|
+
facts = Rehearsal::Inquiry.new(subject, store).survey
|
|
59
|
+
standing = { released: { value: false }, kept: { value: "" } }
|
|
60
|
+
facts.merge(facts[:copy_empty][:value] ? release(store) : standing)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Runs the block; when it raises, the copy is dropped before the error goes on.
|
|
64
|
+
def guarded(store)
|
|
65
|
+
yield
|
|
66
|
+
rescue StandardError
|
|
67
|
+
release(store)
|
|
68
|
+
raise
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def release(store)
|
|
72
|
+
store.drop!
|
|
73
|
+
{ released: { value: true }, kept: { value: "" } }
|
|
74
|
+
rescue Rehearsal::Refused, StandardError => e
|
|
75
|
+
{ released: { value: false }, kept: { value: e.message } }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def plain(argument) = argument.is_a?(Hash) ? argument[:value] : argument
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "net/http"
|
|
5
|
+
require_relative "../../../projections/site/role_probe"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module Adapters
|
|
9
|
+
class SiteToolchain
|
|
10
|
+
# Dispatches each role-declaring command to a host with no role and no actor, and reports
|
|
11
|
+
# whether the host refused the caller.
|
|
12
|
+
#
|
|
13
|
+
# A host that does not enforce roles would run the command, so this talks only to a host on
|
|
14
|
+
# this machine: the one a spec or a CI job started for the purpose.
|
|
15
|
+
class RoleProber
|
|
16
|
+
# The addresses that name this machine.
|
|
17
|
+
LOOPBACK = %w[localhost 127.0.0.1 ::1 [::1]].freeze
|
|
18
|
+
|
|
19
|
+
# Seconds to wait to connect to the host, and again for its answer.
|
|
20
|
+
TIMEOUT = 10
|
|
21
|
+
|
|
22
|
+
# The failures of the network that fail one check rather than the whole run.
|
|
23
|
+
NETWORK = [SystemCallError, Timeout::Error, SocketError, JSON::ParserError].freeze
|
|
24
|
+
|
|
25
|
+
# @param checks [Array<Projections::Site::RoleProbe::Check>] the commands to dispatch
|
|
26
|
+
# @param base [String] the host's scheme, host and port
|
|
27
|
+
# @raise [ConsoleCapture::Failure] when the host is not on this machine
|
|
28
|
+
def initialize(checks, base:)
|
|
29
|
+
@checks = checks
|
|
30
|
+
@uri = URI.parse(base)
|
|
31
|
+
return if LOOPBACK.include?(@uri.host)
|
|
32
|
+
|
|
33
|
+
raise ConsoleCapture::Failure, "roles are checked against a host on this machine, not #{@uri.host}: " \
|
|
34
|
+
"a host that does not enforce roles would run each command"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [Array<(String, Integer)>] the report, one line per command ending in the count,
|
|
38
|
+
# and how many failed; a command whose synthesized arguments the host refused is counted
|
|
39
|
+
# as unchecked, not failed
|
|
40
|
+
def call
|
|
41
|
+
lines = @checks.map { |check| judged(check) }
|
|
42
|
+
failed = lines.count { |_, state| state == :failed }
|
|
43
|
+
unchecked = lines.count { |_, state| state == :unchecked }
|
|
44
|
+
summary = "#{lines.size} commands, #{failed} failed, #{unchecked} unchecked"
|
|
45
|
+
[lines.map(&:first).append(summary).join("\n"), failed]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def judged(check)
|
|
51
|
+
answer = answer_to(check)
|
|
52
|
+
return [line(check, "unchecked: the host refused the arguments before the role"), :unchecked] if unchecked?(answer)
|
|
53
|
+
|
|
54
|
+
why = Projections::Site::RoleProbe.verdict(check, answer)
|
|
55
|
+
[line(check, why ? "FAILED: #{why}" : "ok"), why ? :failed : :ok]
|
|
56
|
+
rescue *NETWORK => e
|
|
57
|
+
[line(check, "FAILED: #{e.class}: #{e.message}"), :failed]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def unchecked?(answer) = Projections::Site::RoleProbe.unchecked?(answer)
|
|
61
|
+
|
|
62
|
+
def line(check, outcome) = " #{check.verb} (#{check.role}) is refused with no role... #{outcome}"
|
|
63
|
+
|
|
64
|
+
def answer_to(check)
|
|
65
|
+
request = Net::HTTP::Post.new(URI.join(@uri.to_s, "/dispatch"), "Content-Type" => "application/json")
|
|
66
|
+
request.body = JSON.generate(verb: check.verb, with: check.arguments)
|
|
67
|
+
JSON.parse(connection.request(request).body)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def connection
|
|
71
|
+
Net::HTTP.new(@uri.host, @uri.port).tap do |http|
|
|
72
|
+
http.open_timeout = http.read_timeout = TIMEOUT
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -9,6 +9,7 @@ require "hecks/tools"
|
|
|
9
9
|
require "hecks/tools/site_routes"
|
|
10
10
|
require "hecks/projections/site/live_edge"
|
|
11
11
|
require_relative "site_toolchain/prober"
|
|
12
|
+
require_relative "site_toolchain/role_prober"
|
|
12
13
|
|
|
13
14
|
module Hecks
|
|
14
15
|
module Adapters
|
|
@@ -23,7 +24,8 @@ module Hecks
|
|
|
23
24
|
SCRIPTS = { project: "project_site" }.freeze
|
|
24
25
|
|
|
25
26
|
# The tool's flag for each `SiteProjection` field that takes a value.
|
|
26
|
-
FLAGS = { "out" => :out, "template" => :template, "cms" => :cms, "root" => :root, "
|
|
27
|
+
FLAGS = { "out" => :out, "template" => :template, "cms" => :cms, "root" => :root, "editor" => :editor,
|
|
28
|
+
"extension" => :extension }.freeze
|
|
27
29
|
|
|
28
30
|
# Accepts the arguments every driven adapter is built with and keeps none of them.
|
|
29
31
|
#
|
|
@@ -35,7 +37,7 @@ module Hecks
|
|
|
35
37
|
# Writes a project's `routes.ts` from its declared route table, or with `check` only compares.
|
|
36
38
|
#
|
|
37
39
|
# @param held [Hash] the `SiteProjection` record: `domain`, and `out`, `template`, `cms`,
|
|
38
|
-
# `root`, `extension` and `check` when set
|
|
40
|
+
# `root`, `editor`, `extension` and `check` when set
|
|
39
41
|
# @return [Hash{Symbol => Hash}] `output:` one line per file written or found current
|
|
40
42
|
# @raise [ConsoleCapture::Failure] when the route table is refused (an unknown cache class,
|
|
41
43
|
# a duplicate path), a path or the extension is refused, or under `check` a file is out
|
|
@@ -84,6 +86,23 @@ module Hecks
|
|
|
84
86
|
{ output: { value: report } }
|
|
85
87
|
end
|
|
86
88
|
|
|
89
|
+
# Dispatches every command a project declares a role for to a host with no role and no actor,
|
|
90
|
+
# and checks the host refuses each as `Unauthorized`. The host must be on this machine and
|
|
91
|
+
# enforcing roles (`HECKS_ROLE_ENFORCEMENT=enforce`); one that is not would run the commands.
|
|
92
|
+
#
|
|
93
|
+
# @param held [Hash] the `SiteProjection` record: `domain`, the project that declares the
|
|
94
|
+
# commands, and `url`, the host's base address
|
|
95
|
+
# @return [Hash{Symbol => Hash}] `output:` one line per command, ending in the count
|
|
96
|
+
# @raise [ConsoleCapture::Failure] when the host is not on this machine, or a command is not
|
|
97
|
+
# refused for its role; the message is the report
|
|
98
|
+
def roles(**held)
|
|
99
|
+
checks = Projections::Site::RoleProbe.checks(registry_of(located(:domain, held[:domain])))
|
|
100
|
+
report, failed = RoleProber.new(checks, base: base_url(plain(held[:url]))).call
|
|
101
|
+
raise ConsoleCapture::Failure, report if failed.positive?
|
|
102
|
+
|
|
103
|
+
{ output: { value: report } }
|
|
104
|
+
end
|
|
105
|
+
|
|
87
106
|
private
|
|
88
107
|
|
|
89
108
|
# The scheme, host and port of an http(s) address.
|
|
@@ -121,12 +140,18 @@ module Hecks
|
|
|
121
140
|
# The project's checked route table.
|
|
122
141
|
def table_of(root) = read_table(root).last
|
|
123
142
|
|
|
124
|
-
# The registry
|
|
125
|
-
def
|
|
143
|
+
# The registry the project at `root` boots into.
|
|
144
|
+
def registry_of(root)
|
|
126
145
|
registry = nil
|
|
127
146
|
outcome = ConsoleCapture.capture { registry = Tools::SiteRoutes.registry_for(root) }
|
|
128
147
|
raise ConsoleCapture::Failure, outcome.output.strip unless outcome.ok?
|
|
129
148
|
|
|
149
|
+
registry
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# The registry, chapter and checked table of the project at `root`.
|
|
153
|
+
def read_table(root)
|
|
154
|
+
registry = registry_of(root)
|
|
130
155
|
chapter = Projections::Site::Table.chapter(registry)
|
|
131
156
|
[registry, chapter, Projections::Site::Table.read(chapter, registry: registry)]
|
|
132
157
|
rescue Projections::Site::Table::Invalid => e
|