fix generation of updater channel files in CI (#840)

This commit is contained in:
Alberto Iannaccone 2022-02-17 10:29:56 +01:00 committed by GitHub
parent f660058c75
commit 77b430675d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 411 additions and 329 deletions

View File

@ -60,9 +60,7 @@ jobs:
if [ $IS_FORK = true ]; then if [ $IS_FORK = true ]; then
echo "Skipping the app signing: building from a fork." echo "Skipping the app signing: building from a fork."
else else
export BUILD_SUFFIX="linux";
if [ "${{ runner.OS }}" = "macOS" ]; then if [ "${{ runner.OS }}" = "macOS" ]; then
export BUILD_SUFFIX="mac";
export CSC_LINK="${{ runner.temp }}/signing_certificate.p12" export CSC_LINK="${{ runner.temp }}/signing_certificate.p12"
# APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from: # APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from:
# https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate # https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate
@ -71,7 +69,6 @@ jobs:
export CSC_KEY_PASSWORD="${{ secrets.KEYCHAIN_PASSWORD }}" export CSC_KEY_PASSWORD="${{ secrets.KEYCHAIN_PASSWORD }}"
elif [ "${{ runner.OS }}" = "Windows" ]; then elif [ "${{ runner.OS }}" = "Windows" ]; then
export BUILD_SUFFIX="";
export CSC_LINK="${{ runner.temp }}/signing_certificate.pfx" export CSC_LINK="${{ runner.temp }}/signing_certificate.pfx"
npm config set msvs_version 2017 --global npm config set msvs_version 2017 --global
echo "${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PFX }}" | base64 --decode > "$CSC_LINK" echo "${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PFX }}" | base64 --decode > "$CSC_LINK"
@ -81,15 +78,7 @@ jobs:
fi fi
yarn --cwd ./electron/packager/ yarn --cwd ./electron/packager/
yarn --cwd ./electron/packager/ package yarn --cwd ./electron/packager/ package
export BUILD_PREFIX="stable"
if [ "$IS_NIGHTLY" = true ]; then
export BUILD_PREFIX="nightly"
fi
mv electron/build/dist/latest-$BUILD_SUFFIX.yml electron/build/dist/$BUILD_PREFIX-$BUILD_SUFFIX.yml
rm electron/build/dist/alpha* electron/build/dist/beta*
- name: Upload [GitHub Actions] - name: Upload [GitHub Actions]
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2

View File

@ -106,7 +106,7 @@ function getChannel() {
return 'nightly'; return 'nightly';
} }
return 'none'; return '';
} }
function timestamp() { function timestamp() {

View File

@ -1,347 +1,440 @@
//@ts-check //@ts-check
(async () => { (async () => {
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
shell.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = 'true'; // Skip download and avoid `ERROR: Failed to download Chromium`.
const template = require('./config').generateTemplate(
new Date().toISOString()
);
const utils = require('./utils');
const merge = require('deepmerge');
const { isRelease, isElectronPublish, getChannelFile } = utils;
const { version } = template;
const { productName } = template.build;
const fs = require('fs'); echo(`📦 Building ${isRelease ? 'release ' : ''}version '${version}'...`);
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
shell.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = 'true'; // Skip download and avoid `ERROR: Failed to download Chromium`.
const template = require('./config').generateTemplate(new Date().toISOString());
const utils = require('./utils');
const merge = require('deepmerge');
const { isRelease, isElectronPublish, getChannelFile } = utils;
const { version } = template;
const { productName } = template.build;
echo(`📦 Building ${isRelease ? 'release ' : ''}version '${version}'...`); const workingCopy = 'working-copy';
const workingCopy = 'working-copy'; /**
* Relative path from the `__dirname` to the root where the `arduino-ide-extension` and the `electron-app` folders are.
* This could come handy when moving the location of the `electron/packager`.
*/
const rootPath = join('..', '..');
/** // This is a HACK! We rename the root `node_modules` to something else. Otherwise, due to the hoisting,
* Relative path from the `__dirname` to the root where the `arduino-ide-extension` and the `electron-app` folders are. // multiple Theia extensions will be picked up.
* This could come handy when moving the location of the `electron/packager`. if (fs.existsSync(path(rootPath, 'node_modules'))) {
*/ // We either do this or change the project structure.
const rootPath = join('..', '..'); echo(
"🔧 >>> [Hack] Renaming the root 'node_modules' folder to '.node_modules'..."
);
mv('-f', path(rootPath, 'node_modules'), path(rootPath, '.node_modules'));
echo(
"👌 <<< [Hack] Renamed the root 'node_modules' folder to '.node_modules'."
);
}
// This is a HACK! We rename the root `node_modules` to something else. Otherwise, due to the hoisting, //---------------------------+
// multiple Theia extensions will be picked up. // Clean the previous state. |
if (fs.existsSync(path(rootPath, 'node_modules'))) { //---------------------------+
// We either do this or change the project structure. // rm -rf ../working-copy
echo('🔧 >>> [Hack] Renaming the root \'node_modules\' folder to \'.node_modules\'...'); rm('-rf', path('..', workingCopy));
mv('-f', path(rootPath, 'node_modules'), path(rootPath, '.node_modules')); // Clean up the `./electron/build` folder.
echo('👌 <<< [Hack] Renamed the root \'node_modules\' folder to \'.node_modules\'.') const resourcesToKeep = [
} 'patch',
'resources',
'scripts',
'template-package.json',
];
fs.readdirSync(path('..', 'build'))
.filter((filename) => resourcesToKeep.indexOf(filename) === -1)
.forEach((filename) => rm('-rf', path('..', 'build', filename)));
//---------------------------+ const extensions = require('./extensions.json');
// Clean the previous state. | echo(
//---------------------------+ `Building the application with the following extensions:\n${extensions
// rm -rf ../working-copy .map((ext) => ` - ${ext}`)
rm('-rf', path('..', workingCopy)); .join(',\n')}`
// Clean up the `./electron/build` folder. );
const resourcesToKeep = ['patch', 'resources', 'scripts', 'template-package.json']; const allDependencies = [...extensions, 'electron-app'];
for (const filename of fs.readdirSync(path('..', 'build')).filter(filename => resourcesToKeep.indexOf(filename) === -1)) {
rm('-rf', path('..', 'build', filename));
}
const extensions = require('./extensions.json'); //----------------------------------------------------------------------------------------------+
echo(`Building the application with the following extensions:\n${extensions.map(ext => ` - ${ext}`).join(',\n')}`); // Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
const allDependencies = [ //----------------------------------------------------------------------------------------------+
...extensions, mkdir('-p', path('..', workingCopy));
'electron-app' for (const name of [
] ...allDependencies,
'yarn.lock',
'package.json',
'lerna.json',
]) {
cp('-rf', path(rootPath, name), path('..', workingCopy));
}
//----------------------------------------------------------------------------------------------+ //----------------------------------------------+
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. | // Sanity check: all versions must be the same. |
//----------------------------------------------------------------------------------------------+ //----------------------------------------------+
mkdir('-p', path('..', workingCopy)); verifyVersions(allDependencies);
for (const name of [...allDependencies, 'yarn.lock', 'package.json', 'lerna.json']) { //----------------------------------------------------------------------+
cp('-rf', path(rootPath, name), path('..', workingCopy)); // Use the nightly patch version if not a release but requires publish. |
} //----------------------------------------------------------------------+
if (!isRelease) {
//----------------------------------------------+ for (const dependency of allDependencies) {
// Sanity check: all versions must be the same. | const pkg = require(`../working-copy/${dependency}/package.json`);
//----------------------------------------------+ pkg.version = version;
verifyVersions(allDependencies); for (const dependency in pkg.dependencies) {
//----------------------------------------------------------------------+ if (allDependencies.indexOf(dependency) !== -1) {
// Use the nightly patch version if not a release but requires publish. | pkg.dependencies[dependency] = version;
//----------------------------------------------------------------------+
if (!isRelease) {
for (const dependency of allDependencies) {
const pkg = require(`../working-copy/${dependency}/package.json`);
pkg.version = version;
for (const dependency in pkg.dependencies) {
if (allDependencies.indexOf(dependency) !== -1) {
pkg.dependencies[dependency] = version;
}
}
fs.writeFileSync(path('..', workingCopy, dependency, 'package.json'), JSON.stringify(pkg, null, 2));
} }
}
fs.writeFileSync(
path('..', workingCopy, dependency, 'package.json'),
JSON.stringify(pkg, null, 2)
);
} }
verifyVersions(allDependencies); }
verifyVersions(allDependencies);
//-------------------------------------------------------------+ //-------------------------------------------------------------+
// Save some time: no need to build the `browser-app` example. | // Save some time: no need to build the `browser-app` example. |
//-------------------------------------------------------------+ //-------------------------------------------------------------+
//@ts-ignore //@ts-ignore
let pkg = require('../working-copy/package.json'); let pkg = require('../working-copy/package.json');
const workspaces = pkg.workspaces; const workspaces = pkg.workspaces;
// We cannot remove the `electron-app`. Otherwise, there is not way to collect the unused dependencies. // We cannot remove the `electron-app`. Otherwise, there is not way to collect the unused dependencies.
const dependenciesToRemove = ['browser-app']; const dependenciesToRemove = ['browser-app'];
for (const dependencyToRemove of dependenciesToRemove) { for (const dependencyToRemove of dependenciesToRemove) {
const index = workspaces.indexOf(dependencyToRemove); const index = workspaces.indexOf(dependencyToRemove);
if (index !== -1) { if (index !== -1) {
workspaces.splice(index, 1); workspaces.splice(index, 1);
}
} }
pkg.workspaces = workspaces; }
fs.writeFileSync(path('..', workingCopy, 'package.json'), JSON.stringify(pkg, null, 2)); pkg.workspaces = workspaces;
fs.writeFileSync(
path('..', workingCopy, 'package.json'),
JSON.stringify(pkg, null, 2)
);
//-------------------------------------------------------------------------------------------------+ //-------------------------------------------------------------------------------------------------+
// Rebuild the extension with the copied `yarn.lock`. It is a must to use the same Theia versions. | // Rebuild the extension with the copied `yarn.lock`. It is a must to use the same Theia versions. |
//-------------------------------------------------------------------------------------------------+ //-------------------------------------------------------------------------------------------------+
exec(`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)}`, `Building the ${productName} application`); exec(
`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)}`,
`Building the ${productName} application`
);
//-------------------------------------------------------------------------------------------------------------------------+ //-------------------------------------------------------------------------------------------------------------------------+
// Test the application. With this approach, we cannot publish test results to GH Actions but save 6-10 minutes per builds | // Test the application. With this approach, we cannot publish test results to GH Actions but save 6-10 minutes per builds |
//-------------------------------------------------------------------------------------------------------------------------+ //-------------------------------------------------------------------------------------------------------------------------+
exec(`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)} test`, `Testing the ${productName} application`); exec(
`yarn --network-timeout 1000000 --cwd ${path('..', workingCopy)} test`,
`Testing the ${productName} application`
);
// Collect all unused dependencies by the backend. We have to remove them from the electron app. // Collect all unused dependencies by the backend. We have to remove them from the electron app.
// The `bundle.js` already contains everything we need for the frontend. // The `bundle.js` already contains everything we need for the frontend.
// We have to do it before changing the dependencies to `local-path`. // We have to do it before changing the dependencies to `local-path`.
const unusedDependencies = await utils.collectUnusedDependencies('../working-copy/electron-app/'); 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. | // Change the regular NPM dependencies to `local-paths`, so that we can build them without any NPM registries. |
//-------------------------------------------------------------------------------------------------------------+ //-------------------------------------------------------------------------------------------------------------+
for (const extension of extensions) { for (const extension of extensions) {
if (extension !== 'arduino-ide-extension') { // Do not unlink self. if (extension !== 'arduino-ide-extension') {
// @ts-ignore // Do not unlink self.
pkg = require(`../working-copy/${extension}/package.json`); // @ts-ignore
// @ts-ignore pkg = require(`../working-copy/${extension}/package.json`);
pkg.dependencies['arduino-ide-extension'] = 'file:../arduino-ide-extension'; // @ts-ignore
fs.writeFileSync(path('..', workingCopy, extension, 'package.json'), JSON.stringify(pkg, null, 2)); pkg.dependencies['arduino-ide-extension'] =
} 'file:../arduino-ide-extension';
fs.writeFileSync(
path('..', workingCopy, extension, 'package.json'),
JSON.stringify(pkg, null, 2)
);
} }
}
//------------------------------------------------------------------------------------+ //------------------------------------------------------------------------------------+
// Merge the `working-copy/package.json` with `electron/build/template-package.json`. | // Merge the `working-copy/package.json` with `electron/build/template-package.json`. |
//------------------------------------------------------------------------------------+ //------------------------------------------------------------------------------------+
// @ts-ignore
pkg = require('../working-copy/electron-app/package.json');
template.build.files = [
...template.build.files,
...unusedDependencies.map((name) => `!node_modules/${name}`),
];
const dependencies = {};
for (const extension of extensions) {
dependencies[extension] = `file:../working-copy/${extension}`;
}
// @ts-ignore
pkg.dependencies = { ...pkg.dependencies, ...dependencies };
pkg.devDependencies = { ...pkg.devDependencies, ...template.devDependencies };
// Deep-merging the Theia application configuration. We enable the electron window reload in dev mode but not for the final product. (arduino/arduino-pro-ide#187)
// @ts-ignore
const theia = merge(pkg.theia || {}, template.theia || {});
const content = {
...pkg,
...template,
theia,
// @ts-ignore // @ts-ignore
pkg = require('../working-copy/electron-app/package.json'); dependencies: pkg.dependencies,
template.build.files = [...template.build.files, ...unusedDependencies.map(name => `!node_modules/${name}`)]; devDependencies: pkg.devDependencies,
};
const overwriteMerge = (destinationArray, sourceArray, options) =>
sourceArray;
fs.writeFileSync(
path('..', 'build', 'package.json'),
JSON.stringify(
merge(content, template, { arrayMerge: overwriteMerge }),
null,
2
)
);
const dependencies = {}; echo(`📜 Effective 'package.json' for the ${productName} application is:
for (const extension of extensions) {
dependencies[extension] = `file:../working-copy/${extension}`;
}
// @ts-ignore
pkg.dependencies = { ...pkg.dependencies, ...dependencies };
pkg.devDependencies = { ...pkg.devDependencies, ...template.devDependencies };
// Deep-merging the Theia application configuration. We enable the electron window reload in dev mode but not for the final product. (arduino/arduino-pro-ide#187)
// @ts-ignore
const theia = merge((pkg.theia || {}), (template.theia || {}));
const content = {
...pkg,
...template,
theia,
// @ts-ignore
dependencies: pkg.dependencies,
devDependencies: pkg.devDependencies
};
const overwriteMerge = (destinationArray, sourceArray, options) => sourceArray;
fs.writeFileSync(path('..', 'build', 'package.json'), JSON.stringify(merge(content, template, { arrayMerge: overwriteMerge }), null, 2));
echo(`📜 Effective 'package.json' for the ${productName} application is:
----------------------- -----------------------
${fs.readFileSync(path('..', 'build', 'package.json')).toString()} ${fs.readFileSync(path('..', 'build', 'package.json')).toString()}
----------------------- -----------------------
`); `);
// Make sure the original `yarn.lock` file is used from the electron application. // Make sure the original `yarn.lock` file is used from the electron application.
if (fs.existsSync(path('..', 'build', 'yarn.lock'))) { if (fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} must not exist.`); echo(`${path('..', 'build', 'yarn.lock')} must not exist.`);
shell.exit(1);
}
cp('-rf', path(rootPath, 'yarn.lock'), path('..', 'build'));
if (!fs.existsSync(path('..', 'build', 'yarn.lock'))) {
echo(`${path('..', 'build', 'yarn.lock')} does not exist.`);
shell.exit(1);
}
//-------------------------------------------------------------------------------------------+
// Install all private and public dependencies for the electron application and build Theia. |
//-------------------------------------------------------------------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')}`,
'Installing dependencies'
);
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} build${
isElectronPublish ? ':publish' : ''
}`,
`Building the ${productName} application`
);
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} rebuild`,
'Rebuild native dependencies'
);
//------------------------------------------------------------------------------+
// Create a throw away dotenv file which we use to feed the builder with input. |
//------------------------------------------------------------------------------+
const dotenv = 'electron-builder.env';
if (fs.existsSync(path('..', 'build', dotenv))) {
rm('-rf', path('..', 'build', dotenv));
}
// For the releases we use the desired tag as is defined by `$(Release.Tag)` from Azure.
// For the preview builds we use the version from the `electron/build/package.json` with the short commit hash.
fs.writeFileSync(path('..', 'build', dotenv), `ARDUINO_VERSION=${version}`);
//-----------------------------------+
// Package the electron application. |
//-----------------------------------+
exec(
`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} package`,
`Packaging your ${productName} 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();
//--------+
// Utils. |
//--------+
function exec(command, toEcho) {
if (toEcho) {
echo(`⏱️ >>> ${toEcho}...`);
}
const { code, stderr, stdout } = shell.exec(command);
if (code !== 0) {
echo(`🔥 Error when executing ${command} => ${stderr}`);
shell.exit(1);
}
if (toEcho) {
echo(`👌 <<< ${toEcho}.`);
}
return stdout;
}
function cp(options, source, destination) {
shell.cp(options, source, destination);
assertNoError();
}
function rm(options, ...files) {
shell.rm(options, files);
assertNoError();
}
function mv(options, source, destination) {
shell.mv(options, source, destination);
assertNoError();
}
function mkdir(options, ...dir) {
shell.mkdir(options, dir);
assertNoError();
}
function echo(command) {
return shell.echo(command);
}
function assertNoError() {
const error = shell.error();
if (error) {
echo(error);
restore();
shell.exit(1);
}
}
function restore() {
if (fs.existsSync(path(rootPath, '.node_modules'))) {
echo(
"🔧 >>> [Restore] Renaming the root '.node_modules' folder to 'node_modules'..."
);
mv('-f', path(rootPath, '.node_modules'), path(rootPath, 'node_modules'));
echo(
"👌 >>> [Restore] Renamed the root '.node_modules' folder to 'node_modules'."
);
}
}
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 = [];
const channelFile = getChannelFile(platform);
// Channel file might be an empty string if we're not building a
// nightly or a full release. This can happen when building a package
// locally or a tester build when creating a new PR on GH.
if (!!channelFile && fs.existsSync(join(cwd, channelFile))) {
const channelFilePath = join(cwd, channelFile);
const newChannelFilePath = channelFilePath
?.replace('latest', 'stable')
?.replace('beta', 'nightly');
echo(`🔨 >>> Renaming ${channelFilePath} to ${newChannelFilePath}.`);
cp('-f', channelFilePath, newChannelFilePath);
filesToCopy.push(newChannelFilePath);
}
switch (platform) {
case 'linux': {
filesToCopy.push(
...glob
.sync('**/arduino-ide*.{zip,AppImage}', { cwd })
.map((p) => join(cwd, p))
);
break;
}
case 'win32': {
filesToCopy.push(
...glob
.sync('**/arduino-ide*.{exe,msi,zip}', { cwd })
.map((p) => join(cwd, p))
);
break;
}
case 'darwin': {
filesToCopy.push(
...glob
.sync('**/arduino-ide*.{dmg,zip}', { cwd })
.map((p) => join(cwd, p))
);
break;
}
default: {
echo(`Unsupported platform: ${platform}.`);
shell.exit(1); shell.exit(1);
}
} }
cp('-rf', path(rootPath, 'yarn.lock'), path('..', 'build')); if (!filesToCopy.length) {
if (!fs.existsSync(path('..', 'build', 'yarn.lock'))) { echo(`Could not collect any build artifacts from ${cwd}.`);
echo(`${path('..', 'build', 'yarn.lock')} does not exist.`); 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`.
*/
function path(...paths) {
return join(__dirname, ...paths);
}
function verifyVersions(allDependencies, expectedVersion) {
const versions = new Set();
for (const dependency of allDependencies) {
versions.add(
require(`../working-copy/${dependency}/package.json`).version
);
}
if (versions.size !== 1) {
echo(
`Mismatching version configuration. All dependencies must have the same version. Versions were: ${JSON.stringify(
Array.from(versions),
null,
2
)}.`
);
shell.exit(1);
process.exit(1);
}
if (expectedVersion) {
if (!versions.has(expectedVersion)) {
echo(
`Mismatching version configuration. Expected version was: '${expectedVersion}' actual was: '${
Array.from(versions)[0]
}'.`
);
shell.exit(1); shell.exit(1);
process.exit(1);
}
} }
}
//-------------------------------------------------------------------------------------------+
// Install all private and public dependencies for the electron application and build Theia. |
//-------------------------------------------------------------------------------------------+
exec(`yarn --network-timeout 1000000 --cwd ${path('..', 'build')}`, 'Installing dependencies');
exec(`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} build${isElectronPublish ? ':publish' : ''}`, `Building the ${productName} application`);
exec(`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} rebuild`, 'Rebuild native dependencies');
//------------------------------------------------------------------------------+
// Create a throw away dotenv file which we use to feed the builder with input. |
//------------------------------------------------------------------------------+
const dotenv = 'electron-builder.env';
if (fs.existsSync(path('..', 'build', dotenv))) {
rm('-rf', path('..', 'build', dotenv));
}
// For the releases we use the desired tag as is defined by `$(Release.Tag)` from Azure.
// For the preview builds we use the version from the `electron/build/package.json` with the short commit hash.
fs.writeFileSync(path('..', 'build', dotenv), `ARDUINO_VERSION=${version}`);
//-----------------------------------+
// Package the electron application. |
//-----------------------------------+
exec(`yarn --network-timeout 1000000 --cwd ${path('..', 'build')} package`, `Packaging your ${productName} 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();
//--------+
// Utils. |
//--------+
function exec(command, toEcho) {
if (toEcho) {
echo(`⏱️ >>> ${toEcho}...`);
}
const { code, stderr, stdout } = shell.exec(command);
if (code !== 0) {
echo(`🔥 Error when executing ${command} => ${stderr}`);
shell.exit(1);
}
if (toEcho) {
echo(`👌 <<< ${toEcho}.`);
}
return stdout;
}
function cp(options, source, destination) {
shell.cp(options, source, destination);
assertNoError();
}
function rm(options, ...files) {
shell.rm(options, files);
assertNoError();
}
function mv(options, source, destination) {
shell.mv(options, source, destination);
assertNoError();
}
function mkdir(options, ...dir) {
shell.mkdir(options, dir);
assertNoError();
}
function echo(command) {
return shell.echo(command);
}
function assertNoError() {
const error = shell.error();
if (error) {
echo(error);
restore();
shell.exit(1);
}
}
function restore() {
if (fs.existsSync(path(rootPath, '.node_modules'))) {
echo('🔧 >>> [Restore] Renaming the root \'.node_modules\' folder to \'node_modules\'...');
mv('-f', path(rootPath, '.node_modules'), path(rootPath, 'node_modules'));
echo('👌 >>> [Restore] Renamed the root \'.node_modules\' folder to \'node_modules\'.');
}
}
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 = [];
const channelFile = getChannelFile(platform);
// Channel file might be an empty string if we're not building a
// nightly or a full release. This can happen when building a package
// locally or a tester build when creating a new PR on GH.
if (channelFile) {
filesToCopy.push(channelFile)
}
switch (platform) {
case 'linux': {
filesToCopy.push(...glob.sync('**/arduino-ide*.{zip,AppImage}', { cwd }).map(p => join(cwd, p)));
break;
}
case 'win32': {
filesToCopy.push(...glob.sync('**/arduino-ide*.{exe,msi,zip}', { cwd }).map(p => join(cwd, p)));
break;
}
case 'darwin': {
filesToCopy.push(...glob.sync('**/arduino-ide*.{dmg,zip}', { 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`.
*/
function path(...paths) {
return join(__dirname, ...paths);
}
function verifyVersions(allDependencies, expectedVersion) {
const versions = new Set();
for (const dependency of allDependencies) {
versions.add(require(`../working-copy/${dependency}/package.json`).version);
}
if (versions.size !== 1) {
echo(`Mismatching version configuration. All dependencies must have the same version. Versions were: ${JSON.stringify(Array.from(versions), null, 2)}.`);
shell.exit(1);
process.exit(1);
}
if (expectedVersion) {
if (!versions.has(expectedVersion)) {
echo(`Mismatching version configuration. Expected version was: '${expectedVersion}' actual was: '${Array.from(versions)[0]}'.`);
shell.exit(1);
process.exit(1);
}
}
}
})(); })();

View File

@ -199,9 +199,9 @@ function git(command) {
function getChannelFile(platform) { function getChannelFile(platform) {
let currentChannel = ''; let currentChannel = '';
if (isNightly) { if (isNightly) {
currentChannel = 'nightly'; currentChannel = 'beta';
} else if (isRelease) { } else if (isRelease) {
currentChannel = 'stable'; currentChannel = 'latest';
} else { } else {
// We're not creating a nightly build nor releasing // We're not creating a nightly build nor releasing
// a new version, no need for a channel file. // a new version, no need for a channel file.