Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add front-end implementation for conda-standalone uninstall subcommand #897

Merged
merged 26 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b38e95c
Add option to use standalone binary for uninstallation
marcoesters Oct 11, 2024
c323060
Add conda-standalone uninstall to NSIS template
marcoesters Oct 11, 2024
21fc398
Use AbortRetryNSExecWait to call conda-standalone uninstaller
marcoesters Oct 17, 2024
292e2ba
Add uninstallation options panel
marcoesters Oct 29, 2024
d5e11b5
Improve typing for uninstall command templating
marcoesters Oct 29, 2024
67d3620
Implement conda-standalone uninstall command into Windows uninstaller…
marcoesters Oct 30, 2024
00b1608
Add standalone uninstaller options panel
marcoesters Nov 1, 2024
e102de6
Merge branch 'main' of github.com:conda/constructor into uninstall-st…
marcoesters Nov 11, 2024
a5b3ac3
Update documentation
marcoesters Nov 11, 2024
9ecf634
Make /RemoceCondaRcs CLI parsing logic more concise
marcoesters Nov 12, 2024
4209d7b
Add tests
marcoesters Nov 12, 2024
f6e5d83
Add news file
marcoesters Nov 13, 2024
7efbc74
Replace pipe operator with Union
marcoesters Nov 13, 2024
d1f221d
Ensure that ON_CI=False for CI="0"
marcoesters Nov 18, 2024
19dc456
Update uninstallation commands
marcoesters Nov 25, 2024
014f69f
Update CLI options documentation
marcoesters Nov 25, 2024
d851c57
Update uninstaller documentation
marcoesters Nov 25, 2024
837a741
Update description of uninstallation options in the GUI
marcoesters Nov 25, 2024
af2fcad
Merge branch 'main' of github.com:conda/constructor into uninstall-st…
marcoesters Nov 25, 2024
e07a541
Replace UNINSTALL_MENUS with UNINSTALL_COMMANDS
marcoesters Nov 25, 2024
23f913a
Fix jinja syntax
marcoesters Nov 25, 2024
a663b24
Document uninstaller subcommand for Unix
marcoesters Dec 2, 2024
4ed7cb0
Merge branch 'main' into uninstall-standalone
jaimergp Dec 5, 2024
d3ed416
Debug: print directory contents for standalone uninstaller
marcoesters Dec 5, 2024
c8ee571
Merge branch 'uninstall-standalone' of github.com:marcoesters/constru…
marcoesters Dec 5, 2024
152abb2
Always remove installation directory
marcoesters Dec 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CONSTRUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,14 @@ Allowed keys are:
license text. Only relevant if include_text is True. Any str accepted by open()'s 'errors'
argument is valid. See https://docs.python.org/3/library/functions.html#open.

### `uninstall_with_conda_exe`

_required:_ no<br/>
_type:_ boolean<br/>

Use the standalone binary to perform the uninstallation.
Requires conda-standalone 24.11.0 or newer.


