|
3 | 3 | (async () => {
|
4 | 4 | const os = require('node:os');
|
5 | 5 | const path = require('node:path');
|
6 |
| - const { mkdirSync, promises: fs, rmSync } = require('node:fs'); |
| 6 | + const decompress = require('decompress'); |
| 7 | + const unzip = require('decompress-unzip'); |
| 8 | + const { mkdirSync, promises: fs, rmSync, existsSync } = require('node:fs'); |
7 | 9 | const { exec } = require('./utils');
|
8 | 10 | const { glob } = require('glob');
|
9 |
| - const { SemVer, gte, valid: validSemVer, gt } = require('semver'); |
| 11 | + const { SemVer, gte, valid: validSemVer, eq } = require('semver'); |
10 | 12 | // Use a node-protoc fork until apple arm32 is supported
|
11 | 13 | // https://github.com/YePpHa/node-protoc/pull/10
|
12 | 14 | const protoc = path.dirname(require('@pingghost/protoc/protoc'));
|
|
90 | 92 | }
|
91 | 93 | */
|
92 | 94 | const versionObject = JSON.parse(versionJson);
|
93 |
| - const version = versionObject.VersionString; |
94 | 95 |
|
95 |
| - // Clone the repository and check out the tagged version |
96 |
| - // Return folder with proto files |
97 |
| - async function getProtoPath(forceCliVersion) { |
98 |
| - const repository = await fs.mkdtemp(path.join(os.tmpdir(), 'arduino-cli-')); |
| 96 | + async function globProtos(folder, pattern = '**/*.proto') { |
| 97 | + let protos = []; |
| 98 | + try { |
| 99 | + const matches = await glob(pattern, { cwd: folder }); |
| 100 | + protos = matches.map((filename) => path.join(folder, filename)); |
| 101 | + } catch (error) { |
| 102 | + console.log(error.stack ?? error.message); |
| 103 | + } |
| 104 | + return protos; |
| 105 | + } |
| 106 | + |
| 107 | + async function getProtosFromRepo( |
| 108 | + commitish = '', |
| 109 | + version = '', |
| 110 | + owner = 'arduino', |
| 111 | + repo = 'arduino-cli' |
| 112 | + ) { |
| 113 | + const repoFolder = await fs.mkdtemp(path.join(os.tmpdir(), 'arduino-cli-')); |
99 | 114 |
|
100 | 115 | const url = `https://github.com/${owner}/${repo}.git`;
|
101 | 116 | console.log(`>>> Cloning repository from '${url}'...`);
|
102 |
| - exec('git', ['clone', url, repository], { logStdout: true }); |
| 117 | + exec('git', ['clone', url, repoFolder], { logStdout: true }); |
103 | 118 | console.log(`<<< Repository cloned.`);
|
104 | 119 |
|
105 |
| - let cliVersion = forceCliVersion || version; |
106 |
| - if (validSemVer(cliVersion)) { |
| 120 | + if (validSemVer(version)) { |
| 121 | + let versionTag = version; |
107 | 122 | // https://github.com/arduino/arduino-cli/pull/2374
|
108 | 123 | if (
|
109 | 124 | gte(new SemVer(version, { loose: true }), new SemVer('0.35.0-rc.1'))
|
110 | 125 | ) {
|
111 |
| - cliVersion = `v${cliVersion}`; |
| 126 | + versionTag = `v${version}`; |
112 | 127 | }
|
113 |
| - console.log(`>>> Checking out tagged version: '${cliVersion}'...`); |
114 |
| - exec('git', ['-C', repository, 'fetch', '--all', '--tags'], { |
| 128 | + console.log(`>>> Checking out tagged version: '${versionTag}'...`); |
| 129 | + exec('git', ['-C', repoFolder, 'fetch', '--all', '--tags'], { |
115 | 130 | logStdout: true,
|
116 | 131 | });
|
117 | 132 | exec(
|
118 | 133 | 'git',
|
119 |
| - ['-C', repository, 'checkout', `tags/${cliVersion}`, '-b', cliVersion], |
| 134 | + ['-C', repoFolder, 'checkout', `tags/${versionTag}`, '-b', versionTag], |
120 | 135 | { logStdout: true }
|
121 | 136 | );
|
122 |
| - console.log(`<<< Checked out tagged version: '${cliVersion}'.`); |
123 |
| - } else if (forceCliVersion) { |
124 |
| - console.log(`WARN: invalid semver: '${forceCliVersion}'.`); |
125 |
| - // If the forced version is invalid, do not proceed with fallbacks. |
126 |
| - return undefined; |
| 137 | + console.log(`<<< Checked out tagged version: '${versionTag}'.`); |
127 | 138 | } else if (commitish) {
|
128 |
| - console.log( |
129 |
| - `>>> Checking out commitish from 'package.json': '${commitish}'...` |
130 |
| - ); |
131 |
| - exec('git', ['-C', repository, 'checkout', commitish], { |
| 139 | + console.log(`>>> Checking out commitish: '${commitish}'...`); |
| 140 | + exec('git', ['-C', repoFolder, 'checkout', commitish], { |
132 | 141 | logStdout: true,
|
133 | 142 | });
|
134 |
| - console.log( |
135 |
| - `<<< Checked out commitish from 'package.json': '${commitish}'.` |
136 |
| - ); |
137 |
| - } else if (versionObject.Commit) { |
138 |
| - console.log( |
139 |
| - `>>> Checking out commitish from the CLI: '${versionObject.Commit}'...` |
140 |
| - ); |
141 |
| - exec('git', ['-C', repository, 'checkout', versionObject.Commit], { |
142 |
| - logStdout: true, |
143 |
| - }); |
144 |
| - console.log( |
145 |
| - `<<< Checked out commitish from the CLI: '${versionObject.Commit}'.` |
146 |
| - ); |
| 143 | + console.log(`<<< Checked out commitish: '${commitish}'.`); |
147 | 144 | } else {
|
148 | 145 | console.log(
|
149 | 146 | `WARN: no 'git checkout'. Generating from the HEAD revision.`
|
150 | 147 | );
|
151 | 148 | }
|
152 | 149 |
|
153 |
| - return path.join(repository, 'rpc'); |
| 150 | + const rpcFolder = await fs.mkdtemp( |
| 151 | + path.join(os.tmpdir(), 'arduino-cli-rpc') |
| 152 | + ); |
| 153 | + |
| 154 | + // Copy the the repository rpc folder so we can remove the repository |
| 155 | + await fs.cp(path.join(repoFolder, 'rpc'), path.join(rpcFolder), { |
| 156 | + recursive: true, |
| 157 | + }); |
| 158 | + rmSync(repoFolder, { recursive: true, maxRetries: 5, force: true }); |
| 159 | + |
| 160 | + // Patch for https://github.com/arduino/arduino-cli/issues/2755 |
| 161 | + // Google proto files are removed from source since v1.1.0 |
| 162 | + if (!existsSync(path.join(rpcFolder, 'google'))) { |
| 163 | + // Include packaged google proto files from v1.1.1 |
| 164 | + // See https://github.com/arduino/arduino-cli/pull/2761 |
| 165 | + console.log(`>>> Missing google proto files. Including from v1.1.1...`); |
| 166 | + const v111ProtoFolder = await getProtosFromZip('1.1.1'); |
| 167 | + |
| 168 | + // Create an return a folder name google in rpcFolder |
| 169 | + const googleFolder = path.join(rpcFolder, 'google'); |
| 170 | + await fs.cp(path.join(v111ProtoFolder, 'google'), googleFolder, { |
| 171 | + recursive: true, |
| 172 | + }); |
| 173 | + console.log(`<<< Included google proto files from v1.1.1.`); |
| 174 | + } |
| 175 | + |
| 176 | + return rpcFolder; |
154 | 177 | }
|
155 | 178 |
|
156 |
| - const protoPath = await getProtoPath(); |
| 179 | + async function getProtosFromZip(version) { |
| 180 | + if (!version) { |
| 181 | + console.log(`Could not download proto files: CLI version not provided.`); |
| 182 | + process.exit(1); |
| 183 | + } |
| 184 | + console.log(`>>> Downloading proto files from zip for ${version}.`); |
| 185 | + |
| 186 | + const url = `https://downloads.arduino.cc/arduino-cli/arduino-cli_${version}_proto.zip`; |
| 187 | + const protos = await fs.mkdtemp( |
| 188 | + path.join(os.tmpdir(), 'arduino-cli-proto') |
| 189 | + ); |
157 | 190 |
|
158 |
| - if (!protoPath) { |
159 |
| - console.log(`Could not find the proto files folder.`); |
| 191 | + const { default: download } = await import('@xhmikosr/downloader'); |
| 192 | + /** @type {import('node:buffer').Buffer} */ |
| 193 | + // eslint-disable-next-line @typescript-eslint/ban-ts-comment |
| 194 | + // @ts-ignore |
| 195 | + const data = await download(url); |
| 196 | + |
| 197 | + await decompress(data, protos, { |
| 198 | + plugins: [unzip()], |
| 199 | + filter: (file) => file.path.endsWith('.proto'), |
| 200 | + }); |
| 201 | + |
| 202 | + console.log( |
| 203 | + `<<< Finished downloading and extracting proto files for ${version}.` |
| 204 | + ); |
| 205 | + |
| 206 | + return protos; |
| 207 | + } |
| 208 | + |
| 209 | + let protosFolder; |
| 210 | + |
| 211 | + if (commitish) { |
| 212 | + protosFolder = await getProtosFromRepo(commitish, undefined, owner, repo); |
| 213 | + } else if ( |
| 214 | + versionObject.VersionString && |
| 215 | + validSemVer(versionObject.VersionString) |
| 216 | + ) { |
| 217 | + const version = versionObject.VersionString; |
| 218 | + // v1.1.0 does not contains google proto files in zip |
| 219 | + // See https://github.com/arduino/arduino-cli/issues/2755 |
| 220 | + const isV110 = eq(new SemVer(version, { loose: true }), '1.1.0'); |
| 221 | + protosFolder = isV110 |
| 222 | + ? await getProtosFromRepo(undefined, version) |
| 223 | + : await getProtosFromZip(version); |
| 224 | + } else if (versionObject.Commit) { |
| 225 | + protosFolder = await getProtosFromRepo(versionObject.Commit); |
| 226 | + } |
| 227 | + |
| 228 | + if (!protosFolder) { |
| 229 | + console.log(`Could not get proto files: missing commitish or version.`); |
| 230 | + process.exit(1); |
| 231 | + } |
| 232 | + |
| 233 | + const protos = await globProtos(protosFolder); |
| 234 | + |
| 235 | + if (!protos || protos.length === 0) { |
| 236 | + rmSync(protosFolder, { recursive: true, maxRetries: 5, force: true }); |
| 237 | + console.log(`Could not find any .proto files under ${protosFolder}.`); |
160 | 238 | process.exit(1);
|
161 | 239 | }
|
162 | 240 |
|
163 | 241 | console.log('>>> Generating TS/JS API from:');
|
164 |
| - exec('git', ['-C', protoPath, 'rev-parse', '--abbrev-ref', 'HEAD'], { |
165 |
| - logStdout: true, |
166 |
| - }); |
167 | 242 |
|
168 | 243 | const out = path.join(__dirname, '..', 'src', 'node', 'cli-protocol');
|
169 | 244 | // Must wipe the gen output folder. Otherwise, dangling service implementation remain in IDE2 code,
|
170 | 245 | // although it has been removed from the proto file.
|
171 | 246 | // For example, https://github.com/arduino/arduino-cli/commit/50a8bf5c3e61d5b661ccfcd6a055e82eeb510859.
|
172 |
| - rmSync(out, { recursive: true, maxRetries: 5, force: true }); |
| 247 | + // rmSync(out, { recursive: true, maxRetries: 5, force: true }); |
173 | 248 | mkdirSync(out, { recursive: true });
|
174 | 249 |
|
175 |
| - if (gt(new SemVer(version, { loose: true }), new SemVer('1.0.4'))) { |
176 |
| - // Patch for https://github.com/arduino/arduino-cli/issues/2755 |
177 |
| - // Credit https://github.com/dankeboy36/ardunno-cli-gen/pull/9/commits/64a5ac89aae605249261c8ceff7255655ecfafca |
178 |
| - // Download the 1.0.4 version and use the missing google/rpc/status.proto file. |
179 |
| - console.log('<<< Generating missing google proto files'); |
180 |
| - const v104ProtoPath = await getProtoPath('1.0.4'); |
181 |
| - if (!v104ProtoPath) { |
182 |
| - console.log(`Could not find the proto files folder for version 1.0.4.`); |
183 |
| - process.exit(1); |
184 |
| - } |
185 |
| - await fs.cp( |
186 |
| - path.join(v104ProtoPath, 'google'), |
187 |
| - path.join(protoPath, 'google'), |
188 |
| - { |
189 |
| - recursive: true, |
190 |
| - } |
| 250 | + try { |
| 251 | + // Generate JS code from the `.proto` files. |
| 252 | + exec( |
| 253 | + 'grpc_tools_node_protoc', |
| 254 | + [ |
| 255 | + `--js_out=import_style=commonjs,binary:${out}`, |
| 256 | + `--grpc_out=generate_package_definition:${out}`, |
| 257 | + '-I', |
| 258 | + protosFolder, |
| 259 | + ...protos, |
| 260 | + ], |
| 261 | + { logStdout: true } |
191 | 262 | );
|
192 |
| - console.log(`>>> Generated missing google file`); |
193 |
| - } |
194 | 263 |
|
195 |
| - let protos = []; |
196 |
| - try { |
197 |
| - const matches = await glob('**/*.proto', { cwd: protoPath }); |
198 |
| - protos = matches.map((filename) => path.join(protoPath, filename)); |
| 264 | + // Generate the `.d.ts` files for JS. |
| 265 | + exec( |
| 266 | + path.join(protoc, `protoc${platform === 'win32' ? '.exe' : ''}`), |
| 267 | + [ |
| 268 | + `--plugin=protoc-gen-ts=${path.resolve( |
| 269 | + __dirname, |
| 270 | + '..', |
| 271 | + 'node_modules', |
| 272 | + '.bin', |
| 273 | + `protoc-gen-ts${platform === 'win32' ? '.cmd' : ''}` |
| 274 | + )}`, |
| 275 | + `--ts_out=generate_package_definition:${out}`, |
| 276 | + '-I', |
| 277 | + protosFolder, |
| 278 | + ...protos, |
| 279 | + ], |
| 280 | + { logStdout: true } |
| 281 | + ); |
199 | 282 | } catch (error) {
|
200 |
| - console.log(error.stack ?? error.message); |
201 |
| - } |
202 |
| - |
203 |
| - if (!protos || protos.length === 0) { |
204 |
| - console.log(`Could not find any .proto files under ${protoPath}.`); |
205 |
| - process.exit(1); |
| 283 | + console.log(error); |
| 284 | + } finally { |
| 285 | + rmSync(protosFolder, { recursive: true, maxRetries: 5, force: true }); |
206 | 286 | }
|
207 | 287 |
|
208 |
| - // Generate JS code from the `.proto` files. |
209 |
| - exec( |
210 |
| - 'grpc_tools_node_protoc', |
211 |
| - [ |
212 |
| - `--js_out=import_style=commonjs,binary:${out}`, |
213 |
| - `--grpc_out=generate_package_definition:${out}`, |
214 |
| - '-I', |
215 |
| - protoPath, |
216 |
| - ...protos, |
217 |
| - ], |
218 |
| - { logStdout: true } |
219 |
| - ); |
220 |
| - |
221 |
| - // Generate the `.d.ts` files for JS. |
222 |
| - exec( |
223 |
| - path.join(protoc, `protoc${platform === 'win32' ? '.exe' : ''}`), |
224 |
| - [ |
225 |
| - `--plugin=protoc-gen-ts=${path.resolve( |
226 |
| - __dirname, |
227 |
| - '..', |
228 |
| - 'node_modules', |
229 |
| - '.bin', |
230 |
| - `protoc-gen-ts${platform === 'win32' ? '.cmd' : ''}` |
231 |
| - )}`, |
232 |
| - `--ts_out=generate_package_definition:${out}`, |
233 |
| - '-I', |
234 |
| - protoPath, |
235 |
| - ...protos, |
236 |
| - ], |
237 |
| - { logStdout: true } |
238 |
| - ); |
239 |
| - |
240 | 288 | console.log('<<< Generation was successful.');
|
241 | 289 | })();
|
0 commit comments