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/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": { 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..." 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 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..3c98302f4 --- /dev/null +++ b/examples/uninstall_with_conda_exe/pre-uninstall.bat @@ -0,0 +1,14 @@ +IF "%UNINSTALLER_REMOVE_USER_DATA%" == "1" ( + DEL "%USER_FILES%\data" +) +IF "%UNINSTALLER_REMOVE_CACHES%" == "1" ( + 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/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 + +* 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)