diff --git a/CHANGELOG.md b/CHANGELOG.md index 87cb1cc9..2e14cc25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow changing namespaces and IPM package context from web UI (#280) - Support for editing repo from filesystem perspective via web application (#464) - Support for downloading a VSCode workspace file from web UI +- IncrementalLoad pull event handler will update the running production, if any (#473) ### Fixed - Instance wide settings are placed in proper global (#444) - Avoid delay/errors in loading interop JS when there is a URL prefix (e.g., instance name in multi-instance webserver configuration) - Added proper JS escaping in sync output - Added support to switch branch in basic mode from menu (#451) +- Pull event handler will not fail when change set includes unmapped files (#453) +- Pull event handler will attempt compile even if there are failures to load (#457) +- Improved logging in preview and when errors occur via WebSocket commands (#467) +- Fixed pull event handler handling of extremely long class names from diff (#467) ## [2.4.1] - 2024-08-02 diff --git a/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls b/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls index 8dd64d3b..ae13af6f 100644 --- a/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls +++ b/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls @@ -9,7 +9,7 @@ Parameter DESCRIPTION = "Performs an incremental load and compile of all changes Method OnPull() As %Status { - set loadSC = $$$OK + set sc = $$$OK set nFiles = 0 for i=1:1:$get(..ModifiedFiles){ @@ -17,8 +17,8 @@ Method OnPull() As %Status if ((internalName = "") && (..ModifiedFiles(i).changeType '= "D")) { write !, ..ModifiedFiles(i).externalName, " was not imported into the database and will not be compiled. " } elseif (..ModifiedFiles(i).changeType = "D") { - set sc = ..DeleteFile(internalName) - if sc { + set delSC = ..DeleteFile(internalName) + if delSC { write !, ..ModifiedFiles(i).externalName, " was deleted." } else { write !, "WARNING: Deletion of ", ..ModifiedFiles(i).externalName, " failed." @@ -26,8 +26,7 @@ Method OnPull() As %Status } else { set compilelist(internalName) = "" set nFiles = nFiles + 1 - set loadSC = $$$ADDSC(loadSC,##class(SourceControl.Git.Utils).ImportItem(internalName, 1)) - $$$ThrowOnError(loadSC) + set sc = $$$ADDSC(sc,##class(SourceControl.Git.Utils).ImportItem(internalName, 1)) } } @@ -35,7 +34,13 @@ Method OnPull() As %Status write !, "Nothing to compile." quit $$$OK } - quit $system.OBJ.CompileList(.compilelist, "ck") + set sc = $$$ADDSC(sc,$system.OBJ.CompileList(.compilelist, "ck")) + if $$$comClassDefined("Ens.Director") && ##class(Ens.Director).IsProductionRunning() { + write !,"Updating production... " + set sc = $$$ADDSC(sc,##class(Ens.Director).UpdateProduction()) + write "done." + } + quit sc } Method DeleteFile(item As %String) As %Status @@ -71,4 +76,3 @@ Method DeleteFile(item As %String) As %Status } } - diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls index 94290b60..bd96f37e 100644 --- a/cls/SourceControl/Git/Utils.cls +++ b/cls/SourceControl/Git/Utils.cls @@ -546,14 +546,19 @@ ClassMethod GetCurrentBranch() As %String ClassMethod Pull(remote As %String = "origin") As %Status { + New %gitSCOutputFlag + Set %gitSCOutputFlag = 1 #define Force 1 do ##class(SourceControl.Git.Utils).RunGitCommandWithInput("branch",,.errStream,.outStream,"--show-current") set branchName = outStream.ReadLine(outStream.Size) write !, "Pulling from branch: ", branchName kill errStream, outStream set returnCode = ..RunGitWithArgs(.errStream, .outStream, "pull", remote, branchName) - - w !, "Pull ran with return code: " _ returnCode + write ! + do outStream.OutputToDevice() + write ! + do errStream.OutputToDevice() + write !, "Pull ran with return code: " _ returnCode quit $$$OK } @@ -626,10 +631,13 @@ ClassMethod ExternalName(InternalName As %String, ByRef MappingExists As %Boolea ClassMethod AddToServerSideSourceControl(InternalName As %String) As %Status { - #dim i as %Integer + #dim i as %Integer #dim ec as %Status = $$$OK for i = 1:1:$length(InternalName, ",") { #dim item as %String = ..NormalizeExtension($piece(InternalName, ",", i)) + if (item = "") { + continue + } set @..#Storage@("items", item) = "" #dim sc as %Status = ..ImportItem(item, 1) if 'sc { @@ -1791,12 +1799,16 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O } } } elseif syncIrisWithCommand { - do ..PrintStreams(errStream, outStream) + if '$data(%gitSCOutputFlag)#2 { + do ..PrintStreams(errStream, outStream) + } set buffer = ##class(SourceControl.Git.Util.Buffer).%New() do buffer.BeginCaptureOutput() set st = ..SyncIrisWithRepoThroughCommand(.outStream) set out = ##class(%Stream.GlobalCharacter).%New() do buffer.EndCaptureOutput(.out) + do outStream.MoveToEnd() + do errStream.MoveToEnd() if $$$ISOK(st) { while 'out.AtEnd { do outStream.WriteLine(out.ReadLine()) @@ -1836,11 +1848,31 @@ ClassMethod SyncIrisWithRepoThroughCommand(ByRef outStream) As %Status } } elseif (line [ "|") { set externalName = $zstrip($piece(line,"|",1),"<>W") - set modification = ##class(SourceControl.Git.Modification).%New() - set modification.changeType = "C" - set modification.internalName = ##class(SourceControl.Git.Utils).NameToInternalName(externalName,,0) - set modification.externalName = externalName - set files($i(files)) = modification + if $Extract(externalName,1,3) = "..." { + // For extremely long file names, git may truncate the path. + // Simplifying assumption: this is a class, because nothing else would have that long a name. + // In other cases, we'll just end up logging the invalid externalName. + if $Piece(externalName,".",*) = "cls" { + set possibleClasses = ..ExpandClasses(externalName) + set pointer = 0 + while $ListNext(possibleClasses,pointer,class) { + set modification = ##class(SourceControl.Git.Modification).%New() + set modification.changeType = "C" + set modification.internalName = class_".CLS" + set modification.externalName = ..ExternalName(modification.internalName) + set files($i(files)) = modification + } + } else { + write !,"WARNING: unable to translate external name ",externalName + continue + } + } else { + set modification = ##class(SourceControl.Git.Modification).%New() + set modification.changeType = "C" + set modification.internalName = ##class(SourceControl.Git.Utils).NameToInternalName(externalName,,0) + set modification.externalName = externalName + set files($i(files)) = modification + } } } @@ -1858,6 +1890,18 @@ ClassMethod SyncIrisWithRepoThroughCommand(ByRef outStream) As %Status quit ##class(SourceControl.Git.PullEventHandler).ForModifications(.files) } +ClassMethod ExpandClasses(externalName As %String) As %List +{ + set internalName = $Piece(externalName,".",1,*-1) + set internalName = $Extract(internalName,4,*) + set internalName = $Translate(internalName,"/\%",".."_..PercentClassReplace()) + &sql(select %DLIST(Name) into :classes from %Dictionary.ClassDefinition where Name like '%'||:internalName) + if (SQLCODE < 0) { + Throw ##class(%Exception.SQL).CreateFromSQLCODE(SQLCODE,%msg) + } + quit classes +} + ClassMethod ParseDiffStream(stream As %Stream.Object, verbose As %Boolean = 1, Output files) { kill files diff --git a/cls/_zpkg/isc/sc/git/Socket.cls b/cls/_zpkg/isc/sc/git/Socket.cls index 0bc691f8..48c5e4ed 100644 --- a/cls/_zpkg/isc/sc/git/Socket.cls +++ b/cls/_zpkg/isc/sc/git/Socket.cls @@ -11,36 +11,40 @@ Property OriginalDevice; ClassMethod Run() { - If %request.Get("method") = "preview" { - set branchName = ##class(SourceControl.Git.Utils).GetCurrentBranch() - do ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "fetch") - kill errStream, outStream - do ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "log", "HEAD..origin", "--name-status") - do ##class(SourceControl.Git.Utils).PrintStreams(errStream, outStream) - } ElseIf %request.Get("method") = "pull" { - Do ##class(SourceControl.Git.API).Pull() - } ElseIf %request.Get("method") = "init" { - Do ##class(SourceControl.Git.Utils).Init() + If %request.Get("method") = "preview" { + Set branchName = ##class(SourceControl.Git.Utils).GetCurrentBranch() + Write !,"Current branch: ",branchName + Do ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "fetch") + Kill errStream, outStream + Do ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "log", "HEAD..origin", "--name-status") + Do ##class(SourceControl.Git.Utils).PrintStreams(errStream, outStream) + If (outStream.Size = 0) && (errStream.Size = 0) { + Write !,"Already up to date." + } + } ElseIf %request.Get("method") = "pull" { + Do ##class(SourceControl.Git.API).Pull() + } ElseIf %request.Get("method") = "init" { + Do ##class(SourceControl.Git.Utils).Init() Write !,"Done." - } ElseIf %request.Get("method") = "clone" { - Set remote = %request.Get("remote") - Do ##class(SourceControl.Git.Utils).Clone(remote) + } ElseIf %request.Get("method") = "clone" { + Set remote = %request.Get("remote") + Do ##class(SourceControl.Git.Utils).Clone(remote) Write !,"Done." - } ElseIf %request.Get("method") = "sshkeygen" { - Do ##class(SourceControl.Git.Utils).GenerateSSHKeyPair() + } ElseIf %request.Get("method") = "sshkeygen" { + Do ##class(SourceControl.Git.Utils).GenerateSSHKeyPair() Write !,"Done." - } Else { - Write !!,"Invalid method selected.",!! - } + } Else { + Write !!,"Invalid method selected.",!! + } } Method OnPreServer() As %Status { - If '$System.Security.Check("%Development","USE") { - Quit $$$ERROR($$$AccessDenied) - } + If '$SYSTEM.Security.Check("%Development","USE") { + Quit $$$ERROR($$$AccessDenied) + } If (%request.Get("$NAMESPACE") '= "") { - Set $Namespace = %request.Get("$NAMESPACE") + Set $NAMESPACE = %request.Get("$NAMESPACE") } Quit $$$OK } @@ -56,9 +60,11 @@ Method Server() As %Status // In subclasses: Do Something that produces output to the current device. // It will be sent back to the client, Base64-encoded, over the web socket connection. - Do ..Run() + Do ..Run() } Catch e { Do e.Log() + Write !,"An error occurred. More details can be found in the Application error log." + Write !,$SYSTEM.Status.GetErrorText(e.AsStatus()) Set tSC = e.AsStatus() } @@ -77,7 +83,7 @@ Method StartOutputCapture() [ ProcedureBlock = 0 ] #dim tSC As %Status = $$$OK #dim tRedirected As %Boolean = 0 Try { - Set %server = $this + Set %server = $THIS Set ..OriginallyRedirected = 0 Set ..OriginalMnemonic = "" Set ..OriginalDevice = $IO @@ -107,27 +113,27 @@ Method StartOutputCapture() [ ProcedureBlock = 0 ] #; Public entry points for I/O redirection wstr(s) Do write(s) Quit -wchr(a) Do write($char(a)) +wchr(a) Do write($CHAR(a)) Quit wnl Do write($$$EOL) Set $X = 0 Quit wff Do wnl Quit wtab(n) New tTab - Set tTab = $J("",$S(n>$X:n-$X,1:0)) + Set tTab = $JUSTIFY("",$SELECT(n>$X:n-$X,1:0)) Do write(tTab) Quit write(str) // If there was an argumentless NEW, cache the output and leave it at that. // This will be output next time there's a write with %server in scope. - If '$IsObject($Get(%server)) { - Set ^||OutputCapture.Cache($i(^||OutputCapture.Cache)) = str + If '$ISOBJECT($GET(%server)) { + Set ^||OutputCapture.Cache($INCREMENT(^||OutputCapture.Cache)) = str Quit } // Restore previous I/O redirection settings. New tOriginalDevice,i - Set tOriginalDevice = $io + Set tOriginalDevice = $IO If ##class(%Library.Device).ReDirectIO(0) { Use tOriginalDevice } @@ -138,8 +144,8 @@ write(str) Do ##class(%Library.Device).ReDirectIO(1) } - If $Data(^||OutputCapture.Cache) { - For i=1:1:$Get(^||OutputCapture.Cache) { + If $DATA(^||OutputCapture.Cache) { + For i=1:1:$GET(^||OutputCapture.Cache) { Do reallywrite(^||OutputCapture.Cache(i)) } Kill ^||OutputCapture.Cache @@ -155,7 +161,7 @@ write(str) reallywrite(pString) New tMsg Set tMsg = {"content":(pString)} // This is handy because it handles escaping of newlines, etc. - Do %server.Write($System.Encryption.Base64Encode(tMsg.%ToJSON())) + Do %server.Write($SYSTEM.Encryption.Base64Encode(tMsg.%ToJSON())) Quit rstr(len, time) Quit "" rchr(time) Quit "" @@ -179,7 +185,7 @@ Method EndOutputCapture() Method SendJSON(pObject As %DynamicAbstractObject) { - Set tOriginalDevice = $io + Set tOriginalDevice = $IO If ##class(%Library.Device).ReDirectIO(0) { Use tOriginalDevice } @@ -189,7 +195,7 @@ Method SendJSON(pObject As %DynamicAbstractObject) If ..OriginallyRedirected { Do ##class(%Library.Device).ReDirectIO(1) } - Do ..Write($System.Encryption.Base64Encode(pObject.%ToJSON())) + Do ..Write($SYSTEM.Encryption.Base64Encode(pObject.%ToJSON())) Do ##class(%Library.Device).ReDirectIO(1) Use tOriginalDevice::("^"_$ZNAME) }