@@ -21,6 +21,7 @@ import {
21
21
import * as commandsGrpcPb from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb' ;
22
22
import { NotificationServiceServer } from '../common/protocol' ;
23
23
import { Deferred } from '@theia/core/lib/common/promise-util' ;
24
+ import { Status as RpcStatus } from './cli-protocol/google/rpc/status_pb' ;
24
25
25
26
@injectable ( )
26
27
export class CoreClientProvider extends GrpcClientProvider < CoreClientProvider . Client > {
@@ -66,23 +67,41 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
66
67
}
67
68
68
69
@postConstruct ( )
69
- protected override async init ( ) : Promise < void > {
70
+ protected override init ( ) : void {
70
71
this . daemon . getPort ( ) . then ( async ( port ) => {
71
72
// First create the client and the instance synchronously
72
73
// and notify client is ready.
73
74
// TODO: Creation failure should probably be handled here
74
- await this . reconcileClient ( port ) . then ( ( ) => {
75
- this . _created . resolve ( ) ;
76
- } ) ;
75
+ await this . reconcileClient ( port ) ; // create instance
76
+ this . _created . resolve ( ) ;
77
+
78
+ // Normal startup workflow:
79
+ // 1. create instance,
80
+ // 2. init instance,
81
+ // 3. update indexes asynchronously.
77
82
78
- // If client has been created correctly update indexes and initialize
79
- // its instance by loading platforms and cores.
83
+ // First startup workflow:
84
+ // 1. create instance,
85
+ // 2. update indexes and wait,
86
+ // 3. init instance.
80
87
if ( this . _client && ! ( this . _client instanceof Error ) ) {
81
- await this . updateIndexes ( this . _client )
82
- . then ( this . initInstance )
83
- . then ( ( ) => {
88
+ try {
89
+ await this . initInstance ( this . _client ) ;
90
+ this . _initialized . resolve ( ) ;
91
+ this . updateIndex ( this . _client ) ; // Update the indexes asynchronously
92
+ } catch ( error : unknown ) {
93
+ if (
94
+ this . isPackageIndexMissingError ( error ) ||
95
+ this . isDiscoveryNotFoundError ( error )
96
+ ) {
97
+ await this . updateIndexes ( this . _client ) ;
98
+ await this . initInstance ( this . _client ) ;
99
+ console . log ( 'fooo' ) ;
84
100
this . _initialized . resolve ( ) ;
85
- } ) ;
101
+ } else {
102
+ throw error ;
103
+ }
104
+ }
86
105
}
87
106
} ) ;
88
107
@@ -95,6 +114,41 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
95
114
} ) ;
96
115
}
97
116
117
+ private isPackageIndexMissingError ( error : unknown ) : boolean {
118
+ const assert = ( message : string ) =>
119
+ message . includes ( 'loading json index file' ) ;
120
+ // https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/package_manager.go#L247
121
+ return this . isRpcStatusError ( error , assert ) ;
122
+ }
123
+
124
+ private isDiscoveryNotFoundError ( error : unknown ) : boolean {
125
+ const assert = ( message : string ) =>
126
+ message . includes ( 'discovery' ) &&
127
+ ( message . includes ( 'not found' ) || message . includes ( 'not installed' ) ) ;
128
+ // https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/loader.go#L740
129
+ // https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/loader.go#L744
130
+ return this . isRpcStatusError ( error , assert ) ;
131
+ }
132
+
133
+ private isCancelError ( error : unknown ) : boolean {
134
+ return (
135
+ error instanceof Error &&
136
+ error . message . toLocaleLowerCase ( ) . includes ( 'cancelled on client' )
137
+ ) ;
138
+ }
139
+
140
+ // Final error codes are not yet defined by the CLI. Hence, we do string match in the error message.
141
+ private isRpcStatusError (
142
+ error : unknown ,
143
+ assert : ( message : string ) => boolean
144
+ ) {
145
+ if ( error instanceof RpcStatus ) {
146
+ const { message } = RpcStatus . toObject ( false , error ) ;
147
+ return assert ( message . toLocaleLowerCase ( ) ) ;
148
+ }
149
+ return false ;
150
+ }
151
+
98
152
protected async createClient (
99
153
port : string | number
100
154
) : Promise < CoreClientProvider . Client > {
@@ -136,8 +190,9 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
136
190
} : CoreClientProvider . Client ) : Promise < void > {
137
191
const initReq = new InitRequest ( ) ;
138
192
initReq . setInstance ( instance ) ;
139
- await new Promise < void > ( ( resolve , reject ) => {
193
+ return new Promise < void > ( ( resolve , reject ) => {
140
194
const stream = client . init ( initReq ) ;
195
+ const errorStatus : RpcStatus [ ] = [ ] ;
141
196
stream . on ( 'data' , ( res : InitResponse ) => {
142
197
const progress = res . getInitProgress ( ) ;
143
198
if ( progress ) {
@@ -153,13 +208,30 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
153
208
}
154
209
}
155
210
156
- const err = res . getError ( ) ;
157
- if ( err ) {
158
- console . error ( err . getMessage ( ) ) ;
211
+ const error = res . getError ( ) ;
212
+ if ( error ) {
213
+ console . error ( error . getMessage ( ) ) ;
214
+ errorStatus . push ( error ) ;
215
+ // Cancel the init request. This will result in a cancel error.
216
+ stream . cancel ( ) ;
217
+ }
218
+ } ) ;
219
+ stream . on ( 'error' , ( error ) => {
220
+ // on any error during the init request, the request is canceled.
221
+ // on cancel, the IDE2 ignores the cancel error and rejects with the original one.
222
+ reject (
223
+ this . isCancelError ( error ) && errorStatus . length
224
+ ? errorStatus [ 0 ]
225
+ : error
226
+ ) ;
227
+ } ) ;
228
+ stream . on ( 'end' , ( ) => {
229
+ if ( errorStatus . length ) {
230
+ reject ( errorStatus ) ;
231
+ } else {
232
+ resolve ( ) ;
159
233
}
160
234
} ) ;
161
- stream . on ( 'error' , ( err ) => reject ( err ) ) ;
162
- stream . on ( 'end' , resolve ) ;
163
235
} ) ;
164
236
}
165
237
@@ -233,7 +305,9 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
233
305
}
234
306
} ) ;
235
307
await new Promise < void > ( ( resolve , reject ) => {
236
- resp . on ( 'error' , reject ) ;
308
+ resp . on ( 'error' , ( error ) => {
309
+ reject ( error ) ;
310
+ } ) ;
237
311
resp . on ( 'end' , resolve ) ;
238
312
} ) ;
239
313
}
0 commit comments