Skip to content

Commit 95f8522

Browse files
authored
Merge pull request #222 from intersystems/better-initialization
Improvements to configuration and initialization
2 parents 4472cc3 + 66a8060 commit 95f8522

File tree

8 files changed

+155
-46
lines changed

8 files changed

+155
-46
lines changed

CHANGELOG.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,22 @@ 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.0.6] - Unreleased
8+
## [2.1.0] - Unreleased
99

1010
### Added
11-
- Support for Caché/Ensemble 2016.2.3, 2017.1.2, 2017.2.1, 2018.1.0 and later. (Treated as patch version bump because no compatibility impact for existing users.)
11+
- Support for Caché/Ensemble 2016.2.3, 2017.1.2, 2017.2.1, 2018.1.0 and later.
12+
- Installation adds a Management Portal favorite for all users (#209)
13+
- Improved configuration and defaults to simplify initial configuration (#213)
14+
- If no name/email is specified, defaults to `$username` and `$username@<hostname>`
1215

1316
### Fixed
1417
- "Import All" will properly recognize new files
1518
- "Import All" and "Export All" apply only to the current package manager context and disregard items outside that context
1619
- "Import All" treats "Other" document types (DFI, LUT, etc.) properly
1720

18-
### Changed
19-
- Various minor things under the hood to support use without the package manager and/or on older platforms
20-
2121
### Internal
2222
- Added CI script and tweaked unit tests to run properly in a container and bootstrap their own extension configuration
23+
- Tweaked various minor things under the hood to support use without the package manager and/or on older platforms
2324

2425
## [2.0.5] - 2022-12-02
2526

README.md

+18
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,26 @@ This might look like:
5050
![Example of mapping configuration](docs/images/settings.PNG "Example of mapping configuration")
5151
5252
### Security
53+
54+
#### Dubious Ownership
55+
Newer git versions may produce output like:
56+
```
57+
fatal: detected dubious ownership in repository at 'C:/Your/Repo/Root'
58+
To add an exception for this directory, call:
59+
60+
git config --global --add safe.directory C:/Your/Repo/Root
61+
62+
Set the environment variable GIT_TEST_DEBUG_UNSAFE_DIRECTORIES=true and run
63+
again for more information.
64+
```
65+
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.
66+
67+
#### Interacting with Remotes
5368
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).
5469
70+
#### IRIS Privileges
71+
For developers to be able to run this extension, they'll need the USE privilege on %System_Callout.
72+
5573
### Setting up multiple GitHub deploy keys on one machine
5674
5775
Assuming you have the local and remote repositories created,

cls/SourceControl/Git/Settings.cls

+14-7
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,8 @@ Method %Save() As %Status
4853
}
4954
kill ^||GitVersion
5055

51-
set @storage@("settings","namespaceTemp") = ##class(SourceControl.Git.Utils).AddSlash(..namespaceTemp)
56+
set ..namespaceTemp = ##class(%Library.File).NormalizeDirectory(..namespaceTemp)
57+
set @storage@("settings","namespaceTemp") = ..namespaceTemp
5258
if ('##class(%File).DirectoryExists(@storage@("settings","namespaceTemp"))){
5359
do ##class(%Library.File).CreateDirectoryChain(@storage@("settings","namespaceTemp"))
5460
}
@@ -94,3 +100,4 @@ ClassMethod Configure() As %Boolean [ CodeMode = objectgenerator ]
94100
}
95101

96102
}
103+

cls/SourceControl/Git/Utils.cls

+67-29
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

@@ -1470,7 +1465,7 @@ ClassMethod Name(InternalName As %String, ByRef MappingExists As %Boolean) As %S
14701465
}
14711466
}
14721467

1473-
if $$CheckProtect^%qccServer(InternalName) {
1468+
if '##class(%Library.RoutineMgr).UserType(InternalName) && $$CheckProtect^%qccServer(InternalName) {
14741469
quit ""
14751470
}
14761471

@@ -1751,6 +1746,45 @@ 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+
If '$System.Security.Check("%System_Callout","USE") {
1783+
write !,"WARNING: You do not have USE privilege on %System_Callout, which is needed to invoke git.",
1784+
"Unless this privilege is granted to users of the extension, it won't work."
1785+
}
1786+
}
1787+
17541788
ClassMethod GetSourceControlInclude() As %String
17551789
{
17561790
quit $select(##class(%Library.EnsembleMgr).IsEnsembleInstalled():
@@ -1808,6 +1842,10 @@ ClassMethod BuildCEInstallationPackage(ByRef destination As %String) As %Status
18081842
set code($i(code)) = " Do ##class(SourceControl.Git.Utils).Localize()"
18091843
set code($i(code)) = " Write !!"
18101844
set code($i(code)) = " Do ##class(SourceControl.Git.Utils).OutputConfigureMessage()"
1845+
set code($i(code)) = " Write !!"
1846+
set code($i(code)) = " Do ##class(SourceControl.Git.Utils).ConfigureWeb()"
1847+
set code($i(code)) = " Write !!"
1848+
set code($i(code)) = " Do ##class(SourceControl.Git.Utils).CheckInitialization()"
18111849

18121850
// Put installer automation in class
18131851
do $System.OBJ.Delete("SourceControl.Git.Installer.CLS","-d")

module.xml

+3-1
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>

test/UnitTest/SourceControl/Git/AddRemove.cls

+4-3
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ Method %OnNew(initvalue) As %Status
5050
{
5151
Merge ..SourceControlGlobal = ^SYS("SourceControl")
5252
Kill ^SYS("SourceControl")
53-
Set folder = ##class(%Library.File).NormalizeFilename(##class(%Library.File).TempFilename()_"dir/")
54-
Set ^SYS("SourceControl","Git","settings","namespaceTemp") = folder
55-
Set ^SYS("SourceControl","Git","settings","mappings","MAC","*")="rtn/"
53+
Set settings = ##class(SourceControl.Git.Settings).%New()
54+
Set settings.namespaceTemp = ##class(%Library.File).TempFilename()_"dir"
55+
Set settings.Mappings("MAC","*")="rtn/"
56+
Do settings.%Save()
5657
Do ##class(%Studio.SourceControl.Interface).SourceControlClassSet("SourceControl.Git.Extension")
5758
Quit ##super(initvalue)
5859
}
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

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,12 @@ 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 folder = ##class(%File).NormalizeDirectory(##class(%Library.File).TempFilename()_"dir/")
100101
set settings.namespaceTemp = folder
101102
set settings.percentClassReplace = "_"
102103
$$$ThrowOnError(settings.%Save())
103104
do ##class(%Library.File).CopyDir($Piece(..Manager.CurrentDir,"test"),folder,1)
104-
merge ..Mappings = @##class(SourceControl.Git.Utils).MappingsNode()
105105
kill @##class(SourceControl.Git.Utils).MappingsNode()
106106
Set app = $System.CSP.GetDefaultApp($Namespace)
107107
If (app '= "") {

0 commit comments

Comments
 (0)