Skip to content

Commit 533e5a5

Browse files
committed
CLI support for configuring SSH keys and clone/init
1 parent 2ddc097 commit 533e5a5

File tree

4 files changed

+80
-14
lines changed

4 files changed

+80
-14
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

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

1515
### Fixed
1616
- Protect against Favorites links containing control characters (#254)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
```
1919
d ##class(SourceControl.Git.API).Configure()
2020
```
21+
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.
2122
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:
2223
```
2324
{

cls/SourceControl/Git/Settings.cls

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Property gitBinPath As %String(MAXLEN = "");
1111
/// Local git repo root folder
1212
Property namespaceTemp As %String(MAXLEN = "") [ InitialExpression = {##class(SourceControl.Git.Utils).TempFolder()}, Required ];
1313

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

1717
/// Event handler class for git pull
@@ -104,8 +104,64 @@ ClassMethod Configure() As %Boolean [ CodeMode = objectgenerator ]
104104
}
105105
do %code.WriteLine(" $$$ThrowOnError(inst.%Save())")
106106
do %code.WriteLine(" write !,""Settings saved.""")
107+
do %code.WriteLine(" do inst.OnAfterConfigure()")
107108
do %code.WriteLine(" quit 1")
108109
}
109110

111+
Method OnAfterConfigure() As %Boolean
112+
{
113+
set defaultPromptFlag = $$$DisableBackupCharMask + $$$TrapCtrlCMask + $$$EnableQuitCharMask + $$$DisableHelpCharMask + $$$DisableHelpContextCharMask + $$$TrapErrorMask
114+
if (..privateKeyFile '= "") && '##class(%File).Exists(..privateKeyFile) {
115+
set value = 1
116+
set response = ##class(%Library.Prompt).GetYesNo("Do you wish to create a new SSH key pair?",.value,,defaultPromptFlag)
117+
if (response '= $$$SuccessResponse) {
118+
quit
119+
}
120+
if value {
121+
#dim workMgr As %SYSTEM.AbstractWorkMgr
122+
set workMgr = $System.WorkMgr.%New("")
123+
$$$ThrowOnError(workMgr.Queue("##class(SourceControl.Git.Utils).GenerateSSHKeyPair"))
124+
$$$ThrowOnError(workMgr.Sync())
125+
set pubKeyName = ..privateKeyFile_".pub"
126+
if ##class(%File).Exists(pubKeyName) {
127+
set pubStream = ##class(%Stream.FileCharacter).%OpenId(pubKeyName,,.sc)
128+
$$$ThrowOnError(sc)
129+
Write !,"Public key (for use as ""deploy key"", etc.):",!
130+
do pubStream.OutputToDevice()
131+
Write !
132+
}
133+
}
134+
}
135+
136+
set gitDir = ##class(%File).NormalizeDirectory(..namespaceTemp)_".git"
137+
if '##class(%File).DirectoryExists(gitDir) {
138+
set list(1) = "Initialize empty repo"
139+
set list(2) = "Clone..."
140+
set list(3) = "Do nothing"
141+
set value = ""
142+
while ('+$get(value)) {
143+
set response = ##class(%Library.Prompt).GetMenu("No git repo exists in "_..namespaceTemp_". Choose an option:",.value,.list,,defaultPromptFlag + $$$InitialDisplayMask)
144+
if (response '= $$$SuccessResponse) && (response '= $$$BackupResponse) {
145+
return
146+
}
147+
}
148+
if (value = 1) {
149+
set workMgr = $System.WorkMgr.%New("")
150+
$$$ThrowOnError(workMgr.Queue("##class(SourceControl.Git.Utils).Init"))
151+
$$$ThrowOnError(workMgr.Sync())
152+
} elseif (value = 2) {
153+
set response = ##class(%Library.Prompt).GetString("Git remote URL (note: if authentication is required, use SSH, not HTTPS):",.remote,,,,defaultPromptFlag)
154+
if (response '= $$$SuccessResponse) {
155+
quit
156+
}
157+
if (remote = "") {
158+
quit
159+
}
160+
set workMgr = $System.WorkMgr.%New("")
161+
$$$ThrowOnError(workMgr.Queue("##class(SourceControl.Git.Utils).Clone",remote))
162+
$$$ThrowOnError(workMgr.Sync())
163+
}
164+
}
110165
}
111166

167+
}

cls/SourceControl/Git/Utils.cls

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -267,13 +267,14 @@ ClassMethod AfterUserAction(Type As %Integer, Name As %String, InternalName As %
267267
quit $$$OK
268268
}
269269

270-
ClassMethod Init()
270+
ClassMethod Init() As %Status
271271
{
272272
do ..RunGitCommand("init",.errStream,.outStream)
273273
$$$NewLineIfNonEmptyStream(errStream)
274274
do errStream.OutputToDevice()
275275
$$$NewLineIfNonEmptyStream(outStream)
276276
do outStream.OutputToDevice()
277+
quit $$$OK
277278
}
278279

279280
ClassMethod Revert(InternalName As %String) As %Status
@@ -433,31 +434,39 @@ ClassMethod Pull(remote As %String = "origin", preview As %Boolean = 0) As %Stat
433434
quit event.OnPull()
434435
}
435436

436-
ClassMethod Clone(remote As %String)
437+
ClassMethod Clone(remote As %String) As %Status
437438
{
438439
set settings = ##class(SourceControl.Git.Settings).%New()
440+
// TODO: eventually use /ENV flag with GIT_TERMINAL_PROMPT=0. (This isn't doc'd yet and is only in really new versions.)
439441
set sc = ..RunGitWithArgs(.errStream, .outStream, "clone", remote, settings.namespaceTemp)
440442
$$$NewLineIfNonEmptyStream(errStream)
441-
do errStream.OutputToDevice()
443+
while 'errStream.AtEnd {
444+
write errStream.ReadLine(),!
445+
}
442446
$$$NewLineIfNonEmptyStream(outStream)
443-
do outStream.OutputToDevice()
447+
while 'outStream.AtEnd {
448+
write outStream.ReadLine(),!
449+
}
450+
quit $$$OK
444451
}
445452

446-
ClassMethod GenerateSSHKeyPair()
453+
ClassMethod GenerateSSHKeyPair() As %Status
447454
{
448455
set settings = ##class(SourceControl.Git.Settings).%New()
449-
set dir = ##class(%File).GetDirectory(settings.privateKeyFile)
450-
if ##class(%File).Exists(settings.privateKeyFile) {
451-
Throw ##class(%Exception.General).%New("File "_settings.privateKeyFile_" already exists")
456+
set filename = settings.privateKeyFile
457+
set email = settings.gitUserEmail
458+
set dir = ##class(%File).GetDirectory(filename)
459+
if ##class(%File).Exists(filename) {
460+
Throw ##class(%Exception.General).%New("File "_filename_" already exists")
452461
}
453462
do ##class(%File).CreateDirectoryChain(dir)
454463
set outLog = ##class(%Library.File).TempFilename()
455464
set errLog = ##class(%Library.File).TempFilename()
456465
do $zf(-100,"/SHELL /STDOUT="_$$$QUOTE(outLog)_" /STDERR="_$$$QUOTE(errLog),
457466
"ssh-keygen",
458467
"-t","ed25519",
459-
"-C",settings.gitUserEmail,
460-
"-f",settings.privateKeyFile,
468+
"-C",email,
469+
"-f",filename,
461470
"-N","")
462471

463472
set errStream = ##class(%Stream.FileCharacter).%OpenId(errLog,,.sc)
@@ -468,6 +477,7 @@ ClassMethod GenerateSSHKeyPair()
468477
}
469478
do outStream.OutputToDevice()
470479
do errStream.OutputToDevice()
480+
quit $$$OK
471481
}
472482

473483
ClassMethod IsNamespaceInGit() As %Boolean [ CodeMode = expression ]
@@ -1973,4 +1983,3 @@ ClassMethod BuildCEInstallationPackage(ByRef destination As %String) As %Status
19731983
}
19741984

19751985
}
1976-

0 commit comments

Comments
 (0)