Article
· Aug 29, 2019 4m read

Introducing InterSystems ObjectScript Package Manager

Hi Developers!

Often when we develop some library, tool, package, whatever on InterSystems ObjectScript we have a question, how we deploy this package on the target machine?

Also, we often expect that some other libraries already installed, so our package depends on them, and often on some particular version of it.

When you code on javascript, python, etc the role of packages deployment with dependency management takes package manager.

So, I'm pleased to announce that InterSystems ObjectScript Package Manager available!

CAUTION!

Official Disclaimer.

InterSystems ObjectScript Package Manager server situated on pm.community.intersystems.com and InterSystems ObjectScript Package Manager client installable from pm.community.intersystems.com or from Github are not supported by InterSystems Corporation and are presented as-is under MIT License. Use it, develop it, contribute to it on your own risk.

How does it work?

InterSystems ObjectScript Package Manager consists of two parts. There is a Package Manager server which hosts ObjectScript packages and exposes API for ZPM clients to deploy and list packages. Today we have a Developers Community Package Manager server available at pm.community.intersystems.com

You can install any package into InterSystems IRIS via ZPM client installed first into IRIS system.

How to use InterSystems Package Manager?

1. Check the list of available packages

Open https://pm.community.intersystems.com/packages/-/all to see the list of currently available packages. 

[{"name":"analyzethis","versions":["1.1.1"]},{"name":"deepseebuttons","versions":["0.1.7"]},{"name":"dsw","versions":["2.1.35"]},{"name":"holefoods","versions":["0.1.0"]},{"name":"isc-dev","versions":["1.2.0"]},{"name":"mdx2json","versions":["2.2.0"]},{"name":"objectscript","versions":["1.0.0"]},{"name":"pivotsubscriptions","versions":["0.0.3"]},{"name":"restforms","versions":["1.6.1"]},{"name":"thirdpartychartportlets","versions":["0.0.1"]},{"name":"webterminal","versions":["4.8.3"]},{"name":"zpm","versions":["0.0.6"]}]

Every package has the name and the version.

If you want to install one on InterSystems IRIS you need to have InterSystems ObjectScript Package Manager Client aka ZPM client installed first.

2. Install Package Manager client

Get the release of the ZPM client from ZPM server: https://pm.community.intersystems.com/packages/zpm/latest/installer

It is ObjectScript package in XML, so it could be installed by importing into classes via Management Portal, or by terminal:

USER>Do $System.OBJ.Load("/yourpath/zpm.xml","ck")

once installed it can be called from any Namespace cause it installs itself in %SYS as Z-package.

3. Working with ZPM client

Zpm client has CLI interface. Call zpm in any  namespace like:

USER>zpm

zpm: USER>

Call help to see the list of all the available commands.

Check the list of currently available packages on ZPM server (pm.community.intersystems.com):

zpm: USER>repo -list-modules -n registry

deepseebuttons 0.1.7 dsw 2.1.35 holefoods 0.1.0 isc-dev 1.2.0 mdx2json 2.2.0 objectscript 1.0.0 pivotsubscriptions 0.0.3 restforms 1.6.1 thirdpartychartportlets 0.0.1 webterminal 4.8.3 zpm 0.0.6

Installing a Package

To install the package call

install package-name version

This will install the package with all the dependencies. You can omit version to get the latest package. Here is how to install the latest version of web terminal:

zpm: USER> install webterminal

How to know what is already installed?

Call list command:

zpm:USER> list

zpm 0.0.6

webterminal 4.8.3

Uninstall the Package

zpm: USER> uninstall webterminal

Supported InterSystems Data Platforms

Currently, ZPM supports InterSystems IRIS and InterSystems IRIS for Health.

I want my package to be listed on Package Manager

It's possible. The requirements are:

  • Code should work in InterSystems IRIS
  • You need to have module.xml in the root.

Module.xml is the file which describes the structure of the package and what is need to be set up on the deployment phase. Examples of module.xml could be very simple, e.g.

ObjectScript Example

Or relatively simple:

Samples BI (previously known as HoleFoods),

Web Terminal

Module with dependencies:

DeepSee Web expects MDX2JSON to be installed and this is how it described in module.xml:
DeepSeeWeb


If you want your application to be listed on the Community Package Manager comment in this post or DM me.

Collaboration and Support

ZPM server source code is not available at the moment and will be available soon.

ZPM client source code is available here and is currently supported by InterSystems Developers Community and is not supported by InterSystems Corporation. You are welcome to submit issues and pull requests.

