Skip to content

Commit 7c3f797

Browse files
authored
Use subdirectory inside zpm "package" path to avoid clashes and cleanup afterwards (#926)
* Use subdirectory inside package path to avoid clashes and cleanup afterwards * Fix case where package path is empty * Improve code clarity and error handling for %Package * Wrap variable with $get
1 parent f83b207 commit 7c3f797

File tree

4 files changed

+44
-25
lines changed

4 files changed

+44
-25
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3737
- #278: Modules will now be installed at a well-defined default location: `$System.Util.DataDirectory()/ipm/<packagename>/<version>/`
3838
- #374: A new System Expression `${ipmdir}` points to the module's default installation location: `$System.Util.DataDirectory()/ipm/<packagename>/<version>/`
3939
- #563: The `verify` phase will uninstall all modules after every integration test
40+
- #611, #686: If a path is supplied for `package`, it will now create a temporary subdirectory in that path to export the module into to avoid clashes with any existing files. This subdirectory will be deleted afterwards, leaving just the .tgz file.
4041

4142
## [0.10.3] - 2025-09-17
4243

src/cls/IPM/Lifecycle/Module.cls

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,32 +131,38 @@ Method %Package(ByRef pParams) As %Status
131131
try {
132132
set tVerbose = $get(pParams("Verbose"))
133133
if ..Module.HaveToDeploy() {
134-
$$$ThrowOnError(..MakeDeployed(.pParams))
134+
$$$ThrowOnError(..MakeDeployed(.pParams))
135135
}
136136

137137
set tExportDirectory = $get(pParams("Path"))
138-
if (tExportDirectory'="") && ('##class(%File).DirectoryExists(tExportDirectory)) {
139-
set tExportDirectory = ##class(%File).NormalizeDirectory(tExportDirectory)
140-
if '##class(%File).CreateDirectoryChain(tExportDirectory,.tReturn) {
141-
set tSC = $$$ERROR($$$GeneralError,$$$FormatText("Error creating directory chain %1: %2",tExportDirectory,tReturn))
138+
if (tExportDirectory'="") {
139+
// create temporary subdirectory to prevent clashes
140+
set randomDir = $translate($system.Encryption.Base64Encode($system.Encryption.GenCryptRand(6)),"+/=")
141+
set tExportSubDirectory = ##class(%Library.File).NormalizeDirectory(tExportDirectory) _ randomDir _ "/"
142+
if '##class(%File).CreateDirectoryChain(tExportSubDirectory,.tReturn) {
143+
set tSC = $$$ERROR($$$GeneralError,$$$FormatText("Error creating directory chain %1: %2",tExportSubDirectory,tReturn))
142144
quit
143145
}
144146
}
145147

146-
set tSC = ..%Export(.pParams,.tExportDirectory)
148+
set tSC = ..%Export(.pParams,.tExportSubDirectory)
147149
if $$$ISERR(tSC) {
148150
quit
149151
}
152+
// If no path is specified, the above call to %Export will generate a temporary directory
153+
if tExportDirectory="" {
154+
set tExportDirectory = tExportSubDirectory
155+
}
150156

151-
set tSC = ..OnBeforeArtifact(tExportDirectory,tExportDirectory,.pParams)
157+
set tSC = ..OnBeforeArtifact(tExportSubDirectory,tExportSubDirectory,.pParams)
152158
if $$$ISERR(tSC) {
153159
quit
154160
}
155161

156-
write:tVerbose !,"Module exported to:",!,$char(9),tExportDirectory,!
162+
write:tVerbose !,"Module contents exported to temporary directory:",!,$char(9),tExportSubDirectory,!
157163

158164
set tTgzFile = $extract(tExportDirectory,1,*-1)_".tgz"
159-
set tSC = ##class(%IPM.General.Archive).Create(tExportDirectory,tTgzFile,.tOutput)
165+
set tSC = ##class(%IPM.General.Archive).Create(tExportSubDirectory,tTgzFile,.tOutput)
160166
if $$$ISERR(tSC) {
161167
quit
162168
}
@@ -179,10 +185,16 @@ Method %Package(ByRef pParams) As %Status
179185
quit
180186
}
181187
set pParams("PackageFile") = tTgzFile
188+
182189
} catch e {
183190
set tSC = e.AsStatus()
184191
}
185192

