Skip to content

Simplify pull and configuration #255

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

Merged
merged 7 commits into from
May 18, 2023
Merged
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ 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.2.0] - Unreleased

### Added
- Page to support deployment (git pull and run pull event handler) with verbose output
- Support for git clone to initialize namespace via Settings page and `##class(SourceControl.Git.API).Configure()` (#234, #237)
- Support for automatically creating SSH keys for use as deploy keys via Settings page and `Configure()` (#33)

### Fixed
- Protect against Favorites links containing control characters (#254)
- Green checks for valid paths shown consistently (#229)

## [2.1.1] - 2023-02-24

### Fixed
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
```
d ##class(SourceControl.Git.API).Configure()
```
This will also allow you to generate an SSH key for use as (e.g.) a deploy key and to initialize or clone a git repo.
3. If using VSCode: Set up `isfs` server-side editing. First, save your current workspace in which you have the code open. Then, open the `.code-workspace` file generated by VS Code and add the following to the list of folders:
```
{
Expand Down
4 changes: 2 additions & 2 deletions cls/SourceControl/Git/API.cls
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ ClassMethod Configure()
}

/// API for git pull - just wraps Utils
ClassMethod Pull()
ClassMethod Pull(preview As %Boolean = 0)
{
quit ##class(SourceControl.Git.Utils).Pull()
quit ##class(SourceControl.Git.Utils).Pull(,.preview)
}

/// Locks the environment to prevent changes to code other than through git pull.
Expand Down
1 change: 1 addition & 0 deletions cls/SourceControl/Git/Extension.cls
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,4 @@ Method AddToSourceControl(InternalName As %String, Description As %String = "")
}

}

1 change: 1 addition & 0 deletions cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ Method OnPull() As %Status
}

}

61 changes: 60 additions & 1 deletion cls/SourceControl/Git/Settings.cls
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Property gitBinPath As %String(MAXLEN = "");
/// Local git repo root folder
Property namespaceTemp As %String(MAXLEN = "") [ InitialExpression = {##class(SourceControl.Git.Utils).TempFolder()}, Required ];

/// Path to private key file (for ssh remotes)
/// Path to private key file for SSH remotes; if file does not exist, later prompts will help set it up with proper ownership
Property privateKeyFile As %String(MAXLEN = "") [ InitialExpression = {##class(SourceControl.Git.Utils).PrivateKeyFile()} ];

/// Event handler class for git pull
Expand Down Expand Up @@ -104,8 +104,67 @@ ClassMethod Configure() As %Boolean [ CodeMode = objectgenerator ]
}
do %code.WriteLine(" $$$ThrowOnError(inst.%Save())")
do %code.WriteLine(" write !,""Settings saved.""")
do %code.WriteLine(" do inst.OnAfterConfigure()")
do %code.WriteLine(" quit 1")
}

Method OnAfterConfigure() As %Boolean
{
set defaultPromptFlag = $$$DisableBackupCharMask + $$$TrapCtrlCMask + $$$EnableQuitCharMask + $$$DisableHelpCharMask + $$$DisableHelpContextCharMask + $$$TrapErrorMask
if (..privateKeyFile '= "") && '##class(%File).Exists(..privateKeyFile) {
set value = 1
set response = ##class(%Library.Prompt).GetYesNo("Do you wish to create a new SSH key pair?",.value,,defaultPromptFlag)
if (response '= $$$SuccessResponse) {
quit
}
if value {
#dim workMgr As %SYSTEM.AbstractWorkMgr
// using work queue manager ensures proper OS user context when running ssh-keygen
set workMgr = $System.WorkMgr.%New("")
$$$ThrowOnError(workMgr.Queue("##class(SourceControl.Git.Utils).GenerateSSHKeyPair"))
$$$ThrowOnError(workMgr.Sync())
set pubKeyName = ..privateKeyFile_".pub"
if ##class(%File).Exists(pubKeyName) {
set pubStream = ##class(%Stream.FileCharacter).%OpenId(pubKeyName,,.sc)
$$$ThrowOnError(sc)
Write !,"Public key (for use as ""deploy key"", etc.):",!
do pubStream.OutputToDevice()
Write !
}
}
}

set gitDir = ##class(%File).NormalizeDirectory(..namespaceTemp)_".git"
if '##class(%File).DirectoryExists(gitDir) {
set list(1) = "Initialize empty repo"
set list(2) = "Clone..."
set list(3) = "Do nothing"
set value = ""
while ('+$get(value)) {
set response = ##class(%Library.Prompt).GetMenu("No git repo exists in "_..namespaceTemp_". Choose an option:",.value,.list,,defaultPromptFlag + $$$InitialDisplayMask)
if (response '= $$$SuccessResponse) && (response '= $$$BackupResponse) {
return
}
}
if (value = 1) {
// using work queue manager ensures proper OS user context/file ownership
set workMgr = $System.WorkMgr.%New("")
$$$ThrowOnError(workMgr.Queue("##class(SourceControl.Git.Utils).Init"))
$$$ThrowOnError(workMgr.Sync())
} elseif (value = 2) {
set response = ##class(%Library.Prompt).GetString("Git remote URL (note: if authentication is required, use SSH, not HTTPS):",.remote,,,,defaultPromptFlag)
if (response '= $$$SuccessResponse) {
quit
}
if (remote = "") {
quit
}
// using work queue manager ensures proper OS user context/file ownership
set workMgr = $System.WorkMgr.%New("")
$$$ThrowOnError(workMgr.Queue("##class(SourceControl.Git.Utils).Clone",remote))
$$$ThrowOnError(workMgr.Sync())
}
}
}

}
85 changes: 74 additions & 11 deletions cls/SourceControl/Git/Utils.cls
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,7 @@ ClassMethod UserAction(InternalName As %String, MenuName As %String, ByRef Targe
// cleanup items info
kill @..#Storage@("items")
kill @..#Storage@("TSH")
do ..RunGitCommand("init",.errStream,.outStream)
$$$NewLineIfNonEmptyStream(errStream)
do errStream.OutputToDevice()
$$$NewLineIfNonEmptyStream(outStream)
do outStream.OutputToDevice()
do ..Init()
} else {
set ec = ..MakeError("Unable to create folder "_..TempFolder())
}
Expand Down Expand Up @@ -271,6 +267,16 @@ ClassMethod AfterUserAction(Type As %Integer, Name As %String, InternalName As %
quit $$$OK
}

ClassMethod Init() As %Status
{
do ..RunGitCommand("init",.errStream,.outStream)
$$$NewLineIfNonEmptyStream(errStream)
do errStream.OutputToDevice()
$$$NewLineIfNonEmptyStream(outStream)
do outStream.OutputToDevice()
quit $$$OK
}

ClassMethod Revert(InternalName As %String) As %Status
{
set filename = ..FullExternalName(.InternalName)
Expand Down Expand Up @@ -342,7 +348,7 @@ ClassMethod Fetch(ByRef diffFiles) As %Status
quit $$$OK
}

ClassMethod Pull(remote As %String = "origin") As %Status
ClassMethod Pull(remote As %String = "origin", preview As %Boolean = 0) As %Status
{
#define Force 1
do ##class(SourceControl.Git.Utils).RunGitCommandWithInput("branch",,.errStream,.outStream,"--show-current")
Expand Down Expand Up @@ -376,7 +382,12 @@ ClassMethod Pull(remote As %String = "origin") As %Status
}
if ('$data(files)) {
write !, ?4, "None"
write !, "Already up to date. Git Pull will not be executed."
if preview {
quit $$$OK
}
write !, "Already up to date."
quit $$$OK
} elseif preview {
quit $$$OK
}

Expand Down Expand Up @@ -423,6 +434,52 @@ ClassMethod Pull(remote As %String = "origin") As %Status
quit event.OnPull()
}

ClassMethod Clone(remote As %String) As %Status
{
set settings = ##class(SourceControl.Git.Settings).%New()
// TODO: eventually use /ENV flag with GIT_TERMINAL_PROMPT=0. (This isn't doc'd yet and is only in really new versions.)
set sc = ..RunGitWithArgs(.errStream, .outStream, "clone", remote, settings.namespaceTemp)
$$$NewLineIfNonEmptyStream(errStream)
while 'errStream.AtEnd {
write errStream.ReadLine(),!
}
$$$NewLineIfNonEmptyStream(outStream)
while 'outStream.AtEnd {
write outStream.ReadLine(),!
}
quit $$$OK
}

ClassMethod GenerateSSHKeyPair() As %Status
{
set settings = ##class(SourceControl.Git.Settings).%New()
set filename = settings.privateKeyFile
set email = settings.gitUserEmail
set dir = ##class(%File).GetDirectory(filename)
if ##class(%File).Exists(filename) {
Throw ##class(%Exception.General).%New("File "_filename_" already exists")
}
do ##class(%File).CreateDirectoryChain(dir)
set outLog = ##class(%Library.File).TempFilename()
set errLog = ##class(%Library.File).TempFilename()
do $zf(-100,"/SHELL /STDOUT="_$$$QUOTE(outLog)_" /STDERR="_$$$QUOTE(errLog),
"ssh-keygen",
"-t","ed25519",
"-C",email,
"-f",filename,
"-N","")

set errStream = ##class(%Stream.FileCharacter).%OpenId(errLog,,.sc)
set outStream = ##class(%Stream.FileCharacter).%OpenId(outLog,,.sc)
set outStream.TranslateTable="UTF8"
for stream=errStream,outStream {
set stream.RemoveOnClose = 1
}
do outStream.OutputToDevice()
do errStream.OutputToDevice()
quit $$$OK
}

ClassMethod IsNamespaceInGit() As %Boolean [ CodeMode = expression ]
{
##class(%File).Exists(..TempFolder()_".git")
Expand Down Expand Up @@ -1789,15 +1846,22 @@ ClassMethod ConfigureWeb()
set installNamespace = $Namespace
new $Namespace
set $Namespace = "%SYS"
write !,"Adding favorite for all users... "
write !,"Adding favorites for all users:"
set sql = "insert or update into %SYS_Portal.Users (Username, Page, Data) "_
"select ID,?,? from Security.Users"
set caption = "Git: "_installNamespace
set link = "/isc/studio/usertemplates/gitsourcecontrol/webuidriver.csp/"_installNamespace_"/"
do ##class(%SQL.Statement).%ExecDirect(,sql,caption,link).%Display()
write !,"Adding Git favorite... "
set statement = ##class(%SQL.Statement).%New()
set statement.%SelectMode = 0
do ##class(%SQL.Statement).%ExecDirect(statement,sql,caption,link).%Display()
set caption = "Git Pull: "_installNamespace
set link = "/isc/studio/usertemplates/gitsourcecontrol/pull.csp?$NAMESPACE="_installNamespace
write !,"Adding Git Pull favorite... "
do ##class(%SQL.Statement).%ExecDirect(statement,sql,caption,link).%Display()
write !,"Setting GroupById to %ISCMgtPortal for /isc/studio/usertemplates... "
set sql = "update Security.Applications set GroupById='%ISCMgtPortal' where ID = '/isc/studio/usertemplates'"
do ##class(%SQL.Statement).%ExecDirect(,sql).%Display()
do ##class(%SQL.Statement).%ExecDirect(statement,sql).%Display()
}

ClassMethod CheckInitialization()
Expand Down Expand Up @@ -1919,4 +1983,3 @@ ClassMethod BuildCEInstallationPackage(ByRef destination As %String) As %Status
}

}

Loading