@@ -21,12 +21,13 @@ import {
21
21
RenderMimeProvider
22
22
} from './components/diff/Diff' ;
23
23
import { getRefValue , IDiffContext } from './components/diff/model' ;
24
+ import { AUTH_ERROR_MESSAGES } from './git' ;
25
+ import { logger } from './logger' ;
24
26
import { GitExtension } from './model' ;
25
27
import { diffIcon } from './style/icons' ;
26
- import { Git } from './tokens' ;
28
+ import { Git , Level } from './tokens' ;
27
29
import { GitCredentialsForm } from './widgets/CredentialsBox' ;
28
- import { doGitClone } from './widgets/gitClone' ;
29
- import { GitPullPushDialog , Operation } from './widgets/gitPushPull' ;
30
+ import { GitCloneForm } from './widgets/GitCloneForm' ;
30
31
31
32
const RESOURCES = [
32
33
{
@@ -39,6 +40,26 @@ const RESOURCES = [
39
40
}
40
41
] ;
41
42
43
+ interface IGitCloneArgs {
44
+ /**
45
+ * Path in which to clone the Git repository
46
+ */
47
+ path : string ;
48
+ /**
49
+ * Git repository url
50
+ */
51
+ url : string ;
52
+ }
53
+
54
+ /**
55
+ * Git operations requiring authentication
56
+ */
57
+ enum Operation {
58
+ Clone = 'Clone' ,
59
+ Pull = 'Pull' ,
60
+ Push = 'Push'
61
+ }
62
+
42
63
/**
43
64
* The command IDs used by the git plugin.
44
65
*/
@@ -133,8 +154,28 @@ export function addCommands(
133
154
} ) ;
134
155
135
156
if ( result . button . accept ) {
136
- await model . init ( currentPath ) ;
137
- model . pathRepository = currentPath ;
157
+ logger . log ( {
158
+ message : 'Initializing...' ,
159
+ level : Level . RUNNING
160
+ } ) ;
161
+ try {
162
+ await model . init ( currentPath ) ;
163
+ model . pathRepository = currentPath ;
164
+ logger . log ( {
165
+ message : 'Git repository initialized.' ,
166
+ level : Level . SUCCESS
167
+ } ) ;
168
+ } catch ( error ) {
169
+ console . error (
170
+ 'Encountered an error when initializing the repository. Error: ' ,
171
+ error
172
+ ) ;
173
+ logger . log ( {
174
+ message : 'Failed to initialize the Git repository' ,
175
+ level : Level . ERROR ,
176
+ error
177
+ } ) ;
178
+ }
138
179
}
139
180
} ,
140
181
isEnabled : ( ) => model . pathRepository === null
@@ -208,8 +249,41 @@ export function addCommands(
208
249
caption : 'Clone a repository from a URL' ,
209
250
isEnabled : ( ) => model . pathRepository === null ,
210
251
execute : async ( ) => {
211
- await doGitClone ( model , fileBrowser . model . path ) ;
212
- fileBrowser . model . refresh ( ) ;
252
+ const result = await showDialog ( {
253
+ title : 'Clone a repo' ,
254
+ body : new GitCloneForm ( ) ,
255
+ focusNodeSelector : 'input' ,
256
+ buttons : [ Dialog . cancelButton ( ) , Dialog . okButton ( { label : 'CLONE' } ) ]
257
+ } ) ;
258
+
259
+ if ( result . button . accept && result . value ) {
260
+ logger . log ( {
261
+ level : Level . RUNNING ,
262
+ message : 'Cloning...'
263
+ } ) ;
264
+ try {
265
+ await Private . showGitOperationDialog < IGitCloneArgs > (
266
+ model ,
267
+ Operation . Clone ,
268
+ { path : fileBrowser . model . path , url : result . value }
269
+ ) ;
270
+ logger . log ( {
271
+ message : 'Successfully cloned' ,
272
+ level : Level . SUCCESS
273
+ } ) ;
274
+ await fileBrowser . model . refresh ( ) ;
275
+ } catch ( error ) {
276
+ console . error (
277
+ 'Encountered an error when cloning the repository. Error: ' ,
278
+ error
279
+ ) ;
280
+ logger . log ( {
281
+ message : 'Failed to clone' ,
282
+ level : Level . ERROR ,
283
+ error
284
+ } ) ;
285
+ }
286
+ }
213
287
}
214
288
} ) ;
215
289
@@ -229,13 +303,27 @@ export function addCommands(
229
303
caption : 'Push code to remote repository' ,
230
304
isEnabled : ( ) => model . pathRepository !== null ,
231
305
execute : async ( ) => {
232
- await Private . showGitOperationDialog ( model , Operation . Push ) . catch (
233
- reason => {
234
- console . error (
235
- `Encountered an error when pushing changes. Error: ${ reason } `
236
- ) ;
237
- }
238
- ) ;
306
+ logger . log ( {
307
+ level : Level . RUNNING ,
308
+ message : 'Pushing...'
309
+ } ) ;
310
+ try {
311
+ await Private . showGitOperationDialog ( model , Operation . Push ) ;
312
+ logger . log ( {
313
+ message : 'Successfully pushed' ,
314
+ level : Level . SUCCESS
315
+ } ) ;
316
+ } catch ( error ) {
317
+ console . error (
318
+ 'Encountered an error when pushing changes. Error: ' ,
319
+ error
320
+ ) ;
321
+ logger . log ( {
322
+ message : 'Failed to push' ,
323
+ level : Level . ERROR ,
324
+ error
325
+ } ) ;
326
+ }
239
327
}
240
328
} ) ;
241
329
@@ -245,13 +333,27 @@ export function addCommands(
245
333
caption : 'Pull latest code from remote repository' ,
246
334
isEnabled : ( ) => model . pathRepository !== null ,
247
335
execute : async ( ) => {
248
- await Private . showGitOperationDialog ( model , Operation . Pull ) . catch (
249
- reason => {
250
- console . error (
251
- `Encountered an error when pulling changes. Error: ${ reason } `
252
- ) ;
253
- }
254
- ) ;
336
+ logger . log ( {
337
+ level : Level . RUNNING ,
338
+ message : 'Pulling...'
339
+ } ) ;
340
+ try {
341
+ await Private . showGitOperationDialog ( model , Operation . Pull ) ;
342
+ logger . log ( {
343
+ message : 'Successfully pulled' ,
344
+ level : Level . SUCCESS
345
+ } ) ;
346
+ } catch ( error ) {
347
+ console . error (
348
+ 'Encountered an error when pulling changes. Error: ' ,
349
+ error
350
+ ) ;
351
+ logger . log ( {
352
+ message : 'Failed to pull' ,
353
+ level : Level . ERROR ,
354
+ error
355
+ } ) ;
356
+ }
255
357
}
256
358
} ) ;
257
359
@@ -515,44 +617,69 @@ export function createGitMenu(commands: CommandRegistry): Menu {
515
617
/* eslint-disable no-inner-declarations */
516
618
namespace Private {
517
619
/**
518
- * Displays an error dialog when a Git operation fails .
620
+ * Handle Git operation that may require authentication .
519
621
*
520
622
* @private
521
623
* @param model - Git extension model
522
624
* @param operation - Git operation name
625
+ * @param args - Git operation arguments
626
+ * @param authentication - Git authentication information
627
+ * @param retry - Is this operation retried?
523
628
* @returns Promise for displaying a dialog
524
629
*/
525
- export async function showGitOperationDialog (
630
+ export async function showGitOperationDialog < T > (
526
631
model : GitExtension ,
527
- operation : Operation
632
+ operation : Operation ,
633
+ args ?: T ,
634
+ authentication ?: Git . IAuth ,
635
+ retry = false
528
636
) : Promise < void > {
529
- const title = `Git ${ operation } ` ;
530
- let result = await showDialog ( {
531
- title : title ,
532
- body : new GitPullPushDialog ( model , operation ) ,
533
- buttons : [ Dialog . okButton ( { label : 'DISMISS' } ) ]
534
- } ) ;
535
- let retry = false ;
536
- while ( ! result . button . accept ) {
537
- const credentials = await showDialog ( {
538
- title : 'Git credentials required' ,
539
- body : new GitCredentialsForm (
540
- 'Enter credentials for remote repository' ,
541
- retry ? 'Incorrect username or password.' : ''
542
- ) ,
543
- buttons : [ Dialog . cancelButton ( ) , Dialog . okButton ( { label : 'OK' } ) ]
544
- } ) ;
545
-
546
- if ( ! credentials . button . accept ) {
547
- break ;
637
+ try {
638
+ // the Git action
639
+ switch ( operation ) {
640
+ case Operation . Clone :
641
+ // eslint-disable-next-line no-case-declarations
642
+ const { path, url } = ( args as any ) as IGitCloneArgs ;
643
+ await model . clone ( path , url , authentication ) ;
644
+ break ;
645
+ case Operation . Pull :
646
+ await model . pull ( authentication ) ;
647
+ break ;
648
+ case Operation . Push :
649
+ await model . push ( authentication ) ;
650
+ break ;
651
+ default :
652
+ return ;
548
653
}
654
+ } catch ( error ) {
655
+ if (
656
+ AUTH_ERROR_MESSAGES . some (
657
+ errorMessage => error . message . indexOf ( errorMessage ) > - 1
658
+ )
659
+ ) {
660
+ // If the error is an authentication error, ask the user credentials
661
+ const credentials = await showDialog ( {
662
+ title : 'Git credentials required' ,
663
+ body : new GitCredentialsForm (
664
+ 'Enter credentials for remote repository' ,
665
+ retry ? 'Incorrect username or password.' : ''
666
+ )
667
+ } ) ;
549
668
550
- result = await showDialog ( {
551
- title : title ,
552
- body : new GitPullPushDialog ( model , operation , credentials . value ) ,
553
- buttons : [ Dialog . okButton ( { label : 'DISMISS' } ) ]
554
- } ) ;
555
- retry = true ;
669
+ if ( credentials . button . accept ) {
670
+ // Retry the operation if the user provides its credentials
671
+ return await showGitOperationDialog < T > (
672
+ model ,
673
+ operation ,
674
+ args ,
675
+ credentials . value ,
676
+ true
677
+ ) ;
678
+ }
679
+ }
680
+ // Throw the error if it cannot be handled or
681
+ // if the user did not accept to provide its credentials
682
+ throw error ;
556
683
}
557
684
}
558
685
}
0 commit comments