diff --git a/src/cmds.ts b/src/cmds.ts index 0576d4d..ce34635 100644 --- a/src/cmds.ts +++ b/src/cmds.ts @@ -140,10 +140,17 @@ export const commands: Cmd[] = [ requestController: async () => (await import('./commands/status')).status, }, { - cmd: 'updates browser', + cmd: 'updates-browser', description: 'Generate update manifest for the browser binary. This should be run after packaging', requestController: async () => (await import('./commands/updates/browser')).generateBrowserUpdateFiles, }, + { + cmd: 'updates-addons', + description: + 'Generates update manifests for system addons that are included in the browser', + requestController: async () => + (await import('./commands/updates/addons')).generateAddonUpdateFiles, + }, ] diff --git a/src/commands/download.ts b/src/commands/download.ts index e43900c..07200c1 100644 --- a/src/commands/download.ts +++ b/src/commands/download.ts @@ -13,6 +13,7 @@ import { addAddonsToMozBuild, downloadAddon, generateAddonMozBuild, + getAddons, initializeAddon, resolveAddonDownloadUrl, unpackAddon, @@ -29,30 +30,20 @@ export const download = async (): Promise => { process.exit(1) } - const addons = Object.keys(config.addons).map((addon) => ({ - name: addon, - ...config.addons[addon], - })) - if (shouldSetupFirefoxSource()) { await setupFirefoxSource(version) } - for (const addon of addons) { + for (const addon of getAddons()) { const downloadUrl = await resolveAddonDownloadUrl(addon) const downloadedXPI = await downloadAddon(downloadUrl, addon) - if (!downloadedXPI) { - log.info(`Skipping ${addon.name}... Already installed`) - continue - } - await unpackAddon(downloadedXPI, addon) await generateAddonMozBuild(addon) await initializeAddon(addon) } - await addAddonsToMozBuild(addons) + await addAddonsToMozBuild(getAddons()) log.success( `You should be ready to make changes to ${config.name}.`, diff --git a/src/commands/download/addon.ts b/src/commands/download/addon.ts index 69a9d16..b14ea5d 100644 --- a/src/commands/download/addon.ts +++ b/src/commands/download/addon.ts @@ -1,6 +1,7 @@ import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs' import { join } from 'path' import { isMatch } from 'picomatch' +import { config } from '../..' import { ENGINE_DIR, MELON_TMP_DIR } from '../../constants' import { log } from '../../log' @@ -16,6 +17,12 @@ import { downloadFileToLocation } from '../../utils/download' import { readItem } from '../../utils/store' import { discard } from '../discard' +export const getAddons = (): (AddonInfo & { name: string })[] => + Object.keys(config.addons).map((addon) => ({ + name: addon, + ...config.addons[addon], + })) + export async function resolveAddonDownloadUrl( addon: AddonInfo ): Promise { @@ -55,27 +62,22 @@ export async function resolveAddonDownloadUrl( export async function downloadAddon( url: string, addon: AddonInfo & { name: string } -): Promise { +): Promise { const tempFile = join(MELON_TMP_DIR, addon.name + '.xpi') - const outPath = join(ENGINE_DIR, 'browser', 'extensions', addon.name) log.info(`Download addon from ${url}`) - if (existsSync(outPath)) { - // Now we need to do some tests. First, if there is no cache file, - // we must discard the existing folder and download the file again. - // If there is a cache file and the cache file points to the same path - // we can return and skip the download. - + { const extensionCache = readItem<{ url: string }>(addon.name) if (extensionCache.isNone()) { - // We haven't stored it in the cache, therefore we need to redonwload + // We haven't stored it in the cache, therefore we need to redownload // it } else { const cache = extensionCache.unwrap() - if (cache.url == url) { - return false + if (cache.url == url && existsSync(tempFile)) { + log.info(`Using cached version of ${addon.name}`) + return tempFile } } } @@ -98,6 +100,13 @@ export async function unpackAddon( ) { const outPath = join(ENGINE_DIR, 'browser', 'extensions', addon.name) + if (existsSync(outPath)) { + log.info( + `The extension ${addon.name} has already been unpacked... skipping` + ) + return + } + log.info(`Unpacking extension...`) // I do not know why, but this delay causes unzip to work reliably diff --git a/src/commands/download/firefox.ts b/src/commands/download/firefox.ts index 3fc4e97..ef54b01 100644 --- a/src/commands/download/firefox.ts +++ b/src/commands/download/firefox.ts @@ -80,7 +80,7 @@ async function downloadFirefoxSource(version: string) { log.info(`Locating Firefox release ${version}...`) - await ensureDir(fsParent) + await ensureDir(fsSaveLocation) if (existsSync(fsSaveLocation)) { log.info('Using cached download') diff --git a/src/commands/updates/addons.ts b/src/commands/updates/addons.ts new file mode 100644 index 0000000..77c737b --- /dev/null +++ b/src/commands/updates/addons.ts @@ -0,0 +1,50 @@ +import { stat, writeFile } from 'fs/promises' +import { dirname, join } from 'path' +import { create } from 'xmlbuilder2' +import { DIST_DIR } from '../../constants' +import { dynamicConfig, ensureDir, generateHash, getSize } from '../../utils' +import { + downloadAddon, + getAddons, + resolveAddonDownloadUrl, +} from '../download/addon' + +export async function generateAddonUpdateFiles() { + const addons = [] + + for (const addon of getAddons()) { + const url = await resolveAddonDownloadUrl(addon) + const xpi = await downloadAddon(url, addon) + + addons.push({ + ...addon, + url, + xpi, + hash: await generateHash(xpi, 'sha256'), + hashType: 'sha256', + size: await getSize(xpi), + }) + } + + const root = create().ele('updates').ele('addons') + + for (const addon of addons) { + const addonNode = root.ele('addon') + addonNode.att('id', addon.id) + addonNode.att('URL', addon.url) + addonNode.att('hashFunction', addon.hashType) + addonNode.att('hashValue', addon.hash) + addonNode.att('size', addon.size.toString()) + addonNode.att('version', addon.version) + } + + const path = join( + DIST_DIR, + 'update/browser/addons', + dynamicConfig.get('brand'), + 'update.xml' + ) + + ensureDir(path) + await writeFile(path, root.end({ prettyPrint: true })) +} diff --git a/src/commands/updates/browser.ts b/src/commands/updates/browser.ts index 2fb8556..e484982 100644 --- a/src/commands/updates/browser.ts +++ b/src/commands/updates/browser.ts @@ -1,5 +1,5 @@ import { existsSync } from 'fs' -import { readFile, stat, writeFile } from 'fs/promises' +import { readFile, writeFile } from 'fs/promises' import { parse } from 'ini' import { isAppleSilicon } from 'is-apple-silicon' import { dirname, join } from 'path' @@ -11,6 +11,7 @@ import { dynamicConfig, ensureEmpty, generateHash, + getSize, ReleaseInfo, } from '../../utils' @@ -165,7 +166,7 @@ export async function generateBrowserUpdateFiles() { '@URL': completeMarURL, '@hashFunction': 'sha512', '@hashValue': marHash, - '@size': (await stat(marPath)).size, + '@size': await getSize(marPath), }, }, }, diff --git a/src/utils/addons.ts b/src/utils/addons.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/config.ts b/src/utils/config.ts index 93d1aa8..11faa06 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -86,6 +86,7 @@ export interface AMOAddonInfo { export interface UrlAddonInfo { platform: 'url' + version: string id: string url: string } diff --git a/src/utils/fs.ts b/src/utils/fs.ts index 55e3833..3fd76af 100644 --- a/src/utils/fs.ts +++ b/src/utils/fs.ts @@ -173,3 +173,7 @@ export function ensureEmpty(path: string) { mkdirSync(path, { recursive: true }) } + +export async function getSize(path: string): Promise { + return (await stat(path)).size +}