Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow mapping and unmapping repos & improve zpm error output #663

Merged
merged 15 commits into from
Dec 18, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ jobs:
CONTAINER=$(docker run -d --rm -v $(pwd):/home/irisowner/zpm/ containers.intersystems.com/intersystems/${{ needs.prepare.outputs.main }} --check-caps false)
sleep 5; docker exec $CONTAINER /usr/irissys/dev/Cloud/ICM/waitISC.sh
docker exec -i $CONTAINER iris session iris -UUSER << EOF
set sc=##class(%SYSTEM.OBJ).Load("/home/irisowner/zpm/Installer.cls","ck")
set sc=##class(%SYSTEM.OBJ).Load("/home/irisowner/zpm/preload/cls/IPM/Installer.cls","ck")
set sc=##class(IPM.Installer).setup("/home/irisowner/zpm/",3)
zpm "repo -r -name registry -url ""https://pm.community.intersystems.com/"" -username ${{ secrets.REGISTRY_USERNAME }} -password ${{ secrets.REGISTRY_PASSWORD }}":1
zpm "publish zpm -v":1
Expand Down
19 changes: 12 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased - 0.9.0+snapshot]
## [0.9.1] - 2024-12-17

### Added
- #663 Added support for mapping of repository settings along with, or in addition to, IPM package and routines
- #663 Added functionality to always unmap repository settings when IPM package and routines are unmapped
- #663 Added support for unmapping of repository settings alone
- #663 Added support for `enable -community`, which resets repository settings to default and maps IPM along with repo settings globally

### Fixed
- #663 Improved error output and instructions in the language extension when "zpm" is run from a namespace without IPM

## [0.9.0] - 2024-12-16

### Added
- #364 Added ability to restrict the installation to IRIS or IRIS for Health platform to the SystemRequirements attribute
Expand Down Expand Up @@ -64,11 +75,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #606: Don't put garbage folders in tar archive
- #652: Don't create extra needless mappings (could cause deadlock with parallel installation of dependencies)

### Security
-

### Removed
-

### Deprecated
- #593 CSPApplication is deprecated in favor of WebApplication. User will be warned when installing a package containing CSPApplication.
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
# ObjectScript Package Manager Client - ZPM
# InterSystems Package Manager - IPM

