Skip to content

Commit ffef64b

Browse files
Merge remote-tracking branch 'origin/main' into feat/2025-cors-settings-module-and-gen
2 parents e23691b + 64df42c commit ffef64b

File tree

36 files changed

+1190
-305
lines changed

36 files changed

+1190
-305
lines changed

CHANGELOG.md

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,41 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [0.10.5] - Unreleased
8+
## [0.10.6] - Unreleased
9+
10+
## Added
11+
- #973: Enables CORS and JWT configuration for WebApplications in module xml
12+
13+
### Fixed
14+
- #996: Ensure COS commands execute in exec under a dedicated, isolated context
15+
- #1002: When listing configured repositories, only show the TokenAuthMethod when a token is defined.
16+
17+
## [0.10.5] - 2026-01-15
918

1019
### Added
11-
- #938 Added flag -export-python-deps to package command
20+
- #938: Added flag -export-python-deps to package command
1221
- #462: The `repo` command for repository configuration now supports secret input terminal mode for passwords with the `-password-stdin` flag
1322
- #935: Adding a generic JFrog Artifactory tarball resource processor for bundling artifact with a package and deploying it to a final location on install.
1423
- #950: Added support for listing installed Python packages using `list -python`, `list -py` and `list-installed -python`
1524
- #822: The CPF resource processor now supports system expressions and macros in CPF merge files
16-
- #578 Added functionality to record and display IPM history of install, uninstall, load, and update
25+
- #578: Added functionality to record and display IPM history of install, uninstall, load, and update
1726
- #961: Adding creation of a lock file for a module by using the `-create-lockfile` flag on install.
27+
- #959: In ORAS repos, external name can now be used interchangeably with (default) name for `install` and `update`, i.e. a module published with its (default) name can be installed using its external name.
28+
- #951: The `unpublish` command will skip user confirmation prompt if the `-force` flag is provided.
29+
- #1018: Require module name for uninstall when not using the -all flag
1830
- #973: Enables CORS and JWT configuration for WebApplications in module xml
1931
### Changed
2032
- #316: All parameters, except developer mode, included with a `load`, `install` or `update` command will be propagated to dependencies
2133
- #885: Always synchronously load dependencies and let each module do multi-threading as needed
2234
to load using multicompile instead of trying to do own multi-threading of item load which causes
2335
lock contention by bypassing IRIS compiler.
2436
- #481: Improve BuildDependencyGraph performance by doing the following:
25-
- Eliminate recursion and use iteration.
26-
- Remove depth first search and do pure breadth first search.
27-
- Have better caching of results for module searches by collapsing search expressions (reducing expressions that are intersections).
37+
- Eliminate recursion and use iteration.
38+
- Remove depth first search and do pure breadth first search.
39+
- Have better caching of results for module searches by collapsing search expressions (reducing expressions that are intersections).
2840

2941
### Removed
30-
- #938 Removed secret flag NewVersion handling in %Publish()
42+
- #938: Removed secret flag NewVersion handling in %Publish()
3143

3244
### Fixed
3345
- #943: The `load` command when used with a GitHub repository URL accepts a `branch` argument again
@@ -37,11 +49,19 @@ lock contention by bypassing IRIS compiler.
3749
- #965: FileCopy on a directory with a Name without the leading slash now works
3850
- #937: Publishing a module with a `<WebApplication>` containing a `Path` no longer errors out
3951
- #957: Improved error messages for OS command execution. Now, when a command fails, the error message includes the full command and its return code. Also fixed argument separation for the Windows `attrib` command and removed misleading error handling for missing commands.
52+
- #789: Fix error when listing modules for an ORAS repo with a specified namespace.
53+
- #999, #1000: Installing IPM cleans up stale mappings used in old versions of IPM
54+
- #1007: The `${ipmDir}` expression now works in the `<Arg>` of an `<Invoke>`
55+
- #1015: Fix dependency resolution bugs where `*` as the version requirement and intersecting ranges wouldn't work properly.
56+
- #1036: The `update` command no longer propagates developer mode to dependencies
4057