193+
// Delete the temporary export directory
194+
if ($get(tExportSubDirectory)'="") && ##class(%Library.File).DirectoryExists(tExportSubDirectory) {
195+
set tSC = $$$ADDSC(tSC, ##class(%IPM.Utils.File).RemoveDirectoryTree(tExportSubDirectory))
196+
}
197+
186198
if '$get(pParams("Package","KeepNamespace")) {
187199
set tPackageDeployNamespace = $get(pParams("PackageDeployNamespace"))
188200
set tInitNamespace = $get(pParams("InitNamespace"))

tests/integration_tests/Test/PM/Integration/DeployedItems.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Method TestDeployedItems()
3333
do $$$AssertStatusOK(sc, "Successfully published module")
3434
do ..UninstallModule()
3535

36-
set sc = ##class(%IPM.Main).Shell("load -v "_tmpDir)
36+
set sc = ##class(%IPM.Main).Shell("load -v "_tmpDir_".tgz")
3737
do $$$AssertStatusOK(sc, "Successfully loaded module with deployed items at "_tmpDir)
3838
do ..RunDeployedCode()
3939
do ..UninstallModule()

tests/integration_tests/Test/PM/Integration/StaticFileExport.cls

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,30 @@ Class Test.PM.Integration.StaticFileExport Extends Test.PM.Integration.Base
55

66
Method TestWSGIApp()
77
{
8-
Set tSC = $$$OK
9-
Try {
10-
Set tTestRoot = ##class(%File).NormalizeDirectory($Get(^UnitTestRoot))
8+
set tSC = $$$OK
9+
try {
10+
set tTestRoot = ##class(%File).NormalizeDirectory($get(^UnitTestRoot))
1111
set tModuleDir = ##class(%File).NormalizeDirectory(##class(%File).GetDirectory(tTestRoot)_"/_data/static-file-export-test/")
12-
Set tSC = ##class(%IPM.Main).Shell("load -verbose " _ tModuleDir)
13-
Do $$$AssertStatusOK(tSC,"Module successfully. " _ tModuleDir)
14-
Set exportDir = ##class(%File).NormalizeDirectory($$$FileTempDirSys)
15-
Set tSC = ##class(%IPM.Main).Shell("static-file-export-test package -DPath="_exportDir)
16-
Do $$$AssertStatusOK(tSC,"Exported to directory " _ exportDir _ " successfully.")
17-
Do $$$AssertTrue(##class(%File).DirectoryExists(exportDir))
18-
For file = "LICENSE","README.md","requirements.txt","CHANGELOG.md" {
19-
Set tFile = ##class(%File).NormalizeFilename(file, exportDir)
20-
If '$$$AssertTrue(##class(%File).Exists(tFile)) {
21-
Do $$$LogMessage("File "_tFile_" does not exist.")
12+
set tSC = ##class(%IPM.Main).Shell("load -verbose " _ tModuleDir)
13+
do $$$AssertStatusOK(tSC,"Module successfully. " _ tModuleDir)
14+
set exportDir = ##class(%File).NormalizeDirectory($$$FileTempDirSys)
15+
set tSC = ##class(%IPM.Main).Shell("static-file-export-test package -verbose -DPath="_exportDir)
16+
do $$$AssertStatusOK(tSC,"Exported to directory " _ exportDir _ " successfully.")
17+
// Need to unpack the tgz file
18+
do $$$AssertStatusOK(##class(%IPM.Utils.File).RemoveDirectoryTree(exportDir))
19+
set tgzFile = $zstrip(exportDir,">","/\")_".tgz"
20+
set tSC = ##class(%IPM.General.Archive).Extract(tgzFile,exportDir,.tOutput)
21+
do $$$AssertStatusOK(tSC, "Unpackaged " _ tgzFile _ " to "_exportDir)
22+
23+
do $$$AssertTrue(##class(%File).DirectoryExists(exportDir))
24+
for file = "LICENSE","README.md","requirements.txt","CHANGELOG.md" {
25+
set tFile = ##class(%File).NormalizeFilename(file, exportDir)
26+
if '$$$AssertTrue(##class(%File).Exists(tFile)) {
27+
do $$$LogMessage("File "_tFile_" does not exist.")
2228
}
2329
}
24-
} Catch e {
25-
Do $$$AssertStatusOK(e.AsStatus(), "An exception occurred.")
30+
} catch e {
31+
do $$$AssertStatusOK(e.AsStatus(), "An exception occurred.")
2632
}
2733
}
2834

0 commit comments

Comments
 (0)