domainforge 0.13.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.
- package/.cargo/config.toml +6 -0
- package/.claude/settings.local.json +18 -0
- package/.coderabbit.yml +43 -0
- package/.codex/skills/release-management/SKILL.md +151 -0
- package/.codex/skills/release-management/agents/openai.yaml +4 -0
- package/.github/actions/decrypt-secrets/action.yml +121 -0
- package/.github/agents/Coder.agent.md +97 -0
- package/.github/agents/DeepResearch.agent.md +61 -0
- package/.github/chatmodes/tdd.vibepro.chatmode.md +1183 -0
- package/.github/copilot-instructions.md +13 -0
- package/.github/dependabot.yml +68 -0
- package/.github/workflows/README.md +165 -0
- package/.github/workflows/ci.yml +335 -0
- package/.github/workflows/dependabot-automerge.yml +114 -0
- package/.github/workflows/dependency-review.yml +27 -0
- package/.github/workflows/deploy.yml +87 -0
- package/.github/workflows/prepare-release.yml +168 -0
- package/.github/workflows/release-crates.yml +42 -0
- package/.github/workflows/release-npm.yml +137 -0
- package/.github/workflows/release-please.yml +29 -0
- package/.github/workflows/release-pypi.yml +96 -0
- package/.gitkeep +1 -0
- package/.release-please-manifest.json +5 -0
- package/.sea-registry.toml +10 -0
- package/.serena/project.yml +133 -0
- package/.sops.yaml +10 -0
- package/AGENTS.md +216 -0
- package/CHANGELOG.md +400 -0
- package/CLAUDE.md +62 -0
- package/CONTRIBUTING.md +323 -0
- package/Cargo.lock +3612 -0
- package/Cargo.toml +12 -0
- package/LICENSE +201 -0
- package/README.md +660 -0
- package/README_PYTHON.md +256 -0
- package/README_TYPESCRIPT.md +305 -0
- package/README_WASM.md +329 -0
- package/RELEASE_NOTES.md +41 -0
- package/bun.lock +378 -0
- package/bunfig.toml +11 -0
- package/check_output.txt +83 -0
- package/clippy_output.txt +80 -0
- package/commitlint.config.cjs +8 -0
- package/deny.toml +42 -0
- package/devbox.json +14 -0
- package/devbox.lock +76 -0
- package/docs/RELEASE_PROCESS.md +360 -0
- package/docs/diagnostics.md +161 -0
- package/docs/doc_guidelines.md +53 -0
- package/docs/explanations/README.md +21 -0
- package/docs/explanations/architecture-overview.md +109 -0
- package/docs/explanations/cross-language-binding-strategy.md +68 -0
- package/docs/explanations/graph-store-design.md +47 -0
- package/docs/explanations/performance-benchmarks.md +63 -0
- package/docs/explanations/policy-evaluation-logic.md +106 -0
- package/docs/explanations/semantic-modeling-concepts.md +109 -0
- package/docs/explanations/three-valued-logic.md +66 -0
- package/docs/explanations/versioning-strategy.md +45 -0
- package/docs/governance.md +168 -0
- package/docs/how-tos/README.md +46 -0
- package/docs/how-tos/ci-cd-validation.md +93 -0
- package/docs/how-tos/create-custom-units.md +125 -0
- package/docs/how-tos/define-policies.md +119 -0
- package/docs/how-tos/export-to-calm.md +110 -0
- package/docs/how-tos/export-to-protobuf.md +312 -0
- package/docs/how-tos/extend-grammar.md +133 -0
- package/docs/how-tos/generate-rdf-turtle.md +106 -0
- package/docs/how-tos/import-from-calm.md +114 -0
- package/docs/how-tos/import-from-sbvr.md +249 -0
- package/docs/how-tos/install-cli.md +126 -0
- package/docs/how-tos/parse-sea-files.md +132 -0
- package/docs/how-tos/policy-evaluation-modes.md +30 -0
- package/docs/how-tos/run-cross-language-tests.md +115 -0
- package/docs/how-tos/troubleshoot-napi-builds.md +55 -0
- package/docs/how-tos/use-modules-imports.md +285 -0
- package/docs/index.md +13 -0
- package/docs/plans/canonical-normalizer.md +121 -0
- package/docs/plans/cd_improvement.md +112 -0
- package/docs/plans/cli-ast.md +29 -0
- package/docs/plans/expression-bindings-and-normalizer-integration.md +174 -0
- package/docs/plans/protobuf_advanced_features_plan.md +597 -0
- package/docs/plans/protobuf_plan.yml +525 -0
- package/docs/plans/refactor_dsl_architecture.md +131 -0
- package/docs/plans/release-plan.md +163 -0
- package/docs/plans/sea_fmt_implementation_plan.md +516 -0
- package/docs/playbooks/README.md +18 -0
- package/docs/playbooks/adding-new-primitive.md +68 -0
- package/docs/playbooks/debugging-parser-failures.md +42 -0
- package/docs/playbooks/local-release-preparation.md +139 -0
- package/docs/playbooks/migrating-schema-versions.md +43 -0
- package/docs/playbooks/onboarding-contributors.md +64 -0
- package/docs/playbooks/releasing-beta.md +86 -0
- package/docs/playbooks/secret-management.md +64 -0
- package/docs/reference/README.md +199 -0
- package/docs/reference/ast-json-api.md +427 -0
- package/docs/reference/calm-mapping.md +519 -0
- package/docs/reference/cli-commands.md +588 -0
- package/docs/reference/configuration.md +202 -0
- package/docs/reference/error-codes.md +664 -0
- package/docs/reference/generated-artifacts-policy.md +53 -0
- package/docs/reference/grammar-spec.md +255 -0
- package/docs/reference/primitives-api.md +317 -0
- package/docs/reference/protobuf-api.md +426 -0
- package/docs/reference/python-api.md +485 -0
- package/docs/reference/registry.md +50 -0
- package/docs/reference/sea-dsl-ai-cheatsheet.yaml +913 -0
- package/docs/reference/security-model.md +74 -0
- package/docs/reference/typescript-api.md +508 -0
- package/docs/reference/wasm-api.md +420 -0
- package/docs/semantic-pack-review.md +144 -0
- package/docs/semantic-pack-signing.md +234 -0
- package/docs/semantic-packs.md +284 -0
- package/docs/specs/ADR-001-sea-dsl-semantic-source-of-truth.md +33 -0
- package/docs/specs/ADR-002-projection-first-class-construct.md +50 -0
- package/docs/specs/ADR-003-protobuf-projection-target.md +51 -0
- package/docs/specs/ADR-004-projection-compatibility-semantics.md +57 -0
- package/docs/specs/ADR-005-multi-language-support-strategy.md +112 -0
- package/docs/specs/ADR-006-error-handling-strategy.md +115 -0
- package/docs/specs/ADR-007-policy-evaluation-engine.md +95 -0
- package/docs/specs/ADR-008-knowledge-graph-integration.md +90 -0
- package/docs/specs/ADR-009-module-resolution-strategy.md +115 -0
- package/docs/specs/ADR-010-unit-system.md +106 -0
- package/docs/specs/PRD-001-sea-projection-framework.md +155 -0
- package/docs/specs/PRD-002-sea-cli-tooling.md +169 -0
- package/docs/specs/PRD-003-dsl-core-capabilities.md +275 -0
- package/docs/specs/README.md +62 -0
- package/docs/specs/SDS-001-protobuf-projection-engine.md +451 -0
- package/docs/specs/SDS-002-sea-core-architecture.md +268 -0
- package/docs/specs/SDS-003-parser-semantic-graph.md +377 -0
- package/docs/specs/SDS-004-policy-engine-design.md +362 -0
- package/docs/specs/SDS-005-knowledge-graph-module.md +364 -0
- package/docs/specs/SDS-006-calm-integration.md +367 -0
- package/docs/specs/SDS-007-sbvr-import.md +347 -0
- package/docs/templates/template_explanation.md +14 -0
- package/docs/templates/template_howto.md +21 -0
- package/docs/templates/template_playbook.md +21 -0
- package/docs/templates/template_reference.md +17 -0
- package/docs/templates/template_tutorial.md +24 -0
- package/docs/tutorials/README.md +12 -0
- package/docs/tutorials/first-sea-model.md +85 -0
- package/docs/tutorials/getting-started.md +98 -0
- package/docs/tutorials/python-binding-quickstart.md +107 -0
- package/docs/tutorials/typescript-binding-quickstart.md +91 -0
- package/docs/tutorials/wasm-in-browser.md +75 -0
- package/domainforge-core/CHANGELOG.md +138 -0
- package/domainforge-core/Cargo.toml +101 -0
- package/domainforge-core/MIGRATING.md +32 -0
- package/domainforge-core/README.md +197 -0
- package/domainforge-core/benchmark_results.txt +51 -0
- package/domainforge-core/build.rs +6 -0
- package/domainforge-core/deny.toml +31 -0
- package/domainforge-core/docs/specs/projections/sbvr_kg_mapping.md +43 -0
- package/domainforge-core/examples/basic.sea +7 -0
- package/domainforge-core/examples/cli/import_export_workflow.sh +38 -0
- package/domainforge-core/examples/cli/validate_example.sh +30 -0
- package/domainforge-core/examples/evolution_semantics.sea +31 -0
- package/domainforge-core/examples/parser_demo.rs +203 -0
- package/domainforge-core/grammar/sea.pest +408 -0
- package/domainforge-core/schemas/calm-v1.schema.json +170 -0
- package/domainforge-core/schemas/shacl/sea_shapes.ttl +19 -0
- package/domainforge-core/src/authority/compiler.rs +309 -0
- package/domainforge-core/src/authority/environment.rs +203 -0
- package/domainforge-core/src/authority/error.rs +164 -0
- package/domainforge-core/src/authority/fact_resolver.rs +224 -0
- package/domainforge-core/src/authority/mod.rs +25 -0
- package/domainforge-core/src/authority/pack.rs +133 -0
- package/domainforge-core/src/authority/policy.rs +224 -0
- package/domainforge-core/src/authority/resolver.rs +446 -0
- package/domainforge-core/src/authority/trace.rs +217 -0
- package/domainforge-core/src/authority/transform.rs +168 -0
- package/domainforge-core/src/authority/types.rs +617 -0
- package/domainforge-core/src/bin/domainforge.rs +25 -0
- package/domainforge-core/src/calm/export.rs +538 -0
- package/domainforge-core/src/calm/import.rs +1220 -0
- package/domainforge-core/src/calm/mod.rs +9 -0
- package/domainforge-core/src/calm/models.rs +108 -0
- package/domainforge-core/src/calm/sbvr_import.rs +9 -0
- package/domainforge-core/src/cli/authority.rs +149 -0
- package/domainforge-core/src/cli/format.rs +85 -0
- package/domainforge-core/src/cli/import.rs +133 -0
- package/domainforge-core/src/cli/mod.rs +64 -0
- package/domainforge-core/src/cli/normalize.rs +180 -0
- package/domainforge-core/src/cli/pack.rs +904 -0
- package/domainforge-core/src/cli/parse.rs +112 -0
- package/domainforge-core/src/cli/project.rs +294 -0
- package/domainforge-core/src/cli/registry.rs +41 -0
- package/domainforge-core/src/cli/test.rs +12 -0
- package/domainforge-core/src/cli/validate.rs +195 -0
- package/domainforge-core/src/cli/validate_kg.rs +80 -0
- package/domainforge-core/src/concept_id.rs +89 -0
- package/domainforge-core/src/error/diagnostics.rs +426 -0
- package/domainforge-core/src/error/fuzzy.rs +253 -0
- package/domainforge-core/src/error/mod.rs +13 -0
- package/domainforge-core/src/formatter/comments.rs +223 -0
- package/domainforge-core/src/formatter/config.rs +114 -0
- package/domainforge-core/src/formatter/mod.rs +22 -0
- package/domainforge-core/src/formatter/printer.rs +906 -0
- package/domainforge-core/src/graph/mod.rs +858 -0
- package/domainforge-core/src/graph/to_ast.rs +66 -0
- package/domainforge-core/src/kg.rs +1476 -0
- package/domainforge-core/src/kg_import.rs +251 -0
- package/domainforge-core/src/lib.rs +203 -0
- package/domainforge-core/src/module/mod.rs +1 -0
- package/domainforge-core/src/module/resolver.rs +260 -0
- package/domainforge-core/src/parser/ast.rs +2919 -0
- package/domainforge-core/src/parser/ast_convert.rs +494 -0
- package/domainforge-core/src/parser/ast_schema.rs +491 -0
- package/domainforge-core/src/parser/error.rs +291 -0
- package/domainforge-core/src/parser/lint.rs +39 -0
- package/domainforge-core/src/parser/mod.rs +193 -0
- package/domainforge-core/src/parser/printer.rs +702 -0
- package/domainforge-core/src/parser/profiles.rs +71 -0
- package/domainforge-core/src/parser/string_utils.rs +138 -0
- package/domainforge-core/src/patterns.rs +68 -0
- package/domainforge-core/src/policy/core.rs +1148 -0
- package/domainforge-core/src/policy/expression.rs +399 -0
- package/domainforge-core/src/policy/mod.rs +18 -0
- package/domainforge-core/src/policy/normalize.rs +1028 -0
- package/domainforge-core/src/policy/quantifier.rs +940 -0
- package/domainforge-core/src/policy/three_valued.rs +140 -0
- package/domainforge-core/src/policy/three_valued_microbench.rs +104 -0
- package/domainforge-core/src/policy/type_inference.rs +67 -0
- package/domainforge-core/src/policy/violation.rs +36 -0
- package/domainforge-core/src/primitives/concept_change.rs +61 -0
- package/domainforge-core/src/primitives/entity.rs +224 -0
- package/domainforge-core/src/primitives/flow.rs +111 -0
- package/domainforge-core/src/primitives/instance.rs +93 -0
- package/domainforge-core/src/primitives/mapping_contract.rs +50 -0
- package/domainforge-core/src/primitives/metric.rs +79 -0
- package/domainforge-core/src/primitives/mod.rs +25 -0
- package/domainforge-core/src/primitives/projection_contract.rs +50 -0
- package/domainforge-core/src/primitives/quantity.rs +56 -0
- package/domainforge-core/src/primitives/relation.rs +68 -0
- package/domainforge-core/src/primitives/resource.rs +237 -0
- package/domainforge-core/src/primitives/resource_instance.rs +88 -0
- package/domainforge-core/src/primitives/role.rs +49 -0
- package/domainforge-core/src/projection/buf.rs +404 -0
- package/domainforge-core/src/projection/contracts.rs +22 -0
- package/domainforge-core/src/projection/engine.rs +19 -0
- package/domainforge-core/src/projection/mod.rs +16 -0
- package/domainforge-core/src/projection/protobuf.rs +3331 -0
- package/domainforge-core/src/projection/registry.rs +43 -0
- package/domainforge-core/src/python/authority.rs +253 -0
- package/domainforge-core/src/python/error.rs +227 -0
- package/domainforge-core/src/python/formatter.rs +86 -0
- package/domainforge-core/src/python/graph.rs +366 -0
- package/domainforge-core/src/python/mod.rs +9 -0
- package/domainforge-core/src/python/policy.rs +651 -0
- package/domainforge-core/src/python/primitives.rs +796 -0
- package/domainforge-core/src/python/registry.rs +98 -0
- package/domainforge-core/src/python/semantic_pack.rs +619 -0
- package/domainforge-core/src/python/units.rs +96 -0
- package/domainforge-core/src/registry/mod.rs +432 -0
- package/domainforge-core/src/registry/tests.rs +210 -0
- package/domainforge-core/src/sbvr.rs +744 -0
- package/domainforge-core/src/semantic_pack/builder.rs +470 -0
- package/domainforge-core/src/semantic_pack/canonical_json.rs +184 -0
- package/domainforge-core/src/semantic_pack/diagnostics.rs +214 -0
- package/domainforge-core/src/semantic_pack/diff.rs +216 -0
- package/domainforge-core/src/semantic_pack/mod.rs +31 -0
- package/domainforge-core/src/semantic_pack/pack_set.rs +240 -0
- package/domainforge-core/src/semantic_pack/resolver.rs +437 -0
- package/domainforge-core/src/semantic_pack/review.rs +125 -0
- package/domainforge-core/src/semantic_pack/schema.rs +342 -0
- package/domainforge-core/src/semantic_pack/signing.rs +105 -0
- package/domainforge-core/src/semantic_pack/validator.rs +368 -0
- package/domainforge-core/src/semantic_version.rs +140 -0
- package/domainforge-core/src/test_utils.rs +12 -0
- package/domainforge-core/src/typescript/authority.rs +184 -0
- package/domainforge-core/src/typescript/error.rs +146 -0
- package/domainforge-core/src/typescript/formatter.rs +76 -0
- package/domainforge-core/src/typescript/graph.rs +391 -0
- package/domainforge-core/src/typescript/mod.rs +9 -0
- package/domainforge-core/src/typescript/policy.rs +564 -0
- package/domainforge-core/src/typescript/primitives.rs +784 -0
- package/domainforge-core/src/typescript/registry.rs +88 -0
- package/domainforge-core/src/typescript/semantic_pack.rs +470 -0
- package/domainforge-core/src/typescript/units.rs +76 -0
- package/domainforge-core/src/units/mod.rs +462 -0
- package/domainforge-core/src/uuid_module.rs +42 -0
- package/domainforge-core/src/validation_error.rs +818 -0
- package/domainforge-core/src/validation_result.rs +30 -0
- package/domainforge-core/src/wasm/authority.rs +192 -0
- package/domainforge-core/src/wasm/error.rs +145 -0
- package/domainforge-core/src/wasm/formatter.rs +69 -0
- package/domainforge-core/src/wasm/graph.rs +471 -0
- package/domainforge-core/src/wasm/mod.rs +16 -0
- package/domainforge-core/src/wasm/policy.rs +607 -0
- package/domainforge-core/src/wasm/primitives.rs +295 -0
- package/domainforge-core/src/wasm/semantic_pack.rs +471 -0
- package/domainforge-core/src/wasm/units.rs +62 -0
- package/domainforge-core/std/aws.sea +6 -0
- package/domainforge-core/std/core.sea +6 -0
- package/domainforge-core/std/http.sea +27 -0
- package/domainforge-core/tests/aggregation_enhanced_tests.rs +162 -0
- package/domainforge-core/tests/aggregation_eval_tests.rs +248 -0
- package/domainforge-core/tests/aggregation_integration_tests.rs +379 -0
- package/domainforge-core/tests/aggregation_parser_tests.rs +92 -0
- package/domainforge-core/tests/aggregation_tests.rs +102 -0
- package/domainforge-core/tests/authority_conformance_tests.rs +1173 -0
- package/domainforge-core/tests/calm_round_trip_tests.rs +283 -0
- package/domainforge-core/tests/calm_schema_validation_tests.rs +137 -0
- package/domainforge-core/tests/cast_operator_tests.rs +85 -0
- package/domainforge-core/tests/cli_binary_check.rs +37 -0
- package/domainforge-core/tests/cli_import_tests.rs +291 -0
- package/domainforge-core/tests/cli_path_traversal_tests.rs +124 -0
- package/domainforge-core/tests/cli_tests.rs +63 -0
- package/domainforge-core/tests/diagnostics_tests.rs +203 -0
- package/domainforge-core/tests/dimension_unit_tests.rs +80 -0
- package/domainforge-core/tests/entity_tests.rs +69 -0
- package/domainforge-core/tests/evolution_semantics_tests.rs +157 -0
- package/domainforge-core/tests/flow_tests.rs +78 -0
- package/domainforge-core/tests/flow_unit_validation_tests.rs +31 -0
- package/domainforge-core/tests/graph_integration_tests.rs +218 -0
- package/domainforge-core/tests/graph_tests.rs +626 -0
- package/domainforge-core/tests/import_parsing_tests.rs +23 -0
- package/domainforge-core/tests/instance_integration_tests.rs +98 -0
- package/domainforge-core/tests/instance_parsing_tests.rs +58 -0
- package/domainforge-core/tests/instance_tests.rs +61 -0
- package/domainforge-core/tests/kg_uri_encoding_tests.rs +53 -0
- package/domainforge-core/tests/lint_tests.rs +19 -0
- package/domainforge-core/tests/metric_tests.rs +143 -0
- package/domainforge-core/tests/module_resolution_tests.rs +100 -0
- package/domainforge-core/tests/namespace_registry_tests.rs +247 -0
- package/domainforge-core/tests/null_handling_tests.rs +26 -0
- package/domainforge-core/tests/parser_ast_v3.rs +53 -0
- package/domainforge-core/tests/parser_dimension_registry_tests.rs +20 -0
- package/domainforge-core/tests/parser_integration_tests.rs +294 -0
- package/domainforge-core/tests/parser_metadata_tests.rs +97 -0
- package/domainforge-core/tests/parser_resource_domain_only_graph_test.rs +21 -0
- package/domainforge-core/tests/parser_resource_limits_tests.rs +122 -0
- package/domainforge-core/tests/parser_tests.rs +512 -0
- package/domainforge-core/tests/pattern_semantics_tests.rs +87 -0
- package/domainforge-core/tests/phase_14_determinism_tests.rs +166 -0
- package/domainforge-core/tests/phase_15_validation_error_tests.rs +136 -0
- package/domainforge-core/tests/phase_16_unicode_tests.rs +248 -0
- package/domainforge-core/tests/phase_17_export_tests.rs +285 -0
- package/domainforge-core/tests/phase_17_round_trip_tests.rs +264 -0
- package/domainforge-core/tests/policy_tests.rs +635 -0
- package/domainforge-core/tests/primitives_integration_tests.rs +151 -0
- package/domainforge-core/tests/print_rdf_xml.rs +14 -0
- package/domainforge-core/tests/printer_tests.rs +204 -0
- package/domainforge-core/tests/profile_tests.rs +35 -0
- package/domainforge-core/tests/projection_contracts_tests.rs +154 -0
- package/domainforge-core/tests/protobuf_projection_tests.rs +199 -0
- package/domainforge-core/tests/quantity_tests.rs +41 -0
- package/domainforge-core/tests/rdf_xml_typed_literal_tests.rs +105 -0
- package/domainforge-core/tests/registry_schema_tests.rs +33 -0
- package/domainforge-core/tests/resource_tests.rs +50 -0
- package/domainforge-core/tests/resource_unit_tests.rs +24 -0
- package/domainforge-core/tests/roles_relations_tests.rs +61 -0
- package/domainforge-core/tests/round_trip_tests.rs +34 -0
- package/domainforge-core/tests/runtime_toggle_tests.rs +70 -0
- package/domainforge-core/tests/sbvr_fact_schema_tests.rs +60 -0
- package/domainforge-core/tests/sbvr_flow_facts_tests.rs +55 -0
- package/domainforge-core/tests/sbvr_parsing_tests.rs +53 -0
- package/domainforge-core/tests/semantic_pack_alias_resolution.rs +197 -0
- package/domainforge-core/tests/semantic_pack_build.rs +302 -0
- package/domainforge-core/tests/semantic_pack_consumer_smoke.rs +150 -0
- package/domainforge-core/tests/semantic_pack_pack_set.rs +160 -0
- package/domainforge-core/tests/semantic_pack_signing.rs +157 -0
- package/domainforge-core/tests/semantic_pack_three_valued.rs +250 -0
- package/domainforge-core/tests/semantic_pack_validate.rs +196 -0
- package/domainforge-core/tests/std_lib_tests.rs +37 -0
- package/domainforge-core/tests/temporal_evaluation_tests.rs +159 -0
- package/domainforge-core/tests/temporal_semantics_tests.rs +214 -0
- package/domainforge-core/tests/three_valued_quantifiers_tests.rs +164 -0
- package/domainforge-core/tests/turtle_entity_export_tests.rs +38 -0
- package/domainforge-core/tests/turtle_escaping_tests.rs +53 -0
- package/domainforge-core/tests/turtle_resource_export_tests.rs +34 -0
- package/domainforge-core/tests/type_inference_tests.rs +40 -0
- package/domainforge-core/tests/unicode_validation_tests.rs +169 -0
- package/domainforge-core/tests/unit_tests.rs +81 -0
- package/domainforge-core/tests/validate_tests.rs +38 -0
- package/domainforge-core/tests/validation_unit_mismatch_tests.rs +83 -0
- package/domainforge-core/tests/wasm_tests.rs +229 -0
- package/domainforge-python/CHANGELOG-python.md +12 -0
- package/domainforge-python/MIGRATING.md +24 -0
- package/domainforge-python/README.md +256 -0
- package/domainforge-python/domainforge/__init__.py +95 -0
- package/domainforge-python/domainforge/domainforge.pyi +519 -0
- package/domainforge-python/pyproject.toml +36 -0
- package/domainforge-typescript/CHANGELOG-typescript.md +12 -0
- package/domainforge-typescript/LICENSE +201 -0
- package/domainforge-typescript/MIGRATING.md +24 -0
- package/domainforge-typescript/README.md +305 -0
- package/domainforge-typescript/index.d.ts +452 -0
- package/domainforge-typescript/index.js +361 -0
- package/domainforge-typescript/package.json +60 -0
- package/example.js +61 -0
- package/examples/browser.html +366 -0
- package/examples/namespaces/finance/cashflow.sea +5 -0
- package/examples/namespaces/logistics/core.sea +7 -0
- package/examples/observability_metrics.sea +38 -0
- package/fixtures/semantic_packs/acme_procurement/domain/entities.sea +39 -0
- package/fixtures/semantic_packs/acme_procurement/domain/metrics.sea +11 -0
- package/fixtures/semantic_packs/acme_procurement/domain/relations.sea +7 -0
- package/fixtures/semantic_packs/acme_procurement/domain/resources.sea +9 -0
- package/fixtures/semantic_packs/acme_procurement/review/acme.procurement.semantic-review.jsonl +7 -0
- package/fixtures/semantic_packs/acme_procurement/tests/ambiguous_vendor_alias.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/deprecated_vendor_alias.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/invalid_relation.sea +3 -0
- package/fixtures/semantic_packs/acme_procurement/tests/proposed_concept.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/rejected_concept.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/unit_mismatch.sea +7 -0
- package/fixtures/semantic_packs/acme_procurement/tests/unknown_vendor_policy.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/valid_purchase_policy.sea +8 -0
- package/index.d.ts +2 -0
- package/index.js +8 -0
- package/justfile +200 -0
- package/lefthook.yml +13 -0
- package/lib/validate_native_exports.d.ts +4 -0
- package/lib/validate_native_exports.js +12 -0
- package/package.json +22 -0
- package/pytest.ini +5 -0
- package/python/tests/test_registry.py +75 -0
- package/python/tests/test_units.py +18 -0
- package/release-please-config.json +49 -0
- package/requirements-dev.txt +3 -0
- package/requirements.txt +3 -0
- package/rust-toolchain.toml +3 -0
- package/schemas/ast-v1.schema.json +72 -0
- package/schemas/ast-v2.schema.json +1200 -0
- package/schemas/ast-v3.schema.json +1200 -0
- package/schemas/sea-registry.schema.json +45 -0
- package/scripts/build-python.sh +37 -0
- package/scripts/build-release.sh +279 -0
- package/scripts/build-typescript.sh +13 -0
- package/scripts/build-wasm.sh +113 -0
- package/scripts/bump-version.sh +245 -0
- package/scripts/check_unused_test_imports.py +85 -0
- package/scripts/ci_tasks.py +379 -0
- package/scripts/clear_debug_test.sh +10 -0
- package/scripts/create-github-release.sh +262 -0
- package/scripts/create-tag.sh +203 -0
- package/scripts/find_and_link_test_binary.sh +70 -0
- package/scripts/generate-changelog.sh +271 -0
- package/scripts/generate-release-notes.sh +205 -0
- package/scripts/lint_release_security.py +96 -0
- package/scripts/lint_release_workflows.py +82 -0
- package/scripts/lint_workflow_gates.py +113 -0
- package/scripts/optimized-wasm-build.sh +61 -0
- package/scripts/patch_napi_types.py +62 -0
- package/scripts/pre-release-check.sh +289 -0
- package/scripts/prepare_rust_debug.sh +52 -0
- package/scripts/release.sh +373 -0
- package/scripts/resolve_rust_binary.py +230 -0
- package/scripts/run_commitlint.sh +29 -0
- package/scripts/test-all.sh +77 -0
- package/scripts/update_launch_program.py +93 -0
- package/secrets/README.md +27 -0
- package/secrets/secrets.yaml +21 -0
- package/test_integration.py +67 -0
- package/tests/test_authority.py +328 -0
- package/tests/test_ci_tasks.py +143 -0
- package/tests/test_expression.py +256 -0
- package/tests/test_golden_payment_flow.py +42 -0
- package/tests/test_graph.py +127 -0
- package/tests/test_instance.py +136 -0
- package/tests/test_parser.py +82 -0
- package/tests/test_primitives.py +68 -0
- package/tests/test_role_relation_parity.py +56 -0
- package/tests/test_runtime_toggle.py +156 -0
- package/tests/test_semantic_pack.py +639 -0
- package/tests/test_three_valued_eval.py +159 -0
- package/tsconfig.json +30 -0
- package/typescript-tests/advanced.test.ts +165 -0
- package/typescript-tests/authority.test.ts +216 -0
- package/typescript-tests/expression.test.ts +228 -0
- package/typescript-tests/golden-payment-flow.test.ts +51 -0
- package/typescript-tests/graph.test.ts +142 -0
- package/typescript-tests/native-binding.test.ts +20 -0
- package/typescript-tests/primitives.test.ts +88 -0
- package/typescript-tests/registry.test.ts +122 -0
- package/typescript-tests/role_relation.test.ts +63 -0
- package/typescript-tests/runtime_toggle.test.ts +141 -0
- package/typescript-tests/semantic-pack.test.ts +556 -0
- package/typescript-tests/three_valued_eval.test.ts +135 -0
- package/typescript-tests/units.test.ts +36 -0
- package/vitest.config.ts +13 -0
- package/wasm_demo.html +225 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# bump-version.sh - Semantic version bumping for DomainForge
|
|
3
|
+
# Usage: ./scripts/bump-version.sh [major|minor|patch|X.Y.Z] [OPTIONS]
|
|
4
|
+
#
|
|
5
|
+
# Updates version in:
|
|
6
|
+
# - domainforge-core/Cargo.toml
|
|
7
|
+
# - domainforge-python/pyproject.toml
|
|
8
|
+
# - domainforge-typescript/package.json
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# Configuration
|
|
14
|
+
# ============================================================================
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
17
|
+
|
|
18
|
+
# Colors
|
|
19
|
+
RED='\033[0;31m'
|
|
20
|
+
GREEN='\033[0;32m'
|
|
21
|
+
YELLOW='\033[1;33m'
|
|
22
|
+
BLUE='\033[0;34m'
|
|
23
|
+
NC='\033[0m'
|
|
24
|
+
|
|
25
|
+
# Flags
|
|
26
|
+
DRY_RUN=false
|
|
27
|
+
NO_COMMIT=false
|
|
28
|
+
|
|
29
|
+
# ============================================================================
|
|
30
|
+
# Helper Functions
|
|
31
|
+
# ============================================================================
|
|
32
|
+
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
33
|
+
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
|
34
|
+
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
35
|
+
log_error() { echo -e "${RED}[✗]${NC} $1"; }
|
|
36
|
+
|
|
37
|
+
usage() {
|
|
38
|
+
cat <<EOF
|
|
39
|
+
Usage: $(basename "$0") [VERSION_TYPE] [OPTIONS]
|
|
40
|
+
|
|
41
|
+
Bump version across all DomainForge package files.
|
|
42
|
+
|
|
43
|
+
VERSION_TYPE:
|
|
44
|
+
major Bump major version (X.0.0)
|
|
45
|
+
minor Bump minor version (x.Y.0)
|
|
46
|
+
patch Bump patch version (x.y.Z)
|
|
47
|
+
X.Y.Z Set explicit version (e.g., 1.2.3)
|
|
48
|
+
X.Y.Z-suffix Set version with pre-release (e.g., 1.2.3-alpha)
|
|
49
|
+
|
|
50
|
+
OPTIONS:
|
|
51
|
+
--dry-run Show what would be changed without modifying files
|
|
52
|
+
--no-commit Update files but don't create git commit
|
|
53
|
+
-h, --help Show this help message
|
|
54
|
+
|
|
55
|
+
FILES UPDATED:
|
|
56
|
+
- domainforge-core/Cargo.toml
|
|
57
|
+
- domainforge-python/pyproject.toml
|
|
58
|
+
- domainforge-typescript/package.json
|
|
59
|
+
|
|
60
|
+
EXAMPLES:
|
|
61
|
+
$(basename "$0") patch # 0.6.2 -> 0.6.3
|
|
62
|
+
$(basename "$0") minor # 0.6.2 -> 0.7.0
|
|
63
|
+
$(basename "$0") major # 0.6.2 -> 1.0.0
|
|
64
|
+
$(basename "$0") 2.0.0 # Set explicit version
|
|
65
|
+
$(basename "$0") 1.0.0-beta.1 # Pre-release version
|
|
66
|
+
$(basename "$0") patch --dry-run # Preview changes
|
|
67
|
+
EOF
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get_current_version() {
|
|
71
|
+
grep -m1 '^version = ' "$PROJECT_ROOT/domainforge-core/Cargo.toml" | sed 's/version = "\(.*\)"/\1/'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
calculate_new_version() {
|
|
75
|
+
local current="$1"
|
|
76
|
+
local bump_type="$2"
|
|
77
|
+
|
|
78
|
+
# Handle explicit version
|
|
79
|
+
if [[ "$bump_type" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
|
80
|
+
echo "$bump_type"
|
|
81
|
+
return
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# Parse current version (strip pre-release suffix for calculation)
|
|
85
|
+
local base_version="${current%-*}"
|
|
86
|
+
IFS='.' read -r major minor patch <<< "$base_version"
|
|
87
|
+
|
|
88
|
+
case "$bump_type" in
|
|
89
|
+
major)
|
|
90
|
+
major=$((major + 1))
|
|
91
|
+
minor=0
|
|
92
|
+
patch=0
|
|
93
|
+
;;
|
|
94
|
+
minor)
|
|
95
|
+
minor=$((minor + 1))
|
|
96
|
+
patch=0
|
|
97
|
+
;;
|
|
98
|
+
patch)
|
|
99
|
+
patch=$((patch + 1))
|
|
100
|
+
;;
|
|
101
|
+
*)
|
|
102
|
+
log_error "Invalid bump type: $bump_type"
|
|
103
|
+
exit 1
|
|
104
|
+
;;
|
|
105
|
+
esac
|
|
106
|
+
|
|
107
|
+
echo "${major}.${minor}.${patch}"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
update_cargo_toml() {
|
|
111
|
+
local current="$1"
|
|
112
|
+
local new="$2"
|
|
113
|
+
|
|
114
|
+
if $DRY_RUN; then
|
|
115
|
+
log_info "Would update domainforge-core/Cargo.toml: $current -> $new"
|
|
116
|
+
else
|
|
117
|
+
sed -i "s/^version = \"$current\"/version = \"$new\"/" "$PROJECT_ROOT/domainforge-core/Cargo.toml"
|
|
118
|
+
log_success "Updated domainforge-core/Cargo.toml"
|
|
119
|
+
fi
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
update_pyproject_toml() {
|
|
123
|
+
local current="$1"
|
|
124
|
+
local new="$2"
|
|
125
|
+
|
|
126
|
+
if $DRY_RUN; then
|
|
127
|
+
log_info "Would update pyproject.toml: $current -> $new"
|
|
128
|
+
else
|
|
129
|
+
sed -i "s/^version = \"$current\"/version = \"$new\"/" "$PROJECT_ROOT/domainforge-python/pyproject.toml"
|
|
130
|
+
log_success "Updated domainforge-python/pyproject.toml"
|
|
131
|
+
fi
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
update_package_json() {
|
|
135
|
+
local new="$1"
|
|
136
|
+
|
|
137
|
+
if $DRY_RUN; then
|
|
138
|
+
log_info "Would update package.json to: $new"
|
|
139
|
+
else
|
|
140
|
+
# Use jq to update version (avoids npm dependency issues)
|
|
141
|
+
cd "$PROJECT_ROOT"
|
|
142
|
+
jq --arg v "$new" '.version = $v' domainforge-typescript/package.json > domainforge-typescript/package.json.tmp && mv domainforge-typescript/package.json.tmp domainforge-typescript/package.json
|
|
143
|
+
log_success "Updated domainforge-typescript/package.json"
|
|
144
|
+
fi
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# ============================================================================
|
|
148
|
+
# Parse Arguments
|
|
149
|
+
# ============================================================================
|
|
150
|
+
VERSION_TYPE=""
|
|
151
|
+
|
|
152
|
+
while [[ $# -gt 0 ]]; do
|
|
153
|
+
case $1 in
|
|
154
|
+
--dry-run)
|
|
155
|
+
DRY_RUN=true
|
|
156
|
+
shift
|
|
157
|
+
;;
|
|
158
|
+
--no-commit)
|
|
159
|
+
NO_COMMIT=true
|
|
160
|
+
shift
|
|
161
|
+
;;
|
|
162
|
+
-h|--help)
|
|
163
|
+
usage
|
|
164
|
+
exit 0
|
|
165
|
+
;;
|
|
166
|
+
-*)
|
|
167
|
+
log_error "Unknown option: $1"
|
|
168
|
+
usage
|
|
169
|
+
exit 1
|
|
170
|
+
;;
|
|
171
|
+
*)
|
|
172
|
+
if [[ -z "$VERSION_TYPE" ]]; then
|
|
173
|
+
VERSION_TYPE="$1"
|
|
174
|
+
else
|
|
175
|
+
log_error "Too many arguments"
|
|
176
|
+
usage
|
|
177
|
+
exit 1
|
|
178
|
+
fi
|
|
179
|
+
shift
|
|
180
|
+
;;
|
|
181
|
+
esac
|
|
182
|
+
done
|
|
183
|
+
|
|
184
|
+
if [[ -z "$VERSION_TYPE" ]]; then
|
|
185
|
+
log_error "Version type required"
|
|
186
|
+
usage
|
|
187
|
+
exit 1
|
|
188
|
+
fi
|
|
189
|
+
|
|
190
|
+
# ============================================================================
|
|
191
|
+
# Main
|
|
192
|
+
# ============================================================================
|
|
193
|
+
cd "$PROJECT_ROOT"
|
|
194
|
+
|
|
195
|
+
echo ""
|
|
196
|
+
echo "=============================================="
|
|
197
|
+
echo " DomainForge Version Bump"
|
|
198
|
+
echo "=============================================="
|
|
199
|
+
echo ""
|
|
200
|
+
|
|
201
|
+
if $DRY_RUN; then
|
|
202
|
+
log_warn "DRY RUN MODE - No files will be modified"
|
|
203
|
+
echo ""
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
# Get current version
|
|
207
|
+
CURRENT_VERSION=$(get_current_version)
|
|
208
|
+
log_info "Current version: $CURRENT_VERSION"
|
|
209
|
+
|
|
210
|
+
# Calculate new version
|
|
211
|
+
NEW_VERSION=$(calculate_new_version "$CURRENT_VERSION" "$VERSION_TYPE")
|
|
212
|
+
log_info "New version: $NEW_VERSION"
|
|
213
|
+
echo ""
|
|
214
|
+
|
|
215
|
+
# Validate new version
|
|
216
|
+
if [[ "$CURRENT_VERSION" == "$NEW_VERSION" ]]; then
|
|
217
|
+
log_warn "New version is same as current version"
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
# Update files
|
|
221
|
+
log_info "Updating version files..."
|
|
222
|
+
update_cargo_toml "$CURRENT_VERSION" "$NEW_VERSION"
|
|
223
|
+
update_pyproject_toml "$CURRENT_VERSION" "$NEW_VERSION"
|
|
224
|
+
update_package_json "$NEW_VERSION"
|
|
225
|
+
|
|
226
|
+
# Create git commit
|
|
227
|
+
if ! $DRY_RUN && ! $NO_COMMIT; then
|
|
228
|
+
echo ""
|
|
229
|
+
log_info "Creating git commit..."
|
|
230
|
+
git add domainforge-core/Cargo.toml domainforge-python/pyproject.toml domainforge-typescript/package.json
|
|
231
|
+
git commit -m "chore: bump version to v$NEW_VERSION" --quiet
|
|
232
|
+
log_success "Created commit: chore: bump version to v$NEW_VERSION"
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
echo ""
|
|
236
|
+
echo "=============================================="
|
|
237
|
+
log_success "Version bumped to $NEW_VERSION"
|
|
238
|
+
echo "=============================================="
|
|
239
|
+
|
|
240
|
+
# Output just the version for use in scripts
|
|
241
|
+
if $DRY_RUN; then
|
|
242
|
+
echo ""
|
|
243
|
+
echo "# Output (for script chaining):"
|
|
244
|
+
fi
|
|
245
|
+
echo "$NEW_VERSION"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Scan test files (domainforge-core/tests/*.rs) for `use` statements and detect likely unused imports.
|
|
4
|
+
Only flags imports where the `use` imports a single identifier and that identifier is not used elsewhere in the file.
|
|
5
|
+
This heuristic avoids removing multi-import groups that might be used via macros or shadowed names.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
repo_root = Path(__file__).resolve().parents[1]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_test_files():
|
|
15
|
+
tests_dir = repo_root / "domainforge-core" / "tests"
|
|
16
|
+
return list(tests_dir.glob("*.rs"))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
USE_RE = re.compile(r"^use\s+([^;]+);")
|
|
20
|
+
GROUPED_RE = re.compile(r"^(?P<prefix>[^:;{]+::)\{(?P<items>[^}]+)\}$")
|
|
21
|
+
SIMPLE_SINGLE_IDENT_RE = re.compile(r"^(?P<path>[^:]+)::(?P<ident>[A-Za-z0-9_]+)$")
|
|
22
|
+
|
|
23
|
+
candidates = []
|
|
24
|
+
for f in get_test_files():
|
|
25
|
+
text = f.read_text(encoding="utf8")
|
|
26
|
+
lines = text.splitlines()
|
|
27
|
+
for i, line in enumerate(lines, start=1):
|
|
28
|
+
m = USE_RE.match(line.strip())
|
|
29
|
+
if not m:
|
|
30
|
+
continue
|
|
31
|
+
use_expr = m.group(1).strip()
|
|
32
|
+
# Detect grouped imports like `foo::bar::{A, B as C}`
|
|
33
|
+
group_match = GROUPED_RE.match(use_expr)
|
|
34
|
+
if group_match:
|
|
35
|
+
prefix = group_match.group("prefix")
|
|
36
|
+
items = [it.strip() for it in group_match.group("items").split(",")]
|
|
37
|
+
for item in items:
|
|
38
|
+
# Handle `A as B` case, take alias as `B` or `A` when no alias
|
|
39
|
+
parts = [p.strip() for p in item.split(" as ")]
|
|
40
|
+
if len(parts) == 2:
|
|
41
|
+
ident = parts[1]
|
|
42
|
+
else:
|
|
43
|
+
ident = parts[0]
|
|
44
|
+
# If the item has a path `kg::KnowledgeGraph`, only the last segment
|
|
45
|
+
# (the type or ident) is relevant for references in the file.
|
|
46
|
+
if "::" in ident:
|
|
47
|
+
ident = ident.split("::")[-1]
|
|
48
|
+
# Don't try to remove common traits which are used via method calls
|
|
49
|
+
if ident == "FromStr":
|
|
50
|
+
continue
|
|
51
|
+
occ = [
|
|
52
|
+
j
|
|
53
|
+
for j, line_text in enumerate(lines, start=1)
|
|
54
|
+
if j != i and re.search(r"\b" + re.escape(ident) + r"\b", line_text)
|
|
55
|
+
]
|
|
56
|
+
if not occ:
|
|
57
|
+
candidates.append((str(f), i, line.strip(), ident))
|
|
58
|
+
continue
|
|
59
|
+
# Try to capture single import names like `crate::graph::Graph` or `domainforge_core::Graph` or `std::fmt::Result`
|
|
60
|
+
# Remove trailing traits like "pub use"? Not present here
|
|
61
|
+
if "::" not in use_expr:
|
|
62
|
+
continue
|
|
63
|
+
# Extract the last path component
|
|
64
|
+
ident = use_expr.split("::")[-1]
|
|
65
|
+
if not ident.isidentifier():
|
|
66
|
+
continue
|
|
67
|
+
# Search if ident appears elsewhere in file
|
|
68
|
+
# Exclude the import line
|
|
69
|
+
occ = [
|
|
70
|
+
j
|
|
71
|
+
for j, line_text in enumerate(lines, start=1)
|
|
72
|
+
if j != i and re.search(r"\b" + re.escape(ident) + r"\b", line_text)
|
|
73
|
+
]
|
|
74
|
+
# Skip flagging trait names which are used implicitly (FromStr)
|
|
75
|
+
if ident == "FromStr":
|
|
76
|
+
continue
|
|
77
|
+
if not occ:
|
|
78
|
+
candidates.append((str(f), i, line.strip(), ident))
|
|
79
|
+
|
|
80
|
+
if candidates:
|
|
81
|
+
print("Potential unused single-ident imports detected:")
|
|
82
|
+
for path, line_no, line, ident in candidates:
|
|
83
|
+
print(f"{path}:{line_no}: {line} (ident={ident})")
|
|
84
|
+
else:
|
|
85
|
+
print("No obvious single-identifier unused imports detected in test files.")
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CI Tasks Script - Cross-platform CI automation helpers.
|
|
4
|
+
|
|
5
|
+
This script consolidates common CI tasks to reduce duplication across
|
|
6
|
+
GitHub Actions workflows and ensure consistency between local and CI environments.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
import tarfile
|
|
18
|
+
import zipfile
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Optional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def check_size(file_path: str, max_bytes: int, label: str = "File") -> int:
|
|
24
|
+
"""
|
|
25
|
+
Check if a file's size is within the specified limit.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
file_path: Path to the file to check
|
|
29
|
+
max_bytes: Maximum allowed size in bytes
|
|
30
|
+
label: Human-readable label for error messages
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
0 if size is within limit, 1 otherwise
|
|
34
|
+
"""
|
|
35
|
+
path = Path(file_path)
|
|
36
|
+
|
|
37
|
+
if not path.exists():
|
|
38
|
+
print(f"::error::File not found: {file_path}", file=sys.stderr)
|
|
39
|
+
return 1
|
|
40
|
+
|
|
41
|
+
if not path.is_file():
|
|
42
|
+
print(f"::error::Not a file: {file_path}", file=sys.stderr)
|
|
43
|
+
return 1
|
|
44
|
+
|
|
45
|
+
size = path.stat().st_size
|
|
46
|
+
size_mb = size / (1024 * 1024)
|
|
47
|
+
max_mb = max_bytes / (1024 * 1024)
|
|
48
|
+
|
|
49
|
+
print(f"{label} size: {size:,} bytes ({size_mb:.2f} MB)")
|
|
50
|
+
|
|
51
|
+
if size > max_bytes:
|
|
52
|
+
print(
|
|
53
|
+
f"::error::{label} exceeds {max_bytes:,} bytes ({max_mb:.2f} MB): "
|
|
54
|
+
f"actual size is {size:,} bytes ({size_mb:.2f} MB)",
|
|
55
|
+
file=sys.stderr,
|
|
56
|
+
)
|
|
57
|
+
return 1
|
|
58
|
+
|
|
59
|
+
print(f"[OK] {label} is within size limit ({max_mb:.2f} MB)")
|
|
60
|
+
return 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def package_archive(
|
|
64
|
+
input_path: str,
|
|
65
|
+
output_path: str,
|
|
66
|
+
archive_format: Optional[str] = None,
|
|
67
|
+
base_name: Optional[str] = None,
|
|
68
|
+
) -> int:
|
|
69
|
+
"""
|
|
70
|
+
Package a file or directory into an archive.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
input_path: Path to file or directory to package
|
|
74
|
+
output_path: Output archive path
|
|
75
|
+
archive_format: 'zip' or 'tar.gz' (auto-detected from output_path if not specified)
|
|
76
|
+
base_name: Base name for the archived file (defaults to input filename)
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
0 on success, 1 on failure
|
|
80
|
+
"""
|
|
81
|
+
input_p = Path(input_path)
|
|
82
|
+
output_p = Path(output_path)
|
|
83
|
+
|
|
84
|
+
if not input_p.exists():
|
|
85
|
+
print(f"::error::Input path not found: {input_path}", file=sys.stderr)
|
|
86
|
+
return 1
|
|
87
|
+
|
|
88
|
+
# Auto-detect format from extension
|
|
89
|
+
if archive_format is None:
|
|
90
|
+
if output_path.endswith(".zip"):
|
|
91
|
+
archive_format = "zip"
|
|
92
|
+
elif output_path.endswith(".tar.gz") or output_path.endswith(".tgz"):
|
|
93
|
+
archive_format = "tar.gz"
|
|
94
|
+
else:
|
|
95
|
+
print(
|
|
96
|
+
f"::error::Cannot determine archive format from: {output_path}",
|
|
97
|
+
file=sys.stderr,
|
|
98
|
+
)
|
|
99
|
+
return 1
|
|
100
|
+
|
|
101
|
+
# Determine the name to use inside the archive
|
|
102
|
+
if base_name is None:
|
|
103
|
+
base_name = input_p.name
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
output_p.parent.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
|
|
108
|
+
if archive_format == "zip":
|
|
109
|
+
with zipfile.ZipFile(output_p, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
110
|
+
if input_p.is_file():
|
|
111
|
+
zf.write(input_p, base_name)
|
|
112
|
+
else:
|
|
113
|
+
for file in input_p.rglob("*"):
|
|
114
|
+
if file.is_file():
|
|
115
|
+
arcname = base_name / file.relative_to(input_p)
|
|
116
|
+
zf.write(file, arcname)
|
|
117
|
+
print(f"[OK] Created ZIP archive: {output_p}")
|
|
118
|
+
|
|
119
|
+
elif archive_format == "tar.gz":
|
|
120
|
+
with tarfile.open(output_p, "w:gz") as tf:
|
|
121
|
+
if input_p.is_file():
|
|
122
|
+
tf.add(input_p, arcname=base_name)
|
|
123
|
+
else:
|
|
124
|
+
tf.add(input_p, arcname=base_name)
|
|
125
|
+
print(f"[OK] Created tar.gz archive: {output_p}")
|
|
126
|
+
|
|
127
|
+
else:
|
|
128
|
+
print(
|
|
129
|
+
f"::error::Unsupported archive format: {archive_format}",
|
|
130
|
+
file=sys.stderr,
|
|
131
|
+
)
|
|
132
|
+
return 1
|
|
133
|
+
|
|
134
|
+
return 0
|
|
135
|
+
|
|
136
|
+
except Exception as e:
|
|
137
|
+
print(f"::error::Failed to create archive: {e}", file=sys.stderr)
|
|
138
|
+
return 1
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def verify_cli_binary(binary_path: str, expected_output: Optional[str] = None) -> int:
|
|
142
|
+
"""
|
|
143
|
+
Verify that a CLI binary can execute successfully.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
binary_path: Path to the binary to test
|
|
147
|
+
expected_output: Optional substring to check in output
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
0 if binary runs successfully, 1 otherwise
|
|
151
|
+
"""
|
|
152
|
+
path = Path(binary_path)
|
|
153
|
+
|
|
154
|
+
if not path.exists():
|
|
155
|
+
print(f"::error::Binary not found: {binary_path}", file=sys.stderr)
|
|
156
|
+
return 1
|
|
157
|
+
|
|
158
|
+
if not path.is_file():
|
|
159
|
+
print(f"::error::Not a file: {binary_path}", file=sys.stderr)
|
|
160
|
+
return 1
|
|
161
|
+
|
|
162
|
+
# Make executable on Unix-like systems
|
|
163
|
+
if platform.system() != "Windows":
|
|
164
|
+
path.chmod(path.stat().st_mode | 0o111)
|
|
165
|
+
|
|
166
|
+
# Set up environment for dynamic libraries
|
|
167
|
+
env = os.environ.copy()
|
|
168
|
+
bin_dir = str(path.parent.absolute())
|
|
169
|
+
|
|
170
|
+
if platform.system() == "Darwin":
|
|
171
|
+
dyld_path = env.get("DYLD_LIBRARY_PATH", "")
|
|
172
|
+
env["DYLD_LIBRARY_PATH"] = f"{bin_dir}:{dyld_path}" if dyld_path else bin_dir
|
|
173
|
+
elif platform.system() == "Linux":
|
|
174
|
+
ld_path = env.get("LD_LIBRARY_PATH", "")
|
|
175
|
+
env["LD_LIBRARY_PATH"] = f"{bin_dir}:{ld_path}" if ld_path else bin_dir
|
|
176
|
+
elif platform.system() == "Windows":
|
|
177
|
+
path_env = env.get("PATH", "")
|
|
178
|
+
env["PATH"] = f"{bin_dir};{path_env}" if path_env else bin_dir
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
print(f"Running: {binary_path} --version")
|
|
182
|
+
result = subprocess.run(
|
|
183
|
+
[str(path), "--version"],
|
|
184
|
+
capture_output=True,
|
|
185
|
+
text=True,
|
|
186
|
+
env=env,
|
|
187
|
+
timeout=10,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if result.returncode != 0:
|
|
191
|
+
print(
|
|
192
|
+
f"::error::Binary exited with code {result.returncode}", file=sys.stderr
|
|
193
|
+
)
|
|
194
|
+
print(f"stdout: {result.stdout}", file=sys.stderr)
|
|
195
|
+
print(f"stderr: {result.stderr}", file=sys.stderr)
|
|
196
|
+
return 1
|
|
197
|
+
|
|
198
|
+
output = result.stdout + result.stderr
|
|
199
|
+
print("[OK] Binary executed successfully")
|
|
200
|
+
print(f"Output: {output.strip()}")
|
|
201
|
+
|
|
202
|
+
if expected_output and expected_output not in output:
|
|
203
|
+
print(
|
|
204
|
+
f"::error::Expected output '{expected_output}' not found in: {output}",
|
|
205
|
+
file=sys.stderr,
|
|
206
|
+
)
|
|
207
|
+
return 1
|
|
208
|
+
|
|
209
|
+
return 0
|
|
210
|
+
|
|
211
|
+
except subprocess.TimeoutExpired:
|
|
212
|
+
print("::error::Binary execution timed out", file=sys.stderr)
|
|
213
|
+
return 1
|
|
214
|
+
except Exception as e:
|
|
215
|
+
print(f"::error::Failed to execute binary: {e}", file=sys.stderr)
|
|
216
|
+
return 1
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _is_safe_path(target_dir: str, member_path: str) -> bool:
|
|
220
|
+
abs_target = os.path.realpath(target_dir)
|
|
221
|
+
abs_member = os.path.realpath(os.path.join(target_dir, member_path))
|
|
222
|
+
return abs_member.startswith(abs_target + os.sep) or abs_member == abs_target
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _validate_tar_member(target_dir: str, member: tarfile.TarInfo) -> None:
|
|
226
|
+
if member.name.startswith("/") or ".." in member.name.split("/"):
|
|
227
|
+
raise ValueError(f"Unsafe path in tar: {member.name}")
|
|
228
|
+
if member.issym() or member.islnk():
|
|
229
|
+
raise ValueError(f"Symlink/hardlink in tar: {member.name}")
|
|
230
|
+
if not _is_safe_path(target_dir, member.name):
|
|
231
|
+
raise ValueError(f"Path traversal in tar: {member.name}")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _validate_zip_member(target_dir: str, filename: str) -> None:
|
|
235
|
+
if filename.startswith("/") or ".." in filename.split("/"):
|
|
236
|
+
raise ValueError(f"Unsafe path in zip: {filename}")
|
|
237
|
+
if not _is_safe_path(target_dir, filename):
|
|
238
|
+
raise ValueError(f"Path traversal in zip: {filename}")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def unpack_and_verify(archive_path: str, binary_name: str) -> int:
|
|
242
|
+
"""
|
|
243
|
+
Unpack an archive and verify the binary inside can execute.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
archive_path: Path to the archive
|
|
247
|
+
binary_name: Name of the binary to verify
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
0 on success, 1 on failure
|
|
251
|
+
"""
|
|
252
|
+
archive_p = Path(archive_path)
|
|
253
|
+
|
|
254
|
+
if not archive_p.exists():
|
|
255
|
+
print(f"::error::Archive not found: {archive_path}", file=sys.stderr)
|
|
256
|
+
return 1
|
|
257
|
+
|
|
258
|
+
temp_dir = Path("tmp_verify_unpack")
|
|
259
|
+
temp_dir.mkdir(exist_ok=True)
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
if archive_path.endswith(".zip"):
|
|
263
|
+
with zipfile.ZipFile(archive_p, "r") as zf:
|
|
264
|
+
for info in zf.infolist():
|
|
265
|
+
_validate_zip_member(str(temp_dir), info.filename)
|
|
266
|
+
zf.extractall(temp_dir)
|
|
267
|
+
elif archive_path.endswith(".tar.gz") or archive_path.endswith(".tgz"):
|
|
268
|
+
with tarfile.open(archive_p, "r:gz") as tf:
|
|
269
|
+
for member in tf.getmembers():
|
|
270
|
+
_validate_tar_member(str(temp_dir), member)
|
|
271
|
+
if sys.version_info >= (3, 12):
|
|
272
|
+
tf.extractall(temp_dir, filter="data")
|
|
273
|
+
else:
|
|
274
|
+
tf.extractall(temp_dir)
|
|
275
|
+
else:
|
|
276
|
+
print(
|
|
277
|
+
f"::error::Unsupported archive format: {archive_path}", file=sys.stderr
|
|
278
|
+
)
|
|
279
|
+
return 1
|
|
280
|
+
|
|
281
|
+
print(f"[OK] Unpacked archive to {temp_dir}")
|
|
282
|
+
|
|
283
|
+
# Find the binary
|
|
284
|
+
binary_path = None
|
|
285
|
+
for file in temp_dir.rglob(binary_name):
|
|
286
|
+
if file.is_file():
|
|
287
|
+
binary_path = file
|
|
288
|
+
break
|
|
289
|
+
|
|
290
|
+
# Also check with .exe extension on Windows
|
|
291
|
+
if binary_path is None and platform.system() == "Windows":
|
|
292
|
+
for file in temp_dir.rglob(f"{binary_name}.exe"):
|
|
293
|
+
if file.is_file():
|
|
294
|
+
binary_path = file
|
|
295
|
+
break
|
|
296
|
+
|
|
297
|
+
if binary_path is None:
|
|
298
|
+
print(
|
|
299
|
+
f"::error::Binary '{binary_name}' not found in archive", file=sys.stderr
|
|
300
|
+
)
|
|
301
|
+
print("Archive contents:", file=sys.stderr)
|
|
302
|
+
for file in temp_dir.rglob("*"):
|
|
303
|
+
print(f" {file.relative_to(temp_dir)}", file=sys.stderr)
|
|
304
|
+
return 1
|
|
305
|
+
|
|
306
|
+
print(f"Found binary: {binary_path}")
|
|
307
|
+
|
|
308
|
+
# Verify it runs
|
|
309
|
+
return verify_cli_binary(str(binary_path))
|
|
310
|
+
|
|
311
|
+
finally:
|
|
312
|
+
# Cleanup
|
|
313
|
+
if temp_dir.exists():
|
|
314
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def main() -> int:
|
|
318
|
+
"""Main entry point."""
|
|
319
|
+
parser = argparse.ArgumentParser(
|
|
320
|
+
description="CI tasks automation script",
|
|
321
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
325
|
+
|
|
326
|
+
# check-size command
|
|
327
|
+
size_parser = subparsers.add_parser("check-size", help="Check file size")
|
|
328
|
+
size_parser.add_argument("--file", required=True, help="File to check")
|
|
329
|
+
size_parser.add_argument(
|
|
330
|
+
"--max-bytes", type=int, required=True, help="Maximum size in bytes"
|
|
331
|
+
)
|
|
332
|
+
size_parser.add_argument(
|
|
333
|
+
"--label", default="File", help="Label for output messages"
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# package command
|
|
337
|
+
package_parser = subparsers.add_parser("package", help="Package files into archive")
|
|
338
|
+
package_parser.add_argument(
|
|
339
|
+
"--input", required=True, help="Input file or directory"
|
|
340
|
+
)
|
|
341
|
+
package_parser.add_argument("--output", required=True, help="Output archive path")
|
|
342
|
+
package_parser.add_argument(
|
|
343
|
+
"--format", choices=["zip", "tar.gz"], help="Archive format"
|
|
344
|
+
)
|
|
345
|
+
package_parser.add_argument("--base-name", help="Base name for archived content")
|
|
346
|
+
|
|
347
|
+
# verify-cli command
|
|
348
|
+
verify_parser = subparsers.add_parser(
|
|
349
|
+
"verify-cli", help="Verify CLI binary executes"
|
|
350
|
+
)
|
|
351
|
+
verify_parser.add_argument("--binary", required=True, help="Path to binary")
|
|
352
|
+
verify_parser.add_argument("--expected-output", help="Expected substring in output")
|
|
353
|
+
|
|
354
|
+
# unpack-verify command
|
|
355
|
+
unpack_parser = subparsers.add_parser(
|
|
356
|
+
"unpack-verify", help="Unpack archive and verify binary"
|
|
357
|
+
)
|
|
358
|
+
unpack_parser.add_argument("--archive", required=True, help="Archive path")
|
|
359
|
+
unpack_parser.add_argument(
|
|
360
|
+
"--binary-name", required=True, help="Binary name to verify"
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
args = parser.parse_args()
|
|
364
|
+
|
|
365
|
+
if args.command == "check-size":
|
|
366
|
+
return check_size(args.file, args.max_bytes, args.label)
|
|
367
|
+
elif args.command == "package":
|
|
368
|
+
return package_archive(args.input, args.output, args.format, args.base_name)
|
|
369
|
+
elif args.command == "verify-cli":
|
|
370
|
+
return verify_cli_binary(args.binary, args.expected_output)
|
|
371
|
+
elif args.command == "unpack-verify":
|
|
372
|
+
return unpack_and_verify(args.archive, args.binary_name)
|
|
373
|
+
else:
|
|
374
|
+
parser.print_help()
|
|
375
|
+
return 1
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
if __name__ == "__main__":
|
|
379
|
+
sys.exit(main())
|