// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // This script is used to download WebAssembly build artifacts from CI pipeline. // // The goal of this script is to save time for ORT Web developers. For most TypeScript tasks, there is no change in the // WebAssembly side, so there is no need to rebuild WebAssembly. // // It performs the following operations: // 1. query build ID for latest successful build on main branch // 2. query download URL of build artifacts // 3. download and unzip the files to folders // import fs from 'fs'; import https from 'https'; import jszip from 'jszip'; import path from 'path'; function downloadJson(url: string, onSuccess: (data: any) => void) { https.get(url, res => { const {statusCode} = res; const contentType = res.headers['content-type']; if (statusCode !== 200) { throw new Error(`Failed to download build list. HTTP status code = ${statusCode}`); } if (!contentType || !/^application\/json/.test(contentType)) { throw new Error(`unexpected content type: ${contentType}`); } res.setEncoding('utf8'); let rawData = ''; res.on('data', (chunk) => { rawData += chunk; }); res.on('end', () => { onSuccess(JSON.parse(rawData)); }); }); } function downloadZip(url: string, onSuccess: (data: Buffer) => void) { https.get(url, res => { const {statusCode} = res; const contentType = res.headers['content-type']; if (statusCode !== 200) { throw new Error(`Failed to download build list. HTTP status code = ${statusCode}`); } if (!contentType || !/^application\/zip/.test(contentType)) { throw new Error(`unexpected content type: ${contentType}`); } const chunks: Buffer[] = []; res.on('data', (chunk) => { chunks.push(chunk); }); res.on('end', () => { onSuccess(Buffer.concat(chunks)); }); }); } function extractFile(zip: jszip, folder: string, file: string, artifactName: string) { zip.file(`${artifactName}/${file}`)!.nodeStream() .pipe(fs.createWriteStream(path.join(folder, file))) .on('finish', () => { console.log('# file downloaded and extracted: ' + file); }); } console.log('=== Start to pull WebAssembly artifacts from CI ==='); // API reference: https://docs.microsoft.com/en-us/rest/api/azure/devops/build/builds/list downloadJson( 'https://dev.azure.com/onnxruntime/onnxruntime/_apis/build/builds?api-version=6.1-preview.6' + '&definitions=161' + '&resultFilter=succeeded' + '&$top=1' + '&repositoryId=Microsoft/onnxruntime' + '&repositoryType=GitHub' + '&branchName=refs/heads/main', data => { const buildId = data.value[0].id; console.log(`=== Found latest build on main branch: ${buildId} ===`); // API reference: https://docs.microsoft.com/en-us/rest/api/azure/devops/build/artifacts/get%20artifact downloadJson( `https://dev.azure.com/onnxruntime/onnxruntime/_apis/build/builds/${ buildId}/artifacts?api-version=6.1-preview.5`, data => { let zipLink; for (const v of data.value) { if (v.name === 'Release_wasm') { zipLink = v.resource.downloadUrl; } } console.log('=== Ready to download zip files ==='); const WASM_FOLDER = path.join(__dirname, '../dist'); if (!fs.existsSync(WASM_FOLDER)) { fs.mkdirSync(WASM_FOLDER); } const JS_FOLDER = path.join(__dirname, '../lib/wasm/binding'); downloadZip(zipLink, buffer => { void jszip.loadAsync(buffer).then(zip => { extractFile(zip, WASM_FOLDER, 'ort-wasm.wasm', 'Release_wasm'); extractFile(zip, WASM_FOLDER, 'ort-wasm-threaded.wasm', 'Release_wasm'); extractFile(zip, WASM_FOLDER, 'ort-wasm-simd.wasm', 'Release_wasm'); extractFile(zip, WASM_FOLDER, 'ort-wasm-simd-threaded.wasm', 'Release_wasm'); extractFile(zip, JS_FOLDER, 'ort-wasm.js', 'Release_wasm'); extractFile(zip, JS_FOLDER, 'ort-wasm-threaded.js', 'Release_wasm'); extractFile(zip, JS_FOLDER, 'ort-wasm-threaded.worker.js', 'Release_wasm'); }); }); }); });