Skip to content

Commit 0c20ae0

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
Various library/platform index update fixes
- IDE2 can start if the package index download fails. Closes #1084 - Split the lib and platform index update. Closes #1156 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent 945a8f4 commit 0c20ae0

18 files changed

+1410
-338
lines changed

arduino-ide-extension/package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,11 @@
158158
],
159159
"arduino": {
160160
"cli": {
161-
"version": "0.27.1"
161+
"version": {
162+
"owner": "cmaglie",
163+
"repo": "arduino-cli",
164+
"commitish": "download_progress_refactor"
165+
}
162166
},
163167
"fwuploader": {
164168
"version": "2.2.0"

arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ import { OutputEditorFactory } from './theia/output/output-editor-factory';
332332
import { StartupTaskProvider } from '../electron-common/startup-task';
333333
import { DeleteSketch } from './contributions/delete-sketch';
334334
import { UserFields } from './contributions/user-fields';
335+
import { UpdateIndexes } from './contributions/update-indexes';
335336

336337
const registerArduinoThemes = () => {
337338
const themes: MonacoThemeJson[] = [
@@ -744,6 +745,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
744745
Contribution.configure(bind, CheckForUpdates);
745746
Contribution.configure(bind, UserFields);
746747
Contribution.configure(bind, DeleteSketch);
748+
Contribution.configure(bind, UpdateIndexes);
747749

748750
bindContributionProvider(bind, StartupTaskProvider);
749751
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window

arduino-ide-extension/src/browser/boards/boards-config.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export class BoardsConfig extends React.Component<
132132
this.props.notificationCenter.onPlatformDidUninstall(() =>
133133
this.updateBoards(this.state.query)
134134
),
135-
this.props.notificationCenter.onIndexDidUpdate(() =>
135+
this.props.notificationCenter.onIndexUpdateDidComplete(() =>
136136
this.updateBoards(this.state.query)
137137
),
138138
this.props.notificationCenter.onDaemonDidStart(() =>

arduino-ide-extension/src/browser/contributions/indexes-update-progress.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ export class IndexesUpdateProgress extends Contribution {
1616
| undefined;
1717

1818
override onStart(): void {
19-
this.notificationCenter.onIndexWillUpdate((progressId) =>
19+
this.notificationCenter.onIndexUpdateWillStart(({ progressId }) =>
2020
this.getOrCreateProgress(progressId)
2121
);
2222
this.notificationCenter.onIndexUpdateDidProgress((progress) => {
2323
this.getOrCreateProgress(progress).then((delegate) =>
2424
delegate.report(progress)
2525
);
2626
});
27-
this.notificationCenter.onIndexDidUpdate((progressId) => {
27+
this.notificationCenter.onIndexUpdateDidComplete(({ progressId }) => {
2828
this.cancelProgress(progressId);
2929
});
3030
this.notificationCenter.onIndexUpdateDidFail(({ progressId, message }) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
2+
import { nls } from '@theia/core/lib/common/nls';
3+
import { inject, injectable } from '@theia/core/shared/inversify';
4+
import { CoreService, IndexType } from '../../common/protocol';
5+
import { NotificationCenter } from '../notification-center';
6+
import { WindowServiceExt } from '../theia/core/window-service-ext';
7+
import { Command, CommandRegistry, Contribution } from './contribution';
8+
9+
@injectable()
10+
export class UpdateIndexes extends Contribution {
11+
@inject(WindowServiceExt)
12+
private readonly windowService: WindowServiceExt;
13+
@inject(LocalStorageService)
14+
private readonly localStorage: LocalStorageService;
15+
@inject(CoreService)
16+
private readonly coreService: CoreService;
17+
@inject(NotificationCenter)
18+
private readonly notificationCenter: NotificationCenter;
19+
20+
protected override init(): void {
21+
super.init();
22+
this.notificationCenter.onIndexUpdateDidComplete(({ summary }) =>
23+
Promise.all(
24+
Object.entries(summary).map(([type, updatedAt]) =>
25+
this.setLastUpdateDateTime(type as IndexType, updatedAt)
26+
)
27+
)
28+
);
29+
}
30+
31+
override onReady(): void {
32+
this.checkForUpdates();
33+
}
34+
35+
override registerCommands(registry: CommandRegistry): void {
36+
registry.registerCommand(UpdateIndexes.Commands.UPDATE_INDEXES, {
37+
execute: () => this.updateIndexes(IndexType.All, true),
38+
});
39+
registry.registerCommand(UpdateIndexes.Commands.UPDATE_PLATFORM_INDEX, {
40+
execute: () => this.updateIndexes(['platform'], true),
41+
});
42+
registry.registerCommand(UpdateIndexes.Commands.UPDATE_LIBRARY_INDEX, {
43+
execute: () => this.updateIndexes(['library'], true),
44+
});
45+
}
46+
47+
private async checkForUpdates(): Promise<void> {
48+
const checkForUpdates = this.preferences['arduino.checkForUpdates'];
49+
if (!checkForUpdates) {
50+
console.debug(
51+
'[update-indexes]: `arduino.checkForUpdates` is `false`. Skipping updating the indexes.'
52+
);
53+
return;
54+
}
55+
56+
if (await this.windowService.isFirstWindow()) {
57+
const summary = await this.coreService.indexUpdateSummaryBeforeInit();
58+
if (summary.message) {
59+
this.messageService.error(summary.message);
60+
}
61+
const typesToCheck = IndexType.All.filter((type) => !(type in summary));
62+
if (Object.keys(summary).length) {
63+
console.debug(
64+
`[update-indexes]: Detected an index update summary before the core gRPC client initialization. Updating local storage with ${JSON.stringify(
65+
summary
66+
)}`
67+
);
68+
} else {
69+
console.debug(
70+
'[update-indexes]: No index update summary was available before the core gRPC client initialization. Checking the status of the all the index types.'
71+
);
72+
}
73+
await Promise.allSettled([
74+
...Object.entries(summary).map(([type, updatedAt]) =>
75+
this.setLastUpdateDateTime(type as IndexType, updatedAt)
76+
),
77+
this.updateIndexes(typesToCheck),
78+
]);
79+
}
80+
}
81+
82+
private async updateIndexes(
83+
types: IndexType[],
84+
force = false
85+
): Promise<void> {
86+
const updatedAt = new Date().toISOString();
87+
return Promise.all(
88+
types.map((type) => this.needsIndexUpdate(type, updatedAt, force))
89+
).then((needsIndexUpdateResults) => {
90+
const typesToUpdate = needsIndexUpdateResults.filter(IndexType.is);
91+
if (typesToUpdate.length) {
92+
console.debug(
93+
`[update-indexes]: Requesting the index update of type: ${JSON.stringify(
94+
typesToUpdate
95+
)} with date time: ${updatedAt}.`
96+
);
97+
return this.coreService.updateIndex({ types: typesToUpdate });
98+
}
99+
});
100+
}
101+
102+
private async needsIndexUpdate(
103+
type: IndexType,
104+
now: string,
105+
force = false
106+
): Promise<IndexType | false> {
107+
if (force) {
108+
console.debug(
109+
`[update-indexes]: Update for index type: '${type}' was forcefully requested.`
110+
);
111+
return type;
112+
}
113+
const lastUpdateIsoDateTime = await this.getLastUpdateDateTime(type);
114+
if (!lastUpdateIsoDateTime) {
115+
console.debug(
116+
`[update-indexes]: No last update date time was persisted for index type: '${type}'. Index update is required.`
117+
);
118+
return type;
119+
}
120+
const lastUpdateDateTime = Date.parse(lastUpdateIsoDateTime);
121+
if (Number.isNaN(lastUpdateDateTime)) {
122+
console.debug(
123+
`[update-indexes]: Invalid last update date time was persisted for index type: '${type}'. Last update date time was: ${lastUpdateDateTime}. Index update is required.`
124+
);
125+
return type;
126+
}
127+
const diff = new Date(now).getTime() - lastUpdateDateTime;
128+
const needsIndexUpdate = diff >= this.threshold;
129+
console.debug(
130+
`[update-indexes]: Update for index type '${type}' is ${
131+
needsIndexUpdate ? '' : 'not '
132+
}required. Now: ${now}, Last index update date time: ${new Date(
133+
lastUpdateDateTime
134+
).toISOString()}, diff: ${diff} ms, threshold: ${this.threshold} ms.`
135+
);
136+
return needsIndexUpdate ? type : false;
137+
}
138+
139+
private async getLastUpdateDateTime(
140+
type: IndexType
141+
): Promise<string | undefined> {
142+
const key = this.storageKeyOf(type);
143+
return this.localStorage.getData<string>(key);
144+
}
145+
146+
private async setLastUpdateDateTime(
147+
type: IndexType,
148+
updatedAt: string
149+
): Promise<void> {
150+
const key = this.storageKeyOf(type);
151+
return this.localStorage.setData<string>(key, updatedAt).finally(() => {
152+
console.debug(
153+
`[update-indexes]: Updated the last index update date time of '${type}' to ${updatedAt}.`
154+
);
155+
});
156+
}
157+
158+
private storageKeyOf(type: IndexType): string {
159+
return `index-last-update-time--${type}`;
160+
}
161+
162+
private get threshold(): number {
163+
return 4 * 60 * 60 * 1_000; // four hours in millis
164+
}
165+
}
166+
export namespace UpdateIndexes {
167+
export namespace Commands {
168+
export const UPDATE_INDEXES: Command & { label: string } = {
169+
id: 'arduino-update-indexes',
170+
label: nls.localize(
171+
'arduino/updateIndexes/updateIndexes',
172+
'Update Indexes'
173+
),
174+
category: 'Arduino',
175+
};
176+
export const UPDATE_PLATFORM_INDEX: Command & { label: string } = {
177+
id: 'arduino-update-package-index',
178+
label: nls.localize(
179+
'arduino/updateIndexes/updatePackageIndex',
180+
'Update Package Index'
181+
),
182+
category: 'Arduino',
183+
};
184+
export const UPDATE_LIBRARY_INDEX: Command & { label: string } = {
185+
id: 'arduino-update-library-index',
186+
label: nls.localize(
187+
'arduino/updateIndexes/updateLibraryIndex',
188+
'Update Library Index'
189+
),
190+
category: 'Arduino',
191+
};
192+
}
193+
}

arduino-ide-extension/src/browser/notification-center.ts

+31-34
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory';
88
import { DisposableCollection } from '@theia/core/lib/common/disposable';
99
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
1010
import {
11+
IndexUpdateDidCompleteParams,
12+
IndexUpdateDidFailParams,
13+
IndexUpdateWillStartParams,
1114
NotificationServiceClient,
1215
NotificationServiceServer,
1316
} from '../common/protocol/notification-service';
@@ -29,48 +32,48 @@ export class NotificationCenter
2932
implements NotificationServiceClient, FrontendApplicationContribution
3033
{
3134
@inject(NotificationServiceServer)
32-
protected readonly server: JsonRpcProxy<NotificationServiceServer>;
35+
private readonly server: JsonRpcProxy<NotificationServiceServer>;
3336

3437
@inject(FrontendApplicationStateService)
3538
private readonly appStateService: FrontendApplicationStateService;
3639

37-
protected readonly indexDidUpdateEmitter = new Emitter<string>();
38-
protected readonly indexWillUpdateEmitter = new Emitter<string>();
39-
protected readonly indexUpdateDidProgressEmitter =
40+
private readonly indexUpdateDidCompleteEmitter =
41+
new Emitter<IndexUpdateDidCompleteParams>();
42+
private readonly indexUpdateWillStartEmitter =
43+
new Emitter<IndexUpdateWillStartParams>();
44+
private readonly indexUpdateDidProgressEmitter =
4045
new Emitter<ProgressMessage>();
41-
protected readonly indexUpdateDidFailEmitter = new Emitter<{
42-
progressId: string;
43-
message: string;
44-
}>();
45-
protected readonly daemonDidStartEmitter = new Emitter<string>();
46-
protected readonly daemonDidStopEmitter = new Emitter<void>();
47-
protected readonly configDidChangeEmitter = new Emitter<{
46+
private readonly indexUpdateDidFailEmitter =
47+
new Emitter<IndexUpdateDidFailParams>();
48+
private readonly daemonDidStartEmitter = new Emitter<string>();
49+
private readonly daemonDidStopEmitter = new Emitter<void>();
50+
private readonly configDidChangeEmitter = new Emitter<{
4851
config: Config | undefined;
4952
}>();
50-
protected readonly platformDidInstallEmitter = new Emitter<{
53+
private readonly platformDidInstallEmitter = new Emitter<{
5154
item: BoardsPackage;
5255
}>();
53-
protected readonly platformDidUninstallEmitter = new Emitter<{
56+
private readonly platformDidUninstallEmitter = new Emitter<{
5457
item: BoardsPackage;
5558
}>();
56-
protected readonly libraryDidInstallEmitter = new Emitter<{
59+
private readonly libraryDidInstallEmitter = new Emitter<{
5760
item: LibraryPackage;
5861
}>();
59-
protected readonly libraryDidUninstallEmitter = new Emitter<{
62+
private readonly libraryDidUninstallEmitter = new Emitter<{
6063
item: LibraryPackage;
6164
}>();
62-
protected readonly attachedBoardsDidChangeEmitter =
65+
private readonly attachedBoardsDidChangeEmitter =
6366
new Emitter<AttachedBoardsChangeEvent>();
64-
protected readonly recentSketchesChangedEmitter = new Emitter<{
67+
private readonly recentSketchesChangedEmitter = new Emitter<{
6568
sketches: Sketch[];
6669
}>();
6770
private readonly onAppStateDidChangeEmitter =
6871
new Emitter<FrontendApplicationState>();
6972

70-
protected readonly toDispose = new DisposableCollection(
71-
this.indexWillUpdateEmitter,
73+
private readonly toDispose = new DisposableCollection(
74+
this.indexUpdateWillStartEmitter,
7275
this.indexUpdateDidProgressEmitter,
73-
this.indexDidUpdateEmitter,
76+
this.indexUpdateDidCompleteEmitter,
7477
this.indexUpdateDidFailEmitter,
7578
this.daemonDidStartEmitter,
7679
this.daemonDidStopEmitter,
@@ -82,8 +85,8 @@ export class NotificationCenter
8285
this.attachedBoardsDidChangeEmitter
8386
);
8487

85-
readonly onIndexDidUpdate = this.indexDidUpdateEmitter.event;
86-
readonly onIndexWillUpdate = this.indexDidUpdateEmitter.event;
88+
readonly onIndexUpdateDidComplete = this.indexUpdateDidCompleteEmitter.event;
89+
readonly onIndexUpdateWillStart = this.indexUpdateWillStartEmitter.event;
8790
readonly onIndexUpdateDidProgress = this.indexUpdateDidProgressEmitter.event;
8891
readonly onIndexUpdateDidFail = this.indexUpdateDidFailEmitter.event;
8992
readonly onDaemonDidStart = this.daemonDidStartEmitter.event;
@@ -112,26 +115,20 @@ export class NotificationCenter
112115
this.toDispose.dispose();
113116
}
114117

115-
notifyIndexWillUpdate(progressId: string): void {
116-
this.indexWillUpdateEmitter.fire(progressId);
118+
notifyIndexUpdateWillStart(params: IndexUpdateWillStartParams): void {
119+
this.indexUpdateWillStartEmitter.fire(params);
117120
}
118121

119122
notifyIndexUpdateDidProgress(progressMessage: ProgressMessage): void {
120123
this.indexUpdateDidProgressEmitter.fire(progressMessage);
121124
}
122125

123-
notifyIndexDidUpdate(progressId: string): void {
124-
this.indexDidUpdateEmitter.fire(progressId);
126+
notifyIndexUpdateDidComplete(params: IndexUpdateDidCompleteParams): void {
127+
this.indexUpdateDidCompleteEmitter.fire(params);
125128
}
126129

127-
notifyIndexUpdateDidFail({
128-
progressId,
129-
message,
130-
}: {
131-
progressId: string;
132-
message: string;
133-
}): void {
134-
this.indexUpdateDidFailEmitter.fire({ progressId, message });
130+
notifyIndexUpdateDidFail(params: IndexUpdateDidFailParams): void {
131+
this.indexUpdateDidFailEmitter.fire(params);
135132
}
136133

137134
notifyDaemonDidStart(port: string): void {

arduino-ide-extension/src/browser/widgets/component-list/list-widget.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export abstract class ListWidget<
6868
@postConstruct()
6969
protected init(): void {
7070
this.toDispose.pushAll([
71-
this.notificationCenter.onIndexDidUpdate(() => this.refresh(undefined)),
71+
this.notificationCenter.onIndexUpdateDidComplete(() => this.refresh(undefined)),
7272
this.notificationCenter.onDaemonDidStart(() => this.refresh(undefined)),
7373
this.notificationCenter.onDaemonDidStop(() => this.refresh(undefined)),
7474
]);

0 commit comments

Comments
 (0)