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,782 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import pathlib
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
import types
|
|
7
|
+
from configparser import ConfigParser
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from io import StringIO
|
|
10
|
+
from itertools import chain
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Union
|
|
13
|
+
|
|
14
|
+
from cumulusci.core.config.base_config import BaseConfig
|
|
15
|
+
from cumulusci.core.debug import get_debug_mode
|
|
16
|
+
from cumulusci.core.versions import PackageVersionNumber
|
|
17
|
+
from cumulusci.plugins.plugin_loader import load_plugins
|
|
18
|
+
from cumulusci.utils.version_strings import LooseVersion
|
|
19
|
+
|
|
20
|
+
API_VERSION_RE = re.compile(r"^\d\d+\.0$")
|
|
21
|
+
|
|
22
|
+
from pydantic import ValidationError
|
|
23
|
+
|
|
24
|
+
from cumulusci.core.config import FlowConfig, TaskConfig
|
|
25
|
+
from cumulusci.core.config.base_task_flow_config import BaseTaskFlowConfig
|
|
26
|
+
from cumulusci.core.exceptions import (
|
|
27
|
+
ConfigError,
|
|
28
|
+
CumulusCIException,
|
|
29
|
+
KeychainNotFound,
|
|
30
|
+
NamespaceNotFoundError,
|
|
31
|
+
NotInProject,
|
|
32
|
+
ProjectConfigNotFound,
|
|
33
|
+
VcsException,
|
|
34
|
+
)
|
|
35
|
+
from cumulusci.core.source import LocalFolderSource, NullSource
|
|
36
|
+
from cumulusci.core.utils import merge_config
|
|
37
|
+
from cumulusci.utils.fileutils import FSResource, open_fs_resource
|
|
38
|
+
from cumulusci.utils.git import current_branch, generic_parse_repo_url, git_path
|
|
39
|
+
from cumulusci.utils.yaml.cumulusci_yml import (
|
|
40
|
+
LocalFolderSourceModel,
|
|
41
|
+
VCSSourceModel,
|
|
42
|
+
cci_safe_load,
|
|
43
|
+
)
|
|
44
|
+
from cumulusci.vcs.models import AbstractRepo
|
|
45
|
+
from cumulusci.vcs.vcs_source import VCSSource
|
|
46
|
+
|
|
47
|
+
sys.modules.setdefault(
|
|
48
|
+
"tasks", types.ModuleType("tasks", "Synthetic package for all repo tasks")
|
|
49
|
+
)
|
|
50
|
+
import tasks
|
|
51
|
+
|
|
52
|
+
tasks.__path__ = []
|
|
53
|
+
|
|
54
|
+
if TYPE_CHECKING:
|
|
55
|
+
from cumulusci.core.config.universal_config import UniversalConfig
|
|
56
|
+
from cumulusci.core.keychain.base_project_keychain import BaseProjectKeychain
|
|
57
|
+
from cumulusci.vcs.base import VCSService
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ProjectConfigPropertiesMixin(BaseConfig):
|
|
61
|
+
"""Mixin for shared properties used by ProjectConfigs and UniversalConfigs"""
|
|
62
|
+
|
|
63
|
+
cli: dict
|
|
64
|
+
services: dict
|
|
65
|
+
project: dict
|
|
66
|
+
cumulusci: dict
|
|
67
|
+
orgs: dict
|
|
68
|
+
minimum_cumulusci_version: str
|
|
69
|
+
sources: dict
|
|
70
|
+
flows: dict
|
|
71
|
+
plans: dict
|
|
72
|
+
tasks: dict
|
|
73
|
+
plugins: dict
|
|
74
|
+
dev_config: dict # this is not documented and should be deprecated
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class BaseProjectConfig(BaseTaskFlowConfig, ProjectConfigPropertiesMixin):
|
|
78
|
+
"""Base class for a project's configuration which extends the global config"""
|
|
79
|
+
|
|
80
|
+
config_filename = "cumulusci.yml"
|
|
81
|
+
universal_config_obj: "UniversalConfig"
|
|
82
|
+
keychain: Optional["BaseProjectKeychain"]
|
|
83
|
+
_repo_info: Dict[str, Any]
|
|
84
|
+
config_project: dict
|
|
85
|
+
config_project_local: dict
|
|
86
|
+
config_additional_yaml: dict
|
|
87
|
+
config_plugins_yaml: dict
|
|
88
|
+
additional_yaml: Optional[str]
|
|
89
|
+
source: Union[NullSource, VCSSource, LocalFolderSource]
|
|
90
|
+
_cache_dir: Optional[Path]
|
|
91
|
+
included_sources: Dict[
|
|
92
|
+
Union[VCSSourceModel, LocalFolderSourceModel],
|
|
93
|
+
"BaseProjectConfig",
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
universal_config_obj: "UniversalConfig",
|
|
99
|
+
config: Optional[dict] = None,
|
|
100
|
+
cache_dir: Optional[Path] = None,
|
|
101
|
+
*args,
|
|
102
|
+
**kwargs,
|
|
103
|
+
):
|
|
104
|
+
self.universal_config_obj = universal_config_obj
|
|
105
|
+
self.keychain = None
|
|
106
|
+
|
|
107
|
+
# optionally pass in a repo_info dict
|
|
108
|
+
self._repo_info = kwargs.pop("repo_info", None)
|
|
109
|
+
|
|
110
|
+
if not config:
|
|
111
|
+
config = {}
|
|
112
|
+
|
|
113
|
+
# Initialize the dictionaries for the individual configs
|
|
114
|
+
self.config_project = {}
|
|
115
|
+
self.config_project_local = {}
|
|
116
|
+
self.config_additional_yaml = {}
|
|
117
|
+
self.config_plugins_yaml = {}
|
|
118
|
+
|
|
119
|
+
# optionally pass in a kwarg named 'additional_yaml' that will
|
|
120
|
+
# be added to the YAML merge stack.
|
|
121
|
+
# Called from MetaCI in metaci/cumulusci/config.py
|
|
122
|
+
# https://github.com/SFDO-Tooling/MetaCI/blob/36a0f4654/metaci/cumulusci/config.py#L8-L11
|
|
123
|
+
self.additional_yaml = None
|
|
124
|
+
if "additional_yaml" in kwargs:
|
|
125
|
+
self.additional_yaml = kwargs.pop("additional_yaml")
|
|
126
|
+
|
|
127
|
+
# initialize map of project configs referenced from an external source
|
|
128
|
+
self.source = NullSource()
|
|
129
|
+
self.included_sources = kwargs.pop("included_sources", {})
|
|
130
|
+
|
|
131
|
+
# Store requested cache directory, which may be our parent's if we are a subproject
|
|
132
|
+
self._cache_dir = cache_dir
|
|
133
|
+
|
|
134
|
+
super().__init__(config=config)
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def config_project_local_path(self) -> Optional[str]:
|
|
138
|
+
path = Path(self.project_local_dir) / self.config_filename
|
|
139
|
+
if path.is_file():
|
|
140
|
+
return str(path)
|
|
141
|
+
|
|
142
|
+
def _load_config(self):
|
|
143
|
+
"""Loads the configuration from YAML, if no override config was passed in initially."""
|
|
144
|
+
|
|
145
|
+
if (
|
|
146
|
+
self.config
|
|
147
|
+
): # any config being pre-set at init will short circuit out, but not a plain {}
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
# Verify that we're in a project
|
|
151
|
+
repo_root = self.repo_root
|
|
152
|
+
if not repo_root:
|
|
153
|
+
raise NotInProject(
|
|
154
|
+
"No git repository was found in the current path. You must be in a git repository to set up and use CCI for a project."
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Verify that the project's root has a config file
|
|
158
|
+
if not self.config_project_path:
|
|
159
|
+
raise ProjectConfigNotFound(
|
|
160
|
+
f"The file {self.config_filename} was not found in the repo root: {repo_root}. Are you in a CumulusCI Project directory?"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Load the project's yaml config file
|
|
164
|
+
project_config = cci_safe_load(self.config_project_path, logger=self.logger)
|
|
165
|
+
|
|
166
|
+
if project_config:
|
|
167
|
+
self.config_project.update(project_config)
|
|
168
|
+
|
|
169
|
+
# Load the local project yaml config file if it exists
|
|
170
|
+
if self.config_project_local_path:
|
|
171
|
+
local_config = cci_safe_load(
|
|
172
|
+
self.config_project_local_path, logger=self.logger
|
|
173
|
+
)
|
|
174
|
+
if local_config:
|
|
175
|
+
self.config_project_local.update(local_config)
|
|
176
|
+
|
|
177
|
+
# merge in any additional yaml that was passed along
|
|
178
|
+
if self.additional_yaml:
|
|
179
|
+
additional_yaml_config = cci_safe_load(
|
|
180
|
+
StringIO(self.additional_yaml),
|
|
181
|
+
self.config_project_path,
|
|
182
|
+
logger=self.logger,
|
|
183
|
+
)
|
|
184
|
+
if additional_yaml_config:
|
|
185
|
+
self.config_additional_yaml.update(additional_yaml_config)
|
|
186
|
+
|
|
187
|
+
# Loading plugins as classes are loaded and available.
|
|
188
|
+
plugins = load_plugins(self.logger)
|
|
189
|
+
|
|
190
|
+
# Load the plugin yaml config file if it exists
|
|
191
|
+
for plugin in plugins:
|
|
192
|
+
if plugin.plugin_project_config:
|
|
193
|
+
self.config_plugins_yaml.update(plugin.plugin_project_config)
|
|
194
|
+
self.logger.info(
|
|
195
|
+
f"Loaded plugin: {plugin.name} ({plugin.api_name}) v{plugin.version}"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
plugin.teardown() # clean up the plugin
|
|
199
|
+
|
|
200
|
+
self.config = merge_config(
|
|
201
|
+
{
|
|
202
|
+
"universal_config": self.config_universal,
|
|
203
|
+
"global_config": self.config_global,
|
|
204
|
+
"project_config": self.config_project,
|
|
205
|
+
"project_local_config": self.config_project_local,
|
|
206
|
+
"additional_yaml": self.config_additional_yaml,
|
|
207
|
+
"plugins_config": self.config_plugins_yaml,
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
self._validate_config()
|
|
212
|
+
|
|
213
|
+
def _validate_config(self):
|
|
214
|
+
"""Performs validation checks on the configuration"""
|
|
215
|
+
self._validate_package_api_format()
|
|
216
|
+
|
|
217
|
+
def _validate_package_api_format(self):
|
|
218
|
+
api_version = str(self.project__package__api_version)
|
|
219
|
+
|
|
220
|
+
if not API_VERSION_RE.match(api_version):
|
|
221
|
+
message = (
|
|
222
|
+
f"Package API Version must be in the form 'XX.0', found: {api_version}"
|
|
223
|
+
)
|
|
224
|
+
raise ConfigError(message)
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def config_global(self) -> dict:
|
|
228
|
+
return self.universal_config_obj.config_global
|
|
229
|
+
|
|
230
|
+
@property
|
|
231
|
+
def config_universal(self) -> dict:
|
|
232
|
+
return self.universal_config_obj.config_universal
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def repo_info(self) -> Dict[str, Any]:
|
|
236
|
+
if self._repo_info is not None:
|
|
237
|
+
return self._repo_info
|
|
238
|
+
|
|
239
|
+
# Detect if we are running in a CI environment and get repo info
|
|
240
|
+
# from env vars for the environment instead of .git files
|
|
241
|
+
info = {"ci": None}
|
|
242
|
+
|
|
243
|
+
# Make sure that the CUMULUSCI_AUTO_DETECT environment variable is
|
|
244
|
+
# set before trying to auto-detect anything from the environment
|
|
245
|
+
if not os.environ.get("CUMULUSCI_AUTO_DETECT"):
|
|
246
|
+
self._repo_info = info
|
|
247
|
+
return self._repo_info
|
|
248
|
+
|
|
249
|
+
# Heroku CI
|
|
250
|
+
heroku_ci = os.environ.get("HEROKU_TEST_RUN_ID")
|
|
251
|
+
if heroku_ci:
|
|
252
|
+
info = {
|
|
253
|
+
"branch": os.environ.get("HEROKU_TEST_RUN_BRANCH"),
|
|
254
|
+
"commit": os.environ.get("HEROKU_TEST_RUN_COMMIT_VERSION"),
|
|
255
|
+
"ci": "heroku",
|
|
256
|
+
"root": "/app",
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
# Other CI environment implementations can be implemented here...
|
|
260
|
+
|
|
261
|
+
self._apply_repo_env_var_overrides(info)
|
|
262
|
+
|
|
263
|
+
if info["ci"]:
|
|
264
|
+
self._validate_required_git_info(info)
|
|
265
|
+
|
|
266
|
+
if len(info) > 1:
|
|
267
|
+
self._log_detected_overrides_as_warning(info)
|
|
268
|
+
|
|
269
|
+
self._repo_info = info
|
|
270
|
+
return self._repo_info
|
|
271
|
+
|
|
272
|
+
def _apply_repo_env_var_overrides(self, info: Dict[str, Any]):
|
|
273
|
+
"""Apply CUMULUSCI_REPO_* environment variables last so they can
|
|
274
|
+
override and fill in missing values from the CI environment"""
|
|
275
|
+
self._override_repo_env_var("CUMULUSCI_REPO_BRANCH", "branch", info)
|
|
276
|
+
self._override_repo_env_var("CUMULUSCI_REPO_COMMIT", "commit", info)
|
|
277
|
+
self._override_repo_env_var("CUMULUSCI_REPO_ROOT", "root", info)
|
|
278
|
+
|
|
279
|
+
repo_url = os.environ.get("CUMULUSCI_REPO_URL")
|
|
280
|
+
if repo_url:
|
|
281
|
+
if repo_url != info.get("url"):
|
|
282
|
+
self.logger.info(
|
|
283
|
+
"CUMULUSCI_REPO_URL found, using its value as the repo url, owner, and name"
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
url_info = {}
|
|
287
|
+
(
|
|
288
|
+
url_info["owner"],
|
|
289
|
+
url_info["name"],
|
|
290
|
+
url_info["domain"],
|
|
291
|
+
) = generic_parse_repo_url(repo_url)
|
|
292
|
+
url_info["url"] = repo_url
|
|
293
|
+
info.update(url_info)
|
|
294
|
+
|
|
295
|
+
def get_repo_owner_host_from_url(self, repo_url) -> List[str]:
|
|
296
|
+
"""Returns the owner, repo_name, and host from the repository URL.
|
|
297
|
+
The method should return a list containing the owner, repo_name and host."""
|
|
298
|
+
try:
|
|
299
|
+
vcs_service = self.get_service_type_for_repo(repo_url)
|
|
300
|
+
parsed_url = vcs_service.parse_repo_url()
|
|
301
|
+
except Exception:
|
|
302
|
+
# Fallback to generic parsing if the service does not support it
|
|
303
|
+
parsed_url = generic_parse_repo_url(repo_url)
|
|
304
|
+
|
|
305
|
+
if len(parsed_url) < 3:
|
|
306
|
+
raise CumulusCIException("Parsed repository URL is not in expected format.")
|
|
307
|
+
|
|
308
|
+
return [parsed_url[0], parsed_url[1], parsed_url[2]]
|
|
309
|
+
|
|
310
|
+
def _override_repo_env_var(
|
|
311
|
+
self, repo_env_var: str, local_var: str, info: Dict[str, Any]
|
|
312
|
+
):
|
|
313
|
+
env_value: Optional[str] = os.environ.get(repo_env_var)
|
|
314
|
+
if env_value:
|
|
315
|
+
if env_value != info.get(local_var):
|
|
316
|
+
self.logger.info(
|
|
317
|
+
f"{repo_env_var} found, using its value for configuration."
|
|
318
|
+
)
|
|
319
|
+
info[local_var] = env_value
|
|
320
|
+
|
|
321
|
+
def _validate_required_git_info(self, info: Dict[str, Any]):
|
|
322
|
+
"""Ensures that we have the required git info or throw a ConfigError"""
|
|
323
|
+
validate = {
|
|
324
|
+
# <key>: <env var to manually override>
|
|
325
|
+
"branch": "CUMULUSCI_REPO_BRANCH",
|
|
326
|
+
"commit": "CUMULUSCI_REPO_COMMIT",
|
|
327
|
+
"name": "CUMULUSCI_REPO_URL",
|
|
328
|
+
"owner": "CUMULUSCI_REPO_URL",
|
|
329
|
+
"root": "CUMULUSCI_REPO_ROOT",
|
|
330
|
+
"url": "CUMULUSCI_REPO_URL",
|
|
331
|
+
}
|
|
332
|
+
for key, env_var in list(validate.items()):
|
|
333
|
+
if key not in info or not info[key]:
|
|
334
|
+
message = f"Detected CI on {info['ci']} but could not determine the repo {key}"
|
|
335
|
+
if env_var:
|
|
336
|
+
message += f". You can manually pass in the {key} "
|
|
337
|
+
message += f" with the {env_var} environment variable."
|
|
338
|
+
raise ConfigError(message)
|
|
339
|
+
|
|
340
|
+
def _log_detected_overrides_as_warning(self, info: Dict[str, Any]):
|
|
341
|
+
self.logger.info("")
|
|
342
|
+
self.logger.warning("Using environment variables to override repo info:")
|
|
343
|
+
keys = list(info.keys())
|
|
344
|
+
keys.sort()
|
|
345
|
+
for key in keys:
|
|
346
|
+
self.logger.warning(f" {key}: {info[key]}")
|
|
347
|
+
self.logger.info("")
|
|
348
|
+
|
|
349
|
+
def git_config_remote_origin_url(self) -> Optional[str]:
|
|
350
|
+
"""Returns the url under the [remote origin]
|
|
351
|
+
section of the .git/config file. Returns None
|
|
352
|
+
if .git/config file not present or no matching
|
|
353
|
+
line is found."""
|
|
354
|
+
config = ConfigParser(strict=False)
|
|
355
|
+
try:
|
|
356
|
+
config.read(git_path(self.repo_root, "config"))
|
|
357
|
+
url = config['remote "origin"']["url"]
|
|
358
|
+
except (KeyError, TypeError):
|
|
359
|
+
url = None
|
|
360
|
+
return url
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def repo_root(self) -> Optional[str]:
|
|
364
|
+
path = self.repo_info.get("root")
|
|
365
|
+
if path:
|
|
366
|
+
return path
|
|
367
|
+
|
|
368
|
+
path = Path.cwd().resolve()
|
|
369
|
+
paths = chain((path,), path.parents)
|
|
370
|
+
for path in paths:
|
|
371
|
+
if (path / ".git").is_dir():
|
|
372
|
+
return str(path)
|
|
373
|
+
|
|
374
|
+
@property
|
|
375
|
+
def server_domain(self) -> Optional[str]:
|
|
376
|
+
domain = self.repo_info.get("domain")
|
|
377
|
+
|
|
378
|
+
if domain:
|
|
379
|
+
return domain
|
|
380
|
+
|
|
381
|
+
if not self.repo_root:
|
|
382
|
+
return
|
|
383
|
+
|
|
384
|
+
url_line = self.git_config_remote_origin_url()
|
|
385
|
+
if url_line:
|
|
386
|
+
return self.get_repo_owner_host_from_url(url_line)[2]
|
|
387
|
+
|
|
388
|
+
@property
|
|
389
|
+
def repo_name(self) -> Optional[str]:
|
|
390
|
+
name = self.repo_info.get("name")
|
|
391
|
+
if name:
|
|
392
|
+
return name
|
|
393
|
+
|
|
394
|
+
if not self.repo_root:
|
|
395
|
+
return
|
|
396
|
+
|
|
397
|
+
url_line = self.git_config_remote_origin_url()
|
|
398
|
+
if url_line:
|
|
399
|
+
return self.get_repo_owner_host_from_url(url_line)[1]
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def repo_url(self) -> Optional[str]:
|
|
403
|
+
url = self.repo_info.get("url")
|
|
404
|
+
if url:
|
|
405
|
+
return url
|
|
406
|
+
|
|
407
|
+
if not self.repo_root:
|
|
408
|
+
return
|
|
409
|
+
|
|
410
|
+
url = self.git_config_remote_origin_url()
|
|
411
|
+
return url
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def repo_owner(self) -> Optional[str]:
|
|
415
|
+
owner = self.repo_info.get("owner")
|
|
416
|
+
if owner:
|
|
417
|
+
return owner
|
|
418
|
+
|
|
419
|
+
if not self.repo_root:
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
url_line = self.git_config_remote_origin_url()
|
|
423
|
+
|
|
424
|
+
if url_line:
|
|
425
|
+
return self.get_repo_owner_host_from_url(url_line)[0]
|
|
426
|
+
|
|
427
|
+
@property
|
|
428
|
+
def repo_branch(self) -> Optional[str]:
|
|
429
|
+
branch = self.repo_info.get("branch")
|
|
430
|
+
if branch:
|
|
431
|
+
return branch
|
|
432
|
+
|
|
433
|
+
if not self.repo_root:
|
|
434
|
+
return
|
|
435
|
+
|
|
436
|
+
return current_branch(self.repo_root)
|
|
437
|
+
|
|
438
|
+
@property
|
|
439
|
+
def repo_commit(self) -> Optional[str]:
|
|
440
|
+
commit = self.repo_info.get("commit")
|
|
441
|
+
if commit:
|
|
442
|
+
return commit
|
|
443
|
+
|
|
444
|
+
if not self.repo_root:
|
|
445
|
+
return
|
|
446
|
+
|
|
447
|
+
branch = self.repo_branch
|
|
448
|
+
if branch:
|
|
449
|
+
commit_file_path = pathlib.Path(self.repo_root) / ".git" / "refs" / "heads"
|
|
450
|
+
commit_file_path = commit_file_path.joinpath(*branch.split("/"))
|
|
451
|
+
else:
|
|
452
|
+
# We're in detached HEAD mode; .git/HEAD contains the SHA
|
|
453
|
+
commit_file_path = pathlib.Path(self.repo_root) / ".git" / "HEAD"
|
|
454
|
+
|
|
455
|
+
if commit_file_path.exists() and commit_file_path.is_file():
|
|
456
|
+
return commit_file_path.read_text().strip()
|
|
457
|
+
else:
|
|
458
|
+
if branch:
|
|
459
|
+
packed_refs_path = os.path.join(self.repo_root, ".git", "packed-refs")
|
|
460
|
+
with open(packed_refs_path, "r") as f:
|
|
461
|
+
for line in f:
|
|
462
|
+
parts = line.split(" ")
|
|
463
|
+
if len(parts) == 1:
|
|
464
|
+
# Skip lines showing the commit sha of a tag on the
|
|
465
|
+
# preceeding line
|
|
466
|
+
continue
|
|
467
|
+
if (
|
|
468
|
+
parts[1].replace("refs/remotes/origin/", "").strip()
|
|
469
|
+
== branch
|
|
470
|
+
):
|
|
471
|
+
return parts[0]
|
|
472
|
+
|
|
473
|
+
def get_repo(self) -> AbstractRepo:
|
|
474
|
+
repo = self.repo_service.get_repository()
|
|
475
|
+
if repo is None:
|
|
476
|
+
raise VcsException(
|
|
477
|
+
f"VCS repository not found or not authorized. ({self.repo_url})"
|
|
478
|
+
)
|
|
479
|
+
return repo
|
|
480
|
+
|
|
481
|
+
def get_latest_tag(self, beta: bool = False) -> str:
|
|
482
|
+
"""Query Github Releases to find the latest production or beta tag"""
|
|
483
|
+
from cumulusci.vcs.bootstrap import get_latest_tag
|
|
484
|
+
|
|
485
|
+
repo = self.get_repo()
|
|
486
|
+
return get_latest_tag(repo, beta)
|
|
487
|
+
|
|
488
|
+
def get_latest_version(self, beta: bool = False) -> Optional[LooseVersion]:
|
|
489
|
+
"""Query Github Releases to find the latest production or beta release"""
|
|
490
|
+
tag = self.get_latest_tag(beta)
|
|
491
|
+
version = self.get_version_for_tag(tag)
|
|
492
|
+
if version is not None:
|
|
493
|
+
return LooseVersion(version)
|
|
494
|
+
|
|
495
|
+
def get_previous_version(self) -> Optional[LooseVersion]:
|
|
496
|
+
"""Query GitHub releases to find the previous production release"""
|
|
497
|
+
from cumulusci.vcs.bootstrap import find_previous_release
|
|
498
|
+
|
|
499
|
+
repo = self.get_repo()
|
|
500
|
+
release = find_previous_release(repo, self.project__git__prefix_release)
|
|
501
|
+
if release is not None:
|
|
502
|
+
return LooseVersion(self.get_version_for_tag(release.tag_name))
|
|
503
|
+
|
|
504
|
+
@property
|
|
505
|
+
def config_project_path(self) -> Optional[str]:
|
|
506
|
+
if not self.repo_root:
|
|
507
|
+
return
|
|
508
|
+
path = Path(self.repo_root) / self.config_filename
|
|
509
|
+
if path.is_file():
|
|
510
|
+
return str(path)
|
|
511
|
+
|
|
512
|
+
@property
|
|
513
|
+
def project_local_dir(self) -> str:
|
|
514
|
+
"""location of the user local directory for the project
|
|
515
|
+
e.g., ~/.cumulusci/NPSP-Extension-Test/"""
|
|
516
|
+
|
|
517
|
+
# depending on where we are in bootstrapping the UniversalConfig
|
|
518
|
+
# the canonical projectname could be located in one of two places
|
|
519
|
+
if self.project__name:
|
|
520
|
+
name = self.project__name
|
|
521
|
+
else:
|
|
522
|
+
name = self.config_project.get("project", {}).get("name", "")
|
|
523
|
+
|
|
524
|
+
path = str(self.universal_config_obj.cumulusci_config_dir / name)
|
|
525
|
+
if not os.path.isdir(path):
|
|
526
|
+
os.makedirs(path)
|
|
527
|
+
return path
|
|
528
|
+
|
|
529
|
+
@property
|
|
530
|
+
def default_package_path(self) -> Path:
|
|
531
|
+
if self.project__source_format == "sfdx":
|
|
532
|
+
relpath = "force-app"
|
|
533
|
+
for pkg in self.sfdx_project_config.get("packageDirectories", []):
|
|
534
|
+
if pkg.get("default"):
|
|
535
|
+
relpath = pkg["path"]
|
|
536
|
+
else:
|
|
537
|
+
relpath = "src"
|
|
538
|
+
return Path(self.repo_root, relpath).resolve()
|
|
539
|
+
|
|
540
|
+
@property
|
|
541
|
+
def sfdx_project_config(self) -> Dict[str, Any]:
|
|
542
|
+
with open(
|
|
543
|
+
Path(self.repo_root) / "sfdx-project.json", "r", encoding="utf-8"
|
|
544
|
+
) as f:
|
|
545
|
+
config = json.load(f)
|
|
546
|
+
return config
|
|
547
|
+
|
|
548
|
+
@property
|
|
549
|
+
def allow_remote_code(self) -> bool:
|
|
550
|
+
return self.source.allow_remote_code
|
|
551
|
+
|
|
552
|
+
@property
|
|
553
|
+
def repo_service(self):
|
|
554
|
+
vcs_service = self.repo_info.get("vcs_service", None)
|
|
555
|
+
if vcs_service:
|
|
556
|
+
return vcs_service
|
|
557
|
+
|
|
558
|
+
vcs_service = self.get_service_type_for_repo(self.repo_url)
|
|
559
|
+
|
|
560
|
+
if vcs_service is None:
|
|
561
|
+
raise VcsException("Provider class for not found in config")
|
|
562
|
+
|
|
563
|
+
if self._repo_info:
|
|
564
|
+
self._repo_info["vcs_service"] = vcs_service
|
|
565
|
+
|
|
566
|
+
return vcs_service
|
|
567
|
+
|
|
568
|
+
@repo_service.setter
|
|
569
|
+
def repo_service(self, vcs_service: "VCSService"):
|
|
570
|
+
"""Set the VCS service for this project config."""
|
|
571
|
+
from cumulusci.vcs.base import VCSService
|
|
572
|
+
|
|
573
|
+
if not isinstance(vcs_service, VCSService):
|
|
574
|
+
raise TypeError("repo_service must be an instance of VCSService")
|
|
575
|
+
|
|
576
|
+
self._repo_info["vcs_service"] = vcs_service
|
|
577
|
+
|
|
578
|
+
def get_service_type_for_repo(
|
|
579
|
+
self, url: str, service_alias: Optional[str] = None
|
|
580
|
+
) -> "VCSService":
|
|
581
|
+
from cumulusci.vcs.bootstrap import get_service_for_repo_url
|
|
582
|
+
|
|
583
|
+
vcs_service = get_service_for_repo_url(self, url, service_alias=service_alias)
|
|
584
|
+
vcs_service.logger = self.logger
|
|
585
|
+
return vcs_service
|
|
586
|
+
|
|
587
|
+
def get_tag_for_version(self, prefix: str, version: str) -> str:
|
|
588
|
+
"""Given a prefix and version, returns the appropriate tag name to use."""
|
|
589
|
+
try:
|
|
590
|
+
return PackageVersionNumber.parse(version).format_tag(prefix)
|
|
591
|
+
except ValueError:
|
|
592
|
+
return f"{prefix}{version}"
|
|
593
|
+
|
|
594
|
+
def get_version_for_tag(
|
|
595
|
+
self,
|
|
596
|
+
tag: str,
|
|
597
|
+
prefix_beta: Optional[str] = None,
|
|
598
|
+
prefix_release: Optional[str] = None,
|
|
599
|
+
) -> Optional[str]:
|
|
600
|
+
try:
|
|
601
|
+
return PackageVersionNumber.parse_tag(
|
|
602
|
+
tag,
|
|
603
|
+
prefix_beta or self.project__git__prefix_beta,
|
|
604
|
+
prefix_release or self.project__git__prefix_release,
|
|
605
|
+
).format()
|
|
606
|
+
except ValueError:
|
|
607
|
+
pass
|
|
608
|
+
|
|
609
|
+
def set_keychain(self, keychain: "BaseProjectKeychain"):
|
|
610
|
+
self.keychain = keychain
|
|
611
|
+
|
|
612
|
+
def _check_keychain(self):
|
|
613
|
+
if not self.keychain:
|
|
614
|
+
raise KeychainNotFound(
|
|
615
|
+
"Could not find config.keychain. You must call "
|
|
616
|
+
+ "config.set_keychain(keychain) before accessing orgs"
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
def get_repo_from_url(self, url: str) -> Optional[AbstractRepo]:
|
|
620
|
+
from cumulusci.vcs.bootstrap import get_repo_from_url
|
|
621
|
+
|
|
622
|
+
repo = get_repo_from_url(self, url)
|
|
623
|
+
repo.logger = self.logger
|
|
624
|
+
return repo
|
|
625
|
+
|
|
626
|
+
def get_task(self, name: str) -> TaskConfig:
|
|
627
|
+
"""Get a TaskConfig by task name
|
|
628
|
+
|
|
629
|
+
If the name has a colon, look for it in a different project config.
|
|
630
|
+
"""
|
|
631
|
+
if ":" in name:
|
|
632
|
+
ns, name = name.split(":")
|
|
633
|
+
other_config = self.get_namespace(ns)
|
|
634
|
+
task_config = other_config.get_task(name)
|
|
635
|
+
task_config.project_config = other_config
|
|
636
|
+
else:
|
|
637
|
+
task_config = super().get_task(name)
|
|
638
|
+
task_config.project_config = self
|
|
639
|
+
return task_config
|
|
640
|
+
|
|
641
|
+
def get_flow(self, name) -> FlowConfig:
|
|
642
|
+
"""Get a FlowConfig by flow name
|
|
643
|
+
|
|
644
|
+
If the name has a colon, look for it in a different project config.
|
|
645
|
+
"""
|
|
646
|
+
if ":" in name:
|
|
647
|
+
ns, name = name.split(":")
|
|
648
|
+
other_config = self.get_namespace(ns)
|
|
649
|
+
flow_config = other_config.get_flow(name)
|
|
650
|
+
flow_config.name = name
|
|
651
|
+
flow_config.project_config = other_config
|
|
652
|
+
else:
|
|
653
|
+
flow_config = super().get_flow(name)
|
|
654
|
+
flow_config.name = name
|
|
655
|
+
flow_config.project_config = self
|
|
656
|
+
return flow_config
|
|
657
|
+
|
|
658
|
+
def get_namespace(self, ns: str) -> "BaseProjectConfig":
|
|
659
|
+
"""Look up another project config by its name in the `sources` config.
|
|
660
|
+
|
|
661
|
+
Also makes sure the project has been fetched, if it's from an external source.
|
|
662
|
+
"""
|
|
663
|
+
spec = self.lookup(f"sources__{ns}")
|
|
664
|
+
if spec is None:
|
|
665
|
+
raise NamespaceNotFoundError(f"Namespace not found: {ns}")
|
|
666
|
+
|
|
667
|
+
return self.include_source(spec)
|
|
668
|
+
|
|
669
|
+
def include_source(
|
|
670
|
+
self,
|
|
671
|
+
spec: Union[VCSSourceModel, LocalFolderSourceModel, dict],
|
|
672
|
+
) -> "BaseProjectConfig":
|
|
673
|
+
"""Make sure a project has been fetched from its source.
|
|
674
|
+
|
|
675
|
+
This either fetches the project code and constructs its project config,
|
|
676
|
+
or returns a project config that was previously loaded with the same spec.
|
|
677
|
+
"""
|
|
678
|
+
|
|
679
|
+
if isinstance(spec, dict):
|
|
680
|
+
parsed_spec = None
|
|
681
|
+
source_models = VCSSource.registered_source_models()
|
|
682
|
+
source_models.append(LocalFolderSourceModel)
|
|
683
|
+
|
|
684
|
+
for model_class in source_models:
|
|
685
|
+
try:
|
|
686
|
+
parsed_spec = model_class(**spec)
|
|
687
|
+
except ValidationError:
|
|
688
|
+
pass
|
|
689
|
+
except TypeError:
|
|
690
|
+
pass
|
|
691
|
+
else:
|
|
692
|
+
break
|
|
693
|
+
|
|
694
|
+
if not parsed_spec:
|
|
695
|
+
raise ValueError(f"Invalid source spec: {spec}")
|
|
696
|
+
|
|
697
|
+
spec = parsed_spec
|
|
698
|
+
|
|
699
|
+
if spec in self.included_sources:
|
|
700
|
+
project_config = self.included_sources[spec]
|
|
701
|
+
else:
|
|
702
|
+
if isinstance(spec, VCSSourceModel):
|
|
703
|
+
source = VCSSource.create(self, spec)
|
|
704
|
+
elif isinstance(spec, LocalFolderSourceModel):
|
|
705
|
+
source = LocalFolderSource(self, spec)
|
|
706
|
+
|
|
707
|
+
self.logger.info(f"Fetching from {source}")
|
|
708
|
+
project_config = source.fetch()
|
|
709
|
+
project_config.set_keychain(self.keychain)
|
|
710
|
+
project_config.source = source
|
|
711
|
+
self.included_sources[spec] = project_config
|
|
712
|
+
|
|
713
|
+
# If I can't load remote code, make sure that my
|
|
714
|
+
# included repos can't either.
|
|
715
|
+
if not self.allow_remote_code:
|
|
716
|
+
spec.allow_remote_code = False
|
|
717
|
+
else:
|
|
718
|
+
project_config._add_tasks_directory_to_python_path()
|
|
719
|
+
|
|
720
|
+
return project_config
|
|
721
|
+
|
|
722
|
+
def _add_tasks_directory_to_python_path(self):
|
|
723
|
+
# https://stackoverflow.com/a/2700924/113477
|
|
724
|
+
if not self.allow_remote_code:
|
|
725
|
+
return False
|
|
726
|
+
|
|
727
|
+
directory = str(Path(self.repo_root) / "tasks")
|
|
728
|
+
if directory not in tasks.__path__:
|
|
729
|
+
self.logger.debug(f"Adding {directory} to tasks.__path__")
|
|
730
|
+
tasks.__path__.append(directory)
|
|
731
|
+
if get_debug_mode():
|
|
732
|
+
spec = getattr(self.source, "spec", ".")
|
|
733
|
+
self.logger.debug(
|
|
734
|
+
f"After importing {spec}: tasks.__path__ {tasks.__path__}"
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
def construct_subproject_config(self, **kwargs) -> "BaseProjectConfig":
|
|
738
|
+
"""Construct another project config for an external source"""
|
|
739
|
+
return self.__class__(
|
|
740
|
+
self.universal_config_obj,
|
|
741
|
+
included_sources=self.included_sources,
|
|
742
|
+
cache_dir=self.cache_dir,
|
|
743
|
+
**kwargs,
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
def relpath(self, path: str) -> str:
|
|
747
|
+
"""Convert path to be relative to the project repo root."""
|
|
748
|
+
return os.path.relpath(os.path.join(self.repo_root, path))
|
|
749
|
+
|
|
750
|
+
@property
|
|
751
|
+
def cache_dir(self) -> Path:
|
|
752
|
+
"A project cache which is on the local filesystem. Prefer open_cache where possible."
|
|
753
|
+
if self._cache_dir:
|
|
754
|
+
return self._cache_dir
|
|
755
|
+
|
|
756
|
+
assert self.repo_root
|
|
757
|
+
cache_dir = Path(self.repo_root, ".cci")
|
|
758
|
+
cache_dir.mkdir(exist_ok=True)
|
|
759
|
+
|
|
760
|
+
return cache_dir
|
|
761
|
+
|
|
762
|
+
@contextmanager
|
|
763
|
+
def open_cache(self, cache_name: str) -> Iterable[FSResource]:
|
|
764
|
+
"A context managed PyFilesystem-based cache which could theoretically be on any filesystem."
|
|
765
|
+
with open_fs_resource(self.cache_dir / cache_name) as cache_dir:
|
|
766
|
+
cache_dir.mkdir(exist_ok=True, parents=True)
|
|
767
|
+
yield cache_dir
|
|
768
|
+
|
|
769
|
+
@classmethod
|
|
770
|
+
def get_package_data(cls, config: "BaseProjectConfig"):
|
|
771
|
+
namespace = config.project__package__namespace
|
|
772
|
+
package_name = (
|
|
773
|
+
config.project__package__name_managed
|
|
774
|
+
or config.project__package__name
|
|
775
|
+
or "Package"
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
return package_name, namespace
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
class RemoteProjectConfig(ProjectConfigPropertiesMixin):
|
|
782
|
+
pass
|