aws-durable-execution-sdk-python 1.4.0__tar.gz → 1.5.0__tar.gz
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.
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/__init__.py +1 -0
- {aws_durable_execution_sdk_python-1.4.0/ops → aws_durable_execution_sdk_python-1.5.0/.github/scripts}/ci-checks.sh +3 -0
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/lintcommit.py +265 -0
- {aws_durable_execution_sdk_python-1.4.0/ops → aws_durable_execution_sdk_python-1.5.0/.github/scripts}/parse_sdk_branch.py +0 -0
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/__init__.py +1 -0
- aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/test_lintcommit.py +261 -0
- {aws_durable_execution_sdk_python-1.4.0/ops/__tests__ → aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests}/test_parse_sdk_branch.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/ci.yml +10 -11
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/deploy-examples.yml +134 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/integration-tests.yml +15 -35
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-issues.yml +23 -0
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-pr.yml +24 -0
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-release.yml +29 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/pypi-publish.yml +10 -2
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/scorecard.yml +2 -2
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/sync-package.yml +1 -1
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/test-parser.yml +5 -5
- aws_durable_execution_sdk_python-1.5.0/.github/workflows/update-sam-template.yml +42 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.gitignore +6 -1
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/AGENTS.md +1 -6
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/CONTRIBUTING.md +65 -1
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/PKG-INFO +6 -29
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/README.md +4 -27
- aws_durable_execution_sdk_python-1.5.0/examples/cli.py +487 -0
- aws_durable_execution_sdk_python-1.5.0/examples/examples-catalog.json +607 -0
- aws_durable_execution_sdk_python-1.5.0/examples/scripts/generate_sam_template.py +106 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/__init__.py +3 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/block_example/block_example.py +47 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_concurrency.py +51 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_heartbeat.py +22 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_mixed_ops.py +35 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_serdes.py +76 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_simple.py +22 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_with_timeout.py +23 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/comprehensive_operations/comprehensive_operations.py +51 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/handler_error/handler_error.py +13 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/hello_world.py +62 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/logger_example/logger_example.py +64 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_completion.py +117 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_operations.py +23 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_operations_flat.py +23 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_batch_serdes.py +96 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_custom_serdes.py +63 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_failure_tolerance.py +53 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_item_namer.py +30 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_large_scale.py +64 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_max_concurrency.py +23 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_min_successful.py +43 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/no_replay_execution/no_replay_execution.py +15 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/none_results/none_results.py +31 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel.py +27 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_first_successful.py +27 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_flat.py +27 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_batch_serdes.py +97 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_custom_serdes.py +60 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_failure_tolerance.py +59 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_max_concurrency.py +25 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_named_branches.py +51 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_wait.py +24 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context.py +22 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context_large_data.py +73 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context_step_failure.py +50 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/simple_execution/simple_execution.py +18 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step.py +19 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_no_name.py +11 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_semantics_at_most_once.py +18 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_exponential_backoff.py +27 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_name.py +11 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_retry.py +46 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/step/steps_with_retry.py +81 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait/multiple_wait.py +19 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait/wait.py +11 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait/wait_with_name.py +12 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback.py +27 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_anonymous.py +20 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_child.py +42 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_heartbeat.py +33 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_mixed_ops.py +47 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_multiple_invocations.py +53 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_nested.py +66 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_serdes.py +90 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_submitter_failure.py +39 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_submitter_failure_catchable.py +52 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_timeout.py +35 -0
- aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_condition/wait_for_condition.py +32 -0
- aws_durable_execution_sdk_python-1.5.0/examples/template.yaml +982 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/README.md +119 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/__init__.py +1 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/block_example/test_block_example.py +104 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_concurrency.py +83 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_heartbeat.py +51 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_mixed_ops.py +49 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_serdes.py +60 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_simple.py +47 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/comprehensive_operations/test_comprehensive_operations.py +94 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/conftest.py +268 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/handler_error/test_handler_error.py +32 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/logger_example/test_logger_example.py +35 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_completion.py +32 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_operations.py +42 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_operations_flat.py +39 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_batch_serdes.py +43 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_custom_serdes.py +48 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_failure_tolerance.py +52 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_item_namer.py +39 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_large_scale.py +36 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_max_concurrency.py +37 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_min_successful.py +70 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/no_replay_execution/test_no_replay_execution.py +52 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/none_results/test_none_results.py +51 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel.py +42 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_flat.py +42 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_batch_serdes.py +43 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_custom_serdes.py +46 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_failure_tolerance.py +49 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_max_concurrency.py +36 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_named_branches.py +57 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_wait.py +47 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context.py +27 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context_large_data.py +36 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context_step_failure.py +23 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/simple_execution/test_simple_execution.py +40 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step.py +24 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_permutations.py +75 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_semantics_at_most_once.py +32 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_with_retry.py +40 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_steps_with_retry.py +52 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/test_hello_world.py +24 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_multiple_wait.py +57 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_wait.py +27 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_wait_permutations.py +25 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_anonymous.py +39 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_child.py +73 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_failure.py +27 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_heartbeat.py +62 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_mixed_ops.py +52 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_multiple_invocations.py +74 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_nested.py +101 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_serdes.py +66 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_submitter_failure.py +32 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_submitter_failure_catchable.py +28 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_success.py +25 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_timeout.py +32 -0
- aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_condition/test_wait_for_condition.py +24 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/pyproject.toml +50 -2
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/__about__.py +1 -1
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/__init__.py +5 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/concurrency/executor.py +34 -13
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/config.py +108 -14
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/context.py +120 -13
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/exceptions.py +100 -42
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/execution.py +57 -51
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/lambda_service.py +12 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/child.py +43 -29
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/map.py +15 -2
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/parallel.py +25 -4
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/step.py +2 -2
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/state.py +74 -9
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/types.py +3 -1
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/concurrency_test.py +292 -35
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/config_test.py +0 -11
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/context_test.py +317 -1
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/exceptions_test.py +145 -34
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/execution_test.py +230 -97
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/lambda_service_test.py +77 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/child_test.py +248 -11
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/map_test.py +163 -11
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/parallel_test.py +151 -15
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/step_test.py +55 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/state_test.py +187 -1
- aws_durable_execution_sdk_python-1.4.0/.github/workflows/lintcommit.js +0 -178
- aws_durable_execution_sdk_python-1.4.0/.github/workflows/notify_slack.yml +0 -39
- aws_durable_execution_sdk_python-1.4.0/docs/advanced/error-handling.md +0 -955
- aws_durable_execution_sdk_python-1.4.0/docs/advanced/serialization.md +0 -771
- aws_durable_execution_sdk_python-1.4.0/docs/advanced/testing-modes.md +0 -495
- aws_durable_execution_sdk_python-1.4.0/docs/api-reference/.gitkeep +0 -1
- aws_durable_execution_sdk_python-1.4.0/docs/architecture.md +0 -505
- aws_durable_execution_sdk_python-1.4.0/docs/best-practices.md +0 -850
- aws_durable_execution_sdk_python-1.4.0/docs/core/callbacks.md +0 -877
- aws_durable_execution_sdk_python-1.4.0/docs/core/child-contexts.md +0 -703
- aws_durable_execution_sdk_python-1.4.0/docs/core/invoke.md +0 -774
- aws_durable_execution_sdk_python-1.4.0/docs/core/logger.md +0 -737
- aws_durable_execution_sdk_python-1.4.0/docs/core/map.md +0 -589
- aws_durable_execution_sdk_python-1.4.0/docs/core/parallel.md +0 -911
- aws_durable_execution_sdk_python-1.4.0/docs/core/steps.md +0 -597
- aws_durable_execution_sdk_python-1.4.0/docs/core/wait.md +0 -445
- aws_durable_execution_sdk_python-1.4.0/docs/getting-started.md +0 -293
- aws_durable_execution_sdk_python-1.4.0/docs/index.md +0 -205
- aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/.gitkeep +0 -1
- aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/basic-tests.md +0 -701
- aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/complex-workflows.md +0 -675
- aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/stores.md +0 -262
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/ISSUE_TEMPLATE/documentation.yml +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/dependabot.yml +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/CODE_OF_CONDUCT.md +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/LICENSE +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/NOTICE +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/.gitignore +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/concurrency/__init__.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/concurrency/models.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/identifier.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/logger.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/__init__.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/base.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/callback.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/invoke.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/wait.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/wait_for_condition.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/py.typed +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/retries.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/serdes.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/suspend.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/threading.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/waits.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/__init__.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/durable_executions_python_language_sdk_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/e2e/__init__.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/e2e/checkpoint_response_int_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/e2e/execution_int_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/e2e/map_with_concurrent_waits_int_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/logger_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/__init__.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/base_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/callback_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/invoke_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/wait_for_condition_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/wait_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/retries_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/serdes_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/suspend_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/test_helpers.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/threading_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/types_test.py +0 -0
- {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/waits_test.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Checks that commit messages conform to conventional commits
|
|
3
|
+
# (https://www.conventionalcommits.org/).
|
|
4
|
+
#
|
|
5
|
+
# To run tests:
|
|
6
|
+
#
|
|
7
|
+
# python -m pytest .github/scripts/tests/test_lintcommit.py
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import re
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
|
|
17
|
+
TYPES: set[str] = {
|
|
18
|
+
"build",
|
|
19
|
+
"chore",
|
|
20
|
+
"ci",
|
|
21
|
+
"deps",
|
|
22
|
+
"docs",
|
|
23
|
+
"feat",
|
|
24
|
+
"fix",
|
|
25
|
+
"perf",
|
|
26
|
+
"refactor",
|
|
27
|
+
"style",
|
|
28
|
+
"test",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
MAX_SUBJECT_LENGTH: int = 50
|
|
32
|
+
MAX_SCOPE_LENGTH: int = 30
|
|
33
|
+
MAX_BODY_LINE_LENGTH: int = 72
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def validate_subject(subject_line: str) -> str | None:
|
|
37
|
+
"""Validate a commit message subject line.
|
|
38
|
+
|
|
39
|
+
Returns None if valid, else an error message string.
|
|
40
|
+
"""
|
|
41
|
+
parts: list[str] = subject_line.split(":", maxsplit=1)
|
|
42
|
+
|
|
43
|
+
if len(parts) < 2:
|
|
44
|
+
return "missing colon (:) char"
|
|
45
|
+
|
|
46
|
+
type_scope: str = parts[0]
|
|
47
|
+
subject: str = parts[1].strip()
|
|
48
|
+
|
|
49
|
+
# Parse type and optional scope: type or type(scope)
|
|
50
|
+
scope: str | None = None
|
|
51
|
+
commit_type: str = type_scope
|
|
52
|
+
|
|
53
|
+
if "(" in type_scope:
|
|
54
|
+
paren_start: int = type_scope.index("(")
|
|
55
|
+
commit_type = type_scope[:paren_start]
|
|
56
|
+
|
|
57
|
+
if not type_scope.endswith(")"):
|
|
58
|
+
return "must be formatted like type(scope):"
|
|
59
|
+
|
|
60
|
+
scope = type_scope[paren_start + 1 : -1]
|
|
61
|
+
|
|
62
|
+
if " " in commit_type:
|
|
63
|
+
return f'type contains whitespace: "{commit_type}"'
|
|
64
|
+
|
|
65
|
+
if commit_type not in TYPES:
|
|
66
|
+
return f'invalid type "{commit_type}"'
|
|
67
|
+
|
|
68
|
+
if scope is not None:
|
|
69
|
+
if len(scope) > MAX_SCOPE_LENGTH:
|
|
70
|
+
return f"invalid scope (must be <={MAX_SCOPE_LENGTH} chars)"
|
|
71
|
+
|
|
72
|
+
if re.search(r"[^- a-z0-9]", scope):
|
|
73
|
+
return f'invalid scope (must be lowercase, ascii only): "{scope}"'
|
|
74
|
+
|
|
75
|
+
if len(subject) == 0:
|
|
76
|
+
return "empty subject"
|
|
77
|
+
|
|
78
|
+
if len(subject) > MAX_SUBJECT_LENGTH:
|
|
79
|
+
return f"invalid subject (must be <={MAX_SUBJECT_LENGTH} chars)"
|
|
80
|
+
|
|
81
|
+
if subject.endswith("."):
|
|
82
|
+
return "subject must not end with a period"
|
|
83
|
+
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def validate_body(body: str) -> list[str]:
|
|
88
|
+
"""Validate the body of a commit message.
|
|
89
|
+
|
|
90
|
+
Returns a list of warnings (not hard errors) for body issues.
|
|
91
|
+
"""
|
|
92
|
+
warnings: list[str] = []
|
|
93
|
+
for i, line in enumerate(body.splitlines(), start=1):
|
|
94
|
+
if len(line) > MAX_BODY_LINE_LENGTH:
|
|
95
|
+
warnings.append(
|
|
96
|
+
f"body line {i} exceeds {MAX_BODY_LINE_LENGTH} chars ({len(line)} chars)"
|
|
97
|
+
)
|
|
98
|
+
return warnings
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def validate_message(message: str) -> tuple[str | None, list[str]]:
|
|
102
|
+
"""Validate a full commit message (subject + optional body).
|
|
103
|
+
|
|
104
|
+
Returns (error, warnings) where error is None if the subject is valid.
|
|
105
|
+
"""
|
|
106
|
+
lines: list[str] = message.strip().splitlines()
|
|
107
|
+
if not lines:
|
|
108
|
+
return ("empty commit message", [])
|
|
109
|
+
|
|
110
|
+
subject_line: str = lines[0]
|
|
111
|
+
error: str | None = validate_subject(subject_line)
|
|
112
|
+
|
|
113
|
+
warnings: list[str] = []
|
|
114
|
+
# Check for blank line between subject and body
|
|
115
|
+
body_start: int = 2
|
|
116
|
+
if len(lines) > 1 and lines[1].strip() != "":
|
|
117
|
+
warnings.append("missing blank line between subject and body")
|
|
118
|
+
body_start = 1
|
|
119
|
+
|
|
120
|
+
if len(lines) > body_start:
|
|
121
|
+
body: str = "\n".join(lines[body_start:])
|
|
122
|
+
warnings.extend(validate_body(body))
|
|
123
|
+
|
|
124
|
+
return (error, warnings)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass
|
|
128
|
+
class CommitResult:
|
|
129
|
+
"""Result of validating a single commit."""
|
|
130
|
+
|
|
131
|
+
sha: str
|
|
132
|
+
subject: str
|
|
133
|
+
error: str | None = None
|
|
134
|
+
warnings: list[str] = field(default_factory=list)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass
|
|
138
|
+
class LintResult:
|
|
139
|
+
"""Result of linting a range of commits."""
|
|
140
|
+
|
|
141
|
+
commits: list[CommitResult] = field(default_factory=list)
|
|
142
|
+
skipped: bool = False
|
|
143
|
+
skip_reason: str = ""
|
|
144
|
+
empty: bool = False
|
|
145
|
+
git_error: str = ""
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def has_errors(self) -> bool:
|
|
149
|
+
return bool(self.git_error) or any(c.error for c in self.commits)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def lint_range(git_range: str, *, skip_dirty_check: bool = False) -> LintResult:
|
|
153
|
+
"""Validate commit messages in a git range (e.g. 'origin/main..HEAD').
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
git_range: A git revision range like 'origin/main..HEAD'.
|
|
157
|
+
skip_dirty_check: When True, skip the uncommitted changes check
|
|
158
|
+
(useful in CI where the worktree may be clean by definition).
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
A LintResult with per-commit validation results.
|
|
162
|
+
"""
|
|
163
|
+
if not skip_dirty_check:
|
|
164
|
+
status = subprocess.run(
|
|
165
|
+
["git", "status", "--porcelain"],
|
|
166
|
+
capture_output=True,
|
|
167
|
+
text=True,
|
|
168
|
+
)
|
|
169
|
+
if status.stdout.strip():
|
|
170
|
+
return LintResult(
|
|
171
|
+
skipped=True,
|
|
172
|
+
skip_reason=(
|
|
173
|
+
"uncommitted changes detected, skipping commit message validation.\n"
|
|
174
|
+
"Commit your changes and re-run to validate."
|
|
175
|
+
),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
result = subprocess.run(
|
|
179
|
+
["git", "log", "--no-merges", git_range, "-z", "--format=%H%n%B"],
|
|
180
|
+
capture_output=True,
|
|
181
|
+
text=True,
|
|
182
|
+
)
|
|
183
|
+
if result.returncode != 0:
|
|
184
|
+
return LintResult(git_error=result.stderr.strip())
|
|
185
|
+
|
|
186
|
+
if not result.stdout.strip():
|
|
187
|
+
return LintResult(empty=True)
|
|
188
|
+
|
|
189
|
+
commits: list[CommitResult] = []
|
|
190
|
+
for record in result.stdout.split("\0"):
|
|
191
|
+
if not record.strip():
|
|
192
|
+
continue
|
|
193
|
+
sha, _, message = record.partition("\n")
|
|
194
|
+
message = message.strip()
|
|
195
|
+
if not message:
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
error, warnings = validate_message(message)
|
|
199
|
+
subject = message.splitlines()[0]
|
|
200
|
+
commits.append(
|
|
201
|
+
CommitResult(
|
|
202
|
+
sha=sha[:7],
|
|
203
|
+
subject=subject,
|
|
204
|
+
error=error,
|
|
205
|
+
warnings=warnings,
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
return LintResult(commits=commits)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def write_output(lint_result: LintResult, git_range: str) -> None:
|
|
213
|
+
"""Write lint results to stdout/stderr."""
|
|
214
|
+
if lint_result.skipped:
|
|
215
|
+
print(f"WARNING: {lint_result.skip_reason}")
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
if lint_result.git_error:
|
|
219
|
+
print(f"git log failed: {lint_result.git_error}", file=sys.stderr)
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
if lint_result.empty:
|
|
223
|
+
print(f"No commits in range {git_range}")
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
for commit in lint_result.commits:
|
|
227
|
+
if commit.error:
|
|
228
|
+
print(f"FAIL {commit.sha}: {commit.subject}", file=sys.stderr)
|
|
229
|
+
print(f" Error: {commit.error}", file=sys.stderr)
|
|
230
|
+
else:
|
|
231
|
+
print(f"PASS {commit.sha}: {commit.subject}")
|
|
232
|
+
|
|
233
|
+
for warning in commit.warnings:
|
|
234
|
+
print(f" Warning: {warning}")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def run_range(git_range: str, *, skip_dirty_check: bool = False) -> None:
|
|
238
|
+
"""Validate commit messages in a git range and exit on errors."""
|
|
239
|
+
lint_result = lint_range(git_range, skip_dirty_check=skip_dirty_check)
|
|
240
|
+
write_output(lint_result, git_range)
|
|
241
|
+
if lint_result.has_errors:
|
|
242
|
+
sys.exit(1)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def main() -> None:
|
|
246
|
+
parser = argparse.ArgumentParser(
|
|
247
|
+
description="Lint commit messages for conventional commits compliance."
|
|
248
|
+
)
|
|
249
|
+
parser.add_argument(
|
|
250
|
+
"--range",
|
|
251
|
+
default=None,
|
|
252
|
+
dest="git_range",
|
|
253
|
+
help="Validate all commits in a git revision range (e.g. 'origin/main..HEAD'). "
|
|
254
|
+
"Skips the uncommitted-changes check (useful in CI).",
|
|
255
|
+
)
|
|
256
|
+
args = parser.parse_args()
|
|
257
|
+
|
|
258
|
+
if args.git_range is not None:
|
|
259
|
+
run_range(args.git_range, skip_dirty_check=True)
|
|
260
|
+
else:
|
|
261
|
+
run_range("origin/main..HEAD")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
if __name__ == "__main__":
|
|
265
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from subprocess import CompletedProcess
|
|
8
|
+
from unittest.mock import patch
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
from lintcommit import lint_range, validate_message, validate_subject
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# region validate_subject: valid subjects
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_valid_feat() -> None:
|
|
21
|
+
assert validate_subject("feat: add new feature") is None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_valid_fix() -> None:
|
|
25
|
+
assert validate_subject("fix: resolve issue") is None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_valid_fix_with_scope() -> None:
|
|
29
|
+
assert validate_subject("fix(sdk): resolve issue") is None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_valid_build() -> None:
|
|
33
|
+
assert validate_subject("build: update build process") is None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_valid_chore() -> None:
|
|
37
|
+
assert validate_subject("chore: update dependencies") is None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_valid_ci() -> None:
|
|
41
|
+
assert validate_subject("ci: configure ci/cd") is None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_valid_deps() -> None:
|
|
45
|
+
assert validate_subject("deps: bump aws-sdk group with 5 updates") is None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_valid_docs() -> None:
|
|
49
|
+
assert validate_subject("docs: update documentation") is None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_valid_feat_with_scope() -> None:
|
|
53
|
+
assert validate_subject("feat(sdk): add new feature") is None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_valid_feat_scope_bar() -> None:
|
|
57
|
+
assert validate_subject("feat(sdk): bar") is None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_valid_feat_foo() -> None:
|
|
61
|
+
assert validate_subject("feat: foo") is None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_valid_fix_foo() -> None:
|
|
65
|
+
assert validate_subject("fix: foo") is None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# region validate_subject: invalid subjects
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_invalid_type() -> None:
|
|
72
|
+
assert validate_subject("config: foo") == 'invalid type "config"'
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_missing_colon() -> None:
|
|
76
|
+
assert validate_subject("invalid title") == "missing colon (:) char"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_period_at_end() -> None:
|
|
80
|
+
assert validate_subject("feat: add thing.") == "subject must not end with a period"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_empty_subject() -> None:
|
|
84
|
+
assert validate_subject("feat: ") == "empty subject"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_subject_too_long() -> None:
|
|
88
|
+
long_subject: str = "feat: " + "a" * 51
|
|
89
|
+
result = validate_subject(long_subject)
|
|
90
|
+
assert result is not None
|
|
91
|
+
assert "invalid subject" in result
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_type_with_whitespace() -> None:
|
|
95
|
+
assert validate_subject("fe at: foo") == 'type contains whitespace: "fe at"'
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_scope_not_closed() -> None:
|
|
99
|
+
assert validate_subject("feat(sdk: foo") == "must be formatted like type(scope):"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_scope_too_long() -> None:
|
|
103
|
+
long_scope: str = "a" * 31
|
|
104
|
+
result = validate_subject(f"feat({long_scope}): foo")
|
|
105
|
+
assert result is not None
|
|
106
|
+
assert "invalid scope" in result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_scope_uppercase() -> None:
|
|
110
|
+
result = validate_subject("feat(SDK): foo")
|
|
111
|
+
assert result is not None
|
|
112
|
+
assert "invalid scope" in result
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_subject_uppercase() -> None:
|
|
116
|
+
assert validate_subject("feat: Add new feature") == "subject must be lowercase"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# region validate_message
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_valid_subject_only() -> None:
|
|
123
|
+
error, warnings = validate_message("feat: add thing")
|
|
124
|
+
assert error is None
|
|
125
|
+
assert warnings == []
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_valid_with_body() -> None:
|
|
129
|
+
error, warnings = validate_message("feat: add thing\n\nThis is the body.")
|
|
130
|
+
assert error is None
|
|
131
|
+
assert warnings == []
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_missing_blank_line() -> None:
|
|
135
|
+
_, warnings = validate_message("feat: add thing\nNo blank line.")
|
|
136
|
+
assert "missing blank line between subject and body" in warnings
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_missing_blank_line_body_still_checked() -> None:
|
|
140
|
+
_, warnings = validate_message("feat: add thing\n" + "x" * 80)
|
|
141
|
+
assert "missing blank line between subject and body" in warnings
|
|
142
|
+
assert any("exceeds 72 chars" in w for w in warnings), (
|
|
143
|
+
"body line length should be checked even without blank line"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_long_body_line() -> None:
|
|
148
|
+
_, warnings = validate_message("feat: add thing\n\n" + "x" * 80)
|
|
149
|
+
assert len(warnings) == 1
|
|
150
|
+
assert "exceeds 72 chars" in warnings[0]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_empty_message() -> None:
|
|
154
|
+
error, _ = validate_message("")
|
|
155
|
+
assert error == "empty commit message"
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_invalid_subject_in_message() -> None:
|
|
159
|
+
error, _ = validate_message("invalid title")
|
|
160
|
+
assert error == "missing colon (:) char"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# region lint_range
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _make_git_log_output(*messages: str) -> str:
|
|
167
|
+
"""Build fake ``git log --no-merges -z --format=%H%n%B`` output.
|
|
168
|
+
|
|
169
|
+
Records are separated by null characters.
|
|
170
|
+
"""
|
|
171
|
+
records: list[str] = []
|
|
172
|
+
for i, msg in enumerate(messages):
|
|
173
|
+
sha = f"abc{i:04d}" + "0" * 33 # 40-char fake SHA
|
|
174
|
+
records.append(f"{sha}\n{msg}\n")
|
|
175
|
+
return "\0".join(records)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _completed(
|
|
179
|
+
stdout: str = "", stderr: str = "", returncode: int = 0
|
|
180
|
+
) -> CompletedProcess[str]:
|
|
181
|
+
"""Shorthand for a ``subprocess.CompletedProcess``."""
|
|
182
|
+
return CompletedProcess(
|
|
183
|
+
args=[], returncode=returncode, stdout=stdout, stderr=stderr
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@patch("subprocess.run")
|
|
188
|
+
def test_lint_range_all_valid(mock_run) -> None:
|
|
189
|
+
log_output = _make_git_log_output(
|
|
190
|
+
"feat: add new feature",
|
|
191
|
+
"fix(sdk): resolve issue",
|
|
192
|
+
)
|
|
193
|
+
mock_run.return_value = _completed(stdout=log_output)
|
|
194
|
+
|
|
195
|
+
result = lint_range("origin/main..HEAD", skip_dirty_check=True)
|
|
196
|
+
|
|
197
|
+
assert not result.has_errors
|
|
198
|
+
assert len(result.commits) == 2
|
|
199
|
+
assert all(c.error is None for c in result.commits)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@patch("subprocess.run")
|
|
203
|
+
def test_lint_range_with_invalid_commit(mock_run) -> None:
|
|
204
|
+
log_output = _make_git_log_output(
|
|
205
|
+
"feat: add new feature",
|
|
206
|
+
"bad commit no colon",
|
|
207
|
+
)
|
|
208
|
+
mock_run.return_value = _completed(stdout=log_output)
|
|
209
|
+
|
|
210
|
+
result = lint_range("origin/main..HEAD", skip_dirty_check=True)
|
|
211
|
+
|
|
212
|
+
assert result.has_errors
|
|
213
|
+
assert result.commits[0].error is None
|
|
214
|
+
assert result.commits[1].error == "missing colon (:) char"
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@patch("subprocess.run")
|
|
218
|
+
def test_lint_range_empty(mock_run) -> None:
|
|
219
|
+
mock_run.return_value = _completed(stdout="")
|
|
220
|
+
|
|
221
|
+
result = lint_range("origin/main..HEAD", skip_dirty_check=True)
|
|
222
|
+
|
|
223
|
+
assert result.empty
|
|
224
|
+
assert not result.has_errors
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@patch("subprocess.run")
|
|
228
|
+
def test_lint_range_git_failure(mock_run) -> None:
|
|
229
|
+
mock_run.return_value = _completed(returncode=1, stderr="fatal: bad range")
|
|
230
|
+
|
|
231
|
+
result = lint_range("bad..range", skip_dirty_check=True)
|
|
232
|
+
|
|
233
|
+
assert result.has_errors
|
|
234
|
+
assert result.git_error == "fatal: bad range"
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@patch("subprocess.run")
|
|
238
|
+
def test_lint_range_dirty_worktree_skips(mock_run) -> None:
|
|
239
|
+
"""When skip_dirty_check=False and worktree is dirty, validation is skipped."""
|
|
240
|
+
mock_run.return_value = _completed(stdout=" M .github/scripts/lintcommit.py\n")
|
|
241
|
+
|
|
242
|
+
result = lint_range("origin/main..HEAD", skip_dirty_check=False)
|
|
243
|
+
|
|
244
|
+
assert result.skipped
|
|
245
|
+
assert "uncommitted changes" in result.skip_reason
|
|
246
|
+
# git log should never have been called (only git status)
|
|
247
|
+
mock_run.assert_called_once()
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@patch("subprocess.run")
|
|
251
|
+
def test_lint_range_warnings_collected(mock_run) -> None:
|
|
252
|
+
log_output = _make_git_log_output(
|
|
253
|
+
"feat: add thing\n\n" + "x" * 80,
|
|
254
|
+
)
|
|
255
|
+
mock_run.return_value = _completed(stdout=log_output)
|
|
256
|
+
|
|
257
|
+
result = lint_range("origin/main..HEAD", skip_dirty_check=True)
|
|
258
|
+
|
|
259
|
+
assert not result.has_errors
|
|
260
|
+
assert len(result.commits) == 1
|
|
261
|
+
assert any("exceeds 72 chars" in w for w in result.commits[0].warnings)
|
|
File without changes
|
|
@@ -13,21 +13,20 @@ on:
|
|
|
13
13
|
|
|
14
14
|
jobs:
|
|
15
15
|
lint-commits:
|
|
16
|
-
|
|
16
|
+
if: github.event_name == 'pull_request'
|
|
17
17
|
runs-on: ubuntu-latest
|
|
18
18
|
steps:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
- name: Lint commit messages
|
|
27
|
+
run: python .github/scripts/lintcommit.py --range "origin/${{ github.event.pull_request.base.ref }}..${{ github.event.pull_request.head.sha }}"
|
|
27
28
|
|
|
28
29
|
build:
|
|
29
|
-
needs: lint-commits
|
|
30
|
-
|
|
31
30
|
runs-on: ubuntu-latest
|
|
32
31
|
strategy:
|
|
33
32
|
fail-fast: false
|