Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 49d12d9

Browse files
fstasiAlberto Iannaccone
and
Alberto Iannaccone
authored
IDE to run CLI with auto assigned port (#673)
* get daemon port from CLI stdout * config-service to use CLI daemon port * updating LS * fixed tests * fix upload blocked when selectedBoard.port is undefined * bump arduino-cli to 0.20.2 Co-authored-by: Alberto Iannaccone <a.iannaccone@arduino.cc>
1 parent 767b09d commit 49d12d9

File tree

10 files changed

+87
-74
lines changed

10 files changed

+87
-74
lines changed

‎arduino-ide-extension/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
],
152152
"arduino": {
153153
"cli": {
154-
"version": "0.20.1"
154+
"version": "0.20.2"
155155
},
156156
"fwuploader": {
157157
"version": "2.0.0"

‎arduino-ide-extension/scripts/download-ls.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// - https://downloads.arduino.cc/arduino-language-server/clangd/clangd_${VERSION}_${SUFFIX}
55

66
(() => {
7-
const DEFAULT_ALS_VERSION = '0.5.0-rc2';
7+
const DEFAULT_ALS_VERSION = '0.5.0-rc6';
88
const DEFAULT_CLANGD_VERSION = 'snapshot_20210124';
99

1010
const path = require('path');

‎arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ export class ArduinoFrontendContribution
374374
'arduino.languageserver.start',
375375
{
376376
lsPath,
377-
cliDaemonAddr: `localhost:${config.daemon.port}`,
377+
cliDaemonAddr: `localhost:${config.daemon.port}`,// TODO: verify if this port is coming from the BE
378378
clangdPath,
379379
log: currentSketchPath ? currentSketchPath : log,
380380
cliDaemonInstance: '1',

‎arduino-ide-extension/src/browser/contributions/upload-sketch.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ export class UploadSketch extends SketchContribution {
6363
if (!fqbn) {
6464
return '';
6565
}
66-
const address = boardsConfig.selectedBoard?.port?.address;
66+
const address =
67+
boardsConfig.selectedBoard?.port?.address ||
68+
boardsConfig.selectedPort?.address;
6769
if (!address) {
6870
return '';
6971
}
@@ -277,8 +279,8 @@ export class UploadSketch extends SketchContribution {
277279
{ timeout: 3000 }
278280
);
279281
} catch (e) {
280-
let errorMessage = "";
281-
if (typeof e === "string") {
282+
let errorMessage = '';
283+
if (typeof e === 'string') {
282284
errorMessage = e;
283285
} else {
284286
errorMessage = e.toString();

‎arduino-ide-extension/src/common/protocol/arduino-daemon.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export const ArduinoDaemonPath = '/services/arduino-daemon';
22
export const ArduinoDaemon = Symbol('ArduinoDaemon');
33
export interface ArduinoDaemon {
44
isRunning(): Promise<boolean>;
5+
getPort(): Promise<string>;
56
}

‎arduino-ide-extension/src/node/arduino-daemon-impl.ts‎

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class ArduinoDaemonImpl
4242
protected _running = false;
4343
protected _ready = new Deferred<void>();
4444
protected _execPath: string | undefined;
45+
protected _port: string;
4546

4647
// Backend application lifecycle.
4748

@@ -55,12 +56,17 @@ export class ArduinoDaemonImpl
5556
return Promise.resolve(this._running);
5657
}
5758

59+
async getPort(): Promise<string> {
60+
return Promise.resolve(this._port);
61+
}
62+
5863
async startDaemon(): Promise<void> {
5964
try {
6065
this.toDispose.dispose(); // This will `kill` the previously started daemon process, if any.
6166
const cliPath = await this.getExecPath();
6267
this.onData(`Starting daemon from ${cliPath}...`);
63-
const daemon = await this.spawnDaemonProcess();
68+
const { daemon, port } = await this.spawnDaemonProcess();
69+
this._port = port;
6470
// Watchdog process for terminating the daemon process when the backend app terminates.
6571
spawn(
6672
process.execPath,
@@ -148,6 +154,10 @@ export class ArduinoDaemonImpl
148154
const cliConfigPath = join(FileUri.fsPath(configDirUri), CLI_CONFIG);
149155
return [
150156
'daemon',
157+
'--format',
158+
'jsonmini',
159+
'--port',
160+
'0',
151161
'--config-file',
152162
`"${cliConfigPath}"`,
153163
'-v',
@@ -156,12 +166,15 @@ export class ArduinoDaemonImpl
156166
];
157167
}
158168

159-
protected async spawnDaemonProcess(): Promise<ChildProcess> {
169+
protected async spawnDaemonProcess(): Promise<{
170+
daemon: ChildProcess;
171+
port: string;
172+
}> {
160173
const [cliPath, args] = await Promise.all([
161174
this.getExecPath(),
162175
this.getSpawnArgs(),
163176
]);
164-
const ready = new Deferred<ChildProcess>();
177+
const ready = new Deferred<{daemon: ChildProcess;port: string}>();
165178
const options = { shell: true };
166179
const daemon = spawn(`"${cliPath}"`, args, options);
167180

@@ -171,20 +184,37 @@ export class ArduinoDaemonImpl
171184

172185
daemon.stdout.on('data', (data) => {
173186
const message = data.toString();
187+
188+
let port = '';
189+
let address = '';
190+
message
191+
.split('\n')
192+
.filter((line: string) => line.length)
193+
.forEach((line: string) => {
194+
try {
195+
const parsedLine = JSON.parse(line);
196+
if ('Port' in parsedLine) {
197+
port = parsedLine.Port;
198+
}
199+
if ('IP' in parsedLine) {
200+
address = parsedLine.IP;
201+
}
202+
} catch (err) {
203+
// ignore
204+
}
205+
});
206+
174207
this.onData(message);
175208
if (!grpcServerIsReady) {
176209
const error = DaemonError.parse(message);
177210
if (error) {
178211
ready.reject(error);
212+
return;
179213
}
180-
for (const expected of [
181-
'Daemon is listening on TCP port',
182-
'Daemon is now listening on 127.0.0.1',
183-
]) {
184-
if (message.includes(expected)) {
185-
grpcServerIsReady = true;
186-
ready.resolve(daemon);
187-
}
214+
215+
if (port.length && address.length) {
216+
grpcServerIsReady = true;
217+
ready.resolve({ daemon, port });
188218
}
189219
}
190220
});

‎arduino-ide-extension/src/node/config-service-impl.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,11 @@ export class ConfigServiceImpl
7575

7676
async getConfiguration(): Promise<Config> {
7777
await this.ready.promise;
78-
return this.config;
78+
await this.daemon.ready;
79+
return { ...this.config, daemon: { port: await this.daemon.getPort() } };
7980
}
8081

82+
// Used by frontend to update the config.
8183
async setConfiguration(config: Config): Promise<void> {
8284
await this.ready.promise;
8385
if (Config.sameAs(this.config, config)) {
@@ -108,7 +110,9 @@ export class ConfigServiceImpl
108110
copyDefaultCliConfig.locale = locale || 'en';
109111
const proxy = Network.stringify(network);
110112
copyDefaultCliConfig.network = { proxy };
111-
const { port } = copyDefaultCliConfig.daemon;
113+
114+
// always use the port of the daemon
115+
const port = await this.daemon.getPort();
112116
await this.updateDaemon(port, copyDefaultCliConfig);
113117
await this.writeDaemonState(port);
114118

‎arduino-ide-extension/src/node/core-client-provider.ts‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,31 +48,28 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
4848
this._initialized = new Deferred<void>();
4949
}
5050

51-
protected async reconcileClient(
52-
port: string|number|undefined
53-
): Promise<void>{
51+
protected async reconcileClient(): Promise<void>{
52+
constport=awaitthis.daemon.getPort();
53+
5454
if (port && port === this._port) {
5555
// No need to create a new gRPC client, but we have to update the indexes.
5656
if (this._client && !(this._client instanceof Error)) {
5757
await this.updateIndexes(this._client);
5858
this.onClientReadyEmitter.fire();
5959
}
6060
} else {
61-
await super.reconcileClient(port);
61+
await super.reconcileClient();
6262
this.onClientReadyEmitter.fire();
6363
}
6464
}
6565

6666
@postConstruct()
6767
protected async init(): Promise<void> {
6868
this.daemon.ready.then(async () => {
69-
const cliConfig = this.configService.cliConfiguration;
7069
// First create the client and the instance synchronously
7170
// and notify client is ready.
7271
// TODO: Creation failure should probably be handled here
73-
await this.reconcileClient(
74-
cliConfig ? cliConfig.daemon.port : undefined
75-
).then(() => {
72+
await this.reconcileClient().then(() => {
7673
this._created.resolve();
7774
});
7875

‎arduino-ide-extension/src/node/grpc-client-provider.ts‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ export abstract class GrpcClientProvider<C> {
2121
@postConstruct()
2222
protected init(): void {
2323
const updateClient = () => {
24-
const cliConfig = this.configService.cliConfiguration;
25-
this.reconcileClient(cliConfig ? cliConfig.daemon.port : undefined);
24+
this.reconcileClient();
2625
};
2726
this.configService.onConfigChange(updateClient);
2827
this.daemon.ready.then(updateClient);
@@ -44,9 +43,9 @@ export abstract class GrpcClientProvider<C> {
4443
}
4544
}
4645

47-
protected async reconcileClient(
48-
port: string|number|undefined
49-
): Promise<void>{
46+
protected async reconcileClient(): Promise<void>{
47+
constport=awaitthis.daemon.getPort();
48+
5049
if (this._port === port) {
5150
return; // Nothing to do.
5251
}

‎arduino-ide-extension/src/test/node/arduino-daemon-impl.test.ts‎

Lines changed: 22 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,36 @@ import * as fs from 'fs';
22
// import * as net from 'net';
33
import * as path from 'path';
44
import * as temp from 'temp';
5-
import { fail } from 'assert';
65
import { expect } from 'chai';
76
import { ChildProcess } from 'child_process';
87
import { safeLoad, safeDump } from 'js-yaml';
9-
import { DaemonError,ArduinoDaemonImpl } from '../../node/arduino-daemon-impl';
8+
import { ArduinoDaemonImpl } from '../../node/arduino-daemon-impl';
109
import { spawnCommand } from '../../node/exec-util';
1110
import { CLI_CONFIG } from '../../node/cli-config';
1211

1312
const track = temp.track();
1413

1514
class SilentArduinoDaemonImpl extends ArduinoDaemonImpl {
16-
constructor(
17-
private port: string | number,
18-
private logFormat: 'text' | 'json'
19-
) {
15+
constructor(private logFormat: 'text' | 'json') {
2016
super();
2117
}
2218

2319
onData(data: string): void {
2420
// NOOP
2521
}
2622

27-
async spawnDaemonProcess(): Promise<ChildProcess> {
23+
async spawnDaemonProcess(): Promise<{daemon: ChildProcess;port: string}> {
2824
return super.spawnDaemonProcess();
2925
}
3026

3127
protected async getSpawnArgs(): Promise<string[]> {
3228
const cliConfigPath = await this.initCliConfig();
3329
return [
3430
'daemon',
31+
'--format',
32+
'jsonmini',
33+
'--port',
34+
'0',
3535
'--config-file',
3636
cliConfigPath,
3737
'-v',
@@ -53,7 +53,7 @@ class SilentArduinoDaemonImpl extends ArduinoDaemonImpl {
5353
encoding: 'utf8',
5454
});
5555
const cliConfig = safeLoad(content) as any;
56-
cliConfig.daemon.port = String(this.port);
56+
// cliConfig.daemon.port = String(this.port);
5757
const modifiedContent = safeDump(cliConfig);
5858
fs.writeFileSync(path.join(destDir, CLI_CONFIG), modifiedContent, {
5959
encoding: 'utf8',
@@ -113,43 +113,23 @@ describe('arduino-daemon-impl', () => {
113113
// }
114114
// });
115115

116-
it('should parse an error - unknown address [json]', async () => {
117-
try {
118-
await new SilentArduinoDaemonImpl('foo', 'json').spawnDaemonProcess();
119-
fail('Expected a failure.');
120-
} catch (e) {
121-
expect(e).to.be.instanceOf(DaemonError);
122-
expect(e.code).to.be.equal(DaemonError.UNKNOWN_ADDRESS);
123-
}
124-
});
116+
it('should parse the port address when the log format is json', async () => {
117+
const { daemon, port } = await new SilentArduinoDaemonImpl(
118+
'json'
119+
).spawnDaemonProcess();
125120

126-
it('should parse an error - unknown address [text]', async () => {
127-
try {
128-
await new SilentArduinoDaemonImpl('foo', 'text').spawnDaemonProcess();
129-
fail('Expected a failure.');
130-
} catch (e) {
131-
expect(e).to.be.instanceOf(DaemonError);
132-
expect(e.code).to.be.equal(DaemonError.UNKNOWN_ADDRESS);
133-
}
121+
expect(port).not.to.be.undefined;
122+
expect(port).not.to.be.equal('0');
123+
daemon.kill();
134124
});
135125

136-
it('should parse an error - invalid port [json]', async () => {
137-
try {
138-
await new SilentArduinoDaemonImpl(-1, 'json').spawnDaemonProcess();
139-
fail('Expected a failure.');
140-
} catch (e) {
141-
expect(e).to.be.instanceOf(DaemonError);
142-
expect(e.code).to.be.equal(DaemonError.INVALID_PORT);
143-
}
144-
});
126+
it('should parse the port address when the log format is text', async () => {
127+
const { daemon, port } = await new SilentArduinoDaemonImpl(
128+
'text'
129+
).spawnDaemonProcess();
145130

146-
it('should parse an error - invalid port [text]', async () => {
147-
try {
148-
await new SilentArduinoDaemonImpl(-1, 'text').spawnDaemonProcess();
149-
fail('Expected a failure.');
150-
} catch (e) {
151-
expect(e).to.be.instanceOf(DaemonError);
152-
expect(e.code).to.be.equal(DaemonError.INVALID_PORT);
153-
}
131+
expect(port).not.to.be.undefined;
132+
expect(port).not.to.be.equal('0');
133+
daemon.kill();
154134
});
155135
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /