From 3943f0f525b268d9cffb57c53db19cab785358b0 Mon Sep 17 00:00:00 2001
From: Pravin Barton <9560941+isc-pbarton@users.noreply.github.com>
Date: Fri, 10 Jan 2025 14:28:24 -0500
Subject: [PATCH 1/2] enh: API method to load a production directory in a CD
context without Git configured
---
cls/SourceControl/Git/API.cls | 8 +++
cls/SourceControl/Git/Util/Production.cls | 72 +++++++++++++++++++
.../SourceControl/Git/Util/Production.cls | 16 +++++
.../cls/UnitTest/SampleProduction.cls | 16 +++++
.../ProdStgs-UnitTest_SampleProduction.xml | 32 +++++++++
.../UnitTest_SampleProduction/Stgs-b949C.xml | 32 +++++++++
.../UnitTest_SampleProduction/Stgs-c949C.xml | 32 +++++++++
7 files changed, 208 insertions(+)
create mode 100644 test/_resources/cls/UnitTest/SampleProduction.cls
create mode 100644 test/_resources/ptd/UnitTest_SampleProduction/ProdStgs-UnitTest_SampleProduction.xml
create mode 100644 test/_resources/ptd/UnitTest_SampleProduction/Stgs-b949C.xml
create mode 100644 test/_resources/ptd/UnitTest_SampleProduction/Stgs-c949C.xml
diff --git a/cls/SourceControl/Git/API.cls b/cls/SourceControl/Git/API.cls
index e77ec05f..d3dab115 100644
--- a/cls/SourceControl/Git/API.cls
+++ b/cls/SourceControl/Git/API.cls
@@ -82,4 +82,12 @@ ClassMethod BaselineProductions()
do ##class(SourceControl.Git.Util.Production).BaselineProductions()
}
+/// Given the path to a directory that contains production items, this method will import them all
+/// and delete any custom items from the production configuration that do not exist in the directory.
+/// This method may be called on a namespace that is not configured with Embedded Git for source control.
+ClassMethod LoadProductionsFromDirectory(pDirectoryName, Output pFailedItems) As %Status
+{
+ return ##class(SourceControl.Git.Util.Production).LoadProductionsFromDirectory(pDirectoryName, .pFailedItems)
+}
+
}
diff --git a/cls/SourceControl/Git/Util/Production.cls b/cls/SourceControl/Git/Util/Production.cls
index fd7f7341..c7d5c794 100644
--- a/cls/SourceControl/Git/Util/Production.cls
+++ b/cls/SourceControl/Git/Util/Production.cls
@@ -70,4 +70,76 @@ ClassMethod ItemIsPTD(externalName) As %Boolean
return 0
}
+/// Given the path to a directory that contains production items, this method will import them all
+/// and delete any custom items from the production configuration that do not exist in the directory.
+/// This method may be called on a namespace that is not configured with Embedded Git for source control.
+ClassMethod LoadProductionsFromDirectory(pDirectoryName, Output pFailedItems) As %Status
+{
+ set st = $$$OK
+ try {
+ set rs = ##class(%File).FileSetFunc(pDirectoryName,,,1)
+ throw:rs.%SQLCODE<0 ##class(%Exception.SQL).CreateFromSQLCODE(rs.%SQLCODE,rs.%Message)
+ kill itemsOnDisk
+ while rs.%Next() {
+ continue:rs.Type'="D"
+ set rs2 = ##class(%File).FileSetFunc(rs.Name,"*.xml",,0)
+ throw:rs2.%SQLCODE<0 ##class(%Exception.SQL).CreateFromSQLCODE(rs2.%SQLCODE,rs2.%Message)
+ while rs2.%Next() {
+ set filePath = rs2.Name
+ $$$ThrowOnError(##class(SourceControl.Git.Production).ParseExternalName(filePath,.internalName))
+ set itemName = "", itemClassName = "", productionName = ""
+ do ##class(SourceControl.Git.Production).ParseInternalName(internalName,,,.itemName,.itemClassName,.productionName)
+ quit:productionName=""
+ if (itemName'="") && (itemClassName'="") {
+ set itemsOnDisk(productionName, itemName, itemClassName) = 1
+ }
+ // if production does not exist, create it
+ if '$isobject(##class(Ens.Config.Production).%OpenId(productionName)) {
+ $$$ThrowOnError(##class(SourceControl.Git.Production).CreateProduction(productionName))
+ }
+ set st = ##class(SourceControl.Git.Production).ImportPTD(filePath, productionName)
+ if $$$ISERR(st) {
+ set pFailedItems(filePath) = st
+ }
+ }
+ }
+ // handle deletes by iterating through XDATA of each production class. for every config item that is not in itemsOnDisk, delete it.
+ set key = $order(itemsOnDisk(""))
+ while (key '= "") {
+ set classDef = ##class(%Dictionary.ClassDefinition).%OpenId(key)
+ if $isobject(classDef) {
+ set productionXData = $$$NULLOREF
+ for i=1:1:classDef.XDatas.Count() {
+ set xdata = classDef.XDatas.GetAt(i)
+ if xdata.Name = "ProductionDefinition" {
+ set productionXData = xdata
+ quit
+ }
+ }
+ if $isobject(productionXData) {
+ $$$ThrowOnError(##class(%XML.XPATH.Document).CreateFromStream(productionXData.Data,.xdoc))
+ $$$ThrowOnError(xdoc.EvaluateExpression("/Production","Item/@Name | Item/@ClassName",.results))
+ for i=1:2:results.Count() {
+ set itemName = results.GetAt(i).Value
+ set itemClassName = results.GetAt(i+1).Value
+ if (itemName'="") && (itemClassName'="") && '$get(itemsOnDisk(key,itemName, itemClassName)) {
+ write !, "Removing item from production ", key, ": ", itemName, ":", itemClassName
+ set internalName = ##class(SourceControl.Git.Production).CreateInternalName(key,itemName, itemClassName)
+ set st = ##class(SourceControl.Git.Production).RemoveItem(internalName)
+ if $$$ISERR(st) {
+ set pFailedItems(itemName, itemClassName) = st
+ }
+ }
+ }
+ }
+ }
+ set key = $order(itemsOnDisk(key))
+ }
+ } catch err {
+ set st = err.AsStatus()
+ }
+ if $data(pFailedItems) set st = $$$ADDSC($$$ERROR($$$GeneralError,"Some items failed to deploy."),st)
+ return st
+}
+
}
diff --git a/test/UnitTest/SourceControl/Git/Util/Production.cls b/test/UnitTest/SourceControl/Git/Util/Production.cls
index 2ccbf11f..f9729b37 100644
--- a/test/UnitTest/SourceControl/Git/Util/Production.cls
+++ b/test/UnitTest/SourceControl/Git/Util/Production.cls
@@ -15,6 +15,22 @@ Method TestItemIsPTD()
do $$$AssertTrue(##class(SourceControl.Git.Util.Production).ItemIsPTD("ptd2\test.xml"))
}
+Method TestLoadProductionsFromDirectory()
+{
+ // load a production from a class file under resources
+ set packageRoot = ##class(SourceControl.Git.PackageManagerContext).ForInternalName("git-source-control.zpm").Package.Root
+ $$$ThrowOnError($System.OBJ.Load(packageRoot_"test/_resources/cls/UnitTest/SampleProduction.cls","ck"))
+ // call LoadProductionsFromDirectory on a directory under resources/ptd
+ do $$$AssertStatusOK(##class(SourceControl.Git.Util.Production).LoadProductionsFromDirectory(packageRoot_"test/_resources/ptd"))
+ // confirm items were deleted and added
+ set itemA = ##class(Ens.Config.Production).OpenItemByConfigName("UnitTest.SampleProduction||a")
+ do $$$AssertNotTrue($isobject(itemA),"item a was deleted")
+ set itemB = ##class(Ens.Config.Production).OpenItemByConfigName("UnitTest.SampleProduction||b")
+ do $$$AssertEquals(itemB.Settings.GetAt(1).Value,71)
+ set itemB = ##class(Ens.Config.Production).OpenItemByConfigName("UnitTest.SampleProduction||c")
+ do $$$AssertTrue($isobject(itemB),"item a was created")
+}
+
Method OnBeforeAllTests() As %Status
{
merge ..Mappings = @##class(SourceControl.Git.Utils).MappingsNode()
diff --git a/test/_resources/cls/UnitTest/SampleProduction.cls b/test/_resources/cls/UnitTest/SampleProduction.cls
new file mode 100644
index 00000000..2f507841
--- /dev/null
+++ b/test/_resources/cls/UnitTest/SampleProduction.cls
@@ -0,0 +1,16 @@
+Class UnitTest.SampleProduction Extends Ens.Production
+{
+
+XData ProductionDefinition
+{
+
+ -
+ 60
+
+ -
+ 61
+
+
+}
+
+}
diff --git a/test/_resources/ptd/UnitTest_SampleProduction/ProdStgs-UnitTest_SampleProduction.xml b/test/_resources/ptd/UnitTest_SampleProduction/ProdStgs-UnitTest_SampleProduction.xml
new file mode 100644
index 00000000..ad951464
--- /dev/null
+++ b/test/_resources/ptd/UnitTest_SampleProduction/ProdStgs-UnitTest_SampleProduction.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+UnitTest.SampleProduction
+1841-01-01 00:00:00.000
+
+
+
+
+ProductionSettings-UnitTest_SampleProduction
+- ProductionSettings:UnitTest.SampleProduction.PTD
+
+
+
+
+]]>
+
+
+
+
+]]>
+
diff --git a/test/_resources/ptd/UnitTest_SampleProduction/Stgs-b949C.xml b/test/_resources/ptd/UnitTest_SampleProduction/Stgs-b949C.xml
new file mode 100644
index 00000000..0bfa76b2
--- /dev/null
+++ b/test/_resources/ptd/UnitTest_SampleProduction/Stgs-b949C.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+UnitTest.SampleProduction
+1841-01-01 00:00:00.000
+
+
+
+
+Settings-b
+- Settings:b.PTD
+
+
+
+
+]]>
+
+
+
+71
+]]>
+
diff --git a/test/_resources/ptd/UnitTest_SampleProduction/Stgs-c949C.xml b/test/_resources/ptd/UnitTest_SampleProduction/Stgs-c949C.xml
new file mode 100644
index 00000000..e66637cc
--- /dev/null
+++ b/test/_resources/ptd/UnitTest_SampleProduction/Stgs-c949C.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+UnitTest.SampleProduction
+1841-01-01 00:00:00.000
+
+
+
+
+Settings-c
+- Settings:c.PTD
+
+
+
+
+]]>
+
+
+
+
+]]>
+
From 70eb962971cac94c7284e212cfd6dcd11e615543 Mon Sep 17 00:00:00 2001
From: Pravin Barton <9560941+isc-pbarton@users.noreply.github.com>
Date: Mon, 13 Jan 2025 10:33:57 -0500
Subject: [PATCH 2/2] chore: update CHANGELOG and module version
---
CHANGELOG.md | 5 +++++
module.xml | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 94231c17..08528980 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ 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).
+## [2.10.0] - Unreleased
+
+### Added
+- LoadProductionsFromDirectory method to help custom deployment scripts load decomposed productions from the repository (#670)
+
## [2.9.0] - 2025-01-09
### Added
diff --git a/module.xml b/module.xml
index 7702e998..14518da8 100644
--- a/module.xml
+++ b/module.xml
@@ -3,7 +3,7 @@
git-source-control
- 2.9.0
+ 2.10.0
Server-side source control extension for use of Git on InterSystems platforms
git source control studio vscode
module