hecks 2.9.0 → 3.0.1
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 +13 -4
- 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 +15 -8
- 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 +15 -5
- 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
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
require "fileutils"
|
|
2
1
|
require_relative "commands"
|
|
2
|
+
require_relative "clean_tree"
|
|
3
|
+
require_relative "git"
|
|
4
|
+
require_relative "../../cli/release_gem"
|
|
5
|
+
require "hecks/hecks/adapters/codebase/gem_registry"
|
|
3
6
|
|
|
4
7
|
module Hecks
|
|
5
8
|
module Release
|
|
6
9
|
class Runner
|
|
7
|
-
# The gem step: hands the push to `
|
|
10
|
+
# The gem step: hands the push to `Hecks::CLI::ReleaseGem`, which builds and pushes
|
|
8
11
|
# it with the key 1Password holds; a dry run only builds it, then deletes it.
|
|
9
12
|
class GemPublisher
|
|
10
13
|
# @param root [String] the repository root
|
|
@@ -14,6 +17,7 @@ module Hecks
|
|
|
14
17
|
@root = root
|
|
15
18
|
@commands = commands
|
|
16
19
|
@console = console
|
|
20
|
+
@registry = Hecks::Adapters::Codebase::GemRegistry.new(root: root, commands: commands)
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
# Pushes the gem, or with `dry_run` only builds it.
|
|
@@ -22,20 +26,23 @@ module Hecks
|
|
|
22
26
|
# @param dry_run [Boolean] build and delete the gem, pushing nothing
|
|
23
27
|
# @return [void]
|
|
24
28
|
# @raise [CommandFailed] if the build or the push fails
|
|
29
|
+
# @raise [Refusal] if a packaged path holds a modified, untracked or ignored file
|
|
25
30
|
def publish!(version, dry_run:)
|
|
26
31
|
return build_only(version) if dry_run
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
CleanTree.new(git: Git.new(root: @root, commands: @commands)).check!
|
|
34
|
+
|
|
35
|
+
@console.say("Publishing hecks #{version} to rubygems.org...")
|
|
36
|
+
status = Hecks::CLI::ReleaseGem.call(root: @root, commands: @commands, out: @console.out,
|
|
37
|
+
err: @console.err, version: version)
|
|
38
|
+
raise CommandFailed, "the gem push was refused" unless status.zero?
|
|
30
39
|
end
|
|
31
40
|
|
|
32
41
|
private
|
|
33
42
|
|
|
34
43
|
def build_only(version)
|
|
35
44
|
@console.say("Dry run: building hecks-#{version}.gem (nothing is pushed)...")
|
|
36
|
-
@
|
|
37
|
-
ensure
|
|
38
|
-
FileUtils.rm_f(File.join(@root, "hecks-#{version}.gem"))
|
|
45
|
+
@registry.build_only!(version)
|
|
39
46
|
end
|
|
40
47
|
end
|
|
41
48
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require "tempfile"
|
|
2
1
|
require_relative "commands"
|
|
2
|
+
require "hecks/hecks/adapters/codebase/npm_registry"
|
|
3
3
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Release
|
|
@@ -7,16 +7,13 @@ module Hecks
|
|
|
7
7
|
# The npm step: publishes `@hecks/client` from `packages/hecks-client`
|
|
8
8
|
# with the token 1Password holds.
|
|
9
9
|
class NpmPublisher
|
|
10
|
-
PACKAGE_DIR = "packages/hecks-client".freeze
|
|
11
|
-
ENV_FILE = "release/npm_publish.env".freeze
|
|
12
10
|
APPROVAL_NOTICE = "npm will print an approval link; open it and approve with your security key or passkey. " \
|
|
13
11
|
"This step waits for you.".freeze
|
|
14
|
-
USERCONFIG_LINE = "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}".freeze
|
|
15
12
|
|
|
16
13
|
def initialize(root:, commands:, console:)
|
|
17
14
|
@root = root
|
|
18
|
-
@commands = commands
|
|
19
15
|
@console = console
|
|
16
|
+
@registry = Hecks::Adapters::Codebase::NpmRegistry.new(root: root, commands: commands)
|
|
20
17
|
end
|
|
21
18
|
|
|
22
19
|
# Publishes the package, or with `dry_run` only packs it, which needs no
|
|
@@ -25,49 +22,22 @@ module Hecks
|
|
|
25
22
|
install_dependencies
|
|
26
23
|
if dry_run
|
|
27
24
|
@console.say("Dry run: npm publish --dry-run for @hecks/client #{version} (nothing is published)...")
|
|
28
|
-
@
|
|
25
|
+
@registry.dry_publish!
|
|
29
26
|
else
|
|
30
27
|
@console.say("Publishing @hecks/client #{version} to npm (1Password will prompt for Touch ID)...")
|
|
31
28
|
@console.say(APPROVAL_NOTICE)
|
|
32
|
-
|
|
29
|
+
@registry.publish!
|
|
33
30
|
end
|
|
34
31
|
end
|
|
35
32
|
|
|
36
33
|
private
|
|
37
34
|
|
|
38
|
-
def package_dir
|
|
39
|
-
File.join(@root, PACKAGE_DIR)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
35
|
# npm's `prepack` hook builds `dist/`; there is no separate build step here.
|
|
43
36
|
def install_dependencies
|
|
44
|
-
return if
|
|
37
|
+
return if @registry.installed?
|
|
45
38
|
|
|
46
39
|
@console.say("Installing the client's dependencies (npm ci)...")
|
|
47
|
-
@
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# The token bypasses 2FA (the account's second factor is a passkey); this
|
|
51
|
-
# fallback publish runs with `--auth-type=web` and doesn't capture output,
|
|
52
|
-
# so an approval prompt reaches the terminal.
|
|
53
|
-
def publish_with_token
|
|
54
|
-
with_userconfig do |userconfig|
|
|
55
|
-
@commands.run!(
|
|
56
|
-
"op", "run", "--env-file=#{File.join(@root, ENV_FILE)}", "--",
|
|
57
|
-
"npm", "publish", "--access", "public", "--auth-type=web", "--userconfig", userconfig,
|
|
58
|
-
chdir: package_dir
|
|
59
|
-
)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def with_userconfig
|
|
64
|
-
file = Tempfile.new(["hecks-npmrc", ".npmrc"])
|
|
65
|
-
file.chmod(0o600)
|
|
66
|
-
file.write("#{USERCONFIG_LINE}\n")
|
|
67
|
-
file.flush
|
|
68
|
-
yield file.path
|
|
69
|
-
ensure
|
|
70
|
-
file&.close!
|
|
40
|
+
@registry.install!
|
|
71
41
|
end
|
|
72
42
|
end
|
|
73
43
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
require_relative "commands"
|
|
3
3
|
require_relative "git"
|
|
4
|
+
require_relative "clean_tree"
|
|
4
5
|
|
|
5
6
|
module Hecks
|
|
6
7
|
module Release
|
|
@@ -20,6 +21,7 @@ module Hecks
|
|
|
20
21
|
}.freeze
|
|
21
22
|
VERSION_LINE = /^\s*VERSION\s*=\s*"(?<version>[^"]+)"/
|
|
22
23
|
CLIENT_PACKAGE = "packages/hecks-client/package.json".freeze
|
|
24
|
+
HOST_RELEASE_FILE = "rust/host/HECKS_RELEASE".freeze
|
|
23
25
|
|
|
24
26
|
def initialize(root:, commands:, git:, tools:)
|
|
25
27
|
@root = root
|
|
@@ -28,6 +30,14 @@ module Hecks
|
|
|
28
30
|
@tools = tools
|
|
29
31
|
end
|
|
30
32
|
|
|
33
|
+
# Refuses when a tool the release runs is not installed.
|
|
34
|
+
#
|
|
35
|
+
# @return [void]
|
|
36
|
+
# @raise [Refusal] naming the tool and how to install it
|
|
37
|
+
def check_tools!
|
|
38
|
+
check_tools
|
|
39
|
+
end
|
|
40
|
+
|
|
31
41
|
# Runs every check; the first refusal stops it.
|
|
32
42
|
def check!
|
|
33
43
|
check_tools
|
|
@@ -36,6 +46,7 @@ module Hecks
|
|
|
36
46
|
check_clean
|
|
37
47
|
version = declared_version
|
|
38
48
|
check_client_version(version)
|
|
49
|
+
check_host_release(version)
|
|
39
50
|
check_changelog(version)
|
|
40
51
|
Facts.new(version: version, sha: git("rev-parse", "HEAD").strip)
|
|
41
52
|
end
|
|
@@ -70,9 +81,11 @@ module Hecks
|
|
|
70
81
|
end
|
|
71
82
|
|
|
72
83
|
def check_clean
|
|
73
|
-
|
|
84
|
+
unless git("status", "--porcelain").strip.empty?
|
|
85
|
+
raise Refusal, "the working tree has uncommitted changes; commit or discard them before releasing"
|
|
86
|
+
end
|
|
74
87
|
|
|
75
|
-
|
|
88
|
+
CleanTree.new(git: @git).check!
|
|
76
89
|
end
|
|
77
90
|
|
|
78
91
|
def declared_version
|
|
@@ -89,6 +102,17 @@ module Hecks
|
|
|
89
102
|
raise Refusal, "packages/hecks-client is at #{client} but Hecks::VERSION is #{version}; bump the package first."
|
|
90
103
|
end
|
|
91
104
|
|
|
105
|
+
# The Rust host reports the Hecks release it was built for from this file, and the
|
|
106
|
+
# committed-approval gate compares rehearsals against it.
|
|
107
|
+
def check_host_release(version)
|
|
108
|
+
path = File.join(@root, HOST_RELEASE_FILE)
|
|
109
|
+
host = File.exist?(path) ? File.read(path).strip : nil
|
|
110
|
+
return if host == version
|
|
111
|
+
|
|
112
|
+
raise Refusal, "#{HOST_RELEASE_FILE} says #{host.inspect} but Hecks::VERSION is #{version}; " \
|
|
113
|
+
"write #{version} into it in the release PR."
|
|
114
|
+
end
|
|
115
|
+
|
|
92
116
|
def check_changelog(version)
|
|
93
117
|
return if File.read(File.join(@root, "CHANGELOG.md")).match?(/^## \[#{Regexp.escape(version)}\]/)
|
|
94
118
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
require "json"
|
|
2
1
|
require_relative "commands"
|
|
2
|
+
require "hecks/hecks/adapters/codebase/gem_registry"
|
|
3
|
+
require "hecks/hecks/adapters/codebase/npm_registry"
|
|
3
4
|
|
|
4
5
|
module Hecks
|
|
5
6
|
module Release
|
|
@@ -7,16 +8,14 @@ module Hecks
|
|
|
7
8
|
# Asks the registries whether a version is already out, so a re-run of the
|
|
8
9
|
# release skips what an earlier run finished.
|
|
9
10
|
#
|
|
10
|
-
# Neither question needs credentials.
|
|
11
|
+
# Neither question needs credentials. The registries themselves are the GemRegistry and
|
|
12
|
+
# NpmRegistry adapters.
|
|
11
13
|
class Published
|
|
12
|
-
GEM_VERSIONS_URL = "https://rubygems.org/api/v1/versions/hecks.json".freeze
|
|
13
|
-
NPM_PACKAGE = "@hecks/client".freeze
|
|
14
|
-
|
|
15
14
|
# @param root [String] the repository root
|
|
16
15
|
# @param commands [#capture] runs curl and npm
|
|
17
16
|
def initialize(root:, commands:)
|
|
18
|
-
@
|
|
19
|
-
@
|
|
17
|
+
@gems = Hecks::Adapters::Codebase::GemRegistry.new(root: root, commands: commands)
|
|
18
|
+
@npm = Hecks::Adapters::Codebase::NpmRegistry.new(root: root, commands: commands)
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
# Says whether RubyGems lists the hecks gem at a version.
|
|
@@ -24,31 +23,14 @@ module Hecks
|
|
|
24
23
|
# @param version [String] the version to look for
|
|
25
24
|
# @return [Boolean] true when it is published
|
|
26
25
|
# @raise [Refusal] if RubyGems cannot be reached or answers with something unreadable
|
|
27
|
-
def gem?(version)
|
|
28
|
-
result = @commands.capture("curl", "-fsS", GEM_VERSIONS_URL, chdir: @root)
|
|
29
|
-
unless result.success?
|
|
30
|
-
raise Refusal, "could not list published hecks versions (#{result.stderr.strip}); " \
|
|
31
|
-
"check the network and re-run"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
JSON.parse(result.stdout).any? { |entry| entry["number"] == version }
|
|
35
|
-
rescue JSON::ParserError
|
|
36
|
-
raise Refusal, "RubyGems answered with something other than a version list; re-run in a minute"
|
|
37
|
-
end
|
|
26
|
+
def gem?(version) = @gems.published?(version)
|
|
38
27
|
|
|
39
28
|
# Says whether npm lists @hecks/client at a version.
|
|
40
29
|
#
|
|
41
30
|
# @param version [String] the version to look for
|
|
42
31
|
# @return [Boolean] true when it is published
|
|
43
32
|
# @raise [Refusal] if npm fails for any reason other than the version not existing
|
|
44
|
-
def npm?(version)
|
|
45
|
-
result = @commands.capture("npm", "view", "#{NPM_PACKAGE}@#{version}", "version", chdir: @root)
|
|
46
|
-
return result.stdout.strip == version if result.success?
|
|
47
|
-
return false if result.stderr.include?("E404")
|
|
48
|
-
|
|
49
|
-
raise Refusal, "could not check npm for #{NPM_PACKAGE}@#{version} (#{result.stderr.strip.lines.first}); " \
|
|
50
|
-
"check the network and re-run"
|
|
51
|
-
end
|
|
33
|
+
def npm?(version) = @npm.published?(version)
|
|
52
34
|
end
|
|
53
35
|
end
|
|
54
36
|
end
|
|
@@ -11,24 +11,50 @@ module Hecks
|
|
|
11
11
|
@git = git
|
|
12
12
|
@console = console
|
|
13
13
|
@dry_run = dry_run
|
|
14
|
+
@changed = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @return [Boolean] whether this run created or pushed the tag, for real
|
|
18
|
+
def changed?
|
|
19
|
+
@changed
|
|
14
20
|
end
|
|
15
21
|
|
|
16
22
|
# Brings the tag to the state described above, saying what it did; returns
|
|
17
23
|
# false only when the person declined the confirmation.
|
|
18
|
-
|
|
24
|
+
#
|
|
25
|
+
# @param facts [#version, #sha] the release
|
|
26
|
+
# @param note [String, nil] what pushing the tag sets off, worded into the question
|
|
27
|
+
def ensure!(facts, note: nil)
|
|
19
28
|
tag = "v#{facts.version}"
|
|
20
29
|
local = local_commit(tag)
|
|
21
30
|
remote = remote_commit(tag)
|
|
22
31
|
refuse_elsewhere(tag, facts.sha, local: local, remote: remote)
|
|
23
32
|
return true.tap { already_there(tag) } if remote
|
|
24
33
|
return true.tap { preview(tag, facts.sha, local) } if @dry_run
|
|
25
|
-
return false unless @console.confirm?(question(tag, facts.sha, local))
|
|
34
|
+
return false unless @console.confirm?(question(tag, facts.sha, local, note))
|
|
26
35
|
|
|
27
36
|
create(tag, facts.sha) unless local
|
|
28
37
|
push(tag)
|
|
38
|
+
@changed = true
|
|
29
39
|
true
|
|
30
40
|
end
|
|
31
41
|
|
|
42
|
+
# @param tag [String] the tag, such as `v3.0.0`
|
|
43
|
+
# @return [String, nil] the commit the tag points at in this checkout, or nil when absent
|
|
44
|
+
def local_commit(tag)
|
|
45
|
+
result = @git.capture("rev-parse", "-q", "--verify", "refs/tags/#{tag}^{commit}")
|
|
46
|
+
result.success? ? result.stdout.strip : nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @param tag [String] the tag, such as `v3.0.0`
|
|
50
|
+
# @return [String, nil] the commit the tag points at on origin, or nil when it is not there
|
|
51
|
+
# @raise [Refusal] when the remote's tags cannot be listed
|
|
52
|
+
def remote_commit(tag)
|
|
53
|
+
listing = @git.read("ls-remote", "--tags", "origin", "refs/tags/#{tag}", "refs/tags/#{tag}^{}")
|
|
54
|
+
shas = listing.lines.to_h { |line| line.split.then { |sha, ref| [ref, sha] } }
|
|
55
|
+
shas.fetch("refs/tags/#{tag}^{}") { shas["refs/tags/#{tag}"] }
|
|
56
|
+
end
|
|
57
|
+
|
|
32
58
|
private
|
|
33
59
|
|
|
34
60
|
def already_there(tag)
|
|
@@ -40,9 +66,9 @@ module Hecks
|
|
|
40
66
|
@console.say("Would push #{tag} to origin.")
|
|
41
67
|
end
|
|
42
68
|
|
|
43
|
-
def question(tag, sha, local)
|
|
69
|
+
def question(tag, sha, local, note)
|
|
44
70
|
verb = local ? "Push" : "Create and push"
|
|
45
|
-
"#{verb} annotated tag #{tag} at #{sha[0, 7]} to origin?"
|
|
71
|
+
"#{verb} annotated tag #{tag} at #{sha[0, 7]} to origin#{" (#{note})" if note}?"
|
|
46
72
|
end
|
|
47
73
|
|
|
48
74
|
def create(tag, sha)
|
|
@@ -63,17 +89,6 @@ module Hecks
|
|
|
63
89
|
"and the missing package must be published from a checkout of #{tag}"
|
|
64
90
|
end
|
|
65
91
|
end
|
|
66
|
-
|
|
67
|
-
def local_commit(tag)
|
|
68
|
-
result = @git.capture("rev-parse", "-q", "--verify", "refs/tags/#{tag}^{commit}")
|
|
69
|
-
result.success? ? result.stdout.strip : nil
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def remote_commit(tag)
|
|
73
|
-
listing = @git.read("ls-remote", "--tags", "origin", "refs/tags/#{tag}", "refs/tags/#{tag}^{}")
|
|
74
|
-
shas = listing.lines.to_h { |line| line.split.then { |sha, ref| [ref, sha] } }
|
|
75
|
-
shas.fetch("refs/tags/#{tag}^{}") { shas["refs/tags/#{tag}"] }
|
|
76
|
-
end
|
|
77
92
|
end
|
|
78
93
|
end
|
|
79
94
|
end
|
data/lib/hecks/release/runner.rb
CHANGED
|
@@ -94,9 +94,16 @@ module Hecks
|
|
|
94
94
|
# @param err [IO] where refusals and failures are written
|
|
95
95
|
# @param pause [#call] sleeps for a number of seconds while waiting for CI
|
|
96
96
|
# @param now [#call] answers the current time in seconds, monotonically, while waiting for CI
|
|
97
|
+
# @param facts [Preflight::Facts, nil] what a caller already checked and cleared (a branch, a
|
|
98
|
+
# clean tree, matching versions, a changelog entry); the release then checks only that its
|
|
99
|
+
# tools are installed, and every rule stays with the caller
|
|
97
100
|
def initialize(root:, options: Options.new, commands: Commands.new, input: $stdin, out: $stdout, err: $stderr,
|
|
98
|
-
pause: ->(seconds) { sleep(seconds) }, now: -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
|
101
|
+
pause: ->(seconds) { sleep(seconds) }, now: -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) },
|
|
102
|
+
facts: nil)
|
|
99
103
|
@root = root
|
|
104
|
+
@facts = facts
|
|
105
|
+
@steps = []
|
|
106
|
+
@verified = false
|
|
100
107
|
@options = options
|
|
101
108
|
@commands = commands
|
|
102
109
|
@console = Console.new(input: input, out: out, err: err, assume_yes: options.yes?)
|
|
@@ -105,16 +112,28 @@ module Hecks
|
|
|
105
112
|
@ci = CiPublisher.new(root: root, published: @published, console: @console, pause: pause, now: now)
|
|
106
113
|
end
|
|
107
114
|
|
|
115
|
+
# @return [Array<Symbol>] the steps this run carried out for real: `:tagged`, `:gem`, `:npm`
|
|
116
|
+
attr_reader :steps
|
|
117
|
+
|
|
118
|
+
# @return [Boolean] whether, after a real run, every registry in scope lists the version
|
|
119
|
+
attr_reader :verified
|
|
120
|
+
|
|
108
121
|
# Runs the release.
|
|
109
122
|
#
|
|
110
123
|
# @return [Integer] 0 when the release finished or had nothing to do, 1 when a check refused,
|
|
111
124
|
# a step failed, CI's publish did not arrive in time or a confirmation was declined
|
|
112
125
|
def call
|
|
113
|
-
facts =
|
|
126
|
+
facts = preflight
|
|
114
127
|
pending = pending_steps(facts.version)
|
|
115
128
|
require_workflow(pending)
|
|
116
|
-
|
|
129
|
+
# Nothing is tagged or pushed until the person has agreed to the publish: the tag push
|
|
130
|
+
# is what starts CI's npm publish.
|
|
131
|
+
return declined unless @options.dry_run || pending.empty? || publish_confirmed?(facts.version, pending)
|
|
117
132
|
|
|
133
|
+
tagger = Tagger.new(git: @git, console: @console, dry_run: @options.dry_run)
|
|
134
|
+
return declined unless tagger.ensure!(facts, note: tag_note(facts.version, pending))
|
|
135
|
+
|
|
136
|
+
@steps << :tagged if tagger.changed?
|
|
118
137
|
publish(facts, pending)
|
|
119
138
|
rescue Refusal, CommandFailed => e
|
|
120
139
|
@console.warn(e.message)
|
|
@@ -123,6 +142,14 @@ module Hecks
|
|
|
123
142
|
|
|
124
143
|
private
|
|
125
144
|
|
|
145
|
+
def preflight
|
|
146
|
+
check = Preflight.new(root: @root, commands: @commands, git: @git, tools: tools)
|
|
147
|
+
return check.check! unless @facts
|
|
148
|
+
|
|
149
|
+
check.check_tools!
|
|
150
|
+
@facts
|
|
151
|
+
end
|
|
152
|
+
|
|
126
153
|
def tools
|
|
127
154
|
list = %w[git curl]
|
|
128
155
|
list << "gem" if @options.gem?
|
|
@@ -143,6 +170,11 @@ module Hecks
|
|
|
143
170
|
published
|
|
144
171
|
end
|
|
145
172
|
|
|
173
|
+
# The steps this run's flags put in scope, whether or not a registry already lists them.
|
|
174
|
+
def in_scope
|
|
175
|
+
[(:gem if @options.gem?), (:npm if @options.npm?)].compact
|
|
176
|
+
end
|
|
177
|
+
|
|
146
178
|
def ci_publishes?(pending)
|
|
147
179
|
pending.include?(:npm) && !@options.npm_local?
|
|
148
180
|
end
|
|
@@ -157,13 +189,19 @@ module Hecks
|
|
|
157
189
|
def publish(facts, pending)
|
|
158
190
|
if pending.empty?
|
|
159
191
|
@console.say("Nothing to publish for #{facts.version}.")
|
|
192
|
+
@verified = registries_list?(facts.version, in_scope) unless @options.dry_run
|
|
160
193
|
return 0
|
|
161
194
|
end
|
|
162
|
-
return declined unless @options.dry_run || publish_confirmed?(facts.version, pending)
|
|
163
|
-
|
|
164
195
|
run_steps(facts.version, pending)
|
|
165
196
|
end
|
|
166
197
|
|
|
198
|
+
# What pushing the tag sets off, for the tag question; nothing when CI publishes nothing.
|
|
199
|
+
def tag_note(version, pending)
|
|
200
|
+
return unless ci_publishes?(pending)
|
|
201
|
+
|
|
202
|
+
"CI then publishes @hecks/client #{version} to npm from the tag, which cannot be undone"
|
|
203
|
+
end
|
|
204
|
+
|
|
167
205
|
def publish_confirmed?(version, pending)
|
|
168
206
|
targets = []
|
|
169
207
|
targets << "hecks #{version} to rubygems.org" if pending.include?(:gem)
|
|
@@ -179,29 +217,42 @@ module Hecks
|
|
|
179
217
|
publish_gem(version, dry_run) if pending.include?(:gem)
|
|
180
218
|
return 1 if pending.include?(:npm) && !publish_npm!(version, dry_run, ci_publishes?(pending))
|
|
181
219
|
|
|
220
|
+
@verified = registries_list?(version, pending) unless dry_run
|
|
221
|
+
|
|
182
222
|
@console.say(dry_run ? "Dry run complete; nothing was tagged, pushed or published." : "Released hecks #{version}.")
|
|
183
223
|
0
|
|
184
224
|
end
|
|
185
225
|
|
|
186
226
|
def publish_gem(version, dry_run)
|
|
187
227
|
GemPublisher.new(root: @root, commands: @commands, console: @console).publish!(version, dry_run: dry_run)
|
|
228
|
+
@steps << :gem unless dry_run
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Asks each registry a step published to whether it now lists the version.
|
|
232
|
+
def registries_list?(version, pending)
|
|
233
|
+
(!pending.include?(:gem) || @published.gem?(version)) && (!pending.include?(:npm) || @published.npm?(version))
|
|
234
|
+
rescue Refusal
|
|
235
|
+
false
|
|
188
236
|
end
|
|
189
237
|
|
|
190
238
|
def publish_npm!(version, dry_run, via_ci)
|
|
191
239
|
return wait_for_ci!(version, dry_run) if via_ci
|
|
192
240
|
|
|
193
241
|
NpmPublisher.new(root: @root, commands: @commands, console: @console).publish!(version, dry_run: dry_run)
|
|
242
|
+
@steps << :npm unless dry_run
|
|
194
243
|
true
|
|
195
244
|
rescue CommandFailed => e
|
|
196
245
|
@console.warn("npm publish failed: #{e.message}")
|
|
197
246
|
unless dry_run
|
|
198
|
-
@console.warn("Finish the release with:
|
|
247
|
+
@console.warn("Finish the release with: hecks publish --npm-only --npm-local --confirm " \
|
|
248
|
+
"(published steps are not repeated)")
|
|
199
249
|
end
|
|
200
250
|
raise
|
|
201
251
|
end
|
|
202
252
|
|
|
203
253
|
def wait_for_ci!(version, dry_run)
|
|
204
254
|
arrived = @ci.wait!(version, dry_run: dry_run, wait: @options.wait?)
|
|
255
|
+
@steps << :npm if arrived && !dry_run
|
|
205
256
|
@console.warn(CiPublisher.timeout_hint(version)) unless arrived
|
|
206
257
|
arrived
|
|
207
258
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require_relative "../fqn"
|
|
2
|
-
require_relative "../
|
|
2
|
+
require_relative "../doors/handle"
|
|
3
3
|
|
|
4
4
|
module Hecks
|
|
5
5
|
class Router
|
|
@@ -100,12 +100,12 @@ module Hecks
|
|
|
100
100
|
|
|
101
101
|
target.define_singleton_method(:find) do |id|
|
|
102
102
|
found = dispatcher.registry.repository(domain, ir).find(id)
|
|
103
|
-
found &&
|
|
103
|
+
found && Doors::Handle.new(dispatcher: dispatcher, domain: domain, aggregate: ir, instance: found)
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
target.define_singleton_method(:all) do
|
|
107
107
|
dispatcher.registry.repository(domain, ir).all.map do |instance|
|
|
108
|
-
|
|
108
|
+
Doors::Handle.new(dispatcher: dispatcher, domain: domain, aggregate: ir, instance: instance)
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
111
|
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
require_relative "errors"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Runtime
|
|
5
|
+
# Finds the one adapter this boot loaded for a port name.
|
|
6
|
+
#
|
|
7
|
+
# Shared by the port-operation interpreter (an aggregate asking out) and the query interpreter
|
|
8
|
+
# (a query answered by a port), so both refuse the same way when nothing, or too much,
|
|
9
|
+
# implements the port.
|
|
10
|
+
module AdapterLookup
|
|
11
|
+
@stand_in = nil
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
# Modules whose instance methods every object inherits; a method they own answers no query.
|
|
16
|
+
INHERITED_OWNERS = [Object, Kernel, BasicObject].freeze
|
|
17
|
+
|
|
18
|
+
# Answers every port with a stand-in instead of its adapter while the block runs, for a
|
|
19
|
+
# caller that must not reach what the adapters reach (the fuzzer replays a domain whose
|
|
20
|
+
# adapters run shells and write files). Boot still checks the real adapters' classes; only
|
|
21
|
+
# what `call` hands out changes.
|
|
22
|
+
#
|
|
23
|
+
# @param stand_in [#call] answers `(port_name, asked)` with the object to ask instead
|
|
24
|
+
# @yield the work to run with the stand-in in place
|
|
25
|
+
# @return [Object] what the block answers
|
|
26
|
+
def standing_in(stand_in)
|
|
27
|
+
previous = @stand_in
|
|
28
|
+
@stand_in = stand_in
|
|
29
|
+
yield
|
|
30
|
+
ensure
|
|
31
|
+
@stand_in = previous
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param registry [Runtime::Registry] the booted registry whose adapters are searched
|
|
35
|
+
# @param port_name [String] the port's name as an adapter declares it
|
|
36
|
+
# @param asked [String] what is being asked, worded into a refusal
|
|
37
|
+
# @return [Object] a new instance of the port's only adapter, or the stand-in for it
|
|
38
|
+
# @raise [Runtime::WiringError] if no adapter, or more than one, implements the port
|
|
39
|
+
def call(registry, port_name, asked:)
|
|
40
|
+
adapter_class(registry, port_name, asked: asked)
|
|
41
|
+
return @stand_in.call(port_name, asked) if @stand_in
|
|
42
|
+
|
|
43
|
+
adapter_class(registry, port_name, asked: asked).new
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Finds the class of the one adapter this boot loaded for a port name, without building it,
|
|
47
|
+
# so boot can check what the adapter answers before anything asks it.
|
|
48
|
+
#
|
|
49
|
+
# @param registry [Runtime::Registry] the booted registry whose adapters are searched
|
|
50
|
+
# @param port_name [String] the port's name as an adapter declares it
|
|
51
|
+
# @param asked [String] what is being asked, worded into a refusal
|
|
52
|
+
# @return [Class] the port's only adapter
|
|
53
|
+
# @raise [Runtime::WiringError] if no adapter, or more than one, implements the port
|
|
54
|
+
def adapter_class(registry, port_name, asked:)
|
|
55
|
+
implementations = registry.adapters.values.select { |adapter| adapter.port == port_name }
|
|
56
|
+
|
|
57
|
+
case implementations.size
|
|
58
|
+
when 1 then Adapters.const_get(implementations.first.name)
|
|
59
|
+
when 0 then raise WiringError, "no adapter implements the #{port_name} port — nothing can answer #{asked}"
|
|
60
|
+
else raise WiringError,
|
|
61
|
+
"#{implementations.size} adapters implement the #{port_name} port " \
|
|
62
|
+
"(#{implementations.map(&:name).sort.join(', ')}) — the runtime will not choose for you"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Checks that an adapter class can be built with a bare `.new` and answers a query: it
|
|
67
|
+
# defines the method itself (not inherited from Object or Kernel, so a query named `hash` or
|
|
68
|
+
# `display` is not answered by them) and takes exactly the query's arguments as keywords.
|
|
69
|
+
#
|
|
70
|
+
# @param klass [Class] the adapter class {adapter_class} found
|
|
71
|
+
# @param port_name [String] the port's name, worded into a refusal
|
|
72
|
+
# @param method [String] the snake-cased query name the adapter is asked by
|
|
73
|
+
# @param query [Bluebook::Query] the declared query whose arguments the method must take
|
|
74
|
+
# @param asked [String] what is being asked, worded into a refusal
|
|
75
|
+
# @return [void]
|
|
76
|
+
# @raise [Runtime::WiringError] if the constructor needs arguments, the method is missing or
|
|
77
|
+
# inherited, or its keywords do not match the query's arguments
|
|
78
|
+
def check_answers!(klass, port_name, method, query, asked:)
|
|
79
|
+
required = klass.instance_method(:initialize).parameters.filter_map { |kind, name| name if %i[req keyreq].include?(kind) }
|
|
80
|
+
unless required.empty?
|
|
81
|
+
raise WiringError, "#{klass} implements the #{port_name} port but its constructor requires " \
|
|
82
|
+
"#{required.join(', ')} — it is built with no arguments"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
unless klass.public_method_defined?(method) &&
|
|
86
|
+
!INHERITED_OWNERS.include?(klass.public_instance_method(method).owner)
|
|
87
|
+
raise WiringError, "#{klass} implements the #{port_name} port but not ##{method}, " \
|
|
88
|
+
"which answers #{asked}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
check_keywords!(klass, method, query, asked)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @raise [Runtime::WiringError] if the method takes a positional argument, requires a keyword
|
|
95
|
+
# the query does not require, or accepts none of a declared argument
|
|
96
|
+
def check_keywords!(klass, method, query, asked)
|
|
97
|
+
parameters = klass.public_instance_method(method).parameters
|
|
98
|
+
wanted = query.attributes.map { |attribute| attribute.name.to_sym }
|
|
99
|
+
required = query.attributes.reject(&:optional?).map { |attribute| attribute.name.to_sym }
|
|
100
|
+
problems = keyword_problems(parameters, wanted, required)
|
|
101
|
+
return if problems.empty?
|
|
102
|
+
|
|
103
|
+
raise WiringError, "#{klass}##{method} answers #{asked} but #{problems.join(' and ')} — it is asked " \
|
|
104
|
+
"with keywords for the query's arguments (#{wanted.empty? ? 'none' : wanted.join(', ')})"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @return [Array<String>] how `parameters` fails to take exactly the `wanted` keywords
|
|
108
|
+
def keyword_problems(parameters, wanted, required)
|
|
109
|
+
kinds = parameters.map(&:first)
|
|
110
|
+
taken = parameters.select { |kind, _| kind.to_s.start_with?("key") && kind != :keyrest }.map(&:last)
|
|
111
|
+
missing = kinds.include?(:keyrest) ? [] : wanted - taken
|
|
112
|
+
extra = parameters.select { |kind, name| kind == :keyreq && !required.include?(name) }.map(&:last)
|
|
113
|
+
[("takes positional arguments" if kinds.intersect?(%i[req opt rest])),
|
|
114
|
+
keyword_problem("does not take", missing),
|
|
115
|
+
keyword_problem("requires", extra, ", which the query does not")].compact
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @return [String, nil] a phrase naming `names` as keywords, or nil when there are none
|
|
119
|
+
def keyword_problem(verb, names, suffix = "")
|
|
120
|
+
"#{verb} #{names.map { |name| "#{name}:" }.join(', ')}#{suffix}" unless names.empty?
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -30,6 +30,10 @@ module Hecks
|
|
|
30
30
|
@rules.resolve_source(mutation.source,
|
|
31
31
|
args)
|
|
32
32
|
end
|
|
33
|
+
# A plain `sets` of a list takes the whole list; only `append:` and `remove:` take
|
|
34
|
+
# one element.
|
|
35
|
+
list_attribute = aggregate.attribute(mutation.target)
|
|
36
|
+
Value.refuse_scalar_list(aggregate, list_attribute, value) if list_attribute && !mutation.source.is_a?(StateRef)
|
|
33
37
|
instance[mutation.target] = Value.for(aggregate, mutation.target, value)
|
|
34
38
|
when :append
|
|
35
39
|
instance[mutation.target] = appended(pre, aggregate, mutation, args)
|