-
Notifications
You must be signed in to change notification settings - Fork 157
feat: download ADK Web assets dynamically and serve from shared folder #427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,3 +9,4 @@ tests/**/package-lock.json | |
| coverage/ | ||
| .env | ||
| go.sum | ||
| dev/.cache/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,11 +4,23 @@ | |
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| import esbuild from 'esbuild'; | ||
| import {cp, writeFile} from 'node:fs/promises'; | ||
| import {execSync} from 'node:child_process'; | ||
| import {createWriteStream, existsSync, unlink} from 'node:fs'; | ||
| import {mkdir, readFile, rm, writeFile} from 'node:fs/promises'; | ||
| import * as https from 'node:https'; | ||
| import * as path from 'node:path'; | ||
| import {fileURLToPath} from 'node:url'; | ||
| // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
| // @ts-ignore | ||
| import {shimPlugin} from 'esbuild-shim-plugin'; | ||
|
|
||
| const __filename = fileURLToPath(import.meta.url); | ||
| const __dirname = path.dirname(__filename); | ||
|
|
||
| const ADK_WEB_VERSION = 'v1.0.0'; | ||
| const cacheDir = path.join(__dirname, '.cache'); | ||
| const zipCachePath = path.join(cacheDir, `adk-web-${ADK_WEB_VERSION}.zip`); | ||
|
|
||
| const licenseHeaderText = `/** | ||
| * @license | ||
| * Copyright 2026 Google LLC | ||
|
|
@@ -26,6 +38,101 @@ const commonOptions = { | |
| sourcemap: false, | ||
| }; | ||
|
|
||
| function downloadFile(url, dest) { | ||
| return new Promise((resolve, reject) => { | ||
| const request = https.get(url, (response) => { | ||
| if ( | ||
| response.statusCode && | ||
| response.statusCode >= 300 && | ||
| response.statusCode < 400 && | ||
| response.headers.location | ||
| ) { | ||
| // Follow redirect | ||
| downloadFile(response.headers.location, dest) | ||
| .then(resolve) | ||
| .catch(reject); | ||
| return; | ||
| } | ||
|
|
||
| if (response.statusCode !== 200) { | ||
| reject( | ||
| new Error( | ||
| `Failed to get '${url}' (status code: ${response.statusCode})`, | ||
| ), | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| const file = createWriteStream(dest); | ||
| response.pipe(file); | ||
| file.on('finish', () => { | ||
| file.close(); | ||
| resolve(undefined); | ||
| }); | ||
| file.on('error', (err) => { | ||
| unlink(dest, () => reject(err)); | ||
| }); | ||
| }); | ||
|
|
||
| request.on('error', (err) => { | ||
| unlink(dest, () => reject(err)); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| function unzipFile(zipPath, destDir) { | ||
| if (process.platform === 'win32') { | ||
| execSync( | ||
| `powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`, | ||
| ); | ||
| } else { | ||
| execSync(`unzip -o "${zipPath}" -d "${destDir}"`); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since import AdmZip from 'adm-zip';
function unzipFile(zipPath, destDir) {
const zip = new AdmZip(zipPath);
zip.extractAllTo(destDir, true);
}Make sure to add |
||
| } | ||
|
|
||
| async function ensureBrowserAssets() { | ||
| const browserOutputDir = path.join(__dirname, 'dist/browser'); | ||
| const versionFile = path.join(browserOutputDir, '.version'); | ||
|
|
||
| let needPopulate = false; | ||
|
|
||
| if (!existsSync(browserOutputDir)) { | ||
| needPopulate = true; | ||
| } else { | ||
| try { | ||
| const version = (await readFile(versionFile, 'utf8')).trim(); | ||
| if (version !== ADK_WEB_VERSION) needPopulate = true; | ||
| } catch { | ||
| needPopulate = true; | ||
| } | ||
| } | ||
|
|
||
| if (needPopulate) { | ||
| if (!existsSync(zipCachePath)) { | ||
| console.log( | ||
| `[ADK Build] ADK Web zip not cached. Fetching ${ADK_WEB_VERSION} from GitHub...`, | ||
| ); | ||
| if (!existsSync(cacheDir)) { | ||
| await mkdir(cacheDir, {recursive: true}); | ||
| } | ||
| const url = `https://github.com/google/adk-web/releases/download/${ADK_WEB_VERSION}/adk-web-browser.zip`; | ||
| await downloadFile(url, zipCachePath); | ||
| console.log( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To prevent corrupt cache files if the download gets interrupted mid-way (which would skip downloads and fail during unzipping on subsequent runs), consider downloading to a temporary file (e.g. |
||
| `[ADK Build] Downloaded and cached ADK Web ${ADK_WEB_VERSION}.`, | ||
| ); | ||
| } | ||
|
|
||
| console.log(`[ADK Build] Extracting ADK Web assets to dist/browser...`); | ||
| if (existsSync(browserOutputDir)) { | ||
| await rm(browserOutputDir, {recursive: true, force: true}); | ||
| } | ||
| await mkdir(browserOutputDir, {recursive: true}); | ||
| unzipFile(zipCachePath, browserOutputDir); | ||
| await writeFile(versionFile, ADK_WEB_VERSION, 'utf8'); | ||
| console.log(`[ADK Build] ADK Web assets successfully populated.`); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Builds the ADK devtools library. | ||
| */ | ||
|
|
@@ -52,8 +159,7 @@ async function main() { | |
|
|
||
| // Run file operations sequentially to avoid race conditions | ||
| await writeFile('./dist/cjs/package.json', '{"type": "commonjs"}'); | ||
| await cp('./src/browser', './dist/esm/browser', {recursive: true}); | ||
| await cp('./src/browser', './dist/cjs/browser', {recursive: true}); | ||
| await ensureBrowserAssets(); | ||
| } | ||
|
|
||
| main().catch((err) => { | ||
|
|
||
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call
response.resume()before recursing/returning here to ensure the redirected response stream is fully consumed and socket resources are released.