Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #992: Implement automatic history purge logic
- #973: Enables CORS and JWT configuration for WebApplications in module.xml
- #1110: Add `iriscli` and `ipm` container utility scripts that are auto-installed to `~/.local/bin/` and `~/bin/` so they work both inside and outside of containers (Unix/Linux only)
- #971: Adds structured test output formats (JSON, YAML, Toon). Use `-f <format>` for a one-shot override or `config set TestReportFormat <format>` for a persistent default. Without either, legacy output is shown. Also adds `-output-file` for writing results to a file (including JUnit XML via `.xml` extension) and improves `-quiet` to suppress build noise.

### Fixed
- #1001: The `unmap` and `enable` commands will now only activate CPF merge once after all namespaces have been configured instead after every namespace
Expand Down
11 changes: 11 additions & 0 deletions src/cls/IPM/Lifecycle/Base.cls
Original file line number Diff line number Diff line change
Expand Up @@ -1311,6 +1311,14 @@ Method %Verify(ByRef pParams) As %Status
new $namespace
set tInitNS = $select($namespace="%SYS": "USER", 1: $namespace)
set tVerbose = $get(pParams("Verbose"))
set explicitQuiet = ($data(pParams("Verbose")) && (pParams("Verbose") = 0))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nitpick: $data(pParams("Verbose"))#2

But probably doesn't really matter.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I don't believe that's quite right as this is checking if pParams("Verbose") has been explicitly set to 0.


// Suppress module install/load noise in quiet mode; ended before resource processors
// run so each processor can manage its own output (Test.cls still shows summary + failures).
// Null device is process-scoped, so suppression survives the namespace switch below.
if explicitQuiet {
set suppressor = ##class(%IPM.Utils.OutputSuppressor).%New()
}

if '$get(pParams("Verify","InCurrentNamespace"),0) {
set tMustCreate = 1
Expand Down Expand Up @@ -1394,6 +1402,9 @@ Method %Verify(ByRef pParams) As %Status
// Load dependencies scoped to the "Verify" phase that are not yet installed.
do ##class(%IPM.Utils.Module).LoadDependencies(..Module, ..PhaseList, .pParams)

// End suppression before resource processors run — each handles its own output.
set suppressor = ""

