From 0175e69cf1bb15bab036db28f69e41347eacc91f Mon Sep 17 00:00:00 2001 From: Marco Esters Date: Fri, 3 Apr 2026 14:42:49 -0700 Subject: [PATCH 1/6] Set environment variables for uninstallation options --- constructor/nsis/main.nsi.tmpl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/constructor/nsis/main.nsi.tmpl b/constructor/nsis/main.nsi.tmpl index 83f062edd..4d6bdbf97 100644 --- a/constructor/nsis/main.nsi.tmpl +++ b/constructor/nsis/main.nsi.tmpl @@ -1842,19 +1842,24 @@ Section "Uninstall" ${If} $UninstRemoveConfigFiles_User_State == ${BST_CHECKED} ${If} $UninstRemoveConfigFiles_System_State == ${BST_CHECKED} StrCpy $R0 "$R0 --remove-config-files=all" + System::Call 'kernel32::SetEnvironmentVariable(t,t)i("UNINSTALLER_REMOVE_CONFIG_FILES", "all")' ${Else} StrCpy $R0 "$R0 --remove-config-files=user" + System::Call 'kernel32::SetEnvironmentVariable(t,t)i("UNINSTALLER_REMOVE_CONFIG_FILES", "user")' ${EndIf} ${ElseIf} $UninstRemoveConfigFiles_System_State == ${BST_CHECKED} StrCpy $R0 "$R0 --remove-config-files=system" + System::Call 'kernel32::SetEnvironmentVariable(t,t)i("UNINSTALLER_REMOVE_CONFIG_FILES", "system")' ${EndIf} ${If} $UninstRemoveUserData_State == ${BST_CHECKED} - StrCpy $R0 "$R0 --remove-user-data" + StrCpy $R0 "$R0 --remove-user-data" + System::Call 'kernel32::SetEnvironmentVariable(t,t)i("UNINSTALLER_REMOVE_USER_DATA", "1")' ${EndIf} ${If} $UninstRemoveCaches_State == ${BST_CHECKED} - StrCpy $R0 "$R0 --remove-caches" + StrCpy $R0 "$R0 --remove-caches" + System::Call 'kernel32::SetEnvironmentVariable(t,t)i("UNINSTALLER_REMOVE_CACHES", "1")' ${EndIf} ${Print} "Removing files and folders..." From 9a523ad6af3dd226019ff981a2c32420348e9fc9 Mon Sep 17 00:00:00 2001 From: Marco Esters Date: Fri, 3 Apr 2026 14:43:52 -0700 Subject: [PATCH 2/6] Add tests --- .../uninstall_with_conda_exe/construct.yaml | 19 ++++++++++++++++ .../pre-uninstall.bat | 14 ++++++++++++ tests/test_examples.py | 22 ++++++++++++++++--- 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 examples/uninstall_with_conda_exe/construct.yaml create mode 100644 examples/uninstall_with_conda_exe/pre-uninstall.bat diff --git a/examples/uninstall_with_conda_exe/construct.yaml b/examples/uninstall_with_conda_exe/construct.yaml new file mode 100644 index 000000000..b4af8e858 --- /dev/null +++ b/examples/uninstall_with_conda_exe/construct.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=../../constructor/data/construct.schema.json +"$schema": "../../constructor/data/construct.schema.json" + +name: UninstallWithCondaExe +version: 1.0.0 +installer_type: all + +channels: + - https://repo.anaconda.com/pkgs/main/ + +specs: + - python + - conda + +register_python: False +initialize_conda: False +initialize_by_default: false +uninstall_with_conda_exe: true +pre_uninstall: pre-uninstall.bat diff --git a/examples/uninstall_with_conda_exe/pre-uninstall.bat b/examples/uninstall_with_conda_exe/pre-uninstall.bat new file mode 100644 index 000000000..5443cc160 --- /dev/null +++ b/examples/uninstall_with_conda_exe/pre-uninstall.bat @@ -0,0 +1,14 @@ +IF DEFINED UNINSTALLER_REMOVE_USER_DATA ( + DEL "%USER_FILES%\data" +) +IF DEFINED UNINSTALLER_REMOVE_CACHES ( + DEL "%USER_FILES%\cache" +) +IF DEFINED UNINSTALLER_REMOVE_CONFIG_FILES ( + IF "%UNINSTALLER_REMOVE_CONFIG_FILES%" NEQ "user" ( + DEL "%USER_FILES%\config_system" + ) + IF "%UNINSTALLER_REMOVE_CONFIG_FILES%" NEQ "system" ( + DEL "%USER_FILES%\config_user" + ) +) diff --git a/tests/test_examples.py b/tests/test_examples.py index e2c09b166..656479614 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1347,11 +1347,23 @@ def test_uninstallation_standalone( remove_config_files: str | None, tmp_path: Path, ): - recipe_path = _example_path("customize_controls") + yaml = YAML() + recipe_path = _example_path("uninstall_with_conda_exe") input_path = tmp_path / "input" shutil.copytree(str(recipe_path), str(input_path)) - with open(input_path / "construct.yaml", "a") as construct: - construct.write("uninstall_with_conda_exe: true\n") + + # Create extra files to delete in pre-uninstall script + user_files_dir = tmp_path / "user_files" + for file in ("cache", "data", "config_user", "config_system"): + (user_files_dir / file).touch() + + construct_yaml_file = input_path / "construct.yaml" + with construct_yaml_file.open() as file: + construct_yaml = yaml.load(file) + construct_yaml["script_env_variables"] = {"USER_FILES": str(user_files_dir)} + with construct_yaml_file.open(mode="w") as file: + yaml.dump(construct_yaml, file) + installer, install_dir = next(create_installer(input_path, tmp_path)) monkeypatch.setenv("USERPROFILE", str(tmp_path)) _run_installer( @@ -1398,6 +1410,10 @@ def test_uninstallation_standalone( assert pkg_cache.exists() != remove_caches assert system_rc.exists() != remove_system_rcs assert user_rc.exists() != remove_user_rcs + assert (user_files_dir / "data").exists() != remove_user_data + assert (user_files_dir / "cache").exists() != remove_caches + assert (user_files_dir / "config_system").exists() != remove_system_rcs + assert (user_files_dir / "config_user").exists() != remove_user_rcs finally: if system_rc.parent.exists(): shutil.rmtree(system_rc.parent) From 4ee8aa8b40c68dea2f6a13c2c1446cf24ee255e3 Mon Sep 17 00:00:00 2001 From: Marco Esters Date: Fri, 3 Apr 2026 14:47:14 -0700 Subject: [PATCH 3/6] Add news --- news/1197-expose-uninstallation-variables | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 news/1197-expose-uninstallation-variables diff --git a/news/1197-expose-uninstallation-variables b/news/1197-expose-uninstallation-variables new file mode 100644 index 000000000..64c8b5e2b --- /dev/null +++ b/news/1197-expose-uninstallation-variables @@ -0,0 +1,19 @@ +### Enhancements + +* EXE: Expose uninstallation options as environment variables for pre-uninstallation scripts. (#1197) + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +* From ab61a85cb786f37f1fab63fe117d5d0c9d160c80 Mon Sep 17 00:00:00 2001 From: Marco Esters Date: Fri, 3 Apr 2026 15:39:03 -0700 Subject: [PATCH 4/6] Compare with 1 instead of looking for undefined --- examples/uninstall_with_conda_exe/pre-uninstall.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/uninstall_with_conda_exe/pre-uninstall.bat b/examples/uninstall_with_conda_exe/pre-uninstall.bat index 5443cc160..3c98302f4 100644 --- a/examples/uninstall_with_conda_exe/pre-uninstall.bat +++ b/examples/uninstall_with_conda_exe/pre-uninstall.bat @@ -1,7 +1,7 @@ -IF DEFINED UNINSTALLER_REMOVE_USER_DATA ( +IF "%UNINSTALLER_REMOVE_USER_DATA%" == "1" ( DEL "%USER_FILES%\data" ) -IF DEFINED UNINSTALLER_REMOVE_CACHES ( +IF "%UNINSTALLER_REMOVE_CACHES%" == "1" ( DEL "%USER_FILES%\cache" ) IF DEFINED UNINSTALLER_REMOVE_CONFIG_FILES ( From 6ea713fff26f076998fe51ab629c635c4124ffbc Mon Sep 17 00:00:00 2001 From: Marco Esters Date: Mon, 6 Apr 2026 07:35:04 -0700 Subject: [PATCH 5/6] Add environment variables to documentation --- CONSTRUCT.md | 5 +++++ constructor/_schema.py | 5 +++++ docs/source/construct-yaml.md | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/CONSTRUCT.md b/CONSTRUCT.md index 5d9fb159c..17a71302f 100644 --- a/CONSTRUCT.md +++ b/CONSTRUCT.md @@ -407,6 +407,11 @@ Metadata about the installer can be found in the `%INSTALLER_NAME%`, `%INSTALLER_VER%`, `%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`. +If the uninstallation is performed with `conda-standalone`, the following +environment variables are available: `%UNINSTALLER_REMOVE_CONFIG_FILES%` (set to +`system`, `user`, or `all` if selected), `%UNINSTALLER_REMOVE_USER_DATA%` (set to `1` +if set), and `%UNINSTALLER_REMOVE_CACHES%` (set to `1` if set). + ### `default_prefix` Set default install prefix. On Linux, if not provided, the default prefix diff --git a/constructor/_schema.py b/constructor/_schema.py index 4d61bb65c..87945ad03 100644 --- a/constructor/_schema.py +++ b/constructor/_schema.py @@ -575,6 +575,11 @@ class ConstructorConfiguration(BaseModel): Metadata about the installer can be found in the `%INSTALLER_NAME%`, `%INSTALLER_VER%`, `%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`. + + If the uninstallation is performed with `conda-standalone`, the following + environment variables are available: `%UNINSTALLER_REMOVE_CONFIG_FILES%` (set to + `system`, `user`, or `all` if selected), `%UNINSTALLER_REMOVE_USER_DATA%` (set to `1` + if set), and `%UNINSTALLER_REMOVE_CACHES%` (set to `1` if set). """ default_prefix: NonEmptyStr | None = None """ diff --git a/docs/source/construct-yaml.md b/docs/source/construct-yaml.md index 5d9fb159c..17a71302f 100644 --- a/docs/source/construct-yaml.md +++ b/docs/source/construct-yaml.md @@ -407,6 +407,11 @@ Metadata about the installer can be found in the `%INSTALLER_NAME%`, `%INSTALLER_VER%`, `%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`. +If the uninstallation is performed with `conda-standalone`, the following +environment variables are available: `%UNINSTALLER_REMOVE_CONFIG_FILES%` (set to +`system`, `user`, or `all` if selected), `%UNINSTALLER_REMOVE_USER_DATA%` (set to `1` +if set), and `%UNINSTALLER_REMOVE_CACHES%` (set to `1` if set). + ### `default_prefix` Set default install prefix. On Linux, if not provided, the default prefix From 927a676d592492a6ca93b2faea222d80219913a7 Mon Sep 17 00:00:00 2001 From: Marco Esters Date: Mon, 6 Apr 2026 08:28:29 -0700 Subject: [PATCH 6/6] Update schema.json --- constructor/data/construct.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constructor/data/construct.schema.json b/constructor/data/construct.schema.json index 4811ccb8a..f0178d738 100644 --- a/constructor/data/construct.schema.json +++ b/constructor/data/construct.schema.json @@ -1072,7 +1072,7 @@ } ], "default": null, - "description": "Path to a pre uninstall script. This is only supported on Windows, and must be a `.bat` file. Installation path is available as `%PREFIX%`. Metadata about the installer can be found in the `%INSTALLER_NAME%`, `%INSTALLER_VER%`, `%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`.", + "description": "Path to a pre uninstall script. This is only supported on Windows, and must be a `.bat` file. Installation path is available as `%PREFIX%`. Metadata about the installer can be found in the `%INSTALLER_NAME%`, `%INSTALLER_VER%`, `%INSTALLER_PLAT%` environment variables. `%INSTALLER_TYPE%` is set to `EXE`.\nIf the uninstallation is performed with `conda-standalone`, the following environment variables are available: `%UNINSTALLER_REMOVE_CONFIG_FILES%` (set to `system`, `user`, or `all` if selected), `%UNINSTALLER_REMOVE_USER_DATA%` (set to `1` if set), and `%UNINSTALLER_REMOVE_CACHES%` (set to `1` if set).", "title": "Pre Uninstall" }, "progress_notifications": {