mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-10 12:56:32 +00:00
[ci]: Made various changes for the electron app:
- Support for multiple electron targe per platform. - Removed packager CLI. Changed the logic we calculate the app name. - Fixed various OS-specific tests: stubbed `os`. - Restructured the final ZIP formats for Windows and Linux. - Added packager tests. - Switched from `@grpc/grpc-js` to native `grpc`. - Updated the version from 0.0.5 to 0.0.6. Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
parent
d54a69935e
commit
6ce4143d49
@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "arduino-debugger-extension",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"description": "An extension for debugging Arduino programs",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.10.0"
|
||||
"node": ">=10.11.0 <12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@theia/debug": "next",
|
||||
"arduino-ide-extension": "0.0.5",
|
||||
"arduino-ide-extension": "0.0.6",
|
||||
"cdt-gdb-adapter": "^0.0.14",
|
||||
"vscode-debugadapter": "^1.26.0",
|
||||
"vscode-debugprotocol": "^1.26.0"
|
||||
|
@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "arduino-ide-extension",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"description": "An extension for Theia building the Arduino IDE",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.10.0"
|
||||
"node": ">=10.11.0 <12"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "yarn download-cli && yarn generate-protocol && yarn download-ls && yarn run clean && yarn run build",
|
||||
@ -19,7 +19,6 @@
|
||||
"test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^0.6.18",
|
||||
"@theia/application-package": "next",
|
||||
"@theia/core": "next",
|
||||
"@theia/cpp": "next",
|
||||
@ -37,11 +36,12 @@
|
||||
"@types/dateformat": "^3.0.1",
|
||||
"@types/deepmerge": "^2.2.0",
|
||||
"@types/glob": "^5.0.35",
|
||||
"@types/google-protobuf": "^3.7.1",
|
||||
"@types/google-protobuf": "^3.7.2",
|
||||
"@types/js-yaml": "^3.12.2",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/ps-tree": "^1.1.0",
|
||||
"@types/react-select": "^3.0.0",
|
||||
"@types/sinon": "^7.5.2",
|
||||
"@types/which": "^1.3.1",
|
||||
"ajv": "^6.5.3",
|
||||
"css-element-queries": "^1.2.0",
|
||||
@ -49,7 +49,8 @@
|
||||
"deepmerge": "^4.2.2",
|
||||
"fuzzy": "^0.1.3",
|
||||
"glob": "^7.1.6",
|
||||
"google-protobuf": "^3.11.0",
|
||||
"google-protobuf": "^3.11.4",
|
||||
"grpc": "^1.24.2",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"js-yaml": "^3.13.1",
|
||||
"p-queue": "^5.0.0",
|
||||
@ -79,6 +80,7 @@
|
||||
"ncp": "^2.0.0",
|
||||
"protoc": "1.0.4",
|
||||
"shelljs": "^0.8.3",
|
||||
"sinon": "^9.0.1",
|
||||
"temp": "^0.9.1",
|
||||
"uuid": "^3.2.1",
|
||||
"yargs": "^11.1.0"
|
||||
|
@ -86,8 +86,6 @@ ${protos.join(' ')}`).code !== 0) {
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const { patch } = require('./patch-grpc-js');
|
||||
patch([out])
|
||||
shell.echo('Done.');
|
||||
|
||||
})();
|
||||
|
@ -1,38 +0,0 @@
|
||||
// Use `@grpc/grpc-js` instead of `grpc` at runtime.
|
||||
// https://github.com/grpc/grpc-node/issues/624
|
||||
// https://github.com/grpc/grpc-node/issues/931
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports.patch = function (roots = [path.join(__dirname, '..', 'src', 'node')]) {
|
||||
console.info('🔧 <<< Patching code...');
|
||||
patch(roots);
|
||||
console.info('👌 <<< Done. The code has been patched.');
|
||||
};
|
||||
|
||||
function patch(paths) {
|
||||
for (const p of paths) {
|
||||
const exist = fs.existsSync(p);
|
||||
if (exist) {
|
||||
const stat = fs.statSync(p);
|
||||
if (stat.isDirectory()) {
|
||||
console.info(`🔧 >>> Scanning code in ${p}...`);
|
||||
patch(fs.readdirSync(p).map(name => path.join(p, name)));
|
||||
} else {
|
||||
let content = fs.readFileSync(p, { encoding: 'utf8' });
|
||||
if (content.indexOf("require('grpc')") !== -1) {
|
||||
console.info(`Updated require('grpc') to require('@grpc/grpc-js') in ${p}.`);
|
||||
fs.writeFileSync(p, content.replace("require('grpc')", "require('@grpc/grpc-js')"));
|
||||
}
|
||||
content = fs.readFileSync(p, { encoding: 'utf8' });
|
||||
if (content.indexOf('import * as grpc from "grpc"') !== -1) {
|
||||
console.info(`Updated import * as grpc from "grpc" to import * as grpc from "@grpc/grpc-js" in ${p}.`);
|
||||
fs.writeFileSync(p, content.replace('import * as grpc from "grpc"', 'import * as grpc from "@grpc/grpc-js"'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn(`${p} does not exist. Skipping.`);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import * as path from 'path';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import * as grpc from 'grpc';
|
||||
import * as deepmerge from 'deepmerge';
|
||||
import { injectable, inject, named } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import * as grpc from 'grpc';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { ToolOutputServiceServer } from '../common/protocol';
|
||||
import { GrpcClientProvider } from './grpc-client-provider';
|
||||
@ -35,7 +35,7 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
}
|
||||
|
||||
protected async createClient(port: string | number): Promise<CoreClientProvider.Client> {
|
||||
const client = new ArduinoCoreClient(`localhost:${port}`, grpc.credentials.createInsecure());
|
||||
const client = new ArduinoCoreClient(`localhost:${port}`, grpc.credentials.createInsecure(), this.channelOptions);
|
||||
const initReq = new InitReq();
|
||||
initReq.setLibraryManagerOnly(false);
|
||||
const initResp = await new Promise<InitResp>(resolve => {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import * as grpc from 'grpc';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
@ -68,4 +69,11 @@ export abstract class GrpcClientProvider<C> {
|
||||
|
||||
protected abstract close(client: C): void;
|
||||
|
||||
protected get channelOptions(): grpc.CallOptions {
|
||||
return {
|
||||
'grpc.max_send_message_length': 512 * 1024 * 1024,
|
||||
'grpc.max_receive_message_length': 512 * 1024 * 1024
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import * as grpc from 'grpc';
|
||||
import { injectable } from 'inversify';
|
||||
import { MonitorClient } from '../cli-protocol/monitor/monitor_grpc_pb';
|
||||
import { GrpcClientProvider } from '../grpc-client-provider';
|
||||
@ -7,7 +7,7 @@ import { GrpcClientProvider } from '../grpc-client-provider';
|
||||
export class MonitorClientProvider extends GrpcClientProvider<MonitorClient> {
|
||||
|
||||
createClient(port: string | number): MonitorClient {
|
||||
return new MonitorClient(`localhost:${port}`, grpc.credentials.createInsecure());
|
||||
return new MonitorClient(`localhost:${port}`, grpc.credentials.createInsecure(), this.channelOptions);
|
||||
}
|
||||
|
||||
close(client: MonitorClient): void {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ClientDuplexStream } from '@grpc/grpc-js';
|
||||
import { ClientDuplexStream } from 'grpc';
|
||||
import { TextDecoder, TextEncoder } from 'util';
|
||||
import { injectable, inject, named } from 'inversify';
|
||||
import { Struct } from 'google-protobuf/google/protobuf/struct_pb';
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import * as os from '@theia/core/lib/common/os';
|
||||
import { Container, injectable } from 'inversify';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
@ -26,18 +28,23 @@ describe('boards-service-client-impl', () => {
|
||||
const guessed = AvailableBoard.State.guessed;
|
||||
const incomplete = AvailableBoard.State.incomplete;
|
||||
|
||||
let stub: sinon.SinonStub;
|
||||
|
||||
let server: MockBoardsService;
|
||||
let client: BoardsServiceClientImpl;
|
||||
// let storage: MockStorageService;
|
||||
|
||||
beforeEach(() => {
|
||||
stub = sinon.stub(os, 'isOSX').value(true);
|
||||
const container = init();
|
||||
server = container.get(MockBoardsService);
|
||||
client = container.get(BoardsServiceClientImpl);
|
||||
// storage = container.get(MockStorageService);
|
||||
server.setClient(client);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
stub.reset();
|
||||
});
|
||||
|
||||
it('should have no available boards by default', () => {
|
||||
expect(client.availableBoards).to.have.length(0);
|
||||
});
|
||||
|
@ -45,16 +45,9 @@ jobs:
|
||||
RELEASE_TAG: $(Release.Tag)
|
||||
condition: or(in(variables['Agent.OS'], 'Windows_NT'), in(variables['Build.Reason'], 'Manual', 'Schedule'))
|
||||
displayName: Package
|
||||
- bash: |
|
||||
export ARDUINO_POC_NAME=$(./electron/packager/cli name)
|
||||
echo "##vso[task.setvariable variable=ArduinoPoC.AppName]$ARDUINO_POC_NAME"
|
||||
env:
|
||||
RELEASE_TAG: $(Release.Tag)
|
||||
condition: or(in(variables['Agent.OS'], 'Windows_NT'), in(variables['Build.Reason'], 'Manual', 'Schedule'))
|
||||
displayName: '[Config] Use - ARDUINO_POC_NAME env'
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
pathtoPublish: electron/build/dist/$(ArduinoPoC.AppName)
|
||||
pathtoPublish: electron/build/dist/build-artifacts
|
||||
artifactName: 'Arduino Pro IDE - Applications'
|
||||
condition: or(in(variables['Agent.OS'], 'Windows_NT'), in(variables['Build.Reason'], 'Manual', 'Schedule'))
|
||||
displayName: Publish
|
||||
@ -77,7 +70,7 @@ jobs:
|
||||
assets: |
|
||||
gh-release/Arduino Pro IDE - Applications/*.zip
|
||||
gh-release/Arduino Pro IDE - Applications/*.dmg
|
||||
gh-release/Arduino Pro IDE - Applications/*.tar.xz
|
||||
gh-release/Arduino Pro IDE - Applications/*.AppImage
|
||||
target: $(Build.SourceVersion)
|
||||
action: Edit
|
||||
tagSource: auto
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "browser-app",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@theia/core": "next",
|
||||
@ -20,8 +20,8 @@
|
||||
"@theia/process": "next",
|
||||
"@theia/terminal": "next",
|
||||
"@theia/workspace": "next",
|
||||
"arduino-ide-extension": "0.0.5",
|
||||
"arduino-debugger-extension": "0.0.5"
|
||||
"arduino-ide-extension": "0.0.6",
|
||||
"arduino-debugger-extension": "0.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@theia/cli": "next"
|
||||
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "electron-app",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"license": "MIT",
|
||||
"main": "src-gen/frontend/electron-main.js",
|
||||
"dependencies": {
|
||||
"@theia/core": "next",
|
||||
"@theia/cpp": "next",
|
||||
@ -21,8 +22,8 @@
|
||||
"@theia/process": "next",
|
||||
"@theia/terminal": "next",
|
||||
"@theia/workspace": "next",
|
||||
"arduino-ide-extension": "0.0.5",
|
||||
"arduino-debugger-extension": "0.0.5"
|
||||
"arduino-ide-extension": "0.0.6",
|
||||
"arduino-debugger-extension": "0.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@theia/cli": "next"
|
||||
|
BIN
electron/build/resources/icons/512x512.png
Normal file
BIN
electron/build/resources/icons/512x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
@ -1,6 +1,9 @@
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
// To be able to propagate the `process.versions.electron` to the backend main, so that we can load natives correctly.
|
||||
process.env.THEIA_ELECTRON_VERSION = process.versions.electron;
|
||||
|
||||
process.env.THEIA_DEFAULT_PLUGINS = `local-dir:${path.resolve(__dirname, '..', 'plugins')}`;
|
||||
process.env.THEIA_PLUGINS = [
|
||||
process.env.THEIA_PLUGINS,
|
||||
|
57
electron/build/scripts/patch-backend-main.js
Normal file
57
electron/build/scripts/patch-backend-main.js
Normal file
@ -0,0 +1,57 @@
|
||||
//@ts-check
|
||||
// Patches the `src-gen/backend/main.js` so that the forked backend process has the `process.versions.electron` in the bundled electron app.
|
||||
// https://github.com/eclipse-theia/theia/issues/7358#issue-583306096
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
if (!args.length) {
|
||||
console.error(`Expected an argument pointing to the app folder. An app folder is where you have the package.json and src-gen folder.`);
|
||||
process.exit(1);
|
||||
}
|
||||
if (args.length > 1) {
|
||||
console.error(`Expected exactly one argument pointing to the app folder. Got multiple instead: ${JSON.stringify(args)}`);
|
||||
process.exit(1);
|
||||
}
|
||||
const arg = args.shift();
|
||||
if (!arg) {
|
||||
console.error('App path was not specified.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const appPath = path.resolve((path.isAbsolute(arg) ? path.join(process.cwd(), arg) : arg));
|
||||
if (!fs.existsSync(appPath)) {
|
||||
console.error(`${appPath} does not exist.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.lstatSync(appPath).isDirectory()) {
|
||||
console.error(`${appPath} is not a directory.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const patched = path.join(appPath, 'src-gen', 'backend', 'original-main.js');
|
||||
if (fs.existsSync(patched)) {
|
||||
console.error(`Already patched. ${patched} already exists.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const toPatch = path.join(appPath, 'src-gen', 'backend', 'main.js');
|
||||
if (fs.existsSync(patched)) {
|
||||
console.error(`Cannot patch. ${toPatch} does not exist.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`⏱️ >>> Patching ${toPatch}...`);
|
||||
|
||||
const originalContent = fs.readFileSync(toPatch, { encoding: 'utf8' });
|
||||
const patchedContent = `if (typeof process.versions.electron === 'undefined' && typeof process.env.THEIA_ELECTRON_VERSION === 'string') {
|
||||
process.versions.electron = process.env.THEIA_ELECTRON_VERSION;
|
||||
}
|
||||
require('./original-main');
|
||||
`
|
||||
|
||||
fs.writeFileSync(patched, originalContent);
|
||||
fs.writeFileSync(toPatch, patchedContent);
|
||||
|
||||
console.log(`👌 <<< Patched ${toPatch}. Original 'main.js' is now at ${patched}.`);
|
@ -12,17 +12,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@theia/cli": "next",
|
||||
"electron-builder": "^21.2.0"
|
||||
"electron-builder": "^22.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn download:plugins && theia build --mode development",
|
||||
"build:release": "yarn download:plugins && theia build --mode development",
|
||||
"build": "yarn download:plugins && theia build --mode development && yarn patch:main",
|
||||
"build:release": "yarn download:plugins && theia build --mode production && yarn patch:main",
|
||||
"package": "electron-builder --publish=never",
|
||||
"package:preview": "electron-builder --dir",
|
||||
"download:plugins": "theia download:plugins"
|
||||
"download:plugins": "theia download:plugins",
|
||||
"patch:main": "node ./scripts/patch-backend-main ."
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.10.0"
|
||||
"node": ">=10.11.0 <12"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -80,9 +80,17 @@
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
"zip"
|
||||
{
|
||||
"target": "zip"
|
||||
},
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": "armv7l"
|
||||
}
|
||||
],
|
||||
"artifactName": "${productName}-${env.ARDUINO_VERSION}-${os}.${ext}"
|
||||
"category": "Development",
|
||||
"icon": "resources/icons",
|
||||
"artifactName": "${productName}-${env.ARDUINO_VERSION}-${os}-${arch}.${ext}"
|
||||
},
|
||||
"dmg": {
|
||||
"icon": "resources/icon.icns",
|
||||
|
@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// @ts-check
|
||||
const { versionInfo } = require('./utils');
|
||||
const yargs = require('yargs');
|
||||
|
||||
(() => {
|
||||
yargs
|
||||
.command({
|
||||
command: 'name',
|
||||
describe: 'Returns with the application name we build. The name includes the full application name with the version, the platform and the file extension.',
|
||||
handler: () => {
|
||||
const { platform } = process;
|
||||
let ext = undefined;
|
||||
let os = undefined;
|
||||
if (platform === 'darwin') {
|
||||
ext = 'dmg';
|
||||
os = 'mac';
|
||||
} else if (platform === 'win32') {
|
||||
ext = 'zip';
|
||||
os = 'win';
|
||||
} else if (platform === 'linux') {
|
||||
ext = 'zip';
|
||||
os = 'linux';
|
||||
} else {
|
||||
process.stderr.write(`Unexpected platform: ${platform}.`);
|
||||
process.exit(1);
|
||||
}
|
||||
process.stdout.write(`Arduino Pro IDE-${versionInfo().version}-${os}.${ext}`);
|
||||
process.exit(0);
|
||||
}
|
||||
})
|
||||
.demandCommand(1)
|
||||
.argv;
|
||||
})();
|
@ -5,6 +5,8 @@
|
||||
const fs = require('fs');
|
||||
const join = require('path').join;
|
||||
const shell = require('shelljs');
|
||||
const glob = require('glob');
|
||||
const isCI = require('is-ci');
|
||||
shell.env.THEIA_ELECTRON_SKIP_REPLACE_FFMPEG = '1'; // Do not run the ffmpeg validation for the packager.
|
||||
shell.env.NODE_OPTIONS = '--max_old_space_size=4096'; // Increase heap size for the CI
|
||||
const utils = require('./utils');
|
||||
@ -72,13 +74,13 @@
|
||||
// We have to do it before changing the dependencies to `local-path`.
|
||||
const unusedDependencies = await utils.collectUnusedDependencies('../working-copy/electron-app/');
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------+
|
||||
// Change the regular NPM dependencies to `local-paths`, so that we can build them without any NPM registries. |
|
||||
//-------------------------------------------------------------------------------------------------------------+
|
||||
// @ts-ignore
|
||||
pkg = require('../working-copy/arduino-debugger-extension/package.json');
|
||||
pkg.dependencies['arduino-ide-extension'] = 'file:../arduino-ide-extension';
|
||||
fs.writeFileSync(path('..', workingCopy, 'arduino-debugger-extension', 'package.json'), JSON.stringify(pkg, null, 2));
|
||||
//-------------------------------------------------------------------------------------------------------------+
|
||||
// Change the regular NPM dependencies to `local-paths`, so that we can build them without any NPM registries. |
|
||||
//-------------------------------------------------------------------------------------------------------------+
|
||||
// @ts-ignore
|
||||
pkg = require('../working-copy/arduino-debugger-extension/package.json');
|
||||
pkg.dependencies['arduino-ide-extension'] = 'file:../arduino-ide-extension';
|
||||
fs.writeFileSync(path('..', workingCopy, 'arduino-debugger-extension', 'package.json'), JSON.stringify(pkg, null, 2));
|
||||
|
||||
//------------------------------------------------------------------------------------+
|
||||
// Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
|
||||
@ -138,6 +140,18 @@ ${fs.readFileSync(path('..', 'build', 'package.json')).toString()}
|
||||
// Package the electron application. |
|
||||
//-----------------------------------+
|
||||
exec(`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} package`, `Packaging your Arduino Pro IDE application`);
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------+
|
||||
// Copy to another folder. Azure does not support wildcard for `PublishBuildArtifacts@1.pathToPublish` |
|
||||
//-----------------------------------------------------------------------------------------------------+
|
||||
if (isCI) {
|
||||
try {
|
||||
await copyFilesToBuildArtifacts();
|
||||
} catch (e) {
|
||||
echo(JSON.stringify(e));
|
||||
shell.exit(1);
|
||||
}
|
||||
}
|
||||
echo(`🎉 Success. Your application is at: ${path('..', 'build', 'dist')}`);
|
||||
|
||||
restore();
|
||||
@ -201,6 +215,47 @@ ${fs.readFileSync(path('..', 'build', 'package.json')).toString()}
|
||||
}
|
||||
}
|
||||
|
||||
async function copyFilesToBuildArtifacts() {
|
||||
echo(`🚢 Detected CI, moving build artifacts...`);
|
||||
const { platform } = process;
|
||||
const cwd = path('..', 'build', 'dist');
|
||||
const targetFolder = path('..', 'build', 'dist', 'build-artifacts');
|
||||
mkdir('-p', targetFolder);
|
||||
const filesToCopy = [];
|
||||
switch (platform) {
|
||||
case 'linux': {
|
||||
filesToCopy.push(...glob.sync('**/Arduino Pro IDE*.{zip,AppImage}', { cwd }).map(p => join(cwd, p)));
|
||||
break;
|
||||
}
|
||||
case 'win32': {
|
||||
filesToCopy.push(...glob.sync('**/Arduino Pro IDE*.zip', { cwd }).map(p => join(cwd, p)));
|
||||
break;
|
||||
}
|
||||
case 'darwin': {
|
||||
filesToCopy.push(...glob.sync('**/Arduino Pro IDE*.dmg', { cwd }).map(p => join(cwd, p)));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
echo(`Unsupported platform: ${platform}.`);
|
||||
shell.exit(1);
|
||||
}
|
||||
}
|
||||
if (!filesToCopy.length) {
|
||||
echo(`Could not collect any build artifacts from ${cwd}.`);
|
||||
shell.exit(1);
|
||||
}
|
||||
for (const fileToCopy of filesToCopy) {
|
||||
echo(`🚢 >>> Copying ${fileToCopy} to ${targetFolder}.`);
|
||||
const isZip = await utils.isZip(fileToCopy);
|
||||
if (isZip) {
|
||||
await utils.adjustArchiveStructure(fileToCopy, targetFolder);
|
||||
} else {
|
||||
cp('-rf', fileToCopy, targetFolder);
|
||||
}
|
||||
echo(`👌 >>> Copied ${fileToCopy} to ${targetFolder}.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins tha path from `__dirname`.
|
||||
*/
|
||||
|
@ -5,19 +5,36 @@
|
||||
"description": "Packager for the Arduino Pro IDE electron application",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"prepare": "yarn test",
|
||||
"package": "node index.js",
|
||||
"cli": "./cli"
|
||||
"test": "mocha \"./test/**/*.test.js\""
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Arduino SA",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"deepmerge": "4.2.2",
|
||||
"depcheck": "^0.7.1",
|
||||
"@types/file-type": "^10.9.1",
|
||||
"@types/temp": "^0.8.32",
|
||||
"7zip-min": "^1.1.1",
|
||||
"chai": "^4.2.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"depcheck": "^0.9.2",
|
||||
"file-type": "^14.1.4",
|
||||
"glob": "^7.1.6",
|
||||
"is-ci": "^2.0.0",
|
||||
"mocha": "^7.1.1",
|
||||
"sinon": "^9.0.1",
|
||||
"shelljs": "^0.8.3",
|
||||
"temp": "^0.9.1",
|
||||
"yargs": "^12.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.12.0"
|
||||
"node": ">=10.11.0 <12"
|
||||
},
|
||||
"mocha": {
|
||||
"reporter": "spec",
|
||||
"colors": true,
|
||||
"watch-extensions": "js",
|
||||
"timeout": 10000
|
||||
}
|
||||
}
|
||||
|
BIN
electron/packager/test/resources/not-a-zip.dmg
Normal file
BIN
electron/packager/test/resources/not-a-zip.dmg
Normal file
Binary file not shown.
BIN
electron/packager/test/resources/zip with whitespace.zip
Normal file
BIN
electron/packager/test/resources/zip with whitespace.zip
Normal file
Binary file not shown.
BIN
electron/packager/test/resources/zip-with-base-folder.zip
Normal file
BIN
electron/packager/test/resources/zip-with-base-folder.zip
Normal file
Binary file not shown.
BIN
electron/packager/test/resources/zip-with-symlink.zip
Normal file
BIN
electron/packager/test/resources/zip-with-symlink.zip
Normal file
Binary file not shown.
BIN
electron/packager/test/resources/zip-without-symlink.zip
Normal file
BIN
electron/packager/test/resources/zip-without-symlink.zip
Normal file
Binary file not shown.
119
electron/packager/test/utils.test.js
Normal file
119
electron/packager/test/utils.test.js
Normal file
@ -0,0 +1,119 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const expect = require('chai').expect;
|
||||
const track = require('temp').track();
|
||||
const unpack = require('../utils').unpack;
|
||||
const testMe = require('../utils');
|
||||
const sinon = require('sinon');
|
||||
|
||||
describe('utils', () => {
|
||||
|
||||
describe('adjustArchiveStructure', () => {
|
||||
|
||||
let consoleStub;
|
||||
|
||||
beforeEach(() => {
|
||||
consoleStub = sinon.stub(console, 'log').value(() => { });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
consoleStub.reset();
|
||||
track.cleanupSync();
|
||||
});
|
||||
|
||||
it('should reject when not a zip file', async () => {
|
||||
try {
|
||||
const invalid = path.join(__dirname, 'resources', 'not-a-zip.dmg');
|
||||
await testMe.adjustArchiveStructure(invalid, track.mkdirSync());
|
||||
throw new Error('Expected a rejection');
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceOf(Error);
|
||||
expect(e.message).to.be.equal('Expected a ZIP file.');
|
||||
}
|
||||
});
|
||||
|
||||
it('should reject when target directory does not exist', async () => {
|
||||
try {
|
||||
const zip = path.join(__dirname, 'resources', 'zip-with-base-folder.zip');
|
||||
await testMe.adjustArchiveStructure(zip, path.join(__dirname, 'some', 'missing', 'path'));
|
||||
throw new Error('Expected a rejection');
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceOf(Error);
|
||||
expect(e.message.endsWith('does not exist.')).to.be.true;
|
||||
}
|
||||
});
|
||||
|
||||
it('should reject when target is a file', async () => {
|
||||
try {
|
||||
const zip = path.join(__dirname, 'resources', 'zip-with-base-folder.zip');
|
||||
await testMe.adjustArchiveStructure(zip, path.join(__filename));
|
||||
throw new Error('Expected a rejection');
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceOf(Error);
|
||||
expect(e.message.endsWith('is not a directory.')).to.be.true;
|
||||
}
|
||||
});
|
||||
|
||||
it('should be a NOOP when the zip already has the desired base folder', async () => {
|
||||
const zip = path.join(__dirname, 'resources', 'zip-with-base-folder.zip');
|
||||
const actual = await testMe.adjustArchiveStructure(zip, track.mkdirSync());
|
||||
expect(actual).to.be.equal(zip);
|
||||
});
|
||||
|
||||
it('should handle whitespace in file path gracefully', async () => {
|
||||
const zip = path.join(__dirname, 'resources', 'zip with whitespace.zip');
|
||||
const out = track.mkdirSync();
|
||||
const actual = await testMe.adjustArchiveStructure(zip, out, true);
|
||||
expect(actual).to.be.equal(path.join(out, 'zip with whitespace.zip'));
|
||||
console.log(actual);
|
||||
expect(fs.existsSync(actual)).to.be.true;
|
||||
|
||||
const verifyOut = track.mkdirSync();
|
||||
await unpack(actual, verifyOut);
|
||||
|
||||
const root = path.join(verifyOut, 'zip with whitespace');
|
||||
expect(fs.existsSync(root)).to.be.true;
|
||||
expect(fs.lstatSync(root).isDirectory()).to.be.true;
|
||||
const subs = fs.readdirSync(root);
|
||||
expect(subs).to.have.lengthOf(3);
|
||||
expect(subs.sort()).to.be.deep.equal(['a.txt', 'b.txt', 'foo']);
|
||||
});
|
||||
|
||||
it('should keep the symlinks after ZIP adjustments', async function () {
|
||||
if (process.platform === 'win32') {
|
||||
this.skip();
|
||||
}
|
||||
const zip = path.join(__dirname, 'resources', 'zip-with-symlink.zip');
|
||||
const out = track.mkdirSync();
|
||||
const actual = await testMe.adjustArchiveStructure(zip, out, true);
|
||||
expect(actual).to.be.equal(path.join(out, 'zip-with-symlink.zip'));
|
||||
console.log(actual);
|
||||
expect(fs.existsSync(actual)).to.be.true;
|
||||
|
||||
const verifyOut = track.mkdirSync();
|
||||
await unpack(actual, verifyOut);
|
||||
expect(fs.lstatSync(path.join(verifyOut, 'zip-with-symlink', 'folder', 'symlinked-sub')).isSymbolicLink()).to.be.true;
|
||||
});
|
||||
|
||||
it('should adjust the archive structure if base folder is not present', async () => {
|
||||
const zip = path.join(__dirname, 'resources', 'zip-without-symlink.zip');
|
||||
const out = track.mkdirSync();
|
||||
const actual = await testMe.adjustArchiveStructure(zip, out, true);
|
||||
expect(actual).to.be.equal(path.join(out, 'zip-without-symlink.zip'));
|
||||
console.log(actual);
|
||||
expect(fs.existsSync(actual)).to.be.true;
|
||||
|
||||
const verifyOut = track.mkdirSync();
|
||||
await unpack(actual, verifyOut);
|
||||
|
||||
const root = path.join(verifyOut, 'zip-without-symlink');
|
||||
expect(fs.existsSync(root)).to.be.true;
|
||||
expect(fs.lstatSync(root).isDirectory()).to.be.true;
|
||||
const subs = fs.readdirSync(root);
|
||||
expect(subs).to.have.lengthOf(3);
|
||||
expect(subs.sort()).to.be.deep.equal(['a.txt', 'b.txt', 'foo']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -2,8 +2,11 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const temp = require('temp');
|
||||
const zip = require('7zip-min');
|
||||
const shell = require('shelljs');
|
||||
const depcheck = require('depcheck');
|
||||
const fromFile = require('file-type').fromFile;
|
||||
|
||||
/**
|
||||
* Returns with the version info for the artifact.
|
||||
@ -67,7 +70,7 @@ function currentCommitish() {
|
||||
*/
|
||||
function collectUnusedDependencies(pathToProject = process.cwd()) {
|
||||
const p = path.isAbsolute(pathToProject) ? pathToProject : path.resolve(process.cwd(), pathToProject);
|
||||
console.log(`⏱️ >>> Collecting unused backend dependencies for ${p}.`);
|
||||
console.log(`⏱️ >>> Collecting unused backend dependencies for ${p}...`);
|
||||
return new Promise(resolve => {
|
||||
depcheck(p, {
|
||||
ignoreDirs: [
|
||||
@ -97,4 +100,108 @@ function collectUnusedDependencies(pathToProject = process.cwd()) {
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { versionInfo, collectUnusedDependencies };
|
||||
/**
|
||||
* `pathToZip` is a `path/to/your/app-name.zip`.
|
||||
* If the `pathToZip` archive does not have a root directory with name `app-name`, it creates one, and move the content from the
|
||||
* archive's root to the new root folder. If the archive already has the desired root folder, calling this function is a NOOP.
|
||||
* If `pathToZip` is not a ZIP, rejects. `targetFolderName` is the destination folder not the new archive location.
|
||||
*/
|
||||
function adjustArchiveStructure(pathToZip, targetFolderName, noCleanup) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (!await isZip(pathToZip)) {
|
||||
reject(new Error(`Expected a ZIP file.`));
|
||||
return;
|
||||
}
|
||||
if (!fs.existsSync(targetFolderName)) {
|
||||
reject(new Error(`${targetFolderName} does not exist.`));
|
||||
return;
|
||||
}
|
||||
if (!fs.lstatSync(targetFolderName).isDirectory()) {
|
||||
reject(new Error(`${targetFolderName} is not a directory.`));
|
||||
return;
|
||||
}
|
||||
console.log(`⏱️ >>> Adjusting ZIP structure ${pathToZip}...`);
|
||||
|
||||
const root = basename(pathToZip);
|
||||
const resources = await list(pathToZip);
|
||||
const hasBaseFolder = resources.find(name => name === root);
|
||||
if (hasBaseFolder) {
|
||||
if (resources.filter(name => name.indexOf(path.sep) === -1).length > 1) {
|
||||
console.warn(`${pathToZip} ZIP has the desired root folder ${root}, however the ZIP contains other entries too: ${JSON.stringify(resources)}`);
|
||||
}
|
||||
console.log(`👌 <<< The ZIP already has the desired ${root} folder.`);
|
||||
resolve(pathToZip);
|
||||
return;
|
||||
}
|
||||
|
||||
const track = temp.track();
|
||||
try {
|
||||
const unzipOut = path.join(track.mkdirSync(), root);
|
||||
fs.mkdirSync(unzipOut);
|
||||
await unpack(pathToZip, unzipOut);
|
||||
const adjustedZip = path.join(targetFolderName, path.basename(pathToZip));
|
||||
await pack(unzipOut, adjustedZip);
|
||||
console.log(`👌 <<< Adjusted the ZIP structure. Moved the modified ${basename(pathToZip)} to the ${targetFolderName} folder.`);
|
||||
resolve(adjustedZip);
|
||||
} finally {
|
||||
if (!noCleanup) {
|
||||
track.cleanupSync();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `basename` of `pathToFile` without the file extension.
|
||||
*/
|
||||
function basename(pathToFile) {
|
||||
const name = path.basename(pathToFile);
|
||||
const ext = path.extname(pathToFile);
|
||||
return name.substr(0, name.length - ext.length);
|
||||
}
|
||||
|
||||
function unpack(what, where) {
|
||||
return new Promise((resolve, reject) => {
|
||||
zip.unpack(what, where, error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function pack(what, where) {
|
||||
return new Promise((resolve, reject) => {
|
||||
zip.pack(what, where, error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function list(what) {
|
||||
return new Promise((resolve, reject) => {
|
||||
zip.list(what, (error, result) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result.map(({ name }) => name));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async function isZip(pathToFile) {
|
||||
if (!fs.existsSync(pathToFile)) {
|
||||
throw new Error(`${pathToFile} does not exist`);
|
||||
}
|
||||
const type = await fromFile(pathToFile);
|
||||
return type && type.ext === 'zip';
|
||||
}
|
||||
|
||||
module.exports = { versionInfo, collectUnusedDependencies, adjustArchiveStructure, isZip, unpack };
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "arduino-editor",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"description": "Arduino Pro IDE",
|
||||
"repository": "https://github.com/bcmi-labs/arduino-editor.git",
|
||||
"author": "Arduino SA",
|
||||
@ -16,7 +16,7 @@
|
||||
"scripts": {
|
||||
"prepare": "lerna run prepare && yarn test && yarn download:plugins",
|
||||
"rebuild:browser": "theia rebuild:browser",
|
||||
"rebuild:electron": "theia rebuild:electron",
|
||||
"rebuild:electron": "theia rebuild:electron --modules \"@theia/node-pty\" nsfw native-keymap find-git-repositories grpc",
|
||||
"start": "yarn --cwd ./browser-app start",
|
||||
"watch": "lerna run watch --parallel",
|
||||
"test": "lerna run test",
|
||||
|
Loading…
x
Reference in New Issue
Block a user