diff --git a/CHANGELOG.md b/CHANGELOG.md index d3c31708..c9604e65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased - 0.9.2+snapshot] ### Fixed +- #682 When enabling IPM in a namespace using local IPM caches, check for existence of `/lib/ipm/` beforing querying it. +- #682 Use more standard wording of mapping when enabling IPM - #681 Convert specified namespaces to upper case for `enable` and `unmap` commands. - #680 Always export static files (README.md, LICENSE, requirements.txt) if existent +### Changed +- #682 When downloading IPM via the `enable` command from a remote registry, allow user to pass in the registry name (or get the only existent one), instead of the deployment enabled registry. + ## [0.9.1] - 2024-12-18 ### Added diff --git a/src/cls/IPM/Main.cls b/src/cls/IPM/Main.cls index 5c79cfb5..e7446bce 100644 --- a/src/cls/IPM/Main.cls +++ b/src/cls/IPM/Main.cls @@ -655,6 +655,7 @@ generate /my/path -export 00000,PacketName2,IgnorePacket2^00000,PacketName3,Igno + @@ -662,16 +663,16 @@ generate /my/path -export 00000,PacketName2,IgnorePacket2^00000,PacketName3,Igno enable -map -globally - - enable -v 0.3.4 -q -ns NS1,NS2,NS3 + + enable -v 0.3.4 -q -ns NS1,NS2,NS3 -remote registry - + enable -globally - + enable -v latest -globally - + enable -v latest -allow-upgrade NS1,NS2,NS3 @@ -2874,6 +2875,7 @@ ClassMethod EnableIPM(ByRef pCommandInfo) Set namespaces = $$$ucase(namespaces) Set allowUpgrade = $$$HasModifier(pCommandInfo,"allow-upgrade") Set mapRepos = $$$HasModifier(pCommandInfo,"repos") + Set remoteName = $$$GetModifier(pCommandInfo,"remote") Set useLocal = 1 // var to store the final decision of whether to use local manifest or get from server Set targetVersion = "" // var to store the final version of IPM to be installed Kill targetNamespaces // multi-dim array to store the final namespaces that need to install IPM @@ -2931,11 +2933,11 @@ ClassMethod EnableIPM(ByRef pCommandInfo) Continue } If 'quiet { - Write !,"Mapping %IPM package in "_namespace_" equivalently to "_initNamespace + Write !,"Mapping %IPM package in "_initNamespace_" equivalently to "_namespace } $$$ThrowOnError(##class(%IPM.Utils.Build).MapPackageEquivalently("%IPM",initNamespace,namespace)) If 'quiet { - Write !,"Mapping %IPM.* routines in "_namespace_" equivalently to "_initNamespace + Write !,"Mapping %IPM.* routines in "_initNamespace_" equivalently to "_namespace } $$$ThrowOnError(##class(%IPM.Utils.Build).MapRoutineEquivalently("%IPM.*",initNamespace,,namespace)) } @@ -2967,7 +2969,7 @@ ClassMethod EnableIPM(ByRef pCommandInfo) Continue } If 'quiet { - Write !,"Mapping IPM repository in "_namespace_" equivalently to "_initNamespace + Write !,"Mapping IPM repository in "_initNamespace_" equivalently to "_namespace } For suffix = "D", "S", "I" { $$$ThrowOnError(##class(%IPM.Utils.Build).MapGlobalEquivalently("IPM.Repo.Definition"_suffix, initNamespace, namespace)) @@ -2995,23 +2997,25 @@ ClassMethod EnableIPM(ByRef pCommandInfo) Set sc = statement.%PrepareClassQuery("%File", "FileSet") $$$ThrowOnError(sc) // Valid IPM installation manifest should be of format: ipm-0.0.1.xml - Set resultSet = statement.%Execute(XMLDir, "ipm-*.xml") - If (resultSet.%SQLCODE < 0) { - $$$ThrowSQLIfError(resultSet.%SQLCODE,resultSet.%Message) - } - Kill ipmLocalArray // multi-dim array to store local ipm manifests; ipmLocalArray()= - Kill menuList // menu to let user choose which version of local ipm to install (if not in quiet mode and no version is specified) - While resultSet.%Next(.sc) { - $$$ThrowOnError(sc) - Set fileName = resultSet.%Get("Name") - If (resultSet.%Get("Type") = "F") { - // get the exact version string from filename - Set exactIPMVer = $Piece($Piece(fileName, "ipm-", 2, *), ".xml", 1, *-1) - Set ipmLocalArray(exactIPMVer) = fileName - Set menuList($Increment(menuList)) = exactIPMVer_" (local version)" + If ##class(%File).DirectoryExists(XMLDir) { + Set resultSet = statement.%Execute(XMLDir, "ipm-*.xml") + If (resultSet.%SQLCODE < 0) { + $$$ThrowSQLIfError(resultSet.%SQLCODE,resultSet.%Message) + } + Kill ipmLocalArray // multi-dim array to store local ipm manifests; ipmLocalArray()= + Kill menuList // menu to let user choose which version of local ipm to install (if not in quiet mode and no version is specified) + While resultSet.%Next(.sc) { + $$$ThrowOnError(sc) + Set fileName = resultSet.%Get("Name") + If (resultSet.%Get("Type") = "F") { + // get the exact version string from filename + Set exactIPMVer = $Piece($Piece(fileName, "ipm-", 2, *), ".xml", 1, *-1) + Set ipmLocalArray(exactIPMVer) = fileName + Set menuList($Increment(menuList)) = exactIPMVer_" (local version)" + } } + $$$ThrowOnError(sc) } - $$$ThrowOnError(sc) If ('$DATA(menuList) && localOnly) { $$$ThrowOnError($$$ERROR($$$GeneralError,"No ipm-.xml installer file is found locally in directory: "__XMLDir)) @@ -3057,7 +3061,7 @@ ClassMethod EnableIPM(ByRef pCommandInfo) Set localIPMCount = $Get(menuList, 0) If 'localOnly { // Add remote version options to menuList - Set server = ##class(%IPM.Repo.Remote.Definition).DeploymentServerOpen(1,,.sc) + Set server = ##class(%IPM.Repo.Remote.Definition).GetOne(remoteName, .sc) $$$ThrowOnError(sc) If $IsObject(server) { Set latestVersion = server.GetPackageService().GetLatestModuleVersion($$$IPMModuleName) @@ -3128,7 +3132,7 @@ ClassMethod EnableIPM(ByRef pCommandInfo) // 4. Now that we got all the namespaces that need to install IPM, do the actual installation If 'useLocal { - Set server = ##class(%IPM.Repo.Remote.Definition).DeploymentServerOpen(1,,.sc) + Set server = ##class(%IPM.Repo.Remote.Definition).GetOne(remoteName, .sc) $$$ThrowOnError(sc) If $IsObject(server) { Set packageService = server.GetPackageService() @@ -3137,7 +3141,7 @@ ClassMethod EnableIPM(ByRef pCommandInfo) Set ipmRef.Name = $$$IPMModuleName If (targetVersion = "latest") { // convert latest to semantic version so that it can be undestood by ModuleInfo class - Set server = ##class(%IPM.Repo.Remote.Definition).DeploymentServerOpen(1,,.sc) + Set server = ##class(%IPM.Repo.Remote.Definition).GetOne(remoteName, .sc) $$$ThrowOnError(sc) If $IsObject(server) { Set targetVersion = server.GetPackageService().GetLatestModuleVersion($$$IPMModuleName) diff --git a/src/cls/IPM/Repo/Definition.cls b/src/cls/IPM/Repo/Definition.cls index 3d32c0a6..172d77f3 100644 --- a/src/cls/IPM/Repo/Definition.cls +++ b/src/cls/IPM/Repo/Definition.cls @@ -211,6 +211,29 @@ SELECT Name FROM %IPM_Repo.Definition ORDER BY %IPM_Repo.Definition_SortOrder(ID) DESC } +/// If a name is provided, return the repo of this type with the name +/// Otherwise, return the only repo of this type in the system +/// If there are multiple repos (or no repos) of this type, return an error +ClassMethod GetOne(name As %String = "", Output sc As %Status) As %IPM.Repo.Definition +{ + If $Get(name) '= "" { + Quit ..ServerDefinitionKeyOpen(name, , .sc) + } + Set tablename = $$$comClassKeyGet($classname(), $$$cCLASSsqlqualifiednameQ) + Set query = "SELECT id FROM " _ tablename // should be safe from SQL injection, since $classname() is safe + Set rs = ##class(%SQL.Statement).%ExecDirect(, query) + $$$ThrowSQLIfError(rs.%SQLCODE, rs.%Message) + Set list = "" + While rs.%Next() { + Set list = list _ $lb(rs.%Get("id")) + } + If $ListLength(list) = 1 { + Quit ..%OpenId($ListGet(list, 1), , .sc) + } + Set sc = $$$ERROR($$$GeneralError, "Unable to find a unique repo of type " _ $CLASSNAME()) + Quit "" +} + Storage Default {