diff --git a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts index 9cee46abb..c21b86190 100644 --- a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts @@ -127,6 +127,7 @@ export class UploadSketch extends CoreServiceContribution { usingProgrammer, verifyOptions ); + if (!uploadOptions) { return; } @@ -137,11 +138,37 @@ export class UploadSketch extends CoreServiceContribution { const uploadResponse = await this.doWithProgress({ progressText: nls.localize('arduino/sketch/uploading', 'Uploading...'), - task: (progressId, coreService, token) => - coreService.upload({ ...uploadOptions, progressId }, token), + task: async (progressId, coreService, token) => { + try { + return await coreService.upload( + { ...uploadOptions, progressId }, + token + ); + } catch (err) { + if (err.code === 4005) { + const uploadWithProgrammerOptions = await this.uploadOptions( + true, + verifyOptions + ); + if (uploadWithProgrammerOptions) { + return coreService.upload( + { ...uploadWithProgrammerOptions, progressId }, + token + ); + } + } else { + throw err; + } + } + }, keepOutput: true, cancelable: true, }); + + if (!uploadResponse) { + return; + } + // the port update is NOOP if nothing has changed this.boardsServiceProvider.updateConfig(uploadResponse.portAfterUpload); diff --git a/arduino-ide-extension/src/common/protocol/core-service.ts b/arduino-ide-extension/src/common/protocol/core-service.ts index f3a681d48..2b4a07652 100644 --- a/arduino-ide-extension/src/common/protocol/core-service.ts +++ b/arduino-ide-extension/src/common/protocol/core-service.ts @@ -71,6 +71,7 @@ export namespace CoreError { Upload: 4002, UploadUsingProgrammer: 4003, BurnBootloader: 4004, + UploadRequiresProgrammer: 4005, }; export const VerifyFailed = declareCoreError(Codes.Verify); export const UploadFailed = declareCoreError(Codes.Upload); @@ -78,6 +79,10 @@ export namespace CoreError { Codes.UploadUsingProgrammer ); export const BurnBootloaderFailed = declareCoreError(Codes.BurnBootloader); + export const UploadRequiresProgrammer = declareCoreError( + Codes.UploadRequiresProgrammer + ); + export function is( error: unknown ): error is ApplicationError { diff --git a/arduino-ide-extension/src/node/core-service-impl.ts b/arduino-ide-extension/src/node/core-service-impl.ts index 9ae7b405e..b8eba0335 100644 --- a/arduino-ide-extension/src/node/core-service-impl.ts +++ b/arduino-ide-extension/src/node/core-service-impl.ts @@ -1,4 +1,4 @@ -import type { ClientReadableStream } from '@grpc/grpc-js'; +import { type ClientReadableStream } from '@grpc/grpc-js'; import { ApplicationError } from '@theia/core/lib/common/application-error'; import type { CancellationToken } from '@theia/core/lib/common/cancellation'; import { CommandService } from '@theia/core/lib/common/command'; @@ -41,6 +41,7 @@ import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_ import { BurnBootloaderRequest, BurnBootloaderResponse, + ProgrammerIsRequiredForUploadError, UploadRequest, UploadResponse, UploadUsingProgrammerRequest, @@ -295,12 +296,24 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService { reject(UserAbortApplicationError()); return; } + + if ( + ServiceError.isInstanceOf( + error, + ProgrammerIsRequiredForUploadError + ) + ) { + reject(CoreError.UploadRequiresProgrammer()); + return; + } + const message = nls.localize( 'arduino/upload/error', '{0} error: {1}', firstToUpperCase(task), error.details ); + this.sendResponse(error.details, OutputMessage.Severity.Error); reject( errorCtor( diff --git a/arduino-ide-extension/src/node/service-error.ts b/arduino-ide-extension/src/node/service-error.ts index a56cf13ea..a42c05d8a 100644 --- a/arduino-ide-extension/src/node/service-error.ts +++ b/arduino-ide-extension/src/node/service-error.ts @@ -1,14 +1,54 @@ import { Metadata, StatusObject } from '@grpc/grpc-js'; +import { Status } from './cli-protocol/google/rpc/status_pb'; +import { stringToUint8Array } from '../common/utils'; +import { ProgrammerIsRequiredForUploadError } from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb'; + +type ProtoError = typeof ProgrammerIsRequiredForUploadError; +const protoErrorsMap = new Map([ + [ + 'type.googleapis.com/cc.arduino.cli.commands.v1.ProgrammerIsRequiredForUploadError', + ProgrammerIsRequiredForUploadError, + ], + // handle other cli defined errors here +]); export type ServiceError = StatusObject & Error; export namespace ServiceError { export function isCancel(arg: unknown): arg is ServiceError & { code: 1 } { return is(arg) && arg.code === 1; // https://grpc.github.io/grpc/core/md_doc_statuscodes.html } + export function is(arg: unknown): arg is ServiceError { - return arg instanceof Error && isStatusObjet(arg); + return arg instanceof Error && isStatusObject(arg); } - function isStatusObjet(arg: unknown): arg is StatusObject { + + export function isInstanceOf(arg: unknown, type: unknown): boolean { + if (!isStatusObject(arg)) { + return false; + } + + const bin = arg.metadata.get('grpc-status-details-bin')[0]; + + const uint8Array = + typeof bin === 'string' + ? stringToUint8Array(bin) + : new Uint8Array(bin.buffer, bin.byteOffset, bin.byteLength); + + const errors = Status.deserializeBinary(uint8Array) + .getDetailsList() + .map((details) => { + const typeUrl = details.getTypeUrl(); + const ErrorType = protoErrorsMap.get(typeUrl); + return ErrorType?.deserializeBinary(details.getValue_asU8()); + }); + + return !!errors.find((error) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return error && error instanceof type; + }); + } + + function isStatusObject(arg: unknown): arg is StatusObject { if (typeof arg === 'object') { // eslint-disable-next-line @typescript-eslint/no-explicit-any const any = arg as any; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy