hecks 2.7.0 → 2.9.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 +11 -0
- data/lib/hecks/adapters/driven/claude_code.rb +4 -104
- data/lib/hecks/adapters/driven/d1.rb +44 -237
- data/lib/hecks/adapters/driven/folder.rb +17 -76
- data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
- data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
- data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
- data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
- data/lib/hecks/adapters/driven/heki.rb +13 -42
- data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
- data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
- data/lib/hecks/adapters/driven/lambda.rb +10 -84
- data/lib/hecks/adapters/driven/local_storage.rb +5 -40
- data/lib/hecks/adapters/driven/memory.rb +18 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
- data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres.rb +145 -134
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +7 -30
- data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
- data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
- data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
- data/lib/hecks/adapters/driven/sqlite.rb +128 -65
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +15 -37
- data/lib/hecks/adapters/driven.rb +5 -36
- data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
- data/lib/hecks/behaviors/dsl.rb +7 -25
- data/lib/hecks/behaviors/expectations.rb +32 -142
- data/lib/hecks/behaviors/ir.rb +7 -20
- data/lib/hecks/behaviors/rspec.rb +6 -17
- data/lib/hecks/behaviors/runner.rb +10 -34
- data/lib/hecks/behaviors.rb +4 -8
- data/lib/hecks/bluebook/aggregate.rb +9 -75
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
- data/lib/hecks/bluebook/assembly/build.rb +4 -15
- data/lib/hecks/bluebook/assembly/contract.rb +15 -136
- data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
- data/lib/hecks/bluebook/assembly/marks.rb +31 -223
- data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
- data/lib/hecks/bluebook/assembly.rb +6 -45
- data/lib/hecks/bluebook/attribute.rb +12 -61
- data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
- data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
- data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
- data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
- data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
- data/lib/hecks/bluebook/behaviour/read_model.rb +38 -26
- data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
- data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
- data/lib/hecks/bluebook/capabilities.rb +17 -49
- data/lib/hecks/bluebook/chapter.rb +10 -48
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +14 -63
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +17 -65
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
- data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
- data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -189
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
- data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
- data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
- data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
- data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
- data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
- data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
- data/lib/hecks/bluebook/dsl/world_builder.rb +19 -106
- data/lib/hecks/bluebook/entity.rb +13 -71
- data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
- data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
- data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
- data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
- data/lib/hecks/bluebook/expression/resolver.rb +37 -660
- data/lib/hecks/bluebook/hexagon.rb +5 -28
- data/lib/hecks/bluebook/lifecycle.rb +5 -16
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
- data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
- data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +79 -349
- data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
- data/lib/hecks/bluebook/model_check.rb +102 -600
- data/lib/hecks/bluebook/pattern_subset.rb +11 -69
- data/lib/hecks/bluebook/process_manager.rb +13 -83
- data/lib/hecks/bluebook/project_loader.rb +3 -39
- data/lib/hecks/bluebook/project_register.rb +12 -60
- data/lib/hecks/bluebook/query.rb +9 -48
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +8 -98
- data/lib/hecks/bluebook/synthesizer.rb +13 -62
- data/lib/hecks/bluebook/translation.rb +26 -62
- data/lib/hecks/bluebook/value_object.rb +11 -36
- data/lib/hecks/bluebook.rb +5 -22
- data/lib/hecks/cache_dir.rb +84 -0
- data/lib/hecks/cli/document.rb +86 -0
- data/lib/hecks/cli/ir.rb +45 -0
- data/lib/hecks/cli/mcp.rb +31 -0
- data/lib/hecks/cli/mcp_door.rb +366 -0
- data/lib/hecks/cli/model_check.rb +203 -0
- data/lib/hecks/cli/project_cli.rb +90 -0
- data/lib/hecks/cli/project_diagrams.rb +36 -0
- data/lib/hecks/cli/run.rb +173 -0
- data/lib/hecks/cli/smoke_test.rb +45 -0
- data/lib/hecks/cli/stores.rb +53 -0
- data/lib/hecks/cli.rb +160 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
- data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
- data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
- data/lib/hecks/embryonaut_bluebook.rb +4 -71
- data/lib/hecks/facade/cli_door.rb +24 -106
- data/lib/hecks/facade/cli_runner.rb +28 -172
- data/lib/hecks/facade/command_request.rb +13 -33
- data/lib/hecks/facade/handle.rb +34 -121
- data/lib/hecks/facade/json_door.rb +31 -146
- data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
- data/lib/hecks/facade/surface/chapter.rb +10 -54
- data/lib/hecks/facade/surface.rb +8 -23
- data/lib/hecks/facade.rb +10 -25
- data/lib/hecks/forms/app.rb +15 -70
- data/lib/hecks/forms/command_form_renderer.rb +10 -29
- data/lib/hecks/forms/field_renderer.rb +17 -156
- data/lib/hecks/forms/field_shape.rb +30 -222
- data/lib/hecks/forms/html.rb +12 -47
- data/lib/hecks/forms/index_renderer.rb +1 -4
- data/lib/hecks/forms/page.rb +3 -9
- data/lib/hecks/forms/params.rb +14 -60
- data/lib/hecks/forms/port_argument.rb +2 -19
- data/lib/hecks/forms/query_form_renderer.rb +4 -79
- data/lib/hecks/forms/record_renderer.rb +8 -75
- data/lib/hecks/forms/record_table.rb +7 -50
- data/lib/hecks/forms/reference_options.rb +3 -30
- data/lib/hecks/forms/value_object_shape.rb +8 -35
- data/lib/hecks/forms.rb +5 -33
- data/lib/hecks/fqn.rb +5 -12
- data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
- data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
- data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
- data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
- data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
- data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
- data/lib/hecks/framework.rb +11 -80
- data/lib/hecks/freezer.rb +5 -39
- data/lib/hecks/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- data/lib/hecks/ir.rb +14 -83
- data/lib/hecks/language/adapter.bluebook +2 -7
- data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
- data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
- data/lib/hecks/language/bluebook/command.bluebook +27 -144
- data/lib/hecks/language/bluebook/entity.bluebook +24 -97
- data/lib/hecks/language/bluebook/policy.bluebook +16 -67
- data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
- data/lib/hecks/language/bluebook/projection.bluebook +126 -60
- data/lib/hecks/language/bluebook/query.bluebook +12 -43
- data/lib/hecks/language/bluebook/shape.bluebook +23 -73
- data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
- data/lib/hecks/language/bluebook/vocabulary.bluebook +98 -197
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
- data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
- data/lib/hecks/language/oidc.json +35 -0
- data/lib/hecks/language/port.bluebook +4 -17
- data/lib/hecks/language/translation/translation.bluebook +3 -9
- data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
- data/lib/hecks/language/world/wiring.bluebook +1 -3
- data/lib/hecks/language/world/world.bluebook +5 -7
- data/lib/hecks/literal.rb +8 -50
- data/lib/hecks/mcp_door_scope.rb +95 -0
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/ports/access_control.rb +7 -23
- data/lib/hecks/ports/agent/answers.rb +5 -23
- data/lib/hecks/ports/agent.rb +47 -172
- data/lib/hecks/ports/authentication.rb +12 -39
- data/lib/hecks/ports/authorization.rb +16 -43
- data/lib/hecks/ports/clock.rb +5 -45
- data/lib/hecks/ports/extraction.rb +7 -12
- data/lib/hecks/ports/identity_assignment.rb +8 -29
- data/lib/hecks/ports/identity_generation.rb +6 -29
- data/lib/hecks/ports/identity_resolution.rb +2 -10
- data/lib/hecks/ports/key_vault.rb +9 -45
- data/lib/hecks/ports/persistence/append_only.rb +62 -65
- data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
- data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
- data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
- data/lib/hecks/ports/persistence/plugin.rb +13 -42
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
- data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
- data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
- data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
- data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
- data/lib/hecks/ports/persistence/state_codec.rb +23 -103
- data/lib/hecks/ports/persistence.rb +7 -14
- data/lib/hecks/ports/projection.rb +42 -84
- data/lib/hecks/ports/query/in_memory.rb +10 -58
- data/lib/hecks/ports/query/ordering.rb +11 -32
- data/lib/hecks/ports/query.rb +14 -37
- data/lib/hecks/ports.rb +2 -8
- data/lib/hecks/projections/bootstrap_table.rb +11 -43
- data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
- data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
- data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
- data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
- data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
- data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
- data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
- data/lib/hecks/projections/deploy/fargate.rb +35 -202
- data/lib/hecks/projections/deploy/lambda.rb +151 -679
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
- data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
- data/lib/hecks/projections/deploy/preview/script.rb +2 -14
- data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
- data/lib/hecks/projections/deploy/preview/template.rb +2 -58
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +47 -146
- data/lib/hecks/projections/deploy/smoke.rb +4 -61
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
- data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
- data/lib/hecks/projections/deploy/template_diff.rb +5 -57
- data/lib/hecks/projections/diagrams.rb +20 -582
- data/lib/hecks/projections/glossary/html.rb +14 -130
- data/lib/hecks/projections/glossary/markdown.rb +8 -69
- data/lib/hecks/projections/glossary/mermaid.rb +9 -66
- data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
- data/lib/hecks/projections/glossary/sentences.rb +14 -159
- data/lib/hecks/projections/glossary.rb +30 -222
- data/lib/hecks/projections/ir.rb +3 -10
- data/lib/hecks/projections/model/deviations.rb +15 -42
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +9 -28
- data/lib/hecks/projections/reference.rb +9 -16
- data/lib/hecks/projections/rust_vocabulary.rb +12 -250
- data/lib/hecks/projections/shape.rb +3 -29
- data/lib/hecks/projections/statements.rb +7 -86
- data/lib/hecks/projections/vocabulary.rb +6 -22
- data/lib/hecks/projections.rb +2 -16
- data/lib/hecks/projector/cli_projector.rb +60 -375
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +46 -138
- data/lib/hecks/projector/ir_projector.rb +2 -9
- data/lib/hecks/projector/narrate_projector.rb +15 -189
- data/lib/hecks/projector/target.rb +13 -76
- data/lib/hecks/projector.rb +30 -121
- data/lib/hecks/query_specification/common/comparators.rb +5 -26
- data/lib/hecks/query_specification/common/comparison.rb +35 -143
- data/lib/hecks/query_specification/common/dsl.rb +8 -24
- data/lib/hecks/query_specification/common/null_policy.rb +20 -70
- data/lib/hecks/query_specification/common/options.rb +2 -6
- data/lib/hecks/query_specification/common/where_clause.rb +2 -10
- data/lib/hecks/query_specification/field_path.rb +37 -77
- data/lib/hecks/query_specification/hop_path.rb +12 -76
- data/lib/hecks/release/gem_pin.rb +2 -26
- data/lib/hecks/release/runner/ci_publisher.rb +93 -0
- data/lib/hecks/release/runner/commands.rb +66 -0
- data/lib/hecks/release/runner/console.rb +52 -0
- data/lib/hecks/release/runner/gem_publisher.rb +43 -0
- data/lib/hecks/release/runner/git.rb +48 -0
- data/lib/hecks/release/runner/npm_publisher.rb +75 -0
- data/lib/hecks/release/runner/preflight.rb +104 -0
- data/lib/hecks/release/runner/published.rb +55 -0
- data/lib/hecks/release/runner/tagger.rb +80 -0
- data/lib/hecks/release/runner.rb +215 -0
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +12 -36
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/aggregate_lock.rb +8 -33
- data/lib/hecks/runtime/boot_gates.rb +3 -18
- data/lib/hecks/runtime/caller.rb +19 -59
- data/lib/hecks/runtime/capability_graph.rb +5 -21
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
- data/lib/hecks/runtime/command_interpreter.rb +125 -307
- data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
- data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
- data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
- data/lib/hecks/runtime/command_rules/emission.rb +8 -21
- data/lib/hecks/runtime/command_rules/references.rb +23 -202
- data/lib/hecks/runtime/dependency_planning.rb +30 -102
- data/lib/hecks/runtime/dispatcher.rb +82 -241
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +65 -220
- data/lib/hecks/runtime/errors.rb +32 -115
- data/lib/hecks/runtime/event.rb +3 -25
- data/lib/hecks/runtime/identity.rb +18 -154
- data/lib/hecks/runtime/instance.rb +18 -150
- data/lib/hecks/runtime/interpreting.rb +11 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +48 -186
- data/lib/hecks/runtime/outbox.rb +38 -261
- data/lib/hecks/runtime/policy_interpreter.rb +38 -200
- data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
- data/lib/hecks/runtime/query_interpreter.rb +45 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
- data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
- data/lib/hecks/runtime/reference_hop.rb +8 -81
- data/lib/hecks/runtime/refusal_wording.rb +8 -89
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +52 -225
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +103 -244
- data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
- data/lib/hecks/runtime/routing.rb +4 -15
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
- data/lib/hecks/runtime/saga_interpreter.rb +79 -263
- data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
- data/lib/hecks/runtime/tenant_check.rb +6 -68
- data/lib/hecks/runtime/tenant_scope.rb +3 -28
- data/lib/hecks/runtime/value/admission.rb +16 -67
- data/lib/hecks/runtime/value/coercion.rb +155 -641
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value.rb +12 -84
- data/lib/hecks/runtime.rb +17 -65
- data/lib/hecks/storehouse.rb +146 -567
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- data/lib/hecks/vendoring/git_environment.rb +2 -8
- data/lib/hecks/vendoring/git_source.rb +4 -10
- data/lib/hecks/vendoring.rb +7 -46
- data/lib/hecks/version.rb +3 -14
- data/lib/hecks/vocabulary.rb +5 -0
- data/lib/hecks.rb +32 -82
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
- metadata +30 -59
- data/lib/hecks/bench/cli.rb +0 -74
- data/lib/hecks/bench/environment.rb +0 -100
- data/lib/hecks/bench/postgres_probe.rb +0 -41
- data/lib/hecks/bench/report.rb +0 -116
- data/lib/hecks/bench/ruby_runner.rb +0 -151
- data/lib/hecks/bench/run.rb +0 -55
- data/lib/hecks/bench/rust_runner.rb +0 -165
- data/lib/hecks/bench/stats.rb +0 -75
- data/lib/hecks/bench/suite.rb +0 -172
- data/lib/hecks/bench/workload.rb +0 -137
- data/lib/hecks/bench.rb +0 -28
- data/lib/hecks/codemod.rb +0 -443
- data/lib/hecks/corpus.rb +0 -479
- data/lib/hecks/doc/reference.rb +0 -585
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
- data/lib/hecks/fuzzing/combination_miner.rb +0 -178
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
- data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
- data/lib/hecks/fuzzing/differential.rb +0 -192
- data/lib/hecks/fuzzing/domain_generator.rb +0 -871
- data/lib/hecks/fuzzing/era_boundary.rb +0 -154
- data/lib/hecks/fuzzing/form_census.rb +0 -285
- data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
- data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
- data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
- data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
- data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
- data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
- data/lib/hecks/fuzzing/properties/guards.rb +0 -398
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
- data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
- data/lib/hecks/fuzzing/properties/querying.rb +0 -260
- data/lib/hecks/fuzzing/properties.rb +0 -267
- data/lib/hecks/fuzzing/qa_settings.rb +0 -164
- data/lib/hecks/fuzzing/replay.rb +0 -930
- data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
- data/lib/hecks/fuzzing/self_consistency.rb +0 -825
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
- data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
- data/lib/hecks/fuzzing/shrinker.rb +0 -309
- data/lib/hecks/fuzzing/structural_skips.rb +0 -53
- data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
- data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
- data/lib/hecks/fuzzing/value_generator.rb +0 -340
- data/lib/hecks/fuzzing.rb +0 -23
- data/lib/hecks/grammar/evolve.rb +0 -524
- data/lib/hecks/query_ir.rb +0 -482
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require "open3"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Release
|
|
5
|
+
class Runner
|
|
6
|
+
# Raised when a command a release step runs exits non-zero or cannot start.
|
|
7
|
+
class CommandFailed < StandardError; end
|
|
8
|
+
|
|
9
|
+
# Raised when a check refuses the release; the message names the fix.
|
|
10
|
+
class Refusal < StandardError; end
|
|
11
|
+
|
|
12
|
+
# Runs the release's external commands (git, gem, npm, curl, op) through
|
|
13
|
+
# any object answering `capture`/`run!`, so a spec injects a recorder.
|
|
14
|
+
class Commands
|
|
15
|
+
# What a captured command printed and whether it succeeded.
|
|
16
|
+
#
|
|
17
|
+
# @!attribute [r] stdout
|
|
18
|
+
# @return [String] standard output
|
|
19
|
+
# @!attribute [r] stderr
|
|
20
|
+
# @return [String] standard error, or the reason the command could not start
|
|
21
|
+
# @!attribute [r] success
|
|
22
|
+
# @return [Boolean] true when the command exited zero
|
|
23
|
+
Result = Struct.new(:stdout, :stderr, :success, keyword_init: true) do
|
|
24
|
+
# Says whether the command exited zero.
|
|
25
|
+
#
|
|
26
|
+
# @return [Boolean] true when it did
|
|
27
|
+
def success?
|
|
28
|
+
success
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Runs a command and returns what it printed, whatever its exit status.
|
|
33
|
+
#
|
|
34
|
+
# @param argv [Array<String>] the command and its arguments, never a shell string
|
|
35
|
+
# @param env [Hash{String => String, nil}] variables to set, or unset with nil
|
|
36
|
+
# @param chdir [String, nil] the directory to run in; the current one when nil
|
|
37
|
+
# @return [Result] the output and status; a command that cannot start is a failed result
|
|
38
|
+
def capture(*argv, env: {}, chdir: nil)
|
|
39
|
+
stdout, stderr, status = Open3.capture3(env, *argv, **directory(chdir))
|
|
40
|
+
Result.new(stdout: stdout, stderr: stderr, success: status.success?)
|
|
41
|
+
rescue SystemCallError => e
|
|
42
|
+
Result.new(stdout: "", stderr: e.message, success: false)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Runs a command with its output going straight to the terminal.
|
|
46
|
+
#
|
|
47
|
+
# @param argv [Array<String>] the command and its arguments, never a shell string
|
|
48
|
+
# @param env [Hash{String => String, nil}] variables to set, or unset with nil
|
|
49
|
+
# @param chdir [String, nil] the directory to run in; the current one when nil
|
|
50
|
+
# @return [true] when the command exits zero
|
|
51
|
+
# @raise [CommandFailed] if the command exits non-zero or cannot start
|
|
52
|
+
def run!(*argv, env: {}, chdir: nil)
|
|
53
|
+
return true if system(env, *argv, **directory(chdir))
|
|
54
|
+
|
|
55
|
+
raise CommandFailed, "`#{argv.first(2).join(' ')}` failed"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def directory(chdir)
|
|
61
|
+
chdir ? { chdir: chdir } : {}
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Release
|
|
3
|
+
class Runner
|
|
4
|
+
# Talks to the person running the release: progress lines, warnings and the
|
|
5
|
+
# y/N questions that come before anything irreversible.
|
|
6
|
+
class Console
|
|
7
|
+
# @param input [IO] where answers are read from
|
|
8
|
+
# @param out [IO] where progress goes
|
|
9
|
+
# @param err [IO] where refusals and failures go
|
|
10
|
+
# @param assume_yes [Boolean] answer every question yes without asking
|
|
11
|
+
def initialize(input:, out:, err:, assume_yes: false)
|
|
12
|
+
@input = input
|
|
13
|
+
@out = out
|
|
14
|
+
@err = err
|
|
15
|
+
@assume_yes = assume_yes
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Prints a progress line.
|
|
19
|
+
#
|
|
20
|
+
# @param text [String] the line
|
|
21
|
+
# @return [void]
|
|
22
|
+
def say(text)
|
|
23
|
+
@out.puts(text)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Prints a refusal or failure line.
|
|
27
|
+
#
|
|
28
|
+
# @param text [String] the line
|
|
29
|
+
# @return [void]
|
|
30
|
+
def warn(text)
|
|
31
|
+
@err.puts(text)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Asks a yes/no question, defaulting to no.
|
|
35
|
+
#
|
|
36
|
+
# @param question [String] what will happen if the answer is yes
|
|
37
|
+
# @return [Boolean] true for `y` or `yes`, or always under `assume_yes`; false on anything
|
|
38
|
+
# else, including a closed input
|
|
39
|
+
def confirm?(question)
|
|
40
|
+
if @assume_yes
|
|
41
|
+
say("#{question} yes (--yes)")
|
|
42
|
+
return true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@out.print("#{question} [y/N] ")
|
|
46
|
+
@out.flush
|
|
47
|
+
%w[y yes].include?(@input.gets.to_s.strip.downcase)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require_relative "commands"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Release
|
|
6
|
+
class Runner
|
|
7
|
+
# The gem step: hands the push to `bin/release_gem`, which builds and pushes
|
|
8
|
+
# it with the key 1Password holds; a dry run only builds it, then deletes it.
|
|
9
|
+
class GemPublisher
|
|
10
|
+
# @param root [String] the repository root
|
|
11
|
+
# @param commands [#run!] runs the build and the push
|
|
12
|
+
# @param console [Console] progress output
|
|
13
|
+
def initialize(root:, commands:, console:)
|
|
14
|
+
@root = root
|
|
15
|
+
@commands = commands
|
|
16
|
+
@console = console
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Pushes the gem, or with `dry_run` only builds it.
|
|
20
|
+
#
|
|
21
|
+
# @param version [String] the version being released
|
|
22
|
+
# @param dry_run [Boolean] build and delete the gem, pushing nothing
|
|
23
|
+
# @return [void]
|
|
24
|
+
# @raise [CommandFailed] if the build or the push fails
|
|
25
|
+
def publish!(version, dry_run:)
|
|
26
|
+
return build_only(version) if dry_run
|
|
27
|
+
|
|
28
|
+
@console.say("Publishing hecks #{version} to rubygems.org via bin/release_gem...")
|
|
29
|
+
@commands.run!(File.join(@root, "bin/release_gem"), chdir: @root)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def build_only(version)
|
|
35
|
+
@console.say("Dry run: building hecks-#{version}.gem (nothing is pushed)...")
|
|
36
|
+
@commands.run!("gem", "build", "hecks.gemspec", chdir: @root)
|
|
37
|
+
ensure
|
|
38
|
+
FileUtils.rm_f(File.join(@root, "hecks-#{version}.gem"))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "hecks/vendoring/git_environment"
|
|
2
|
+
require_relative "commands"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Release
|
|
6
|
+
class Runner
|
|
7
|
+
# The release's view of the repository: git run in the checkout, with the
|
|
8
|
+
# repository-pinning environment variables a git hook exports unset.
|
|
9
|
+
class Git
|
|
10
|
+
# @param root [String] the repository root
|
|
11
|
+
# @param commands [#capture, #run!] runs the git commands
|
|
12
|
+
def initialize(root:, commands:)
|
|
13
|
+
@root = root
|
|
14
|
+
@commands = commands
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Runs a git command and returns what it printed.
|
|
18
|
+
#
|
|
19
|
+
# @param args [Array<String>] git's arguments
|
|
20
|
+
# @return [String] standard output
|
|
21
|
+
# @raise [Refusal] if git exits non-zero
|
|
22
|
+
def read(*args)
|
|
23
|
+
result = capture(*args)
|
|
24
|
+
raise Refusal, "git #{args.first} failed: #{result.stderr.strip}" unless result.success?
|
|
25
|
+
|
|
26
|
+
result.stdout
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Runs a git command and returns the result whatever its status.
|
|
30
|
+
#
|
|
31
|
+
# @param args [Array<String>] git's arguments
|
|
32
|
+
# @return [Commands::Result] output and status
|
|
33
|
+
def capture(*)
|
|
34
|
+
@commands.capture("git", *, env: Vendoring::GitEnvironment.clean, chdir: @root)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Runs a git command that changes something, showing its output.
|
|
38
|
+
#
|
|
39
|
+
# @param args [Array<String>] git's arguments
|
|
40
|
+
# @return [true] when git exits zero
|
|
41
|
+
# @raise [CommandFailed] if git exits non-zero
|
|
42
|
+
def run!(*)
|
|
43
|
+
@commands.run!("git", *, env: Vendoring::GitEnvironment.clean, chdir: @root)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "tempfile"
|
|
2
|
+
require_relative "commands"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Release
|
|
6
|
+
class Runner
|
|
7
|
+
# The npm step: publishes `@hecks/client` from `packages/hecks-client`
|
|
8
|
+
# with the token 1Password holds.
|
|
9
|
+
class NpmPublisher
|
|
10
|
+
PACKAGE_DIR = "packages/hecks-client".freeze
|
|
11
|
+
ENV_FILE = "release/npm_publish.env".freeze
|
|
12
|
+
APPROVAL_NOTICE = "npm will print an approval link; open it and approve with your security key or passkey. " \
|
|
13
|
+
"This step waits for you.".freeze
|
|
14
|
+
USERCONFIG_LINE = "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}".freeze
|
|
15
|
+
|
|
16
|
+
def initialize(root:, commands:, console:)
|
|
17
|
+
@root = root
|
|
18
|
+
@commands = commands
|
|
19
|
+
@console = console
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Publishes the package, or with `dry_run` only packs it, which needs no
|
|
23
|
+
# credentials.
|
|
24
|
+
def publish!(version, dry_run:)
|
|
25
|
+
install_dependencies
|
|
26
|
+
if dry_run
|
|
27
|
+
@console.say("Dry run: npm publish --dry-run for @hecks/client #{version} (nothing is published)...")
|
|
28
|
+
@commands.run!("npm", "publish", "--dry-run", "--access", "public", chdir: package_dir)
|
|
29
|
+
else
|
|
30
|
+
@console.say("Publishing @hecks/client #{version} to npm (1Password will prompt for Touch ID)...")
|
|
31
|
+
@console.say(APPROVAL_NOTICE)
|
|
32
|
+
publish_with_token
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def package_dir
|
|
39
|
+
File.join(@root, PACKAGE_DIR)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# npm's `prepack` hook builds `dist/`; there is no separate build step here.
|
|
43
|
+
def install_dependencies
|
|
44
|
+
return if Dir.exist?(File.join(package_dir, "node_modules"))
|
|
45
|
+
|
|
46
|
+
@console.say("Installing the client's dependencies (npm ci)...")
|
|
47
|
+
@commands.run!("npm", "ci", chdir: package_dir)
|
|
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!
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "commands"
|
|
3
|
+
require_relative "git"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Release
|
|
7
|
+
class Runner
|
|
8
|
+
# The checks that run before a release does anything: tools installed, a
|
|
9
|
+
# clean current `main`, matching versions, and a changelog entry.
|
|
10
|
+
class Preflight
|
|
11
|
+
# What the checks learned; `sha` is also `origin/main`, enforced by check_current.
|
|
12
|
+
Facts = Struct.new(:version, :sha, keyword_init: true)
|
|
13
|
+
|
|
14
|
+
INSTALL_HINTS = {
|
|
15
|
+
"git" => "install git",
|
|
16
|
+
"gem" => "install Ruby, which ships gem",
|
|
17
|
+
"npm" => "install Node.js: brew install node",
|
|
18
|
+
"curl" => "install curl",
|
|
19
|
+
"op" => "install the 1Password CLI: brew install 1password-cli"
|
|
20
|
+
}.freeze
|
|
21
|
+
VERSION_LINE = /^\s*VERSION\s*=\s*"(?<version>[^"]+)"/
|
|
22
|
+
CLIENT_PACKAGE = "packages/hecks-client/package.json".freeze
|
|
23
|
+
|
|
24
|
+
def initialize(root:, commands:, git:, tools:)
|
|
25
|
+
@root = root
|
|
26
|
+
@commands = commands
|
|
27
|
+
@git = git
|
|
28
|
+
@tools = tools
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Runs every check; the first refusal stops it.
|
|
32
|
+
def check!
|
|
33
|
+
check_tools
|
|
34
|
+
check_branch
|
|
35
|
+
check_current
|
|
36
|
+
check_clean
|
|
37
|
+
version = declared_version
|
|
38
|
+
check_client_version(version)
|
|
39
|
+
check_changelog(version)
|
|
40
|
+
Facts.new(version: version, sha: git("rev-parse", "HEAD").strip)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def check_tools
|
|
46
|
+
@tools.each do |tool|
|
|
47
|
+
next if @commands.capture(tool, "--version").success?
|
|
48
|
+
|
|
49
|
+
raise Refusal, "#{tool} not found on PATH; #{INSTALL_HINTS.fetch(tool, "install #{tool}")}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def check_branch
|
|
54
|
+
branch = git("rev-parse", "--abbrev-ref", "HEAD").strip
|
|
55
|
+
return if branch == "main"
|
|
56
|
+
|
|
57
|
+
raise Refusal, "on branch #{branch}, not main; release from main: git checkout main"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def check_current
|
|
61
|
+
fetch = @git.capture("fetch", "origin")
|
|
62
|
+
raise Refusal, "git fetch origin failed (#{fetch.stderr.strip}); check the network and the remote" unless fetch.success?
|
|
63
|
+
|
|
64
|
+
head = git("rev-parse", "HEAD").strip
|
|
65
|
+
upstream = git("rev-parse", "origin/main").strip
|
|
66
|
+
return if head == upstream
|
|
67
|
+
|
|
68
|
+
raise Refusal, "main (#{head[0, 7]}) is not origin/main (#{upstream[0, 7]}); " \
|
|
69
|
+
"merge the release PR, then git pull --ff-only"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def check_clean
|
|
73
|
+
return if git("status", "--porcelain").strip.empty?
|
|
74
|
+
|
|
75
|
+
raise Refusal, "the working tree has uncommitted changes; commit or discard them before releasing"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def declared_version
|
|
79
|
+
match = File.read(File.join(@root, "lib/hecks/version.rb")).match(VERSION_LINE)
|
|
80
|
+
raise Refusal, "lib/hecks/version.rb declares no VERSION; restore it" unless match
|
|
81
|
+
|
|
82
|
+
match[:version]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def check_client_version(version)
|
|
86
|
+
client = JSON.parse(File.read(File.join(@root, CLIENT_PACKAGE))).fetch("version")
|
|
87
|
+
return if client == version
|
|
88
|
+
|
|
89
|
+
raise Refusal, "packages/hecks-client is at #{client} but Hecks::VERSION is #{version}; bump the package first."
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def check_changelog(version)
|
|
93
|
+
return if File.read(File.join(@root, "CHANGELOG.md")).match?(/^## \[#{Regexp.escape(version)}\]/)
|
|
94
|
+
|
|
95
|
+
raise Refusal, "CHANGELOG.md has no `## [#{version}]` heading; add the release entry in the release PR"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def git(*)
|
|
99
|
+
@git.read(*)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "commands"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Release
|
|
6
|
+
class Runner
|
|
7
|
+
# Asks the registries whether a version is already out, so a re-run of the
|
|
8
|
+
# release skips what an earlier run finished.
|
|
9
|
+
#
|
|
10
|
+
# Neither question needs credentials.
|
|
11
|
+
class Published
|
|
12
|
+
GEM_VERSIONS_URL = "https://rubygems.org/api/v1/versions/hecks.json".freeze
|
|
13
|
+
NPM_PACKAGE = "@hecks/client".freeze
|
|
14
|
+
|
|
15
|
+
# @param root [String] the repository root
|
|
16
|
+
# @param commands [#capture] runs curl and npm
|
|
17
|
+
def initialize(root:, commands:)
|
|
18
|
+
@root = root
|
|
19
|
+
@commands = commands
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Says whether RubyGems lists the hecks gem at a version.
|
|
23
|
+
#
|
|
24
|
+
# @param version [String] the version to look for
|
|
25
|
+
# @return [Boolean] true when it is published
|
|
26
|
+
# @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
|
|
38
|
+
|
|
39
|
+
# Says whether npm lists @hecks/client at a version.
|
|
40
|
+
#
|
|
41
|
+
# @param version [String] the version to look for
|
|
42
|
+
# @return [Boolean] true when it is published
|
|
43
|
+
# @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
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require_relative "commands"
|
|
2
|
+
require_relative "git"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Release
|
|
6
|
+
class Runner
|
|
7
|
+
# Makes sure the release tag `vX.Y.Z` is an annotated tag on the release
|
|
8
|
+
# commit and is on origin, creating and pushing it only when it is missing.
|
|
9
|
+
class Tagger
|
|
10
|
+
def initialize(git:, console:, dry_run:)
|
|
11
|
+
@git = git
|
|
12
|
+
@console = console
|
|
13
|
+
@dry_run = dry_run
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Brings the tag to the state described above, saying what it did; returns
|
|
17
|
+
# false only when the person declined the confirmation.
|
|
18
|
+
def ensure!(facts)
|
|
19
|
+
tag = "v#{facts.version}"
|
|
20
|
+
local = local_commit(tag)
|
|
21
|
+
remote = remote_commit(tag)
|
|
22
|
+
refuse_elsewhere(tag, facts.sha, local: local, remote: remote)
|
|
23
|
+
return true.tap { already_there(tag) } if remote
|
|
24
|
+
return true.tap { preview(tag, facts.sha, local) } if @dry_run
|
|
25
|
+
return false unless @console.confirm?(question(tag, facts.sha, local))
|
|
26
|
+
|
|
27
|
+
create(tag, facts.sha) unless local
|
|
28
|
+
push(tag)
|
|
29
|
+
true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def already_there(tag)
|
|
35
|
+
@console.say("Tag #{tag} is already on origin at the release commit; skipping.")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def preview(tag, sha, local)
|
|
39
|
+
@console.say("Would create annotated tag #{tag} at #{sha[0, 7]}.") unless local
|
|
40
|
+
@console.say("Would push #{tag} to origin.")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def question(tag, sha, local)
|
|
44
|
+
verb = local ? "Push" : "Create and push"
|
|
45
|
+
"#{verb} annotated tag #{tag} at #{sha[0, 7]} to origin?"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def create(tag, sha)
|
|
49
|
+
@git.run!("tag", "-a", tag, "-m", "Release #{tag.delete_prefix('v')}", sha)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def push(tag)
|
|
53
|
+
@git.run!("push", "origin", tag)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def refuse_elsewhere(tag, sha, local:, remote:)
|
|
57
|
+
{ "locally" => local, "on origin" => remote }.each do |where, commit|
|
|
58
|
+
next if commit.nil? || commit == sha
|
|
59
|
+
|
|
60
|
+
raise Refusal, "tag #{tag} already points at #{commit[0, 7]} #{where}, not at the release commit " \
|
|
61
|
+
"#{sha[0, 7]}; if that tag was never released, delete it (git tag -d #{tag}; " \
|
|
62
|
+
"git push origin :refs/tags/#{tag}) and re-run, otherwise main has moved since the release " \
|
|
63
|
+
"and the missing package must be published from a checkout of #{tag}"
|
|
64
|
+
end
|
|
65
|
+
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
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|