Skip to content

Commit 23ef7e2

Browse files
committed
Merge branch 'main' into issue-617
2 parents fe2ab28 + fadbaab commit 23ef7e2

File tree

12 files changed

+191
-80
lines changed

12 files changed

+191
-80
lines changed

CHANGELOG.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,25 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [2.8.1] - Unreleased
8+
## [2.9.0] - Unreleased
9+
10+
### Added
11+
- Menu option to export production to support migrating to production decomposition (#665)
12+
13+
### Fixed
14+
- Fixed errors on production page when item settings need to be XML escaped (#667)
15+
- Fixed push button not appearing after commit (#654)
16+
- Fixed merge conflict resolution on stash popping (#531)
17+
18+
## [2.9.0] - Unreleased
19+
20+
### Added
21+
- Menu option to export production to support migrating to production decomposition (#665)
922

1023
### Fixed
24+
- Fixed errors on production page when item settings need to be XML escaped (#667)
25+
- Fixed push button not appearing after commit (#654)
26+
- Fixed merge conflict resolution on stash popping (#531)
1127
- Fix "Max $ZF String" error when committing lots of files (#617)
1228

1329
## [2.8.0] - 2024-12-06

cls/SourceControl/Git/Extension.cls

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ XData Menu
3030
<MenuItem Name="SwitchBranch" Save="101" />
3131
<MenuItem Separator="true"/>
3232
<MenuItem Name="ExportSystemDefaults" />
33+
<MenuItem Name="ExportProduction" />
3334
<MenuItem Name="Export" Save="101" />
3435
<MenuItem Name="ExportForce" Save="101" />
3536
<MenuItem Name="Import" />
@@ -148,6 +149,7 @@ Method LocalizeName(name As %String) As %String
148149
"Fetch":$$$Text("@Fetch@Fetch from remote"),
149150
"Pull":$$$Text("@Pull@Pull changes from remote branch"),
150151
"Status": $$$Text("@Status@Status"),
152+
"ExportProduction": $$$Text("@ExportProduction@Export Production"),
151153
:name)
152154
}
153155

@@ -163,7 +165,7 @@ Method OnSourceMenuItem(name As %String, ByRef Enabled As %String, ByRef Display
163165
}
164166
if ##class(SourceControl.Git.Utils).IsNamespaceInGit() {
165167

166-
if $listfind($listbuild("AddToSC", "RemoveFromSC", "Revert", "Commit"), name) {
168+
if $listfind($listbuild("AddToSC", "RemoveFromSC", "Revert", "Commit", "ExportProduction"), name) {
167169
quit ..OnSourceMenuContextItem(InternalName,name,.Enabled,.DisplayName)
168170
}
169171

@@ -243,6 +245,9 @@ Method OnSourceMenuContextItem(itemName As %String, menuItemName As %String, ByR
243245
if '(##class(SourceControl.Git.Change).IsUncommitted(##class(SourceControl.Git.Utils).FullExternalName(itemName))) || ($username '= userCheckedOut) {
244246
set Enabled = 0
245247
}
248+
} elseif menuItemName = "ExportProduction" {
249+
set itemNameNoExt = $piece(itemName,".",1,*-1)
250+
set Enabled = (##class(SourceControl.Git.Production).IsProductionClass(itemNameNoExt,"FullExternalName"))
246251
} elseif ##class(SourceControl.Git.Utils).IsInSourceControl(itemName) {
247252
set Enabled = $case(menuItemName, "AddToSC":-1,:1)
248253
} else {

cls/SourceControl/Git/Production.cls

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ ClassMethod DeleteProductionDefinitionShards(productionClass As %String, deleteM
6262
while rs.%Next(.sc) {
6363
quit:$$$ISERR(sc)
6464
set ptdFilename = rs.Data("Name")
65-
set sc = ##class(%Studio.SourceControl.Production).ParseExternalName(ptdFilename, .ptdInternalName)
65+
set sc = ..ParseExternalName(ptdFilename, .ptdInternalName)
6666
quit:$$$ISERR(sc)
6767
// TODO: Consider reverting delete if any ptd is not editable by current user
6868
set sc = $method(%SourceControl, deleteMethod, ptdInternalName)
@@ -371,26 +371,32 @@ ClassMethod IsProductionClass(className As %String, nameMethod As %String) As %B
371371
ClassMethod ParseExternalName(externalName, Output internalName = "", Output productionName = "") As %Status
372372
{
373373
set sc = $$$OK
374-
set extNameNormalized = $replace(externalName, "\", "/")
375-
set file = $piece(extNameNormalized, "/", *)
376-
if $extract(file,1,9) = "ProdStgs-" {
377-
set productionName = $replace($extract(file,10,*-4), "_", ".")
378-
set internalName = ..CreateInternalName(productionName,,,1)
379-
} else {
380-
if ##class(%File).Exists(externalName) {
381-
// Special case for Config Item Settings PTD, requires checking PTD CDATA for Item and Class name
382-
set deployDoc = ##class(EnsLib.EDI.XML.Document).%New(externalName)
383-
set exportNotesPTDText = $ZCVT(deployDoc.GetValueAt("/Export/Document[1]/1"),"I","XML")
384-
set exportNotesPTD = ##class(EnsLib.EDI.XML.Document).%New(exportNotesPTDText)
385-
set productionName = exportNotesPTD.GetValueAt("/Deployment/Creation/SourceProduction")
386-
set settingsPTDText = $zconvert(deployDoc.GetValueAt("/Export/Document[2]/1"),"I","XML")
387-
set settingsPTD = ##class(EnsLib.EDI.XML.Document).%New(settingsPTDText)
388-
set itemClass = settingsPTD.GetValueAt("/Item/@ClassName")
389-
set itemName = settingsPTD.GetValueAt("/Item/@Name")
390-
set internalName = ..CreateInternalName(productionName, itemName, itemClass, 0)
391-
} else {
392-
set sc = $$$ERROR($$$GeneralError, "Item settings PTD file " _ externalName _ " does not exist. Cannot parse external name.")
393-
}
374+
try {
375+
set extNameNormalized = $replace(externalName, "\", "/")
376+
set file = $piece(extNameNormalized, "/", *)
377+
if $extract(file,1,9) = "ProdStgs-" {
378+
set productionName = $replace($extract(file,10,*-4), "_", ".")
379+
set internalName = ..CreateInternalName(productionName,,,1)
380+
} else {
381+
if ##class(%File).Exists(externalName) {
382+
// Special case for Config Item Settings PTD, requires checking PTD CDATA for Item and Class name
383+
set deployDoc = ##class(EnsLib.EDI.XML.Document).%New(externalName)
384+
set exportNotesPTDText = $ZCVT(deployDoc.GetValueAt("/Export/Document[1]/1"),"I","XML")
385+
set exportNotesPTD = ##class(EnsLib.EDI.XML.Document).%New(exportNotesPTDText)
386+
set productionName = exportNotesPTD.GetValueAt("/Deployment/Creation/SourceProduction")
387+
set settingsPTDText = $zconvert(deployDoc.GetValueAt("/Export/Document[2]/1"),"I","XML")
388+
// unquote embedded CDATA close markers - see Ens.Util.ProjectTextDocument StreamToGbl method
389+
set settingsPTDText = $replace(settingsPTDText,"]*]>", "]]>")
390+
set settingsPTD = ##class(EnsLib.EDI.XML.Document).%New(settingsPTDText)
391+
set itemClass = settingsPTD.GetValueAt("/Item/@ClassName")
392+
set itemName = settingsPTD.GetValueAt("/Item/@Name")
393+
set internalName = ..CreateInternalName(productionName, itemName, itemClass, 0)
394+
} else {
395+
set sc = $$$ERROR($$$GeneralError, "Item settings PTD file " _ externalName _ " does not exist. Cannot parse external name.")
396+
}
397+
}
398+
} catch err {
399+
set sc = err.AsStatus()
394400
}
395401
return sc
396402
}

cls/SourceControl/Git/Util/Production.cls

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,40 @@ Include SourceControl.Git
44
Class SourceControl.Git.Util.Production
55
{
66

7+
ClassMethod BaselineProduction(productionName, settings As SourceControl.Git.Settings = {##class(SourceControl.Git.Settings).%New()})
8+
{
9+
set productionInternalName = productionName _ ".cls"
10+
if '##class(SourceControl.Git.Utils).FileIsMapped(productionInternalName) {
11+
if settings.decomposeProductions {
12+
write !, "Exporting production in decomposed format: " _ productionInternalName
13+
if ##class(SourceControl.Git.Utils).IsInSourceControl(productionInternalName) {
14+
set st = ##class(SourceControl.Git.Utils).RemoveFromSourceControl(productionInternalName)
15+
$$$ThrowOnError(st)
16+
}
17+
set st = ##class(SourceControl.Git.Production).ExportProductionDefinitionShards(productionName,"FullExternalName",.itemInternalNames)
18+
$$$ThrowOnError(st)
19+
set key = $order(itemInternalNames(""))
20+
while (key '= "") {
21+
set st = ##class(SourceControl.Git.Utils).AddToSourceControl(key)
22+
$$$ThrowOnError(st)
23+
set key = $order(itemInternalNames(key))
24+
}
25+
} else {
26+
write !, "Exporting production in class format: " _ productionInternalName
27+
set st = ##class(SourceControl.Git.Utils).AddToSourceControl(productionInternalName)
28+
$$$ThrowOnError(st)
29+
set key = $order(@##class(SourceControl.Git.Utils).#Storage@("items", ""))
30+
while (key '= "") {
31+
if $match(key,"^"_productionName_"\|\|.*\.(?i)ptd$") {
32+
set st = ##class(SourceControl.Git.Utils).RemoveFromSourceControl(key)
33+
$$$ThrowOnError(st)
34+
}
35+
set key = $order(@##class(SourceControl.Git.Utils).#Storage@("items", key))
36+
}
37+
}
38+
}
39+
}
40+
741
/// Baselines all productions in this namespace from single-file to decomposed or vice versa.
842
ClassMethod BaselineProductions()
943
{
@@ -13,37 +47,7 @@ ClassMethod BaselineProductions()
1347
throw:rs.%SQLCODE<0 ##class(%Exception.SQL).CreateFromSQLCODE(rs.%SQLCODE,rs.%Message)
1448
while rs.%Next(.sc) {
1549
$$$ThrowOnError(sc)
16-
set productionName = rs.Name
17-
set productionInternalName = productionName _ ".cls"
18-
if '##class(SourceControl.Git.Utils).FileIsMapped(productionInternalName) {
19-
if settings.decomposeProductions {
20-
write !, "Decomposing production: " _ productionInternalName
21-
if ##class(SourceControl.Git.Utils).IsInSourceControl(productionInternalName) {
22-
set st = ##class(SourceControl.Git.Utils).RemoveFromSourceControl(productionInternalName)
23-
$$$ThrowOnError(st)
24-
}
25-
set st = ##class(SourceControl.Git.Production).ExportProductionDefinitionShards(productionName,"FullExternalName",.itemInternalNames)
26-
$$$ThrowOnError(st)
27-
set key = $order(itemInternalNames(""))
28-
while (key '= "") {
29-
set st = ##class(SourceControl.Git.Utils).AddToSourceControl(key)
30-
$$$ThrowOnError(st)
31-
set key = $order(itemInternalNames(key))
32-
}
33-
} else {
34-
write !, "Recomposing production: " _ productionInternalName
35-
set st = ##class(SourceControl.Git.Utils).AddToSourceControl(productionInternalName)
36-
$$$ThrowOnError(st)
37-
set key = $order(@##class(SourceControl.Git.Utils).#Storage@("items", ""))
38-
while (key '= "") {
39-
if $match(key,"^"_productionName_"\|\|.*\.(?i)ptd$") {
40-
set st = ##class(SourceControl.Git.Utils).RemoveFromSourceControl(key)
41-
$$$ThrowOnError(st)
42-
}
43-
set key = $order(@##class(SourceControl.Git.Utils).#Storage@("items", key))
44-
}
45-
}
46-
}
50+
do ..BaselineProduction(rs.Name, settings)
4751
}
4852
}
4953

cls/SourceControl/Git/Utils.cls

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ ClassMethod UserAction(InternalName As %String, MenuName As %String, ByRef Targe
323323
do ..RunGitCommand("status", .errStream, .outStream)
324324
write !, !, "Git Status: "
325325
do ..PrintStreams(outStream, errStream)
326+
} elseif (menuItemName = "ExportProduction") {
327+
do ##class(SourceControl.Git.Util.Production).BaselineProduction($piece(InternalName,".",1,*-1))
326328
}
327329
quit ec
328330
}
@@ -3042,6 +3044,38 @@ ClassMethod InDefaultBranchBasicMode() As %Boolean
30423044
quit 0
30433045
}
30443046

3047+
ClassMethod RunGitAndHandleMerge(command As %String, inFile As %String, Output resolver As SourceControl.Git.Util.ProductionConflictResolver, Output succeeded As %Boolean, Output returnCode As %String, Output errStream, Output outStream, args...) As %Status
3048+
{
3049+
set succeeded = 0
3050+
set initTLevel = $TLEVEL
3051+
try {
3052+
TSTART
3053+
set returnCode = ##class(SourceControl.Git.Utils).RunGitCommandWithInput(command,inFile,.errStream,.outStream, args...)
3054+
if (returnCode '= 0) {
3055+
$$$ThrowStatus($$$ERROR($$$GeneralError,"git reported failure"))
3056+
}
3057+
set succeeded = 1
3058+
TCOMMIT
3059+
} catch e {
3060+
write !,"Attempting to resolve differences in production definition..."
3061+
set resolver = ##class(SourceControl.Git.Util.ResolutionManager).FromLog(outStream)
3062+
if resolver.resolved {
3063+
set succeeded = 1
3064+
TCOMMIT
3065+
write " success!"
3066+
} else {
3067+
write " unable to resolve - "_resolver.errorMessage
3068+
}
3069+
}
3070+
while $TLevel > initTLevel {
3071+
TROLLBACK 1
3072+
}
3073+
if succeeded {
3074+
return $$$OK
3075+
}
3076+
return $$$ERROR($$$GeneralError,"git reported failure")
3077+
}
3078+
30453079
/// Runs the commit on the specified files (unstaging and restaging currently staged files)
30463080
ClassMethod RunGitCommandReStage(Output outStream, Output errStream, command As %String, ByRef fileList As %Library.DynamicArray, args...) As %Integer
30473081
{

cls/SourceControl/Git/WebUIDriver.cls

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,11 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out
218218
} else {
219219
set inFile = ""
220220
}
221-
221+
// Want to invoke merge conflict autoresolver in case of issues
222222
set returnCode = ##class(SourceControl.Git.Utils).RunGitCommandWithInput("-c",inFile,.errStream,.outStream,gitArgs...)
223223

224224
do ..ConvertGitOutput(.outStream,.errStream,returnCode,.%data)
225+
225226
if '$listfind(readOnlyCommands,baseCommand) {
226227
do ##class(SourceControl.Git.Change).RefreshUncommitted(,,,1)
227228
}
@@ -262,10 +263,33 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out
262263
}
263264

264265
set inFile = ""
265-
266-
set returnCode = ##class(SourceControl.Git.Utils).RunGitCommandWithInput("-c", inFile, .errStream, .outStream, argsArr...)
267-
268-
do ..ConvertGitOutput(.outStream,.errStream,returnCode,.%data)
266+
if (gitCmd = "stash") {
267+
set st = ##class(SourceControl.Git.Utils).RunGitAndHandleMerge("-c",inFile, .resolver, .succeeded, .returnCode, .errStream, .outStream, argsArr...)
268+
269+
set %data = ##class(%Stream.TmpCharacter).%New()
270+
set changeTerminators = (%data.LineTerminator '= $char(13,10))
271+
set %data.LineTerminator = $char(13,10) // For the CSPGateway.
272+
do outStream.Rewind()
273+
274+
// Don't show merge error if merge succeeded
275+
if succeeded {
276+
do %data.WriteLine(outStream.ReadLine())
277+
do %data.WriteLine("Git-Stderr-Length: " _ 0)
278+
} else {
279+
set nLines = 0
280+
do errStream.Rewind()
281+
while 'errStream.AtEnd {
282+
do %data.WriteLine(errStream.ReadLine())
283+
set:changeTerminators nLines = nLines + 1
284+
}
285+
do %data.WriteLine("Git-Stderr-Length: " _ (errStream.Size + nLines))
286+
}
287+
do %data.Write("Git-Return-Code: " _ returnCode) // No ending newline expected
288+
do %data.Rewind()
289+
} else {
290+
set returnCode = ##class(SourceControl.Git.Utils).RunGitCommandWithInput("-c", inFile, .errStream, .outStream, argsArr...)
291+
do ..ConvertGitOutput(.outStream,.errStream,returnCode,.%data)
292+
}
269293
set handled = 1
270294

271295
// Make sure discarded items are not in the uncommitted queue
@@ -456,11 +480,13 @@ ClassMethod ConvertGitOutput(ByRef outStream, ByRef errStream, returnCode As %St
456480
set %data = ##class(%Stream.TmpCharacter).%New()
457481
set changeTerminators = (%data.LineTerminator '= $char(13,10))
458482
set %data.LineTerminator = $char(13,10) // For the CSPGateway.
483+
do outStream.Rewind()
459484
while 'outStream.AtEnd {
460485
do %data.WriteLine(outStream.ReadLine())
461486
}
462487

463488
set nLines = 0
489+
do errStream.Rewind()
464490
while 'errStream.AtEnd {
465491
do %data.WriteLine(errStream.ReadLine())
466492
set:changeTerminators nLines = nLines + 1

docs/production-decomposition.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Production Decomposition is a feature of Embedded Git that allows multiple devel
44
## Enabling production decomposition
55
The feature may be enabled by checking the "Decompose Productions" box in the Git Settings page. For deployment of changes to other environments through git to work properly, the value of this setting must match on all namespaces connected to this repository. To assist, settings are automatically exported into a `embedded-git-config.json` file at the root of the repository that may be committed and imported into other environments.
66

7-
If there are existing productions in the namespace, they should be migrated to the new decomposed format by running `do ##class(SourceControl.Git.API).BaselineProductions()`. You may then use the Git Web UI to view, commit, and push the corresponding changes. This method should be run in a single namespace and then deployed to other namespaces through normal Embedded Git deployment mechanisms.
7+
If there are existing productions in the namespace, they should be migrated to the new decomposed format. Do this by opening the production and selecting the "Export Production" option in the source control menu. You may then use the Git Web UI to view, commit, and push the corresponding changes. This step should be done in a single namespace and then deployed to other namespaces through normal Embedded Git deployment mechanisms.
88

99
## Editing productions in the IDE
1010
There are a couple of limitations related to editing a production class directly in an integrated development environment (Studio or VS Code).

docs/testing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ The following is a testing plan that should be followed prior to release of a ne
3131
- Test migration of a production to decomposed format:
3232
- On the initial namespace, disable production decomposition. Create a new production and add a number of items. Sync and confirm it has been pushed to the remote repository.
3333
- On the second namespace, sync and confirm the new production has been created.
34-
- On the initial namespace, turn on production decomposition. From terminal, run `do ##class(SourceControl.Git.API).BaselineProductions()`. Confirm the Web UI includes changes for delete of the old production class and adds for all production items. Commit all items and push the branch.
34+
- On the initial namespace, turn on production decomposition. Open the production page and use the "Export Production" option in the source control menu. Confirm the Web UI includes changes for delete of the old production class and adds for all production items. Commit all items and push the branch.
3535
- On the second namespace, turn on production decomposition. Sync. The production should be reloaded with no changes.

0 commit comments

Comments
 (0)