4158
### Deprecated
4259
- #828: The `CheckStatus` flag for `<Invoke>` action has been deprecated. Default behavior is now to always check the status of the method if and only if the method signature returns %Library.Status
4360
- #885: `-synchronous` flag since loading dependencies synchronously is now the default behavior.
4461

62+
### Security
63+
- urllib3 wheel has been updated to 2.6.3
64+
4565
## [0.10.4] - 2025-10-21
4666

4767
### Added
@@ -65,6 +85,8 @@ lock contention by bypassing IRIS compiler.
6585
- #924: Make "module" parameter not required for "uninstall" command so -all modifier works
6686
- #928: `zpm "info"` now recognizes existence of configured ORAS registries
6787
- #930: Fix issue where `load` didn't work on GitHub URLs
88+
- #1011: Hidden flags IgnoreInstalled and UpdateSnapshots cause redundant calling of BuildDependencyGraph()
89+
- #1014: After FileCopy respects scope change #864, compileable resources with specified Scope cause Compile phase to fail on install
6890

6991
### Changed
7092
- #639: All modules installed in developer mode can now be edited, even if they do not contain "snapshot" in the version string

module.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<Document name="ZPM.ZPM">
44
<Module>
55
<Name>ZPM</Name>
6-
<Version>0.10.5-SNAPSHOT</Version>
6+
<Version>0.10.6-SNAPSHOT</Version>
77
<ExternalName>IPM</ExternalName>
88
<Description>InterSystems Package Manager (IPM) provides development tools and
99
infrastructure for defining, building, distributing, and installing modules and
@@ -45,7 +45,7 @@
4545
<PythonWheel Name="referencing-0.36.2-py3-none-any.whl" ExtraPipFlags="--no-deps" />
4646
<PythonWheel Name="requests-2.32.4-py3-none-any.whl" ExtraPipFlags="--no-deps" />
4747
<PythonWheel Name="typing_extensions-4.12.2-py3-none-any.whl" ExtraPipFlags="--no-deps" />
48-
<PythonWheel Name="urllib3-2.6.0-py3-none-any.whl" ExtraPipFlags="--no-deps" />
48+
<PythonWheel Name="urllib3-2.6.3-py3-none-any.whl" ExtraPipFlags="--no-deps" />
4949

5050
<!-- Pure python implementation of rpds-py for offline installation or behind a
5151
firewall.
@@ -54,4 +54,4 @@
5454
<FileCopy Name="modules/python/rpds.py" Target="${mgrdir}python/rpds.py" />
5555
</Module>
5656
</Document>
57-
</Export>
57+
</Export>

preload/cls/IPM/Installer.cls

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ XData PM [ XMLNamespace = INSTALLER ]
2323
<ClassMapping Package="%IPM" From="${CURNSRoutineDB}" />
2424
<RoutineMapping Routines="%IPM.*" Type="ALL" From="${CURNSRoutineDB}" />
2525
</Configuration>
26+
<Invoke Class="IPM.Installer" Method="CleanupOldMappings"/>
2627
<Import File="${SRCDIR}" Recurse="1"/>
2728
<Invoke Class="%IPM.Main" Method="UpdateLanguageExtensions" CheckStatus="true" />
2829
<Invoke Class="IPM.Installer" Method="ZPMInit" CheckStatus="true">
@@ -416,4 +417,63 @@ ClassMethod CreateTempDir() As %String
416417
return ##class(%File).NormalizeDirectory(tDir)
417418
}
418419

