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

Add command for "list-outdated" #586

Draft
wants to merge 8 commits into
base: v0.10.x
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #474 Added compatibility to load ".tar.gz" archives in addition to ".tgz"
- #469 Added ability to include an `IPMVersion` in `SystemRequirement` of module.xml
- #530 Added a `CustomPhase` attribute to `<Invoke>` that doesn't require a corresponding %method in lifecycle class.
- #582 Added functionality to optionally see time of last update and server version of each package

### Changed
- IPM is now namespace-specific rather than being installed in %SYS and being available instance-wide.
Expand Down
120 changes: 116 additions & 4 deletions src/cls/IPM/Main.cls
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,8 @@ run C:\Temp\MyCommands.json, where contents of the file are as follows:
<modifier name="tree" aliases="t" description="If specified, show dependency tree for installed modules." />
<modifier name="description" aliases="d" dataAlias="Desc" dataValue="1" description="Additional information is displayed for each module." />
<modifier name="showsource" aliases="ss" description="If specified, show dependency list with local source for installed modules." />
<modifier name="showtime" aliases="st" description="If specified, show the time of last update for each module" />
<modifier name="showupstream" aliases="su" description="If specified, show the latest version for each module in configured repos if it's different than the local version." />
<modifier name="repository" aliases="repo" value="true" description="If specified, only show modules installed that belong to the provided repository." />

</command>
Expand Down Expand Up @@ -633,6 +635,15 @@ generate /my/path -export 00000,PacketName2,IgnorePacket2^00000,PacketName3,Igno
</example>
</command>

<command name="list-outdated" aliases="outdated">
<description>
Shows outdated modules and the latest version available in each repository.
</description>
<summary>
Shows outdated modules.
</summary>
</command>

</commands>
}

