Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- #992: Implement automatic history purge logic
- #973: Enables CORS and JWT configuration for WebApplications in module.xml
- #1034: Add -clean flag to remove source directory while uninstalling

### Fixed
- #1001: The `unmap` and `enable` commands will now only activate CPF merge once after all namespaces have been configured instead after every namespace
Expand Down
1 change: 1 addition & 0 deletions preload/cls/IPM/Installer.cls
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ ClassMethod ZPMInit(
$$$QuitOnError(##class(%IPM.Repo.UniversalSettings).SetValue("UseStandalonePip", "", 0))
$$$QuitOnError(##class(%IPM.Repo.UniversalSettings).SetValue("SemVerPostRelease", 0, 0))
$$$QuitOnError(##class(%IPM.Repo.UniversalSettings).SetValue("DefaultLogEntryLimit",20, 0))
$$$QuitOnError(##class(%IPM.Repo.UniversalSettings).SetValue("CleanOnUninstall",0, 0))
quit $$$OK
}

Expand Down
20 changes: 20 additions & 0 deletions src/cls/IPM/Lifecycle/Base.cls
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,26 @@ Method %Clean(ByRef pParams) As %Status
quit
}
}
// Remove the source directory while uninstalling the module.
if $data(pParams("Clean","clean")) ||(##class(%IPM.Repo.UniversalSettings).GetCleanOnUninstall()) {
set verbose = $get(pParams("Clean","Verbose"))
set moduleRootDir = ..Module.Root
set moduleName = ..Module.DisplayName
write !,"["_$namespace_"|"_moduleName_"]",$char(9),"Cleanup: Removing source directory"
if ##class(%File).DirectoryExists(moduleRootDir) {
if verbose {
write !,"["_$namespace_"|"_moduleName_"]",$char(9),"Found source directory: "_moduleRootDir
}
set tSC = ##class(%IPM.Utils.File).RemoveDirectoryTree(moduleRootDir)
if verbose {
write !,"["_$namespace_"|"_moduleName_"]",$char(9),"Source directory successfully deleted"
}
if $$$ISERR(tSC) {
write !,"["_$namespace_"|"_moduleName_"]",$char(9),"Source directory delete issue: "_$system.Status.GetErrorText(tSC)
$$$ThrowOnError(tSC)
}
}
}
} catch e {
set tSC = e.AsStatus()
}
Expand Down
7 changes: 7 additions & 0 deletions src/cls/IPM/Main.cls
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ reinstall -env /path/to/env1.json;/path/to/env2.json example-package
<modifier name="quiet" aliases="q" dataAlias="Verbose" dataValue="0" description="Produces minimal output from the command." />
<modifier name="verbose" aliases="v" dataAlias="Verbose" dataValue="1" description="Produces verbose output from the command." />
<modifier name="purge" dataAlias="Purge" dataValue="1" description="Purge data from tables during uninstall." />
<modifier name="clean" aliases="c" description="Remove the module source directory during uninstall." />

</command>

Expand Down Expand Up @@ -2553,6 +2554,12 @@ ClassMethod Uninstall(ByRef pCommandInfo) [ Internal ]
$$$ThrowOnError(..CheckModuleNamespace())
}
set tRecurse = $$$HasModifier(pCommandInfo,"recurse") // Recursively uninstall unneeded dependencies
if $data(pCommandInfo("modifiers","clean")) {
set tParams("Clean","clean") = pCommandInfo("modifiers","clean")
}
if $get(pCommandInfo("data","Verbose")) {
set tParams("Clean","Verbose") = 1
}
$$$ThrowOnError(##class(%IPM.Storage.Module).Uninstall(tModuleName,tForce,tRecurse,.tParams))
}
}
Expand Down
10 changes: 4 additions & 6 deletions src/cls/IPM/Repo/UniversalSettings.cls
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ Parameter SemVerPostRelease = "SemVerPostRelease";
/// to retain IPM history records before they are eligible for cleanup.
Parameter HistoryRetain = "history_retain";

Parameter CONFIGURABLE = "trackingId,analytics,ColorScheme,TerminalPrompt,PublishTimeout,PipCaller,UseStandalonePip,SemVerPostRelease,DefaultLogEntryLimit,HistoryRetain";
/// Determines if the source folder should be physically deleted when a module is uninstalled.
Parameter CleanOnUninstall = "CleanOnUninstall";

Parameter CONFIGURABLE = "trackingId,analytics,ColorScheme,TerminalPrompt,PublishTimeout,PipCaller,UseStandalonePip,SemVerPostRelease,DefaultLogEntryLimit,HistoryRetain,CleanOnUninstall";

