fileUtil.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. /**
  2. * @license
  3. * Copyright 2023 Google Inc.
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. import {exec as execChildProcess, spawnSync} from 'child_process';
  7. import {createReadStream} from 'fs';
  8. import {mkdir, readdir} from 'fs/promises';
  9. import * as path from 'path';
  10. import {promisify} from 'util';
  11. import extractZip from 'extract-zip';
  12. import tar from 'tar-fs';
  13. import bzip from 'unbzip2-stream';
  14. const exec = promisify(execChildProcess);
  15. /**
  16. * @internal
  17. */
  18. export async function unpackArchive(
  19. archivePath: string,
  20. folderPath: string
  21. ): Promise<void> {
  22. if (archivePath.endsWith('.zip')) {
  23. await extractZip(archivePath, {dir: folderPath});
  24. } else if (archivePath.endsWith('.tar.bz2')) {
  25. await extractTar(archivePath, folderPath);
  26. } else if (archivePath.endsWith('.dmg')) {
  27. await mkdir(folderPath);
  28. await installDMG(archivePath, folderPath);
  29. } else if (archivePath.endsWith('.exe')) {
  30. // Firefox on Windows.
  31. const result = spawnSync(archivePath, [`/ExtractDir=${folderPath}`], {
  32. env: {
  33. __compat_layer: 'RunAsInvoker',
  34. },
  35. });
  36. if (result.status !== 0) {
  37. throw new Error(
  38. `Failed to extract ${archivePath} to ${folderPath}: ${result.output}`
  39. );
  40. }
  41. } else {
  42. throw new Error(`Unsupported archive format: ${archivePath}`);
  43. }
  44. }
  45. /**
  46. * @internal
  47. */
  48. function extractTar(tarPath: string, folderPath: string): Promise<void> {
  49. return new Promise((fulfill, reject) => {
  50. const tarStream = tar.extract(folderPath);
  51. tarStream.on('error', reject);
  52. tarStream.on('finish', fulfill);
  53. const readStream = createReadStream(tarPath);
  54. readStream.pipe(bzip()).pipe(tarStream);
  55. });
  56. }
  57. /**
  58. * @internal
  59. */
  60. async function installDMG(dmgPath: string, folderPath: string): Promise<void> {
  61. const {stdout} = await exec(
  62. `hdiutil attach -nobrowse -noautoopen "${dmgPath}"`
  63. );
  64. const volumes = stdout.match(/\/Volumes\/(.*)/m);
  65. if (!volumes) {
  66. throw new Error(`Could not find volume path in ${stdout}`);
  67. }
  68. const mountPath = volumes[0]!;
  69. try {
  70. const fileNames = await readdir(mountPath);
  71. const appName = fileNames.find(item => {
  72. return typeof item === 'string' && item.endsWith('.app');
  73. });
  74. if (!appName) {
  75. throw new Error(`Cannot find app in ${mountPath}`);
  76. }
  77. const mountedPath = path.join(mountPath!, appName);
  78. await exec(`cp -R "${mountedPath}" "${folderPath}"`);
  79. } finally {
  80. await exec(`hdiutil detach "${mountPath}" -quiet`);
  81. }
  82. }