Skip to content

Commit dfdda46

Browse files
isc-kiyerkiyer
andauthored
Fix dependency resolution bug, semver intersection and bug and have better error message for dependency incompatibilities (#1194)
* Fix bug in Intersection semver for exact version against a range * Fix bug in dependency resolution where certain incompatible dependencies would get dropped instead of throwing an error. Also fix 1077 to have better error message when an incompatibility is found. * Bump some timeouts for github actions. Skip products with expired licenses --------- Co-authored-by: kiyer <keshav.iyer@intersystems.com>
1 parent 12ee140 commit dfdda46

50 files changed

Lines changed: 1133 additions & 240 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/images.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ jobs:
2929
tags=$(curl -su ":" https://containers.intersystems.com/v2/intersystems/${n}/tags/list | jq -r '.tags[]' | awk '!/(-linux)|([1-4]-preview)|(-em)|(-cd)/' | awk '!/\.[1-4]\./' | sort | uniq)
3030
for tag in $tags
3131
do
32-
# Skip 2023.3 and 2023.2 because the license has expired
33-
if [ "$tag" = "2023.3" -o "$tag" = "2023.2" ];
32+
# Skip those with expired licenses
33+
if [ "$tag" = "2023.3" -o "$tag" = "2023.2" -o "$tag" = "2024.3" -o "$tag" = "2025.1" ];
3434
then
3535
continue
3636
fi

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ jobs:
109109
sleep 5
110110
docker logs $ORAS
111111
- name: Test and Verify
112-
timeout-minutes: 15
112+
timeout-minutes: 30
113113
run: |
114114
CONTAINER=$(docker run --network zpm -d --rm \
115115
-e TEST_REGISTRY=http://registry:52773/registry/ \
@@ -167,7 +167,7 @@ jobs:
167167
docker logs $CONTAINER
168168
docker stop $CONTAINER
169169
- name: Clean install current version and test new one.
170-
timeout-minutes: 15
170+
timeout-minutes: 30
171171
run: |
172172
curl http://localhost:52773/registry/packages/-/all | jq
173173
curl http://localhost:52773/registry/packages/zpm/ | jq

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@ 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.7] - Unreleased
8+
## [0.10.8] - Unreleased
9+
10+
### Fixed
11+
- #1192: Fix dependency resolution dropping incompatibilities and along with that fix a bug where
12+
semver Intersection would incorrectly pass if an exact version is compared against a range even with
13+
no overlap.
14+
- #1077: Fixed error handling of dependency resolution to correctly report what the offending
15+
modules are.
16+
17+
## [0.10.7] - 2026-05-29
918

1019
### Added
1120
- #408: Modules can now list dependencies without specifying version; will be assumed to be "*"

module.xml

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,60 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Export generator="Cache" version="25">
3-
<Document name="ZPM.ZPM">
4-
<Module>
5-
<Name>ZPM</Name>
6-
<Version>0.10.7-SNAPSHOT</Version>
7-
<ExternalName>IPM</ExternalName>
8-
<Description>InterSystems Package Manager (IPM) provides development tools and
9-
infrastructure for defining, building, distributing, and installing modules and
10-
applications.</Description>
11-
<Keywords>Package Manager</Keywords>
12-
<Author>
13-
<Organization>InterSystems Corporation</Organization>
14-
</Author>
15-
<Packaging>module</Packaging>
16-
<SourcesRoot>src</SourcesRoot>
17-
<Resource Name="%IPM.PKG" />
18-
<Resource Name="%IPM.Common.INC" />
19-
<Resource Name="%IPM.Formatting.INC" />
20-
<Resource Name="IPM.Installer.CLS" Preload="true" />
21-
<UnitTest Name="/tests/unit_tests/" Package="Test.PM.Unit" Phase="test" />
22-
<UnitTest Name="/tests/integration_tests/" Package="Test.PM.Integration" Phase="verify" />
23-
<Invoke Class="IPM.Installer" Method="Map" Phase="Reload" When="Before" />
24-
<Invoke Class="IPM.Installer" Method="MapIfLegacy" Phase="Compile" When="After" />
25-
<Invoke Class="IPM.Installer" Method="InstallORASIfMissing" Phase="Activate"
26-
When="After" />
27-
<Invoke Class="%IPM.Main" Method="UpdateLanguageExtensions" />
28-
<Invoke Class="%IPM.Utils.Migration" Method="RunAll">
29-
<Arg>${verbose}</Arg>
30-
</Invoke>
3+
<Document name="ZPM.ZPM">
4+
<Module>
5+
<Name>ZPM</Name>
6+
<Version>0.10.8-SNAPSHOT</Version>
7+
<ExternalName>IPM</ExternalName>
8+
<Description>InterSystems Package Manager (IPM) provides development tools and
9+
infrastructure for defining, building, distributing, and installing modules and
10+
applications.</Description>
11+
<Keywords>Package Manager</Keywords>
12+
<Author>
13+
<Organization>InterSystems Corporation</Organization>
14+
</Author>
15+
<Packaging>module</Packaging>
16+
<SourcesRoot>src</SourcesRoot>
17+
<Resource Name="%IPM.PKG" />
18+
<Resource Name="%IPM.Common.INC" />
19+
<Resource Name="%IPM.Formatting.INC" />
20+
<Resource Name="IPM.Installer.CLS" Preload="true" />
21+
<UnitTest Name="/tests/unit_tests/" Package="Test.PM.Unit" Phase="test" />
22+
<UnitTest Name="/tests/integration_tests/" Package="Test.PM.Integration" Phase="verify" />
23+
<Invoke Class="IPM.Installer" Method="Map" Phase="Reload" When="Before" />
24+
<Invoke Class="IPM.Installer" Method="MapIfLegacy" Phase="Compile" When="After" />
25+
<Invoke Class="IPM.Installer" Method="InstallORASIfMissing" Phase="Activate"
26+
When="After" />
27+
<Invoke Class="%IPM.Main" Method="UpdateLanguageExtensions" />
28+
<Invoke Class="%IPM.Utils.Migration" Method="RunAll">
29+
<Arg>${verbose}</Arg>
30+
</Invoke>
3131

32-
<!-- Requires 2022.1+ due to Embedded Python -->
33-
<SystemRequirements Version=">=2022.1" />
32+
<!-- Requires 2022.1+ due to Embedded Python -->
33+
<SystemRequirements Version=">=2022.1" />
3434

35-
<!-- List of python wheels for offline installation – oras and its transitive
35+
<!-- List of python wheels for offline installation – oras and its transitive
3636
dependencies -->
37-
<PythonWheel Name="attrs-25.1.0-py3-none-any.whl" ExtraPipFlags="--no-deps" />
38-
<PythonWheel Name="certifi-2025.1.31-py3-none-any.whl" ExtraPipFlags="--no-deps" />
39-
<PythonWheel Name="charset_normalizer-2.1.1-py3-none-any.whl" ExtraPipFlags="--no-deps" />
40-
<PythonWheel Name="idna-3.15-py3-none-any.whl" ExtraPipFlags="--no-deps" />
41-
<PythonWheel Name="jsonschema-4.23.0-py3-none-any.whl" ExtraPipFlags="--no-deps" />
42-
<PythonWheel Name="jsonschema_specifications-2024.10.1-py3-none-any.whl"
43-
ExtraPipFlags="--no-deps" />
44-
<PythonWheel Name="oras-0.1.30-py3-none-any.whl" ExtraPipFlags="--no-deps" />
45-
<PythonWheel Name="referencing-0.36.2-py3-none-any.whl" ExtraPipFlags="--no-deps" />
46-
<PythonWheel Name="requests-2.33.0-py3-none-any.whl" ExtraPipFlags="--no-deps" />
47-
<PythonWheel Name="typing_extensions-4.12.2-py3-none-any.whl" ExtraPipFlags="--no-deps" />
48-
<PythonWheel Name="urllib3-2.7.0-py3-none-any.whl" ExtraPipFlags="--no-deps" />
37+
<PythonWheel Name="attrs-25.1.0-py3-none-any.whl" ExtraPipFlags="--no-deps" />
38+
<PythonWheel Name="certifi-2025.1.31-py3-none-any.whl" ExtraPipFlags="--no-deps" />
39+
<PythonWheel Name="charset_normalizer-2.1.1-py3-none-any.whl" ExtraPipFlags="--no-deps" />
40+
<PythonWheel Name="idna-3.15-py3-none-any.whl" ExtraPipFlags="--no-deps" />
41+
<PythonWheel Name="jsonschema-4.23.0-py3-none-any.whl" ExtraPipFlags="--no-deps" />
42+
<PythonWheel Name="jsonschema_specifications-2024.10.1-py3-none-any.whl"
43+
ExtraPipFlags="--no-deps" />
44+
<PythonWheel Name="oras-0.1.30-py3-none-any.whl" ExtraPipFlags="--no-deps" />
45+
<PythonWheel Name="referencing-0.36.2-py3-none-any.whl" ExtraPipFlags="--no-deps" />
46+
<PythonWheel Name="requests-2.33.0-py3-none-any.whl" ExtraPipFlags="--no-deps" />
47+
<PythonWheel Name="typing_extensions-4.12.2-py3-none-any.whl" ExtraPipFlags="--no-deps" />
48+
<PythonWheel Name="urllib3-2.7.0-py3-none-any.whl" ExtraPipFlags="--no-deps" />
4949

50-
<!-- Pure python implementation of rpds-py for offline installation or behind a
50+
<!-- Pure python implementation of rpds-py for offline installation or behind a
5151
firewall.
5252
This intentionally and necessarily masks possible installation of the real rpds-py package
5353
for the sake of working in a container environment with durable %SYS. -->
54-
<FileCopy Name="modules/python/rpds.py" Target="${mgrdir}python/rpds.py" />
55-
<FileCopy Name="ipm" Target="${ipmdir}bin/ipm" />
56-
<FileCopy Name="iriscli" Target="${ipmdir}bin/iriscli" />
57-
<Invoke Class="IPM.Installer" Method="InstallScripts" Phase="Activate" When="After" />
58-
</Module>
59-
</Document>
60-
</Export>
54+
<FileCopy Name="modules/python/rpds.py" Target="${mgrdir}python/rpds.py" />
55+
<FileCopy Name="ipm" Target="${ipmdir}bin/ipm" />
56+
<FileCopy Name="iriscli" Target="${ipmdir}bin/iriscli" />
57+
<Invoke Class="IPM.Installer" Method="InstallScripts" Phase="Activate" When="After" />
58+
</Module>
59+
</Document>
60+
</Export>

src/cls/IPM/General/SemanticVersionExpression.cls

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,16 @@ Method IsSatisfiedBy(version As %IPM.General.SemanticVersion) As %Boolean
117117
return 0
118118
}
119119

120-
Method And(versionExpression As %IPM.General.SemanticVersionExpression) As %IPM.General.SemanticVersionExpression
120+
/// Combines two version expressions with AND. <br />
121+
/// Throw an error if versions are incompatible. <br />
122+
/// If <var>moduleName</var> and <var>constraintList</var> are provided, throws a descriptive error
123+
/// listing all conflicting requirements if the expressions are incompatible. <br />
124+
/// <p>
125+
/// <b>Parameters:</b><br />
126+
/// <var>versionExpression</var> - Version expression to AND with<br />
127+
/// <var>moduleName</var> - Name of the module whose versions are being resolved (for error message)<br />
128+
/// <var>constraintList</var> - %List of "source: constraint" strings accumulated so far (for error message)<br />
129+
Method And(versionExpression As %IPM.General.SemanticVersionExpression, moduleName As %String = "", constraintList As %List = "") As %IPM.General.SemanticVersionExpression
121130
{
122131
if versionExpression.Expression '= "" {
123132
if (..AnyOf.Count() = 0) {
@@ -146,6 +155,9 @@ Method And(versionExpression As %IPM.General.SemanticVersionExpression) As %IPM.
146155
set resolvedString = ..ToResolvedString()
147156
if (resolvedString = "") {
148157
set msg = $$$FormatText("No overlapping versions in AND operation for %1 and %2",current.ToResolvedString(),versionExpression.ToResolvedString())
158+
if (moduleName '= "") && (constraintList '= "") {
159+
set msg = msg_" for module "_moduleName_" with constraints: "_$listtostring(constraintList,", ")
160+
}
149161
$$$ThrowStatus($$$ERROR($$$GeneralError,msg))
150162
}
151163
set i%Expression = resolvedString

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,19 @@ ClassMethod Intersect(
247247
$$$ThrowStatus($$$ERROR($$$GeneralError,$$$FormatText("Exact version matches do not agree: %1 and %2", firstExact.ToResolvedString(), comp.ToResolvedString())))
248248
}
249249
}
250-
// All exact matches agree; return that version after converting to range
250+
// Verify the exact version satisfies all lower and upper bounds from both ranges
251+
set exactVersion = firstExact.ExtractToSemanticVersion()
252+
for i = 1:1:lowerBounds.Count() {
253+
if 'lowerBounds.GetAt(i).Evaluate(exactVersion) {
254+
return ""
255+
}
256+
}
257+
for i = 1:1:upperBounds.Count() {
258+
if 'upperBounds.GetAt(i).Evaluate(exactVersion) {
259+
return ""
260+
}
261+
}
262+
// All exact matches agree and satisfy all bounds; return that version after converting to range
251263
set sc = ..FromString(firstExact.ToString(), .firstExactRange)
252264
$$$ThrowOnError(sc)
253265
return firstExactRange