/// Returns configArray, that includes all configurable settings
ClassMethod GetAll(Output configArray) As %Status
Expand Down Expand Up @@ -185,9 +188,4 @@ ClassMethod SetAnalyticsAvailable(
return ..SetValue(..#analytics, +val, overwrite)
}

ClassMethod GetHistoryRetain() As %Integer
{
return ..GetValue(..#HistoryRetain)
}

}
72 changes: 72 additions & 0 deletions tests/unit_tests/Test/PM/Unit/CLI.cls
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ Method RunCommand(pCommand As %String) As %Status
return status
}

/// This method returns the %Status for checking the success or failure of executed commands.
Method AssertNoException(pCommand As %String)
{
do ##class(%IPM.Main).ShellInternal(pCommand,.tException)
Expand Down Expand Up @@ -352,4 +353,75 @@ Method TestUninstallWithoutModuleName()
do $$$AssertNotTrue(exists, "Module removed successfully.")
}

Method TestUninstallWithCleanModifier()
{
set module = "objectscript-math"
do $$$LogMessage("Testing: Uninstall with -clean flag to remove source directory.")

//TestRepository method removed the registry configuration. So reconfigure while testing
do ##class(%IPM.Main).GetVersion("zpm",.out)
if $get(out)="" {
do $$$LogMessage("No registry configured. So started configuring https://pm.community.intersystems.com")
set status = ..RunCommand("repo -remote -n registry -url https://pm.community.intersystems.com/ -user """" -pass """"")
do $$$AssertStatusOK(status, "Registory configured successfully")
}

// 1. Setup: Ensure module is installed
if '##class(%IPM.Storage.Module).NameExists(module) {
do $$$LogMessage("Module "_module_" not found. Installing...")
set status = ..RunCommand("install "_module)
do $$$AssertStatusOK(status, "Setup: Module installed successfully.")
}

// 2. Capture the root directory path
set moduleObj = ##class(%IPM.Storage.Module).NameOpen(module)
set moduleRootDir = moduleObj.Root
do $$$AssertNotEquals(moduleRootDir, "", "Verified module root directory path: "_moduleRootDir)

// 3. Test Standard Uninstall (Should NOT delete directory)
do $$$LogMessage("Step 1: Uninstalling "_module_" without -clean flag.")
set status = ..RunCommand("uninstall "_module)
do $$$AssertStatusOK(status, "Standard uninstall completed.")

set dirExists = ##class(%File).DirectoryExists(moduleRootDir)
do $$$AssertTrue(dirExists, "Verification: Directory still exists after standard uninstall (as expected).")

// 4. Re-install for Clean Test
do $$$LogMessage("Step 2: Re-installing for -clean flag test.")
set status = ..RunCommand("install "_module)
do $$$AssertStatusOK(status, "Module re-installed successfully.")

// 5. Test Uninstall with -c (Should delete directory)
do $$$LogMessage("Step 3: Uninstalling "_module_" with -clean flag.")
set status = ..RunCommand("uninstall "_module_" -c")
do $$$AssertStatusOK(status, "Clean uninstall completed.")

set dirDeleted = '##class(%File).DirectoryExists(moduleRootDir)
do $$$AssertTrue(dirDeleted, "Verification: Source directory was physically deleted from the file system.")

do $$$LogMessage("Repeating this test for the UnvierslSettings 'CleanOnUninsall'")
// ZPMInit method configuratio from installer
/// bydefualt keep this flag as 0
set status = ##class(%IPM.Repo.UniversalSettings).SetValue("CleanOnUninstall",0, 0)
do $$$AssertStatusOK(status, "initial setup for CleanOnUninstall done.")

do $$$LogMessage("Step 4: Re-installing for CleanOnUninstall settings test.")
set status = ..RunCommand("install "_module)
do $$$AssertStatusOK(status, "Module re-installed successfully.")

do $$$LogMessage("Step 5: Updating the CleanOnUninstall settings to 1.")
set status = ##class(%IPM.Repo.UniversalSettings).UpdateOne("CleanOnUninstall",1)
do $$$AssertStatusOK(status, "Updated the CleanOnUninstall to 1.")

do $$$LogMessage("Step 6: Uninstall the module without -clean flag")
set status = ..RunCommand("uninstall "_module)
do $$$AssertStatusOK(status, "Clean uninstall completed.")

set dirDeleted = '##class(%File).DirectoryExists(moduleRootDir)
do $$$AssertTrue(dirDeleted, "Verification: Source directory was physically deleted from the file system.")

do ##class(%IPM.Repo.UniversalSettings).ResetToDefault("CleanOnUninstall")
do $$$LogMessage("CleanOnUninstall configuration restored")
}

}