set orderedResourceList = ..Module.GetOrderedResourceList()
set tKey = ""
for {
Expand Down
6 changes: 4 additions & 2 deletions src/cls/IPM/Main.cls
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ Can also specify desired version to update to.
<modifier name="env" aliases="e" dataAlias="EnvFiles" value="true" description="Semicolon separated paths to the environment files in json format. See wiki for details." />
<modifier name="only" aliases="o" description="Only runs the specified phase(s), rather than also running predecessors." />
<modifier name="dev" dataAlias="DeveloperMode" dataValue="1" description="Sets the DeveloperMode flag for the module's lifecycle. Key consequences of this are that ^Sources will be configured for resources in the module, and installer methods will be called with the dev mode flag set." />
<modifier name="quiet" aliases="q" dataAlias="Verbose" dataValue="0" description="Produces minimal output from the command." />
<modifier name="verbose" aliases="v" dataAlias="Verbose" dataValue="1" description="Produces verbose output from the command." />
<modifier name="quiet" aliases="q" dataAlias="Verbose" dataValue="0" description="Produces minimal output. For test and verify: suppresses build/install noise while still showing the result summary and any failures." />
<modifier name="verbose" aliases="v" dataAlias="Verbose" dataValue="1" description="Produces verbose output. For test and verify: shows per-method results in the result summary when combined with -f; without -f only the summary is shown." />
<modifier name="format" aliases="f" dataAlias="outputformat" value="true" valueList="json,yaml,toon" description="(test and verify only) One-shot output format override for the test result summary. Overrides the persistent default set via 'config set TestReportFormat &lt;format&gt;'. When neither -f nor config is set, the default summary and legacy failure lines are shown. Use 'config delete TestReportFormat' to revert to default." />
<modifier name="output-file" dataAlias="outputfile" value="true" description="(test and verify only) Path to write full test results to a file. Format is inferred from the file extension (.json, .yaml, .toon, .xml)." />
<modifier name="export-deps" value="true" valueList="0,1" dataAlias="ExportDependencies" description="If specified, controls whether dependencies are exported. If omitted, defaults to the value of the #EXPORTDEPENDENCIES in lifecycle class. This modifier is only used in &quot;Package&quot; and &quot;Publish&quot; lifecycles." />

</command>
Expand Down
41 changes: 33 additions & 8 deletions src/cls/IPM/Repo/UniversalSettings.cls
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// IPM settings are placed in ^IPM.settings global in %SYS namespace
/// Use this class to set or get settings
///
///
/// Available settings
/// default_registry (string) - default registry url
/// analytics_tracking_id
Expand Down Expand Up @@ -45,7 +45,10 @@ Parameter SemVerPostRelease = "SemVerPostRelease";
/// to retain IPM history records before they are eligible for cleanup.
Parameter HistoryRetain = "history_retain";

Parameter CONFIGURABLE = "trackingId,analytics,ColorScheme,TerminalPrompt,PublishTimeout,PipCaller,UseStandalonePip,SemVerPostRelease,DefaultLogEntryLimit,HistoryRetain";
/// Specifies the serialization format (JSON, TOON, YAML) for unit and integration test results in the shell.
Parameter TestReportFormat = "TestReportFormat";

Parameter CONFIGURABLE = "trackingId,analytics,ColorScheme,TerminalPrompt,PublishTimeout,PipCaller,UseStandalonePip,SemVerPostRelease,DefaultLogEntryLimit,HistoryRetain,TestReportFormat";

/// Returns configArray, that includes all configurable settings
ClassMethod GetAll(Output configArray) As %Status
Expand Down Expand Up @@ -86,11 +89,13 @@ ClassMethod ResetToDefault(key As %String) As %Status
write "Config key = """_key_""" not found",!
quit
}
set sc = ..SetValue($parameter(..%ClassName(1),key), ..GetDefaultValue($parameter(..%ClassName(1),key)))
// TestReportFormat has no factory default; empty means "use legacy output"
set defaultValue = $select(key = "TestReportFormat": "", 1: ..GetDefaultValue($parameter(..%ClassName(1),key)))
set sc = ..SetValue($parameter(..%ClassName(1),key), defaultValue)
if $$$ISOK(sc) {
write "Value for """_key_""" succesfully reset to default",!
write !,"Value for """_key_""" succesfully reset to default",!
} else {
write "Error reseting value for """_key_"""",!
write !,"Error reseting value for """_key_"""",!
}
return sc
}
Expand All @@ -104,11 +109,16 @@ ClassMethod UpdateOne(
write "Config key = """_key_""" not found",!
quit
}
set sc = ..SetValue($parameter(..%ClassName(1),key), value)
if key = "TestReportFormat" {
// Validate format value before saving it
set sc = ..SetTestReportFormat(value)
} else {
set sc = ..SetValue($parameter(..%ClassName(1),key), value)
}
if $$$ISOK(sc) {
write "Key """_key_""" succesfully updated",!
write !,"Key """_key_""" succesfully updated",!
} else {
write "Error updating """_key_"""",!
write !,$system.Status.GetErrorText(sc),!
}
return sc
}
Expand Down Expand Up @@ -190,4 +200,19 @@ ClassMethod GetHistoryRetain() As %Integer
return ..GetValue(..#HistoryRetain)
}

ClassMethod SetTestReportFormat(
val As %String,
overwrite As %Boolean = 1) As %Status
{
if val '= "" && ('$listfind($listfromstring(##class(%IPM.Test.Abstract).#VALIDFORMATS), $zconvert(val, "l"))) {
return $$$ERROR($$$GeneralError, "Unknown format '"_val_"'. Valid formats: "_##class(%IPM.Test.Abstract).#VALIDFORMATS)
}
return ..SetValue(..#TestReportFormat, val, overwrite)
}

ClassMethod GetTestReportFormat() As %String
{
return ..GetValue(..#TestReportFormat)
}

}
9 changes: 8 additions & 1 deletion src/cls/IPM/ResourceProcessor/PythonWheel.cls
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,14 @@ Method OnPhase(
if verbose {
write !,"Running command: ",command
}
$$$ThrowOnError(##class(%IPM.Utils.Module).RunCommand(, command))
set explicitQuiet = ($data(pParams("Verbose")) && (pParams("Verbose") = 0))
if explicitQuiet {
// Redirect pip stdout/stderr to a sink stream so subprocess output doesn't bypass device suppression
set pipOutput = ##class(%Stream.TmpCharacter).%New()
$$$ThrowOnError(##class(%IPM.Utils.Module).RunCommand(, command, .pipOutput, .pipOutput))
} else {
$$$ThrowOnError(##class(%IPM.Utils.Module).RunCommand(, command))
}
} catch ex {
set pResourceHandled = 0
// Special case: we want the installation of IPM to continue, even if the wheel package fails to install
Expand Down
57 changes: 47 additions & 10 deletions src/cls/IPM/ResourceProcessor/Test.cls
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ Method OnPhase(
// In test/verify phase, run unit tests.
set tVerbose = $get(pParams("Verbose"), 0)
set tFlags = $select(tVerbose:"/display=all",1:"/display=none")
set explicitQuiet = ($data(pParams("Verbose")) && (pParams("Verbose") = 0))
if explicitQuiet {
set suppressor = ##class(%IPM.Utils.OutputSuppressor).%New()
}

// Ensure unit tests and related classes are loaded.
set tUnitTestDir = ##class(%File).NormalizeDirectory(..ResourceReference.Module.Root_..ResourceReference.Name)
Expand Down Expand Up @@ -199,21 +203,54 @@ Method OnPhase(
zkill ^UnitTestRoot
$$$ThrowOnError(tSC)

if $data(pParams("UnitTest","JUnitOutput"),tJUnitFile) {
set tPostfix = "-"_$zconvert(pPhase,"L")_"-"
if (..Package '= "") {
set tPostfix = tPostfix_$replace(..Package,".","-")_"-PKG"
} elseif (..Class '= "") {
set tPostfix = tPostfix_$replace(..Class,".","-")_"-CLS"
set testIndex = $get(^||%UnitTest.Manager.AllResults($get(^||%UnitTest.Manager.AllResultsCount)))
if testIndex = "" {
set testIndex = $order(^UnitTest.Result(""),-1)
}
if $data(pParams("outputfile"), outputFile) {
set fileExtension = $zconvert($piece(outputFile,".",*),"L")
set outputClass = $case(fileExtension,
"json":"%IPM.Test.JsonOutput",
"yaml":"%IPM.Test.YamlOutput",
"toon":"%IPM.Test.ToonOutput",
"xml":"%IPM.Test.JUnitOutput",
:"")
if outputClass = "" {
$$$ThrowOnError($$$ERROR($$$GeneralError,"Unsupported output-file extension '."_fileExtension_"'. Use .json, .yaml, .toon, or .xml."))
}
set outputDir = ##class(%File).GetDirectory(outputFile)
if outputDir '= "" {
$$$ThrowOnError(##class(%File).CreateDirectoryChain(outputDir))
}
set tJUnitFile = $piece(tJUnitFile,".",1,*-1)_tPostfix_".xml"
set tSC = ##class(%IPM.Test.JUnitOutput).ToFile(tJUnitFile)
set tSC = $classmethod(outputClass,"ToFile",outputFile)
$$$ThrowOnError(tSC)
}
set suppressor = ""

// By default, detect and report unit test failures as an error from this phase
set outputFormat = $get(pParams("outputformat"))
if outputFormat = "" {
set outputFormat = ##class(%IPM.Repo.UniversalSettings).GetTestReportFormat()
}

write !!,"Test Results:"
if outputFormat '= "" {
set outputClass = "%IPM.Test."_$zconvert(outputFormat,"w")_"Output"
if '$$$defClassDefined(outputClass) {
$$$ThrowOnError($$$ERROR($$$GeneralError,"Unknown output format: "_outputFormat))
}
set tSC = $classmethod(outputClass,"OutputToDevice",testIndex,tVerbose,1)
$$$ThrowOnError(tSC)
} else {
set tSC = ##class(%IPM.Test.Abstract).OutputToDevice(testIndex,tVerbose,0)
$$$ThrowOnError(tSC)
}
write !
// Detect and report unit test failures as an error from this phase.
// OutputFailures shows legacy red FAILED lines only when no format is active.
if $get(pParams("UnitTest","FailuresAreFatal"),1) {
do ##class(%IPM.Test.Manager).OutputFailures(phaseStartIndex)
if outputFormat = "" {
do ##class(%IPM.Test.Manager).OutputFailures(phaseStartIndex)
}
set tSC = ##class(%IPM.Test.Manager).GetAllTestsStatus(,phaseStartIndex)
$$$ThrowOnError(tSC)
}
Expand Down
Loading
Loading