diff --git a/arduino-ide-extension/scripts/download-examples.js b/arduino-ide-extension/scripts/download-examples.js index 6d2d3deb..3b00d1bf 100644 --- a/arduino-ide-extension/scripts/download-examples.js +++ b/arduino-ide-extension/scripts/download-examples.js @@ -4,31 +4,29 @@ const version = '1.10.0'; (async () => { - const os = require('os'); - const { promises: fs } = require('fs'); - const path = require('path'); + const os = require('node:os'); + const { promises: fs } = require('node:fs'); + const path = require('node:path'); const shell = require('shelljs'); const { v4 } = require('uuid'); + const { exec } = require('./utils'); const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`); if (shell.mkdir('-p', repository).code !== 0) { shell.exit(1); } - if ( - shell.exec( - `git clone https://github.com/arduino/arduino-examples.git ${repository}` - ).code !== 0 - ) { - shell.exit(1); - } + exec( + 'git', + ['clone', 'https://github.com/arduino/arduino-examples.git', repository], + shell + ); - if ( - shell.exec(`git -C ${repository} checkout tags/${version} -b ${version}`) - .code !== 0 - ) { - shell.exit(1); - } + exec( + 'git', + ['-C', repository, 'checkout', `tags/${version}`, '-b', version], + shell + ); const destination = path.join(__dirname, '..', 'Examples'); shell.mkdir('-p', destination); diff --git a/arduino-ide-extension/scripts/download-fwuploader.js b/arduino-ide-extension/scripts/download-fwuploader.js index 4c13b628..ed4866ac 100755 --- a/arduino-ide-extension/scripts/download-fwuploader.js +++ b/arduino-ide-extension/scripts/download-fwuploader.js @@ -1,12 +1,11 @@ // @ts-check (async () => { - const fs = require('fs'); - const path = require('path'); - const temp = require('temp'); + const path = require('node:path'); const shell = require('shelljs'); const semver = require('semver'); const downloader = require('./downloader'); + const { taskBuildFromGit } = require('./utils'); const version = (() => { const pkg = require(path.join(__dirname, '..', 'package.json')); @@ -86,81 +85,6 @@ shell.exit(1); } } else { - // We assume an object with `owner`, `repo`, commitish?` properties. - const { owner, repo, commitish } = version; - if (!owner) { - shell.echo(`Could not retrieve 'owner' from ${JSON.stringify(version)}`); - shell.exit(1); - } - if (!repo) { - shell.echo(`Could not retrieve 'repo' from ${JSON.stringify(version)}`); - shell.exit(1); - } - const url = `https://github.com/${owner}/${repo}.git`; - shell.echo( - `Building Firmware Uploader from ${url}. Commitish: ${ - commitish ? commitish : 'HEAD' - }` - ); - - if (fs.existsSync(destinationPath)) { - shell.echo( - `Skipping the Firmware Uploader build because it already exists: ${destinationPath}` - ); - return; - } - - if (shell.mkdir('-p', buildFolder).code !== 0) { - shell.echo('Could not create build folder.'); - shell.exit(1); - } - - const tempRepoPath = temp.mkdirSync(); - shell.echo(`>>> Cloning Firmware Uploader source to ${tempRepoPath}...`); - if (shell.exec(`git clone ${url} ${tempRepoPath}`).code !== 0) { - shell.exit(1); - } - shell.echo('<<< Cloned Firmware Uploader repo.'); - - if (commitish) { - shell.echo(`>>> Checking out ${commitish}...`); - if ( - shell.exec(`git -C ${tempRepoPath} checkout ${commitish}`).code !== 0 - ) { - shell.exit(1); - } - shell.echo(`<<< Checked out ${commitish}.`); - } - - shell.echo(`>>> Building the Firmware Uploader...`); - if (shell.exec('go build', { cwd: tempRepoPath }).code !== 0) { - shell.exit(1); - } - shell.echo('<<< Firmware Uploader build done.'); - - if (!fs.existsSync(path.join(tempRepoPath, fwuploderName))) { - shell.echo( - `Could not find the Firmware Uploader at ${path.join( - tempRepoPath, - fwuploderName - )}.` - ); - shell.exit(1); - } - - const builtFwUploaderPath = path.join(tempRepoPath, fwuploderName); - shell.echo( - `>>> Copying Firmware Uploader from ${builtFwUploaderPath} to ${destinationPath}...` - ); - if (shell.cp(builtFwUploaderPath, destinationPath).code !== 0) { - shell.exit(1); - } - shell.echo(`<<< Copied the Firmware Uploader.`); - - shell.echo('<<< Verifying Firmware Uploader...'); - if (!fs.existsSync(destinationPath)) { - shell.exit(1); - } - shell.echo('>>> Verified Firmware Uploader.'); + taskBuildFromGit(version, destinationPath, 'Firmware Uploader'); } })(); diff --git a/arduino-ide-extension/scripts/generate-protocol.js b/arduino-ide-extension/scripts/generate-protocol.js index ca51d6d7..4b197088 100644 --- a/arduino-ide-extension/scripts/generate-protocol.js +++ b/arduino-ide-extension/scripts/generate-protocol.js @@ -1,156 +1,184 @@ // @ts-check (async () => { + const os = require('node:os'); + const path = require('node:path'); + const { exec } = require('./utils'); + const glob = require('glob'); + const { v4 } = require('uuid'); + const shell = require('shelljs'); + const protoc = path.dirname(require('protoc/protoc')); - const os = require('os'); - const path = require('path'); - const glob = require('glob'); - const { v4 } = require('uuid'); - const shell = require('shelljs'); - const protoc = path.dirname(require('protoc/protoc')); - shell.env.PATH = `${shell.env.PATH}${path.delimiter}${protoc}`; - shell.env.PATH = `${shell.env.PATH}${path.delimiter}${path.join(__dirname, '..', 'node_modules', '.bin')}`; + const repository = path.join(os.tmpdir(), `${v4()}-arduino-cli`); + if (shell.mkdir('-p', repository).code !== 0) { + shell.exit(1); + } - const repository = path.join(os.tmpdir(), `${v4()}-arduino-cli`); - if (shell.mkdir('-p', repository).code !== 0) { - shell.exit(1); + const { owner, repo, commitish } = (() => { + const pkg = require(path.join(__dirname, '..', 'package.json')); + if (!pkg) { + shell.echo(`Could not parse the 'package.json'.`); + shell.exit(1); } - const { owner, repo, commitish } = (() => { - const pkg = require(path.join(__dirname, '..', 'package.json')); - if (!pkg) { - shell.echo(`Could not parse the 'package.json'.`); - shell.exit(1); - } - - const { arduino } = pkg; - if (!arduino) { - return { owner: 'arduino', repo: 'arduino-cli' }; - } - - const { cli } = arduino; - if (!cli) { - return { owner: 'arduino', repo: 'arduino-cli' }; - } - - const { version } = cli; - if (!version) { - return { owner: 'arduino', repo: 'arduino-cli' }; - } - - if (typeof version === 'string') { - return { owner: 'arduino', repo: 'arduino-cli' }; - } - - // We assume an object with `owner`, `repo`, commitish?` properties. - const { owner, repo, commitish } = version; - if (!owner) { - shell.echo(`Could not retrieve 'owner' from ${JSON.stringify(version)}`); - shell.exit(1); - } - if (!repo) { - shell.echo(`Could not retrieve 'repo' from ${JSON.stringify(version)}`); - shell.exit(1); - } - - return { owner, repo, commitish }; - })(); - - const url = `https://github.com/${owner}/${repo}.git`; - shell.echo(`>>> Cloning repository from '${url}'...`); - if (shell.exec(`git clone ${url} ${repository}`).code !== 0) { - shell.exit(1); - } - shell.echo(`<<< Repository cloned.`); - - const { platform } = process; - const build = path.join(__dirname, '..', 'build'); - const cli = path.join(build, `arduino-cli${platform === 'win32' ? '.exe' : ''}`); - const versionJson = shell.exec(`${cli} version --format json`).trim(); - if (!versionJson) { - shell.echo(`Could not retrieve the CLI version from ${cli}.`); - shell.exit(1); - } - // As of today (28.01.2021), the `VersionString` can be one of the followings: - // - `nightly-YYYYMMDD` stands for the nightly build, we use the , the `commitish` from the `package.json` to check out the code. - // - `0.0.0-git` for local builds, we use the `commitish` from the `package.json` to check out the code and generate the APIs. - // - `git-snapshot` for local build executed via `task build`. We do not do this. - // - rest, we assume it is a valid semver and has the corresponding tagged code, we use the tag to generate the APIs from the `proto` files. - /* - { - "Application": "arduino-cli", - "VersionString": "nightly-20210126", - "Commit": "079bb6c6", - "Status": "alpha", - "Date": "2021-01-26T01:46:31Z" - } - */ - const versionObject = JSON.parse(versionJson); - const version = versionObject.VersionString; - if (version && !version.startsWith('nightly-') && version !== '0.0.0-git' && version !== 'git-snapshot') { - shell.echo(`>>> Checking out tagged version: '${version}'...`); - shell.exec(`git -C ${repository} fetch --all --tags`); - if (shell.exec(`git -C ${repository} checkout tags/${version} -b ${version}`).code !== 0) { - shell.exit(1); - } - shell.echo(`<<< Checked out tagged version: '${commitish}'.`); - } else if (commitish) { - shell.echo(`>>> Checking out commitish from 'package.json': '${commitish}'...`); - if (shell.exec(`git -C ${repository} checkout ${commitish}`).code !== 0) { - shell.exit(1); - } - shell.echo(`<<< Checked out commitish from 'package.json': '${commitish}'.`); - } else if (versionObject.Commit) { - shell.echo(`>>> Checking out commitish from the CLI: '${versionObject.Commit}'...`); - if (shell.exec(`git -C ${repository} checkout ${versionObject.Commit}`).code !== 0) { - shell.exit(1); - } - shell.echo(`<<< Checked out commitish from the CLI: '${versionObject.Commit}'.`); - } else { - shell.echo(`WARN: no 'git checkout'. Generating from the HEAD revision.`); + const defaultVersion = { + owner: 'arduino', + repo: 'arduino-cli', + commitish: undefined, + }; + const { arduino } = pkg; + if (!arduino) { + return defaultVersion; } - shell.echo('>>> Generating TS/JS API from:'); - if (shell.exec(`git -C ${repository} rev-parse --abbrev-ref HEAD`).code !== 0) { - shell.exit(1); + const { cli } = arduino; + if (!cli) { + return defaultVersion; } - const rpc = path.join(repository, 'rpc'); - const out = path.join(__dirname, '..', 'src', 'node', 'cli-protocol'); - shell.mkdir('-p', out); - - const protos = await new Promise(resolve => - glob('**/*.proto', { cwd: rpc }, (error, matches) => { - if (error) { - shell.echo(error.stack); - resolve([]); - return; - } - resolve(matches.map(filename => path.join(rpc, filename))); - })); - if (!protos || protos.length === 0) { - shell.echo(`Could not find any .proto files under ${rpc}.`); - shell.exit(1); + const { version } = cli; + if (!version) { + return defaultVersion; } - // Generate JS code from the `.proto` files. - if (shell.exec(`grpc_tools_node_protoc \ ---js_out=import_style=commonjs,binary:${out} \ ---grpc_out=generate_package_definition:${out} \ --I ${rpc} \ -${protos.join(' ')}`).code !== 0) { - shell.exit(1); + if (typeof version === 'string') { + return defaultVersion; } - // Generate the `.d.ts` files for JS. - if (shell.exec(`protoc \ ---plugin=protoc-gen-ts=${path.resolve(__dirname, '..', 'node_modules', '.bin', `protoc-gen-ts${platform === 'win32' ? '.cmd' : ''}`)} \ ---ts_out=generate_package_definition:${out} \ --I ${rpc} \ -${protos.join(' ')}`).code !== 0) { - shell.exit(1); + // We assume an object with `owner`, `repo`, commitish?` properties. + const { owner, repo, commitish } = version; + if (!owner) { + shell.echo(`Could not retrieve 'owner' from ${JSON.stringify(version)}`); + shell.exit(1); + } + if (!repo) { + shell.echo(`Could not retrieve 'repo' from ${JSON.stringify(version)}`); + shell.exit(1); } - shell.echo('<<< Generation was successful.'); + return { owner, repo, commitish }; + })(); + const url = `https://github.com/${owner}/${repo}.git`; + shell.echo(`>>> Cloning repository from '${url}'...`); + exec('git', ['clone', url, repository], shell); + shell.echo(`<<< Repository cloned.`); + + const { platform } = process; + const build = path.join(__dirname, '..', 'build'); + const cli = path.join( + build, + `arduino-cli${platform === 'win32' ? '.exe' : ''}` + ); + const versionJson = exec(cli, ['version', '--format', 'json'], shell).trim(); + if (!versionJson) { + shell.echo(`Could not retrieve the CLI version from ${cli}.`); + shell.exit(1); + } + // As of today (28.01.2021), the `VersionString` can be one of the followings: + // - `nightly-YYYYMMDD` stands for the nightly build, we use the , the `commitish` from the `package.json` to check out the code. + // - `0.0.0-git` for local builds, we use the `commitish` from the `package.json` to check out the code and generate the APIs. + // - `git-snapshot` for local build executed via `task build`. We do not do this. + // - rest, we assume it is a valid semver and has the corresponding tagged code, we use the tag to generate the APIs from the `proto` files. + /* + { + "Application": "arduino-cli", + "VersionString": "nightly-20210126", + "Commit": "079bb6c6", + "Status": "alpha", + "Date": "2021-01-26T01:46:31Z" + } + */ + const versionObject = JSON.parse(versionJson); + const version = versionObject.VersionString; + if ( + version && + !version.startsWith('nightly-') && + version !== '0.0.0-git' && + version !== 'git-snapshot' + ) { + shell.echo(`>>> Checking out tagged version: '${version}'...`); + exec('git', ['-C', repository, 'fetch', '--all', '--tags'], shell); + exec( + 'git', + ['-C', repository, 'checkout', `tags/${version}`, '-b', version], + shell + ); + shell.echo(`<<< Checked out tagged version: '${version}'.`); + } else if (commitish) { + shell.echo( + `>>> Checking out commitish from 'package.json': '${commitish}'...` + ); + exec('git', ['-C', repository, 'checkout', commitish], shell); + shell.echo( + `<<< Checked out commitish from 'package.json': '${commitish}'.` + ); + } else if (versionObject.Commit) { + shell.echo( + `>>> Checking out commitish from the CLI: '${versionObject.Commit}'...` + ); + exec('git', ['-C', repository, 'checkout', versionObject.Commit], shell); + shell.echo( + `<<< Checked out commitish from the CLI: '${versionObject.Commit}'.` + ); + } else { + shell.echo(`WARN: no 'git checkout'. Generating from the HEAD revision.`); + } + + shell.echo('>>> Generating TS/JS API from:'); + exec('git', ['-C', repository, 'rev-parse', '--abbrev-ref', 'HEAD'], shell); + + const rpc = path.join(repository, 'rpc'); + const out = path.join(__dirname, '..', 'src', 'node', 'cli-protocol'); + shell.mkdir('-p', out); + + const protos = await new Promise((resolve) => + glob('**/*.proto', { cwd: rpc }, (error, matches) => { + if (error) { + shell.echo(error.stack ?? error.message); + resolve([]); + return; + } + resolve(matches.map((filename) => path.join(rpc, filename))); + }) + ); + if (!protos || protos.length === 0) { + shell.echo(`Could not find any .proto files under ${rpc}.`); + shell.exit(1); + } + + // Generate JS code from the `.proto` files. + + exec( + 'grpc_tools_node_protoc', + [ + `--js_out=import_style=commonjs,binary:${out}`, + `--grpc_out=generate_package_definition:${out}`, + '-I', + rpc, + ...protos, + ], + shell + ); + + // Generate the `.d.ts` files for JS. + exec( + path.join(protoc, `protoc${platform === 'win32' ? '.exe' : ''}`), + [ + `--plugin=protoc-gen-ts=${path.resolve( + __dirname, + '..', + 'node_modules', + '.bin', + `protoc-gen-ts${platform === 'win32' ? '.cmd' : ''}` + )}`, + `--ts_out=generate_package_definition:${out}`, + '-I', + rpc, + ...protos, + ], + shell + ); + + shell.echo('<<< Generation was successful.'); })(); diff --git a/arduino-ide-extension/scripts/utils.js b/arduino-ide-extension/scripts/utils.js index c7ca7851..4b18c74f 100644 --- a/arduino-ide-extension/scripts/utils.js +++ b/arduino-ide-extension/scripts/utils.js @@ -1,3 +1,31 @@ +// @ts-check + +const exec = ( + /** @type {string} */ command, + /** @type {readonly string[]} */ args, + /** @type {import('shelljs')|undefined}*/ shell = undefined, + /** @type {import('node:child_process').ExecFileSyncOptionsWithStringEncoding|undefined} */ options = undefined +) => { + try { + const stdout = require('node:child_process').execFileSync( + command, + args, + options ? options : { encoding: 'utf8' } + ); + if (shell) { + shell.echo(stdout.trim()); + } + return stdout; + } catch (err) { + if (shell) { + shell.echo(err instanceof Error ? err.message : String(err)); + shell.exit(1); + } + throw err; + } +}; +exports.exec = exec; + /** * Clones something from GitHub and builds it with [`Task`](https://taskfile.dev/). * @@ -21,11 +49,15 @@ exports.goBuildFromGit = (version, destinationPath, taskName) => { }; /** - * The `command` is either `go` or `task`. + * The `command` must be either `'go'` or `'task'`. + * @param {string} command + * @param {{ owner: any; repo: any; commitish: any; }} version + * @param {string} destinationPath + * @param {string} taskName */ function buildFromGit(command, version, destinationPath, taskName) { - const fs = require('fs'); - const path = require('path'); + const fs = require('node:fs'); + const path = require('node:path'); const temp = require('temp'); const shell = require('shelljs'); @@ -66,23 +98,17 @@ function buildFromGit(command, version, destinationPath, taskName) { const tempRepoPath = temp.mkdirSync(); shell.echo(`>>> Cloning ${taskName} source to ${tempRepoPath}...`); - if (shell.exec(`git clone ${url} ${tempRepoPath}`).code !== 0) { - shell.exit(1); - } + exec('git', ['clone', url, tempRepoPath], shell); shell.echo(`<<< Cloned ${taskName} repo.`); if (commitish) { shell.echo(`>>> Checking out ${commitish}...`); - if (shell.exec(`git -C ${tempRepoPath} checkout ${commitish}`).code !== 0) { - shell.exit(1); - } + exec('git', ['-C', tempRepoPath, 'checkout', commitish], shell); shell.echo(`<<< Checked out ${commitish}.`); } shell.echo(`>>> Building the ${taskName}...`); - if (shell.exec(`${command} build`, { cwd: tempRepoPath }).code !== 0) { - shell.exit(1); - } + exec(command, ['build'], shell, { cwd: tempRepoPath, encoding: 'utf8' }); shell.echo(`<<< Done ${taskName} build.`); const binName = path.basename(destinationPath);