Helps to install ObjectScript classes and routines, globals, Embedded Python modules, CSP and Frontend packages, and any files into InterSystems IRIS published on the official [ZPM Registry](https://pm.community.intersystems.com/packages/-/all) or private ZPM registry of your own.
Helps to install ObjectScript classes and routines, globals, Embedded Python modules, CSP and Frontend packages, and any files into InterSystems IRIS published on the official [Registry](https://pm.community.intersystems.com/packages/-/all) or private registry of your own.

## Documentation
* [The official documenation in the wiki](https://github.com/intersystems-community/zpm/wiki/)
* [Articles on the InterSystems Developer Community](https://community.intersystems.com/tags/objectscript-package-manager-zpm)
* [Videos on YouTube](https://www.youtube.com/playlist?list=PLKb2cBVphNQRcmxt4LtYDyLJEPfF4X4-4)

## Compatibility Note
With the release of IPM v0.9.0 on Dec 2024, IPM is no longer mapped across namespaces.
This is an intentional change so that users can have different IPM versions and configurations in different namespaces.
If you install IPM on an instance without the legacy 0.7.x version, IPM is only installed to the current namespace.

* To retain the old behavior where %IPM routines and classes mapped across all namespaces, run `zpm "enable -map -globally`. This is automatically performed when upgrading from a legacy version and can be undone by running `zpm "unmap -globally"`.
* You can optionally choose to map IPM repositories across namespaces with `zpm "enable -map -repos -namespaces NS1,NS2,NS3` or `zpm "enable -map -repos -globally`. Repositories are only mapped if %IPM classes and routines are also mapped from the same namespace.
* As a convenience command, `zpm "enable -community"` will make IPM behave essentially the same as legacy versions (v0.7.x) by setting up the the community registry and maping %IPM routines and classes, as well IPM repository settings to all namespaces.

## Installing ObjectScript Package Manager Client:

0. Use one-liner in terminal call or programmatically:
```
s r=##class(%Net.HttpRequest).%New(),r.Server="pm.community.intersystems.com",r.SSLConfiguration="ISC.FeatureTracker.SSL.Config" d r.Get("/packages/zpm/latest/installer"),$system.OBJ.LoadStream(r.HttpResponse.Data,"c")
s version="latest" s r=##class(%Net.HttpRequest).%New(),r.Server="pm.community.intersystems.com",r.SSLConfiguration="ISC.FeatureTracker.SSL.Config" d r.Get("/packages/zpm/"_version_"/installer"),$system.OBJ.LoadStream(r.HttpResponse.Data,"c")
```
**If you want the legacy behavior of mapping IPM classes, routines, and repository settings to all namespaces, run `zpm "enable -community"` after installing IPM. See `zpm "help enable"` for details.**
**In a CI script, for deterministic behavior, you should replace version="latest" with the IPM version you wish to use.**

OR:

Expand Down Expand Up @@ -65,4 +75,4 @@ Here is the [alternative supported folder structure.](https://openexchange.inter

## Support and Collaboration
ObjectScript Package Manager is a community supported project and thus open to collaboration via Pull Requests.
Issues and feature requests [are very welcome](https://github.com/intersystems-community/zpm/issues)
Issues and feature requests [are very welcome](https://github.com/intersystems-community/zpm/issues)
93 changes: 87 additions & 6 deletions src/cls/IPM/Main.cls
Original file line number Diff line number Diff line change
Expand Up @@ -654,8 +654,10 @@ generate /my/path -export 00000,PacketName2,IgnorePacket2^00000,PacketName3,Igno
<modifier name="local-only" description="If specified, only local artifacts will be used for installation. By default, this modifier is not set and will not limit to local artifacts." />
<modifier name="allow-upgrade" description="If specified, will also check for IPM version in specified namespaces and upgrade if version is lower than the target version. By default, this modifier is not ste and will not allow upgrade." />
<modifier name="map" aliases="m" description="If specified, will map IPM code from the current namespace-default code database rather than installing a separate copy." />
<modifier name="repos" aliases="r" description="If specified, will map repository settings across namespaces. Must be used together with -map." />
<modifier name="quiet" aliases="q" description="Quiet mode. By default, this modifier is not set and will display the contents onto the terminal/caller command line." />
<modifier name="preview" aliases="p" description="Preview what will be changed without actually making the changes." />
<modifier name="community" description="If specified, will reset repository to the community repository and map IPM to all namespaces along with the repository settings. This is functionaly equivalent to &quot;repo -delete-all&quot;, &quot;repo -reset-defaults&quot;, and &quot;enable -map -repos -globally&quot;. With this modifier, all other modifiers will be ignored."/>
<!-- Examples -->
<example description="Make IPM available in all namespaces (including %SYS) by mapping the version in the current namespace default routine database. Namespace-specific installation will override this.">
enable -map -globally
Expand All @@ -672,15 +674,21 @@ generate /my/path -export 00000,PacketName2,IgnorePacket2^00000,PacketName3,Igno
<example description="Install or upgrade IPM to latest IPM version in namespaces: NS1, NS2, NS3.">
enable -v latest -allow-upgrade NS1,NS2,NS3
</example>
<example description="Reset repository to the community repository and map IPM to all namespaces along with the repository settings.">
enable -community
</example>
</command>

<command name="unmap">
<summary>Unmap %IPM package and routines in specified namespaces.</summary>
<description>
Unmap %IPM package and routines in specified namespaces. Will Skip non-mapped namespaces.
If repository settings are mapped, will also unmap repository settings.
</description>
<modifier name="namespaces" aliases="ns" value="true" description="Comma-separated namespaces in which IPM mapping needs to be deleted."/>
<modifier name="globally" aliases="g" description="Will unmap IPM in all explicit namespaces that currently do not have IPM installed."/>
<modifier name="quiet" aliases="q" description="Quiet mode. By default, this modifier is not set and will display the contents onto the terminal/caller command line." />
<modifier name="repos-only" description="If specified, will only unmap repository settings across namespaces. This doesn't affect mapping of IPM packages and routines." />
<example description="Unmap IPM from namespaces NS1, NS2, NS3.">
unmap -ns NS1,NS2,NS3
</example>
Expand Down Expand Up @@ -2803,9 +2811,12 @@ ZPM(pArgs...)
Set found = 0
While rs.%Next() {
Set $Namespace = $Zstrip(rs.%Get("Nsp"), "<>WC")
If $System.CLS.IsMthd("%IPM.Main", "Shell") {
// Some I4H containers come with %IPM.Main but the "version" command doesn't work ?!
Copy link
Contributor

Choose a reason for hiding this comment

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

This would probably be those with IPM mapped from HSLIB/HSCUSTOM, on older versions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, for example, containers.intersystems.com/intersystems/irishealth-community:2024.1 has this problem in HSLIB.

If $System.CLS.IsMthd("%IPM.Main", "Shell") && ($Namespace '= "HSLIB") && ($Namespace '= "HSSYS") {
Write !, "Change namepace to one of the following to run the ""zpm"" command"
Do ##class(%IPM.Main).Shell("version")
Write !, "If you want to map IPM globally, switch to one of the namespaces above and run: zpm ""enable -map -globally""."
Write !, "If you want to reset repository and map IPM globally along with repository settings, switch to one of the namespaces above and run: zpm ""enable -community""."
Set found = 1
Quit
}
Expand Down Expand Up @@ -2840,6 +2851,19 @@ ClassMethod EnableIPM(ByRef pCommandInfo)
Write !,($namespace)_"> "_$$$FormattedLine($$$Green,$$$IPMModuleName_" ")_modDef.VersionString
}

If $$$HasModifier(pCommandInfo,"community") {
For cmd = "repo -delete-all", "repo -reset-defaults", "enable -map -repos -globally" {
Write !!, "RUNNING command: """, cmd, """"
Do ..ShellInternal(cmd, .exc)
If exc '= $$$NULLOREF {
Write !, $$$FormattedLine($$$Red, $$$FormatText("Error running command ""%1"" - %2", cmd, exc.DisplayString()))
} Else {
Write !, $$$FormattedLine($$$Green, $$$FormatText("Command ""%1"" finished successfull", cmd))
}
}
Return
}

Set quiet = $$$HasModifier(pCommandInfo,"quiet")
Set preview = $$$HasModifier(pCommandInfo,"preview")
Set map = $$$HasModifier(pCommandInfo,"map")
Expand All @@ -2848,6 +2872,7 @@ ClassMethod EnableIPM(ByRef pCommandInfo)
Set version = $$$GetModifier(pCommandInfo, "version")
Set namespaces = $$$GetModifier(pCommandInfo, "namespaces")
Set allowUpgrade = $$$HasModifier(pCommandInfo,"allow-upgrade")
Set mapRepos = $$$HasModifier(pCommandInfo,"repos")
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
Expand All @@ -2866,6 +2891,9 @@ ClassMethod EnableIPM(ByRef pCommandInfo)
If map && 'globally && (namespaces = "") {
$$$ThrowOnError($$$ERROR($$$GeneralError,"If mapping from the current namespace's routine database with -map, must specify either -globally or a list of namespaces with -ns"))
}
If ('map) && (mapRepos) {
$$$ThrowOnError($$$ERROR($$$GeneralError,"Cannot specify -repos without -map"))
}

If map {
If globally {
Expand All @@ -2884,13 +2912,15 @@ ClassMethod EnableIPM(ByRef pCommandInfo)
} Else {
Set namespaces = $ListFromString(namespaces)
}

// First try to map IPM itself
Set pointer = 0
While $ListNext(namespaces,pointer,namespace) {
Set namespace = $Zstrip(namespace, "<>WC")
Set $Namespace = namespace
If ..IPMInstalled() {
If 'quiet || preview {
Write !,"Skipping "_namespace_" - IPM already installed."
Write !,"Skipping IPM mapping of "_namespace_" - IPM already installed."
}
Continue
}
Expand All @@ -2908,6 +2938,42 @@ ClassMethod EnableIPM(ByRef pCommandInfo)
}
$$$ThrowOnError(##class(%IPM.Utils.Build).MapRoutineEquivalently("%IPM.*",initNamespace,,namespace))
}

// Then try to map repositories if -repos is specified
If mapRepos {
Do ..GetMapInfo(.isMappedFrom)
Set pointer = 0
While $ListNext(namespaces,pointer,namespace) {
Set namespace = $ZStrip(namespace, "<>WC")
// If IPM is not mapped from source namespace, skip repo mapping
If $Get(isMappedFrom(namespace)) '= initNamespace {
If 'quiet || preview {
Write !,"Skipping repository mapping of "_namespace_" - IPM not mapped from source namespace."
}
Continue
}
// If repository are already present, also skip repo mapping avoid override
Set $Namespace = namespace
If $Data(^IPM.Repo.DefinitionD) \ 2 {
If 'quiet || preview {
Write !,"Skipping repository mapping of "_namespace_" - IPM repositories found."
}
Continue
}
Set $Namespace = initNamespace
If preview {
Write !,"Would add IPM repository mappings to "_namespace
Continue
}
If 'quiet {
Write !,"Mapping IPM repository in "_namespace_" equivalently to "_initNamespace
}
For suffix = "D", "S", "I" {
$$$ThrowOnError(##class(%IPM.Utils.Build).MapGlobalEquivalently("IPM.Repo.Definition"_suffix, initNamespace, namespace))
}
}
}

Set $Namespace = initNamespace
If preview {
Write !,"Preview mode; no configuration changes were made."
Expand Down Expand Up @@ -3141,6 +3207,7 @@ ClassMethod UnmapIPM(ByRef pCommandInfo)
Set globally = $$$HasModifier(pCommandInfo,"globally")
Set namespaces = $ListFromString($$$GetModifier(pCommandInfo,"namespaces"), ",")
Set verbose = '$$$HasModifier(pCommandInfo,"quiet")
set reposOnly = $$$HasModifier(pCommandInfo,"repos-only")
// Sanity check
If (globally && (namespaces '= "")) {
$$$ThrowOnError($$$ERROR($$$GeneralError,"Cannot specify namespaces and global unmap flag at the same time."))
Expand All @@ -3161,7 +3228,11 @@ ClassMethod UnmapIPM(ByRef pCommandInfo)
}
}
If verbose {
Write !,"Will attempt to unmap %IPM package and routines from: "_ $ListToString(namespaces, ", ")
If reposOnly {
Write !,"Will attempt to unmap IPM repository settings from: "_ $ListToString(namespaces, ", ")
} Else {
Write !,"Will attempt to unmap IPM package, routines, and repository settings from: "_ $ListToString(namespaces, ", ")
}
}

// Gather namespaces where %IPM is mapped into
Expand All @@ -3176,11 +3247,21 @@ ClassMethod UnmapIPM(ByRef pCommandInfo)
}
Continue
}

If verbose {
Write !,"Unmapping %IPM package and routines from "_ns_" (mapped from "_src_")"
Write !,"Unmapping repository settings from "_ns_" (mapped from "_src_")"
}
For suffix = "D", "S", "I" {
$$$ThrowOnError(##class(%IPM.Utils.Module).RemoveGlobalMapping(ns, "IPM.Repo.Definition"_suffix))
}

If 'reposOnly {
If verbose {
Write !,"Unmapping IPM package and routines from "_ns_" (mapped from "_src_")"
}
$$$ThrowOnError(##class(%IPM.Utils.Module).RemovePackageMapping(ns, "%IPM"))
$$$ThrowOnError(##class(%IPM.Utils.Module).RemoveRoutineMapping(ns, "%IPM.*"))
}
$$$ThrowOnError(##class(%IPM.Utils.Module).RemovePackageMapping(ns, "%IPM"))
$$$ThrowOnError(##class(%IPM.Utils.Module).RemoveRoutineMapping(ns, "%IPM.*"))
}
}

Expand Down
Loading