420+
/// When installing a new version of IPM, make sure any old mappings are cleaned up
421+
ClassMethod CleanupOldMappings() As %Status
422+
{
423+
set sc = $$$OK
424+
// Get list of all namespaces
425+
new $namespace
426+
set $namespace = "%SYS"
427+
set status = ##class(%SYS.Namespace).ListAll(.nsList)
428+
if $$$ISERR(status) {
429+
return status
430+
}
431+
432+
// Iterate through each namespace and remove old mappings
433+
set namespace = ""
434+
for {
435+
set namespace = $order(nsList(namespace))
436+
if namespace = "" {
437+
quit
438+
}
439+
set $namespace = namespace
440+
// check if IPM is installed in this namespace
441+
if '$$$comClassDefined("%IPM.Main") {
442+
continue
443+
}
444+
445+
// Cleanup old mappings from oddStudioDocument
446+
set key = ""
447+
for {
448+
set key = $order(^oddStudioDocument(key))
449+
quit:key=""
450+
if (key["%ZPM.PackageManager") || (key["%ZHSLIB.PackageManager") {
451+
kill ^oddStudioDocument(key)
452+
}
453+
}
454+
455+
// These mappings were suggested as a temporary workaround for IRIS 2023.x,
456+
// but whose underlying issues have been resolved in 2024.1 and later.
457+
set status = ##class(%IPM.Utils.Module).RemoveGlobalMapping($namespace,"oddStudioDocument:(BEGIN):(""%ZP"")","IRISLIB")
458+
if $$$ISERR(status) {
459+
set sc = $$$ADDSC(sc, status)
460+
}
461+
set status = ##class(%IPM.Utils.Module).RemoveGlobalMapping($namespace,"oddStudioDocument:(""%ZP""):(""A"")","IRISSYS")
462+
if $$$ISERR(status) {
463+
set sc = $$$ADDSC(sc, status)
464+
}
465+
set status = ##class(%IPM.Utils.Module).RemoveGlobalMapping($namespace,"oddStudioDocument:(""Ens""):(""Ent"")","ENSLIB")
466+
if $$$ISERR(status) {
467+
set sc = $$$ADDSC(sc, status)
468+
}
469+
set status = ##class(%IPM.Utils.Module).RemoveGlobalMapping($namespace,"oddStudioDocument",namespace)
470+
if $$$ISERR(status) {
471+
// this mapping deletion will fail if there are still sub-mappings present
472+
// so we can ignore errors here
473+
}
474+
}
475+
476+
return sc
477+
}
478+
419479
}