src/cls/IPM/Storage/Module.cls

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,10 +1014,7 @@ Method BuildDependencyGraph(
10141014
// Caches
10151015
.repoSearchCache, .manifestCache, .moduleCache,
10161016
pCoUpdatingModules)
1017-
// For fuzzy, continue even if one fails
1018-
if $$$ISERR(sc) {
1019-
// Error will surface later if unresolvable
1020-
}
1017+
$$$ThrowOnError(sc)
10211018
}
10221019
}
10231020

@@ -1102,8 +1099,8 @@ Method ProcessSingleDependencyIterative(
11021099

11031100
set sc = ##class(%IPM.General.SemanticVersionExpression).FromString(otherReq,.tExpr)
11041101
$$$ThrowOnError(sc)
1105-
set searchExpr = searchExpr.And(tExpr)
11061102
set constraintList = constraintList_$listbuild(sourcePath_": "_otherReq)
1103+
set searchExpr = searchExpr.And(tExpr, pDep.Name, constraintList)
11071104
}
11081105

11091106
if 'pIgnoreInstalledModules {
@@ -1123,8 +1120,8 @@ Method ProcessSingleDependencyIterative(
11231120
pDep.Name,otherDepsList,,.installedReqExpr,.installedConstraintList
11241121
)
11251122
$$$ThrowOnError(sc)
1126-
set searchExpr = searchExpr.And(installedReqExpr)
11271123
set constraintList = constraintList_installedConstraintList
1124+
set searchExpr = searchExpr.And(installedReqExpr, pDep.Name, constraintList)
11281125
}
11291126