Roadmap

The current roadmap is:

  • introduce Open Exchange support,
  • introduce the automation for package updating and uploading;
  • open source ZPM server.

Stay tuned and develop your InterSystems ObjectScript packages on InterSystems IRIS!

Discussion (19)3
Log in or sign up to continue

Interesting... however I have some questions:

1 - Is there any plans to automatize the module.xml generation by using something like a Wizard?

2 - Is there any plans to support non-specific dependency versions like NPM does?

3 - Is it possible to run pre/post-install scripts as well? Kind of what installer classes do.

4 - Is also possible to use the module.xmlto provide a contextual root? this way it would be used to run UnitTests without the need of defining (or overwriting) the UnitTestRoot global. I already did it with Port, so it's not hard to implement as you basically have to overwrite the Root method:

Class Port.UnitTest.Manager Extends %UnitTest.Manager
{

ClassMethod Root() As %String
{
  // This provides us the capability to search for tests unrelated to ^UnitTestRoot.
  return ##class(%File).NormalizeFilename(##class(Port.Configuration).GetWorkspace())
}

ClassMethod RunTestsFromWorkspace(projectName As %String, targetClass As %String = "", targetMethod As %String = "", targetSpec As %String = "") As %Status
{

  set recursive = "recursive"
  set activeProject = $get(^||Port.Project)
  set ^||Port.Project = projectName

  if targetClass '= "" set target = ##class(Port.UnitTest.Util).GetClassTestSpec(targetClass)
  else  set target = ##class(Port.Configuration).GetTestPath()

  if targetMethod '= "" {
    set target = target_":"_targetMethod
    set recursive = "norecursive"
  }

  set sc = ..RunTest(target_$case(targetSpec, "": "", : ":"_targetSpec), "/"_recursive_"/run/noload/nodelete")
  set ^||Port.Project = activeProject
  return sc
}

}

1 - Is there any plans to automatize the module.xml generation by using something like a Wizard?

Submit an issue?  More over, craft a module which supports that! And PR - it's a Community Package manager.

3 - Is it possible to run pre/post-install scripts as well? Kind of what installer classes do.

I think, this already in place. @Dmitry Maslennikov who contributed a lot will comment.

4 - Is also possible to use the module.xmlto provide a contextual root?

We maybe can use the code! Thanks! @Dmitry Maslennikov ?

1 - Is there any plans to automatize the module.xml generation by using something like a Wizard?

Any reasons for it? Are you so lazy, that you can't write this simple XML by hand? Just kidding, not yet, I think best and fastest what I can do it, add Intellisense in vscode for such files, so you can help to do it easier. Any UI, at the moment, is just a waste of time, it is not so important. And anyway, is there any wizard from NPM?

2 - Is there any plans to support non-specific dependency versions like NPM does?

It is already there, should work the same as semver in npm

3 - Is it possible to run pre/post-install scripts as well? Kind of what installer classes do.

There is already something like this, but I would like to change this way.

4 - Is also possible to use the module.xmlto provide a contextual root? 

Not sure about contextual root. But if saying about UnitTests, yes actually there are many things which should be changed in the original %UnitTests engine. But in this case, it has some way to run tests without care about UnitTestRoot global.  ZPM itself has own module.xml, and look there. You will find lines about UnitTests. with this definition, you run these commands, and it will run tests in different phases

zpm: USER>packagename test

zpm: USER>packagename verify

Any reasons for it? Are you so lazy, that you can't write this simple XML by hand? Just kidding, not yet, I think best and fastest what I can do it, add Intellisense in vscode for such files, so you can help to do it easier. Any UI, at the moment, is just a waste of time, it is not so important. And anyway, is there any wizard from NPM?

Haha, I'll overlook this first line. I meant something like a CLI wizard really, that asks you for steps, but maybe this can be something apart, you know Yeoman don't you?

NPM does have the command npm init which asks you the basic information about your package and generates a package.json.

It is already there, should work the same as semver in npm

Nice! Does it follow the same format as the one from NPM? (symbolically speaking ^ and *)

Not sure about contextual root. But if saying about UnitTests, yes actually there are many things which should be changed in the original %UnitTests engine. But in this case, it has some way to run tests without care about UnitTestRoot global. ZPM itself has own module.xml, and look there. You will find lines about UnitTests. with this definition, you run these commands, and it will run tests in different phases.

