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,569 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import webbrowser
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
from typing import List, Optional, Union
|
|
5
|
+
from urllib.parse import urlparse
|
|
6
|
+
|
|
7
|
+
import github3
|
|
8
|
+
from github3 import GitHub, GitHubEnterprise # , login
|
|
9
|
+
from github3.exceptions import (
|
|
10
|
+
AuthenticationFailed,
|
|
11
|
+
ConnectionError,
|
|
12
|
+
ForbiddenError,
|
|
13
|
+
NotFoundError,
|
|
14
|
+
ResponseError,
|
|
15
|
+
TransportError,
|
|
16
|
+
)
|
|
17
|
+
from github3.session import GitHubSession
|
|
18
|
+
from requests.adapters import HTTPAdapter
|
|
19
|
+
from requests.exceptions import RetryError
|
|
20
|
+
from requests.models import Response
|
|
21
|
+
from requests.packages.urllib3.util.retry import Retry
|
|
22
|
+
|
|
23
|
+
from cumulusci.core.config import BaseProjectConfig, ServiceConfig
|
|
24
|
+
from cumulusci.core.exceptions import ( # DependencyLookupError
|
|
25
|
+
GithubApiError,
|
|
26
|
+
GithubApiNotFoundError,
|
|
27
|
+
GithubException,
|
|
28
|
+
ServiceNotConfigured,
|
|
29
|
+
)
|
|
30
|
+
from cumulusci.tasks.github.util import CommitDir
|
|
31
|
+
from cumulusci.utils.git import parse_repo_url
|
|
32
|
+
from cumulusci.vcs.base import VCSService
|
|
33
|
+
from cumulusci.vcs.github import GitHubRelease, GitHubRepository
|
|
34
|
+
from cumulusci.vcs.github.release_notes.generator import (
|
|
35
|
+
GithubReleaseNotesGenerator,
|
|
36
|
+
ParentPullRequestNotesGenerator,
|
|
37
|
+
)
|
|
38
|
+
from cumulusci.vcs.github.release_notes.parser import parser_configs
|
|
39
|
+
|
|
40
|
+
OAUTH_DEVICE_APP = {
|
|
41
|
+
"client_id": "2a4bc3e5ce4f2c49a957",
|
|
42
|
+
"auth_uri": "https://github.com/login/device/code",
|
|
43
|
+
"token_uri": "https://github.com/login/oauth/access_token",
|
|
44
|
+
"scope": "repo gist",
|
|
45
|
+
}
|
|
46
|
+
SSO_WARNING = """Results may be incomplete. You have not granted your Personal Access token access to the following organizations:"""
|
|
47
|
+
UNAUTHORIZED_WARNING = """
|
|
48
|
+
Bad credentials. Verify that your personal access token is correct and that you are authorized to access this resource.
|
|
49
|
+
"""
|
|
50
|
+
SELF_SIGNED_WARNING = """
|
|
51
|
+
There was a problem verifying the SSL Certificate due to a certificate authority that isn't trusted or a self-signed certificate in the certificate chain. Try setting CUMULUSCI_SYSTEM_CERTS Environment Variable to 'True'. See https://cumulusci.readthedocs.io/en/stable/env-var-reference.html?#cumulusci-system-certs
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class GitHubRety(Retry):
|
|
56
|
+
def __init__(self, *args, **kwargs):
|
|
57
|
+
super().__init__(*args, **kwargs)
|
|
58
|
+
|
|
59
|
+
def increment(self, *args, **kwargs):
|
|
60
|
+
# Check for connnection and fail on SSLerror
|
|
61
|
+
# SSLCertVerificationError
|
|
62
|
+
if "error" in kwargs:
|
|
63
|
+
error = kwargs["error"]
|
|
64
|
+
error_str = "CERTIFICATE_VERIFY_FAILED"
|
|
65
|
+
if error_str in str(error):
|
|
66
|
+
raise error
|
|
67
|
+
# finally call increment
|
|
68
|
+
return super().increment(*args, **kwargs)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Prepare request retry policy to be attached to github sessions.
|
|
72
|
+
# 401 is a weird status code to retry, but sometimes it happens spuriously
|
|
73
|
+
# and https://github.community/t5/GitHub-API-Development-and/Random-401-errors-after-using-freshly-generated-installation/m-p/22905 suggests retrying
|
|
74
|
+
retries = GitHubRety(status_forcelist=(401, 502, 503, 504), backoff_factor=0.3)
|
|
75
|
+
adapter = HTTPAdapter(max_retries=retries)
|
|
76
|
+
|
|
77
|
+
INSTALLATIONS = {}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _determine_github_client(host: Union[str, None], client_params: dict) -> GitHub:
|
|
81
|
+
"""Determine the appropriate GitHub client based on the host.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
host (Union[str, None]): The host for the GitHub client.
|
|
85
|
+
client_params (dict): Parameters for the GitHub client.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
GitHub: The GitHub client instance.
|
|
89
|
+
"""
|
|
90
|
+
# also covers "api.github.com"
|
|
91
|
+
is_github: bool = host in (None, "None") or "github.com" in host
|
|
92
|
+
client_cls: GitHub = GitHub if is_github else GitHubEnterprise # type: ignore
|
|
93
|
+
params: dict = client_params
|
|
94
|
+
if not is_github:
|
|
95
|
+
params["url"] = "https://" + host # type: ignore
|
|
96
|
+
|
|
97
|
+
return client_cls(**params)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_github_api_for_repo(keychain, repo_url, session=None) -> GitHub:
|
|
101
|
+
owner, repo_name, host = parse_repo_url(repo_url)
|
|
102
|
+
gh: GitHub = _determine_github_client(
|
|
103
|
+
host,
|
|
104
|
+
{
|
|
105
|
+
"session": session
|
|
106
|
+
or GitHubSession(default_read_timeout=30, default_connect_timeout=30)
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Apply retry policy
|
|
111
|
+
gh.session.mount("http://", adapter)
|
|
112
|
+
gh.session.mount("https://", adapter)
|
|
113
|
+
|
|
114
|
+
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN")
|
|
115
|
+
APP_KEY = os.environ.get("GITHUB_APP_KEY", "").encode("utf-8")
|
|
116
|
+
APP_ID = os.environ.get("GITHUB_APP_ID")
|
|
117
|
+
if APP_ID and APP_KEY:
|
|
118
|
+
installation = INSTALLATIONS.get((owner, repo_name))
|
|
119
|
+
if installation is None:
|
|
120
|
+
gh.login_as_app(APP_KEY, APP_ID, expire_in=120)
|
|
121
|
+
try:
|
|
122
|
+
installation = gh.app_installation_for_repository(owner, repo_name)
|
|
123
|
+
except github3.exceptions.NotFoundError:
|
|
124
|
+
raise GithubException(
|
|
125
|
+
f"Could not access {owner}/{repo_name} using GitHub app. "
|
|
126
|
+
"Does the app need to be installed for this repository?"
|
|
127
|
+
)
|
|
128
|
+
INSTALLATIONS[(owner, repo_name)] = installation
|
|
129
|
+
gh.login_as_app_installation(APP_KEY, APP_ID, installation.id)
|
|
130
|
+
elif GITHUB_TOKEN:
|
|
131
|
+
gh.login(token=GITHUB_TOKEN)
|
|
132
|
+
else:
|
|
133
|
+
token = get_auth_from_service(host, keychain)
|
|
134
|
+
gh.login(token=token)
|
|
135
|
+
|
|
136
|
+
return gh
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_auth_from_service(host, keychain) -> str:
|
|
140
|
+
"""
|
|
141
|
+
Given a host extracted from a repo_url, returns the username and token for
|
|
142
|
+
the first service with a matching server_domain
|
|
143
|
+
"""
|
|
144
|
+
if host is None or host == "None" or "github.com" in host:
|
|
145
|
+
service_config = keychain.get_service("github")
|
|
146
|
+
else:
|
|
147
|
+
services = keychain.get_services_for_type("github_enterprise")
|
|
148
|
+
service_by_host = {service.server_domain: service for service in services}
|
|
149
|
+
|
|
150
|
+
# Check when connecting to server, but not when creating new service as this would always catch
|
|
151
|
+
if list(service_by_host.keys()).count(host) == 0:
|
|
152
|
+
raise ServiceNotConfigured(
|
|
153
|
+
f"No Github Enterprise service configured for domain {host}."
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
service_config = service_by_host[host]
|
|
157
|
+
|
|
158
|
+
# Basic Auth no longer supported on github.com, so only returning token
|
|
159
|
+
# this requires GitHub Enterprise to use token auth and not Basic auth
|
|
160
|
+
# docs.github.com/en/rest/overview/other-authentication-methods#via-username-and-password
|
|
161
|
+
return service_config.token
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def validate_gh_enterprise(host: str, keychain) -> None:
|
|
165
|
+
services = keychain.get_services_for_type("github_enterprise")
|
|
166
|
+
if services:
|
|
167
|
+
hosts = [service.server_domain for service in services]
|
|
168
|
+
if hosts.count(host) > 1:
|
|
169
|
+
raise GithubException(
|
|
170
|
+
f"More than one Github Enterprise service configured for domain {host}."
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def get_sso_disabled_orgs(response: Response) -> list:
|
|
175
|
+
"""
|
|
176
|
+
Given a response from Github, return a list of organization IDs without SSO
|
|
177
|
+
grants.
|
|
178
|
+
"""
|
|
179
|
+
disabled_orgs = []
|
|
180
|
+
sso_header = response.headers.get("X-Github-Sso")
|
|
181
|
+
partial_results_prefix = "partial-results; organizations="
|
|
182
|
+
|
|
183
|
+
if sso_header and partial_results_prefix in sso_header:
|
|
184
|
+
disabled_orgs = sso_header[len(partial_results_prefix) :].split(",")
|
|
185
|
+
|
|
186
|
+
return disabled_orgs
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def format_github3_exception(
|
|
190
|
+
exc: Union[ResponseError, TransportError, ConnectionError]
|
|
191
|
+
) -> str:
|
|
192
|
+
"""Checks github3 exceptions for the most common GitHub authentication
|
|
193
|
+
issues, returning a user-friendly message if found.
|
|
194
|
+
|
|
195
|
+
@param exc: The exception to process
|
|
196
|
+
@returns: The formatted exception string
|
|
197
|
+
"""
|
|
198
|
+
user_warning = ""
|
|
199
|
+
|
|
200
|
+
too_many_str = "too many 401 error responses"
|
|
201
|
+
is_bad_auth_retry = (
|
|
202
|
+
type(exc) is TransportError
|
|
203
|
+
and type(exc.exception) is RetryError
|
|
204
|
+
and too_many_str in str(exc.exception)
|
|
205
|
+
)
|
|
206
|
+
is_auth_failure = type(exc) is AuthenticationFailed
|
|
207
|
+
|
|
208
|
+
if is_bad_auth_retry or is_auth_failure:
|
|
209
|
+
user_warning = UNAUTHORIZED_WARNING
|
|
210
|
+
|
|
211
|
+
if isinstance(exc, ResponseError):
|
|
212
|
+
scope_error_msg = check_github_scopes(exc)
|
|
213
|
+
sso_error_msg = check_github_sso_auth(exc)
|
|
214
|
+
user_warning = scope_error_msg + sso_error_msg
|
|
215
|
+
|
|
216
|
+
if isinstance(exc, ConnectionError):
|
|
217
|
+
if "self signed certificate" in str(exc.exception):
|
|
218
|
+
user_warning = SELF_SIGNED_WARNING
|
|
219
|
+
else:
|
|
220
|
+
return ""
|
|
221
|
+
|
|
222
|
+
return user_warning
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def get_oauth_scopes(response: Response) -> set:
|
|
226
|
+
"""
|
|
227
|
+
Given a response from Github, return the set of OAuth scopes for its
|
|
228
|
+
request.
|
|
229
|
+
"""
|
|
230
|
+
authorized_scopes = set()
|
|
231
|
+
|
|
232
|
+
# If the token isn't authorized "X-OAuth-Scopes" header won't be present
|
|
233
|
+
x_oauth_scopes = response.headers.get("X-OAuth-Scopes")
|
|
234
|
+
if x_oauth_scopes:
|
|
235
|
+
authorized_scopes = set(x_oauth_scopes.split(", "))
|
|
236
|
+
|
|
237
|
+
return authorized_scopes
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def check_github_scopes(exc: ResponseError) -> str:
|
|
241
|
+
"""
|
|
242
|
+
Parse github3 ResponseError headers for the correct scopes and return a
|
|
243
|
+
warning if the user is missing.
|
|
244
|
+
|
|
245
|
+
@param exc: The exception to process
|
|
246
|
+
@returns: The formatted exception string
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
user_warning = ""
|
|
250
|
+
|
|
251
|
+
has_wrong_status_code = exc.response.status_code not in (403, 404)
|
|
252
|
+
if has_wrong_status_code:
|
|
253
|
+
return user_warning
|
|
254
|
+
|
|
255
|
+
token_scopes = get_oauth_scopes(exc.response)
|
|
256
|
+
|
|
257
|
+
# Gist resource won't return X-Accepted-OAuth-Scopes for some reason, so this
|
|
258
|
+
# string might be `None`; we discard the empty string if so.
|
|
259
|
+
accepted_scopes = exc.response.headers.get("X-Accepted-OAuth-Scopes") or ""
|
|
260
|
+
accepted_scopes = set(accepted_scopes.split(", "))
|
|
261
|
+
accepted_scopes.discard("")
|
|
262
|
+
|
|
263
|
+
request_url = urlparse(exc.response.url)
|
|
264
|
+
if not accepted_scopes and request_url.path == "/gists":
|
|
265
|
+
accepted_scopes = {"gist"}
|
|
266
|
+
|
|
267
|
+
missing_scopes = accepted_scopes.difference(token_scopes)
|
|
268
|
+
if missing_scopes:
|
|
269
|
+
user_warning = f"Your token may be missing the following scopes: {', '.join(missing_scopes)}\n"
|
|
270
|
+
# This assumes we're not on enterprise and 'api.github.com' == request_url.hostname
|
|
271
|
+
user_warning += (
|
|
272
|
+
"Visit Settings > Developer settings > Personal access tokens to add them."
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return user_warning
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def check_github_sso_auth(exc: ResponseError) -> str:
|
|
279
|
+
"""
|
|
280
|
+
Check ResponseError header for SSO authorization and return a warning if
|
|
281
|
+
required
|
|
282
|
+
|
|
283
|
+
@param exc: The exception to process
|
|
284
|
+
@returns: The formatted exception string
|
|
285
|
+
"""
|
|
286
|
+
user_warning = ""
|
|
287
|
+
headers = exc.response.headers
|
|
288
|
+
|
|
289
|
+
if exc.response.status_code != 403 or "X-Github-Sso" not in headers:
|
|
290
|
+
return user_warning
|
|
291
|
+
|
|
292
|
+
sso_header = str(headers["X-Github-Sso"] or "")
|
|
293
|
+
if sso_header.startswith("required; url="):
|
|
294
|
+
# In this case the message from github is good enough, but we can help
|
|
295
|
+
# the user by opening a browser to authorize the token.
|
|
296
|
+
auth_url = sso_header.split("url=", maxsplit=1)[1]
|
|
297
|
+
user_warning = f"{exc.message}\n{auth_url}"
|
|
298
|
+
webbrowser.open(auth_url)
|
|
299
|
+
elif sso_header.startswith("partial-results"):
|
|
300
|
+
# In cases where we don't have complete results we get the
|
|
301
|
+
# partal-results header, so return the organization IDs. This may or
|
|
302
|
+
# may not be useful without help from us to lookup the org IDs.
|
|
303
|
+
unauthorized_org_ids = get_sso_disabled_orgs(exc.response)
|
|
304
|
+
user_warning = f"{SSO_WARNING} {unauthorized_org_ids}"
|
|
305
|
+
|
|
306
|
+
return user_warning
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
@lru_cache(50)
|
|
310
|
+
def get_github_service_for_url(
|
|
311
|
+
project_config: BaseProjectConfig, url: str
|
|
312
|
+
) -> Optional[VCSService]:
|
|
313
|
+
return GitHubService.get_service_for_url(
|
|
314
|
+
project_config, url
|
|
315
|
+
) or GitHubEnterpriseService.get_service_for_url(project_config, url)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class GitHubService(VCSService):
|
|
319
|
+
service_type = "github"
|
|
320
|
+
_repo: GitHubRepository
|
|
321
|
+
github: GitHub
|
|
322
|
+
|
|
323
|
+
def __init__(self, config: BaseProjectConfig, name: Optional[str] = None, **kwargs):
|
|
324
|
+
"""Initializes the GitHub service with the given project configuration.
|
|
325
|
+
Args:
|
|
326
|
+
config (BaseProjectConfig): The configuration for the GitHub service.
|
|
327
|
+
name (str): The name or alias of the VCS service.
|
|
328
|
+
**kwargs: Additional keyword arguments.
|
|
329
|
+
"""
|
|
330
|
+
super().__init__(config, name=name, **kwargs)
|
|
331
|
+
repo_url = kwargs.get("repository_url", self.config.repo_url)
|
|
332
|
+
self.github = get_github_api_for_repo(self.keychain, repo_url)
|
|
333
|
+
self._repo: GitHubRepository = None
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def dynamic_dependency_class(self): # -> Type[GitHubDynamicDependency]:
|
|
337
|
+
"""Returns the dynamic dependency class for the GitHub service."""
|
|
338
|
+
from cumulusci.core.dependencies.github import GitHubDynamicDependency
|
|
339
|
+
|
|
340
|
+
return GitHubDynamicDependency
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def repo(self) -> GitHubRepository:
|
|
344
|
+
"""Returns the GitHub repository associated with the service."""
|
|
345
|
+
return self._repo
|
|
346
|
+
|
|
347
|
+
@repo.setter
|
|
348
|
+
def repo(self, repo: GitHubRepository):
|
|
349
|
+
"""Set the GitHub repository associated with the service.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
repo (GitHubRepository): The GitHub repository instance to set.
|
|
353
|
+
"""
|
|
354
|
+
self._repo = repo
|
|
355
|
+
|
|
356
|
+
@classmethod
|
|
357
|
+
def validate_service(cls, options: dict, keychain) -> dict:
|
|
358
|
+
"""Validates service for Github and GithubEnterprise.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
options (dict): The options for the service validation.
|
|
362
|
+
keychain: The keychain for accessing project credentials.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
dict: The validated options for the service.
|
|
366
|
+
"""
|
|
367
|
+
username = options["username"]
|
|
368
|
+
token = options["token"]
|
|
369
|
+
# Github service doesn't have "server_domain",
|
|
370
|
+
server_domain = options.get("server_domain", None)
|
|
371
|
+
|
|
372
|
+
gh = _determine_github_client(server_domain, {"token": token})
|
|
373
|
+
if isinstance(gh, GitHubEnterprise):
|
|
374
|
+
validate_gh_enterprise(server_domain, keychain)
|
|
375
|
+
try:
|
|
376
|
+
authed_user = gh.me()
|
|
377
|
+
auth_login = authed_user.login
|
|
378
|
+
assert username == auth_login, f"{username}, {auth_login}"
|
|
379
|
+
except AssertionError as e:
|
|
380
|
+
raise GithubException(
|
|
381
|
+
f"Service username and token username do not match. ({str(e)})"
|
|
382
|
+
)
|
|
383
|
+
except Exception as e:
|
|
384
|
+
warning_msg = format_github3_exception(e) or str(e)
|
|
385
|
+
raise GithubException(
|
|
386
|
+
f"Could not confirm access to the GitHub API: {warning_msg}"
|
|
387
|
+
)
|
|
388
|
+
else:
|
|
389
|
+
member_orgs = {f"{org.id}": f"{org.login}" for org in gh.organizations()}
|
|
390
|
+
options["Organizations"] = ", ".join([k for k in member_orgs.values()])
|
|
391
|
+
|
|
392
|
+
# We're checking for a partial-response SSO header and /user/orgs
|
|
393
|
+
# doesn't include one, so we need /user/repos instead.
|
|
394
|
+
repo_generator = gh.repositories()
|
|
395
|
+
_ = next(repo_generator, None)
|
|
396
|
+
repo_response = repo_generator.last_response
|
|
397
|
+
options["scopes"] = ", ".join(sorted(get_oauth_scopes(repo_response)))
|
|
398
|
+
|
|
399
|
+
unauthorized_org_ids = get_sso_disabled_orgs(repo_response)
|
|
400
|
+
unauthorized_orgs = {
|
|
401
|
+
k: member_orgs[k] for k in unauthorized_org_ids if k in member_orgs
|
|
402
|
+
}
|
|
403
|
+
if unauthorized_orgs:
|
|
404
|
+
options["SSO Disabled"] = ", ".join(
|
|
405
|
+
[k for k in unauthorized_orgs.values()]
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
expiration_date = repo_response.headers.get(
|
|
409
|
+
"GitHub-Authentication-Token-Expiration"
|
|
410
|
+
)
|
|
411
|
+
if expiration_date:
|
|
412
|
+
options["expires"] = expiration_date
|
|
413
|
+
|
|
414
|
+
return options
|
|
415
|
+
|
|
416
|
+
@classmethod
|
|
417
|
+
def get_service_for_url(
|
|
418
|
+
cls,
|
|
419
|
+
project_config: BaseProjectConfig,
|
|
420
|
+
url: str,
|
|
421
|
+
service_alias: Optional[str] = None,
|
|
422
|
+
) -> Optional["GitHubService"]:
|
|
423
|
+
"""Returns the service configuration for the given URL."""
|
|
424
|
+
_owner, _repo_name, host = parse_repo_url(url)
|
|
425
|
+
|
|
426
|
+
if host is None or host == "None" or "github.com" in host:
|
|
427
|
+
service_config = project_config.keychain.get_service(
|
|
428
|
+
cls.service_type, service_alias
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
vcs_service = GitHubService(
|
|
432
|
+
project_config,
|
|
433
|
+
name=service_config.name,
|
|
434
|
+
service_config=service_config,
|
|
435
|
+
logger=project_config.logger,
|
|
436
|
+
repository_url=url,
|
|
437
|
+
)
|
|
438
|
+
project_config.logger.info(
|
|
439
|
+
f"Github service configured for domain {host} : {url}."
|
|
440
|
+
)
|
|
441
|
+
return vcs_service
|
|
442
|
+
project_config.logger.debug(
|
|
443
|
+
f"No Github service configured for domain {host} : {url}."
|
|
444
|
+
)
|
|
445
|
+
return None
|
|
446
|
+
|
|
447
|
+
def get_repository(self, options: dict = {}) -> GitHubRepository:
|
|
448
|
+
"""Returns the GitHub repository."""
|
|
449
|
+
try:
|
|
450
|
+
if self._repo is None:
|
|
451
|
+
self._repo = GitHubRepository(
|
|
452
|
+
self.github,
|
|
453
|
+
self.config,
|
|
454
|
+
logger=self.logger,
|
|
455
|
+
service_type=self.service_type,
|
|
456
|
+
service_config=self.service_config,
|
|
457
|
+
options=options,
|
|
458
|
+
)
|
|
459
|
+
except NotFoundError as e:
|
|
460
|
+
raise GithubApiNotFoundError(f"GitHub repository not found: {e}")
|
|
461
|
+
except ForbiddenError as e:
|
|
462
|
+
raise GithubApiError(f"GitHub repository is not accessible: {e}")
|
|
463
|
+
return self.repo
|
|
464
|
+
|
|
465
|
+
def parse_repo_url(self) -> List[str]:
|
|
466
|
+
owner, repo_name, host = parse_repo_url(self.repo_url)
|
|
467
|
+
return [host or "", owner or "", repo_name or ""]
|
|
468
|
+
|
|
469
|
+
def get_committer(self, repo: GitHubRepository) -> CommitDir:
|
|
470
|
+
"""Returns the committer for the GitHub repository."""
|
|
471
|
+
return CommitDir(repo.repo, logger=self.logger)
|
|
472
|
+
|
|
473
|
+
def markdown(
|
|
474
|
+
self, release: GitHubRelease, mode: str = "gfm", context: str = ""
|
|
475
|
+
) -> str:
|
|
476
|
+
"""Converts the given text to GitHub-flavored Markdown."""
|
|
477
|
+
release_html = self.github.markdown(
|
|
478
|
+
release,
|
|
479
|
+
mode=mode,
|
|
480
|
+
context=context,
|
|
481
|
+
)
|
|
482
|
+
return release_html
|
|
483
|
+
|
|
484
|
+
def release_notes_generator(self, options: dict) -> GithubReleaseNotesGenerator:
|
|
485
|
+
github_info = {
|
|
486
|
+
"github_owner": self.config.repo_owner,
|
|
487
|
+
"github_repo": self.config.repo_name,
|
|
488
|
+
"github_username": self.service_config.username,
|
|
489
|
+
"github_password": self.service_config.password,
|
|
490
|
+
"default_branch": self.config.project__git__default_branch,
|
|
491
|
+
"prefix_beta": self.config.project__git__prefix_beta,
|
|
492
|
+
"prefix_prod": self.config.project__git__prefix_release,
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
generator = GithubReleaseNotesGenerator(
|
|
496
|
+
self.github,
|
|
497
|
+
github_info,
|
|
498
|
+
parser_configs(self.config),
|
|
499
|
+
options["tag"],
|
|
500
|
+
options.get("last_tag"),
|
|
501
|
+
options.get("link_pr"),
|
|
502
|
+
options.get("publish"),
|
|
503
|
+
self.get_repository().has_issues,
|
|
504
|
+
options.get("include_empty"),
|
|
505
|
+
version_id=options.get("version_id"),
|
|
506
|
+
trial_info=options.get("trial_info", False),
|
|
507
|
+
sandbox_date=options.get("sandbox_date", None),
|
|
508
|
+
production_date=options.get("production_date", None),
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
return generator
|
|
512
|
+
|
|
513
|
+
def parent_pr_notes_generator(
|
|
514
|
+
self, repo: GitHubRepository
|
|
515
|
+
) -> ParentPullRequestNotesGenerator:
|
|
516
|
+
"""Returns the parent pull request notes generator for the GitHub repository."""
|
|
517
|
+
return ParentPullRequestNotesGenerator(self.github, repo.repo, self.config)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
class GitHubEnterpriseService(GitHubService):
|
|
521
|
+
service_type = "github_enterprise"
|
|
522
|
+
_repo: GitHubRepository
|
|
523
|
+
|
|
524
|
+
def __init__(self, config: BaseProjectConfig, name: Optional[str] = None, **kwargs):
|
|
525
|
+
super().__init__(config, name=name, **kwargs)
|
|
526
|
+
|
|
527
|
+
@property
|
|
528
|
+
def dynamic_dependency_class(self): # -> Type[GitHubDynamicDependency]:
|
|
529
|
+
"""Returns the dynamic dependency class for the GitHub service."""
|
|
530
|
+
from cumulusci.core.dependencies.github import GitHubDynamicDependency
|
|
531
|
+
|
|
532
|
+
return GitHubDynamicDependency
|
|
533
|
+
|
|
534
|
+
@classmethod
|
|
535
|
+
def get_service_for_url(
|
|
536
|
+
cls,
|
|
537
|
+
project_config: BaseProjectConfig,
|
|
538
|
+
url: str,
|
|
539
|
+
service_alias: Optional[str] = None,
|
|
540
|
+
) -> Optional["GitHubEnterpriseService"]:
|
|
541
|
+
"""Returns the service configuration for the given URL."""
|
|
542
|
+
_owner, _repo_name, host = parse_repo_url(url)
|
|
543
|
+
|
|
544
|
+
configured_services: list[
|
|
545
|
+
ServiceConfig
|
|
546
|
+
] = project_config.keychain.get_services_for_type(cls.service_type)
|
|
547
|
+
service_by_host = {
|
|
548
|
+
service.server_domain: service for service in configured_services
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
# Check when connecting to server, but not when creating new service as this would always catch
|
|
552
|
+
if list(service_by_host.keys()).count(host) == 0:
|
|
553
|
+
project_config.logger.debug(
|
|
554
|
+
f"No Github Enterprise service configured for domain {host} : {url}."
|
|
555
|
+
)
|
|
556
|
+
return None
|
|
557
|
+
|
|
558
|
+
service_config = service_by_host[host]
|
|
559
|
+
vcs_service = GitHubEnterpriseService(
|
|
560
|
+
project_config,
|
|
561
|
+
name=service_config.name,
|
|
562
|
+
service_config=service_config,
|
|
563
|
+
logger=project_config.logger,
|
|
564
|
+
repository_url=url,
|
|
565
|
+
)
|
|
566
|
+
project_config.logger.info(
|
|
567
|
+
f"Github Enterprise service configured for domain {host} : {url}."
|
|
568
|
+
)
|
|
569
|
+
return vcs_service
|