11301127
// Check if we already have a satisfactory version
@@ -1158,8 +1155,8 @@ Method ProcessSingleDependencyIterative(
11581155
set rangeString = ">="_localObj.VersionString
11591156
set tSC = ##class(%IPM.General.SemanticVersionExpression).FromString(rangeString,.currentlyInstalledExpr)
11601157
$$$ThrowOnError(tSC)
1161-
set searchExpr = searchExpr.And(currentlyInstalledExpr)
11621158
set constraintList = constraintList_$listbuild(localObj.Name_": "_rangeString_" (currently installed, downgrade not permitted)")
1159+
set searchExpr = searchExpr.And(currentlyInstalledExpr, pDep.Name, constraintList)
11631160
}
11641161
}
11651162

@@ -1252,6 +1249,11 @@ Method ProcessSingleDependencyIterative(
12521249
#dim subDep As %IPM.Storage.ModuleReference
12531250
set subDep = moduleObj.Dependencies.GetAt(k)
12541251

1252+
// Skip out-of-scope dependencies (same logic as BuildDependencyGraph main loop)
1253+
if '..HasScope(pPhases, subDep.Scope) {
1254+
continue
1255+
}
1256+
12551257
// Check if this dependency conflicts with what we already have
12561258
if $data(dependencyGraph(subDep.Name), existingInfo) {
12571259
set existingVersion = $listget(existingInfo, 3)
@@ -1276,7 +1278,9 @@ Method ProcessSingleDependencyIterative(
12761278
}
12771279
}
12781280

