Skip to content

Commit 71f083c

Browse files
committed
Improvements to configuration
Fixes #207 (documentation + simpler config) Fixes #209 (adds favorite) Fixes #213 (avoids confusion on settings page and adds reasonable default user name/email)
1 parent c829f89 commit 71f083c

File tree

6 files changed

+135
-37
lines changed

6 files changed

+135
-37
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ This might look like:
5252
### Security
5353
If you want to interact with remotes from VSCode/Studio directly (e.g., to push/pull), you must use ssh (rather than https), create a public/private key pair to identify the instance (not yourself), configure the private key file for use in Settings, and configure the public key as a deploy key in the remote(s).
5454
55+
### Git Security Features
56+
Newer git versions may produce output like:
57+
```
58+
fatal: detected dubious ownership in repository at 'C:/Your/Repo/Root'
59+
To add an exception for this directory, call:
60+
61+
git config --global --add safe.directory C:/Your/Repo/Root
62+
63+
Set the environment variable GIT_TEST_DEBUG_UNSAFE_DIRECTORIES=true and run
64+
again for more information.
65+
```
66+
It is important for the namespace temp folder to be owned by the user IRIS runs as. (On Unix, commonly irisusr; on Windows, generally a designated service account or SYSTEM.) Setting this config flag is unlikely to actually help; just make sure the ownership is correct.
67+
5568
### Setting up multiple GitHub deploy keys on one machine
5669
5770
Assuming you have the local and remote repositories created,

