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 @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #1122: Packaging should recognize resources in dependency modules set to deploy
- #1119: The update command should check version requirements using post-update values instead of what's currently installed
- #1097: The Test resource processor now supports nested tests
- #1128: Fixed an issue where an update can fail if a resource is moved from one module to another

## [0.10.6] - 2026-02-24

Expand Down
13 changes: 6 additions & 7 deletions src/cls/IPM/Storage/Module.cls
Original file line number Diff line number Diff line change
Expand Up @@ -1336,13 +1336,6 @@ Method GetResolvedReferences(
{
set tSC = $$$OK
try {
if '$data(pDependencyGraph) {
set tSC = ..BuildDependencyGraph(.pDependencyGraph,,,,pPhases)
if $$$ISERR(tSC) {
quit
}
}

set pReferenceArray(..Name, ..Name_".ZPM") = ""

set orderedResourceList = ..GetOrderedResourceList()
Expand Down Expand Up @@ -1371,6 +1364,12 @@ Method GetResolvedReferences(
}

if pLockedDependencies {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: why did this get moved to only when pLockedDependencies is set?

if '$data(pDependencyGraph) {
set tSC = ..BuildDependencyGraph(.pDependencyGraph,,,,pPhases)
if $$$ISERR(tSC) {
quit
}
}
set tModName = ""
for {
set tModName = $order(pDependencyGraph(tModName))
Expand Down
31 changes: 31 additions & 0 deletions src/cls/IPM/Storage/ResourceReference.cls
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ Property Children As array Of %String(MAXLEN = 1, XMLPROJECTION = "NONE");

Index Children On Children(KEYS);

/// Tells us if this resource belongs to a module that will be processed as a part of the "Update" command
/// If set to true, %OnBeforeSave() will not error if another resource of the same name tries to be saved
/// and will instead save that resource with the other module as new
/// This is useful for an "Update" command where a resource was moved from one module to another.
/// If the resource's new module gets updated first, we don't want to error on a resource listed in two modules
/// when it will only be in one in the final environment once the resource's previous home gets updated
Property MarkedForUpdate As %Boolean [ InitialExpression = 0 ];

Method ProcessorGet() As %IPM.ResourceProcessor.Abstract
{
// Similar to LifecycleGet in Module.
Expand Down Expand Up @@ -344,6 +352,21 @@ ClassMethod NonNullResourceNameOpen(
quit tResult
}

/// When given a name for a resource, set its MarkedForUpdate flag
/// See property description for more details on the usage of MarkedForUpdate
ClassMethod MarkResourceForUpdate(
resourceName As %String,
marking As %Boolean = 1)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a use case for this method when marking would be set to 0?

{
set resource = ..NonNullResourceNameOpen(resourceName,,.sc)
if (resource = $$$NULLOREF) {
// If unable to find a resource with this name, do nothing
quit
}
set resource.MarkedForUpdate = marking
$$$ThrowOnError(resource.%Save())
}

ClassMethod GetStatus(
InternalName As %String,
Output pReferenced As %Boolean,
Expand Down Expand Up @@ -432,6 +455,11 @@ Method %OnBeforeSave(insert As %Boolean) As %Status [ Private, ServerOnly = 1 ]
if $$$ISERR(tSC) {
quit
}
if (tOtherInstance.MarkedForUpdate) {
// If other instance marked for update, then we should overwrite it with this instead of throwing an error
$$$ThrowOnError(..%DeleteId(tExistingID))
quit
}
if (tOtherInstance.Module.Name = ..Module.Name) {
set tMsg = $$$FormatText("Resource '%1' is listed more than once in module '%2'",..Name,..Module.Name)
} else {
Expand Down Expand Up @@ -542,6 +570,9 @@ Storage Default
<Value name="10">
<Value>IsAPI</Value>
</Value>
<Value name="11">
<Value>MarkedForUpdate</Value>
</Value>
</Data>
<DataLocation>{%%PARENT}("Resources")</DataLocation>
<DefaultData>ResourceReferenceDefaultData</DefaultData>
Expand Down
20 changes: 20 additions & 0 deletions src/cls/IPM/Utils/Module.cls
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,26 @@ ClassMethod LoadNewModule(
}
}

// Mark relevant resources for update
if (commandLineModuleName '= "") && ($get(params("cmd")) = "update") {
set tSC = moduleCurrent.GetResolvedReferences(.resourceArray, 1)
set moduleKey = ""
for {
set moduleKey = $order(resourceArray(moduleKey))
if (moduleKey = "") {
quit
}
set resourceKey = ""
for {
set resourceKey = $order(resourceArray(moduleKey, resourceKey))
if (resourceKey = "") {
quit
}
do ##class(%IPM.Storage.ResourceReference).MarkResourceForUpdate(resourceKey)
}
}
}

// This loads the new version of the module into storage. Overrides the module context of the currently installed module, if it exists.
set tSC = $system.OBJ.Load(pDirectory_"module.xml",$select(tVerbose:"d",1:"-d"),,.tLoadedList)
$$$ThrowOnError(tSC)
Expand Down
11 changes: 10 additions & 1 deletion tests/integration_tests/Test/PM/Integration/Update.cls
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,8 @@ Method TestDependencyVersionsUpdated()
set modBPreVersion = "1.0.0"
set modCPreVersion = "5.6.45+snapshot"

set modAErrorVersion = "2.0.5+snapshot"

set modAPostVersion = "2.1.0+snapshot"
set modBPostVersion = "2.0.0+snapshot"
set modCPostVersion = "6.2.0+snapshot"
Expand All @@ -695,6 +697,7 @@ Method TestDependencyVersionsUpdated()
do $$$AssertEquals(mod.Version.ToString(), modBPreVersion, "Module " _ modB _ " correctly installed as " _ modBPreVersion)
set mod = ##class(%IPM.Storage.Module).NameOpen(modC)
do $$$AssertEquals(mod.Version.ToString(), modCPreVersion, "Module " _ modC _ " correctly installed as " _ modCPreVersion)
kill mod

// Install module-d prior to update
set sc = ##class(%IPM.Main).Shell("install -v " _ modD)
Expand All @@ -704,9 +707,15 @@ Method TestDependencyVersionsUpdated()
set sc = ##class(%IPM.Main).Shell("update -v " _ modA _ " " _ modAPostVersion)
do $$$AssertStatusNotOK(sc, "Updating " _ modA _ " fails due to version errors with previously installed " _ modD)

// Uninstall module-d then try the update again
// Uninstall module-d since we don't need it anymore
set sc = ##class(%IPM.Main).Shell("uninstall -v " _ modD)
do $$$AssertStatusOK(sc, "Successfully uninstalled " _ modD)

// Try updating module-a to a version that will fail with same resource as in module-c
set sc = ##class(%IPM.Main).Shell("update -v " _ modA _ " " _ modAErrorVersion)
do $$$AssertStatusNotOK(sc, "Updating " _ modA _ " to version " _ modAErrorVersion _ " fails due to same resource in modules a and c")

// Not do an update that should succeed
set sc = ##class(%IPM.Main).Shell("update -v " _ modA _ " " _ modAPostVersion)
do $$$AssertStatusOK(sc, "Successfully updated " _ modA _ " and its dependencies")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Class Shared.Class2
{

ClassMethod MethodA()
{
write !, "This is ##class(Shared.Class2).MethodA() in module-a"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Name>module-a</Name>
<Version>2.1.0+snapshot</Version>
<Resource Name="ModuleA.PKG" />
<Resource Name="Shared.Class2.CLS" />
<Dependencies>
<ModuleReference>
<Name>module-b</Name>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Class Shared.Class1
{

ClassMethod MethodA()
{
write !, "This is ##class(Shared.Class1).MethodA() in module-c"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Name>module-c</Name>
<Version>6.2.0+snapshot</Version>
<Resource Name="ModuleC.PKG" />
<Resource Name="Shared.Class1.CLS" />
</Module>
</Document>
</Export>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Class Shared.Class1
{

ClassMethod MethodA()
{
write !, "This is ##class(Shared.Class1).MethodA() in module-b"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Name>module-b</Name>
<Version>1.0.0</Version>
<Resource Name="ModuleB.PKG" />
<Resource Name="Shared.Class1.CLS" />
<Dependencies>
<ModuleReference>
<Name>module-c</Name>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Class Shared.Class2
{

ClassMethod MethodA()
{
write !, "This is ##class(Shared.Class2).MethodA() in module-c"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Name>module-c</Name>
<Version>5.6.45+snapshot</Version>
<Resource Name="ModuleC.PKG" />
<Resource Name="Shared.Class2.CLS" />
</Module>
</Document>
</Export>
Loading