@@ -13,13 +13,25 @@ import * as ini from 'ini';
13
13
import * as nsfw from 'nsfw' ;
14
14
15
15
import { CheGitClient , CheGitService , GIT_USER_EMAIL , GIT_USER_NAME } from '../common/git-protocol' ;
16
- import { createFile , pathExists , readFile , writeFile } from 'fs-extra' ;
16
+ import {
17
+ CheTheiaUserPreferencesSynchronizer ,
18
+ THEIA_USER_PREFERENCES_PATH ,
19
+ } from '@eclipse-che/theia-user-preferences-synchronizer/lib/node/che-theia-preferences-synchronizer' ;
20
+ import { Disposable , Emitter } from '@theia/core' ;
21
+ import { basename , dirname , resolve } from 'path' ;
22
+ import {
23
+ createFileSync ,
24
+ ensureDirSync ,
25
+ existsSync ,
26
+ pathExistsSync ,
27
+ readFileSync ,
28
+ readdirSync ,
29
+ watch ,
30
+ writeFileSync ,
31
+ } from 'fs-extra' ;
17
32
import { inject , injectable } from 'inversify' ;
18
33
19
- import { CheTheiaUserPreferencesSynchronizer } from '@eclipse-che/theia-user-preferences-synchronizer/lib/node/che-theia-preferences-synchronizer' ;
20
- import { Disposable } from '@theia/core' ;
21
34
import { homedir } from 'os' ;
22
- import { resolve } from 'path' ;
23
35
24
36
export const GIT_USER_CONFIG_PATH = resolve ( homedir ( ) , '.gitconfig' ) ;
25
37
export const GIT_GLOBAL_CONFIG_PATH = '/etc/gitconfig' ;
@@ -36,28 +48,113 @@ export interface GitConfiguration {
36
48
37
49
@injectable ( )
38
50
export class GitConfigurationController implements CheGitService {
39
- @inject ( CheTheiaUserPreferencesSynchronizer )
40
- protected preferencesService : CheTheiaUserPreferencesSynchronizer ;
51
+ constructor (
52
+ @inject ( CheTheiaUserPreferencesSynchronizer ) protected preferencesService : CheTheiaUserPreferencesSynchronizer
53
+ ) {
54
+ this . onGitClientSetEvent ( async ( ) => {
55
+ await this . checkExistsWithTimeout ( THEIA_USER_PREFERENCES_PATH , 60000 ) ;
56
+ this . userGitconfigDirty = this . readConfigurationFromGitConfigFile ( GIT_USER_CONFIG_PATH ) ! ;
57
+ const preferences = await this . preferencesService . getPreferences ( ) ;
58
+ await this . updateUserGitconfigFromPreferences ( preferences ) ;
59
+ } ) ;
60
+ this . onUserGitconfigChangedEvent ( ( ) => this . fetchLocalGitconfig ( ) ) ;
61
+ }
62
+
63
+ private checkExistsWithTimeout ( filePath : string , timeout : number ) : Promise < void > {
64
+ return new Promise ( ( resolvePromise , reject ) => {
65
+ if ( existsSync ( filePath ) ) {
66
+ resolvePromise ( ) ;
67
+ return ;
68
+ }
69
+ const timer = setTimeout ( ( ) => {
70
+ watcher . close ( ) ;
71
+ reject ( new Error ( 'File did not exists and was not created during the timeout.' ) ) ;
72
+ } , timeout ) ;
73
+
74
+ const dir = dirname ( filePath ) ;
75
+ ensureDirSync ( dir ) ;
76
+ const pathBasename = basename ( filePath ) ;
77
+ const watcher = watch ( dir , ( eventType , filename ) => {
78
+ if ( eventType === 'rename' && filename === pathBasename ) {
79
+ clearTimeout ( timer ) ;
80
+ watcher . close ( ) ;
81
+ resolvePromise ( ) ;
82
+ }
83
+ } ) ;
84
+ } ) ;
85
+ }
86
+
87
+ private fetchLocalGitconfig ( ) : void {
88
+ const userGitconfig = this . readConfigurationFromGitConfigFile ( GIT_USER_CONFIG_PATH ) ! ;
89
+ this . updateLocalGitconfig ( userGitconfig ) ;
90
+ this . userGitconfigDirty = userGitconfig ;
91
+ }
41
92
42
93
protected preferencesHandler : Disposable | undefined ;
43
94
44
95
protected gitConfigWatcher : nsfw . NSFW | undefined ;
45
96
46
97
protected client : CheGitClient ;
47
98
99
+ private readonly projectsRoot = process . env . PROJECTS_ROOT || process . env . CHE_PROJECTS_ROOT || '/projects' ;
100
+
101
+ private readonly onUserGitconfigChangedEmitter = new Emitter ( ) ;
102
+ private readonly onUserGitconfigChangedEvent = this . onUserGitconfigChangedEmitter . event ;
103
+
104
+ private readonly onGitClientSetEmitter = new Emitter ( ) ;
105
+ private readonly onGitClientSetEvent = this . onGitClientSetEmitter . event ;
106
+
107
+ private userGitconfigDirty : GitConfiguration ;
108
+
109
+ private updateLocalGitconfig ( gitconfig : GitConfiguration ) : void {
110
+ readdirSync ( this . projectsRoot , { withFileTypes : true } )
111
+ . filter ( dir => dir . isDirectory ( ) )
112
+ . forEach ( dir => {
113
+ const localGitconfigPath = resolve ( this . projectsRoot , dir . name , '.git' , 'config' ) ;
114
+ let localGitconfig : GitConfiguration ;
115
+ if ( existsSync ( localGitconfigPath ) ) {
116
+ localGitconfig = ini . parse ( readFileSync ( localGitconfigPath ) . toString ( ) ) ;
117
+ // Add missing values
118
+ Object . keys ( gitconfig ) . forEach ( key => {
119
+ if (
120
+ localGitconfig [ key ] === undefined ||
121
+ JSON . stringify ( localGitconfig [ key ] ) === JSON . stringify ( this . userGitconfigDirty [ key ] )
122
+ ) {
123
+ localGitconfig [ key ] = gitconfig [ key ] ;
124
+ }
125
+ } ) ;
126
+ // Remove deleted values
127
+ Object . keys ( localGitconfig ) . forEach ( key => {
128
+ if (
129
+ gitconfig [ key ] === undefined &&
130
+ JSON . stringify ( localGitconfig [ key ] ) === JSON . stringify ( this . userGitconfigDirty [ key ] )
131
+ ) {
132
+ delete localGitconfig [ key ] ;
133
+ }
134
+ } ) ;
135
+ } else {
136
+ createFileSync ( localGitconfigPath ) ;
137
+ localGitconfig = gitconfig ;
138
+ }
139
+ writeFileSync ( localGitconfigPath , ini . stringify ( localGitconfig ) ) ;
140
+ } ) ;
141
+ }
142
+
48
143
public async watchGitConfigChanges ( ) : Promise < void > {
49
144
if ( this . gitConfigWatcher ) {
50
145
return ;
51
146
}
52
147
53
- const gitConfigExists = await pathExists ( GIT_USER_CONFIG_PATH ) ;
148
+ const gitConfigExists = pathExistsSync ( GIT_USER_CONFIG_PATH ) ;
54
149
if ( ! gitConfigExists ) {
55
- await createFile ( GIT_USER_CONFIG_PATH ) ;
150
+ createFileSync ( GIT_USER_CONFIG_PATH ) ;
56
151
}
57
152
58
153
this . gitConfigWatcher = await nsfw ( GIT_USER_CONFIG_PATH , async ( events : nsfw . FileChangeEvent [ ] ) => {
59
154
for ( const event of events ) {
60
155
if ( event . action === nsfw . actions . MODIFIED ) {
156
+ this . onUserGitconfigChangedEmitter . fire ( undefined ) ;
157
+
61
158
const userConfig = await this . getUserConfigurationFromGitConfig ( ) ;
62
159
const preferences = await this . preferencesService . getPreferences ( ) ;
63
160
@@ -74,27 +171,27 @@ export class GitConfigurationController implements CheGitService {
74
171
async getUserConfigurationFromGitConfig ( ) : Promise < UserConfiguration > {
75
172
let name : string | undefined ;
76
173
let email : string | undefined ;
77
- const config = await this . readConfigurationFromGitConfigFile ( GIT_USER_CONFIG_PATH ) ;
174
+ const config = this . readConfigurationFromGitConfigFile ( GIT_USER_CONFIG_PATH ) ;
78
175
if ( config && config . user ) {
79
176
name = config . user . name ;
80
177
email = config . user . email ;
81
178
}
82
179
if ( name && email ) {
83
180
return { name, email } ;
84
181
}
85
- const globalConfig = await this . readConfigurationFromGitConfigFile ( GIT_GLOBAL_CONFIG_PATH ) ;
182
+ const globalConfig = this . readConfigurationFromGitConfigFile ( GIT_GLOBAL_CONFIG_PATH ) ;
86
183
if ( globalConfig && globalConfig . user ) {
87
184
name = name ? name : globalConfig . user . name ;
88
185
email = email ? email : globalConfig . user . email ;
89
186
}
90
187
return { name, email } ;
91
188
}
92
189
93
- protected async readConfigurationFromGitConfigFile ( path : string ) : Promise < GitConfiguration | undefined > {
94
- if ( ! ( await pathExists ( path ) ) ) {
190
+ protected readConfigurationFromGitConfigFile ( path : string ) : GitConfiguration | undefined {
191
+ if ( ! pathExistsSync ( path ) ) {
95
192
return ;
96
193
}
97
- const gitConfigContent = await readFile ( path , 'utf-8' ) ;
194
+ const gitConfigContent = readFileSync ( path , 'utf-8' ) ;
98
195
return ini . parse ( gitConfigContent ) ;
99
196
}
100
197
@@ -104,12 +201,16 @@ export class GitConfigurationController implements CheGitService {
104
201
}
105
202
106
203
this . preferencesHandler = this . preferencesService . onUserPreferencesModify ( preferences => {
107
- const userConfig = this . getUserConfigurationFromPreferences ( preferences ) ;
108
- this . updateGlobalGitConfig ( userConfig ) ;
109
- this . client . firePreferencesChanged ( ) ;
204
+ this . updateUserGitconfigFromPreferences ( preferences ) ;
110
205
} ) ;
111
206
}
112
207
208
+ private async updateUserGitconfigFromPreferences ( preferences : object ) : Promise < void > {
209
+ const userConfig = this . getUserConfigurationFromPreferences ( preferences ) ;
210
+ await this . updateUserGitonfigFromUserConfig ( userConfig ) ;
211
+ this . client . firePreferencesChanged ( ) ;
212
+ }
213
+
113
214
// eslint-disable-next-line @typescript-eslint/no-explicit-any
114
215
protected getUserConfigurationFromPreferences ( preferences : any ) : UserConfiguration {
115
216
return {
@@ -118,13 +219,13 @@ export class GitConfigurationController implements CheGitService {
118
219
} ;
119
220
}
120
221
121
- public async updateGlobalGitConfig ( userConfig : UserConfiguration ) : Promise < void > {
222
+ public async updateUserGitonfigFromUserConfig ( userConfig : UserConfiguration ) : Promise < void > {
122
223
if ( userConfig . name === undefined && userConfig . email === undefined ) {
123
224
return ;
124
225
}
125
226
126
227
// read existing content
127
- let gitConfig = await this . readConfigurationFromGitConfigFile ( GIT_USER_CONFIG_PATH ) ;
228
+ let gitConfig = this . readConfigurationFromGitConfigFile ( GIT_USER_CONFIG_PATH ) ;
128
229
if ( ! gitConfig ) {
129
230
gitConfig = { } ;
130
231
} else if ( ! gitConfig . user ) {
@@ -142,14 +243,16 @@ export class GitConfigurationController implements CheGitService {
142
243
if ( this . gitConfigWatcher ) {
143
244
await this . gitConfigWatcher . stop ( ) ;
144
245
}
145
- await writeFile ( GIT_USER_CONFIG_PATH , ini . stringify ( gitConfig ) ) ;
246
+ writeFileSync ( GIT_USER_CONFIG_PATH , ini . stringify ( gitConfig ) ) ;
247
+ this . onUserGitconfigChangedEmitter . fire ( undefined ) ;
146
248
if ( this . gitConfigWatcher ) {
147
249
await this . gitConfigWatcher . start ( ) ;
148
250
}
149
251
}
150
252
151
253
setClient ( client : CheGitClient ) : void {
152
254
this . client = client ;
255
+ this . onGitClientSetEmitter . fire ( undefined ) ;
153
256
}
154
257
155
258
dispose ( ) : void {
0 commit comments