From c5292dd84e02a03b57b72fe62bf7fba295c18f85 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Mon, 15 Sep 2025 21:18:48 -0400 Subject: [PATCH 01/23] Fixes issue where platform name used for main directory. --- tests/e2e_tests/test_basic_plugin.py | 35 +++++++++++++++++++++++++++- utils/generator_utils.py | 16 +++++++++---- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index a147b4f..f2ce0fe 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -32,7 +32,7 @@ def test_new_plugin_e2e(tmp_path_factory, cli_options): # examining this environment. Otherwise pytest runs so many tests, this one can't # easily be found. This is not a CLI arg, because it only needs to be modified when you're # working on this test, not when you're just running it. - run_core_plugin_tests = True + run_core_plugin_tests = False # Build a new plugin in temp dir. tmp_path = tmp_path_factory.mktemp("e2e_new_plugin_test") @@ -114,3 +114,36 @@ def test_new_plugin_e2e(tmp_path_factory, cli_options): if run_core_plugin_tests: e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) + + + # Remove plugin, and test another one. + # This is much faster than having a completely separate test. We lose some test + # independence, but the speedup is worthwhile. + + # Uninstall previous plugin. + cmd = f'uv pip uninstall --python {path_to_python} dsd-newplatform' + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) + + # Test plugin with mismatch between plugin and platform names. + plugin_config = PluginConfig( + platform_name = "MyNewPlatform", + pkg_name = "dsd-my-plugin", + support_automate_all = True, + license_name = "eric", + ) + + args = Namespace(target_dir=tmp_path) + gp.generate_plugin(plugin_config, args) + + # Make sure we have the correct path to the new plugin. + path_new_plugin = tmp_path / "dsd-my-plugin" + assert path_new_plugin.exists() + + # Install plugin editable to django-simple-deploy env. + cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) + + if run_core_plugin_tests: + e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) \ No newline at end of file diff --git a/utils/generator_utils.py b/utils/generator_utils.py index 5f1c390..051547b 100644 --- a/utils/generator_utils.py +++ b/utils/generator_utils.py @@ -121,8 +121,11 @@ def build_new_plugin(args, plugin_config): path_root_new = validate_target_dir(args, plugin_config, path_root) platform_name_lower = get_platform_name_lower(plugin_config.platform_name) + main_dir_name = get_main_dir_name(plugin_config.pkg_name) + replacements = _get_replacements(plugin_config, platform_name_lower) + # Make new plugin dir, and required directory structure. print(f"\nMaking new directory: {path_root_new.as_posix()}") path_root_new.mkdir() @@ -132,7 +135,7 @@ def build_new_plugin(args, plugin_config): # Using mkdir(parents=True), only need to make most deeply nested dirs. new_dirs = [ "developer_resources", - f"dsd_{platform_name_lower}/templates", + f"{main_dir_name}/templates", "tests/integration_tests/reference_files", "tests/e2e_tests", ] @@ -161,7 +164,7 @@ def build_new_plugin(args, plugin_config): for target_file in target_files: print(f" Copying file: {target_file}") path_src = path_root / "plugin_template" / target_file - target_file_new = target_file.replace("dsd_platformname", f"dsd_{platform_name_lower}") + target_file_new = target_file.replace("dsd_platformname", f"{main_dir_name}") path_dest = path_root_new / target_file_new shutil.copy(path_src, path_dest) @@ -196,7 +199,7 @@ def build_new_plugin(args, plugin_config): for k, v in replacements.items(): contents = contents.replace(k, v) - target_file_new = target_file.replace("platformname", f"{platform_name_lower}") + target_file_new = target_file.replace("dsd_platformname", f"{main_dir_name}") path_new = path_root_new / target_file_new path_new.write_text(contents) @@ -209,7 +212,7 @@ def build_new_plugin(args, plugin_config): # Remove automate_all support if needed. if not plugin_config.support_automate_all: print("Commenting out support for --automate-all...") - path = path_root_new / f"dsd_{platform_name_lower}" / "deploy_messages.py" + path = path_root_new / f"{main_dir_name}" / "deploy_messages.py" lines = path.read_text().splitlines() new_lines = [] for line_num, line in enumerate(lines): @@ -238,6 +241,11 @@ def get_platform_name_lower(platform_name): """Return a lowercase version of the platform name.""" return platform_name.lower().replace("-", "").replace("_", "").replace(".", "").replace(" ", "") +def get_main_dir_name(pkg_name): + """Return name of main dir. For dsd-new-platform, that's dsd_new_platform.""" + pkg_name_lower = pkg_name.lower().replace("-", "_") + return f"{pkg_name_lower}" + # --- Helper functions --- From bac38373d3360c7944b4e5b9ca715075e5d1344a Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Tue, 16 Sep 2025 11:45:58 -0400 Subject: [PATCH 02/23] Only generate one plugin for e2e test. --- tests/e2e_tests/test_basic_plugin.py | 128 +++++++++++++-------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index f2ce0fe..2542f35 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -83,67 +83,67 @@ def test_new_plugin_e2e(tmp_path_factory, cli_options): e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) - # Remove plugin, and test another one. - # This is much faster than having a completely separate test. We lose some test - # independence, but the speedup is worthwhile. - - # Uninstall previous plugin. - cmd = f'uv pip uninstall --python {path_to_python} dsd-newfly' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) - - # Build "New Platform" plugin. - plugin_config = PluginConfig( - platform_name = "New Platform", - pkg_name = "dsd-newplatform", - support_automate_all = True, - license_name = "eric", - ) - - args = Namespace(target_dir=tmp_path) - gp.generate_plugin(plugin_config, args) - - # Make sure we have the correct path to the new plugin. - path_new_plugin = tmp_path / "dsd-newplatform" - assert path_new_plugin.exists() - - # Install plugin editable to django-simple-deploy env. - cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) - - if run_core_plugin_tests: - e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) - - - # Remove plugin, and test another one. - # This is much faster than having a completely separate test. We lose some test - # independence, but the speedup is worthwhile. - - # Uninstall previous plugin. - cmd = f'uv pip uninstall --python {path_to_python} dsd-newplatform' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) - - # Test plugin with mismatch between plugin and platform names. - plugin_config = PluginConfig( - platform_name = "MyNewPlatform", - pkg_name = "dsd-my-plugin", - support_automate_all = True, - license_name = "eric", - ) - - args = Namespace(target_dir=tmp_path) - gp.generate_plugin(plugin_config, args) - - # Make sure we have the correct path to the new plugin. - path_new_plugin = tmp_path / "dsd-my-plugin" - assert path_new_plugin.exists() - - # Install plugin editable to django-simple-deploy env. - cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) - - if run_core_plugin_tests: - e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) \ No newline at end of file + # # Remove plugin, and test another one. + # # This is much faster than having a completely separate test. We lose some test + # # independence, but the speedup is worthwhile. + + # # Uninstall previous plugin. + # cmd = f'uv pip uninstall --python {path_to_python} dsd-newfly' + # cmd_parts = shlex.split(cmd) + # subprocess.run(cmd_parts) + + # # Build "New Platform" plugin. + # plugin_config = PluginConfig( + # platform_name = "New Platform", + # pkg_name = "dsd-newplatform", + # support_automate_all = True, + # license_name = "eric", + # ) + + # args = Namespace(target_dir=tmp_path) + # gp.generate_plugin(plugin_config, args) + + # # Make sure we have the correct path to the new plugin. + # path_new_plugin = tmp_path / "dsd-newplatform" + # assert path_new_plugin.exists() + + # # Install plugin editable to django-simple-deploy env. + # cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' + # cmd_parts = shlex.split(cmd) + # subprocess.run(cmd_parts) + + # if run_core_plugin_tests: + # e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) + + + # # Remove plugin, and test another one. + # # This is much faster than having a completely separate test. We lose some test + # # independence, but the speedup is worthwhile. + + # # Uninstall previous plugin. + # cmd = f'uv pip uninstall --python {path_to_python} dsd-newplatform' + # cmd_parts = shlex.split(cmd) + # subprocess.run(cmd_parts) + + # # Test plugin with mismatch between plugin and platform names. + # plugin_config = PluginConfig( + # platform_name = "MyNewPlatform", + # pkg_name = "dsd-my-plugin", + # support_automate_all = True, + # license_name = "eric", + # ) + + # args = Namespace(target_dir=tmp_path) + # gp.generate_plugin(plugin_config, args) + + # # Make sure we have the correct path to the new plugin. + # path_new_plugin = tmp_path / "dsd-my-plugin" + # assert path_new_plugin.exists() + + # # Install plugin editable to django-simple-deploy env. + # cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' + # cmd_parts = shlex.split(cmd) + # subprocess.run(cmd_parts) + + # if run_core_plugin_tests: + # e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) \ No newline at end of file From 336a8c2ef9cf3f8607cb4be46d7967ce3f61f711 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Tue, 16 Sep 2025 12:12:37 -0400 Subject: [PATCH 03/23] Integration tests pass again. --- tests/e2e_tests/test_basic_plugin.py | 2 +- tests/e2e_tests/utils/e2e_utils.py | 1 + tests/integration_tests/test_generator.py | 1 + utils/generator_utils.py | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index 2542f35..1579c8b 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -32,7 +32,7 @@ def test_new_plugin_e2e(tmp_path_factory, cli_options): # examining this environment. Otherwise pytest runs so many tests, this one can't # easily be found. This is not a CLI arg, because it only needs to be modified when you're # working on this test, not when you're just running it. - run_core_plugin_tests = False + run_core_plugin_tests = True # Build a new plugin in temp dir. tmp_path = tmp_path_factory.mktemp("e2e_new_plugin_test") diff --git a/tests/e2e_tests/utils/e2e_utils.py b/tests/e2e_tests/utils/e2e_utils.py index e997a93..fdd36fe 100644 --- a/tests/e2e_tests/utils/e2e_utils.py +++ b/tests/e2e_tests/utils/e2e_utils.py @@ -38,6 +38,7 @@ def run_core_plugin_tests(path_dsd, plugin_config, cli_options): output = subprocess.run(cmd, capture_output=True,shell=True) stdout = output.stdout.decode() + breakpoint() assert "[100%]" in stdout check_core_plugin_tests(stdout, cli_options) diff --git a/tests/integration_tests/test_generator.py b/tests/integration_tests/test_generator.py index d3d7121..8f351ad 100644 --- a/tests/integration_tests/test_generator.py +++ b/tests/integration_tests/test_generator.py @@ -27,6 +27,7 @@ def test_no_spaces_anywhere(tmp_path_factory): path_ref_dir = Path(__file__).parent / "reference_files" / "dsd-newfly-no-space" path_test_plugin = tmp_path / "dsd-newfly" + assert path_test_plugin.exists() dc = dircmp(path_test_plugin, path_ref_dir, ignore=[".DS_Store", "__pycache__"]) assert_dirs_match(dc) diff --git a/utils/generator_utils.py b/utils/generator_utils.py index 051547b..c698d7a 100644 --- a/utils/generator_utils.py +++ b/utils/generator_utils.py @@ -200,6 +200,7 @@ def build_new_plugin(args, plugin_config): contents = contents.replace(k, v) target_file_new = target_file.replace("dsd_platformname", f"{main_dir_name}") + target_file_new = target_file_new.replace("test_platformname_config.py", f"test_{platform_name_lower}_config.py") path_new = path_root_new / target_file_new path_new.write_text(contents) From 9fd7ac74d6aaf78a3b14bd0fc45cc054e0235a67 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Tue, 16 Sep 2025 12:19:39 -0400 Subject: [PATCH 04/23] Both e2e test plugins pass. --- tests/e2e_tests/test_basic_plugin.py | 50 ++++++++++++++-------------- tests/e2e_tests/utils/e2e_utils.py | 1 - 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index 1579c8b..fd08d3f 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -83,37 +83,37 @@ def test_new_plugin_e2e(tmp_path_factory, cli_options): e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) - # # Remove plugin, and test another one. - # # This is much faster than having a completely separate test. We lose some test - # # independence, but the speedup is worthwhile. + # Remove plugin, and test another one. + # This is much faster than having a completely separate test. We lose some test + # independence, but the speedup is worthwhile. - # # Uninstall previous plugin. - # cmd = f'uv pip uninstall --python {path_to_python} dsd-newfly' - # cmd_parts = shlex.split(cmd) - # subprocess.run(cmd_parts) + # Uninstall previous plugin. + cmd = f'uv pip uninstall --python {path_to_python} dsd-newfly' + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) - # # Build "New Platform" plugin. - # plugin_config = PluginConfig( - # platform_name = "New Platform", - # pkg_name = "dsd-newplatform", - # support_automate_all = True, - # license_name = "eric", - # ) + # Build "New Platform" plugin. + plugin_config = PluginConfig( + platform_name = "New Platform", + pkg_name = "dsd-newplatform", + support_automate_all = True, + license_name = "eric", + ) - # args = Namespace(target_dir=tmp_path) - # gp.generate_plugin(plugin_config, args) + args = Namespace(target_dir=tmp_path) + gp.generate_plugin(plugin_config, args) - # # Make sure we have the correct path to the new plugin. - # path_new_plugin = tmp_path / "dsd-newplatform" - # assert path_new_plugin.exists() + # Make sure we have the correct path to the new plugin. + path_new_plugin = tmp_path / "dsd-newplatform" + assert path_new_plugin.exists() - # # Install plugin editable to django-simple-deploy env. - # cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' - # cmd_parts = shlex.split(cmd) - # subprocess.run(cmd_parts) + # Install plugin editable to django-simple-deploy env. + cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) - # if run_core_plugin_tests: - # e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) + if run_core_plugin_tests: + e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) # # Remove plugin, and test another one. diff --git a/tests/e2e_tests/utils/e2e_utils.py b/tests/e2e_tests/utils/e2e_utils.py index fdd36fe..e997a93 100644 --- a/tests/e2e_tests/utils/e2e_utils.py +++ b/tests/e2e_tests/utils/e2e_utils.py @@ -38,7 +38,6 @@ def run_core_plugin_tests(path_dsd, plugin_config, cli_options): output = subprocess.run(cmd, capture_output=True,shell=True) stdout = output.stdout.decode() - breakpoint() assert "[100%]" in stdout check_core_plugin_tests(stdout, cli_options) From f5ca7ae040583a707c1e6d4f85f337fee86a9a42 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Tue, 16 Sep 2025 12:20:36 -0400 Subject: [PATCH 05/23] All three plugin e2e tests pass. --- tests/e2e_tests/test_basic_plugin.py | 62 ++++++++++++++-------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index fd08d3f..a0a8e06 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -116,34 +116,34 @@ def test_new_plugin_e2e(tmp_path_factory, cli_options): e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) - # # Remove plugin, and test another one. - # # This is much faster than having a completely separate test. We lose some test - # # independence, but the speedup is worthwhile. - - # # Uninstall previous plugin. - # cmd = f'uv pip uninstall --python {path_to_python} dsd-newplatform' - # cmd_parts = shlex.split(cmd) - # subprocess.run(cmd_parts) - - # # Test plugin with mismatch between plugin and platform names. - # plugin_config = PluginConfig( - # platform_name = "MyNewPlatform", - # pkg_name = "dsd-my-plugin", - # support_automate_all = True, - # license_name = "eric", - # ) - - # args = Namespace(target_dir=tmp_path) - # gp.generate_plugin(plugin_config, args) - - # # Make sure we have the correct path to the new plugin. - # path_new_plugin = tmp_path / "dsd-my-plugin" - # assert path_new_plugin.exists() - - # # Install plugin editable to django-simple-deploy env. - # cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' - # cmd_parts = shlex.split(cmd) - # subprocess.run(cmd_parts) - - # if run_core_plugin_tests: - # e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) \ No newline at end of file + # Remove plugin, and test another one. + # This is much faster than having a completely separate test. We lose some test + # independence, but the speedup is worthwhile. + + # Uninstall previous plugin. + cmd = f'uv pip uninstall --python {path_to_python} dsd-newplatform' + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) + + # Test plugin with mismatch between plugin and platform names. + plugin_config = PluginConfig( + platform_name = "MyNewPlatform", + pkg_name = "dsd-my-plugin", + support_automate_all = True, + license_name = "eric", + ) + + args = Namespace(target_dir=tmp_path) + gp.generate_plugin(plugin_config, args) + + # Make sure we have the correct path to the new plugin. + path_new_plugin = tmp_path / "dsd-my-plugin" + assert path_new_plugin.exists() + + # Install plugin editable to django-simple-deploy env. + cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) + + if run_core_plugin_tests: + e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) \ No newline at end of file From 9518a3d7b5f56635c8f52421c4dd2e7009e02935 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 09:51:32 -0400 Subject: [PATCH 06/23] Document project structure and naming conventions. --- docs/plugin_structure.md | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 docs/plugin_structure.md diff --git a/docs/plugin_structure.md b/docs/plugin_structure.md new file mode 100644 index 0000000..221abee --- /dev/null +++ b/docs/plugin_structure.md @@ -0,0 +1,67 @@ +Plugin project structure +=== + +This document describes the overall project structure, and naming conventions within the project. + +Example plugin +--- + +Consider a plugin that should be built from the following user input: + +```sh +$ python generate_plugin.py +What platform are you targeting? (Example: Fly.io) GreenHost +What's the name of your plugin package? (Example: dsd-flyio) dsd-greenhost-high-traffic +Will your plugin support the --automate-all CLI arg? (yes/no) y +What name do you want to appear in the LICENSE file? Eric +``` + +This is a plugin that will support high-traffic sites on a platform called GreenHost. + +Example project structure +--- + +Here's the project structure for the plugin that's generated: + +```sh +$ tree -L 3 dsd-greenhost-high-traffic +dsd-greenhost-high-traffic +├── CHANGELOG.md +├── LICENSE +├── MANIFEST.in +├── README.md +├── developer_resources +│   └── README.md +├── dsd_greenhost_high_traffic +│   ├── __init__.py +│   ├── deploy.py +│   ├── deploy_messages.py +│   ├── platform_deployer.py +│   ├── plugin_config.py +│   └── templates +│   ├── dockerfile_example +│   └── settings.py +├── pyproject.toml +└── tests + ├── conftest.py + ├── e2e_tests + │   ├── __init__.py + │   ├── test_deployment.py + │   └── utils.py + └── integration_tests + ├── reference_files + └── test_greenhost_config.py + +8 directories, 18 files +``` + +Notes +--- + +Here's what's important to note: + +- The outer project directory is the name of the plugin package. +- The main project directory is the plugin package name, with hyphens replaced by underscores. +- The main integration test for configuration changes is named `test__config.py`. + +Currently, plugin package names need to start with `dsd-`. This requirement will be removed before long. From 5e4702dc1ea5e613fb01d903fbf8211772f8c177 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 09:57:36 -0400 Subject: [PATCH 07/23] dsd_platformname -> plugin_pkg_name, to better reflect project naming conventions. --- .../__init__.py | 0 .../deploy.py | 0 .../deploy_messages.py | 0 .../platform_deployer.py | 0 .../plugin_config.py | 0 .../templates/dockerfile_example | 0 .../templates/settings.py | 0 utils/generator_utils.py | 18 +++++++++--------- 8 files changed, 9 insertions(+), 9 deletions(-) rename plugin_template/{dsd_platformname => plugin_pkg_name}/__init__.py (100%) rename plugin_template/{dsd_platformname => plugin_pkg_name}/deploy.py (100%) rename plugin_template/{dsd_platformname => plugin_pkg_name}/deploy_messages.py (100%) rename plugin_template/{dsd_platformname => plugin_pkg_name}/platform_deployer.py (100%) rename plugin_template/{dsd_platformname => plugin_pkg_name}/plugin_config.py (100%) rename plugin_template/{dsd_platformname => plugin_pkg_name}/templates/dockerfile_example (100%) rename plugin_template/{dsd_platformname => plugin_pkg_name}/templates/settings.py (100%) diff --git a/plugin_template/dsd_platformname/__init__.py b/plugin_template/plugin_pkg_name/__init__.py similarity index 100% rename from plugin_template/dsd_platformname/__init__.py rename to plugin_template/plugin_pkg_name/__init__.py diff --git a/plugin_template/dsd_platformname/deploy.py b/plugin_template/plugin_pkg_name/deploy.py similarity index 100% rename from plugin_template/dsd_platformname/deploy.py rename to plugin_template/plugin_pkg_name/deploy.py diff --git a/plugin_template/dsd_platformname/deploy_messages.py b/plugin_template/plugin_pkg_name/deploy_messages.py similarity index 100% rename from plugin_template/dsd_platformname/deploy_messages.py rename to plugin_template/plugin_pkg_name/deploy_messages.py diff --git a/plugin_template/dsd_platformname/platform_deployer.py b/plugin_template/plugin_pkg_name/platform_deployer.py similarity index 100% rename from plugin_template/dsd_platformname/platform_deployer.py rename to plugin_template/plugin_pkg_name/platform_deployer.py diff --git a/plugin_template/dsd_platformname/plugin_config.py b/plugin_template/plugin_pkg_name/plugin_config.py similarity index 100% rename from plugin_template/dsd_platformname/plugin_config.py rename to plugin_template/plugin_pkg_name/plugin_config.py diff --git a/plugin_template/dsd_platformname/templates/dockerfile_example b/plugin_template/plugin_pkg_name/templates/dockerfile_example similarity index 100% rename from plugin_template/dsd_platformname/templates/dockerfile_example rename to plugin_template/plugin_pkg_name/templates/dockerfile_example diff --git a/plugin_template/dsd_platformname/templates/settings.py b/plugin_template/plugin_pkg_name/templates/settings.py similarity index 100% rename from plugin_template/dsd_platformname/templates/settings.py rename to plugin_template/plugin_pkg_name/templates/settings.py diff --git a/utils/generator_utils.py b/utils/generator_utils.py index c698d7a..04bcf67 100644 --- a/utils/generator_utils.py +++ b/utils/generator_utils.py @@ -152,7 +152,7 @@ def build_new_plugin(args, plugin_config): target_files = [ ".gitignore", "developer_resources/README.md", - "dsd_platformname/__init__.py", + "plugin_pkg_name/__init__.py", "tests/e2e_tests/__init__.py", "tests/integration_tests/reference_files/.gitignore", "tests/integration_tests/reference_files/Pipfile", @@ -164,7 +164,7 @@ def build_new_plugin(args, plugin_config): for target_file in target_files: print(f" Copying file: {target_file}") path_src = path_root / "plugin_template" / target_file - target_file_new = target_file.replace("dsd_platformname", f"{main_dir_name}") + target_file_new = target_file.replace("plugin_pkg_name", f"{main_dir_name}") path_dest = path_root_new / target_file_new shutil.copy(path_src, path_dest) @@ -181,12 +181,12 @@ def build_new_plugin(args, plugin_config): "README.md", "CHANGELOG.md", "LICENSE", - "dsd_platformname/platform_deployer.py", - "dsd_platformname/deploy.py", - "dsd_platformname/plugin_config.py", - "dsd_platformname/templates/dockerfile_example", - "dsd_platformname/templates/settings.py", - "dsd_platformname/deploy_messages.py", + "plugin_pkg_name/platform_deployer.py", + "plugin_pkg_name/deploy.py", + "plugin_pkg_name/plugin_config.py", + "plugin_pkg_name/templates/dockerfile_example", + "plugin_pkg_name/templates/settings.py", + "plugin_pkg_name/deploy_messages.py", ] print("\nCustomizing files...") @@ -199,7 +199,7 @@ def build_new_plugin(args, plugin_config): for k, v in replacements.items(): contents = contents.replace(k, v) - target_file_new = target_file.replace("dsd_platformname", f"{main_dir_name}") + target_file_new = target_file.replace("plugin_pkg_name", f"{main_dir_name}") target_file_new = target_file_new.replace("test_platformname_config.py", f"test_{platform_name_lower}_config.py") path_new = path_root_new / target_file_new path_new.write_text(contents) From 493383a95eb1945e21b2be1c1a522b32addb9cb2 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 10:09:02 -0400 Subject: [PATCH 08/23] Validate package name starts with dsd-, test for that rejection. --- tests/integration_tests/test_generator.py | 18 ++++++++++++++++++ utils/plugin_config.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/integration_tests/test_generator.py b/tests/integration_tests/test_generator.py index 8f351ad..751221f 100644 --- a/tests/integration_tests/test_generator.py +++ b/tests/integration_tests/test_generator.py @@ -52,6 +52,24 @@ def test_single_space_platform_name(tmp_path_factory): dc = dircmp(path_test_plugin, path_ref_dir, ignore=[".DS_Store", "__pycache__"]) assert_dirs_match(dc) +def test_reject_not_start_dsd_dash(tmp_path_factory): + """Test that a package name not starting with dsd- is rejected.""" + tmp_path = tmp_path_factory.mktemp("sample_plugin_no_space") + print(f"\nWriting plugin to: {tmp_path.as_posix()}") + + plugin_config = PluginConfig( + platform_name = "NewFly", + pkg_name = "newfly-deployer", + support_automate_all = True, + license_name = "eric", + ) + + args = Namespace(target_dir=tmp_path) + + with pytest.raises(AssertionError): + gp.generate_plugin(plugin_config, args) + + # --- Helper functions --- diff --git a/utils/plugin_config.py b/utils/plugin_config.py index baf6b10..8ee579c 100644 --- a/utils/plugin_config.py +++ b/utils/plugin_config.py @@ -15,4 +15,4 @@ class PluginConfig: def validate(self): """Validate the plugin config.""" assert self.platform_name - assert self.pkg_name \ No newline at end of file + assert self.pkg_name.startswith("dsd-") \ No newline at end of file From 900d4c166d20c52123e24a04dc42b675aef1d76f Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 11:18:35 -0400 Subject: [PATCH 09/23] Remove unneeded f strings. --- utils/generator_utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/generator_utils.py b/utils/generator_utils.py index 04bcf67..07a5d89 100644 --- a/utils/generator_utils.py +++ b/utils/generator_utils.py @@ -164,7 +164,7 @@ def build_new_plugin(args, plugin_config): for target_file in target_files: print(f" Copying file: {target_file}") path_src = path_root / "plugin_template" / target_file - target_file_new = target_file.replace("plugin_pkg_name", f"{main_dir_name}") + target_file_new = target_file.replace("plugin_pkg_name", main_dir_name) path_dest = path_root_new / target_file_new shutil.copy(path_src, path_dest) @@ -199,7 +199,7 @@ def build_new_plugin(args, plugin_config): for k, v in replacements.items(): contents = contents.replace(k, v) - target_file_new = target_file.replace("plugin_pkg_name", f"{main_dir_name}") + target_file_new = target_file.replace("plugin_pkg_name", main_dir_name) target_file_new = target_file_new.replace("test_platformname_config.py", f"test_{platform_name_lower}_config.py") path_new = path_root_new / target_file_new path_new.write_text(contents) @@ -213,7 +213,7 @@ def build_new_plugin(args, plugin_config): # Remove automate_all support if needed. if not plugin_config.support_automate_all: print("Commenting out support for --automate-all...") - path = path_root_new / f"{main_dir_name}" / "deploy_messages.py" + path = path_root_new / main_dir_name / "deploy_messages.py" lines = path.read_text().splitlines() new_lines = [] for line_num, line in enumerate(lines): @@ -245,7 +245,7 @@ def get_platform_name_lower(platform_name): def get_main_dir_name(pkg_name): """Return name of main dir. For dsd-new-platform, that's dsd_new_platform.""" pkg_name_lower = pkg_name.lower().replace("-", "_") - return f"{pkg_name_lower}" + return pkg_name_lower # --- Helper functions --- From b7f93e2ec373525f513c6bbd40ee5044df1e2c6c Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 11:46:13 -0400 Subject: [PATCH 10/23] E2e tests pass with a fixture. --- tests/e2e_tests/test_basic_plugin.py | 95 ++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index a0a8e06..6a09db3 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -26,32 +26,19 @@ import generate_plugin as gp -@pytest.mark.skipif(not e2e_utils.uv_available(), reason="uv must be installed in order to run e2e tests.") -def test_new_plugin_e2e(tmp_path_factory, cli_options): - # Flag to temporarily disable running dsd and plugin tests. This is helpful when - # examining this environment. Otherwise pytest runs so many tests, this one can't - # easily be found. This is not a CLI arg, because it only needs to be modified when you're - # working on this test, not when you're just running it. - run_core_plugin_tests = True - - # Build a new plugin in temp dir. +@pytest.fixture(scope="module") +def get_dev_env(tmp_path_factory, cli_options): + """Set up an env where plugins can be generated and tested within django-simple-deploy. + + - Set up a temp dir. + - Install dev env for django-simple-deploy. + - We can generate plugins, with this temp dir as target location. + - Then install newly-generated plugins to dsd dev env, and run tests from dsd. + """ + # Make the temp directory for the dsd development env. tmp_path = tmp_path_factory.mktemp("e2e_new_plugin_test") print(f"\nBuilding e2e test env at: {tmp_path.as_posix()}") - plugin_config = PluginConfig( - platform_name = "NewFly", - pkg_name = "dsd-newfly", - support_automate_all = True, - license_name = "eric", - ) - - args = Namespace(target_dir=tmp_path) - gp.generate_plugin(plugin_config, args) - - # Make sure we have the correct path to the new plugin. - path_new_plugin = tmp_path / "dsd-newfly" - assert path_new_plugin.exists() - # Clone django-simple-deploy in temp env. path_dsd = tmp_path / "django-simple-deploy" cmd = f"git clone https://github.com/django-simple-deploy/django-simple-deploy.git {path_dsd.as_posix()} --depth 1" @@ -70,10 +57,64 @@ def test_new_plugin_e2e(tmp_path_factory, cli_options): cmd_parts = shlex.split(cmd) subprocess.run(cmd_parts) + run_core_plugin_tests = True if run_core_plugin_tests: # Run core tests without a plugin installed. e2e_utils.run_core_tests(path_dsd, path_to_python, cli_options) + return tmp_path, path_to_python, path_dsd + + + +@pytest.mark.skipif(not e2e_utils.uv_available(), reason="uv must be installed in order to run e2e tests.") +def test_new_plugin_e2e(get_dev_env, cli_options): + dev_env_dir, path_to_python, path_dsd = get_dev_env + # Flag to temporarily disable running dsd and plugin tests. This is helpful when + # examining this environment. Otherwise pytest runs so many tests, this one can't + # easily be found. This is not a CLI arg, because it only needs to be modified when you're + # working on this test, not when you're just running it. + run_core_plugin_tests = True + + # # Build a new plugin in temp dir. + # tmp_path = tmp_path_factory.mktemp("e2e_new_plugin_test") + # print(f"\nBuilding e2e test env at: {tmp_path.as_posix()}") + + plugin_config = PluginConfig( + platform_name = "NewFly", + pkg_name = "dsd-newfly", + support_automate_all = True, + license_name = "eric", + ) + + args = Namespace(target_dir=dev_env_dir) + gp.generate_plugin(plugin_config, args) + + # Make sure we have the correct path to the new plugin. + path_new_plugin = dev_env_dir / "dsd-newfly" + assert path_new_plugin.exists() + + # # Clone django-simple-deploy in temp env. + # path_dsd = tmp_path / "django-simple-deploy" + # cmd = f"git clone https://github.com/django-simple-deploy/django-simple-deploy.git {path_dsd.as_posix()} --depth 1" + # cmd_parts = shlex.split(cmd) + # subprocess.run(cmd_parts) + + # # Build a venv in the django-simple-deploy temp dir. + # venv_dir = path_dsd / ".venv" + # cmd = f"uv venv {venv_dir}" + # cmd_parts = shlex.split(cmd) + # subprocess.run(cmd_parts) + + # # Make an editable install of django-simple-deploy in its environment. + # path_to_python = venv_dir / "bin" / "python" + # cmd = f'uv pip install --python {path_to_python} -e "{path_dsd.as_posix()}[dev]"' + # cmd_parts = shlex.split(cmd) + # subprocess.run(cmd_parts) + + # if run_core_plugin_tests: + # # Run core tests without a plugin installed. + # e2e_utils.run_core_tests(path_dsd, path_to_python, cli_options) + # Install plugin editable to django-simple-deploy env. cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' cmd_parts = shlex.split(cmd) @@ -100,11 +141,11 @@ def test_new_plugin_e2e(tmp_path_factory, cli_options): license_name = "eric", ) - args = Namespace(target_dir=tmp_path) + args = Namespace(target_dir=dev_env_dir) gp.generate_plugin(plugin_config, args) # Make sure we have the correct path to the new plugin. - path_new_plugin = tmp_path / "dsd-newplatform" + path_new_plugin = dev_env_dir / "dsd-newplatform" assert path_new_plugin.exists() # Install plugin editable to django-simple-deploy env. @@ -133,11 +174,11 @@ def test_new_plugin_e2e(tmp_path_factory, cli_options): license_name = "eric", ) - args = Namespace(target_dir=tmp_path) + args = Namespace(target_dir=dev_env_dir) gp.generate_plugin(plugin_config, args) # Make sure we have the correct path to the new plugin. - path_new_plugin = tmp_path / "dsd-my-plugin" + path_new_plugin = dev_env_dir / "dsd-my-plugin" assert path_new_plugin.exists() # Install plugin editable to django-simple-deploy env. From 2abe659cedbd48fc510ca28b9597e31f89ea3b7d Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 12:39:12 -0400 Subject: [PATCH 11/23] Uninstalls previously-tested plugins during e2e tests. --- tests/e2e_tests/test_basic_plugin.py | 81 +++++++++++++--------------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index 6a09db3..6cd3562 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -15,6 +15,7 @@ """ from argparse import Namespace +import json from pathlib import Path import subprocess import shlex @@ -64,10 +65,29 @@ def get_dev_env(tmp_path_factory, cli_options): return tmp_path, path_to_python, path_dsd +@pytest.fixture(scope="function", autouse=True) +def clear_plugins(get_dev_env): + """Remove any plugins from dev env. + + Most tests install a plugin to the dev env. Remove any previously-installed + plugins, so the plugin being tested is the only one in the env. + """ + dev_env_dir, path_to_python, path_dsd = get_dev_env + cmd = f"uv pip list --python {path_to_python} --format=json" + cmd_parts = shlex.split(cmd) + package_dicts_str = subprocess.run(cmd_parts, capture_output=True).stdout + package_dicts = json.loads(package_dicts_str) + package_names = [pd["name"] for pd in package_dicts if pd["name"].startswith("dsd-")] + + for pkg_name in package_names: + cmd = f"uv pip uninstall {pkg_name} --python {path_to_python}" + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) @pytest.mark.skipif(not e2e_utils.uv_available(), reason="uv must be installed in order to run e2e tests.") -def test_new_plugin_e2e(get_dev_env, cli_options): +def test_simple_plugin(get_dev_env, cli_options): + """Test a simple plugin config.""" dev_env_dir, path_to_python, path_dsd = get_dev_env # Flag to temporarily disable running dsd and plugin tests. This is helpful when # examining this environment. Otherwise pytest runs so many tests, this one can't @@ -75,10 +95,6 @@ def test_new_plugin_e2e(get_dev_env, cli_options): # working on this test, not when you're just running it. run_core_plugin_tests = True - # # Build a new plugin in temp dir. - # tmp_path = tmp_path_factory.mktemp("e2e_new_plugin_test") - # print(f"\nBuilding e2e test env at: {tmp_path.as_posix()}") - plugin_config = PluginConfig( platform_name = "NewFly", pkg_name = "dsd-newfly", @@ -93,28 +109,6 @@ def test_new_plugin_e2e(get_dev_env, cli_options): path_new_plugin = dev_env_dir / "dsd-newfly" assert path_new_plugin.exists() - # # Clone django-simple-deploy in temp env. - # path_dsd = tmp_path / "django-simple-deploy" - # cmd = f"git clone https://github.com/django-simple-deploy/django-simple-deploy.git {path_dsd.as_posix()} --depth 1" - # cmd_parts = shlex.split(cmd) - # subprocess.run(cmd_parts) - - # # Build a venv in the django-simple-deploy temp dir. - # venv_dir = path_dsd / ".venv" - # cmd = f"uv venv {venv_dir}" - # cmd_parts = shlex.split(cmd) - # subprocess.run(cmd_parts) - - # # Make an editable install of django-simple-deploy in its environment. - # path_to_python = venv_dir / "bin" / "python" - # cmd = f'uv pip install --python {path_to_python} -e "{path_dsd.as_posix()}[dev]"' - # cmd_parts = shlex.split(cmd) - # subprocess.run(cmd_parts) - - # if run_core_plugin_tests: - # # Run core tests without a plugin installed. - # e2e_utils.run_core_tests(path_dsd, path_to_python, cli_options) - # Install plugin editable to django-simple-deploy env. cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' cmd_parts = shlex.split(cmd) @@ -124,16 +118,16 @@ def test_new_plugin_e2e(get_dev_env, cli_options): e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) - # Remove plugin, and test another one. - # This is much faster than having a completely separate test. We lose some test - # independence, but the speedup is worthwhile. +def test_plugin_single_space_platform_name(get_dev_env, cli_options): + """Test a plugin for a platform with a space in the name.""" + dev_env_dir, path_to_python, path_dsd = get_dev_env - # Uninstall previous plugin. - cmd = f'uv pip uninstall --python {path_to_python} dsd-newfly' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) + # Flag to temporarily disable running dsd and plugin tests. This is helpful when + # examining this environment. Otherwise pytest runs so many tests, this one can't + # easily be found. This is not a CLI arg, because it only needs to be modified when you're + # working on this test, not when you're just running it. + run_core_plugin_tests = True - # Build "New Platform" plugin. plugin_config = PluginConfig( platform_name = "New Platform", pkg_name = "dsd-newplatform", @@ -156,17 +150,16 @@ def test_new_plugin_e2e(get_dev_env, cli_options): if run_core_plugin_tests: e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) +def test_plugin_different_plugin_platform_name(get_dev_env, cli_options): + """Test a plugin where platform and plugin names differ significantly.""" + dev_env_dir, path_to_python, path_dsd = get_dev_env - # Remove plugin, and test another one. - # This is much faster than having a completely separate test. We lose some test - # independence, but the speedup is worthwhile. - - # Uninstall previous plugin. - cmd = f'uv pip uninstall --python {path_to_python} dsd-newplatform' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) + # Flag to temporarily disable running dsd and plugin tests. This is helpful when + # examining this environment. Otherwise pytest runs so many tests, this one can't + # easily be found. This is not a CLI arg, because it only needs to be modified when you're + # working on this test, not when you're just running it. + run_core_plugin_tests = True - # Test plugin with mismatch between plugin and platform names. plugin_config = PluginConfig( platform_name = "MyNewPlatform", pkg_name = "dsd-my-plugin", From d90b587143bb6d39f480d67ff3a38d7482a42d19 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 12:57:58 -0400 Subject: [PATCH 12/23] --include-core-tests -> --run-core-tests --- tests/e2e_tests/conftest.py | 6 +++--- tests/e2e_tests/utils/e2e_utils.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 35423e4..528aa04 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -7,17 +7,17 @@ def pytest_addoption(parser): parser.addoption( - "--include-core-tests", + "--run-core-tests", action="store_true", help="Run the full set of core django-simple-deploy tests for each new plugin.", ) @dataclass class CLIOptions: - include_core_tests: bool=False + run_core_tests: bool=False @pytest.fixture(scope="session") def cli_options(request): return CLIOptions( - include_core_tests=request.config.getoption("--include-core-tests"), + run_core_tests=request.config.getoption("--run-core-tests"), ) diff --git a/tests/e2e_tests/utils/e2e_utils.py b/tests/e2e_tests/utils/e2e_utils.py index e997a93..61f0a21 100644 --- a/tests/e2e_tests/utils/e2e_utils.py +++ b/tests/e2e_tests/utils/e2e_utils.py @@ -47,7 +47,7 @@ def get_core_plugin_test_cmd(path_dsd, cli_options, platform_name_lower): """Get the command for running django-simple-deploy tests after a new plugin has been installed.""" test_filename = f"test_{platform_name_lower}_config.py" - if cli_options.include_core_tests: + if cli_options.run_core_tests: # Run full set of core django-simple-deploy tests, and plugin integration tests. cmd = f"cd {path_dsd.as_posix()} && source .venv/bin/activate && pytest" else: @@ -69,7 +69,7 @@ def check_core_plugin_tests(stdout, cli_options, core_only=False): if core_only: # Core tests, no plugin installed. assert passed >= 31 - elif cli_options.include_core_tests: + elif cli_options.run_core_tests: # Core tests, and plugin's integrationtests. assert passed >= 65 else: From e242d8f893bf46d43a0dd30c26ec6add864b7c40 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 14:43:42 -0400 Subject: [PATCH 13/23] CLI arg to skip tests, and only setup the test plugins. --- tests/e2e_tests/conftest.py | 12 ++++++++++++ tests/e2e_tests/test_basic_plugin.py | 16 +++++----------- tests/e2e_tests/utils/e2e_utils.py | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 528aa04..dd128bb 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -11,13 +11,25 @@ def pytest_addoption(parser): action="store_true", help="Run the full set of core django-simple-deploy tests for each new plugin.", ) + parser.addoption( + # Useful for troubleshooting this test env. May also be useful for poking around + # the full set of test plugins. + # In a full test run, this test runs many django-simple-deploy pytest calls. Each of + # those leads to a pytest-___ temp dir. That can make it difficult or impossible + # to find the dir associated with a specific failing test. + "--setup-plugins-only", + action="store_true", + help="Build full set of test dev env with test plugins, but don't run any tests." + ) @dataclass class CLIOptions: run_core_tests: bool=False + setup_plugins_only: bool=False @pytest.fixture(scope="session") def cli_options(request): return CLIOptions( run_core_tests=request.config.getoption("--run-core-tests"), + setup_plugins_only=request.config.getoption("--setup-plugins-only"), ) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index 6cd3562..b3b1c5b 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -58,10 +58,9 @@ def get_dev_env(tmp_path_factory, cli_options): cmd_parts = shlex.split(cmd) subprocess.run(cmd_parts) - run_core_plugin_tests = True - if run_core_plugin_tests: + if not cli_options.setup_plugins_only: # Run core tests without a plugin installed. - e2e_utils.run_core_tests(path_dsd, path_to_python, cli_options) + e2e_utils.run_dsd_core_tests(path_dsd, path_to_python, cli_options) return tmp_path, path_to_python, path_dsd @@ -89,11 +88,6 @@ def clear_plugins(get_dev_env): def test_simple_plugin(get_dev_env, cli_options): """Test a simple plugin config.""" dev_env_dir, path_to_python, path_dsd = get_dev_env - # Flag to temporarily disable running dsd and plugin tests. This is helpful when - # examining this environment. Otherwise pytest runs so many tests, this one can't - # easily be found. This is not a CLI arg, because it only needs to be modified when you're - # working on this test, not when you're just running it. - run_core_plugin_tests = True plugin_config = PluginConfig( platform_name = "NewFly", @@ -114,7 +108,7 @@ def test_simple_plugin(get_dev_env, cli_options): cmd_parts = shlex.split(cmd) subprocess.run(cmd_parts) - if run_core_plugin_tests: + if not cli_options.setup_plugins_only: e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) @@ -147,7 +141,7 @@ def test_plugin_single_space_platform_name(get_dev_env, cli_options): cmd_parts = shlex.split(cmd) subprocess.run(cmd_parts) - if run_core_plugin_tests: + if not cli_options.setup_plugins_only: e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) def test_plugin_different_plugin_platform_name(get_dev_env, cli_options): @@ -179,5 +173,5 @@ def test_plugin_different_plugin_platform_name(get_dev_env, cli_options): cmd_parts = shlex.split(cmd) subprocess.run(cmd_parts) - if run_core_plugin_tests: + if not cli_options.setup_plugins_only: e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) \ No newline at end of file diff --git a/tests/e2e_tests/utils/e2e_utils.py b/tests/e2e_tests/utils/e2e_utils.py index 61f0a21..100666b 100644 --- a/tests/e2e_tests/utils/e2e_utils.py +++ b/tests/e2e_tests/utils/e2e_utils.py @@ -19,7 +19,7 @@ def uv_available(): return False -def run_core_tests(path_dsd, path_to_python, cli_options): +def run_dsd_core_tests(path_dsd, path_to_python, cli_options): """Run django-simple-deploy's test suite with no plugin installed.""" tests_dir = path_dsd / "tests" cmd = f"{path_to_python} -m pytest {tests_dir.as_posix()}" From ba275b1b83366042b33ba4bf5c88ad2ae0ecae80 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 16:12:39 -0400 Subject: [PATCH 14/23] Uninstall plugins after they're tested. --- tests/e2e_tests/test_basic_plugin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index b3b1c5b..b101412 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -68,10 +68,13 @@ def get_dev_env(tmp_path_factory, cli_options): def clear_plugins(get_dev_env): """Remove any plugins from dev env. - Most tests install a plugin to the dev env. Remove any previously-installed - plugins, so the plugin being tested is the only one in the env. + Most tests install a plugin to the dev env. Remove each plugin after its test runs. """ dev_env_dir, path_to_python, path_dsd = get_dev_env + + # Yield to let test function run, then clear any plugins that were installed. + yield + cmd = f"uv pip list --python {path_to_python} --format=json" cmd_parts = shlex.split(cmd) package_dicts_str = subprocess.run(cmd_parts, capture_output=True).stdout From a2d91fa1c37ab2381ffb056c4735acc6d8996f43 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 16:12:47 -0400 Subject: [PATCH 15/23] Document e2e tests. --- docs/e2e_tests.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 docs/e2e_tests.md diff --git a/docs/e2e_tests.md b/docs/e2e_tests.md new file mode 100644 index 0000000..1e14597 --- /dev/null +++ b/docs/e2e_tests.md @@ -0,0 +1,77 @@ +End to end tests +=== + +Currently, plugins can only be tested when they're installed into a django-simple-deploy test environment. + +To manage this, the end to end tests for this project do the following: + +- Get a temp dir from pytest. +- Make a shallow clone of django-simple-deploy into the temp dir. +- For each plugin test: + - Generate the plugin using `generate_plugin()`. + - Install the plugin to the temp django-simple-deploy env. + - Run django-simple-deploy's tests, which in turn run the plugin's tests. + - Uninstall the plugin from the django-simple-deploy env. + +CLI args +--- + +There are three CLI args that can help with e2e testing: + +`-s` +--- + +This is a pytest flag that streams output as the test runs. Since these are long running tests, it's almost always useful to include this flag. Otherwise it can look like the tests are hanging. + +`--run-core-tests` +--- + +This flag runs the full set of django-simple-deploy tests, for every test plugin. This takes a lot longer. The default behavior is to only use django-simple-deploy to run the plugin's tests. + +`--setup-plugins-only` +--- + +When we run this project's e2e tests, we're actually calling pytest within a temp django-simple-deploy env, once for every test plugin. That means the temp dirs such as `pytest-237` get garbage collected even before the test finishes running. That makes it difficult to see what kind of environment is being built. + +The `--setup-plugins-only` flag prevents any tests from being run. We just set up the temp dev environment for django-simple-deploy, and then generate the full series of test plugins. You can then drop into the latest temp pytest directory, and poke around the full test environment. + +Here's what that looks like: + +```sh +$ pytest tests/e2e_tests -s --setup-plugins-only +... +Building e2e test env at: /private/.../pytest-239/e2e_new_plugin_test0 +... + Built django-simple-deploy... + Built dsd-newfly... + Built dsd-newplatform... + Built dsd-my-plugin... +... +``` + +In just a few seconds, you have a development environment for django-simple-deploy, alongside a set of plugins generated by this project. + +You can cd to this folder, and do anything you want with these resources. Here's how to drop into that folder, see the plugins, install one to the django-simple-deploy environment, and run the new platform's integration tests: + +```sh +$ cd /private/.../pytest-239/e2e_new_plugin_test0 +e2e_new_plugin_test0$ ls -l +django-simple-deploy +dsd-my-plugin +dsd-newfly +dsd-newplatform +e2e_new_plugin_test0$ cd django-simple-deploy +django-simple-deploy$ source .venv/bin/activate +(.venv) django-simple-deploy$ uv pip install -e ../dsd-newplatform + Built dsd-newplatform +... +(.venv) django-simple-deploy$ pytest -k newplatform +========== test session starts ======================== +collected 77 items / 59 deselected / 18 selected +test_newplatform_config.py .................. [100%] +========== 18 passed, 59 deselected in 3.65s ========== +``` + +This is really helpful for maintaining the test suite, and for development work. + +**Note:** This can get a little flaky, because we're working in a temp environment that pytest will destroy when more resources are created. If you want to keep working with these resources, you can copy the entire `e2e_new_plugin_test0` directory to a more stable location. You'll have to rebuild the `django-simple-deploy/.venv` environment, but you'll have a useful, stable development environment to work with. You'll be able to run any tests you want from that environment. From b5690daa4d09049a89b0c9791fecaba017a21c96 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 16:34:17 -0400 Subject: [PATCH 16/23] Links to current docs. --- README.md | 6 ++++++ docs/README.md | 8 ++++++++ 2 files changed, 14 insertions(+) create mode 100644 docs/README.md diff --git a/README.md b/README.md index b840e1b..67672df 100644 --- a/README.md +++ b/README.md @@ -86,3 +86,9 @@ $ pytest tests/e2e_tests -s --include-core-tests ``` Currently, CI tests only run unit and integration tests. There's an open task in django-simple-deploy to remove the dependence on poetry and pipenv for running tests. When that is implemented, e2e tests can run much more easily in CI. + +Documentation +--- + +For more information, see the [docs](docs/) directory. + diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..b3754fa --- /dev/null +++ b/docs/README.md @@ -0,0 +1,8 @@ +dsd-plugin-generator documentation +--- + +I'll probably make a full documentation site on read the docs before long for this project, but for now the most important information is in the main README and here. + +- [Plugin structure](plugin_structure.md): This page describes the structure of a plugin, and the naming conventions used. +- [End to end tests](e2e_tests.md): This page describes how the end to end tests are set up, and how they can be used for development purposes as well as for testing purposes. +- \ No newline at end of file From 640279e3f09afdea76aac068c18158c60d4c42f1 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 16:39:56 -0400 Subject: [PATCH 17/23] Module-level mark to skip e2e tests if uv not available. --- tests/e2e_tests/test_basic_plugin.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index b101412..a71443b 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -27,6 +27,14 @@ import generate_plugin as gp +# Skip these tests if uv is not available. +pytestmark = pytest.mark.skipif( + not e2e_utils.uv_available(), reason="uv must be installed in order to run e2e tests." +) + + +# --- Fixtures --- + @pytest.fixture(scope="module") def get_dev_env(tmp_path_factory, cli_options): """Set up an env where plugins can be generated and tested within django-simple-deploy. @@ -87,7 +95,6 @@ def clear_plugins(get_dev_env): subprocess.run(cmd_parts) -@pytest.mark.skipif(not e2e_utils.uv_available(), reason="uv must be installed in order to run e2e tests.") def test_simple_plugin(get_dev_env, cli_options): """Test a simple plugin config.""" dev_env_dir, path_to_python, path_dsd = get_dev_env From d3c692a07eea73948b0f7d05f09ace99a786b7b1 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 16:45:58 -0400 Subject: [PATCH 18/23] Move fixtures to conftest. --- tests/e2e_tests/conftest.py | 76 +++++++++++++++++ tests/e2e_tests/test_basic_plugin.py | 120 +++++++++++++-------------- 2 files changed, 136 insertions(+), 60 deletions(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index dd128bb..df0d1ee 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -1,10 +1,24 @@ """Configuration for e2e test runs.""" +# from argparse import Namespace +import json +# from pathlib import Path +import subprocess +import shlex + +import pytest + +# from utils.plugin_config import PluginConfig +from tests.e2e_tests.utils import e2e_utils +# import generate_plugin as gp + + from dataclasses import dataclass import pytest +# --- Custom CLI args --- def pytest_addoption(parser): parser.addoption( "--run-core-tests", @@ -33,3 +47,65 @@ def cli_options(request): run_core_tests=request.config.getoption("--run-core-tests"), setup_plugins_only=request.config.getoption("--setup-plugins-only"), ) + + +# --- Fixtures --- + +@pytest.fixture(scope="module") +def get_dev_env(tmp_path_factory, cli_options): + """Set up an env where plugins can be generated and tested within django-simple-deploy. + + - Set up a temp dir. + - Install dev env for django-simple-deploy. + - We can generate plugins, with this temp dir as target location. + - Then install newly-generated plugins to dsd dev env, and run tests from dsd. + """ + # Make the temp directory for the dsd development env. + tmp_path = tmp_path_factory.mktemp("e2e_new_plugin_test") + print(f"\nBuilding e2e test env at: {tmp_path.as_posix()}") + + # Clone django-simple-deploy in temp env. + path_dsd = tmp_path / "django-simple-deploy" + cmd = f"git clone https://github.com/django-simple-deploy/django-simple-deploy.git {path_dsd.as_posix()} --depth 1" + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) + + # Build a venv in the django-simple-deploy temp dir. + venv_dir = path_dsd / ".venv" + cmd = f"uv venv {venv_dir}" + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) + + # Make an editable install of django-simple-deploy in its environment. + path_to_python = venv_dir / "bin" / "python" + cmd = f'uv pip install --python {path_to_python} -e "{path_dsd.as_posix()}[dev]"' + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) + + if not cli_options.setup_plugins_only: + # Run core tests without a plugin installed. + e2e_utils.run_dsd_core_tests(path_dsd, path_to_python, cli_options) + + return tmp_path, path_to_python, path_dsd + +@pytest.fixture(scope="function", autouse=True) +def clear_plugins(get_dev_env): + """Remove any plugins from dev env. + + Most tests install a plugin to the dev env. Remove each plugin after its test runs. + """ + dev_env_dir, path_to_python, path_dsd = get_dev_env + + # Yield to let test function run, then clear any plugins that were installed. + yield + + cmd = f"uv pip list --python {path_to_python} --format=json" + cmd_parts = shlex.split(cmd) + package_dicts_str = subprocess.run(cmd_parts, capture_output=True).stdout + package_dicts = json.loads(package_dicts_str) + package_names = [pd["name"] for pd in package_dicts if pd["name"].startswith("dsd-")] + + for pkg_name in package_names: + cmd = f"uv pip uninstall {pkg_name} --python {path_to_python}" + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) \ No newline at end of file diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index a71443b..664a00d 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -33,66 +33,66 @@ ) -# --- Fixtures --- - -@pytest.fixture(scope="module") -def get_dev_env(tmp_path_factory, cli_options): - """Set up an env where plugins can be generated and tested within django-simple-deploy. - - - Set up a temp dir. - - Install dev env for django-simple-deploy. - - We can generate plugins, with this temp dir as target location. - - Then install newly-generated plugins to dsd dev env, and run tests from dsd. - """ - # Make the temp directory for the dsd development env. - tmp_path = tmp_path_factory.mktemp("e2e_new_plugin_test") - print(f"\nBuilding e2e test env at: {tmp_path.as_posix()}") - - # Clone django-simple-deploy in temp env. - path_dsd = tmp_path / "django-simple-deploy" - cmd = f"git clone https://github.com/django-simple-deploy/django-simple-deploy.git {path_dsd.as_posix()} --depth 1" - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) - - # Build a venv in the django-simple-deploy temp dir. - venv_dir = path_dsd / ".venv" - cmd = f"uv venv {venv_dir}" - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) - - # Make an editable install of django-simple-deploy in its environment. - path_to_python = venv_dir / "bin" / "python" - cmd = f'uv pip install --python {path_to_python} -e "{path_dsd.as_posix()}[dev]"' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) - - if not cli_options.setup_plugins_only: - # Run core tests without a plugin installed. - e2e_utils.run_dsd_core_tests(path_dsd, path_to_python, cli_options) - - return tmp_path, path_to_python, path_dsd - -@pytest.fixture(scope="function", autouse=True) -def clear_plugins(get_dev_env): - """Remove any plugins from dev env. - - Most tests install a plugin to the dev env. Remove each plugin after its test runs. - """ - dev_env_dir, path_to_python, path_dsd = get_dev_env - - # Yield to let test function run, then clear any plugins that were installed. - yield - - cmd = f"uv pip list --python {path_to_python} --format=json" - cmd_parts = shlex.split(cmd) - package_dicts_str = subprocess.run(cmd_parts, capture_output=True).stdout - package_dicts = json.loads(package_dicts_str) - package_names = [pd["name"] for pd in package_dicts if pd["name"].startswith("dsd-")] - - for pkg_name in package_names: - cmd = f"uv pip uninstall {pkg_name} --python {path_to_python}" - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) +# # --- Fixtures --- + +# @pytest.fixture(scope="module") +# def get_dev_env(tmp_path_factory, cli_options): +# """Set up an env where plugins can be generated and tested within django-simple-deploy. + +# - Set up a temp dir. +# - Install dev env for django-simple-deploy. +# - We can generate plugins, with this temp dir as target location. +# - Then install newly-generated plugins to dsd dev env, and run tests from dsd. +# """ +# # Make the temp directory for the dsd development env. +# tmp_path = tmp_path_factory.mktemp("e2e_new_plugin_test") +# print(f"\nBuilding e2e test env at: {tmp_path.as_posix()}") + +# # Clone django-simple-deploy in temp env. +# path_dsd = tmp_path / "django-simple-deploy" +# cmd = f"git clone https://github.com/django-simple-deploy/django-simple-deploy.git {path_dsd.as_posix()} --depth 1" +# cmd_parts = shlex.split(cmd) +# subprocess.run(cmd_parts) + +# # Build a venv in the django-simple-deploy temp dir. +# venv_dir = path_dsd / ".venv" +# cmd = f"uv venv {venv_dir}" +# cmd_parts = shlex.split(cmd) +# subprocess.run(cmd_parts) + +# # Make an editable install of django-simple-deploy in its environment. +# path_to_python = venv_dir / "bin" / "python" +# cmd = f'uv pip install --python {path_to_python} -e "{path_dsd.as_posix()}[dev]"' +# cmd_parts = shlex.split(cmd) +# subprocess.run(cmd_parts) + +# if not cli_options.setup_plugins_only: +# # Run core tests without a plugin installed. +# e2e_utils.run_dsd_core_tests(path_dsd, path_to_python, cli_options) + +# return tmp_path, path_to_python, path_dsd + +# @pytest.fixture(scope="function", autouse=True) +# def clear_plugins(get_dev_env): +# """Remove any plugins from dev env. + +# Most tests install a plugin to the dev env. Remove each plugin after its test runs. +# """ +# dev_env_dir, path_to_python, path_dsd = get_dev_env + +# # Yield to let test function run, then clear any plugins that were installed. +# yield + +# cmd = f"uv pip list --python {path_to_python} --format=json" +# cmd_parts = shlex.split(cmd) +# package_dicts_str = subprocess.run(cmd_parts, capture_output=True).stdout +# package_dicts = json.loads(package_dicts_str) +# package_names = [pd["name"] for pd in package_dicts if pd["name"].startswith("dsd-")] + +# for pkg_name in package_names: +# cmd = f"uv pip uninstall {pkg_name} --python {path_to_python}" +# cmd_parts = shlex.split(cmd) +# subprocess.run(cmd_parts) def test_simple_plugin(get_dev_env, cli_options): From 78784374d5396403e053b941029166ed66ddaa6e Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 16:47:59 -0400 Subject: [PATCH 19/23] Remove unused code. --- tests/e2e_tests/test_basic_plugin.py | 64 ---------------------------- 1 file changed, 64 deletions(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index 664a00d..a1a35b4 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -15,8 +15,6 @@ """ from argparse import Namespace -import json -from pathlib import Path import subprocess import shlex @@ -33,68 +31,6 @@ ) -# # --- Fixtures --- - -# @pytest.fixture(scope="module") -# def get_dev_env(tmp_path_factory, cli_options): -# """Set up an env where plugins can be generated and tested within django-simple-deploy. - -# - Set up a temp dir. -# - Install dev env for django-simple-deploy. -# - We can generate plugins, with this temp dir as target location. -# - Then install newly-generated plugins to dsd dev env, and run tests from dsd. -# """ -# # Make the temp directory for the dsd development env. -# tmp_path = tmp_path_factory.mktemp("e2e_new_plugin_test") -# print(f"\nBuilding e2e test env at: {tmp_path.as_posix()}") - -# # Clone django-simple-deploy in temp env. -# path_dsd = tmp_path / "django-simple-deploy" -# cmd = f"git clone https://github.com/django-simple-deploy/django-simple-deploy.git {path_dsd.as_posix()} --depth 1" -# cmd_parts = shlex.split(cmd) -# subprocess.run(cmd_parts) - -# # Build a venv in the django-simple-deploy temp dir. -# venv_dir = path_dsd / ".venv" -# cmd = f"uv venv {venv_dir}" -# cmd_parts = shlex.split(cmd) -# subprocess.run(cmd_parts) - -# # Make an editable install of django-simple-deploy in its environment. -# path_to_python = venv_dir / "bin" / "python" -# cmd = f'uv pip install --python {path_to_python} -e "{path_dsd.as_posix()}[dev]"' -# cmd_parts = shlex.split(cmd) -# subprocess.run(cmd_parts) - -# if not cli_options.setup_plugins_only: -# # Run core tests without a plugin installed. -# e2e_utils.run_dsd_core_tests(path_dsd, path_to_python, cli_options) - -# return tmp_path, path_to_python, path_dsd - -# @pytest.fixture(scope="function", autouse=True) -# def clear_plugins(get_dev_env): -# """Remove any plugins from dev env. - -# Most tests install a plugin to the dev env. Remove each plugin after its test runs. -# """ -# dev_env_dir, path_to_python, path_dsd = get_dev_env - -# # Yield to let test function run, then clear any plugins that were installed. -# yield - -# cmd = f"uv pip list --python {path_to_python} --format=json" -# cmd_parts = shlex.split(cmd) -# package_dicts_str = subprocess.run(cmd_parts, capture_output=True).stdout -# package_dicts = json.loads(package_dicts_str) -# package_names = [pd["name"] for pd in package_dicts if pd["name"].startswith("dsd-")] - -# for pkg_name in package_names: -# cmd = f"uv pip uninstall {pkg_name} --python {path_to_python}" -# cmd_parts = shlex.split(cmd) -# subprocess.run(cmd_parts) - - def test_simple_plugin(get_dev_env, cli_options): """Test a simple plugin config.""" dev_env_dir, path_to_python, path_dsd = get_dev_env From 33ad5b29fa1db1a4fdc5bdfa7f009da1bddeda93 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 16:54:19 -0400 Subject: [PATCH 20/23] Helper function to generate new plugin. --- tests/e2e_tests/test_basic_plugin.py | 39 +++++++++++++++++++++------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index a1a35b4..28aa3ae 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -31,6 +31,25 @@ ) +def _generate_plugin(dev_env, plugin_config): + """Generate a new plugin, and install it to the django-simple-deploy dev env. + """ + dev_env_dir, path_to_python, path_dsd = dev_env + + args = Namespace(target_dir=dev_env_dir) + gp.generate_plugin(plugin_config, args) + + # Make sure we have the correct path to the new plugin. + path_new_plugin = dev_env_dir / "dsd-newfly" + assert path_new_plugin.exists() + + # Install plugin editable to django-simple-deploy env. + cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) + + + def test_simple_plugin(get_dev_env, cli_options): """Test a simple plugin config.""" dev_env_dir, path_to_python, path_dsd = get_dev_env @@ -42,17 +61,19 @@ def test_simple_plugin(get_dev_env, cli_options): license_name = "eric", ) - args = Namespace(target_dir=dev_env_dir) - gp.generate_plugin(plugin_config, args) + _generate_plugin(get_dev_env, plugin_config) - # Make sure we have the correct path to the new plugin. - path_new_plugin = dev_env_dir / "dsd-newfly" - assert path_new_plugin.exists() + # args = Namespace(target_dir=dev_env_dir) + # gp.generate_plugin(plugin_config, args) - # Install plugin editable to django-simple-deploy env. - cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) + # # Make sure we have the correct path to the new plugin. + # path_new_plugin = dev_env_dir / "dsd-newfly" + # assert path_new_plugin.exists() + + # # Install plugin editable to django-simple-deploy env. + # cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' + # cmd_parts = shlex.split(cmd) + # subprocess.run(cmd_parts) if not cli_options.setup_plugins_only: e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) From 2d0a962f7ba3ee632dd962a82d4e1335bfd8fb22 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 17:12:39 -0400 Subject: [PATCH 21/23] Clean up imports, unused code, and move generate_plugin() to utils. --- tests/e2e_tests/conftest.py | 10 +--- tests/e2e_tests/test_basic_plugin.py | 74 ++-------------------------- tests/e2e_tests/utils/e2e_utils.py | 23 +++++++++ 3 files changed, 27 insertions(+), 80 deletions(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index df0d1ee..ed8a8ab 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -1,21 +1,13 @@ """Configuration for e2e test runs.""" -# from argparse import Namespace import json -# from pathlib import Path import subprocess import shlex +from dataclasses import dataclass import pytest -# from utils.plugin_config import PluginConfig from tests.e2e_tests.utils import e2e_utils -# import generate_plugin as gp - - -from dataclasses import dataclass - -import pytest # --- Custom CLI args --- diff --git a/tests/e2e_tests/test_basic_plugin.py b/tests/e2e_tests/test_basic_plugin.py index 28aa3ae..e50b7a3 100644 --- a/tests/e2e_tests/test_basic_plugin.py +++ b/tests/e2e_tests/test_basic_plugin.py @@ -22,7 +22,6 @@ from utils.plugin_config import PluginConfig from tests.e2e_tests.utils import e2e_utils -import generate_plugin as gp # Skip these tests if uv is not available. @@ -31,25 +30,6 @@ ) -def _generate_plugin(dev_env, plugin_config): - """Generate a new plugin, and install it to the django-simple-deploy dev env. - """ - dev_env_dir, path_to_python, path_dsd = dev_env - - args = Namespace(target_dir=dev_env_dir) - gp.generate_plugin(plugin_config, args) - - # Make sure we have the correct path to the new plugin. - path_new_plugin = dev_env_dir / "dsd-newfly" - assert path_new_plugin.exists() - - # Install plugin editable to django-simple-deploy env. - cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) - - - def test_simple_plugin(get_dev_env, cli_options): """Test a simple plugin config.""" dev_env_dir, path_to_python, path_dsd = get_dev_env @@ -60,53 +40,22 @@ def test_simple_plugin(get_dev_env, cli_options): support_automate_all = True, license_name = "eric", ) - - _generate_plugin(get_dev_env, plugin_config) - - # args = Namespace(target_dir=dev_env_dir) - # gp.generate_plugin(plugin_config, args) - - # # Make sure we have the correct path to the new plugin. - # path_new_plugin = dev_env_dir / "dsd-newfly" - # assert path_new_plugin.exists() - - # # Install plugin editable to django-simple-deploy env. - # cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' - # cmd_parts = shlex.split(cmd) - # subprocess.run(cmd_parts) + e2e_utils.generate_plugin(get_dev_env, plugin_config) if not cli_options.setup_plugins_only: e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) - def test_plugin_single_space_platform_name(get_dev_env, cli_options): """Test a plugin for a platform with a space in the name.""" dev_env_dir, path_to_python, path_dsd = get_dev_env - # Flag to temporarily disable running dsd and plugin tests. This is helpful when - # examining this environment. Otherwise pytest runs so many tests, this one can't - # easily be found. This is not a CLI arg, because it only needs to be modified when you're - # working on this test, not when you're just running it. - run_core_plugin_tests = True - plugin_config = PluginConfig( platform_name = "New Platform", pkg_name = "dsd-newplatform", support_automate_all = True, license_name = "eric", ) - - args = Namespace(target_dir=dev_env_dir) - gp.generate_plugin(plugin_config, args) - - # Make sure we have the correct path to the new plugin. - path_new_plugin = dev_env_dir / "dsd-newplatform" - assert path_new_plugin.exists() - - # Install plugin editable to django-simple-deploy env. - cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) + e2e_utils.generate_plugin(get_dev_env, plugin_config) if not cli_options.setup_plugins_only: e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) @@ -115,30 +64,13 @@ def test_plugin_different_plugin_platform_name(get_dev_env, cli_options): """Test a plugin where platform and plugin names differ significantly.""" dev_env_dir, path_to_python, path_dsd = get_dev_env - # Flag to temporarily disable running dsd and plugin tests. This is helpful when - # examining this environment. Otherwise pytest runs so many tests, this one can't - # easily be found. This is not a CLI arg, because it only needs to be modified when you're - # working on this test, not when you're just running it. - run_core_plugin_tests = True - plugin_config = PluginConfig( platform_name = "MyNewPlatform", pkg_name = "dsd-my-plugin", support_automate_all = True, license_name = "eric", ) - - args = Namespace(target_dir=dev_env_dir) - gp.generate_plugin(plugin_config, args) - - # Make sure we have the correct path to the new plugin. - path_new_plugin = dev_env_dir / "dsd-my-plugin" - assert path_new_plugin.exists() - - # Install plugin editable to django-simple-deploy env. - cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' - cmd_parts = shlex.split(cmd) - subprocess.run(cmd_parts) + e2e_utils.generate_plugin(get_dev_env, plugin_config) if not cli_options.setup_plugins_only: e2e_utils.run_core_plugin_tests(path_dsd, plugin_config, cli_options) \ No newline at end of file diff --git a/tests/e2e_tests/utils/e2e_utils.py b/tests/e2e_tests/utils/e2e_utils.py index 100666b..5868a05 100644 --- a/tests/e2e_tests/utils/e2e_utils.py +++ b/tests/e2e_tests/utils/e2e_utils.py @@ -1,9 +1,15 @@ """Utility functions for e2e tests.""" + +from argparse import Namespace import shlex import subprocess import re +import pytest + +import generate_plugin as gp +from utils.plugin_config import PluginConfig from utils.generator_utils import get_platform_name_lower @@ -18,6 +24,23 @@ def uv_available(): # This is the exception raised on macOS when the command uv is unavailable. return False +def generate_plugin(dev_env, plugin_config): + """Generate a new plugin, and install it to the django-simple-deploy dev env. + """ + dev_env_dir, path_to_python, path_dsd = dev_env + + args = Namespace(target_dir=dev_env_dir) + gp.generate_plugin(plugin_config, args) + + # Make sure we have the correct path to the new plugin. + path_new_plugin = dev_env_dir / plugin_config.pkg_name + assert path_new_plugin.exists() + + # Install plugin editable to django-simple-deploy env. + cmd = f'uv pip install --python {path_to_python} -e "{path_new_plugin.as_posix()}[dev]"' + cmd_parts = shlex.split(cmd) + subprocess.run(cmd_parts) + def run_dsd_core_tests(path_dsd, path_to_python, cli_options): """Run django-simple-deploy's test suite with no plugin installed.""" From f0baa16d58b66db9fac6164ee16b2b6b764eb55b Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 21:11:18 -0400 Subject: [PATCH 22/23] Remove unused code. --- utils/generator_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/generator_utils.py b/utils/generator_utils.py index 07a5d89..65e2f46 100644 --- a/utils/generator_utils.py +++ b/utils/generator_utils.py @@ -90,7 +90,6 @@ def validate_target_dir(args, plugin_config, path_root): sys.exit(msg) else: # Get permission to write to target directory. - # path_root = Path(__file__).parent path_root_new = path_root.parent / plugin_config.pkg_name if path_root_new.exists(): From 2fb449ceb12fe2dc406d8bdf5b40554dfa284581 Mon Sep 17 00:00:00 2001 From: Eric Matthes Date: Wed, 17 Sep 2025 21:15:10 -0400 Subject: [PATCH 23/23] Move functions to helper functions in utils. --- tests/e2e_tests/utils/e2e_utils.py | 4 ++-- tests/unit_tests/test_generator_utils.py | 4 ++-- utils/generator_utils.py | 15 +++++++-------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/e2e_tests/utils/e2e_utils.py b/tests/e2e_tests/utils/e2e_utils.py index 5868a05..9bad2c5 100644 --- a/tests/e2e_tests/utils/e2e_utils.py +++ b/tests/e2e_tests/utils/e2e_utils.py @@ -10,7 +10,7 @@ import generate_plugin as gp from utils.plugin_config import PluginConfig -from utils.generator_utils import get_platform_name_lower +from utils.generator_utils import _get_platform_name_lower def uv_available(): @@ -56,7 +56,7 @@ def run_dsd_core_tests(path_dsd, path_to_python, cli_options): def run_core_plugin_tests(path_dsd, plugin_config, cli_options): """Run django-simple-deploy's test suite with a plugin installed.""" tests_dir = path_dsd / "tests" - platform_name_lower = get_platform_name_lower(plugin_config.platform_name) + platform_name_lower = _get_platform_name_lower(plugin_config.platform_name) cmd = get_core_plugin_test_cmd(path_dsd, cli_options, platform_name_lower) output = subprocess.run(cmd, capture_output=True,shell=True) diff --git a/tests/unit_tests/test_generator_utils.py b/tests/unit_tests/test_generator_utils.py index a4aad04..e35475b 100644 --- a/tests/unit_tests/test_generator_utils.py +++ b/tests/unit_tests/test_generator_utils.py @@ -4,7 +4,7 @@ def test_get_platform_name_lower(): name = "NewFly" - assert gu.get_platform_name_lower(name) == "newfly" + assert gu._get_platform_name_lower(name) == "newfly" name = "New Fly" - assert gu.get_platform_name_lower(name) == "newfly" \ No newline at end of file + assert gu._get_platform_name_lower(name) == "newfly" \ No newline at end of file diff --git a/utils/generator_utils.py b/utils/generator_utils.py index 65e2f46..c872165 100644 --- a/utils/generator_utils.py +++ b/utils/generator_utils.py @@ -119,8 +119,8 @@ def build_new_plugin(args, plugin_config): # Make sure it's okay to write to the target directory. path_root_new = validate_target_dir(args, plugin_config, path_root) - platform_name_lower = get_platform_name_lower(plugin_config.platform_name) - main_dir_name = get_main_dir_name(plugin_config.pkg_name) + platform_name_lower = _get_platform_name_lower(plugin_config.platform_name) + main_dir_name = _get_main_dir_name(plugin_config.pkg_name) replacements = _get_replacements(plugin_config, platform_name_lower) @@ -237,18 +237,18 @@ def show_summary(): msg += "\nshould pass." print(msg) -def get_platform_name_lower(platform_name): + +# --- Helper functions --- + +def _get_platform_name_lower(platform_name): """Return a lowercase version of the platform name.""" return platform_name.lower().replace("-", "").replace("_", "").replace(".", "").replace(" ", "") -def get_main_dir_name(pkg_name): +def _get_main_dir_name(pkg_name): """Return name of main dir. For dsd-new-platform, that's dsd_new_platform.""" pkg_name_lower = pkg_name.lower().replace("-", "_") return pkg_name_lower - -# --- Helper functions --- - def _get_replacements(plugin_config, platform_name_lower): """Get substitions for...""" replacements = { @@ -261,4 +261,3 @@ def _get_replacements(plugin_config, platform_name_lower): } return replacements -