Yeah, that's exactly what I meant about contextual root, my wording looked wrong because I intended for this feature to be used outside UnitTest, but now I see that there isn't much use for it outside of unit testing.

NPM does have the command npm init which asks you the basic information about your package and generates a package.json.

Yes, kind of init command sounds useful. You know, we anyway have many differences with npm, for instance. Like, zpm works inside the database with nothing on disk, while npm in OS close to source files, but I think we can find the way how to achieve the best way.

It is already there, should work the same as semver in npm
Nice! Does it follow the same format as the one from NPM?

Yes, the same way

This brought me to another question. How does the ZPM handle two modules that use the same dependency but both have different versions while being used as peer dependencies?

Example:

Module A using dependency C requires its major 1.
Module B also uses dependency C but requires the newer major 2.

The requisite for this case is: You must have both because you're making a module D that uses both module A and B.

Can ZPM run in Cache' ?

Tried to install ZPM on Cache' but failed:

%SYS>D $system.OBJ.Load("/tmp/deps/zpm.xml","ck")Load started on 04/21/2021 11:30:27
Loading file /tmp/deps/zpm.xml as xml
Imported class: %ZPM.Installer
Compiling class %ZPM.Installer
Compiling routine : %ZPM.Installer.G1.MAC
ERROR #6353: Unexpected attributes for element Import (ending at line 9 character 45): Recurse
  > ERROR #5490: Error reported while running generator for method '%ZPM.Installer:setup'
    > ERROR #5030: An error occurred while compiling class %ZPM.Installer
Compiling routine %ZPM.Installer.1
ERROR: Compiling method/s: ExtractPackage
ERROR:  %ZPM.Installer.1(3) : MPP5610 : Referenced macro not defined: 'FileTempDir'
 TEXT:   Set pFolder = ##class(%File).NormalizeDirectory($$$FileTempDir)
ERROR: Compiling method/s: Make
ERROR:  %ZPM.Installer.1(7) : MPP5610 : Referenced macro not defined: 'FileTempDir'
 TEXT:     Set tmpDir = ##class(%File).NormalizeDirectory($$$FileTempDir)
%ZPM.Installer.1.INT(79) ERROR #1002: Invalid character in tag : '##class(%Library.File).NormalizeDirectory($$$FileTempDir)' : Offset:61 [zExtractPackage+1^%ZPM.Installer.1]
 TEXT:   Set pFolder = ##class(%Library.File).NormalizeDirectory($$$FileTempDir)
%ZPM.Installer.1.INT(117) ERROR #1002: Invalid character in tag : '##class(%Library.File).NormalizeDirectory($$$FileTempDir)' : Offset:62 [zMake+4^%ZPM.Installer.1]
 TEXT:     Set tmpDir = ##class(%Library.File).NormalizeDirectory($$$FileTempDir)
Detected 5 errors during load.

When I try to install webterminal, I am getting the following...

IRIS for Windows (x86-64) 2022.1 (Build 209U) Tue May 31 2022 12:16:40 EDT

zpm:USER>install webterminal
 
[USER|webterminal]      Reload START (C:\InterSystems\HealthConnect_2022_1\mgr\.modules\USER\webterminal\4.9.2\)
[USER|webterminal]      Reload SUCCESS
[webterminal]   Module object refreshed.
[USER|webterminal]      Validate START
[USER|webterminal]      Validate SUCCESS
[USER|webterminal]      Compile START
Installing WebTerminal application to USER
Creating WEB application "/terminal"...
WEB application "/terminal" is created.
Assigning role %DB_CACHESYS to a web application; resulting roles: :%DB_CACHESYS:%DB_USER
Creating WEB application "/terminalsocket"...
WEB application "/terminalsocket" is created.
ERROR #879: Target role %DB_CACHESYS does not exist.
[webterminal]   Compile FAILURE
ERROR! Target role %DB_CACHESYS does not exist.
  > ERROR #5090: An error has occurred while creating projection WebTerminal.Installer:Reference.

I have seen this on both...  HealthShare Health Connect

IRIS for Windows (x86-64) 2022.1 (Build 209U) Tue May 31 2022 12:16:40 EDT

IRIS for UNIX (Red Hat Enterprise Linux 8 for x86-64) 2022.1 (Build 209U) Tue May 31 2022 12:13:24 EDT

using the link from https://pm.community.intersystems.com/packages/zpm/latest/installer

zpm:USER>version

%SYS> zpm 0.5.0
https://pm.community.intersystems.com - 1.0.6