Initial support for updating/downgrading cores.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta 2019-11-14 21:06:51 +01:00
parent 7077303a36
commit fdda4a72d0
27 changed files with 195 additions and 65 deletions

View File

@ -43,17 +43,17 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
// tslint:disable-next-line:max-line-length // tslint:disable-next-line:max-line-length
this.messageService.info(`The \`"${candidate.name}"\` core has to be installed for the currently selected \`"${selectedBoard.name}"\` board. Do you want to install it now?`, 'Yes', 'Install Manually').then(async answer => { this.messageService.info(`The \`"${candidate.name}"\` core has to be installed for the currently selected \`"${selectedBoard.name}"\` board. Do you want to install it now?`, 'Yes', 'Install Manually').then(async answer => {
if (answer === 'Yes') { if (answer === 'Yes') {
const dialog = new InstallationProgressDialog(candidate.name); const dialog = new InstallationProgressDialog(candidate.name, candidate.availableVersions[0]);
dialog.open(); dialog.open();
try { try {
await this.boardsService.install(candidate); await this.boardsService.install({ item: candidate });
} finally { } finally {
dialog.close(); dialog.close();
} }
} }
if (answer) { if (answer) {
this.boardsManagerFrontendContribution.openView({ reveal: true }).then(widget => widget.refresh(candidate.name.toLocaleLowerCase())); this.boardsManagerFrontendContribution.openView({ reveal: true }).then(widget => widget.refresh(candidate.name.toLocaleLowerCase()));
} }
}); });
} }
}) })

View File