## Available selectors
- `aarch64`
Expand Down
5 changes: 5 additions & 0 deletions constructor/construct.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,11 @@
- `text_errors` (optional str, default=`None`): How to handle decoding errors when reading the
license text. Only relevant if include_text is True. Any str accepted by open()'s 'errors'
argument is valid. See https://docs.python.org/3/library/functions.html#open.
'''),

('uninstall_with_conda_exe', False, bool, '''
Use the standalone binary to perform the uninstallation.
Requires conda-standalone 24.11.0 or newer.
'''),
]

Expand Down
10 changes: 9 additions & 1 deletion constructor/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ def main_build(dir_path, output_dir='.', platform=cc_platform,
# TODO: Investigate errors on Windows and re-enable
sys.exit("Error: micromamba is not supported on Windows installers.")

if (
info.get("uninstall_with_conda_exe")
and not (
exe_type == StandaloneExe.CONDA and exe_version and exe_version >= Version("24.11.0")
)
):
sys.exit("Error: uninstalling with conda.exe requires conda-standalone 24.11.0 or newer.")

logger.debug('conda packages download: %s', info['_download_dir'])

for key in ('welcome_image_text', 'header_image_text'):
Expand Down Expand Up @@ -184,7 +192,7 @@ def main_build(dir_path, output_dir='.', platform=cc_platform,
"Will assume it is compatible with shortcuts."
)
elif sys.platform != "win32" and (
exe_type != StandaloneExe.CONDA or exe_version < Version("23.11.0")
exe_type != StandaloneExe.CONDA or (exe_version and exe_version < Version("23.11.0"))
):
logger.warning("conda-standalone 23.11.0 or above is required for shortcuts on Unix.")
info['_enable_shortcuts'] = "incompatible"
Expand Down
108 changes: 108 additions & 0 deletions constructor/nsis/StandaloneUninstallerOptions.nsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
var UninstCustomOptions
var UninstCustomOptions.RemoveConfigFiles_User
var UninstCustomOptions.RemoveConfigFiles_System
var UninstCustomOptions.RemoveUserData
var UninstCustomOptions.RemoveCaches

# These are the checkbox states, to be used by the uninstaller
var UninstRemoveConfigFiles_User_State
var UninstRemoveConfigFiles_System_State
var UninstRemoveUserData_State
var UninstRemoveCaches_State

Function un.UninstCustomOptions_InitDefaults
StrCpy $UninstRemoveConfigFiles_User_State ${BST_UNCHECKED}
StrCpy $UninstRemoveConfigFiles_System_State ${BST_UNCHECKED}
StrCpy $UninstRemoveUserData_State ${BST_UNCHECKED}
StrCpy $UninstRemoveCaches_State ${BST_UNCHECKED}
FunctionEnd

Function un.UninstCustomOptions_Show
${If} $UninstRemoveCaches_State == ""
Abort
${EndIf}
# Create dialog
nsDialogs::Create 1018
Pop $UninstCustomOptions
${If} $UninstCustomOptions == error
Abort
${EndIf}

!insertmacro MUI_HEADER_TEXT \
"Advanced uninstallation options" \
"Remove configuration, data, and cache files"

# We will use $5 as the y axis accumulator, starting at 0
# We sum the the number of 'u' units added by 'NSD_Create*' functions
IntOp $5 0 + 0

# Option to remove configuration files
${NSD_CreateCheckbox} 0 "$5u" 100% 11u "Remove user configuration files."
IntOp $5 $5 + 11
Pop $UninstCustomOptions.RemoveConfigFiles_User
${NSD_SetState} $UninstCustomOptions.RemoveConfigFiles_User $UninstRemoveConfigFiles_User_State
${NSD_OnClick} $UninstCustomOptions.RemoveConfigFiles_User un.UninstRemoveConfigFiles_User_Onclick
${NSD_CreateLabel} 5% "$5u" 90% 10u \
"This removes configuration files such as .condarc files in the Users directory."
IntOp $5 $5 + 10

${If} ${UAC_IsAdmin}
${NSD_CreateCheckbox} 0 "$5u" 100% 11u "Remove system-wide configuration files."
IntOp $5 $5 + 11
Pop $UninstCustomOptions.RemoveConfigFiles_System
${NSD_SetState} $UninstCustomOptions.RemoveConfigFiles_System $UninstRemoveConfigFiles_System_State
${NSD_OnClick} $UninstCustomOptions.RemoveConfigFiles_System un.UninstRemoveConfigFiles_System_Onclick
${NSD_CreateLabel} 5% "$5u" 90% 10u \
"This removes configuration files such as .condarc files in the ProgramData directory."
IntOp $5 $5 + 10
${EndIf}

# Option to remove user data files
${NSD_CreateCheckbox} 0 "$5u" 100% 11u "Remove user data."
IntOp $5 $5 + 11
Pop $UninstCustomOptions.RemoveUserData
${NSD_SetState} $UninstCustomOptions.RemoveUserData $UninstRemoveUserData_State
${NSD_OnClick} $UninstCustomOptions.RemoveUserData un.UninstRemoveUserData_Onclick
${NSD_CreateLabel} 5% "$5u" 90% 10u \
"This removes user data files such as the .conda directory inside the Users folder."
IntOp $5 $5 + 10

# Option to remove caches
${NSD_CreateCheckbox} 0 "$5u" 100% 11u "Remove caches."
IntOp $5 $5 + 11
Pop $UninstCustomOptions.RemoveCaches
${NSD_SetState} $UninstCustomOptions.RemoveCaches $UninstRemoveCaches_State
${NSD_OnClick} $UninstCustomOptions.RemoveCaches un.UninstRemoveCaches_Onclick
${NSD_CreateLabel} 5% "$5u" 90% 10u \
"This removes cache directories such as package caches and notices."
IntOp $5 $5 + 20

IntOp $5 $5 + 5
${NSD_CreateLabel} 0 "$5u" 100% 10u \
"These options are not recommended if multiple conda installations exist on the same system."
IntOp $5 $5 + 10
Pop $R0
SetCtlColors $R0 ff0000 transparent

nsDialogs::Show
FunctionEnd

Function un.UninstRemoveConfigFiles_User_OnClick
Pop $0
${NSD_GetState} $0 $UninstRemoveConfigFiles_User_State
FunctionEnd

Function un.UninstRemoveConfigFiles_System_OnClick
Pop $0
${NSD_GetState} $0 $UninstRemoveConfigFiles_System_State
FunctionEnd

Function un.UninstRemoveUserData_OnClick
Pop $0
${NSD_GetState} $0 $UninstRemoveUserData_State
FunctionEnd

Function un.UninstRemoveCaches_OnClick
Pop $0
${NSD_GetState} $0 $UninstRemoveCaches_State
FunctionEnd
92 changes: 77 additions & 15 deletions constructor/nsis/main.nsi.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ var /global StdOutHandleSet

!include "Utils.nsh"

{%- if uninstall_with_conda_exe %}
!include "StandaloneUninstallerOptions.nsh"
{%- endif %}

!define NAME {{ installer_name }}
!define VERSION {{ installer_version }}
!define COMPANY {{ company }}
Expand Down Expand Up @@ -110,6 +114,11 @@ var /global ARGV_NoScripts
var /global ARGV_NoShortcuts
var /global ARGV_CheckPathLength
var /global ARGV_QuietMode
{%- if uninstall_with_conda_exe %}
var /global ARGV_Uninst_RemoveConfigFiles
var /global ARGV_Uninst_RemoveUserData
var /global ARGV_Uninst_RemoveCaches
{%- endif %}

var /global IsDomainUser
var /global CheckPathLength
Expand Down Expand Up @@ -195,14 +204,17 @@ Page Custom mui_AnaCustomOptions_Show
{%- if custom_conclusion %}
# Custom conclusion file(s)
{{ CUSTOM_CONCLUSION_FILE }}
#else
{%- else %}
!insertmacro MUI_PAGE_FINISH
{%- endif %}


!insertmacro MUI_UNPAGE_WELCOME
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.OnDirectoryLeave
!insertmacro MUI_UNPAGE_CONFIRM
{%- if uninstall_with_conda_exe %}
UninstPage Custom un.UninstCustomOptions_Show
{%- endif %}
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH

Expand Down Expand Up @@ -716,7 +728,7 @@ Function .onInit
Pop $0
FunctionEnd

Function un.onInit
!macro un.ParseCommandLineArgs
ClearErrors
${GetParameters} $ARGV
${GetOptions} $ARGV "/?" $ARGV_Help
Expand All @@ -736,6 +748,11 @@ Function un.onInit
/? (show this help message)$\n\
/S (run in CLI/headless mode)$\n\
/Q (quiet mode, do not print output to console)$\n\
{%- if uninstall_with_conda_exe %}
/RemoveCaches=[0|1] [default: 0]$\n\
/RemoveConfigFiles=[none|users|system|all] [default: none]$\n\
/RemoveUserData=[0|1] [default: 0]$\n\
{%- endif %}
/_?=[installation directory] (must be last parameter)$\n\
$\n\
EXAMPLES$\n\
Expand All @@ -747,8 +764,7 @@ Function un.onInit
Closing in 10s..."
# Give it some time so users can read it the pop-up console
# The pop-up console happens because the uninstaller copies itself to
# a temporary location because actually running, so we can't get the parent
# console handle
# a temporary location, so we can't get the parent console handle
Sleep 10000
Abort
${EndIf}
Expand All @@ -758,6 +774,59 @@ Function un.onInit
${IfNot} ${Errors}
StrCpy $QuietMode "1"
${EndIf}
{%- if uninstall_with_conda_exe %}
ClearErrors
${GetOptions} $ARGV "/RemoveConfigFiles=" $ARGV_Uninst_RemoveConfigFiles
${IfNot} ${Errors}
${IfNot} ${UAC_IsAdmin}
${If} $ARGV_Uninst_RemoveConfigFiles == "all"
${OrIf} $ARGV_Uninst_RemoveConfigFiles == "system"
MessageBox MB_ICONSTOP "Removing system .condarc files requires an elevated prompt."
Abort
${EndIf}
${EndIf}
${If} $ARGV_Uninst_RemoveConfigFiles == "user"
StrCpy $UninstRemoveConfigFiles_User_State ${BST_CHECKED}
StrCpy $UninstRemoveConfigFiles_System_State ${BST_UNCHECKED}
${ElseIf} $ARGV_Uninst_RemoveConfigFiles == "system"
StrCpy $UninstRemoveConfigFiles_User_State ${BST_UNCHECKED}
StrCpy $UninstRemoveConfigFiles_System_State ${BST_CHECKED}
${ElseIf} $ARGV_Uninst_RemoveConfigFiles == "all"
StrCpy $UninstRemoveConfigFiles_User_State ${BST_CHECKED}
StrCpy $UninstRemoveConfigFiles_System_State ${BST_CHECKED}
${Else}
StrCpy $UninstRemoveConfigFiles_User_State ${BST_UNCHECKED}
StrCpy $UninstRemoveConfigFiles_System_State ${BST_UNCHECKED}
${EndIf}
${EndIf}

ClearErrors
${GetOptions} $ARGV "/RemoveUserData=" $ARGV_Uninst_RemoveUserData
${IfNot} ${Errors}
${If} $ARGV_Uninst_RemoveUserData = "1"
StrCpy $UninstRemoveUserData_State ${BST_CHECKED}
${ElseIf} $ARGV_Uninst_RemoveUserData = "0"
StrCpy $UninstRemoveUserData_State ${BST_UNCHECKED}
${EndIf}
${EndIf}

ClearErrors
${GetOptions} $ARGV "/RemoveCaches=" $ARGV_Uninst_RemoveCaches
${IfNot} ${Errors}
${If} $ARGV_Uninst_RemoveCaches = "1"
StrCpy $UninstRemoveCaches_State ${BST_CHECKED}
${ElseIf} $ARGV_Uninst_RemoveCaches = "0"
StrCpy $UninstRemoveCaches_State ${BST_UNCHECKED}
${EndIf}
${EndIf}
{%- endif %}
!macroend

Function un.onInit

{%- if uninstall_with_conda_exe %}
Call un.UninstCustomOptions_InitDefaults
{%- endif %}

Push $0
Push $1
Expand Down Expand Up @@ -1384,10 +1453,11 @@ SectionEnd

Section "Uninstall"
${LogSet} on
${If} ${Silent}
!insertmacro un.ParseCommandLineArgs
${EndIf}

# Remove menu items, path entries
System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_ROOT_PREFIX", "$INSTDIR")".r0'
{{ UNINSTALL_MENUS }}

# ensure that MSVC runtime DLLs are on PATH during uninstallation
ReadEnvStr $0 PATH
Expand Down Expand Up @@ -1437,15 +1507,7 @@ Section "Uninstall"
System::Call 'kernel32::SetEnvironmentVariable(t,t)i("INSTALLER_UNATTENDED", "0").r0'
${EndIf}

!insertmacro AbortRetryNSExecWaitLibNsisCmd "pre_uninstall"
!insertmacro AbortRetryNSExecWaitLibNsisCmd "rmpath"
!insertmacro AbortRetryNSExecWaitLibNsisCmd "rmreg"

${Print} "Removing files and folders..."
nsExec::Exec 'cmd.exe /D /C RMDIR /Q /S "$INSTDIR"'

# In case the last command fails, run the slow method to remove leftover
RMDir /r /REBOOTOK "$INSTDIR"
{{ UNINSTALL_COMMANDS }}

${If} $INSTALLER_NAME_FULL != ""
DeleteRegKey SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$INSTALLER_NAME_FULL"
Expand Down
Loading
Loading