Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #870: When running tests for just a single suite or class, will only load and compile the relevant classes instead of all the tests
- #843: Optionally include tests when packaging using either the `-include-tests` flag or `<IncludeTests>1</IncludeTests>` in module.xml
- #1079: Add semantic sorting and shortcuts to list-installed
- #1152: Adds information about scoped dependencies in the output array of BuildDependencyGraph()

### Fixed
- #1175: Fix issue parsing version for packages with both deployed and non-deployed versions
Expand Down
8 changes: 5 additions & 3 deletions src/cls/IPM/Storage/Module.cls
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,8 @@ ClassMethod HasScope(
/// <li>Depth in the dependency tree (1 for direct dependencies, 2 for transitive, etc.)</li>
/// <li>Repository from which the module will be obtained (empty if already installed)</li>
/// <li>Version string of the module to be installed</li>
/// <li>Display name of the module</li>
/// <li>Scope of the module, or empty string if the dependency is not scoped</li>
/// </ul>
/// <p>
/// <b>Parameters:</b><br>
Expand Down Expand Up @@ -1142,7 +1144,7 @@ Method ProcessSingleDependencyIterative(
localObj.Version.Satisfies(searchExpr) &&
((version = "") || (version = localObj.VersionString))
if installedVersionValid && '(localObj.Version.IsSnapshot() && pForceSnapshotReload) {
set pDependencyGraph(pDep.Name) = $listbuild(pDepth,"",localObj.VersionString,pDep.DisplayName)
set pDependencyGraph(pDep.Name) = $listbuild(pDepth,"",localObj.VersionString,pDep.DisplayName,pDep.Scope)
Comment thread
isc-cborbonm marked this conversation as resolved.
set pDependencyGraph(pDep.Name,pParentInfo) = pDep.VersionString

// Add to work queue for next depth
Expand Down Expand Up @@ -1204,7 +1206,7 @@ Method ProcessSingleDependencyIterative(
}

set pDependencyGraph(pDep.Name) = $listbuild(
pDepth, qualifiedReference.ServerName, moduleObj.VersionString, qualifiedReference.Deployed, qualifiedReference.PlatformVersion,pDep.DisplayName
pDepth, qualifiedReference.ServerName, moduleObj.VersionString, qualifiedReference.Deployed, qualifiedReference.PlatformVersion,pDep.DisplayName,pDep.Scope
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.

Wait, why does this setting of pDependencyGraph have an extra argument of deployed in there? I know this predates your change, but I'm a bit confused by it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

absolutely no clue 😃

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.

So I asked Claude to do some investigative journalism and here's what it found:

Dependency graph format inconsistency in BuildDependencyGraph

The pDependencyGraph array written by BuildDependencyGraph has three different
entry formats depending on which code path populated it:

Path $list(data,1) $list(data,2) $list(data,3) $list(data,4) $list(data,5) $list(data,6)
Already installed (line 1145) depth "" version DisplayName
Exact-match from repo (line 1206) depth serverName version Deployed PlatformVersion DisplayName
Fuzzy-match from repo (line 1241) depth serverName version DisplayName

Root cause

This is a remnant of the iterative BFS rewrite in #975. The exact-match path was
ported from the old recursive implementation, which embedded Deployed and
PlatformVersion in the graph for downstream consumption. The new iterative design
no longer reads those positions back out of the graph (the inverted graph pipeline
only reads positions 2–3), so they became dead weight — but the format was never
cleaned up, leaving DisplayName at position 4 on two paths and position 6 on the
third.

Current impact

No active bug: ConstructInvertedDependencyGraph only reads $list(data, 2, 3), so
nothing downstream is broken today. However, any code added to read DisplayName
from the graph would silently get a Deployed boolean (0/1) for exact-match
dependencies.

@isc-kiyer what's your sense of the best thing to do here? This path-dependent inconsistency smells like a footgun and we should probably fully normalize it. Right now the caller has to know which path has been taken to know what the actual format will be.

)
set pDependencyGraph(pDep.Name,pParentInfo) = pDep.VersionString

Expand Down Expand Up @@ -1239,7 +1241,7 @@ Method ProcessSingleDependencyIterative(
// occurs if needed.
set depth = $select(previousDepth=0:pDepth,previousDepth>pDepth:previousDepth,1:pDepth)
set dependencyGraph(pDep.Name) = $listbuild(
depth,qualifiedReference.ServerName,moduleObj.VersionString,pDep.DisplayName
depth,qualifiedReference.ServerName,moduleObj.VersionString,pDep.DisplayName,pDep.Scope
)
set dependencyGraph(pDep.Name,pParentInfo) = pDep.VersionString

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Class Test.PM.Integration.BuildDependencyGraphScopedDeps Extends Test.PM.Integration.Base
Comment thread
isc-kiyer marked this conversation as resolved.
{

Parameter REPONAME = "build-dependency-graph-scoped-dep";

Parameter MODNAME = "module-a";

Property RepoPath As %String;

Method OnBeforeAllTests() As %Status
{
// Set up the repo path - use GetModuleDir utility
set ..RepoPath = ..GetModuleDir(..#REPONAME)

// Create filesystem repo pointing to test data
set sc = ##class(%IPM.Main).Shell("repo -n "_..#REPONAME_" -fs -path "_..RepoPath)
do $$$AssertStatusOK(sc,"Created"_..#REPONAME_"repo successfully.")
quit sc
}

Method OnAfterAllTests() As %Status
{
// Remove test repository
set sc = ##class(%IPM.Main).Shell("repo -delete -name "_..#REPONAME)
do $$$AssertStatusOK(sc,"Deleted "_..#REPONAME_"repo successfully.")
quit sc
}

/// BuildDependencyGraph should include scope information for scoped transitive dependencies
Method TestDependencyGraphScopedDependency()
{
do $$$AssertStatusOK(##class(%IPM.Utils.Module).LoadModuleFromDirectory(..RepoPath_..#MODNAME), "Successfully loaded module from directory.")
set module = ##class(%IPM.Storage.Module).NameOpen(..#MODNAME,,.sc)
do $$$AssertStatusOK(sc, "Successfully opened module object.")

// Expected dependencyGraph format: dependencyGraph(<module name>) = $listbuild(<depth>, <server>, <version>, <display name>, <scope>)
set scopes = $listbuild("test", "verify")
set sc = module.BuildDependencyGraph(.dependencyGraph, , , ,scopes)
do $$$AssertStatusOK(sc, "BuildDependencyGraph() call was successful.")

// Check non-scoped dependency
do $$$AssertTrue($data(dependencyGraph("module-b")))
set $listbuild(,,,,moduleBScope) = dependencyGraph("module-b")
do $$$AssertEquals(moduleBScope, "", "module-b scope successfully checked to be empty.")

// Check scoped dependencies
do $$$AssertTrue($data(dependencyGraph("module-c")))
set $listbuild(,,,,moduleCScope) = dependencyGraph("module-c")
do $$$AssertEquals(moduleCScope, "test", "module-c scope successfully checked to be 'test'.")

do $$$AssertTrue($data(dependencyGraph("module-d")))
set $listbuild(,,,,moduleDScope) = dependencyGraph("module-d")
do $$$AssertEquals(moduleDScope, "verify", "module-d scope successfully checked to be 'verify'.")
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<Export generator="IRIS" version="26">
<Document name="module-a.ZPM">
<Module>
<Name>module-a</Name>
<Version>1.0.0</Version>
<Dependencies>
<ModuleReference>
<Name>module-b</Name>
<Version>^2.0.0</Version>
</ModuleReference>
</Dependencies>
</Module>
</Document>
</Export>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<Export generator="IRIS" version="26">
<Document name="module-b.ZPM">
<Module>
<Name>module-b</Name>
<Version>2.0.0</Version>
<Dependencies>
<ModuleReference Scope="test">
<Name>module-c</Name>
<Version>^3.0.0</Version>
</ModuleReference>
<ModuleReference Scope="verify">
<Name>module-d</Name>
<Version>^4.0.0</Version>
</ModuleReference>
</Dependencies>
</Module>
</Document>
</Export>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Export generator="IRIS" version="26">
<Document name="module-c.ZPM">
<Module>
<Name>module-c</Name>
<Version>3.0.0</Version>
</Module>
</Document>
</Export>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Export generator="IRIS" version="26">
<Document name="module-d.ZPM">
<Module>
<Name>module-d</Name>
<Version>4.0.0</Version>
</Module>
</Document>
</Export>
Loading