@ -2,11 +2,18 @@ import * as React from 'react';
import { injectable } from 'inversify'; import { injectable } from 'inversify';
import { ListItemRenderer } from '../components/component-list/list-item-renderer'; import { ListItemRenderer } from '../components/component-list/list-item-renderer';
import { BoardPackage } from '../../common/protocol/boards-service'; import { BoardPackage } from '../../common/protocol/boards-service';
import { Installable } from '../../common/protocol/installable';
import { ComponentListItem } from '../components/component-list/component-list-item';
@injectable() @injectable()
export class BoardItemRenderer extends ListItemRenderer<BoardPackage> { export class BoardItemRenderer extends ListItemRenderer<BoardPackage> {
renderItem(item: BoardPackage, install: (item: BoardPackage) => Promise<void>): React.ReactNode { renderItem(
input: ComponentListItem.State & { item: BoardPackage },
install: (item: BoardPackage) => Promise<void>,
onVersionChange: (version: Installable.Version) => void): React.ReactNode {
const { item } = input;
const name = <span className='name'>{item.name}</span>; const name = <span className='name'>{item.name}</span>;
const author = <span className='author'>{item.author}</span>; const author = <span className='author'>{item.author}</span>;
const installedVersion = !!item.installedVersion && <div className='version-info'> const installedVersion = !!item.installedVersion && <div className='version-info'>
@ -17,18 +24,34 @@ export class BoardItemRenderer extends ListItemRenderer<BoardPackage> {
const summary = <div className='summary'>{item.summary}</div>; const summary = <div className='summary'>{item.summary}</div>;
const description = <div className='summary'>{item.description}</div>; const description = <div className='summary'>{item.description}</div>;
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onClick}>More info</a>; const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onMoreInfoClick}>More info</a>;
const installButton = item.installable && !item.installedVersion && const onClickInstall = () => install(item);
<button className='install' onClick={install.bind(this, item)}>INSTALL</button>; const installButton = item.installable &&
<button className='install' onClick={onClickInstall}>INSTALL</button>;
const onSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const version = event.target.value;
if (version) {
onVersionChange(version);
}
}
const versions = (() => { const versions = (() => {
const { availableVersions } = item; const { availableVersions } = item;
if (!!item.installedVersion || availableVersions.length === 0) { if (availableVersions.length === 0) {
return undefined; return undefined;
} else if (availableVersions.length === 1) { } else if (availableVersions.length === 1) {
return <label>{availableVersions[0]}</label> return <label>{availableVersions[0]}</label>
} else { } else {
return <select>{item.availableVersions.map(version => <option value={version} key={version}>{version}</option>)}</select>; return <select
value={input.selectedVersion}
onChange={onSelectChange}>
{
item.availableVersions
.filter(version => version !== item.installedVersion) // Filter the version that is currently installed.
.map(version => <option value={version} key={version}>{version}</option>)
}
</select>;
} }
})(); })();

View File

@ -1,25 +1,45 @@
import * as React from 'react'; import * as React from 'react';
import { Installable } from '../../../common/protocol/installable';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { ListItemRenderer } from './list-item-renderer'; import { ListItemRenderer } from './list-item-renderer';
export class ComponentListItem<T> extends React.Component<ComponentListItem.Props<T>> { export class ComponentListItem<T extends ArduinoComponent> extends React.Component<ComponentListItem.Props<T>, ComponentListItem.State> {
constructor(props: ComponentListItem.Props<T>) {
super(props);
if (props.item.installable) {
const version = props.item.availableVersions.filter(version => version !== props.item.installedVersion)[0];
this.state = {
selectedVersion: version
};
}
}
protected async install(item: T): Promise<void> { protected async install(item: T): Promise<void> {
await this.props.install(item); await this.props.install(item, this.state.selectedVersion);
}
protected onVersionChange(version: Installable.Version) {
this.setState({ selectedVersion: version });
} }
render(): React.ReactNode { render(): React.ReactNode {
const { item, itemRenderer, install } = this.props; const { item, itemRenderer } = this.props;
return itemRenderer.renderItem(item, install.bind(this)); return itemRenderer.renderItem(Object.assign(this.state, { item }), this.install.bind(this), this.onVersionChange.bind(this));
} }
} }
export namespace ComponentListItem { export namespace ComponentListItem {
export interface Props<T> { export interface Props<T extends ArduinoComponent> {
readonly item: T; readonly item: T;
readonly install: (item: T) => Promise<void>; readonly install: (item: T, version?: Installable.Version) => Promise<void>;
readonly itemRenderer: ListItemRenderer<T>; readonly itemRenderer: ListItemRenderer<T>;
} }
export interface State {
selectedVersion?: Installable.Version;
}
} }

View File

@ -1,8 +1,10 @@
import * as React from 'react'; import * as React from 'react';
import { Installable } from '../../../common/protocol/installable';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { ComponentListItem } from './component-list-item'; import { ComponentListItem } from './component-list-item';
import { ListItemRenderer } from './list-item-renderer'; import { ListItemRenderer } from './list-item-renderer';
export class ComponentList<T> extends React.Component<ComponentList.Props<T>> { export class ComponentList<T extends ArduinoComponent> extends React.Component<ComponentList.Props<T>> {
protected container?: HTMLElement; protected container?: HTMLElement;
@ -36,11 +38,11 @@ export class ComponentList<T> extends React.Component<ComponentList.Props<T>> {
export namespace ComponentList { export namespace ComponentList {
export interface Props<T> { export interface Props<T extends ArduinoComponent> {
readonly items: T[]; readonly items: T[];
readonly itemLabel: (item: T) => string; readonly itemLabel: (item: T) => string;
readonly itemRenderer: ListItemRenderer<T>; readonly itemRenderer: ListItemRenderer<T>;
readonly install: (item: T) => Promise<void>; readonly install: (item: T, version?: Installable.Version) => Promise<void>;
readonly resolveContainer: (element: HTMLElement) => void; readonly resolveContainer: (element: HTMLElement) => void;
} }

View File

@ -3,13 +3,14 @@ import debounce = require('lodash.debounce');
import { Event } from '@theia/core/lib/common/event'; import { Event } from '@theia/core/lib/common/event';
import { Searchable } from '../../../common/protocol/searchable'; import { Searchable } from '../../../common/protocol/searchable';
import { Installable } from '../../../common/protocol/installable'; import { Installable } from '../../../common/protocol/installable';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { InstallationProgressDialog } from '../installation-progress-dialog'; import { InstallationProgressDialog } from '../installation-progress-dialog';
import { SearchBar } from './search-bar'; import { SearchBar } from './search-bar';
import { ListWidget } from './list-widget'; import { ListWidget } from './list-widget';
import { ComponentList } from './component-list'; import { ComponentList } from './component-list';
import { ListItemRenderer } from './list-item-renderer'; import { ListItemRenderer } from './list-item-renderer';
export class FilterableListContainer<T> extends React.Component<FilterableListContainer.Props<T>, FilterableListContainer.State<T>> { export class FilterableListContainer<T extends ArduinoComponent> extends React.Component<FilterableListContainer.Props<T>, FilterableListContainer.State<T>> {
constructor(props: Readonly<FilterableListContainer.Props<T>>) { constructor(props: Readonly<FilterableListContainer.Props<T>>) {
super(props); super(props);
@ -77,12 +78,12 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
return items.sort((left, right) => itemLabel(left).localeCompare(itemLabel(right))); return items.sort((left, right) => itemLabel(left).localeCompare(itemLabel(right)));
} }
protected async install(item: T): Promise<void> { protected async install(item: T, version: Installable.Version): Promise<void> {
const { installable, searchable, itemLabel } = this.props; const { installable, searchable, itemLabel } = this.props;
const dialog = new InstallationProgressDialog(itemLabel(item)); const dialog = new InstallationProgressDialog(itemLabel(item), version);
dialog.open(); dialog.open();
try { try {
await installable.install(item); await installable.install({ item, version });
const { items } = await searchable.search({ query: this.state.filterText }); const { items } = await searchable.search({ query: this.state.filterText });
this.setState({ items: this.sort(items) }); this.setState({ items: this.sort(items) });
} finally { } finally {
@ -94,7 +95,7 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
export namespace FilterableListContainer { export namespace FilterableListContainer {
export interface Props<T> { export interface Props<T extends ArduinoComponent> {
readonly container: ListWidget<T>; readonly container: ListWidget<T>;
readonly installable: Installable<T>; readonly installable: Installable<T>;
readonly searchable: Searchable<T>; readonly searchable: Searchable<T>;

View File

@ -1,14 +1,17 @@
import * as React from 'react'; import * as React from 'react';
import { inject, injectable } from 'inversify'; import { inject, injectable } from 'inversify';
import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { Installable } from '../../../common/protocol/installable';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { ComponentListItem } from './component-list-item';
@injectable() @injectable()
export abstract class ListItemRenderer<T> { export abstract class ListItemRenderer<T extends ArduinoComponent> {
@inject(WindowService) @inject(WindowService)
protected windowService: WindowService; protected windowService: WindowService;
protected onClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => { protected onMoreInfoClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => {
const { target } = event.nativeEvent; const { target } = event.nativeEvent;
if (target instanceof HTMLAnchorElement) { if (target instanceof HTMLAnchorElement) {
this.windowService.openNewWindow(target.href, { external: true }); this.windowService.openNewWindow(target.href, { external: true });
@ -16,6 +19,10 @@ export abstract class ListItemRenderer<T> {
} }
} }
abstract renderItem(item: T, install: (item: T) => Promise<void>): React.ReactNode; abstract renderItem(
input: ComponentListItem.State & { item: T },
install: (item: T) => Promise<void>,
onVersionChange: (version: Installable.Version) => void
): React.ReactNode;
} }

View File

@ -1,10 +1,11 @@
import { injectable } from 'inversify'; import { injectable } from 'inversify';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { ListWidget } from './list-widget'; import { ListWidget } from './list-widget';
@injectable() @injectable()
export abstract class ListWidgetFrontendContribution<T> extends AbstractViewContribution<ListWidget<T>> implements FrontendApplicationContribution { export abstract class ListWidgetFrontendContribution<T extends ArduinoComponent> extends AbstractViewContribution<ListWidget<T>> implements FrontendApplicationContribution {
async initializeLayout(): Promise<void> { async initializeLayout(): Promise<void> {
} }

View File

@ -7,11 +7,12 @@ import { MaybePromise } from '@theia/core/lib/common/types';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { Installable } from '../../../common/protocol/installable'; import { Installable } from '../../../common/protocol/installable';
import { Searchable } from '../../../common/protocol/searchable'; import { Searchable } from '../../../common/protocol/searchable';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { FilterableListContainer } from './filterable-list-container'; import { FilterableListContainer } from './filterable-list-container';
import { ListItemRenderer } from './list-item-renderer'; import { ListItemRenderer } from './list-item-renderer';
@injectable() @injectable()
export abstract class ListWidget<T> extends ReactWidget { export abstract class ListWidget<T extends ArduinoComponent> extends ReactWidget {
/** /**
* Do not touch or use it. It is for setting the focus on the `input` after the widget activation. * Do not touch or use it. It is for setting the focus on the `input` after the widget activation.
@ -78,7 +79,7 @@ export abstract class ListWidget<T> extends ReactWidget {
} }
export namespace ListWidget { export namespace ListWidget {
export interface Options<T> { export interface Options<T extends ArduinoComponent> {
readonly id: string; readonly id: string;
readonly label: string; readonly label: string;
readonly iconClass: string; readonly iconClass: string;

View File

@ -4,9 +4,9 @@ export class InstallationProgressDialog extends AbstractDialog<undefined> {
readonly value = undefined; readonly value = undefined;
constructor(componentName: string) { constructor(componentName: string, version: string) {
super({ title: 'Installation in progress' }); super({ title: 'Installation in progress' });
this.contentNode.textContent = `Installing ${componentName}. Please wait.`; this.contentNode.textContent = `Installing ${componentName} [${version}]. Please wait...`;
} }
} }

View File

@ -1,12 +1,18 @@
import * as React from 'react'; import * as React from 'react';
import { injectable } from 'inversify'; import { injectable } from 'inversify';
import { Library } from '../../common/protocol/library-service'; import { Library } from '../../common/protocol/library-service';
import { Installable } from '../../common/protocol/installable';
import { ListItemRenderer } from '../components/component-list/list-item-renderer'; import { ListItemRenderer } from '../components/component-list/list-item-renderer';
import { ComponentListItem } from '../components/component-list/component-list-item';
@injectable() @injectable()
export class LibraryItemRenderer extends ListItemRenderer<Library> { export class LibraryItemRenderer extends ListItemRenderer<Library> {
renderItem(item: Library, install: (item: Library) => Promise<void>): React.ReactNode { renderItem(
input: ComponentListItem.State & { item: Library },
install: (item: Library, version: Installable.Version) => Promise<void>): React.ReactNode {
const { item } = input;
const name = <span className='name'>{item.name}</span>; const name = <span className='name'>{item.name}</span>;
const author = <span className='author'>by {item.author}</span>; const author = <span className='author'>by {item.author}</span>;
const installedVersion = !!item.installedVersion && <div className='version-info'> const installedVersion = !!item.installedVersion && <div className='version-info'>
@ -16,7 +22,7 @@ export class LibraryItemRenderer extends ListItemRenderer<Library> {
const summary = <div className='summary'>{item.summary}</div>; const summary = <div className='summary'>{item.summary}</div>;
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onClick}>More info</a>; const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onMoreInfoClick}>More info</a>;
const installButton = item.installable && !item.installedVersion && const installButton = item.installable && !item.installedVersion &&
<button className='install' onClick={install.bind(this, item)}>INSTALL</button>; <button className='install' onClick={install.bind(this, item)}>INSTALL</button>;

View File

@ -1,3 +1,4 @@
import { Installable } from './installable';
export interface ArduinoComponent { export interface ArduinoComponent {
readonly name: string; readonly name: string;
@ -6,8 +7,8 @@ export interface ArduinoComponent {
readonly description: string; readonly description: string;
readonly moreInfoLink?: string; readonly moreInfoLink?: string;
readonly availableVersions: string[]; readonly availableVersions: Installable.Version[];
readonly installable: boolean; readonly installable: boolean;
readonly installedVersion?: string; readonly installedVersion?: Installable.Version;
} }

View File

@ -170,6 +170,12 @@ export interface BoardPackage extends ArduinoComponent {
id: string; id: string;
boards: Board[]; boards: Board[];
} }
export namespace BoardPackage {
/**
* Most recent version comes first, then the previous versions. (`1.8.1`, `1.6.3`, `1.6.2`, `1.6.1` and so on.)
*/
export const VERSION_COMPARATOR = (left: string, right: string) => naturalCompare(right, left);
}
export interface Board { export interface Board {
name: string name: string

View File

@ -1,3 +1,11 @@
export interface Installable<T> { import { ArduinoComponent } from './arduino-component';
install(item: T): Promise<void>;
} export interface Installable<T extends ArduinoComponent> {
/**
* If `options.version` is specified, that will be installed. Otherwise, `item.availableVersions[0]`.
*/
install(options: { item: T, version?: Installable.Version }): Promise<void>;
}
export namespace Installable {
export type Version = string;
}

View File

@ -5,14 +5,9 @@ import { ArduinoComponent } from './arduino-component';
export const LibraryServicePath = '/services/library-service'; export const LibraryServicePath = '/services/library-service';
export const LibraryService = Symbol('LibraryService'); export const LibraryService = Symbol('LibraryService');
export interface LibraryService extends Installable<Library>, Searchable<Library> { export interface LibraryService extends Installable<Library>, Searchable<Library> {
install(library: Library): Promise<void>; install(options: { item: Library, version?: Installable.Version }): Promise<void>;
} }
export interface Library extends ArduinoComponent { export interface Library extends ArduinoComponent {
readonly builtIn?: boolean; readonly builtIn?: boolean;
} }
export namespace Library {
// TODO: figure out whether we need a dedicated `version` type.
export type Version = string;
}

View File

@ -2,10 +2,11 @@ import * as PQueue from 'p-queue';
import { injectable, inject, postConstruct, named } from 'inversify'; import { injectable, inject, postConstruct, named } from 'inversify';
import { ILogger } from '@theia/core/lib/common/logger'; import { ILogger } from '@theia/core/lib/common/logger';
import { BoardsService, AttachedSerialBoard, BoardPackage, Board, AttachedNetworkBoard, BoardsServiceClient, Port } from '../common/protocol/boards-service'; import { BoardsService, AttachedSerialBoard, BoardPackage, Board, AttachedNetworkBoard, BoardsServiceClient, Port } from '../common/protocol/boards-service';
import { PlatformSearchReq, PlatformSearchResp, PlatformInstallReq, PlatformInstallResp, PlatformListReq, PlatformListResp } from './cli-protocol/commands/core_pb'; import { PlatformSearchReq, PlatformSearchResp, PlatformInstallReq, PlatformInstallResp, PlatformListReq, PlatformListResp, Platform } from './cli-protocol/commands/core_pb';
import { CoreClientProvider } from './core-client-provider'; import { CoreClientProvider } from './core-client-provider';
import { BoardListReq, BoardListResp } from './cli-protocol/commands/board_pb'; import { BoardListReq, BoardListResp } from './cli-protocol/commands/board_pb';
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service'; import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
import { Installable } from '../common/protocol/installable';
@injectable() @injectable()
export class BoardsServiceImpl implements BoardsService { export class BoardsServiceImpl implements BoardsService {
@ -214,35 +215,47 @@ export class BoardsServiceImpl implements BoardsService {
const req = new PlatformSearchReq(); const req = new PlatformSearchReq();
req.setSearchArgs(options.query || ""); req.setSearchArgs(options.query || "");
req.setAllVersions(true);
req.setInstance(instance); req.setInstance(instance);
const resp = await new Promise<PlatformSearchResp>((resolve, reject) => client.platformSearch(req, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp))); const resp = await new Promise<PlatformSearchResp>((resolve, reject) => client.platformSearch(req, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp)));
const packages = new Map<string, BoardPackage>();
let items = resp.getSearchOutputList().map(item => { const toPackage = (platform: Platform) => {
let installedVersion: string | undefined; let installedVersion: string | undefined;
const matchingPlatform = installedPlatforms.find(ip => ip.getId() === item.getId()); const matchingPlatform = installedPlatforms.find(ip => ip.getId() === platform.getId());
if (!!matchingPlatform) { if (!!matchingPlatform) {
installedVersion = matchingPlatform.getInstalled(); installedVersion = matchingPlatform.getInstalled();
} }
return {
const result: BoardPackage = { id: platform.getId(),
id: item.getId(), name: platform.getName(),
name: item.getName(), author: platform.getMaintainer(),
author: item.getMaintainer(), availableVersions: [platform.getLatest()],
availableVersions: [item.getLatest()], description: platform.getBoardsList().map(b => b.getName()).join(", "),
description: item.getBoardsList().map(b => b.getName()).join(", "),
installable: true, installable: true,
summary: "Boards included in this package:", summary: "Boards included in this package:",
installedVersion, installedVersion,
boards: item.getBoardsList().map(b => <Board>{ name: b.getName(), fqbn: b.getFqbn() }), boards: platform.getBoardsList().map(b => <Board>{ name: b.getName(), fqbn: b.getFqbn() }),
moreInfoLink: item.getWebsite() moreInfoLink: platform.getWebsite()
} }
return result; }
});
return { items }; for (const platform of resp.getSearchOutputList()) {
const id = platform.getId();
const pkg = packages.get(id);
if (pkg) {
pkg.availableVersions.push(platform.getLatest());
pkg.availableVersions.sort(BoardPackage.VERSION_COMPARATOR);
} else {
packages.set(id, toPackage(platform));
}
}
return { items: [...packages.values()] };
} }
async install(pkg: BoardPackage): Promise<void> { async install(options: { item: BoardPackage, version?: Installable.Version }): Promise<void> {
const pkg = options.item;
const version = !!options.version ? options.version : pkg.availableVersions[0];
const coreClient = await this.coreClientProvider.getClient(); const coreClient = await this.coreClientProvider.getClient();
if (!coreClient) { if (!coreClient) {
return; return;
@ -255,7 +268,7 @@ export class BoardsServiceImpl implements BoardsService {
req.setInstance(instance); req.setInstance(instance);
req.setArchitecture(boardName); req.setArchitecture(boardName);
req.setPlatformPackage(platform); req.setPlatformPackage(platform);
req.setVersion(pkg.availableVersions[0]); req.setVersion(version);
console.info("Starting board installation", pkg); console.info("Starting board installation", pkg);
const resp = client.platformInstall(req); const resp = client.platformInstall(req);

View File

@ -2,6 +2,7 @@
// file: commands/board.proto // file: commands/board.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
import * as commands_common_pb from "../commands/common_pb"; import * as commands_common_pb from "../commands/common_pb";

View File

@ -2,6 +2,7 @@
// file: commands/commands.proto // file: commands/commands.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as grpc from "@grpc/grpc-js"; import * as grpc from "@grpc/grpc-js";
import * as commands_commands_pb from "../commands/commands_pb"; import * as commands_commands_pb from "../commands/commands_pb";

View File

@ -2,6 +2,7 @@
// file: commands/commands.proto // file: commands/commands.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
import * as commands_common_pb from "../commands/common_pb"; import * as commands_common_pb from "../commands/common_pb";

View File

@ -2,6 +2,7 @@
// file: commands/common.proto // file: commands/common.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";

View File

@ -2,6 +2,7 @@
// file: commands/compile.proto // file: commands/compile.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
import * as commands_common_pb from "../commands/common_pb"; import * as commands_common_pb from "../commands/common_pb";

View File

@ -2,6 +2,7 @@
// file: commands/core.proto // file: commands/core.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
import * as commands_common_pb from "../commands/common_pb"; import * as commands_common_pb from "../commands/common_pb";
@ -262,6 +263,9 @@ export class PlatformSearchReq extends jspb.Message {
getSearchArgs(): string; getSearchArgs(): string;
setSearchArgs(value: string): void; setSearchArgs(value: string): void;
getAllVersions(): boolean;
setAllVersions(value: boolean): void;
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformSearchReq.AsObject; toObject(includeInstance?: boolean): PlatformSearchReq.AsObject;
@ -277,6 +281,7 @@ export namespace PlatformSearchReq {
export type AsObject = { export type AsObject = {
instance?: commands_common_pb.Instance.AsObject, instance?: commands_common_pb.Instance.AsObject,
searchArgs: string, searchArgs: string,
allVersions: boolean,
} }
} }

View File

@ -1705,7 +1705,8 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.prototype.toObject = function(op
proto.cc.arduino.cli.commands.PlatformSearchReq.toObject = function(includeInstance, msg) { proto.cc.arduino.cli.commands.PlatformSearchReq.toObject = function(includeInstance, msg) {
var f, obj = { var f, obj = {
instance: (f = msg.getInstance()) && commands_common_pb.Instance.toObject(includeInstance, f), instance: (f = msg.getInstance()) && commands_common_pb.Instance.toObject(includeInstance, f),
searchArgs: jspb.Message.getFieldWithDefault(msg, 2, "") searchArgs: jspb.Message.getFieldWithDefault(msg, 2, ""),
allVersions: jspb.Message.getFieldWithDefault(msg, 3, false)
}; };
if (includeInstance) { if (includeInstance) {
@ -1751,6 +1752,10 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.deserializeBinaryFromReader = fu
var value = /** @type {string} */ (reader.readString()); var value = /** @type {string} */ (reader.readString());
msg.setSearchArgs(value); msg.setSearchArgs(value);
break; break;
case 3:
var value = /** @type {boolean} */ (reader.readBool());
msg.setAllVersions(value);
break;
default: default:
reader.skipField(); reader.skipField();
break; break;
@ -1795,6 +1800,13 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.serializeBinaryToWriter = functi
f f
); );
} }
f = message.getAllVersions();
if (f) {
writer.writeBool(
3,
f
);
}
}; };
@ -1843,6 +1855,23 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.prototype.setSearchArgs = functi
}; };
/**
* optional bool all_versions = 3;
* Note that Boolean fields may be set to 0/1 when serialized from a Java server.
* You should avoid comparisons like {@code val === true/false} in those cases.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.PlatformSearchReq.prototype.getAllVersions = function() {
return /** @type {boolean} */ (jspb.Message.getFieldWithDefault(this, 3, false));
};
/** @param {boolean} value */
proto.cc.arduino.cli.commands.PlatformSearchReq.prototype.setAllVersions = function(value) {
jspb.Message.setProto3BooleanField(this, 3, value);
};
/** /**
* Generated by JsPbCodeGenerator. * Generated by JsPbCodeGenerator.

View File

@ -2,6 +2,7 @@
// file: commands/lib.proto // file: commands/lib.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
import * as commands_common_pb from "../commands/common_pb"; import * as commands_common_pb from "../commands/common_pb";

View File

@ -2,6 +2,7 @@
// file: commands/upload.proto // file: commands/upload.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
import * as commands_common_pb from "../commands/common_pb"; import * as commands_common_pb from "../commands/common_pb";

View File

@ -2,6 +2,7 @@
// file: monitor/monitor.proto // file: monitor/monitor.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as grpc from "@grpc/grpc-js"; import * as grpc from "@grpc/grpc-js";
import * as monitor_monitor_pb from "../monitor/monitor_pb"; import * as monitor_monitor_pb from "../monitor/monitor_pb";

View File

@ -2,6 +2,7 @@
// file: monitor/monitor.proto // file: monitor/monitor.proto
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
import * as google_protobuf_struct_pb from "google-protobuf/google/protobuf/struct_pb"; import * as google_protobuf_struct_pb from "google-protobuf/google/protobuf/struct_pb";

View File

@ -4,6 +4,7 @@ import { CoreClientProvider } from './core-client-provider';
import { LibrarySearchReq, LibrarySearchResp, LibraryListReq, LibraryListResp, LibraryRelease, import { LibrarySearchReq, LibrarySearchResp, LibraryListReq, LibraryListResp, LibraryRelease,
InstalledLibrary, LibraryInstallReq, LibraryInstallResp } from './cli-protocol/commands/lib_pb'; InstalledLibrary, LibraryInstallReq, LibraryInstallResp } from './cli-protocol/commands/lib_pb';
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service'; import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
import { Installable } from '../common/protocol/installable';
@injectable() @injectable()
export class LibraryServiceImpl implements LibraryService { export class LibraryServiceImpl implements LibraryService {
@ -58,7 +59,9 @@ export class LibraryServiceImpl implements LibraryService {
return { items }; return { items };
} }
async install(library: Library): Promise<void> { async install(options: { item: Library, version?: Installable.Version }): Promise<void> {
const library = options.item;
const version = !!options.version ? options.version : library.availableVersions[0];
const coreClient = await this.coreClientProvider.getClient(); const coreClient = await this.coreClientProvider.getClient();
if (!coreClient) { if (!coreClient) {
return; return;
@ -68,7 +71,7 @@ export class LibraryServiceImpl implements LibraryService {
const req = new LibraryInstallReq(); const req = new LibraryInstallReq();
req.setInstance(instance); req.setInstance(instance);
req.setName(library.name); req.setName(library.name);
req.setVersion(library.availableVersions[0]); req.setVersion(version);
const resp = client.libraryInstall(req); const resp = client.libraryInstall(req);
resp.on('data', (r: LibraryInstallResp) => { resp.on('data', (r: LibraryInstallResp) => {