cls/SourceControl/Git/Settings.cls

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ Class SourceControl.Git.Settings Extends %RegisteredObject
66
{
77

88
/// Path to git executable
9-
Property gitBinPath As %String;
9+
Property gitBinPath As %String(MAXLEN = "");
1010

1111
/// Local git repo root folder
12-
Property namespaceTemp As %String [ InitialExpression = {##class(SourceControl.Git.Utils).TempFolder()}, Required ];
12+
Property namespaceTemp As %String(MAXLEN = "") [ InitialExpression = {##class(SourceControl.Git.Utils).TempFolder()}, Required ];
1313

1414
/// Path to private key file (for ssh remotes)
15-
Property privateKeyFile As %String [ InitialExpression = {##class(SourceControl.Git.Utils).PrivateKeyFile()} ];
15+
Property privateKeyFile As %String(MAXLEN = "") [ InitialExpression = {##class(SourceControl.Git.Utils).PrivateKeyFile()} ];
1616

1717
/// Event handler class for git pull
18-
Property pullEventClass As %String [ InitialExpression = {##class(SourceControl.Git.Utils).PullEventClass()}, Required ];
18+
Property pullEventClass As %String(MAXLEN = 255) [ InitialExpression = {##class(SourceControl.Git.Utils).PullEventClass()}, Required ];
1919

2020
/// Character to replace % symbol when importing %-classes into the file systems
2121
Property percentClassReplace As %String [ InitialExpression = {##class(SourceControl.Git.Utils).PercentClassReplace()} ];
2222

2323
/// Attribution: Git username for user ${username}
24-
Property gitUserName As %String [ InitialExpression = {##class(SourceControl.Git.Utils).GitUserName()}, Required ];
24+
Property gitUserName As %String(MAXLEN = 255) [ InitialExpression = {##class(SourceControl.Git.Utils).GitUserName()} ];
2525

2626
/// Attribution: Email address for user ${username}
27-
Property gitUserEmail As %String [ InitialExpression = {##class(SourceControl.Git.Utils).GitUserEmail()}, Required ];
27+
Property gitUserEmail As %String(MAXLEN = 255) [ InitialExpression = {##class(SourceControl.Git.Utils).GitUserEmail()} ];
2828

2929
Property Mappings [ MultiDimensional ];
3030

@@ -40,6 +40,11 @@ Method %OnNew() As %Status
4040

4141
Method %Save() As %Status
4242
{
43+
set sc = ..%ValidateObject()
44+
if $$$ISERR(sc) {
45+
quit sc
46+
}
47+
4348
set sysStorage = ##class(SourceControl.Git.Utils).InstallNamespaceStorage()
4449
set storage = ##class(SourceControl.Git.Utils).#Storage
4550
kill @sysStorage@("%gitBinPath")
@@ -48,7 +53,7 @@ Method %Save() As %Status
4853
}
4954
kill ^||GitVersion
5055

51-
set @storage@("settings","namespaceTemp") = ##class(SourceControl.Git.Utils).AddSlash(..namespaceTemp)
56+
set @storage@("settings","namespaceTemp") = ##class(%Library.File).NormalizeDirectory(..namespaceTemp)
5257
if ('##class(%File).DirectoryExists(@storage@("settings","namespaceTemp"))){
5358
do ##class(%Library.File).CreateDirectoryChain(@storage@("settings","namespaceTemp"))
5459
}
@@ -94,3 +99,4 @@ ClassMethod Configure() As %Boolean [ CodeMode = objectgenerator ]
9499
}
95100

96101
}
102+

cls/SourceControl/Git/Utils.cls

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Include (%occStatus, %occErrors, SourceControl.Git)
1+
Include (%occStatus, %occErrors, SourceControl.Git, %sySecurity)
22

33
Class SourceControl.Git.Utils [ Abstract, ProcedureBlock ]
44
{
@@ -99,12 +99,12 @@ ClassMethod GitBinPath(Output isDefault) As %String
9999

100100
ClassMethod GitUserName() As %String
101101
{
102-
quit $get(@..#Storage@("settings","user",$username,"gitUserName"))
102+
quit $get(@..#Storage@("settings","user",$username,"gitUserName"),$username)
103103
}
104104

105105
ClassMethod GitUserEmail() As %String
106106
{
107-
quit $get(@..#Storage@("settings","user",$username,"gitUserEmail"))
107+
quit $get(@..#Storage@("settings","user",$username,"gitUserEmail"),$username_"@"_$zconvert(##class(%SYS.System).GetNodeName(),"L"))
108108
}
109109

110110
ClassMethod PrivateKeyFile() As %String
@@ -122,14 +122,6 @@ ClassMethod InstallNamespace() As %String [ CodeMode = expression ]
122122
..#InstallNamespace
123123
}
124124

125-
ClassMethod AddSlash(path As %String) As %String
126-
{
127-
if path'="" && ($extract(path,*)'=..#Slash) {
128-
set path = path_..#Slash
129-
}
130-
quit path
131-
}
132-
133125
ClassMethod IsMenuGitCommand(menuItemName As %String) As %Boolean [ CodeMode = expression ]
134126
{
135127
$Find(..#GitMenuItems, ","_menuItemName_",") > 0
@@ -1373,27 +1365,30 @@ ClassMethod RunGitCommand(command As %String, Output errStream, Output outStream
13731365

13741366
ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", Output errStream, Output outStream, args...) As %Integer
13751367
{
1376-
set newArgs($increment(newArgs)) = "-C"
1377-
set newArgs($increment(newArgs)) = ..TempFolder()
1368+
// Special case: git --version is used internally even when the settings incorporated here may be invalid/unspecified.
1369+
if (command '= "--version") {
1370+
set newArgs($increment(newArgs)) = "-C"
1371+
set newArgs($increment(newArgs)) = ..TempFolder()
13781372

1379-
set privateKeyFile = ..PrivateKeyFile()
1380-
if (privateKeyFile '= "") {
1381-
if $$$isWINDOWS {
1382-
// Escape slashes
1383-
set privateKeyFile = $replace(privateKeyFile,"\","\\")
1373+
set privateKeyFile = ..PrivateKeyFile()
1374+
if (privateKeyFile '= "") {
1375+
if $$$isWINDOWS {
1376+
// Escape slashes
1377+
set privateKeyFile = $replace(privateKeyFile,"\","\\")
1378+
}
1379+
set newArgs($increment(newArgs)) = "-c"
1380+
// StrictHostKeyChecking=accept-new for good behavior on first connection
1381+
set newArgs($increment(newArgs)) = "core.sshCommand=ssh -F /dev/null -o StrictHostKeyChecking=accept-new -i "_privateKeyFile
13841382
}
1385-
set newArgs($increment(newArgs)) = "-c"
1386-
// StrictHostKeyChecking=accept-new for good behavior on first connection
1387-
set newArgs($increment(newArgs)) = "core.sshCommand=ssh -F /dev/null -o StrictHostKeyChecking=accept-new -i "_privateKeyFile
1388-
}
13891383

1390-
set username = ..GitUserName()
1391-
set email = ..GitUserEmail()
1384+
set username = ..GitUserName()
1385+
set email = ..GitUserEmail()
13921386

1393-
set newArgs($increment(newArgs)) = "-c"
1394-
set newArgs($increment(newArgs)) = "user.name="_username
1395-
set newArgs($increment(newArgs)) = "-c"
1396-
set newArgs($increment(newArgs)) = "user.email="_email
1387+
set newArgs($increment(newArgs)) = "-c"
1388+
set newArgs($increment(newArgs)) = "user.name="_username
1389+
set newArgs($increment(newArgs)) = "-c"
1390+
set newArgs($increment(newArgs)) = "user.email="_email
1391+
}
13971392

13981393
set newArgs($increment(newArgs)) = command
13991394

@@ -1751,6 +1746,41 @@ ClassMethod Localize()
17511746
}
17521747
}
17531748

1749+
ClassMethod ConfigureWeb()
1750+
{
1751+
set installNamespace = $Namespace
1752+
new $Namespace
1753+
set $Namespace = "%SYS"
1754+
write !,"Adding favorite for all users... "
1755+
set sql = "insert or update into %SYS_Portal.Users (Username, Page, Data) "_
1756+
"select ID,?,? from Security.Users"
1757+
set caption = "Git: "_installNamespace
1758+
set link = "/isc/studio/usertemplates/gitsourcecontrol/webuidriver.csp/"_installNamespace_"/"
1759+
do ##class(%SQL.Statement).%ExecDirect(,sql,caption,link).%Display()
1760+
write !,"Setting GroupById to %ISCMgtPortal for /isc/studio/usertemplates... "
1761+
set sql = "update Security.Applications set GroupById='%ISCMgtPortal' where ID = '/isc/studio/usertemplates'"
1762+
do ##class(%SQL.Statement).%ExecDirect(,sql).%Display()
1763+
}
1764+
1765+
ClassMethod CheckInitialization()
1766+
{
1767+
if ##class(SourceControl.Git.Utils).GitBinExists(.version) {
1768+
// Note: version includes "git"
1769+
write !,"Will use "_version_" (already installed and on the PATH)."
1770+
} else {
1771+
set path = ##class(SourceControl.Git.Utils).GitBinPath(.isDefault)
1772+
if isDefault {
1773+
write !,"WARNING: Could not find git on the PATH. Confirm that git is installed, then add it to the PATH or point to its path by running: "
1774+
write !,?5,"do ##class(SourceControl.Git.API).Configure()"
1775+
write !,"and answering the prompts."
1776+
} else {
1777+
write !,path," is not a valid path to a Git executable. Confirm that git is installed, then update the path to it by running: "
1778+
write !,?5,"do ##class(SourceControl.Git.API).Configure()"
1779+
write !,"and answering the prompts."
1780+
}
1781+
}
1782+
}
1783+
17541784
ClassMethod GetSourceControlInclude() As %String
17551785
{
17561786
quit $select(##class(%Library.EnsembleMgr).IsEnsembleInstalled():
@@ -1808,6 +1838,10 @@ ClassMethod BuildCEInstallationPackage(ByRef destination As %String) As %Status
18081838
set code($i(code)) = " Do ##class(SourceControl.Git.Utils).Localize()"
18091839
set code($i(code)) = " Write !!"
18101840
set code($i(code)) = " Do ##class(SourceControl.Git.Utils).OutputConfigureMessage()"
1841+
set code($i(code)) = " Write !!"
1842+
set code($i(code)) = " Do ##class(SourceControl.Git.Utils).ConfigureWeb()"
1843+
set code($i(code)) = " Write !!"
1844+
set code($i(code)) = " Do ##class(SourceControl.Git.Utils).CheckInitialization()"
18111845

18121846
// Put installer automation in class
18131847
do $System.OBJ.Delete("SourceControl.Git.Installer.CLS","-d")
@@ -1843,3 +1877,4 @@ ClassMethod BuildCEInstallationPackage(ByRef destination As %String) As %Status
18431877
}
18441878

18451879
}
1880+

module.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<Document name="git-source-control.ZPM">
44
<Module>
55
<Name>git-source-control</Name>
6-
<Version>2.0.6</Version>
6+
<Version>2.1.0</Version>
77
<Description>Server-side source control extension for use of Git on InterSystems platforms</Description>
88
<Keywords>git source control studio vscode</Keywords>
99
<Packaging>module</Packaging>
@@ -23,6 +23,8 @@
2323

2424
<Invoke Class="SourceControl.Git.Utils" Method="OutputConfigureMessage" />
2525
<Invoke Class="SourceControl.Git.Utils" Method="Localize" />
26+
<Invoke Class="SourceControl.Git.Utils" Method="ConfigureWeb" />
27+
<Invoke Class="SourceControl.Git.Utils" Method="CheckInitialization" />
2628
</Module>
2729
</Document>
2830
</Export>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
Include %sySecurity
2+
3+
Class UnitTest.SourceControl.Git.Initialization Extends %UnitTest.TestCase
4+
{
5+
6+
Method TestSetupFavorites()
7+
{
8+
Set page = "Git: "_$Namespace
9+
Set username = $Username
10+
&sql(delete from %SYS_Portal.Users where Page = :page and Username = :username)
11+
// Intentionally called twice!
12+
Do ##class(SourceControl.Git.Utils).ConfigureWeb()
13+
Do ##class(SourceControl.Git.Utils).ConfigureWeb()
14+
&sql(select count(*) into :favoriteCount from %SYS_Portal.Users where Page = :page and Username = :username)
15+
Do $$$AssertEquals(favoriteCount,1)
16+
Do $$$AssertTrue($$$SecurityApplicationsExists("/isc/studio/usertemplates",record))
17+
Do $$$AssertEquals($$$GetSecurityApplicationsGroupById(record),"%ISCMgtPortal")
18+
}
19+
20+
Method TestRunGitInGarbageContext()
21+
{
22+
Set settings = ##class(SourceControl.Git.Settings).%New()
23+
Set oldTemp = settings.namespaceTemp
24+
Set settings.namespaceTemp = ##class(%Library.File).TempFilename()_"nonexistentdir"
25+
Do $$$AssertStatusOK(settings.%Save())
26+
Try {
27+
Do ##class(%Library.File).RemoveDirectory(settings.namespaceTemp)
28+
// This is a prerequisite in any testing environment.
29+
Write ##class(SourceControl.Git.Utils).TempFolder()
30+
Do $$$AssertTrue(##class(SourceControl.Git.Utils).GitBinExists())
31+
} Catch e {
32+
Do $$$AssertFailure("Error occurred: "_$System.Status.GetErrorText(e.AsStatus()))
33+
}
34+
// OK for unit test to leak this if it was empty to start
35+
If (oldTemp '= "") {
36+
Set settings.namespaceTemp = oldTemp
37+
Do $$$AssertStatusOK(settings.%Save())
38+
}
39+
}
40+
41+
}
42+

test/UnitTest/SourceControl/Git/NameToInternalNameTest.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ Method OnBeforeAllTests() As %Status
9696
set settings = ##class(SourceControl.Git.Settings).%New()
9797
set ..OldNamespaceTemp = settings.namespaceTemp
9898
set ..OldPercentClassReplace = settings.percentClassReplace
99+
merge ..Mappings = @##class(SourceControl.Git.Utils).MappingsNode()
99100
set settings.namespaceTemp = $Piece(..Manager.CurrentDir,"test",1)
100101
set settings.percentClassReplace = "_"
101102
$$$ThrowOnError(settings.%Save())
102-
merge ..Mappings = @##class(SourceControl.Git.Utils).MappingsNode()
103103
kill @##class(SourceControl.Git.Utils).MappingsNode()
104104
Set app = $System.CSP.GetDefaultApp($Namespace)
105105
If (app '= "") {

0 commit comments

Comments
 (0)