1279-
// If no candidate worked, use last one and let errors surface later
1281+
// If no valid candidate was found via quick validation, fall through with the last
1282+
// candidate. The real constraint conflict will surface when BFS processes its
1283+
// sub-dependencies and the constraint accumulation detects the incompatibility.
12801284
if 'resultFound {
12811285
merge pDependencyGraph = dependencyGraph
12821286
set pWorkQueue(pDepth + 1, pDep.Name) = $listbuild(pModulePath_$listbuild(pDep.Name), pDep.Scope)

src/cls/IPM/Utils/Module.cls

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ ClassMethod GetRequiredVersionExpression(
390390
$$$ThrowOnError(sc)
391391
set version = result.%Get("Version")
392392
$$$ThrowOnError(##class(%IPM.General.SemanticVersionExpression).FromString(version,.versionExpr))
393-
set expression = expression.And(versionExpr)
394393
set sourceList = sourceList_$listbuild($listtostring(result.%Get("ModuleNames"),", ")_": "_version)
394+
set expression = expression.And(versionExpr, moduleName, sourceList)
395395
}
396396
$$$ThrowOnError(sc)
397397

@@ -419,8 +419,8 @@ ClassMethod GetRequiredVersionExpression(
419419
// If this is a new required version expression, add a new item to the list
420420
if (newVersion) {
421421
$$$ThrowOnError(##class(%IPM.General.SemanticVersionExpression).FromString(version, .versionExpr))
422-
set expression = expression.And(versionExpr)
423422
set sourceList = sourceList _ $listbuild(requiringModuleName _ ": " _ version)
423+
set expression = expression.And(versionExpr, moduleName, sourceList)
424424
}
425425
}
426426
}

0 commit comments

Comments
 (0)