cumulusci-plus 5.0.0__py3-none-any.whl
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.
Potentially problematic release.
This version of cumulusci-plus might be problematic. Click here for more details.
- cumulusci/__about__.py +1 -0
- cumulusci/__init__.py +22 -0
- cumulusci/__main__.py +3 -0
- cumulusci/cli/__init__.py +0 -0
- cumulusci/cli/cci.py +244 -0
- cumulusci/cli/error.py +125 -0
- cumulusci/cli/flow.py +185 -0
- cumulusci/cli/logger.py +72 -0
- cumulusci/cli/org.py +692 -0
- cumulusci/cli/plan.py +181 -0
- cumulusci/cli/project.py +391 -0
- cumulusci/cli/robot.py +116 -0
- cumulusci/cli/runtime.py +190 -0
- cumulusci/cli/service.py +521 -0
- cumulusci/cli/task.py +295 -0
- cumulusci/cli/tests/__init__.py +0 -0
- cumulusci/cli/tests/test_cci.py +545 -0
- cumulusci/cli/tests/test_error.py +170 -0
- cumulusci/cli/tests/test_flow.py +276 -0
- cumulusci/cli/tests/test_logger.py +25 -0
- cumulusci/cli/tests/test_org.py +1438 -0
- cumulusci/cli/tests/test_plan.py +245 -0
- cumulusci/cli/tests/test_project.py +235 -0
- cumulusci/cli/tests/test_robot.py +177 -0
- cumulusci/cli/tests/test_runtime.py +197 -0
- cumulusci/cli/tests/test_service.py +853 -0
- cumulusci/cli/tests/test_task.py +266 -0
- cumulusci/cli/tests/test_ui.py +310 -0
- cumulusci/cli/tests/test_utils.py +122 -0
- cumulusci/cli/tests/utils.py +52 -0
- cumulusci/cli/ui.py +234 -0
- cumulusci/cli/utils.py +150 -0
- cumulusci/conftest.py +181 -0
- cumulusci/core/__init__.py +0 -0
- cumulusci/core/config/BaseConfig.py +5 -0
- cumulusci/core/config/BaseTaskFlowConfig.py +5 -0
- cumulusci/core/config/OrgConfig.py +5 -0
- cumulusci/core/config/ScratchOrgConfig.py +5 -0
- cumulusci/core/config/__init__.py +125 -0
- cumulusci/core/config/base_config.py +111 -0
- cumulusci/core/config/base_task_flow_config.py +82 -0
- cumulusci/core/config/marketing_cloud_service_config.py +83 -0
- cumulusci/core/config/oauth2_service_config.py +17 -0
- cumulusci/core/config/org_config.py +604 -0
- cumulusci/core/config/project_config.py +782 -0
- cumulusci/core/config/scratch_org_config.py +251 -0
- cumulusci/core/config/sfdx_org_config.py +220 -0
- cumulusci/core/config/tests/_test_config_backwards_compatibility.py +33 -0
- cumulusci/core/config/tests/test_config.py +1895 -0
- cumulusci/core/config/tests/test_config_expensive.py +839 -0
- cumulusci/core/config/tests/test_config_util.py +91 -0
- cumulusci/core/config/universal_config.py +88 -0
- cumulusci/core/config/util.py +18 -0
- cumulusci/core/datasets.py +303 -0
- cumulusci/core/debug.py +33 -0
- cumulusci/core/dependencies/__init__.py +55 -0
- cumulusci/core/dependencies/base.py +561 -0
- cumulusci/core/dependencies/dependencies.py +273 -0
- cumulusci/core/dependencies/github.py +177 -0
- cumulusci/core/dependencies/github_resolvers.py +244 -0
- cumulusci/core/dependencies/resolvers.py +580 -0
- cumulusci/core/dependencies/tests/__init__.py +0 -0
- cumulusci/core/dependencies/tests/conftest.py +385 -0
- cumulusci/core/dependencies/tests/test_dependencies.py +950 -0
- cumulusci/core/dependencies/tests/test_github.py +83 -0
- cumulusci/core/dependencies/tests/test_resolvers.py +1027 -0
- cumulusci/core/dependencies/utils.py +13 -0
- cumulusci/core/enums.py +11 -0
- cumulusci/core/exceptions.py +311 -0
- cumulusci/core/flowrunner.py +888 -0
- cumulusci/core/github.py +665 -0
- cumulusci/core/keychain/__init__.py +24 -0
- cumulusci/core/keychain/base_project_keychain.py +441 -0
- cumulusci/core/keychain/encrypted_file_project_keychain.py +945 -0
- cumulusci/core/keychain/environment_project_keychain.py +7 -0
- cumulusci/core/keychain/serialization.py +152 -0
- cumulusci/core/keychain/subprocess_keychain.py +24 -0
- cumulusci/core/keychain/tests/conftest.py +50 -0
- cumulusci/core/keychain/tests/test_base_project_keychain.py +299 -0
- cumulusci/core/keychain/tests/test_encrypted_file_project_keychain.py +1228 -0
- cumulusci/core/metadeploy/__init__.py +0 -0
- cumulusci/core/metadeploy/api.py +88 -0
- cumulusci/core/metadeploy/plans.py +25 -0
- cumulusci/core/metadeploy/tests/test_api.py +276 -0
- cumulusci/core/runtime.py +115 -0
- cumulusci/core/sfdx.py +162 -0
- cumulusci/core/source/__init__.py +16 -0
- cumulusci/core/source/github.py +50 -0
- cumulusci/core/source/local_folder.py +35 -0
- cumulusci/core/source_transforms/__init__.py +0 -0
- cumulusci/core/source_transforms/tests/test_transforms.py +1091 -0
- cumulusci/core/source_transforms/transforms.py +532 -0
- cumulusci/core/tasks.py +404 -0
- cumulusci/core/template_utils.py +59 -0
- cumulusci/core/tests/__init__.py +0 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_e2e.yaml +215 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_extract_standard_objects.yaml +199 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_read_explicit_extract_declaration.yaml +3 -0
- cumulusci/core/tests/fake_remote_repo/cumulusci.yml +32 -0
- cumulusci/core/tests/fake_remote_repo/tasks/directory/example_2.py +6 -0
- cumulusci/core/tests/fake_remote_repo/tasks/example.py +43 -0
- cumulusci/core/tests/fake_remote_repo_2/cumulusci.yml +11 -0
- cumulusci/core/tests/fake_remote_repo_2/tasks/example_3.py +6 -0
- cumulusci/core/tests/test_datasets_e2e.py +386 -0
- cumulusci/core/tests/test_exceptions.py +11 -0
- cumulusci/core/tests/test_flowrunner.py +836 -0
- cumulusci/core/tests/test_github.py +942 -0
- cumulusci/core/tests/test_sfdx.py +138 -0
- cumulusci/core/tests/test_source.py +678 -0
- cumulusci/core/tests/test_tasks.py +262 -0
- cumulusci/core/tests/test_utils.py +141 -0
- cumulusci/core/tests/test_utils_merge_config.py +276 -0
- cumulusci/core/tests/test_versions.py +76 -0
- cumulusci/core/tests/untrusted_repo_child/cumulusci.yml +7 -0
- cumulusci/core/tests/untrusted_repo_child/tasks/untrusted_child.py +6 -0
- cumulusci/core/tests/untrusted_repo_parent/cumulusci.yml +26 -0
- cumulusci/core/tests/untrusted_repo_parent/tasks/untrusted_parent.py +6 -0
- cumulusci/core/tests/utils.py +116 -0
- cumulusci/core/tests/yaml/global.yaml +0 -0
- cumulusci/core/utils.py +402 -0
- cumulusci/core/versions.py +149 -0
- cumulusci/cumulusci.yml +1621 -0
- cumulusci/files/admin_profile.xml +20 -0
- cumulusci/files/delete_excludes.txt +424 -0
- cumulusci/files/templates/project/README.md +12 -0
- cumulusci/files/templates/project/cumulusci.yml +63 -0
- cumulusci/files/templates/project/dot-gitignore +60 -0
- cumulusci/files/templates/project/mapping.yml +45 -0
- cumulusci/files/templates/project/scratch_def.json +25 -0
- cumulusci/oauth/__init__.py +0 -0
- cumulusci/oauth/client.py +400 -0
- cumulusci/oauth/exceptions.py +9 -0
- cumulusci/oauth/salesforce.py +95 -0
- cumulusci/oauth/tests/__init__.py +0 -0
- cumulusci/oauth/tests/cassettes/test_get_device_code.yaml +22 -0
- cumulusci/oauth/tests/cassettes/test_get_device_oauth_token.yaml +74 -0
- cumulusci/oauth/tests/test_client.py +308 -0
- cumulusci/oauth/tests/test_salesforce.py +46 -0
- cumulusci/plugins/__init__.py +3 -0
- cumulusci/plugins/plugin_base.py +93 -0
- cumulusci/plugins/plugin_loader.py +59 -0
- cumulusci/robotframework/CumulusCI.py +340 -0
- cumulusci/robotframework/CumulusCI.robot +7 -0
- cumulusci/robotframework/Performance.py +165 -0
- cumulusci/robotframework/Salesforce.py +936 -0
- cumulusci/robotframework/Salesforce.robot +192 -0
- cumulusci/robotframework/SalesforceAPI.py +416 -0
- cumulusci/robotframework/SalesforcePlaywright.py +220 -0
- cumulusci/robotframework/SalesforcePlaywright.robot +40 -0
- cumulusci/robotframework/__init__.py +2 -0
- cumulusci/robotframework/base_library.py +39 -0
- cumulusci/robotframework/faker_mixin.py +89 -0
- cumulusci/robotframework/form_handlers.py +222 -0
- cumulusci/robotframework/javascript/cci_init.js +34 -0
- cumulusci/robotframework/javascript/cumulusci.js +4 -0
- cumulusci/robotframework/locator_manager.py +197 -0
- cumulusci/robotframework/locators_56.py +88 -0
- cumulusci/robotframework/locators_57.py +5 -0
- cumulusci/robotframework/pageobjects/BasePageObjects.py +433 -0
- cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +246 -0
- cumulusci/robotframework/pageobjects/PageObjectLibrary.py +45 -0
- cumulusci/robotframework/pageobjects/PageObjects.py +351 -0
- cumulusci/robotframework/pageobjects/__init__.py +12 -0
- cumulusci/robotframework/pageobjects/baseobjects.py +120 -0
- cumulusci/robotframework/perftests/short/collection_perf.robot +105 -0
- cumulusci/robotframework/tests/CustomObjectTestPage.py +10 -0
- cumulusci/robotframework/tests/FooTestPage.py +8 -0
- cumulusci/robotframework/tests/cumulusci/base.robot +40 -0
- cumulusci/robotframework/tests/cumulusci/bulkdata.robot +38 -0
- cumulusci/robotframework/tests/cumulusci/communities.robot +57 -0
- cumulusci/robotframework/tests/cumulusci/datagen.robot +84 -0
- cumulusci/robotframework/tests/salesforce/TestLibraryA.py +24 -0
- cumulusci/robotframework/tests/salesforce/TestLibraryB.py +20 -0
- cumulusci/robotframework/tests/salesforce/TestListener.py +93 -0
- cumulusci/robotframework/tests/salesforce/api.robot +178 -0
- cumulusci/robotframework/tests/salesforce/browsers.robot +143 -0
- cumulusci/robotframework/tests/salesforce/classic.robot +51 -0
- cumulusci/robotframework/tests/salesforce/create_contact.robot +59 -0
- cumulusci/robotframework/tests/salesforce/faker.robot +68 -0
- cumulusci/robotframework/tests/salesforce/forms.robot +172 -0
- cumulusci/robotframework/tests/salesforce/label_locator.robot +244 -0
- cumulusci/robotframework/tests/salesforce/labels.html +33 -0
- cumulusci/robotframework/tests/salesforce/locators.robot +149 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/base_pageobjects.robot +100 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/example_page_object.py +25 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/listing_page.robot +115 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/objectmanager.robot +74 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/pageobjects.robot +171 -0
- cumulusci/robotframework/tests/salesforce/performance.robot +109 -0
- cumulusci/robotframework/tests/salesforce/playwright/javascript_keywords.robot +33 -0
- cumulusci/robotframework/tests/salesforce/playwright/open_test_browser.robot +48 -0
- cumulusci/robotframework/tests/salesforce/playwright/playwright.robot +24 -0
- cumulusci/robotframework/tests/salesforce/playwright/ui.robot +32 -0
- cumulusci/robotframework/tests/salesforce/populate.robot +89 -0
- cumulusci/robotframework/tests/salesforce/test_testlistener.py +37 -0
- cumulusci/robotframework/tests/salesforce/ui.robot +361 -0
- cumulusci/robotframework/tests/test_cumulusci_library.py +304 -0
- cumulusci/robotframework/tests/test_locator_manager.py +158 -0
- cumulusci/robotframework/tests/test_pageobjects.py +291 -0
- cumulusci/robotframework/tests/test_performance.py +38 -0
- cumulusci/robotframework/tests/test_salesforce.py +79 -0
- cumulusci/robotframework/tests/test_salesforce_locators.py +73 -0
- cumulusci/robotframework/tests/test_template_util.py +53 -0
- cumulusci/robotframework/tests/test_utils.py +106 -0
- cumulusci/robotframework/utils.py +283 -0
- cumulusci/salesforce_api/__init__.py +0 -0
- cumulusci/salesforce_api/exceptions.py +23 -0
- cumulusci/salesforce_api/filterable_objects.py +96 -0
- cumulusci/salesforce_api/mc_soap_envelopes.py +89 -0
- cumulusci/salesforce_api/metadata.py +721 -0
- cumulusci/salesforce_api/org_schema.py +571 -0
- cumulusci/salesforce_api/org_schema_models.py +226 -0
- cumulusci/salesforce_api/package_install.py +265 -0
- cumulusci/salesforce_api/package_zip.py +301 -0
- cumulusci/salesforce_api/rest_deploy.py +148 -0
- cumulusci/salesforce_api/retrieve_profile_api.py +301 -0
- cumulusci/salesforce_api/soap_envelopes.py +177 -0
- cumulusci/salesforce_api/tests/__init__.py +0 -0
- cumulusci/salesforce_api/tests/metadata_test_strings.py +24 -0
- cumulusci/salesforce_api/tests/test_metadata.py +1015 -0
- cumulusci/salesforce_api/tests/test_package_install.py +219 -0
- cumulusci/salesforce_api/tests/test_package_zip.py +380 -0
- cumulusci/salesforce_api/tests/test_rest_deploy.py +264 -0
- cumulusci/salesforce_api/tests/test_retrieve_profile_api.py +337 -0
- cumulusci/salesforce_api/tests/test_utils.py +124 -0
- cumulusci/salesforce_api/utils.py +51 -0
- cumulusci/schema/cumulusci.jsonschema.json +782 -0
- cumulusci/tasks/__init__.py +0 -0
- cumulusci/tasks/apex/__init__.py +0 -0
- cumulusci/tasks/apex/anon.py +157 -0
- cumulusci/tasks/apex/batch.py +180 -0
- cumulusci/tasks/apex/testrunner.py +835 -0
- cumulusci/tasks/apex/tests/cassettes/ManualEditTestApexIntegrationTests.test_run_tests__integration_test.yaml +703 -0
- cumulusci/tasks/apex/tests/test_apex_tasks.py +1558 -0
- cumulusci/tasks/base_source_control_task.py +17 -0
- cumulusci/tasks/bulkdata/__init__.py +15 -0
- cumulusci/tasks/bulkdata/base_generate_data_task.py +96 -0
- cumulusci/tasks/bulkdata/dates.py +97 -0
- cumulusci/tasks/bulkdata/delete.py +156 -0
- cumulusci/tasks/bulkdata/extract.py +441 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/calculate_dependencies.py +117 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/extract_yml.py +123 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/hardcoded_default_declarations.py +49 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/synthesize_extract_declarations.py +283 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_extract_yml.py +142 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_synthesize_extract_declarations.py +575 -0
- cumulusci/tasks/bulkdata/factory_utils.py +134 -0
- cumulusci/tasks/bulkdata/generate.py +4 -0
- cumulusci/tasks/bulkdata/generate_and_load_data.py +232 -0
- cumulusci/tasks/bulkdata/generate_and_load_data_from_yaml.py +19 -0
- cumulusci/tasks/bulkdata/generate_from_yaml.py +183 -0
- cumulusci/tasks/bulkdata/generate_mapping.py +434 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/dependency_map.py +169 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/extract_mapping_file_generator.py +45 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/generate_mapping_from_declarations.py +121 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/load_mapping_file_generator.py +127 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_generator_post_processes.py +53 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_transforms.py +139 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_extract_mapping_from_declarations.py +135 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_load_mapping_from_declarations.py +330 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_generator_post_processes.py +60 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_transforms.py +188 -0
- cumulusci/tasks/bulkdata/load.py +1196 -0
- cumulusci/tasks/bulkdata/mapping_parser.py +811 -0
- cumulusci/tasks/bulkdata/query_transformers.py +264 -0
- cumulusci/tasks/bulkdata/select_utils.py +792 -0
- cumulusci/tasks/bulkdata/snowfakery.py +753 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/queue_manager.py +478 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_run_until.py +141 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_working_directory.py +53 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/subtask_configurator.py +64 -0
- cumulusci/tasks/bulkdata/step.py +1242 -0
- cumulusci/tasks/bulkdata/tests/__init__.py +0 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_random_strategy.yaml +147 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_annoy_strategy.yaml +123 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy.yaml +313 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy_bulk.yaml +550 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_strategy.yaml +175 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_standard_strategy.yaml +147 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__multiple_needed.yaml +69 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__none_needed.yaml +22 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__one_needed.yaml +24 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_snowfakery_query_salesforce.yaml +25 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpdatesIntegrationTests.test_updates_task.yaml +80 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_simple_upsert__rest.yaml +270 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert__rest.yaml +267 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field__rest.yaml +369 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field_rest__duplicate_error.yaml +204 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_fields__bulk.yaml +675 -0
- cumulusci/tasks/bulkdata/tests/dummy_data_factory.py +36 -0
- cumulusci/tasks/bulkdata/tests/integration_test_utils.py +49 -0
- cumulusci/tasks/bulkdata/tests/mapping-oid.yml +87 -0
- cumulusci/tasks/bulkdata/tests/mapping_after.yml +38 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly.yml +34 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly_incomplete.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly_wrong.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_strategy.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_number.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_strategy.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__non_float.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_missing_priority_fields.yml +22 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_no_priority_fields.yml +18 -0
- cumulusci/tasks/bulkdata/tests/mapping_simple.yml +27 -0
- cumulusci/tasks/bulkdata/tests/mapping_v1.yml +28 -0
- cumulusci/tasks/bulkdata/tests/mapping_v2.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_v3.yml +32 -0
- cumulusci/tasks/bulkdata/tests/mapping_vanilla_sf.yml +69 -0
- cumulusci/tasks/bulkdata/tests/mock_data_factory_without_mapping.py +12 -0
- cumulusci/tasks/bulkdata/tests/person_accounts.yml +23 -0
- cumulusci/tasks/bulkdata/tests/person_accounts_minimal.yml +15 -0
- cumulusci/tasks/bulkdata/tests/recordtypes.yml +8 -0
- cumulusci/tasks/bulkdata/tests/recordtypes_2.yml +6 -0
- cumulusci/tasks/bulkdata/tests/recordtypes_with_ispersontype.yml +8 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/child/child2.yml +3 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/child.yml +4 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/gen_npsp_standard_objects.recipe.yml +89 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/include_parent.yml +3 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/npsp_standard_objects_macros.yml +34 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/options.recipe.yml +6 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/query_snowfakery.recipe.yml +16 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/sf_standard_object_macros.yml +83 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.load.yml +2 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.recipe.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_2.load.yml +5 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.load.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.recipe.yml +12 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels_2.load.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/unique_values.recipe.yml +4 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert.recipe.yml +23 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert_2.recipe.yml +29 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert_before.yml +10 -0
- cumulusci/tasks/bulkdata/tests/test_base_generate_data_tasks.py +61 -0
- cumulusci/tasks/bulkdata/tests/test_dates.py +99 -0
- cumulusci/tasks/bulkdata/tests/test_delete.py +404 -0
- cumulusci/tasks/bulkdata/tests/test_extract.py +1311 -0
- cumulusci/tasks/bulkdata/tests/test_factory_utils.py +55 -0
- cumulusci/tasks/bulkdata/tests/test_generate_and_load.py +252 -0
- cumulusci/tasks/bulkdata/tests/test_generate_from_snowfakery_task.py +343 -0
- cumulusci/tasks/bulkdata/tests/test_generatemapping.py +1039 -0
- cumulusci/tasks/bulkdata/tests/test_load.py +3175 -0
- cumulusci/tasks/bulkdata/tests/test_mapping_parser.py +1658 -0
- cumulusci/tasks/bulkdata/tests/test_query_db__joins_self_lookups.yml +12 -0
- cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups.yml +26 -0
- cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups_select.yml +48 -0
- cumulusci/tasks/bulkdata/tests/test_select.py +171 -0
- cumulusci/tasks/bulkdata/tests/test_select_utils.py +1057 -0
- cumulusci/tasks/bulkdata/tests/test_snowfakery.py +1153 -0
- cumulusci/tasks/bulkdata/tests/test_step.py +3957 -0
- cumulusci/tasks/bulkdata/tests/test_updates.py +513 -0
- cumulusci/tasks/bulkdata/tests/test_upsert.py +1015 -0
- cumulusci/tasks/bulkdata/tests/test_utils.py +158 -0
- cumulusci/tasks/bulkdata/tests/testdata.db +0 -0
- cumulusci/tasks/bulkdata/tests/update_describe.py +50 -0
- cumulusci/tasks/bulkdata/tests/update_person_accounts.yml +23 -0
- cumulusci/tasks/bulkdata/tests/utils.py +114 -0
- cumulusci/tasks/bulkdata/update_data.py +260 -0
- cumulusci/tasks/bulkdata/upsert_utils.py +130 -0
- cumulusci/tasks/bulkdata/utils.py +249 -0
- cumulusci/tasks/command.py +178 -0
- cumulusci/tasks/connectedapp.py +186 -0
- cumulusci/tasks/create_package_version.py +778 -0
- cumulusci/tasks/datadictionary.py +745 -0
- cumulusci/tasks/dx_convert_from.py +26 -0
- cumulusci/tasks/github/__init__.py +17 -0
- cumulusci/tasks/github/base.py +16 -0
- cumulusci/tasks/github/commit_status.py +13 -0
- cumulusci/tasks/github/merge.py +11 -0
- cumulusci/tasks/github/publish.py +11 -0
- cumulusci/tasks/github/pull_request.py +11 -0
- cumulusci/tasks/github/release.py +11 -0
- cumulusci/tasks/github/release_report.py +11 -0
- cumulusci/tasks/github/tag.py +11 -0
- cumulusci/tasks/github/tests/__init__.py +0 -0
- cumulusci/tasks/github/tests/test_util.py +202 -0
- cumulusci/tasks/github/tests/test_vcs_migration.py +44 -0
- cumulusci/tasks/github/tests/util_github_api.py +666 -0
- cumulusci/tasks/github/util.py +252 -0
- cumulusci/tasks/marketing_cloud/__init__.py +0 -0
- cumulusci/tasks/marketing_cloud/api.py +188 -0
- cumulusci/tasks/marketing_cloud/base.py +38 -0
- cumulusci/tasks/marketing_cloud/deploy.py +345 -0
- cumulusci/tasks/marketing_cloud/get_user_info.py +40 -0
- cumulusci/tasks/marketing_cloud/mc_constants.py +1 -0
- cumulusci/tasks/marketing_cloud/tests/__init__.py +0 -0
- cumulusci/tasks/marketing_cloud/tests/conftest.py +46 -0
- cumulusci/tasks/marketing_cloud/tests/expected-payload.json +110 -0
- cumulusci/tasks/marketing_cloud/tests/test_api.py +97 -0
- cumulusci/tasks/marketing_cloud/tests/test_api_soap_envelopes.py +145 -0
- cumulusci/tasks/marketing_cloud/tests/test_base.py +14 -0
- cumulusci/tasks/marketing_cloud/tests/test_deploy.py +400 -0
- cumulusci/tasks/marketing_cloud/tests/test_get_user_info.py +141 -0
- cumulusci/tasks/marketing_cloud/tests/validation-response.json +39 -0
- cumulusci/tasks/metadata/__init__.py +0 -0
- cumulusci/tasks/metadata/ee_src.py +94 -0
- cumulusci/tasks/metadata/managed_src.py +100 -0
- cumulusci/tasks/metadata/metadata_map.yml +868 -0
- cumulusci/tasks/metadata/modify.py +99 -0
- cumulusci/tasks/metadata/package.py +684 -0
- cumulusci/tasks/metadata/tests/__init__.py +0 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/.hidden/.keep +0 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/destructiveChanges.xml +9 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package.xml +9 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package_install_uninstall.xml +11 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/reports/namespace__TestFolder/TestReport.report +3 -0
- cumulusci/tasks/metadata/tests/sample_package.xml +9 -0
- cumulusci/tasks/metadata/tests/test_ee_src.py +112 -0
- cumulusci/tasks/metadata/tests/test_managed_src.py +111 -0
- cumulusci/tasks/metadata/tests/test_modify.py +123 -0
- cumulusci/tasks/metadata/tests/test_package.py +476 -0
- cumulusci/tasks/metadata_etl/__init__.py +29 -0
- cumulusci/tasks/metadata_etl/base.py +436 -0
- cumulusci/tasks/metadata_etl/duplicate_rules.py +24 -0
- cumulusci/tasks/metadata_etl/field_sets.py +70 -0
- cumulusci/tasks/metadata_etl/help_text.py +92 -0
- cumulusci/tasks/metadata_etl/layouts.py +550 -0
- cumulusci/tasks/metadata_etl/objects.py +68 -0
- cumulusci/tasks/metadata_etl/permissions.py +167 -0
- cumulusci/tasks/metadata_etl/picklists.py +221 -0
- cumulusci/tasks/metadata_etl/remote_site_settings.py +99 -0
- cumulusci/tasks/metadata_etl/sharing.py +138 -0
- cumulusci/tasks/metadata_etl/tests/test_base.py +512 -0
- cumulusci/tasks/metadata_etl/tests/test_duplicate_rules.py +22 -0
- cumulusci/tasks/metadata_etl/tests/test_field_sets.py +156 -0
- cumulusci/tasks/metadata_etl/tests/test_help_text.py +387 -0
- cumulusci/tasks/metadata_etl/tests/test_ip_ranges.py +85 -0
- cumulusci/tasks/metadata_etl/tests/test_layouts.py +858 -0
- cumulusci/tasks/metadata_etl/tests/test_objects.py +236 -0
- cumulusci/tasks/metadata_etl/tests/test_permissions.py +223 -0
- cumulusci/tasks/metadata_etl/tests/test_picklists.py +547 -0
- cumulusci/tasks/metadata_etl/tests/test_remote_site_settings.py +46 -0
- cumulusci/tasks/metadata_etl/tests/test_sharing.py +333 -0
- cumulusci/tasks/metadata_etl/tests/test_value_sets.py +298 -0
- cumulusci/tasks/metadata_etl/value_sets.py +106 -0
- cumulusci/tasks/metadeploy.py +393 -0
- cumulusci/tasks/metaxml.py +88 -0
- cumulusci/tasks/preflight/__init__.py +0 -0
- cumulusci/tasks/preflight/dataset_load.py +49 -0
- cumulusci/tasks/preflight/licenses.py +86 -0
- cumulusci/tasks/preflight/packages.py +14 -0
- cumulusci/tasks/preflight/permsets.py +23 -0
- cumulusci/tasks/preflight/recordtypes.py +16 -0
- cumulusci/tasks/preflight/retrieve_tasks.py +30 -0
- cumulusci/tasks/preflight/settings.py +77 -0
- cumulusci/tasks/preflight/sobjects.py +202 -0
- cumulusci/tasks/preflight/tests/test_dataset_load.py +85 -0
- cumulusci/tasks/preflight/tests/test_licenses.py +174 -0
- cumulusci/tasks/preflight/tests/test_packages.py +14 -0
- cumulusci/tasks/preflight/tests/test_permset_preflights.py +51 -0
- cumulusci/tasks/preflight/tests/test_recordtypes.py +30 -0
- cumulusci/tasks/preflight/tests/test_retrieve_tasks.py +62 -0
- cumulusci/tasks/preflight/tests/test_settings.py +130 -0
- cumulusci/tasks/preflight/tests/test_sobjects.py +231 -0
- cumulusci/tasks/push/README.md +59 -0
- cumulusci/tasks/push/__init__.py +0 -0
- cumulusci/tasks/push/push_api.py +659 -0
- cumulusci/tasks/push/pushfails.py +136 -0
- cumulusci/tasks/push/tasks.py +476 -0
- cumulusci/tasks/push/tests/conftest.py +263 -0
- cumulusci/tasks/push/tests/test_push_api.py +951 -0
- cumulusci/tasks/push/tests/test_push_tasks.py +659 -0
- cumulusci/tasks/release_notes/README.md +63 -0
- cumulusci/tasks/release_notes/__init__.py +0 -0
- cumulusci/tasks/release_notes/exceptions.py +5 -0
- cumulusci/tasks/release_notes/generator.py +137 -0
- cumulusci/tasks/release_notes/parser.py +232 -0
- cumulusci/tasks/release_notes/provider.py +44 -0
- cumulusci/tasks/release_notes/task.py +300 -0
- cumulusci/tasks/release_notes/tests/__init__.py +0 -0
- cumulusci/tasks/release_notes/tests/change_notes/full/example1.md +17 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/1.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/2.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/3.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/single/1.txt +1 -0
- cumulusci/tasks/release_notes/tests/test_generator.py +582 -0
- cumulusci/tasks/release_notes/tests/test_parser.py +867 -0
- cumulusci/tasks/release_notes/tests/test_provider.py +512 -0
- cumulusci/tasks/release_notes/tests/test_task.py +461 -0
- cumulusci/tasks/release_notes/tests/utils.py +153 -0
- cumulusci/tasks/robotframework/__init__.py +3 -0
- cumulusci/tasks/robotframework/debugger/DebugListener.py +100 -0
- cumulusci/tasks/robotframework/debugger/__init__.py +10 -0
- cumulusci/tasks/robotframework/debugger/model.py +87 -0
- cumulusci/tasks/robotframework/debugger/ui.py +259 -0
- cumulusci/tasks/robotframework/libdoc.py +269 -0
- cumulusci/tasks/robotframework/robotframework.py +392 -0
- cumulusci/tasks/robotframework/stylesheet.css +130 -0
- cumulusci/tasks/robotframework/template.html +109 -0
- cumulusci/tasks/robotframework/tests/TestLibrary.py +18 -0
- cumulusci/tasks/robotframework/tests/TestPageObjects.py +31 -0
- cumulusci/tasks/robotframework/tests/TestResource.robot +8 -0
- cumulusci/tasks/robotframework/tests/failing_tests.robot +16 -0
- cumulusci/tasks/robotframework/tests/performance.robot +23 -0
- cumulusci/tasks/robotframework/tests/test_browser_proxies.py +137 -0
- cumulusci/tasks/robotframework/tests/test_debugger.py +360 -0
- cumulusci/tasks/robotframework/tests/test_robot_parallel.py +141 -0
- cumulusci/tasks/robotframework/tests/test_robotframework.py +860 -0
- cumulusci/tasks/salesforce/BaseRetrieveMetadata.py +58 -0
- cumulusci/tasks/salesforce/BaseSalesforceApiTask.py +45 -0
- cumulusci/tasks/salesforce/BaseSalesforceMetadataApiTask.py +18 -0
- cumulusci/tasks/salesforce/BaseSalesforceTask.py +4 -0
- cumulusci/tasks/salesforce/BaseUninstallMetadata.py +41 -0
- cumulusci/tasks/salesforce/CreateCommunity.py +124 -0
- cumulusci/tasks/salesforce/CreatePackage.py +29 -0
- cumulusci/tasks/salesforce/Deploy.py +240 -0
- cumulusci/tasks/salesforce/DeployBundles.py +88 -0
- cumulusci/tasks/salesforce/DescribeMetadataTypes.py +26 -0
- cumulusci/tasks/salesforce/EnsureRecordTypes.py +202 -0
- cumulusci/tasks/salesforce/GetInstalledPackages.py +8 -0
- cumulusci/tasks/salesforce/ListCommunities.py +40 -0
- cumulusci/tasks/salesforce/ListCommunityTemplates.py +19 -0
- cumulusci/tasks/salesforce/PublishCommunity.py +62 -0
- cumulusci/tasks/salesforce/RetrievePackaged.py +41 -0
- cumulusci/tasks/salesforce/RetrieveReportsAndDashboards.py +82 -0
- cumulusci/tasks/salesforce/RetrieveUnpackaged.py +36 -0
- cumulusci/tasks/salesforce/SOQLQuery.py +39 -0
- cumulusci/tasks/salesforce/UninstallLocal.py +15 -0
- cumulusci/tasks/salesforce/UninstallLocalBundles.py +28 -0
- cumulusci/tasks/salesforce/UninstallLocalNamespacedBundles.py +58 -0
- cumulusci/tasks/salesforce/UninstallPackage.py +32 -0
- cumulusci/tasks/salesforce/UninstallPackaged.py +56 -0
- cumulusci/tasks/salesforce/UpdateAdminProfile.py +8 -0
- cumulusci/tasks/salesforce/__init__.py +79 -0
- cumulusci/tasks/salesforce/activate_flow.py +74 -0
- cumulusci/tasks/salesforce/check_components.py +324 -0
- cumulusci/tasks/salesforce/composite.py +142 -0
- cumulusci/tasks/salesforce/create_permission_sets.py +35 -0
- cumulusci/tasks/salesforce/custom_settings.py +134 -0
- cumulusci/tasks/salesforce/custom_settings_wait.py +132 -0
- cumulusci/tasks/salesforce/enable_prediction.py +107 -0
- cumulusci/tasks/salesforce/insert_record.py +40 -0
- cumulusci/tasks/salesforce/install_package_version.py +242 -0
- cumulusci/tasks/salesforce/license_preflights.py +8 -0
- cumulusci/tasks/salesforce/network_member_group.py +178 -0
- cumulusci/tasks/salesforce/nonsourcetracking.py +228 -0
- cumulusci/tasks/salesforce/org_settings.py +193 -0
- cumulusci/tasks/salesforce/package_upload.py +328 -0
- cumulusci/tasks/salesforce/profiles.py +74 -0
- cumulusci/tasks/salesforce/promote_package_version.py +376 -0
- cumulusci/tasks/salesforce/retrieve_profile.py +195 -0
- cumulusci/tasks/salesforce/salesforce_files.py +244 -0
- cumulusci/tasks/salesforce/sourcetracking.py +507 -0
- cumulusci/tasks/salesforce/tests/__init__.py +3 -0
- cumulusci/tasks/salesforce/tests/test_CreateCommunity.py +278 -0
- cumulusci/tasks/salesforce/tests/test_CreatePackage.py +22 -0
- cumulusci/tasks/salesforce/tests/test_Deploy.py +470 -0
- cumulusci/tasks/salesforce/tests/test_DeployBundles.py +76 -0
- cumulusci/tasks/salesforce/tests/test_EnsureRecordTypes.py +345 -0
- cumulusci/tasks/salesforce/tests/test_ListCommunities.py +84 -0
- cumulusci/tasks/salesforce/tests/test_ListCommunityTemplates.py +49 -0
- cumulusci/tasks/salesforce/tests/test_PackageUpload.py +547 -0
- cumulusci/tasks/salesforce/tests/test_ProfileGrantAllAccess.py +699 -0
- cumulusci/tasks/salesforce/tests/test_PublishCommunity.py +181 -0
- cumulusci/tasks/salesforce/tests/test_RetrievePackaged.py +24 -0
- cumulusci/tasks/salesforce/tests/test_RetrieveReportsAndDashboards.py +56 -0
- cumulusci/tasks/salesforce/tests/test_RetrieveUnpackaged.py +21 -0
- cumulusci/tasks/salesforce/tests/test_SOQLQuery.py +30 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocal.py +15 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocalBundles.py +19 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocalNamespacedBundles.py +22 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackage.py +19 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackaged.py +66 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackagedIncremental.py +127 -0
- cumulusci/tasks/salesforce/tests/test_activate_flow.py +132 -0
- cumulusci/tasks/salesforce/tests/test_base_tasks.py +110 -0
- cumulusci/tasks/salesforce/tests/test_check_components.py +445 -0
- cumulusci/tasks/salesforce/tests/test_composite.py +250 -0
- cumulusci/tasks/salesforce/tests/test_create_permission_sets.py +41 -0
- cumulusci/tasks/salesforce/tests/test_custom_settings.py +227 -0
- cumulusci/tasks/salesforce/tests/test_custom_settings_wait.py +174 -0
- cumulusci/tasks/salesforce/tests/test_describemetadatatypes.py +18 -0
- cumulusci/tasks/salesforce/tests/test_enable_prediction.py +240 -0
- cumulusci/tasks/salesforce/tests/test_insert_record.py +110 -0
- cumulusci/tasks/salesforce/tests/test_install_package_version.py +464 -0
- cumulusci/tasks/salesforce/tests/test_network_member_group.py +444 -0
- cumulusci/tasks/salesforce/tests/test_nonsourcetracking.py +235 -0
- cumulusci/tasks/salesforce/tests/test_org_settings.py +407 -0
- cumulusci/tasks/salesforce/tests/test_profiles.py +202 -0
- cumulusci/tasks/salesforce/tests/test_retrieve_profile.py +287 -0
- cumulusci/tasks/salesforce/tests/test_salesforce_files.py +228 -0
- cumulusci/tasks/salesforce/tests/test_sourcetracking.py +350 -0
- cumulusci/tasks/salesforce/tests/test_trigger_handlers.py +300 -0
- cumulusci/tasks/salesforce/tests/test_update_dependencies.py +509 -0
- cumulusci/tasks/salesforce/tests/util.py +79 -0
- cumulusci/tasks/salesforce/trigger_handlers.py +119 -0
- cumulusci/tasks/salesforce/uninstall_packaged_incremental.py +136 -0
- cumulusci/tasks/salesforce/update_dependencies.py +290 -0
- cumulusci/tasks/salesforce/update_profile.py +339 -0
- cumulusci/tasks/salesforce/users/permsets.py +227 -0
- cumulusci/tasks/salesforce/users/photos.py +162 -0
- cumulusci/tasks/salesforce/users/tests/photo.mock.txt +1 -0
- cumulusci/tasks/salesforce/users/tests/test_permsets.py +950 -0
- cumulusci/tasks/salesforce/users/tests/test_photos.py +373 -0
- cumulusci/tasks/sample_data/capture_sample_data.py +77 -0
- cumulusci/tasks/sample_data/load_sample_data.py +85 -0
- cumulusci/tasks/sample_data/test_capture_sample_data.py +117 -0
- cumulusci/tasks/sample_data/test_load_sample_data.py +121 -0
- cumulusci/tasks/sfdx.py +83 -0
- cumulusci/tasks/tests/__init__.py +1 -0
- cumulusci/tasks/tests/conftest.py +30 -0
- cumulusci/tasks/tests/test_command.py +129 -0
- cumulusci/tasks/tests/test_connectedapp.py +236 -0
- cumulusci/tasks/tests/test_create_package_version.py +847 -0
- cumulusci/tasks/tests/test_datadictionary.py +1575 -0
- cumulusci/tasks/tests/test_dx_convert_from.py +60 -0
- cumulusci/tasks/tests/test_metadeploy.py +624 -0
- cumulusci/tasks/tests/test_metaxml.py +99 -0
- cumulusci/tasks/tests/test_promote_package_version.py +488 -0
- cumulusci/tasks/tests/test_pushfails.py +96 -0
- cumulusci/tasks/tests/test_salesforce.py +72 -0
- cumulusci/tasks/tests/test_sfdx.py +105 -0
- cumulusci/tasks/tests/test_util.py +207 -0
- cumulusci/tasks/util.py +261 -0
- cumulusci/tasks/vcs/__init__.py +19 -0
- cumulusci/tasks/vcs/commit_status.py +58 -0
- cumulusci/tasks/vcs/create_commit_status.py +37 -0
- cumulusci/tasks/vcs/download_extract.py +199 -0
- cumulusci/tasks/vcs/merge.py +298 -0
- cumulusci/tasks/vcs/publish.py +207 -0
- cumulusci/tasks/vcs/pull_request.py +9 -0
- cumulusci/tasks/vcs/release.py +134 -0
- cumulusci/tasks/vcs/release_report.py +105 -0
- cumulusci/tasks/vcs/tag.py +31 -0
- cumulusci/tasks/vcs/tests/github/test_commit_status.py +196 -0
- cumulusci/tasks/vcs/tests/github/test_download_extract.py +896 -0
- cumulusci/tasks/vcs/tests/github/test_merge.py +1118 -0
- cumulusci/tasks/vcs/tests/github/test_publish.py +823 -0
- cumulusci/tasks/vcs/tests/github/test_pull_request.py +29 -0
- cumulusci/tasks/vcs/tests/github/test_release.py +390 -0
- cumulusci/tasks/vcs/tests/github/test_release_report.py +109 -0
- cumulusci/tasks/vcs/tests/github/test_tag.py +90 -0
- cumulusci/tasks/vlocity/exceptions.py +2 -0
- cumulusci/tasks/vlocity/tests/test_vlocity.py +283 -0
- cumulusci/tasks/vlocity/vlocity.py +342 -0
- cumulusci/tests/__init__.py +1 -0
- cumulusci/tests/cassettes/GET_sobjects_Account_PersonAccount_describe.yaml +18 -0
- cumulusci/tests/cassettes/TestIntegrationInfrastructure.test_integration_tests.yaml +19 -0
- cumulusci/tests/pytest_plugins/pytest_sf_orgconnect.py +307 -0
- cumulusci/tests/pytest_plugins/pytest_sf_vcr.py +275 -0
- cumulusci/tests/pytest_plugins/pytest_sf_vcr_serializer.py +160 -0
- cumulusci/tests/pytest_plugins/pytest_typeguard.py +5 -0
- cumulusci/tests/pytest_plugins/test_vcr_string_compressor.py +49 -0
- cumulusci/tests/pytest_plugins/vcr_string_compressor.py +97 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Account_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Case_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Contact_describe.yaml +4838 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Custom__c_describe.yaml +242 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Event_describe.yaml +19 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Global_describe.yaml +1338 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Lead_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_OpportunityContactRole_describe.yaml +34 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Opportunity_describe.yaml +1261 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Organization.yaml +49 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfoList_xml.tpl +15 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfo_xml.tpl +13 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_insert_xml.tpl +24 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_upsert_xml.tpl +25 -0
- cumulusci/tests/test_entry_points.py +20 -0
- cumulusci/tests/test_integration_infrastructure.py +131 -0
- cumulusci/tests/test_main.py +9 -0
- cumulusci/tests/test_schema.py +32 -0
- cumulusci/tests/test_utils.py +657 -0
- cumulusci/tests/test_vcr_serializer.py +134 -0
- cumulusci/tests/uncompressed_cassette.yaml +83 -0
- cumulusci/tests/util.py +344 -0
- cumulusci/utils/__init__.py +731 -0
- cumulusci/utils/classutils.py +9 -0
- cumulusci/utils/collections.py +32 -0
- cumulusci/utils/deprecation.py +11 -0
- cumulusci/utils/encryption.py +31 -0
- cumulusci/utils/fileutils.py +295 -0
- cumulusci/utils/git.py +142 -0
- cumulusci/utils/http/multi_request.py +214 -0
- cumulusci/utils/http/requests_utils.py +103 -0
- cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +32 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_composite_parallel_salesforce.yaml +65 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_errors.yaml +24 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_reference_ids.yaml +49 -0
- cumulusci/utils/http/tests/test_multi_request.py +255 -0
- cumulusci/utils/iterators.py +21 -0
- cumulusci/utils/logging.py +128 -0
- cumulusci/utils/metaprogramming.py +10 -0
- cumulusci/utils/options.py +138 -0
- cumulusci/utils/parallel/queries_in_parallel/run_queries_in_parallel.py +29 -0
- cumulusci/utils/parallel/queries_in_parallel/tests/test_run_queries_in_parallel.py +50 -0
- cumulusci/utils/parallel/task_worker_queues/parallel_worker.py +238 -0
- cumulusci/utils/parallel/task_worker_queues/parallel_worker_queue.py +243 -0
- cumulusci/utils/parallel/task_worker_queues/tests/test_parallel_worker.py +353 -0
- cumulusci/utils/salesforce/count_sobjects.py +46 -0
- cumulusci/utils/salesforce/soql.py +17 -0
- cumulusci/utils/salesforce/tests/cassettes/ManualEdit_TestCountSObjects.test_count_sobjects__network_errors.yaml +23 -0
- cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects__errors.yaml +33 -0
- cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects_simple.yaml +29 -0
- cumulusci/utils/salesforce/tests/test_count_sobjects.py +29 -0
- cumulusci/utils/salesforce/tests/test_soql.py +30 -0
- cumulusci/utils/tests/cassettes/ManualEditTestDescribeOrg.test_minimal_schema.yaml +36 -0
- cumulusci/utils/tests/cassettes/ManualEdit_test_describe_to_sql.yaml +191 -0
- cumulusci/utils/tests/test_fileutils.py +284 -0
- cumulusci/utils/tests/test_git.py +85 -0
- cumulusci/utils/tests/test_logging.py +70 -0
- cumulusci/utils/tests/test_option_parsing.py +188 -0
- cumulusci/utils/tests/test_org_schema.py +691 -0
- cumulusci/utils/tests/test_org_schema_models.py +79 -0
- cumulusci/utils/tests/test_waiting.py +25 -0
- cumulusci/utils/version_strings.py +391 -0
- cumulusci/utils/waiting.py +42 -0
- cumulusci/utils/xml/__init__.py +91 -0
- cumulusci/utils/xml/metadata_tree.py +299 -0
- cumulusci/utils/xml/robot_xml.py +114 -0
- cumulusci/utils/xml/salesforce_encoding.py +100 -0
- cumulusci/utils/xml/test/test_metadata_tree.py +251 -0
- cumulusci/utils/xml/test/test_salesforce_encoding.py +173 -0
- cumulusci/utils/yaml/cumulusci_yml.py +401 -0
- cumulusci/utils/yaml/model_parser.py +156 -0
- cumulusci/utils/yaml/safer_loader.py +74 -0
- cumulusci/utils/yaml/tests/bad_cci.yml +5 -0
- cumulusci/utils/yaml/tests/cassettes/TestCumulusciYml.test_validate_url__with_errors.yaml +20 -0
- cumulusci/utils/yaml/tests/test_cumulusci_yml.py +286 -0
- cumulusci/utils/yaml/tests/test_model_parser.py +175 -0
- cumulusci/utils/yaml/tests/test_safer_loader.py +88 -0
- cumulusci/utils/ziputils.py +61 -0
- cumulusci/vcs/base.py +143 -0
- cumulusci/vcs/bootstrap.py +272 -0
- cumulusci/vcs/github/__init__.py +24 -0
- cumulusci/vcs/github/adapter.py +689 -0
- cumulusci/vcs/github/release_notes/generator.py +219 -0
- cumulusci/vcs/github/release_notes/parser.py +151 -0
- cumulusci/vcs/github/release_notes/provider.py +143 -0
- cumulusci/vcs/github/service.py +569 -0
- cumulusci/vcs/github/tests/test_adapter.py +138 -0
- cumulusci/vcs/github/tests/test_service.py +408 -0
- cumulusci/vcs/models.py +586 -0
- cumulusci/vcs/tests/conftest.py +41 -0
- cumulusci/vcs/tests/dummy_service.py +241 -0
- cumulusci/vcs/tests/test_vcs_base.py +687 -0
- cumulusci/vcs/tests/test_vcs_bootstrap.py +727 -0
- cumulusci/vcs/utils/__init__.py +31 -0
- cumulusci/vcs/vcs_source.py +287 -0
- cumulusci_plus-5.0.0.dist-info/METADATA +145 -0
- cumulusci_plus-5.0.0.dist-info/RECORD +744 -0
- cumulusci_plus-5.0.0.dist-info/WHEEL +4 -0
- cumulusci_plus-5.0.0.dist-info/entry_points.txt +3 -0
- cumulusci_plus-5.0.0.dist-info/licenses/AUTHORS.rst +41 -0
- cumulusci_plus-5.0.0.dist-info/licenses/LICENSE +30 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from cumulusci.core.exceptions import DependencyLookupError, VcsNotFoundError
|
|
2
|
+
from cumulusci.tasks.base_source_control_task import BaseSourceControlTask
|
|
3
|
+
from cumulusci.tasks.salesforce.BaseSalesforceApiTask import BaseSalesforceApiTask
|
|
4
|
+
from cumulusci.vcs.bootstrap import get_version_id_from_commit
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GetPackageDataFromCommitStatus(BaseSourceControlTask, BaseSalesforceApiTask):
|
|
8
|
+
task_options = {
|
|
9
|
+
"context": {
|
|
10
|
+
"description": "Name of the commit status context",
|
|
11
|
+
"required": True,
|
|
12
|
+
},
|
|
13
|
+
"version_id": {"description": "Package version id"},
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def _run_task(self):
|
|
17
|
+
self.api_version = self.project_config.project__api_version
|
|
18
|
+
repo = self.get_repo()
|
|
19
|
+
context = self.options["context"]
|
|
20
|
+
commit_sha = self.project_config.repo_commit or ""
|
|
21
|
+
|
|
22
|
+
dependencies = []
|
|
23
|
+
version_id = self.options.get("version_id")
|
|
24
|
+
if version_id is None:
|
|
25
|
+
try:
|
|
26
|
+
version_id = get_version_id_from_commit(repo, commit_sha, context)
|
|
27
|
+
except VcsNotFoundError as e:
|
|
28
|
+
self.logger.error(e)
|
|
29
|
+
self.logger.error(
|
|
30
|
+
"This error usually means your local commit has not been pushed "
|
|
31
|
+
"or that a feature test package has not yet been built."
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
if version_id:
|
|
35
|
+
dependencies = self._get_dependencies(version_id)
|
|
36
|
+
else:
|
|
37
|
+
raise DependencyLookupError(
|
|
38
|
+
f"Could not find package version id in '{context}' commit status for commit {commit_sha}."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
self.return_values = {"dependencies": dependencies, "version_id": version_id}
|
|
42
|
+
|
|
43
|
+
def _get_dependencies(self, version_id):
|
|
44
|
+
res = self.tooling.query(
|
|
45
|
+
f"SELECT Dependencies FROM SubscriberPackageVersion WHERE Id='{version_id}'"
|
|
46
|
+
)
|
|
47
|
+
if res["records"]:
|
|
48
|
+
subscriber_version = res["records"][0]
|
|
49
|
+
dependencies = subscriber_version["Dependencies"] or {"ids": []}
|
|
50
|
+
dependencies = [
|
|
51
|
+
{"version_id": d["subscriberPackageVersionId"]}
|
|
52
|
+
for d in dependencies["ids"]
|
|
53
|
+
]
|
|
54
|
+
return dependencies
|
|
55
|
+
else:
|
|
56
|
+
raise DependencyLookupError(
|
|
57
|
+
f"Could not look up dependencies of {version_id}"
|
|
58
|
+
)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from cumulusci.tasks.base_source_control_task import BaseSourceControlTask
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CreatePackageDataFromCommitStatus(BaseSourceControlTask):
|
|
5
|
+
task_options = {
|
|
6
|
+
"state": {
|
|
7
|
+
"description": "sha of the commit",
|
|
8
|
+
"required": True,
|
|
9
|
+
},
|
|
10
|
+
"context": {
|
|
11
|
+
"description": "Name of the commit status context",
|
|
12
|
+
"required": True,
|
|
13
|
+
},
|
|
14
|
+
"commit_id": {
|
|
15
|
+
"description": "sha of the commit",
|
|
16
|
+
},
|
|
17
|
+
"description": {
|
|
18
|
+
"description": "Description of the commit status",
|
|
19
|
+
},
|
|
20
|
+
"target_url": {
|
|
21
|
+
"description": "URL to associate with the commit status",
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
def _run_task(self):
|
|
26
|
+
repo = self.get_repo()
|
|
27
|
+
commit_sha = self.options["commit_id"] or self.project_config.repo_commit or ""
|
|
28
|
+
|
|
29
|
+
commit = repo.create_commit_status(
|
|
30
|
+
commit_sha,
|
|
31
|
+
state=self.options["state"],
|
|
32
|
+
context=self.options["context"],
|
|
33
|
+
description=self.options.get("description"),
|
|
34
|
+
target_url=self.options.get("target_url"),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
self.return_values = {"commit_id": commit.sha}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
from cumulusci.cli.utils import timestamp_file
|
|
8
|
+
from cumulusci.core.exceptions import CumulusCIException, TaskOptionsError
|
|
9
|
+
from cumulusci.core.tasks import BaseTask
|
|
10
|
+
from cumulusci.core.utils import process_bool_arg, process_list_arg
|
|
11
|
+
from cumulusci.utils import download_extract_vcs_from_repo, filter_namelist
|
|
12
|
+
from cumulusci.vcs.bootstrap import get_repo_from_url
|
|
13
|
+
from cumulusci.vcs.models import AbstractRepo
|
|
14
|
+
from cumulusci.vcs.utils import get_ref_from_options
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DownloadExtract(BaseTask):
|
|
18
|
+
task_docs = """A task to download and extract files and folders from a Git repository.
|
|
19
|
+
Will download the repository and extract it to a specified directory.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
task_options = {
|
|
23
|
+
"repo_url": {"description": "The url to the repo", "required": True},
|
|
24
|
+
"target_directory": {
|
|
25
|
+
"description": "The directory to extract the repo contents to."
|
|
26
|
+
"If not set, contents will be extracted to the current directory."
|
|
27
|
+
"If set, it must be a relative path from the project root or an absolute path.",
|
|
28
|
+
"required": True,
|
|
29
|
+
},
|
|
30
|
+
"sub_folder": {
|
|
31
|
+
"description": "The subfolder directory to download from the repo."
|
|
32
|
+
},
|
|
33
|
+
"branch": {
|
|
34
|
+
"description": "The branch to fetch from the repo."
|
|
35
|
+
"if 'ref' or 'tag_name' is not set. The default branch will be set."
|
|
36
|
+
},
|
|
37
|
+
"tag_name": {
|
|
38
|
+
"description": "The name of the tag that should be downloaded."
|
|
39
|
+
"Values of 'latest' and 'latest_beta' are also allowed."
|
|
40
|
+
"Not required if 'branch' is set."
|
|
41
|
+
"Required if 'ref' is not set."
|
|
42
|
+
},
|
|
43
|
+
"ref": {
|
|
44
|
+
"description": "The git reference to download. Takes precedence over 'tag_name' and 'branch'."
|
|
45
|
+
"Required if 'tag_name' or 'branch' is not set."
|
|
46
|
+
},
|
|
47
|
+
"include": {
|
|
48
|
+
"description": "A list of paths from repo root to include. Directories must end with a trailing slash."
|
|
49
|
+
},
|
|
50
|
+
"renames": {
|
|
51
|
+
"description": "A list of paths to rename in the target repo, given as `local:` `target:` pairs."
|
|
52
|
+
},
|
|
53
|
+
"force": {"description": "Force Download files in the repo."},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def _init_options(self, kwargs):
|
|
57
|
+
super()._init_options(kwargs)
|
|
58
|
+
self.options["include"] = process_list_arg(self.options.get("include", None))
|
|
59
|
+
|
|
60
|
+
self.options["renames"] = self._process_renames(self.options.get("renames", []))
|
|
61
|
+
|
|
62
|
+
self.options["force"] = process_bool_arg(self.options.get("force", False))
|
|
63
|
+
|
|
64
|
+
def _init_repo(self):
|
|
65
|
+
self.repo: AbstractRepo = get_repo_from_url(
|
|
66
|
+
self.project_config, self.options["repo_url"]
|
|
67
|
+
)
|
|
68
|
+
self._set_ref()
|
|
69
|
+
self.commit = self.repo.get_ref(self.ref).sha
|
|
70
|
+
|
|
71
|
+
def _run_task(self):
|
|
72
|
+
self._set_target_directory()
|
|
73
|
+
|
|
74
|
+
if not self._check_latest_commit():
|
|
75
|
+
self.logger.info(
|
|
76
|
+
f"Skipping download, no new changes in {self.options['repo_url']} since last run."
|
|
77
|
+
)
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
self.logger.info(
|
|
81
|
+
f"{'Force ' if self.options['force'] else ''}Downloading files from {self.options['repo_url']}."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
target = Path(os.path.join(self.options["target_directory"]))
|
|
85
|
+
if not target.exists():
|
|
86
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
|
|
88
|
+
self._download_repo_and_extract(target)
|
|
89
|
+
self._rename_files(target)
|
|
90
|
+
|
|
91
|
+
with timestamp_file(self.options["target_directory"]) as f:
|
|
92
|
+
yaml.dump({"commit": self.commit, "timestamp": time.time()}, f)
|
|
93
|
+
|
|
94
|
+
def _process_renames(self, renamed_paths):
|
|
95
|
+
"""
|
|
96
|
+
For each entry in renames, any renames and store them
|
|
97
|
+
in self.local_to_target_paths.
|
|
98
|
+
"""
|
|
99
|
+
if not renamed_paths:
|
|
100
|
+
return {}
|
|
101
|
+
|
|
102
|
+
is_list_of_dicts = all(isinstance(pair, dict) for pair in renamed_paths)
|
|
103
|
+
dicts_have_correct_keys = is_list_of_dicts and all(
|
|
104
|
+
{"local", "target"} == pair.keys() for pair in renamed_paths
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
ERROR_MSG = (
|
|
108
|
+
"Renamed paths must be a list of dicts with `local:` and `target:` keys."
|
|
109
|
+
)
|
|
110
|
+
if not dicts_have_correct_keys:
|
|
111
|
+
raise TaskOptionsError(ERROR_MSG)
|
|
112
|
+
|
|
113
|
+
local_to_target_paths = {}
|
|
114
|
+
|
|
115
|
+
for rename in renamed_paths:
|
|
116
|
+
local_path = rename.get("local")
|
|
117
|
+
target_path = rename.get("target")
|
|
118
|
+
|
|
119
|
+
if local_path and target_path:
|
|
120
|
+
local_to_target_paths[local_path] = target_path
|
|
121
|
+
else:
|
|
122
|
+
raise TaskOptionsError(ERROR_MSG)
|
|
123
|
+
|
|
124
|
+
return local_to_target_paths
|
|
125
|
+
|
|
126
|
+
def _set_ref(self):
|
|
127
|
+
if "branch" in self.options:
|
|
128
|
+
branch = self.options.get("branch")
|
|
129
|
+
self.ref = f"heads/{branch}"
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
self.ref = get_ref_from_options(self.project_config, self.options)
|
|
134
|
+
except CumulusCIException:
|
|
135
|
+
# If no ref, tag_name or branch is set, default to the repo's default branch
|
|
136
|
+
self.ref = f"heads/{self.repo.default_branch}"
|
|
137
|
+
|
|
138
|
+
def _download_repo_and_extract(self, path):
|
|
139
|
+
zf = download_extract_vcs_from_repo(
|
|
140
|
+
self.repo, subfolder=self.options.get("sub_folder", None), ref=self.commit
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
included_members = zf.namelist()
|
|
144
|
+
if self.options["include"]:
|
|
145
|
+
included_members = filter_namelist(
|
|
146
|
+
includes=self.options["include"], namelist=zf.namelist()
|
|
147
|
+
)
|
|
148
|
+
zf.extractall(path=path, members=included_members)
|
|
149
|
+
zf.close()
|
|
150
|
+
|
|
151
|
+
def _rename_files(self, zip_dir):
|
|
152
|
+
for local_name, target_name in self.options["renames"].items():
|
|
153
|
+
local_path = Path(zip_dir, local_name)
|
|
154
|
+
if local_path.exists():
|
|
155
|
+
target_path = Path(zip_dir, target_name)
|
|
156
|
+
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
157
|
+
local_path.replace(target_path)
|
|
158
|
+
|
|
159
|
+
def _set_target_directory(self):
|
|
160
|
+
"""
|
|
161
|
+
Sets the target directory where the repo contents will be extracted.
|
|
162
|
+
If not set, defaults to the current directory.
|
|
163
|
+
"""
|
|
164
|
+
# Check if self.options.get("target_directory") is absolute or relative path.
|
|
165
|
+
target_directory = self.options["target_directory"]
|
|
166
|
+
if not os.path.isabs(target_directory):
|
|
167
|
+
# If it's a relative path, make it absolute based on project root
|
|
168
|
+
target_directory = os.path.join(
|
|
169
|
+
self.project_config.repo_root, target_directory
|
|
170
|
+
)
|
|
171
|
+
self.options["target_directory"] = target_directory
|
|
172
|
+
|
|
173
|
+
def _check_latest_commit(self):
|
|
174
|
+
"""checks for the latest commit in repo, max once per hour"""
|
|
175
|
+
if self.options["force"]:
|
|
176
|
+
self._init_repo()
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
check = True
|
|
180
|
+
|
|
181
|
+
timestamp = 0
|
|
182
|
+
commit = ""
|
|
183
|
+
if os.path.isfile(f"{self.options['target_directory']}/cumulus_timestamp"):
|
|
184
|
+
with timestamp_file(self.options["target_directory"]) as f:
|
|
185
|
+
loaded_data = yaml.safe_load(f)
|
|
186
|
+
timestamp = loaded_data.get("timestamp", 0)
|
|
187
|
+
commit = loaded_data.get("commit", "")
|
|
188
|
+
|
|
189
|
+
delta = time.time() - timestamp
|
|
190
|
+
check = delta > 3600
|
|
191
|
+
|
|
192
|
+
if not check:
|
|
193
|
+
return False
|
|
194
|
+
|
|
195
|
+
self._init_repo()
|
|
196
|
+
if self.commit != commit:
|
|
197
|
+
return True
|
|
198
|
+
|
|
199
|
+
return False
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
from cumulusci.core.utils import process_bool_arg
|
|
2
|
+
from cumulusci.tasks.base_source_control_task import BaseSourceControlTask
|
|
3
|
+
from cumulusci.utils.git import is_release_branch
|
|
4
|
+
from cumulusci.vcs.models import AbstractBranch, AbstractRepoCommit
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MergeBranch(BaseSourceControlTask):
|
|
8
|
+
task_docs = """
|
|
9
|
+
Merges the most recent commit on the current branch into other branches depending on the value of source_branch.
|
|
10
|
+
|
|
11
|
+
If source_branch is a branch that does not start with the specified branch_prefix, then the commit will be
|
|
12
|
+
merged to all branches that begin with branch_prefix and are not themselves child branches (i.e. branches don't contain '__' in their name).
|
|
13
|
+
|
|
14
|
+
If source_branch begins with branch_prefix, then the commit is merged to all child branches of source_branch.
|
|
15
|
+
"""
|
|
16
|
+
task_options = { # TODO: should use `class Options instead`
|
|
17
|
+
"commit": {
|
|
18
|
+
"description": "The commit to merge into feature branches. Defaults to the current head commit."
|
|
19
|
+
},
|
|
20
|
+
"source_branch": {
|
|
21
|
+
"description": "The source branch to merge from. Defaults to project__git__default_branch."
|
|
22
|
+
},
|
|
23
|
+
"branch_prefix": {
|
|
24
|
+
"description": "A list of prefixes of branches that should receive the merge. Defaults to project__git__prefix_feature"
|
|
25
|
+
},
|
|
26
|
+
"skip_future_releases": {
|
|
27
|
+
"description": "If true, then exclude branches that start with the branch prefix if they are not for the lowest release number. Defaults to True."
|
|
28
|
+
},
|
|
29
|
+
"update_future_releases": {
|
|
30
|
+
"description": "If true, then include release branches that are not the lowest release number even if they are not child branches. Defaults to False."
|
|
31
|
+
},
|
|
32
|
+
"create_pull_request_on_conflict": {
|
|
33
|
+
"description": "If true, then create a pull request when a merge conflict arises. Defaults to True."
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
def _init_options(self, kwargs):
|
|
38
|
+
super()._init_options(kwargs)
|
|
39
|
+
|
|
40
|
+
if "commit" not in self.options:
|
|
41
|
+
self.options["commit"] = self.project_config.repo_commit
|
|
42
|
+
if "branch_prefix" not in self.options:
|
|
43
|
+
self.options[
|
|
44
|
+
"branch_prefix"
|
|
45
|
+
] = self.project_config.project__git__prefix_feature
|
|
46
|
+
if "source_branch" not in self.options:
|
|
47
|
+
self.options[
|
|
48
|
+
"source_branch"
|
|
49
|
+
] = self.project_config.project__git__default_branch
|
|
50
|
+
if "skip_future_releases" not in self.options:
|
|
51
|
+
self.options["skip_future_releases"] = True
|
|
52
|
+
else:
|
|
53
|
+
self.options["skip_future_releases"] = process_bool_arg(
|
|
54
|
+
self.options.get("skip_future_releases")
|
|
55
|
+
)
|
|
56
|
+
self.options["update_future_releases"] = process_bool_arg(
|
|
57
|
+
self.options.get("update_future_releases") or False
|
|
58
|
+
)
|
|
59
|
+
if "create_pull_request_on_conflict" not in self.options:
|
|
60
|
+
self.options["create_pull_request_on_conflict"] = True
|
|
61
|
+
else:
|
|
62
|
+
self.options["create_pull_request_on_conflict"] = process_bool_arg(
|
|
63
|
+
self.options.get("create_pull_request_on_conflict")
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def _init_task(self):
|
|
67
|
+
super()._init_task()
|
|
68
|
+
self.repo = self.get_repo()
|
|
69
|
+
|
|
70
|
+
def _run_task(self):
|
|
71
|
+
self._validate_source_branch(self.options["source_branch"])
|
|
72
|
+
branches_to_merge = self._get_branches_to_merge()
|
|
73
|
+
|
|
74
|
+
for branch in branches_to_merge:
|
|
75
|
+
self._merge(
|
|
76
|
+
branch.name,
|
|
77
|
+
self.options["source_branch"],
|
|
78
|
+
self.options["commit"],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def _validate_source_branch(self, source_branch):
|
|
82
|
+
"""Validates that the source branch exists in the repository"""
|
|
83
|
+
branch: AbstractBranch = self.repo.branch(source_branch)
|
|
84
|
+
self.options["source_branch"] = branch.name
|
|
85
|
+
|
|
86
|
+
def _get_existing_prs(self, source_branch, branch_prefix):
|
|
87
|
+
"""Returns the existing pull requests from the source branch
|
|
88
|
+
to other branches that are candidates for merging."""
|
|
89
|
+
existing_prs = []
|
|
90
|
+
for pr in self.repo.pull_requests(state="open"):
|
|
91
|
+
if pr.base_ref.startswith(branch_prefix) and pr.head_ref == source_branch:
|
|
92
|
+
existing_prs.append(pr.base_ref)
|
|
93
|
+
return existing_prs
|
|
94
|
+
|
|
95
|
+
def _get_branches_to_merge(self):
|
|
96
|
+
"""
|
|
97
|
+
If source_branch is the default branch (or a branch that doesn't start with a prefix), we
|
|
98
|
+
gather all branches with branch_prefix that are not child branches.
|
|
99
|
+
NOTE: We only include the _next_ closest release branch when automerging from main.
|
|
100
|
+
A change on main may conflict with the current contents of the lowest release branch.
|
|
101
|
+
In this case, we would like for that conflict to only need to be resolved once
|
|
102
|
+
(not once for each release branch).
|
|
103
|
+
|
|
104
|
+
If source_branch starts with branch prefix, we gather
|
|
105
|
+
all branches with branch_prefix that are direct descendents of source_branch.
|
|
106
|
+
|
|
107
|
+
If update_future_releases is True, and source_branch is a release branch
|
|
108
|
+
then we also collect all future release branches.
|
|
109
|
+
"""
|
|
110
|
+
repo_branches = list(self.repo.branches())
|
|
111
|
+
next_release = self._get_next_release(repo_branches)
|
|
112
|
+
skip_future_releases = self.options["skip_future_releases"]
|
|
113
|
+
update_future_releases = self._update_future_releases(next_release)
|
|
114
|
+
|
|
115
|
+
child_branches = []
|
|
116
|
+
main_descendents = []
|
|
117
|
+
release_branches = []
|
|
118
|
+
for branch in repo_branches:
|
|
119
|
+
# check for adding future release branches
|
|
120
|
+
if update_future_releases and self._is_future_release_branch(
|
|
121
|
+
branch.name, next_release
|
|
122
|
+
):
|
|
123
|
+
release_branches.append(branch)
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
# check if we looking at the source_branch
|
|
127
|
+
if branch.name == self.options["source_branch"]:
|
|
128
|
+
self.logger.debug(f"Skipping branch {branch.name}: is source branch")
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
# check for branch prefix match
|
|
132
|
+
elif not branch.name.startswith(self.options["branch_prefix"]):
|
|
133
|
+
self.logger.debug(
|
|
134
|
+
f"Skipping branch {branch.name}: does not match prefix '{self.options['branch_prefix']}'"
|
|
135
|
+
)
|
|
136
|
+
continue
|
|
137
|
+
|
|
138
|
+
# check if source_branch doesn't have prefix and is not a child (e.g. main)
|
|
139
|
+
elif (
|
|
140
|
+
not self.options["source_branch"].startswith(
|
|
141
|
+
self.options["branch_prefix"]
|
|
142
|
+
)
|
|
143
|
+
and "__" not in branch.name
|
|
144
|
+
):
|
|
145
|
+
# only merge to the lowest numbered release branch
|
|
146
|
+
# when merging from a branch without a prefix (e.g. main)
|
|
147
|
+
if skip_future_releases and self._is_future_release_branch(
|
|
148
|
+
branch.name, next_release
|
|
149
|
+
):
|
|
150
|
+
continue
|
|
151
|
+
main_descendents.append(branch)
|
|
152
|
+
|
|
153
|
+
# else, we have a branch that starts with branch_prefix
|
|
154
|
+
# check is this branch is a direct descendent
|
|
155
|
+
elif self._is_source_branch_direct_descendent(branch.name):
|
|
156
|
+
child_branches.append(branch)
|
|
157
|
+
|
|
158
|
+
# else not a direct descendent
|
|
159
|
+
else:
|
|
160
|
+
self.logger.debug(
|
|
161
|
+
f"Skipping branch {branch.name}: is not a direct descendent of {self.options['source_branch']}"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
to_merge = []
|
|
165
|
+
if child_branches:
|
|
166
|
+
self.logger.debug(
|
|
167
|
+
f"Found child branches to update: {[branch.name for branch in child_branches]}"
|
|
168
|
+
)
|
|
169
|
+
to_merge = child_branches
|
|
170
|
+
elif self.options["source_branch"].startswith(self.options["branch_prefix"]):
|
|
171
|
+
self.logger.debug(
|
|
172
|
+
f"No children found for branch {self.options['source_branch']}"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if release_branches:
|
|
176
|
+
self.logger.debug(
|
|
177
|
+
f"Found future release branches to update: {[branch.name for branch in release_branches]}"
|
|
178
|
+
)
|
|
179
|
+
to_merge = to_merge + release_branches
|
|
180
|
+
|
|
181
|
+
if main_descendents:
|
|
182
|
+
self.logger.debug(
|
|
183
|
+
f"Found descendents of {self.options['source_branch']} to update: {[branch.name for branch in main_descendents]}"
|
|
184
|
+
)
|
|
185
|
+
to_merge = to_merge + main_descendents
|
|
186
|
+
|
|
187
|
+
return to_merge
|
|
188
|
+
|
|
189
|
+
def _get_next_release(self, repo_branches):
|
|
190
|
+
"""Returns the integer that corresponds to the lowest release number found on all release branches.
|
|
191
|
+
NOTE: We assume that once a release branch is merged that it will be deleted.
|
|
192
|
+
"""
|
|
193
|
+
release_nums = [
|
|
194
|
+
self._get_release_number(branch.name)
|
|
195
|
+
for branch in repo_branches
|
|
196
|
+
if self._is_release_branch(branch.name)
|
|
197
|
+
]
|
|
198
|
+
next_release = sorted(release_nums)[0] if release_nums else None
|
|
199
|
+
return next_release
|
|
200
|
+
|
|
201
|
+
def _update_future_releases(self, next_release):
|
|
202
|
+
"""Determines whether or not to update future releases.
|
|
203
|
+
Returns True if all of the below checks are True. False otherwise.
|
|
204
|
+
|
|
205
|
+
Checks:
|
|
206
|
+
(1) Did we receive the 'update_future_release' flag?
|
|
207
|
+
(2) Is the source_branch a release branch?
|
|
208
|
+
(3) Is it the lowest numbered release branch that exists?
|
|
209
|
+
|
|
210
|
+
NOTE: This functionality assumes that the lowest numbered release branch in the repo is
|
|
211
|
+
the next closest release. Put another way, once a release branch is merged we assume that it is immediately deleted.
|
|
212
|
+
"""
|
|
213
|
+
update_future_releases = False
|
|
214
|
+
if (
|
|
215
|
+
self.options["update_future_releases"]
|
|
216
|
+
and self._is_release_branch(self.options["source_branch"])
|
|
217
|
+
and next_release == self._get_release_number(self.options["source_branch"])
|
|
218
|
+
):
|
|
219
|
+
update_future_releases = True
|
|
220
|
+
return update_future_releases
|
|
221
|
+
|
|
222
|
+
def _is_release_branch(self, branch_name):
|
|
223
|
+
"""A release branch begins with the given prefix"""
|
|
224
|
+
return is_release_branch(branch_name, self.options["branch_prefix"])
|
|
225
|
+
|
|
226
|
+
def _get_release_number(self, branch_name) -> int:
|
|
227
|
+
"""Get the release number from a release branch name.
|
|
228
|
+
|
|
229
|
+
Assumes we already know it is a release branch.
|
|
230
|
+
"""
|
|
231
|
+
return int(branch_name.split(self.options["branch_prefix"])[1])
|
|
232
|
+
|
|
233
|
+
def _merge(self, branch_name, source, commit):
|
|
234
|
+
"""Attempt to merge a commit from source to branch with branch_name"""
|
|
235
|
+
compare = self.repo.compare_commits(branch_name, commit, source)
|
|
236
|
+
if not compare or not compare.files:
|
|
237
|
+
self.logger.info(f"Skipping branch {branch_name}: no file diffs found")
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
ret = self.repo.merge(branch_name, commit, source)
|
|
241
|
+
if isinstance(ret, AbstractRepoCommit):
|
|
242
|
+
self.logger.info(
|
|
243
|
+
f"Merged {compare.behind_by} commits into branch: {branch_name}"
|
|
244
|
+
)
|
|
245
|
+
elif ret is None:
|
|
246
|
+
if self.options["create_pull_request_on_conflict"]:
|
|
247
|
+
self._create_conflict_pull_request(branch_name, source)
|
|
248
|
+
else:
|
|
249
|
+
self.logger.info(
|
|
250
|
+
f"Merge conflict on branch {branch_name}: skipping pull request creation"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
def _create_conflict_pull_request(self, branch_name, source):
|
|
254
|
+
"""Attempt to create a pull request from source into branch_name if merge operation encounters a conflict"""
|
|
255
|
+
if branch_name in self._get_existing_prs(
|
|
256
|
+
self.options["source_branch"], self.options["branch_prefix"]
|
|
257
|
+
):
|
|
258
|
+
self.logger.info(
|
|
259
|
+
f"Merge conflict on branch {branch_name}: merge PR already exists"
|
|
260
|
+
)
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
pull = self.repo.create_pull(
|
|
264
|
+
title=f"Merge {source} into {branch_name}",
|
|
265
|
+
base=branch_name,
|
|
266
|
+
head=source,
|
|
267
|
+
body="This pull request was automatically generated because "
|
|
268
|
+
"an automated merge hit a merge conflict",
|
|
269
|
+
options={
|
|
270
|
+
"error_message": f"Error creating merge conflict pull request to merge {source} into {branch_name}"
|
|
271
|
+
},
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
if pull is not None:
|
|
275
|
+
self.logger.info(
|
|
276
|
+
f"Merge conflict on branch {branch_name}: created pull request #{pull.number}"
|
|
277
|
+
)
|
|
278
|
+
return pull
|
|
279
|
+
|
|
280
|
+
def _is_source_branch_direct_descendent(self, branch_name):
|
|
281
|
+
"""Returns True if branch is a direct descendent of the source branch"""
|
|
282
|
+
source_dunder_count = self.options["source_branch"].count("__")
|
|
283
|
+
return (
|
|
284
|
+
branch_name.startswith(f"{self.options['source_branch']}__")
|
|
285
|
+
and branch_name.count("__") == source_dunder_count + 1
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def _is_future_release_branch(self, branch_name, next_release):
|
|
289
|
+
return (
|
|
290
|
+
self._is_release_branch(branch_name)
|
|
291
|
+
and branch_name != self.options["source_branch"]
|
|
292
|
+
and self._get_release_num(branch_name) > next_release
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
def _get_release_num(self, release_branch_name):
|
|
296
|
+
"""Given a release branch, returns an integer that
|
|
297
|
+
corresponds to the release number for that branch"""
|
|
298
|
+
return int(release_branch_name.split(self.options["branch_prefix"])[1])
|