hecks 2.9.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/hecks +51 -6
- data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
- data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +1 -1
- data/lib/hecks/adapters/driven/memory.rb +16 -3
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +6 -2
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
- data/lib/hecks/adapters/driven/postgres.rb +4 -2
- data/lib/hecks/adapters/driven/prism.rb +41 -8
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +73 -16
- data/lib/hecks/behaviors/expectations.rb +1 -1
- data/lib/hecks/behaviors.rb +1 -1
- data/lib/hecks/bench/cli.rb +62 -0
- data/lib/hecks/bench/environment.rb +71 -0
- data/lib/hecks/bench/postgres_probe.rb +30 -0
- data/lib/hecks/bench/report.rb +76 -0
- data/lib/hecks/bench/ruby_runner.rb +93 -0
- data/lib/hecks/bench/run.rb +55 -0
- data/lib/hecks/bench/rust_runner.rb +101 -0
- data/lib/hecks/bench/stats.rb +46 -0
- data/lib/hecks/bench/suite.rb +98 -0
- data/lib/hecks/bench/workload.rb +89 -0
- data/lib/hecks/bench.rb +17 -0
- data/lib/hecks/bluebook/assembly/contracts.rb +2 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/aggregate.rb +30 -1
- data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -0
- data/lib/hecks/bluebook/chapter.rb +6 -2
- data/lib/hecks/bluebook/domain_port.rb +36 -5
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +10 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +8 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -1
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +31 -3
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +122 -3
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +1 -1
- data/lib/hecks/bluebook/dsl/query_builder.rb +27 -1
- data/lib/hecks/bluebook/dsl/rule_reference.rb +1 -1
- data/lib/hecks/bluebook/dsl/world_builder.rb +29 -2
- data/lib/hecks/bluebook/expression/canonical_form.rb +1 -1
- data/lib/hecks/bluebook/expression/resolver.rb +3 -2
- data/lib/hecks/bluebook/hexagon.rb +13 -5
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
- data/lib/hecks/bluebook/meta_validator.rb +9 -4
- data/lib/hecks/bluebook/model_check.rb +23 -0
- data/lib/hecks/bluebook/pattern_subset.rb +38 -0
- data/lib/hecks/bluebook/query.rb +20 -5
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/canonical_json.rb +30 -0
- data/lib/hecks/chapters.rb +102 -0
- data/lib/hecks/cli/behaviors.rb +61 -0
- data/lib/hecks/cli/canonicalise.rb +25 -0
- data/lib/hecks/cli/check_engine_agreement.rb +32 -0
- data/lib/hecks/cli/check_era.rb +88 -0
- data/lib/hecks/cli/console.rb +66 -0
- data/lib/hecks/cli/deploy_template_diff.rb +40 -0
- data/lib/hecks/cli/doc_coverage.rb +32 -0
- data/lib/hecks/cli/document.rb +2 -2
- data/lib/hecks/cli/expression_projection.rb +41 -0
- data/lib/hecks/cli/follow.rb +88 -0
- data/lib/hecks/cli/history.rb +44 -0
- data/lib/hecks/cli/ir.rb +1 -1
- data/lib/hecks/cli/mcp.rb +3 -3
- data/lib/hecks/cli/model_check.rb +7 -4
- data/lib/hecks/cli/pattern_cases.rb +47 -0
- data/lib/hecks/cli/present.rb +39 -0
- data/lib/hecks/cli/project_cli.rb +224 -32
- data/lib/hecks/cli/project_diagrams.rb +4 -3
- data/lib/hecks/cli/project_glossary.rb +44 -0
- data/lib/hecks/cli/reference.rb +46 -0
- data/lib/hecks/cli/refresh_projections.rb +44 -0
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
- data/lib/hecks/cli/release.rb +70 -0
- data/lib/hecks/cli/release_gem.rb +59 -0
- data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
- data/lib/hecks/cli/rspec_shard_files.rb +66 -0
- data/lib/hecks/cli/run.rb +3 -3
- data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
- data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
- data/lib/hecks/cli/shape.rb +70 -0
- data/lib/hecks/cli/smoke_http.rb +230 -0
- data/lib/hecks/cli/smoke_test.rb +5 -3
- data/lib/hecks/cli/spec_example.rb +27 -0
- data/lib/hecks/cli/statements.rb +32 -0
- data/lib/hecks/cli/stores.rb +4 -5
- data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
- data/lib/hecks/cli.rb +201 -52
- data/lib/hecks/codemod.rb +324 -0
- data/lib/hecks/corpus.rb +423 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +591 -23
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -4
- data/lib/hecks/deploy/oidc.json +82 -2
- data/lib/hecks/doc/reference.rb +351 -0
- data/lib/hecks/doors/cli_door.rb +196 -0
- data/lib/hecks/doors/cli_runner.rb +297 -0
- data/lib/hecks/{facade → doors}/command_request.rb +1 -1
- data/lib/hecks/{facade → doors}/handle.rb +2 -2
- data/lib/hecks/{facade → doors}/json_door.rb +4 -4
- data/lib/hecks/doors/launcher_options.rb +129 -0
- data/lib/hecks/{cli → doors}/mcp_door.rb +7 -7
- data/lib/hecks/doors/mcp_door_scope.rb +97 -0
- data/lib/hecks/{facade/surface → doors/ruby_door}/aggregate_door.rb +8 -3
- data/lib/hecks/{facade/surface → doors/ruby_door}/chapter.rb +3 -3
- data/lib/hecks/{facade/surface.rb → doors/ruby_door.rb} +36 -5
- data/lib/hecks/doors.rb +61 -0
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +1 -1
- data/lib/hecks/engine_agreement.rb +156 -0
- data/lib/hecks/facade.rb +24 -37
- data/lib/hecks/forms/app.rb +6 -6
- data/lib/hecks/forms/banking_presentation.rb +50 -0
- data/lib/hecks/forms/field_shape.rb +2 -1
- data/lib/hecks/forms/port_argument.rb +1 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
- data/lib/hecks/fuzzing/combination_miner.rb +86 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
- data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
- data/lib/hecks/fuzzing/differential.rb +156 -0
- data/lib/hecks/fuzzing/domain_generator.rb +615 -0
- data/lib/hecks/fuzzing/era_boundary.rb +71 -0
- data/lib/hecks/fuzzing/form_census.rb +136 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
- data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
- data/lib/hecks/fuzzing/outside_world.rb +45 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
- data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
- data/lib/hecks/fuzzing/properties/guards.rb +190 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
- data/lib/hecks/fuzzing/properties/querying.rb +175 -0
- data/lib/hecks/fuzzing/properties.rb +162 -0
- data/lib/hecks/fuzzing/qa_settings.rb +123 -0
- data/lib/hecks/fuzzing/replay.rb +505 -0
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
- data/lib/hecks/fuzzing/self_consistency.rb +339 -0
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
- data/lib/hecks/fuzzing/shrinker.rb +166 -0
- data/lib/hecks/fuzzing/structural_skips.rb +33 -0
- data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
- data/lib/hecks/fuzzing/value_generator.rb +216 -0
- data/lib/hecks/fuzzing.rb +23 -0
- data/lib/hecks/grammar/evolve.rb +336 -0
- data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
- data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
- data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
- data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
- data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
- data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
- data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
- data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
- data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
- data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
- data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
- data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
- data/lib/hecks/hecks/adapters/git.adapter +7 -0
- data/lib/hecks/hecks/adapters/git.rb +102 -0
- data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
- data/lib/hecks/hecks/adapters/host_http.rb +65 -0
- data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
- data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
- data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
- data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
- data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
- data/lib/hecks/hecks/adapters/local_files.rb +46 -0
- data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
- data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
- data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
- data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
- data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
- data/lib/hecks/hecks/adapters/shell.adapter +7 -0
- data/lib/hecks/hecks/adapters/shell.rb +49 -0
- data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
- data/lib/hecks/hecks/adapters/terminal.rb +74 -0
- data/lib/hecks/hecks/codebase.behaviors +172 -0
- data/lib/hecks/hecks/codebase.bluebook +2453 -0
- data/lib/hecks/hecks/context_map.hecksagon +13 -0
- data/lib/hecks/hecks/custodian.behaviors +179 -0
- data/lib/hecks/hecks/custodian.bluebook +1847 -0
- data/lib/hecks/hecks/deploy.behaviors +74 -0
- data/lib/hecks/hecks/deploy.world +12 -0
- data/lib/hecks/hecks/environments/memory.world +10 -0
- data/lib/hecks/hecks/governance.world +7 -0
- data/lib/hecks/hecks/hecks.behaviors +62 -0
- data/lib/hecks/hecks/hecks.bluebook +124 -0
- data/lib/hecks/hecks/hecks.hecksagon +390 -0
- data/lib/hecks/hecks/hecks.world +42 -0
- data/lib/hecks/kernel_capabilities.rb +194 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +11 -0
- data/lib/hecks/language/bluebook/query.bluebook +7 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +4 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +10 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +20 -0
- data/lib/hecks/mcp_stdio_guard.rb +2 -2
- data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
- data/lib/hecks/persistence_legacy_fixture.rb +113 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +8 -5
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +4 -1
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +15 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +32 -15
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +6 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +43 -24
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
- data/lib/hecks/ports/persistence/state_codec.rb +24 -5
- data/lib/hecks/projection_files.rb +285 -0
- data/lib/hecks/projections/bootstrap_table.rb +1 -1
- data/lib/hecks/projections/deploy/fargate.rb +5 -5
- data/lib/hecks/projections/deploy/lambda.rb +43 -32
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/preview/template.rb +1 -1
- data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
- data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/shared.rb +1 -1
- data/lib/hecks/projections/diagrams.rb +3 -3
- data/lib/hecks/projections/model/deviations.rb +1 -1
- data/lib/hecks/projections/parser_table.rb +4 -4
- data/lib/hecks/projections/reference.rb +2 -3
- data/lib/hecks/projections/rust_vocabulary.rb +1 -1
- data/lib/hecks/projector/cli_projector.rb +106 -37
- data/lib/hecks/projector.rb +1 -1
- data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
- data/lib/hecks/quality_control/adapters/agent.rb +58 -0
- data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
- data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
- data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
- data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
- data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
- data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
- data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
- data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
- data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
- data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
- data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
- data/lib/hecks/quality_control/cli/child.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
- data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
- data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
- data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
- data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
- data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
- data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
- data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
- data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
- data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
- data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
- data/lib/hecks/query_ir.rb +346 -0
- data/lib/hecks/query_ir_mcp.rb +198 -0
- data/lib/hecks/release/runner/ci_publisher.rb +2 -2
- data/lib/hecks/release/runner/clean_tree.rb +42 -0
- data/lib/hecks/release/runner/console.rb +6 -0
- data/lib/hecks/release/runner/gem_publisher.rb +14 -7
- data/lib/hecks/release/runner/npm_publisher.rb +6 -36
- data/lib/hecks/release/runner/preflight.rb +26 -2
- data/lib/hecks/release/runner/published.rb +8 -26
- data/lib/hecks/release/runner/tagger.rb +30 -15
- data/lib/hecks/release/runner.rb +57 -6
- data/lib/hecks/router/namespace_installer.rb +3 -3
- data/lib/hecks/runtime/adapter_lookup.rb +124 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +4 -0
- data/lib/hecks/runtime/dependency_planning.rb +20 -2
- data/lib/hecks/runtime/dispatcher.rb +53 -3
- data/lib/hecks/runtime/interpreting.rb +3 -0
- data/lib/hecks/runtime/loader.rb +65 -12
- data/lib/hecks/runtime/outbox.rb +1 -1
- data/lib/hecks/runtime/policy_interpreter.rb +5 -2
- data/lib/hecks/runtime/port_operation_interpreter.rb +2 -10
- data/lib/hecks/runtime/query_interpreter.rb +77 -0
- data/lib/hecks/runtime/reaction_outcome.rb +89 -0
- data/lib/hecks/runtime/refusal_wording.rb +23 -1
- data/lib/hecks/runtime/registry/verification.rb +163 -4
- data/lib/hecks/runtime/registry.rb +57 -3
- data/lib/hecks/runtime/remote_dispatcher.rb +31 -2
- data/lib/hecks/runtime/value/coercion.rb +23 -135
- data/lib/hecks/runtime/value/field_checks.rb +172 -0
- data/lib/hecks/runtime/value.rb +4 -1
- data/lib/hecks/runtime.rb +20 -6
- data/lib/hecks/rust_build/conformance.rb +158 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
- data/lib/hecks/rust_build/coverage.rb +218 -0
- data/lib/hecks/rust_build/native_build.rb +124 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
- data/lib/hecks/rust_build/project_rust.rb +332 -0
- data/lib/hecks/rust_build/wasm.rb +105 -0
- data/lib/hecks/rust_build/wasm_browser.rb +113 -0
- data/lib/hecks/rust_build.rb +144 -0
- data/lib/hecks/storehouse.rb +16 -16
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +2 -2
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +1 -1
- data/lib/hecks/three_zero/forms.yml +127 -0
- data/lib/hecks/three_zero.rb +12 -0
- data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
- data/lib/hecks/tools/comment_style.rb +1070 -0
- data/lib/hecks/tools/corpus_report.rb +87 -0
- data/lib/hecks/tools/deploy_recipe.rb +255 -0
- data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
- data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
- data/lib/hecks/tools/era_projection_backfill.rb +94 -0
- data/lib/hecks/tools/era_reattest.rb +146 -0
- data/lib/hecks/tools/evolve_run.rb +189 -0
- data/lib/hecks/tools/fuzz_sweep.rb +373 -0
- data/lib/hecks/tools/heki_compaction.rb +100 -0
- data/lib/hecks/tools/hoist_local_givens.rb +246 -0
- data/lib/hecks/tools/journal_compaction.rb +89 -0
- data/lib/hecks/tools/oidc_manifests.rb +70 -0
- data/lib/hecks/tools/query_ir_run.rb +81 -0
- data/lib/hecks/tools/regeneration_run.rb +166 -0
- data/lib/hecks/tools/rust_comment_style.rb +642 -0
- data/lib/hecks/tools/sequence_script.rb +62 -0
- data/lib/hecks/tools/tail_merge.rb +117 -0
- data/lib/hecks/tools/tenant_provisioning.rb +142 -0
- data/lib/hecks/tools/translation_audit.rb +208 -0
- data/lib/hecks/tools/translation_scaffold.rb +131 -0
- data/lib/hecks/tools.rb +86 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks.rb +32 -15
- data/qa/settings.yml +66 -0
- data/rust/.cargo/config.toml +6 -0
- data/rust/.gitignore +2 -0
- data/rust/Cargo.lock +54 -0
- data/rust/Cargo.toml +61 -0
- data/rust/build/.cargo/config.toml +3 -0
- data/rust/build/.gitignore +1 -0
- data/rust/build/Cargo.lock +7 -0
- data/rust/build/Cargo.toml +19 -0
- data/rust/build/src/build_artifact.rs +47 -0
- data/rust/build/src/cargo_sync.rs +248 -0
- data/rust/build/src/json.rs +421 -0
- data/rust/build/src/lineage_pass.rs +285 -0
- data/rust/build/src/main.rs +51 -0
- data/rust/build/src/optional_pass.rs +150 -0
- data/rust/build/src/pipeline.rs +264 -0
- data/rust/build/src/reserved_names.rs +76 -0
- data/rust/build/src/resolve.rs +124 -0
- data/rust/build/src/root.rs +25 -0
- data/rust/build/src/sidecars.rs +73 -0
- data/rust/build/src/subprocess.rs +51 -0
- data/rust/build/src/tmp.rs +31 -0
- data/rust/codegen/.cargo/config.toml +3 -0
- data/rust/codegen/.gitignore +1 -0
- data/rust/codegen/Cargo.lock +7 -0
- data/rust/codegen/Cargo.toml +20 -0
- data/rust/codegen/src/attr.rs +32 -0
- data/rust/codegen/src/bridging.rs +481 -0
- data/rust/codegen/src/commands.rs +1226 -0
- data/rust/codegen/src/constraints.rs +109 -0
- data/rust/codegen/src/dependency_planning.rs +209 -0
- data/rust/codegen/src/domain_generator.rs +1288 -0
- data/rust/codegen/src/exemplar.rs +230 -0
- data/rust/codegen/src/expr_emitter.rs +87 -0
- data/rust/codegen/src/fielded.rs +192 -0
- data/rust/codegen/src/hecks_naming.rs +33 -0
- data/rust/codegen/src/json.rs +335 -0
- data/rust/codegen/src/json_codec.rs +697 -0
- data/rust/codegen/src/literal.rs +207 -0
- data/rust/codegen/src/main.rs +290 -0
- data/rust/codegen/src/manifest.rs +227 -0
- data/rust/codegen/src/mutations.rs +784 -0
- data/rust/codegen/src/naming.rs +286 -0
- data/rust/codegen/src/ports.rs +208 -0
- data/rust/codegen/src/prelude.rs +116 -0
- data/rust/codegen/src/queries.rs +786 -0
- data/rust/codegen/src/reactions.rs +532 -0
- data/rust/codegen/src/read_models.rs +697 -0
- data/rust/codegen/src/reference_specs.rs +40 -0
- data/rust/codegen/src/registry.rs +966 -0
- data/rust/codegen/src/reserved_names.rs +76 -0
- data/rust/codegen/src/shared.rs +93 -0
- data/rust/codegen/src/skip_reason.rs +25 -0
- data/rust/codegen/src/types.rs +362 -0
- data/rust/host/.cargo/config.toml +3 -0
- data/rust/host/.gitignore +1 -0
- data/rust/host/Cargo.lock +4297 -0
- data/rust/host/Cargo.toml +61 -0
- data/rust/host/HECKS_RELEASE +1 -0
- data/rust/host/rds-ca-bundle.pem +2736 -0
- data/rust/host/src/api.rs +1596 -0
- data/rust/host/src/approval.rs +652 -0
- data/rust/host/src/auth.rs +1287 -0
- data/rust/host/src/bin/expr_harness.rs +46 -0
- data/rust/host/src/bin/lineage_harness.rs +119 -0
- data/rust/host/src/bin/mint_harness.rs +135 -0
- data/rust/host/src/checkout.rs +265 -0
- data/rust/host/src/dispatch.rs +1443 -0
- data/rust/host/src/expr_json.rs +1117 -0
- data/rust/host/src/field_hints.rs +52 -0
- data/rust/host/src/ir.rs +733 -0
- data/rust/host/src/journal.rs +874 -0
- data/rust/host/src/lambda_client.rs +378 -0
- data/rust/host/src/log.rs +147 -0
- data/rust/host/src/main.rs +474 -0
- data/rust/host/src/mint.rs +1865 -0
- data/rust/host/src/payments/keystore.rs +330 -0
- data/rust/host/src/payments/tests.rs +1400 -0
- data/rust/host/src/payments.rs +767 -0
- data/rust/host/src/presentation.rs +698 -0
- data/rust/host/src/presentation_write.rs +1431 -0
- data/rust/host/src/query_step.rs +149 -0
- data/rust/host/src/rate_limit.rs +904 -0
- data/rust/host/src/reference_transform.rs +241 -0
- data/rust/host/src/reference_validate.rs +338 -0
- data/rust/host/src/resend.rs +296 -0
- data/rust/host/src/secrets.rs +106 -0
- data/rust/host/src/server.rs +663 -0
- data/rust/host/src/storage_shape.rs +175 -0
- data/rust/host/src/test_pg.rs +40 -0
- data/rust/host/src/ui_schema.rs +1351 -0
- data/rust/host/src/wasm_runner.rs +191 -0
- data/rust/host/src/web/newsletter/cooldown.rs +126 -0
- data/rust/host/src/web/newsletter/tests.rs +104 -0
- data/rust/host/src/web/newsletter.rs +310 -0
- data/rust/host/src/web/newsletter_send.rs +357 -0
- data/rust/host/src/web/registrations.rs +1505 -0
- data/rust/host/src/web.rs +2937 -0
- data/rust/lsp/.gitignore +1 -0
- data/rust/lsp/Cargo.lock +7 -0
- data/rust/lsp/Cargo.toml +16 -0
- data/rust/lsp/README.md +133 -0
- data/rust/lsp/src/diagnostics.rs +165 -0
- data/rust/lsp/src/json.rs +385 -0
- data/rust/lsp/src/main.rs +399 -0
- data/rust/lsp/src/outline.rs +177 -0
- data/rust/lsp/src/rpc.rs +67 -0
- data/rust/parser/.cargo/config.toml +5 -0
- data/rust/parser/.gitignore +1 -0
- data/rust/parser/Cargo.lock +7 -0
- data/rust/parser/Cargo.toml +21 -0
- data/rust/parser/src/build/closed_sets.rs +27 -0
- data/rust/parser/src/build/identity.rs +276 -0
- data/rust/parser/src/build/mod.rs +12 -0
- data/rust/parser/src/build/naming.rs +141 -0
- data/rust/parser/src/build/pattern_subset.rs +161 -0
- data/rust/parser/src/build/query_derive.rs +72 -0
- data/rust/parser/src/build/query_inference.rs +269 -0
- data/rust/parser/src/build/query_options.rs +100 -0
- data/rust/parser/src/build/read_model.rs +94 -0
- data/rust/parser/src/build/references.rs +32 -0
- data/rust/parser/src/canonical.rs +114 -0
- data/rust/parser/src/diag.rs +55 -0
- data/rust/parser/src/emit.rs +953 -0
- data/rust/parser/src/expr/ast_json.rs +147 -0
- data/rust/parser/src/expr/evaluator.rs +81 -0
- data/rust/parser/src/expr/mod.rs +84 -0
- data/rust/parser/src/expr/resolver.rs +497 -0
- data/rust/parser/src/ir.rs +387 -0
- data/rust/parser/src/keywords.rs +585 -0
- data/rust/parser/src/lex.rs +662 -0
- data/rust/parser/src/main.rs +252 -0
- data/rust/parser/src/parse/aggregate.rs +612 -0
- data/rust/parser/src/parse/chapter.rs +517 -0
- data/rust/parser/src/parse/command.rs +637 -0
- data/rust/parser/src/parse/domain_port.rs +127 -0
- data/rust/parser/src/parse/entity.rs +527 -0
- data/rust/parser/src/parse/file.rs +67 -0
- data/rust/parser/src/parse/hecksagon.rs +231 -0
- data/rust/parser/src/parse/lifecycle.rs +162 -0
- data/rust/parser/src/parse/mod.rs +1262 -0
- data/rust/parser/src/parse/policy.rs +87 -0
- data/rust/parser/src/parse/process_manager.rs +305 -0
- data/rust/parser/src/parse/query.rs +100 -0
- data/rust/parser/src/parse/read_model.rs +214 -0
- data/rust/parser/src/parse/value_object.rs +250 -0
- data/rust/parser/src/ruby_value.rs +422 -0
- data/rust/project/bridging.rb +257 -0
- data/rust/project/commands.rb +877 -0
- data/rust/project/constraints.rb +86 -0
- data/rust/project/dependency_planning.rb +178 -0
- data/rust/project/domain_generator.rb +980 -0
- data/rust/project/exemplar.rb +148 -0
- data/rust/project/expr_emitter.rb +72 -0
- data/rust/project/fielded.rb +141 -0
- data/rust/project/json_codec.rb +641 -0
- data/rust/project/mutations.rb +721 -0
- data/rust/project/naming.rb +205 -0
- data/rust/project/ports.rb +65 -0
- data/rust/project/queries.rb +493 -0
- data/rust/project/reactions.rb +317 -0
- data/rust/project/read_models.rb +690 -0
- data/rust/project/reference_specs.rb +27 -0
- data/rust/project/registry.rb +396 -0
- data/rust/project/skip_reason.rb +29 -0
- data/rust/project/types.rb +225 -0
- data/rust/project/write_if_changed.rb +83 -0
- data/rust/project.rb +30 -0
- data/rust/project_rust_pipeline.rb +383 -0
- data/rust/src/exemplar/commands.rs +279 -0
- data/rust/src/exemplar/constraints.rs +18 -0
- data/rust/src/exemplar/fielded.rs +280 -0
- data/rust/src/exemplar/json.rs +277 -0
- data/rust/src/exemplar/mod.rs +17 -0
- data/rust/src/exemplar/mutations.rs +80 -0
- data/rust/src/exemplar/queries.rs +33 -0
- data/rust/src/exemplar/reactions.rs +88 -0
- data/rust/src/exemplar/read_models.rs +48 -0
- data/rust/src/exemplar/registry.rs +166 -0
- data/rust/src/exemplar/types.rs +44 -0
- data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
- data/rust/src/kernel/attribute_shapes/list.rs +18 -0
- data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
- data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
- data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
- data/rust/src/kernel/cli.rs +585 -0
- data/rust/src/kernel/dispatch.rs +1090 -0
- data/rust/src/kernel/expr.rs +458 -0
- data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
- data/rust/src/kernel/expression_operators/comparison.rs +48 -0
- data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
- data/rust/src/kernel/expression_operators/logical.rs +13 -0
- data/rust/src/kernel/expression_operators/membership.rs +18 -0
- data/rust/src/kernel/expression_operators/mod.rs +68 -0
- data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
- data/rust/src/kernel/expression_operators/positional.rs +126 -0
- data/rust/src/kernel/expression_operators/presence.rs +101 -0
- data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
- data/rust/src/kernel/expression_operators/sized.rs +35 -0
- data/rust/src/kernel/expression_operators/text.rs +79 -0
- data/rust/src/kernel/expression_operators/to_string.rs +18 -0
- data/rust/src/kernel/json.rs +779 -0
- data/rust/src/kernel/mod.rs +133 -0
- data/rust/src/kernel/named_query.rs +219 -0
- data/rust/src/kernel/orchestrate.rs +1810 -0
- data/rust/src/kernel/pattern.rs +307 -0
- data/rust/src/kernel/query_comparators.rs +206 -0
- data/rust/src/kernel/query_ordering.rs +103 -0
- data/rust/src/kernel/read_model.rs +914 -0
- data/rust/src/kernel/reference_lookup.rs +149 -0
- data/rust/src/kernel/repository.rs +468 -0
- data/rust/src/kernel/routing.rs +612 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
- data/rust/src/kernel/vocab/field_hint.rs +70 -0
- data/rust/src/kernel/vocab/mod.rs +20 -0
- data/rust/src/kernel/vocab/query_comparator.rs +65 -0
- data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
- data/rust/src/lib.rs +8 -0
- data/rust/src/main.rs +16 -0
- data/rust/web/.gitignore +1 -0
- data/rust/web/Cargo.lock +119 -0
- data/rust/web/Cargo.toml +18 -0
- data/rust/web/src/lib.rs +8 -0
- metadata +477 -12
- data/lib/hecks/facade/cli_door.rb +0 -96
- data/lib/hecks/facade/cli_runner.rb +0 -127
- data/lib/hecks/mcp_door_scope.rb +0 -95
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../tools"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Tools
|
|
7
|
+
# Hoists `given`s that two or more commands under one owner repeat verbatim into a single
|
|
8
|
+
# owner-level `given`, leaving bare references behind. Single-owner groups in example domains
|
|
9
|
+
# only.
|
|
10
|
+
#
|
|
11
|
+
# It is not a `Hecks::Codemod::Runner`: hoisting adds an owner-level precondition, so the
|
|
12
|
+
# exported IR cannot stay byte-identical. The invariant checked instead is each command's
|
|
13
|
+
# effective rules.
|
|
14
|
+
#
|
|
15
|
+
# The `given` is inserted right after the owner's opening line: `command` calls are deferred
|
|
16
|
+
# to `drain_pending!`, so the position of `given` inside the owner does not matter (ADR 0028).
|
|
17
|
+
#
|
|
18
|
+
# hecks hoist_local_givens [--dry-run]
|
|
19
|
+
module HoistLocalGivens
|
|
20
|
+
# One rule several commands of an owner repeat.
|
|
21
|
+
Candidate = Struct.new(:description, :canonical, :owner, :locations, keyword_init: true)
|
|
22
|
+
|
|
23
|
+
module_function
|
|
24
|
+
|
|
25
|
+
# Rewrites every example domain that has a candidate, and prints one line for each.
|
|
26
|
+
#
|
|
27
|
+
# @param argv [Array<String>] `--dry-run` to restore every file after the rewrite is checked
|
|
28
|
+
# @param root [String] the checkout (unused: the examples come from `Codemod`)
|
|
29
|
+
# @return [Integer] 0
|
|
30
|
+
def main(argv, root: Tools::ROOT)
|
|
31
|
+
_ = root
|
|
32
|
+
require "hecks"
|
|
33
|
+
require "hecks/codemod"
|
|
34
|
+
require "hecks/query_ir"
|
|
35
|
+
dry_run = argv.include?("--dry-run")
|
|
36
|
+
|
|
37
|
+
results = Hecks::Codemod::EXAMPLE_ROOTS.filter_map do |domain_dir|
|
|
38
|
+
bluebook_files = Dir.glob(File.join(domain_dir, "bluebook", "*.bluebook"))
|
|
39
|
+
next if bluebook_files.empty?
|
|
40
|
+
|
|
41
|
+
run_files(bluebook_files, dry_run: dry_run)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
report(results, dry_run)
|
|
45
|
+
0
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @param results [Array<Hash>] one outcome for each example domain
|
|
49
|
+
# @param dry_run [Boolean] whether nothing was kept
|
|
50
|
+
# @return [void]
|
|
51
|
+
def report(results, dry_run)
|
|
52
|
+
label = ->(c) { "#{c.owner}##{c.description.inspect} (#{c.locations.size} locations)" }
|
|
53
|
+
|
|
54
|
+
puts "== results (#{dry_run ? 'DRY RUN — nothing written' : 'applied'}) =="
|
|
55
|
+
results.each do |r|
|
|
56
|
+
case r[:status]
|
|
57
|
+
when :clean then puts "clean (no candidates): #{r[:file]}"
|
|
58
|
+
when :applied then puts "APPLIED #{r[:file]}: #{r[:candidates].map(&label).join(', ')}"
|
|
59
|
+
when :skipped then puts "SKIPPED #{r[:file]} (#{r[:reason]}): #{r[:candidates].map(&label).join(', ')}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Grouped by owner as well as description: identical rules recur across unrelated
|
|
65
|
+
# aggregates, and a cross-owner match must not block hoisting the single-owner ones.
|
|
66
|
+
#
|
|
67
|
+
# @param registry [Object] the booted bluebooks
|
|
68
|
+
# @return [Array<Candidate>] the rules to hoist
|
|
69
|
+
def find_candidates(registry)
|
|
70
|
+
Hecks::QueryIR.collect_rules(registry)
|
|
71
|
+
.select { |r| r.kind == "given" }
|
|
72
|
+
.group_by { |r| [r.description, r.canonical, Hecks::QueryIR.owner_of(r.location)] }
|
|
73
|
+
.filter_map do |(description, canonical, owner), rules|
|
|
74
|
+
next if rules.size < 2
|
|
75
|
+
# An owner-level "(declared)" rule is already hoisted for this owner.
|
|
76
|
+
next if rules.any? { |r| r.location.end_with?(" (declared)") }
|
|
77
|
+
|
|
78
|
+
Candidate.new(description: description, canonical: canonical, owner: owner,
|
|
79
|
+
locations: rules.map(&:location))
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The owner's own window: its opening line to the next `end` at the same indentation.
|
|
84
|
+
# `collect_rules` yields a dotted owner ("Account.LedgerEntry") but the source spells the
|
|
85
|
+
# bare name, so the last segment is matched; a name that is not unique in the file is
|
|
86
|
+
# skipped.
|
|
87
|
+
#
|
|
88
|
+
# @param text [String] a bluebook's source
|
|
89
|
+
# @param owner [String] the owner's dotted name
|
|
90
|
+
# @return [Array(MatchData, Integer), nil] the opening line's match and the closing offset
|
|
91
|
+
def owner_window(text, owner)
|
|
92
|
+
bare_name = owner.split(".").last
|
|
93
|
+
open_re = /^([ \t]*)(?:aggregate|entity)\s+"#{Regexp.escape(bare_name)}"\s+do\n/
|
|
94
|
+
return nil if text.scan(open_re).size != 1
|
|
95
|
+
|
|
96
|
+
open_match = text.match(open_re)
|
|
97
|
+
indent = open_match[1]
|
|
98
|
+
close_at = text.index(/^#{indent}end\n/, open_match.end(0))
|
|
99
|
+
return nil unless close_at
|
|
100
|
+
|
|
101
|
+
[open_match, close_at]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Nested entity blocks are carved out of the scan: an entity may declare a `given` with the
|
|
105
|
+
# same description but a different predicate.
|
|
106
|
+
#
|
|
107
|
+
# @param window [String] the owner's source
|
|
108
|
+
# @return [Array<Array(String, Boolean)>] each stretch of source, and whether it is nested
|
|
109
|
+
def strip_nested_entities(window)
|
|
110
|
+
entity_re = /^([ \t]*)entity\s+"[^"]+"\s+do\n/
|
|
111
|
+
segments = []
|
|
112
|
+
pos = 0
|
|
113
|
+
|
|
114
|
+
while (m = window.match(entity_re, pos))
|
|
115
|
+
end_match = window.match(/^#{m[1]}end\n/, m.end(0))
|
|
116
|
+
break unless end_match
|
|
117
|
+
|
|
118
|
+
segments << [window[pos...m.begin(0)], false]
|
|
119
|
+
segments << [window[m.begin(0)...end_match.end(0)], true]
|
|
120
|
+
pos = end_match.end(0)
|
|
121
|
+
end
|
|
122
|
+
segments << [window[pos..], false]
|
|
123
|
+
segments
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# A match needs the predicate to equal the candidate's canonical, not just the description:
|
|
127
|
+
# the same words can carry a different rule (`disputed_by.status` vs
|
|
128
|
+
# `account.customer.status`).
|
|
129
|
+
#
|
|
130
|
+
# @param predicate_src [String] a `given`'s block source
|
|
131
|
+
# @param candidate [Candidate] the rule being hoisted
|
|
132
|
+
# @return [Boolean] whether it is the same rule
|
|
133
|
+
def matching_occurrence?(predicate_src, candidate)
|
|
134
|
+
predicate_src.strip == candidate.canonical
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @param description [String] a `given`'s description
|
|
138
|
+
# @return [Regexp] the line that declares it with a block
|
|
139
|
+
def desc_pattern_for(description)
|
|
140
|
+
/^([ \t]*)given\(#{Regexp.escape(description.inspect)}\)\s*\{([^\n}]*)\}\n/
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @param text [String] a bluebook's source
|
|
144
|
+
# @param candidate [Candidate] the rule to hoist
|
|
145
|
+
# @return [Array(String, Boolean)] the new source and whether the rule was hoisted
|
|
146
|
+
def apply_candidate(text, candidate)
|
|
147
|
+
bounds = owner_window(text, candidate.owner)
|
|
148
|
+
return [text, false] unless bounds
|
|
149
|
+
|
|
150
|
+
open_match, close_at = bounds
|
|
151
|
+
window = text[open_match.end(0)...close_at]
|
|
152
|
+
segments = strip_nested_entities(window)
|
|
153
|
+
pattern = desc_pattern_for(candidate.description)
|
|
154
|
+
|
|
155
|
+
outside_matches = segments.flat_map { |seg, nested| nested ? [] : seg.scan(pattern) }
|
|
156
|
+
.select { |_, predicate_src| matching_occurrence?(predicate_src, candidate) }
|
|
157
|
+
return [text, false] if outside_matches.size < 2
|
|
158
|
+
|
|
159
|
+
# Indent one level inside the owner, not as deep as the occurrence in a command block.
|
|
160
|
+
child_indent = "#{open_match[1]} "
|
|
161
|
+
new_given_line = "#{child_indent}given(#{candidate.description.inspect}) { #{candidate.canonical} }\n"
|
|
162
|
+
|
|
163
|
+
bare_window = segments.map do |seg, nested|
|
|
164
|
+
next seg if nested
|
|
165
|
+
|
|
166
|
+
seg.gsub(pattern) do
|
|
167
|
+
indent = Regexp.last_match(1)
|
|
168
|
+
predicate_src = Regexp.last_match(2)
|
|
169
|
+
if matching_occurrence?(predicate_src, candidate)
|
|
170
|
+
"#{indent}given(#{candidate.description.inspect})\n"
|
|
171
|
+
else
|
|
172
|
+
Regexp.last_match(0)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end.join
|
|
176
|
+
|
|
177
|
+
new_text = text[0...open_match.end(0)] + new_given_line + bare_window + text[close_at..]
|
|
178
|
+
[new_text, true]
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Every command's effective rule set (block-declared or referenced) must be unchanged.
|
|
182
|
+
# Owner-level "(declared)" entries are excluded: they are what is meant to differ.
|
|
183
|
+
#
|
|
184
|
+
# @param registry [Object] the booted bluebooks
|
|
185
|
+
# @return [Hash{String => Array}] each command's rules, by location
|
|
186
|
+
def command_rule_map(registry)
|
|
187
|
+
Hecks::QueryIR.collect_rules(registry)
|
|
188
|
+
.reject { |r| r.location.end_with?(" (declared)") }
|
|
189
|
+
.group_by(&:location)
|
|
190
|
+
.transform_values { |rules| rules.map { |r| [r.kind, r.description, r.canonical] }.sort }
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Kept in one method: `originals` and `before_rules` must be captured before any candidate
|
|
194
|
+
# is applied, and splitting would mean threading that snapshot through parameters.
|
|
195
|
+
#
|
|
196
|
+
# @param files [Array<String>] one example domain's bluebook files
|
|
197
|
+
# @param dry_run [Boolean] whether to judge the edit in memory and write nothing
|
|
198
|
+
# @return [Hash] `file`, `status` (`:clean`, `:applied`, `:skipped`), and the candidates
|
|
199
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
200
|
+
def run_files(files, dry_run:)
|
|
201
|
+
files = files.sort
|
|
202
|
+
originals = files.to_h { |file| [file, File.read(file)] }
|
|
203
|
+
before_rules = command_rule_map(Hecks::Codemod.load_bluebook(files))
|
|
204
|
+
candidates = find_candidates(Hecks::Codemod.load_bluebook(files))
|
|
205
|
+
result_path = files.one? ? files.first : File.dirname(files.first)
|
|
206
|
+
|
|
207
|
+
return { file: result_path, status: :clean } if candidates.empty?
|
|
208
|
+
|
|
209
|
+
texts = originals.transform_values(&:dup)
|
|
210
|
+
applied = []
|
|
211
|
+
candidates.each do |c|
|
|
212
|
+
target = files.find { |file| apply_candidate(texts[file], c).last }
|
|
213
|
+
next unless target
|
|
214
|
+
|
|
215
|
+
text, changed = apply_candidate(texts[target], c)
|
|
216
|
+
texts[target] = text
|
|
217
|
+
applied << c if changed
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
if applied.empty?
|
|
221
|
+
return { file: result_path, status: :skipped,
|
|
222
|
+
reason: "no candidate matched its own source text", candidates: [] }
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
texts.each { |file, text| Hecks::Codemod.stage(file, text, dry_run: dry_run) }
|
|
226
|
+
after_rules, error = Hecks::Codemod.safely do
|
|
227
|
+
command_rule_map(Hecks::Codemod.load_bluebook(files))
|
|
228
|
+
end
|
|
229
|
+
if dry_run || after_rules != before_rules
|
|
230
|
+
originals.each { |file, text| Hecks::Codemod.unstage(file, text, dry_run: dry_run) }
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
if after_rules == before_rules
|
|
234
|
+
{ file: result_path, status: :applied, candidates: applied }
|
|
235
|
+
else
|
|
236
|
+
reason = if error
|
|
237
|
+
"reboot raised after edit (#{error}) — reverted"
|
|
238
|
+
else
|
|
239
|
+
"a command's own effective rule set changed after edit — reverted"
|
|
240
|
+
end
|
|
241
|
+
{ file: result_path, status: :skipped, reason: reason, candidates: applied }
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hecks"
|
|
4
|
+
require_relative "../tools"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module Tools
|
|
8
|
+
# Compacts a Postgres- or Sqlite-backed aggregate's journal: deletes entries already projected
|
|
9
|
+
# into its own aggregate table (its own `checkpoint`), so a `:refresh` projection rebuild never
|
|
10
|
+
# needs them. A `:strict` projection catch-up refuses loudly on its own if it is ever behind
|
|
11
|
+
# what this deletes (see `Ports::Projection::Worker#catch_up!`).
|
|
12
|
+
#
|
|
13
|
+
# hecks compact <domain> [aggregate_name ...] # dry run
|
|
14
|
+
# hecks compact <domain> [aggregate_name ...] --force # apply
|
|
15
|
+
module JournalCompaction
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# Reports, or with `--force` applies, the compaction of each matching aggregate's journal.
|
|
19
|
+
#
|
|
20
|
+
# @param argv [Array<String>] the domain directory, aggregate names and `--force`
|
|
21
|
+
# @param root [String] unused: the domain is read from the path given
|
|
22
|
+
# @return [Integer] 0, or 1 when the domain directory does not exist
|
|
23
|
+
def main(argv, **)
|
|
24
|
+
argv = argv.dup
|
|
25
|
+
force = argv.delete("--force")
|
|
26
|
+
domain = argv.shift
|
|
27
|
+
if domain.nil? || !Dir.exist?(domain)
|
|
28
|
+
warn "hecks compact: no such domain #{domain.inspect}"
|
|
29
|
+
warn "usage: hecks compact <domain> [aggregate_name ...] [--force]"
|
|
30
|
+
return 1
|
|
31
|
+
end
|
|
32
|
+
wanted = argv.dup
|
|
33
|
+
|
|
34
|
+
registry = Hecks.boot(domain).registry
|
|
35
|
+
candidates = candidates(registry, wanted)
|
|
36
|
+
if candidates.empty?
|
|
37
|
+
puts "hecks compact: no Postgres/Sqlite-backed aggregate matched " \
|
|
38
|
+
"#{wanted.empty? ? '(any)' : wanted.inspect} in #{domain}"
|
|
39
|
+
return 0
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
candidates.each { |candidate| compact_candidate(candidate, force: force) }
|
|
43
|
+
0
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @param registry [Hecks::Runtime::Registry] the booted registry
|
|
47
|
+
# @param wanted [Array<String>] aggregate names, or empty for every aggregate
|
|
48
|
+
# @return [Array<Hash>] each compactable aggregate and its adapter
|
|
49
|
+
def candidates(registry, wanted)
|
|
50
|
+
registry.bluebooks.each_with_object([]) do |(domain_name, bluebook), all|
|
|
51
|
+
bluebook.aggregates.each do |aggregate|
|
|
52
|
+
next unless wanted.empty? || wanted.include?(aggregate.hecks_name) || wanted.include?(aggregate.storage_name)
|
|
53
|
+
|
|
54
|
+
repository = registry.repository(domain_name, aggregate)
|
|
55
|
+
next unless repository.is_a?(Hecks::Ports::Persistence::AppendOnly)
|
|
56
|
+
|
|
57
|
+
adapter = repository.adapter
|
|
58
|
+
next unless adapter.respond_to?(:compact_entries!) && adapter.respond_to?(:checkpoint)
|
|
59
|
+
|
|
60
|
+
all << { aggregate: aggregate, adapter: adapter }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @return [void]
|
|
66
|
+
def compact_candidate(candidate, force:)
|
|
67
|
+
aggregate = candidate[:aggregate]
|
|
68
|
+
adapter = candidate[:adapter]
|
|
69
|
+
through = adapter.checkpoint
|
|
70
|
+
|
|
71
|
+
if through <= adapter.compacted_through
|
|
72
|
+
puts "SKIP #{aggregate.storage_name}: nothing new to compact (already compacted through #{through})"
|
|
73
|
+
return
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
unless force
|
|
77
|
+
puts "DRY RUN #{aggregate.storage_name}: would delete journal entries through sequence " \
|
|
78
|
+
"#{through} (compacted_through is currently #{adapter.compacted_through}); current " \
|
|
79
|
+
"state (#{adapter.count} records) is unaffected. Re-run with --force to apply."
|
|
80
|
+
return
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
removed = adapter.compact_entries!(through: through)
|
|
84
|
+
puts "COMPACTED #{aggregate.storage_name}: deleted #{removed} journal entries through " \
|
|
85
|
+
"sequence #{through}; #{adapter.count} records preserved"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hecks"
|
|
4
|
+
require_relative "../tools"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module Tools
|
|
8
|
+
# Projects each domain's OIDC client and scope manifest (`Hecks::Projections::OIDC`) into an
|
|
9
|
+
# `oidc.json` beside the domain.
|
|
10
|
+
#
|
|
11
|
+
# A domain that has no bluebook is skipped and one that cannot be projected is reported, so a
|
|
12
|
+
# sweep over the whole checkout carries on past either. `hecks deploy project_oidc` loads
|
|
13
|
+
# `bundler/setup` first, which pins the `json` gem: newer releases pretty-print an empty array
|
|
14
|
+
# as `[\n\n]`, a diff spec/oidc_manifest_spec.rb would report as drift.
|
|
15
|
+
module OidcManifests
|
|
16
|
+
# Directories that hold a `.hecksagon` but are never domains.
|
|
17
|
+
NOT_DOMAINS = %r{\A(rust|deploy|tmp|coverage)/}
|
|
18
|
+
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
# Projects the manifests and prints one line for each domain.
|
|
22
|
+
#
|
|
23
|
+
# @param argv [Array<String>] domain directories, relative to `root`; every domain of the
|
|
24
|
+
# checkout when empty
|
|
25
|
+
# @param root [String] the checkout
|
|
26
|
+
# @return [Integer] 0
|
|
27
|
+
def main(argv, root: Tools::ROOT)
|
|
28
|
+
wanted = argv.empty? ? domains(root) : argv.map { |path| path.delete_prefix("#{root}/").chomp("/") }
|
|
29
|
+
wanted.each { |path| project(root, path) }
|
|
30
|
+
0
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Every `.hecksagon` names a domain root; rust/, deploy/, tmp/ and coverage/ are never
|
|
34
|
+
# domains.
|
|
35
|
+
#
|
|
36
|
+
# @param root [String] the checkout
|
|
37
|
+
# @return [Array<String>] the domain directories, relative to `root`, sorted
|
|
38
|
+
def domains(root)
|
|
39
|
+
folder = Hecks::Adapters::Folder.new
|
|
40
|
+
Dir.glob(File.join(root, "**/*.hecksagon"))
|
|
41
|
+
.map { |path| folder.domain_root(File.dirname(path)) }
|
|
42
|
+
.compact
|
|
43
|
+
.map { |path| path.delete_prefix("#{root}/") }
|
|
44
|
+
.grep_v(NOT_DOMAINS)
|
|
45
|
+
.uniq.sort
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @param root [String] the checkout
|
|
49
|
+
# @param path [String] a domain directory, relative to `root`
|
|
50
|
+
# @return [void]
|
|
51
|
+
def project(root, path)
|
|
52
|
+
runtime = Hecks.boot(File.join(root, path), install_doors: false)
|
|
53
|
+
name = runtime.registry.bluebooks.keys.first
|
|
54
|
+
|
|
55
|
+
unless name
|
|
56
|
+
warn " #{path}: no bluebook found — skipped"
|
|
57
|
+
return
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
bluebook = runtime.registry.bluebook(name)
|
|
61
|
+
out = File.join(root, path, "oidc.json")
|
|
62
|
+
|
|
63
|
+
Hecks::Projector.write(Hecks::Projector.call(:oidc, bluebook: bluebook), out)
|
|
64
|
+
puts " #{path}/oidc.json <- #{name}"
|
|
65
|
+
rescue StandardError => e
|
|
66
|
+
warn " #{path}: cannot project — #{e.message.lines.first.strip}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../tools"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Tools
|
|
7
|
+
# Structured queries against the language's own IR: construct diffs, duplicate rules, impact.
|
|
8
|
+
# A thin front end over `Hecks::QueryIR`, which `hecks serve_query_ir_mcp` shares; needs no
|
|
9
|
+
# Postgres.
|
|
10
|
+
#
|
|
11
|
+
# hecks query_ir constructs [Name ...]
|
|
12
|
+
# hecks query_ir duplicates [--meta] [domain_dir ...]
|
|
13
|
+
# hecks query_ir impact Name field (advisory, not a gate)
|
|
14
|
+
module QueryIrRun
|
|
15
|
+
USAGE = <<~USAGE
|
|
16
|
+
Usage:
|
|
17
|
+
hecks query_ir constructs [Name ...]
|
|
18
|
+
hecks query_ir duplicates [--meta] [domain_dir ...]
|
|
19
|
+
hecks query_ir impact Name field
|
|
20
|
+
USAGE
|
|
21
|
+
|
|
22
|
+
module_function
|
|
23
|
+
|
|
24
|
+
# Prints the answer to one query.
|
|
25
|
+
#
|
|
26
|
+
# @param argv [Array<String>] `constructs`, `duplicates` or `impact`, then its arguments
|
|
27
|
+
# @param root [String] the checkout (unused: the queries read the loaded language)
|
|
28
|
+
# @return [Integer] 0, or 1 when the arguments are refused
|
|
29
|
+
def main(argv, root: Tools::ROOT)
|
|
30
|
+
_ = root
|
|
31
|
+
require "hecks"
|
|
32
|
+
require "hecks/query_ir"
|
|
33
|
+
args = argv.dup
|
|
34
|
+
case args.shift
|
|
35
|
+
when "constructs" then constructs(args)
|
|
36
|
+
when "duplicates" then duplicates(args)
|
|
37
|
+
when "impact" then impact(args)
|
|
38
|
+
else
|
|
39
|
+
warn USAGE
|
|
40
|
+
1
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @param args [Array<String>] construct names, none for all
|
|
45
|
+
# @return [Integer] the exit status
|
|
46
|
+
def constructs(args)
|
|
47
|
+
puts Hecks::QueryIR.format_constructs(Hecks::QueryIR.constructs(args))
|
|
48
|
+
0
|
|
49
|
+
rescue ArgumentError => e
|
|
50
|
+
warn e.message
|
|
51
|
+
1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param args [Array<String>] `--meta` and domain directories
|
|
55
|
+
# @return [Integer] the exit status
|
|
56
|
+
def duplicates(args)
|
|
57
|
+
use_meta = args.delete("--meta") || args.empty?
|
|
58
|
+
domains = args.empty? ? nil : args
|
|
59
|
+
|
|
60
|
+
puts Hecks::QueryIR.format_duplicates(Hecks::QueryIR.duplicates(domains: domains, include_meta: use_meta))
|
|
61
|
+
0
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param args [Array<String>] a construct's name and a field's
|
|
65
|
+
# @return [Integer] the exit status
|
|
66
|
+
def impact(args)
|
|
67
|
+
name, field = args
|
|
68
|
+
unless name && field
|
|
69
|
+
warn "usage: hecks query_ir impact Name field"
|
|
70
|
+
return 1
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
puts Hecks::QueryIR.format_impact_preview(Hecks::QueryIR.impact_preview(name, field))
|
|
74
|
+
0
|
|
75
|
+
rescue ArgumentError => e
|
|
76
|
+
warn e.message
|
|
77
|
+
1
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
require_relative "../tools"
|
|
7
|
+
require_relative "../corpus"
|
|
8
|
+
|
|
9
|
+
module Hecks
|
|
10
|
+
module Tools
|
|
11
|
+
# Regenerates every corpus domain's committed Rust output with `hecks project_rust`, or checks
|
|
12
|
+
# that the committed output is what a regeneration would write.
|
|
13
|
+
#
|
|
14
|
+
# The domain list comes from `Hecks::Corpus.rust_regen_order`, never a hand-kept list. The
|
|
15
|
+
# order is fixed (sorted by relative path): domains that share governance/, identity/,
|
|
16
|
+
# Cargo.toml and generated/mod.rs stamp them with whichever ran last, so it must not flap. If
|
|
17
|
+
# the domain set or the sort rule changes, re-run and commit the output; never hand-edit stamps.
|
|
18
|
+
#
|
|
19
|
+
# hecks regenerate_corpus # discover, regenerate, print the plan
|
|
20
|
+
# hecks regenerate_corpus --check # regenerate into a scratch crate; fail on any difference
|
|
21
|
+
#
|
|
22
|
+
# `--check` never writes the working tree: it runs beside the parallel suite.
|
|
23
|
+
module RegenerationRun
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
# Regenerates the corpus, or checks it.
|
|
27
|
+
#
|
|
28
|
+
# @param argv [Array<String>] `--check` to project into a scratch crate and compare
|
|
29
|
+
# @param root [String] the checkout
|
|
30
|
+
# @return [Integer] 0, or 1 when a check finds a difference
|
|
31
|
+
# @raise [SystemExit] when no corpus domain is found or a domain fails to regenerate
|
|
32
|
+
def main(argv, root: Tools::ROOT)
|
|
33
|
+
Dir.chdir(root) do
|
|
34
|
+
domains = plan(root)
|
|
35
|
+
# A check projects into a scratch copy of the crate; the forked children inherit
|
|
36
|
+
# HECKS_RUST_DIR.
|
|
37
|
+
scratch = argv.include?("--check") && scratch_crate(root)
|
|
38
|
+
saved = ENV.fetch("HECKS_RUST_DIR", nil)
|
|
39
|
+
ENV["HECKS_RUST_DIR"] = scratch if scratch
|
|
40
|
+
begin
|
|
41
|
+
begin
|
|
42
|
+
regenerate(domains)
|
|
43
|
+
ensure
|
|
44
|
+
saved ? ENV["HECKS_RUST_DIR"] = saved : ENV.delete("HECKS_RUST_DIR")
|
|
45
|
+
end
|
|
46
|
+
scratch ? verdict(scratch) : 0
|
|
47
|
+
ensure
|
|
48
|
+
FileUtils.rm_rf(scratch) if scratch
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# `meta` is never in the list: `Hecks::Corpus::RUST_ELSEWHERE` routes it to the check that
|
|
54
|
+
# owns it instead.
|
|
55
|
+
#
|
|
56
|
+
# @param root [String] the checkout
|
|
57
|
+
# @return [Array<String>] the domains to regenerate, relative to `root`, in run order
|
|
58
|
+
# @raise [SystemExit] when there are none
|
|
59
|
+
def plan(root)
|
|
60
|
+
domains = Hecks::Corpus.rust_regen_order.map { |domain| domain.dir.delete_prefix("#{root}/") }
|
|
61
|
+
|
|
62
|
+
if domains.empty?
|
|
63
|
+
abort "hecks regenerate_corpus: discovered ZERO domains with committed rust/src/generated/ " \
|
|
64
|
+
"output in Hecks::Corpus — that almost certainly means this script is running from the " \
|
|
65
|
+
"wrong directory, or something upstream deleted rust/src/generated/ entirely. Refusing " \
|
|
66
|
+
"to silently treat that as \"nothing to regenerate.\""
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
puts "hecks regenerate_corpus: regenerating #{domains.size} domain(s), in this fixed order:"
|
|
70
|
+
domains.each { |d| puts " #{d}" }
|
|
71
|
+
puts
|
|
72
|
+
domains
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Builds the memoized grammar registry once and forks for each domain, one at a time and in
|
|
76
|
+
# order, because the shared outputs depend on which domain ran last.
|
|
77
|
+
#
|
|
78
|
+
# @param domains [Array<String>] the domains, in run order
|
|
79
|
+
# @return [void]
|
|
80
|
+
def regenerate(domains)
|
|
81
|
+
require "hecks"
|
|
82
|
+
require "hecks/bluebook/meta_validator"
|
|
83
|
+
require "hecks/rust_build"
|
|
84
|
+
Hecks::Bluebook::MetaValidator.grammar_registry
|
|
85
|
+
domains.each { |domain| project_rust_in_fork(domain) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Forks so the child inherits the memoized grammar registry instead of rebuilding it. The
|
|
89
|
+
# child's output goes through a pipe and is printed here, so it reaches whoever captures this
|
|
90
|
+
# process's stdout.
|
|
91
|
+
#
|
|
92
|
+
# @param domain [String] the domain, relative to the checkout
|
|
93
|
+
# @return [void]
|
|
94
|
+
# @raise [SystemExit] when the child fails
|
|
95
|
+
def project_rust_in_fork(domain)
|
|
96
|
+
puts "== hecks project_rust #{domain} =="
|
|
97
|
+
reader, writer = IO.pipe
|
|
98
|
+
pid = fork { run_child(reader, writer, domain) }
|
|
99
|
+
writer.close
|
|
100
|
+
$stdout.write(reader.read)
|
|
101
|
+
reader.close
|
|
102
|
+
_, status = Process.wait2(pid)
|
|
103
|
+
abort "hecks regenerate_corpus: hecks project_rust #{domain} failed (#{status.inspect})" unless status.success?
|
|
104
|
+
puts
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @param reader [IO] the parent's end of the pipe, closed here
|
|
108
|
+
# @param writer [IO] the child's end, which becomes its stdout and stderr
|
|
109
|
+
# @param domain [String] the domain
|
|
110
|
+
# @return [void] never returns: the child leaves without the parent's exit handlers
|
|
111
|
+
def run_child(reader, writer, domain)
|
|
112
|
+
reader.close
|
|
113
|
+
# The real descriptors, not the globals: a caller may have swapped those for a buffer.
|
|
114
|
+
STDOUT.reopen(writer) # rubocop:disable Style/GlobalStdStream
|
|
115
|
+
STDERR.reopen(writer) # rubocop:disable Style/GlobalStdStream
|
|
116
|
+
$stdout = STDOUT
|
|
117
|
+
$stderr = STDERR
|
|
118
|
+
status = begin
|
|
119
|
+
Hecks::RustBuild.run("project_rust", [domain], out: STDOUT, err: STDERR) # rubocop:disable Style/GlobalStdStream
|
|
120
|
+
rescue SystemExit => e
|
|
121
|
+
e.status
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
warn "#{e.class}: #{e.message}"
|
|
124
|
+
1
|
|
125
|
+
end
|
|
126
|
+
$stdout.flush
|
|
127
|
+
exit!(status)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# A scratch crate: copies of the generated tree and Cargo.toml, all `hecks project_rust`
|
|
131
|
+
# writes.
|
|
132
|
+
#
|
|
133
|
+
# @param root [String] the checkout
|
|
134
|
+
# @return [String] the scratch directory
|
|
135
|
+
def scratch_crate(root)
|
|
136
|
+
scratch = Dir.mktmpdir("regen-codegen-check")
|
|
137
|
+
FileUtils.mkdir_p(File.join(scratch, "src"))
|
|
138
|
+
FileUtils.cp_r(File.join(root, "rust/src/generated"), File.join(scratch, "src/generated"))
|
|
139
|
+
FileUtils.cp(File.join(root, "rust/Cargo.toml"), scratch)
|
|
140
|
+
scratch
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# The gate: the scratch projection must match the generated sources and Cargo.toml on disk.
|
|
144
|
+
#
|
|
145
|
+
# @param scratch [String] the scratch crate, removed here
|
|
146
|
+
# @return [Integer] 0 when it matches, 1 after printing the difference
|
|
147
|
+
def verdict(scratch)
|
|
148
|
+
same = matches_scratch?(scratch)
|
|
149
|
+
FileUtils.rm_rf(scratch)
|
|
150
|
+
same ? 0 : 1
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# @param scratch [String] the scratch crate
|
|
154
|
+
# @return [Boolean] whether it matches the tree, printing `git diff` output when it does not
|
|
155
|
+
def matches_scratch?(scratch)
|
|
156
|
+
pairs = [["rust/src/generated", File.join(scratch, "src/generated")],
|
|
157
|
+
["rust/Cargo.toml", File.join(scratch, "Cargo.toml")]]
|
|
158
|
+
pairs.map do |on_disk, projected|
|
|
159
|
+
out, status = Open3.capture2e("git", "diff", "--no-index", "--exit-code", "--", on_disk, projected)
|
|
160
|
+
print out
|
|
161
|
+
status.success?
|
|
162
|
+
end.all?
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|