Skip to content

Commit f660058

Browse files
Alberto Iannacconesilvanocerzafstasimsujew
authored
Check for IDE update at startup (#797)
* Remove check for updates on startup setting * Remove useless exported function * Update template-package.json used to package IDE * Add function to get channel file during packaging step * Add updates check * move ide updater on backend * configure updater options * add auto update preferences * TMP check updates on start and download * index on check-update-startup: fcb8f6e TMP check updates on start and download * set version to skip on local storage * add IDE setting to toggle update check on start-up * comment out check for updates on startup and auto update settings * Update Theia to 1.22.1 * updated CI * download changelog and show it in IDE updater dialog * remove useless file * remove useless code * add i18n to updater dialog * fix i18n * refactor UpdateInfo typing * add macos zip to artifacts * Simply use `--ignore-engines` * Use correct --ignore-engines * Fix semver#valid call * Use C++17 * updated documentation * add update channel preference * update updater url * updated documentation * Fix the C++ version * Build flag for cpp * add disclaimer with correct node version * Update `electron-builder` * Fix `Electron.Menu` issue * Skip electron rebuild * Rebuild native dependencies beforehand * Use resolutions section * Update template-package.json as well * move ide-updater to electron application * refactor ide-updater service * update yarn.lock * update i18n * Revert "Add gRPC user agent (#834)" This reverts commit 5ab3a74. * fix ide download url * update latest file in CI * fix i18n check Co-authored-by: Silvano Cerza <silvanocerza@gmail.com> Co-authored-by: Francesco Stasi <f.stasi@me.com> Co-authored-by: Mark Sujew <msujew@yahoo.de>
1 parent 9ecff86 commit f660058

30 files changed

+1911
-342
lines changed

Diff for: .github/workflows/build.yml

+15-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ jobs:
6060
if [ $IS_FORK = true ]; then
6161
echo "Skipping the app signing: building from a fork."
6262
else
63+
export BUILD_SUFFIX="linux";
6364
if [ "${{ runner.OS }}" = "macOS" ]; then
65+
export BUILD_SUFFIX="mac";
6466
export CSC_LINK="${{ runner.temp }}/signing_certificate.p12"
6567
# APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from:
6668
# https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate
@@ -69,6 +71,7 @@ jobs:
6971
export CSC_KEY_PASSWORD="${{ secrets.KEYCHAIN_PASSWORD }}"
7072
7173
elif [ "${{ runner.OS }}" = "Windows" ]; then
74+
export BUILD_SUFFIX="";
7275
export CSC_LINK="${{ runner.temp }}/signing_certificate.pfx"
7376
npm config set msvs_version 2017 --global
7477
echo "${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PFX }}" | base64 --decode > "$CSC_LINK"
@@ -78,7 +81,15 @@ jobs:
7881
fi
7982
8083
yarn --cwd ./electron/packager/
81-
yarn --cwd ./electron/packager/ package
84+
yarn --cwd ./electron/packager/ package
85+
86+
export BUILD_PREFIX="stable"
87+
if [ "$IS_NIGHTLY" = true ]; then
88+
export BUILD_PREFIX="nightly"
89+
fi
90+
91+
mv electron/build/dist/latest-$BUILD_SUFFIX.yml electron/build/dist/$BUILD_PREFIX-$BUILD_SUFFIX.yml
92+
rm electron/build/dist/alpha* electron/build/dist/beta*
8293
8394
- name: Upload [GitHub Actions]
8495
uses: actions/upload-artifact@v2
@@ -98,7 +109,9 @@ jobs:
98109
- path: '*Linux_64bit.zip'
99110
name: Linux_X86-64
100111
- path: '*macOS_64bit.dmg'
101-
name: macOS
112+
name: macOS_dmg
113+
- path: '*macOS_64bit.zip'
114+
name: macOS_zip
102115
- path: '*Windows_64bit.exe'
103116
name: Windows_X86-64_interactive_installer
104117
- path: '*Windows_64bit.msi'

Diff for: arduino-ide-extension/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"css-element-queries": "^1.2.0",
6666
"dateformat": "^3.0.3",
6767
"deepmerge": "2.0.1",
68+
"electron-updater": "^4.6.5",
6869
"fuzzy": "^0.1.3",
6970
"glob": "^7.1.6",
7071
"google-protobuf": "^3.11.4",
@@ -82,6 +83,7 @@
8283
"ps-tree": "^1.2.0",
8384
"query-string": "^7.0.1",
8485
"react-disable": "^0.1.0",
86+
"react-markdown": "^8.0.0",
8587
"react-select": "^3.0.4",
8688
"react-tabs": "^3.1.2",
8789
"react-window": "^1.8.6",

Diff for: arduino-ide-extension/scripts/compose-changelog.js

+39-28
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,61 @@
11
// @ts-check
22

3-
43
(async () => {
54
const { Octokit } = require('@octokit/rest');
6-
const fs = require("fs");
7-
const path = require("path");
5+
const fs = require('fs');
6+
const path = require('path');
87

98
const octokit = new Octokit({
109
userAgent: 'Arduino IDE compose-changelog.js',
1110
});
1211

13-
const response = await octokit.rest.repos.listReleases({
14-
owner: 'arduino',
15-
repo: 'arduino-ide',
16-
}).catch(err => {
17-
console.error(err);
18-
process.exit(1);
19-
})
12+
const response = await octokit.rest.repos
13+
.listReleases({
14+
owner: 'arduino',
15+
repo: 'arduino-ide',
16+
})
17+
.catch((err) => {
18+
console.error(err);
19+
process.exit(1);
20+
});
2021

2122
const releases = response.data;
2223

23-
let fullChangelog = releases.reduce((acc, item) => {
24+
let fullChangelog = releases.reduce((acc, item, index) => {
2425
// Process each line separately
25-
const body = item.body.split('\n').map(processLine).join('\n')
26+
const body = item.body.split('\n').map(processLine).join('\n');
2627
// item.name is the name of the release changelog
27-
return acc + `# ${item.name}\n\n${body}\n\n---\n\n`;
28+
return (
29+
acc +
30+
`## ${item.name}\n\n${body}${
31+
index !== releases.length - 1 ? '\n\n---\n\n' : '\n'
32+
}`
33+
);
2834
}, '');
2935

30-
const args = process.argv.slice(2)
36+
const args = process.argv.slice(2);
3137
if (args.length == 0) {
32-
console.error("Missing argument to destination file")
33-
process.exit(1)
38+
console.error('Missing argument to destination file');
39+
process.exit(1);
3440
}
3541
const changelogFile = path.resolve(args[0]);
3642

3743
await fs.writeFile(
3844
changelogFile,
3945
fullChangelog,
4046
{
41-
flag: "w+",
47+
flag: 'w+',
4248
},
43-
err => {
49+
(err) => {
4450
if (err) {
4551
console.error(err);
4652
process.exit(1);
4753
}
48-
console.log("Changelog written to", changelogFile);
54+
console.log('Changelog written to', changelogFile);
4955
}
50-
)
56+
);
5157
})();
5258

53-
5459
// processLine applies different substitutions to line string.
5560
// We're assuming that there are no more than one substitution
5661
// per line to be applied.
@@ -61,7 +66,8 @@ const processLine = (line) => {
6166
// * [#123](https://github.com/arduino/arduino-ide/pull/123/)
6267
// * [#123](https://github.com/arduino/arduino-ide/issues/123/)
6368
// If it does return the line as is.
64-
let r = /(\(|\[)#\d+(\)|\])(\(|\[)https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?(\)|\])/gm;
69+
let r =
70+
/(\(|\[)#\d+(\)|\])(\(|\[)https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?(\)|\])/gm;
6571
if (r.test(line)) {
6672
return line;
6773
}
@@ -70,9 +76,12 @@ const processLine = (line) => {
7076
// * #123
7177
// If it does it's changed to:
7278
// * [#123](https://github.com/arduino/arduino-ide/pull/123)
73-
r = /#(\d+)/gm;
79+
r = /(?<![\w\d\/_]{1})#((\d)+)(?![\w\d\/_]{1})/gm;
7480
if (r.test(line)) {
75-
return line.replace(r, `[#$1](https://github.com/arduino/arduino-ide/pull/$1)`)
81+
return line.replace(
82+
r,
83+
`[#$1](https://github.com/arduino/arduino-ide/pull/$1)`
84+
);
7685
}
7786

7887
// Check if a link with one of the following format exists:
@@ -85,7 +94,8 @@ const processLine = (line) => {
8594
// * [#123](https://github.com/arduino/arduino-ide/issues/123)
8695
// * [#123](https://github.com/arduino/arduino-ide/pull/123/)
8796
// * [#123](https://github.com/arduino/arduino-ide/issues/123/)
88-
r = /(https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?)/gm;
97+
r =
98+
/(https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?)/gm;
8999
if (r.test(line)) {
90100
return line.replace(r, `[#$3]($1)`);
91101
}
@@ -95,11 +105,12 @@ const processLine = (line) => {
95105
// * https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3/
96106
// If it does it's changed to:
97107
// * [`2.0.0-rc2...2.0.0-rc3`](https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3)
98-
r = /(https:\/\/github\.com\/arduino\/arduino-ide\/compare\/([^\/]*))\/?\s?/gm;
108+
r =
109+
/(https:\/\/github\.com\/arduino\/arduino-ide\/compare\/([^\/]*))\/?\s?/gm;
99110
if (r.test(line)) {
100-
return line.replace(r, '[`$2`]($1)');;
111+
return line.replace(r, '[`$2`]($1)');
101112
}
102113

103114
// If nothing matches just return the line as is
104115
return line;
105-
}
116+
};

Diff for: arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

+28-3
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,12 @@ import { ArduinoPreferences } from './arduino-preferences';
6868
import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl';
6969
import { SaveAsSketch } from './contributions/save-as-sketch';
7070
import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-widget-contribution';
71+
import { IDEUpdaterCommands } from './ide-updater/ide-updater-commands';
72+
import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog';
73+
import { IDEUpdater } from '../common/protocol/ide-updater';
7174

7275
const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
76+
export const SKIP_IDE_VERSION = 'skipIDEVersion';
7377

7478
@injectable()
7579
export class ArduinoFrontendContribution
@@ -78,8 +82,7 @@ export class ArduinoFrontendContribution
7882
TabBarToolbarContribution,
7983
CommandContribution,
8084
MenuContribution,
81-
ColorContribution
82-
{
85+
ColorContribution {
8386
@inject(ILogger)
8487
protected logger: ILogger;
8588

@@ -157,6 +160,15 @@ export class ArduinoFrontendContribution
157160
@inject(LocalStorageService)
158161
protected readonly localStorageService: LocalStorageService;
159162

163+
@inject(IDEUpdaterCommands)
164+
protected readonly updater: IDEUpdaterCommands;
165+
166+
@inject(IDEUpdaterDialog)
167+
protected readonly updaterDialog: IDEUpdaterDialog;
168+
169+
@inject(IDEUpdater)
170+
protected readonly updaterService: IDEUpdater;
171+
160172
protected invalidConfigPopup:
161173
| Promise<void | 'No' | 'Yes' | undefined>
162174
| undefined;
@@ -251,7 +263,7 @@ export class ArduinoFrontendContribution
251263
});
252264
}
253265

254-
onStart(app: FrontendApplication): void {
266+
async onStart(app: FrontendApplication): Promise<void> {
255267
// Initialize all `pro-mode` widgets. This is a NOOP if in normal mode.
256268
for (const viewContribution of [
257269
this.fileNavigatorContributions,
@@ -266,6 +278,19 @@ export class ArduinoFrontendContribution
266278
viewContribution.initializeLayout(app);
267279
}
268280
}
281+
282+
this.updaterService.init(
283+
this.arduinoPreferences.get('arduino.ide.updateChannel')
284+
);
285+
this.updater.checkForUpdates().then(async (updateInfo) => {
286+
if (!updateInfo) return;
287+
const versionToSkip = await this.localStorageService.getData<string>(
288+
SKIP_IDE_VERSION
289+
);
290+
if (versionToSkip === updateInfo.version) return;
291+
this.updaterDialog.open(updateInfo);
292+
});
293+
269294
const start = async ({ selectedBoard }: BoardsConfig.Config) => {
270295
if (selectedBoard) {
271296
const { name, fqbn } = selectedBoard;

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+50-12
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,19 @@ import {
262262
UserFieldsDialogWidget,
263263
} from './dialogs/user-fields/user-fields-dialog';
264264
import { nls } from '@theia/core/lib/common';
265+
import { IDEUpdaterCommands } from './ide-updater/ide-updater-commands';
266+
import {
267+
IDEUpdater,
268+
IDEUpdaterClient,
269+
IDEUpdaterPath,
270+
} from '../common/protocol/ide-updater';
271+
import { IDEUpdaterClientImpl } from './ide-updater/ide-updater-client-impl';
272+
import {
273+
IDEUpdaterDialog,
274+
IDEUpdaterDialogProps,
275+
IDEUpdaterDialogWidget,
276+
} from './dialogs/ide-updater/ide-updater-dialog';
277+
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
265278

266279
const ElementQueries = require('css-element-queries/src/ElementQueries');
267280

@@ -407,8 +420,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
407420
bind(SerialService)
408421
.toDynamicValue((context) => {
409422
const connection = context.container.get(WebSocketConnectionProvider);
410-
const client =
411-
context.container.get<SerialServiceClient>(SerialServiceClient);
423+
const client = context.container.get<SerialServiceClient>(
424+
SerialServiceClient
425+
);
412426
return connection.createProxy(SerialServicePath, client);
413427
})
414428
.inSingletonScope();
@@ -472,12 +486,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
472486
.inSingletonScope();
473487
rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope();
474488
rebind(TabBarToolbarFactory).toFactory(
475-
({ container: parentContainer }) =>
476-
() => {
477-
const container = parentContainer.createChild();
478-
container.bind(TabBarToolbar).toSelf().inSingletonScope();
479-
return container.get(TabBarToolbar);
480-
}
489+
({ container: parentContainer }) => () => {
490+
const container = parentContainer.createChild();
491+
container.bind(TabBarToolbar).toSelf().inSingletonScope();
492+
return container.get(TabBarToolbar);
493+
}
481494
);
482495
bind(OutputWidget).toSelf().inSingletonScope();
483496
rebind(TheiaOutputWidget).toService(OutputWidget);
@@ -642,13 +655,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
642655

643656
// Enable the dirty indicator on uncloseable widgets.
644657
rebind(TabBarRendererFactory).toFactory((context) => () => {
645-
const contextMenuRenderer =
646-
context.container.get<ContextMenuRenderer>(ContextMenuRenderer);
658+
const contextMenuRenderer = context.container.get<ContextMenuRenderer>(
659+
ContextMenuRenderer
660+
);
647661
const decoratorService = context.container.get<TabBarDecoratorService>(
648662
TabBarDecoratorService
649663
);
650-
const iconThemeService =
651-
context.container.get<IconThemeService>(IconThemeService);
664+
const iconThemeService = context.container.get<IconThemeService>(
665+
IconThemeService
666+
);
652667
return new TabBarRenderer(
653668
contextMenuRenderer,
654669
decoratorService,
@@ -756,9 +771,32 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
756771
title: 'UploadCertificate',
757772
});
758773

774+
bind(IDEUpdaterDialogWidget).toSelf().inSingletonScope();
775+
bind(IDEUpdaterDialog).toSelf().inSingletonScope();
776+
bind(IDEUpdaterDialogProps).toConstantValue({
777+
title: 'IDEUpdater',
778+
});
779+
759780
bind(UserFieldsDialogWidget).toSelf().inSingletonScope();
760781
bind(UserFieldsDialog).toSelf().inSingletonScope();
761782
bind(UserFieldsDialogProps).toConstantValue({
762783
title: 'UserFields',
763784
});
785+
786+
bind(IDEUpdaterCommands).toSelf().inSingletonScope();
787+
bind(CommandContribution).toService(IDEUpdaterCommands);
788+
789+
// Frontend binding for the IDE Updater service
790+
bind(IDEUpdaterClientImpl).toSelf().inSingletonScope();
791+
bind(IDEUpdaterClient).toService(IDEUpdaterClientImpl);
792+
bind(IDEUpdater)
793+
.toDynamicValue((context) => {
794+
const client = context.container.get(IDEUpdaterClientImpl);
795+
return ElectronIpcConnectionProvider.createProxy(
796+
context.container,
797+
IDEUpdaterPath,
798+
client
799+
);
800+
})
801+
.inSingletonScope();
764802
});

0 commit comments

Comments
 (0)