Expand Down Expand Up @@ -802,6 +813,8 @@ ClassMethod ShellInternal(pCommand As %String, Output pException As %Exception.A
Do ..Namespace(.tCommandInfo)
} ElseIf (tCommandInfo = "enable") {
Do ..EnableIPM(.tCommandInfo)
} ElseIf (tCommandInfo = "list-outdated") {
Do ..ListOutdated(.tCommandInfo)
}
} Catch pException {
If (pException.Code = $$$ERCTRLC) {
Expand Down Expand Up @@ -949,7 +962,7 @@ ClassMethod GetListModules(pNamespace As %String = {$Namespace}, pSearch As %Str
Quit
}
Set tArgs = 0
Set tQuery = "SELECT Name,VersionString,Description,ExternalName,DeveloperMode,Root FROM %IPM_Storage.ModuleItem WHERE 1=1 "
Set tQuery = "SELECT Name,VersionString,Description,ExternalName,DeveloperMode,Root,LastUpdated FROM %IPM_Storage.ModuleItem WHERE 1=1 "
Set pSearch = $ZStrip(pSearch, "<>WC")
If pSearch'="" {
If pSearch["," {
Expand Down Expand Up @@ -982,7 +995,7 @@ ClassMethod GetListModules(pNamespace As %String = {$Namespace}, pSearch As %Str
$$$ThrowOnError(tSC)
Set name = tRes.Name
Set list = list + 1
Set list(list) = $ListBuild(name, tRes.VersionString, tRes.ExternalName, tRes.DeveloperMode, tRes.Root)
Set list(list) = $ListBuild(name, tRes.VersionString, tRes.ExternalName, tRes.DeveloperMode, tRes.Root, tRes.LastUpdated)
If maxWidth<$Length(name) {
Set maxWidth = $Length(name)
}
Expand Down Expand Up @@ -2179,7 +2192,7 @@ ClassMethod ListInstalled(ByRef pCommandInfo) [ Private ]
} Else {
Set showFields = ""
If +$Get(pCommandInfo("data","Desc")) {
Set showFields = $ListBuild(
Set showFields = showFields_$ListBuild(
"Description",
"Author.CopyrightDate",
"Author.License",
Expand Down Expand Up @@ -2865,6 +2878,62 @@ ClassMethod EnableIPM(ByRef pCommandInfo)
}
}

ClassMethod CanUpdate(modName As %String, semver As %IPM.General.SemanticVersion) As %Boolean
Copy link
Collaborator

Choose a reason for hiding this comment

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

@isc-shuliu Doesn't look like this method is used?

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, you're right. I haven't gone to that point yet. My plan is to show if an outdated package can be updated to a particular version without violating dependency constraint. This PR is still a Work-In-Progress

{
Do ..GetListModules($Namespace, "", .list)
For i=1:1:list {
Set $ListBuild(otherMod, version) = list(i)
If (otherMod = modName) {
Continue
}
Set otherMod = ##class(%IPM.Storage.Module).NameOpen(otherMod)
// There is no need to recurse here
For i = 1:1:otherMod.Dependencies.Count() {
If (otherMod.Dependencies.GetAt(i).Name '= modName) {
Continue
}
#dim expresion As %IPM.General.SemanticVersionExpression
Set expression = otherMod.Dependencies.GetAt(i).Version
If 'expression.IsSatisfiedBy(semver) {
Return 0
}
}
}
Return 1
}

ClassMethod ListOutdated(ByRef pCommandInfo)
{
Do ..GetListModules($Namespace, "", .list)
Do ..GetUpstreamPackageVersions(.serverVersions)

For i=1:1:list {
Set $ListBuild(module, version) = list(i)
If $Data(serverVersions(module)) \ 10 = 0 {
Continue
}
Set semver = ##class(%IPM.General.SemanticVersion).FromString(version)
Set repo = ""
Set found = 0
For {
Set repo = $Order(serverVersions(module, repo), 1, remoteVersion)
If repo = "" {
Quit
}
Set remoteSemver = ##class(%IPM.General.SemanticVersion).FromString(remoteVersion)
// TODO use better comparison method
If 'remoteSemver.Follows(semver) {
Continue
}
If 'found {
Set found = 1
Write !, $$$FormattedLine($$$Green, module), " ", $$$FormattedLine($$$Blue, version)
}
Write " ", $$$FormattedLine($$$Red, $$$FormatText("%1:v%2", repo, remoteVersion))
}
}
}

/// Runs package manager commands in a way that is friendly to the OS-level shell.
/// Creates <var>pOutputLogFile</var> if it does not exist.
/// If it does, and <var>pAppendToLog</var> is true, appends to it; otherwise, deletes the file before outputting to it.
Expand Down Expand Up @@ -2951,22 +3020,46 @@ ClassMethod DisplayModules(ByRef pList, pNumbered As %Boolean = 0, pWithNamespac
$ListBuild("Repository", $$$Blue)
)

Kill serverVersions
If $Data(pModifiers("showupstream")) {
Do ..GetUpstreamPackageVersions(.serverVersions)
}

Set width = $Get(pList("width")) + 1
Set tIndent = pIndent
Set tIndent = $Select(pIndent > 0: pIndent, 1: width)
For i=1:1:pList {
Set info = pList(i)
Set developerMode = 0
Set root = ""
Set $ListBuild(name, version, externalName, developerMode, root) = info
Set $ListBuild(name, version, externalName, developerMode, root, lastUpdated) = info
Write:i>1 !,?pIndent
Write $$$FormattedLinePadRight($$$Green, name, width), $$$FormattedLine($$$Blue, version)
If $Data(pModifiers("showupstream")) && ($Data(serverVersions(name)) / 10) {
Set r = ""
Set diffs = ""
For {
Set r = $Order(serverVersions(name, r), 1, sVer)
If r = "" {
Quit
}
If sVer '= version {
Set diffs = diffs_$ListBuild($$$FormatText("%1: %2", r, sVer))
}
}
If diffs '= "" {
Write " ", $$$FormattedLine($$$Blue, "(" _ $ListToString(diffs, ", ") _ ")")
}
}
If $Get(developerMode) {
Write " ", $$$FormattedLine($$$Red, "(DeveloperMode)")
}
If $Data(pModifiers("showsource")) {
Write " ", $$$FormattedLine($$$Magenta, root)
}
If $Data(pModifiers("showtime")) {
Write " ", $$$FormattedLine($$$Yellow, lastUpdated)
}

Set ptr = 0
While $ListNext(extraColumns, ptr, column) {
Expand All @@ -2982,4 +3075,23 @@ ClassMethod DisplayModules(ByRef pList, pNumbered As %Boolean = 0, pWithNamespac
}
}

ClassMethod GetUpstreamPackageVersions(Output list)
{
Set query = "SELECT %DLIST(Name) AS Repos FROM %IPM_Repo.Definition"
Set rs = ##class(%SQL.Statement).%ExecDirect(, query)
$$$ThrowSQLIfError(rs.%SQLCODE, rs.%Message)
If rs.%Next() {
Set repos = rs.%Get("Repos")
}

Set ptr = 0
While $ListNext(repos, ptr, r) {
Set query = "SELECT Name,Version FROM %IPM_Utils.Module_GetModuleList(?,?) "
Set rs = ##class(%SQL.Statement).%ExecDirect(, query, r, 0)
While rs.%Next() {
Set list(rs.%Get("Name"), r) = rs.%Get("Version")
}
}
}

}
18 changes: 18 additions & 0 deletions src/cls/IPM/Storage/Module.cls
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Property Packaging As %String [ Required ];

Property Dependencies As list Of %IPM.Storage.ModuleReference(STORAGEDEFAULT = "array");

Property LastUpdated As %TimeStamp;

Relationship Resources As %IPM.Storage.ResourceReference(XMLIO = "IN", XMLITEMNAME = "Resource", XMLPROJECTION = "WRAPPED", XMLREFERENCE = "COMPLETE") [ Cardinality = children, Inverse = Module ];

/// Calculated property used for XML output of Resources relationship (in a reasonable order:
Expand Down Expand Up @@ -1458,6 +1460,19 @@ Method %Evaluate(pAttrValue As %String, ByRef pParams) As %String [ Internal ]
Return attrValue
}

/// This callback method is invoked by the <METHOD>%Save</METHOD> method to
/// provide notification that the object is being saved. It is called before
/// any data is written to disk.
///
/// <P><VAR>insert</VAR> will be set to 1 if this object is being saved for the first time.
///
/// <P>If this method returns an error then the call to <METHOD>%Save</METHOD> will fail.
Method %OnBeforeSave(insert As %Boolean) As %Status [ Private, ServerOnly = 1 ]
{
Set ..LastUpdated = $ZDateTime($Horolog, 3)
Quit $$$OK
}

Storage Default
{
<Data name="Defaults">
Expand Down Expand Up @@ -1552,6 +1567,9 @@ Storage Default
<Value name="27">
<Value>AvailabilityClass</Value>
</Value>
<Value name="28">
<Value>LastUpdated</Value>
</Value>
</Data>
<DataLocation>^IPM.Storage.ModuleD</DataLocation>
<DefaultData>ModuleDefaultData</DefaultData>
Expand Down