src/cls/IPM/General/SemanticVersionExpression/Range.cls

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,16 +339,26 @@ ClassMethod CompareVersions(
339339
comp2 As %IPM.General.SemanticVersionExpression.Comparator) As %Integer [ Internal, Private ]
340340
{
341341
// Create semantic versions for comparison
342+
// For range intersection purposes, we need to compare versions numerically
343+
// to determine which bound is more restrictive, ignoring prerelease semantics
342344
set v1 = comp1.ExtractToSemanticVersion()
343345
set v2 = comp2.ExtractToSemanticVersion()
344346

345-
// Compare versions
346-
if v1.Follows(v2) {
347+
// Compare major.minor.patch numerically
348+
if (v1.Major > v2.Major) {
347349
set result = 1
348-
} elseif v2.Follows(v1) {
350+
} elseif (v1.Major < v2.Major) {
351+
set result = -1
352+
} elseif (v1.Minor > v2.Minor) {
353+
set result = 1
354+
} elseif (v1.Minor < v2.Minor) {
355+
set result = -1
356+
} elseif (v1.Patch > v2.Patch) {
357+
set result = 1
358+
} elseif (v1.Patch < v2.Patch) {
349359
set result = -1
350360
} else {
351-
// Versions are equal, compare operators
361+
// Major.minor.patch are equal: compare operators
352362
// For lower bounds: > is more restrictive than >=
353363
// For upper bounds: < is more restrictive than <=
354364
if (comp1.Operator = ">") && (comp2.Operator = ">=") {

src/cls/IPM/Lifecycle/Base.cls

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,6 @@ Method InstallOrDownloadPythonRequirements(
751751
throw ##class(%Exception.General).%New("Embedded Python is not available in this instance.")
752752
}
753753
set processType = ""
754-
set stdout = ""
755754
set tSysModule = ##class(%SYS.Python).Import("sys")
756755
set tPyMajor = tSysModule."version_info".major
757756
set tPyMinor = tSysModule."version_info".minor
@@ -967,7 +966,7 @@ Method %Compile(ByRef pParams) As %Status
967966
set tResource = orderedResourceList.GetNext(.tKey)
968967
quit:tKey=""
969968

970-
if '$data(tHandledMap(tResource.Name)) && $isobject(tResource.Processor) {
969+
if '$data(tHandledMap(tResource.Name)) && $isobject(tResource.Processor) && tResource.IsInScope("Compile") {
971970
if tResource.Processor.%IsA("%IPM.ResourceProcessor.AbstractCompilable") && tResource.Processor.CompileFromProject {
972971
if tVerbose {
973972
write !, tResource.Name_" was loaded as deployed code, so compilation isn't needed."

src/cls/IPM/Main.cls

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,10 @@ This command is an alias for `module-action module-name makedeployed`
179179
Delete package from repository
180180
</description>
181181
<example description="Delete all versions of the package &quot;MyModuleName&quot; from the repository">unpublish MyModuleName all</example>
182-
<example description="Delete version &quot;1.0.0&quot; of the package &quot;MyModuleName&quot; from the repository">unpublish MyModuleName 1.0.0</example>
182+
<example description="Delete version &quot;1.0.0&quot; of the package &quot;MyModuleName&quot; from the repository named MyRepo">unpublish MyRepo/MyModuleName 1.0.0</example>
183183
<parameter name="module" required="true" description="Name of module on which to perform unpublish actions" />
184184
<parameter name="version" required="true" description="Version of module on which to perform unpublish actions. Use &quot;all&quot; to delete all versions of the package" />
185-
185+
<modifier name="force" aliases="f" value="false" description="Will delete module from the repository without prompting user for confirmation. Still requires authorization from the repository" />
186186
<modifier name="quiet" aliases="q" dataAlias="Verbose" dataValue="0" description="Produces minimal output from the command." />
187187
<modifier name="verbose" aliases="v" dataAlias="Verbose" dataValue="1" description="Produces verbose output from the command." />
188188
</command>
@@ -983,8 +983,7 @@ ClassMethod ShellInternal(
983983
} elseif (tCommandInfo = "load") {
984984
do ..Load(.tCommandInfo)
985985
} elseif (tCommandInfo = "exec") {
986-
write !
987-
xecute tCommandInfo("parameters","expression")
986+
do ..ExecuteCOS(.tCommandInfo)
988987
} elseif (tCommandInfo = "install") {
989988
do ..Install(.tCommandInfo)
990989
} elseif (tCommandInfo = "reinstall") {
@@ -2193,6 +2192,8 @@ ClassMethod ShowModulesForRepository(
21932192
set list(list, "AllVersions") = tRes.AllVersions
21942193
}
21952194
}
2195+
// Throw an error if there are no results and tSC is set
2196+
$$$ThrowOnError(tSC)
21962197
set list("width") = width
21972198
write !
21982199
do ..DisplayModules(.list)
@@ -2255,6 +2256,13 @@ ClassMethod Load(
22552256
$$$ThrowOnError(log.Finalize($$$OK, devMode))
22562257
}
22572258

2259+
ClassMethod ExecuteCOS(ByRef commandInfo)
2260+
{
2261+
new $namespace
2262+
write !
2263+
xecute commandInfo("parameters","expression")
2264+
}
2265+
22582266
ClassMethod LoadInternal(
22592267
ByRef pCommandInfo,
22602268
pLog As %IPM.General.AbstractHistory) [ Internal ]
@@ -2487,7 +2495,10 @@ ClassMethod Uninstall(ByRef pCommandInfo) [ Internal ]
24872495
$$$ThrowOnError(##class(%IPM.Utils.Module).UninstallAll(tForce,.tParams))
24882496
return
24892497
} else {
2490-
set tModuleName = pCommandInfo("parameters","module")
2498+
set tModuleName = $get(pCommandInfo("parameters","module"))
2499+
if (tModuleName = "") {
2500+
$$$ThrowOnError($$$ERROR($$$GeneralError, "A module name must be specified for uninstall unless the -all flag is used."))
2501+
}
24912502
if (tModuleName = $$$IPMModuleName) {
24922503
$$$ThrowOnError(..CheckModuleNamespace())
24932504
}
@@ -2545,25 +2556,29 @@ ClassMethod Unpublish(ByRef pCommandInfo) [ Internal ]
25452556

25462557
if (isEnabled) {
25472558
set tResult = 0
2548-
if ($$$lcase(tVersion)="all") {
2549-
write $$$FormattedLine($$$Red, "Deleting a package and all its versions is an irreversible action")
2550-
set tHelp = "Enter ""Yes"" if you want to delete all package versions."
2551-
set tMsg = "Are you sure you want to delete all versions of the package """_tModuleName_""" from registry """_tServer.Name_""" ("_tServer.URL_")?"
2552-
} else {
2553-
write $$$FormattedLine($$$Red, "Deleting a package version is an irreversible action")
2554-
set tHelp = "Enter ""Yes"" if you want to delete selected package version."
2555-
set tMsg = "Are you sure you want to delete the package """_tModuleName_" "_tVersion_""" from registry """_tServer.Name_""" ("_tServer.URL_")?"
2556-
}
2559+
set force = $data(pCommandInfo("modifiers","force"))
2560+
if 'force {
2561+
if ($$$lcase(tVersion)="all") {
2562+
write $$$FormattedLine($$$Red, "Deleting a package and all its versions is an irreversible action!")
2563+
set tHelp = "Enter ""Yes"" if you want to delete all package versions."
2564+
set tMsg = "Are you sure you want to delete all versions of the package """_tModuleName_""" from registry """_tServer.Name_""" ("_tServer.URL_")?"
2565+
} else {
2566+
write $$$FormattedLine($$$Red, "Deleting a package version is an irreversible action!")
2567+
set tHelp = "Enter ""Yes"" if you want to delete selected package version."
2568+
set tMsg = "Are you sure you want to delete the package """_tModuleName_" version "_tVersion_""" from registry """_tServer.Name_""" ("_tServer.URL_")?"
2569+
}
25572570

2558-
set tResponse = ##class(%Library.Prompt).GetYesNo(tMsg,.tResult,.tHelp)
2571+
set tResponse = ##class(%Library.Prompt).GetYesNo(tMsg,.tResult,.tHelp)
25592572

2560-
if (tResponse '= $$$SuccessResponse) {
2561-
$$$ThrowStatus($$$ERROR($$$GeneralError,"Operation cancelled."))
2573+
if (tResponse '= $$$SuccessResponse) {
2574+
$$$ThrowStatus($$$ERROR($$$GeneralError,"Operation cancelled."))
2575+
}
25622576
}
25632577

2564-
if (tResult) {
2578+
if (tResult || force) {
25652579
$$$ThrowOnError(tManager.Unpublish(tServer.Name, tModuleName, tVersion))
2566-
write !!,"Package deleted"
2580+
set msg = "Package deleted" _ $case(force, 1:" forcefully without user confirmation", :"")
2581+
write !!, msg
25672582
}
25682583
} else {
25692584
write !,"The package could not be deleted (the registry denied the request)",!

src/cls/IPM/Repo/Http/Definition.cls

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ Method Display()
9090
}
9191
if (..Token '= "") {
9292
write !,$char(9),"Token: ",..Padding(0),$case(..Token,"":"<unset>",:"<set>")
93-
}
94-
if (..Token '= "") || (..Password '= "") || (..Username '= "") {
9593
write !,$char(9),"Token Auth Method: ",..Padding(2),..TokenAuthMethod
9694
}
9795
}

0 commit comments

Comments
 (0)