Merge remote-tracking branch 'origin' into esm-asar-entrypoints

This commit is contained in:
Erick Zhao
2024-06-17 15:18:33 -07:00
10 changed files with 179 additions and 33 deletions

View File

@@ -2,7 +2,7 @@ version: 2.1
orbs: orbs:
cfa: continuousauth/npm@2.1.0 cfa: continuousauth/npm@2.1.0
node: electronjs/node@2.2.3 node: electronjs/node@2.3.0
workflows: workflows:
test_and_release: test_and_release:

View File

@@ -27,13 +27,21 @@ const templateApp = async (
export default async () => { export default async () => {
await fs.remove(appsDir); await fs.remove(appsDir);
await fs.mkdirp(appsDir); await fs.mkdirp(appsDir);
await templateApp('Asar.app', 'arm64', async (appPath) => { await templateApp('Arm64Asar.app', 'arm64', async (appPath) => {
await fs.copy( await fs.copy(
path.resolve(asarsDir, 'app.asar'), path.resolve(asarsDir, 'app.asar'),
path.resolve(appPath, 'Contents', 'Resources', 'app.asar'), path.resolve(appPath, 'Contents', 'Resources', 'app.asar'),
); );
}); });
// contains `extra-file.txt`
await templateApp('Arm64AsarExtraFile.app', 'arm64', async (appPath) => {
await fs.copy(
path.resolve(asarsDir, 'app2.asar'),
path.resolve(appPath, 'Contents', 'Resources', 'app.asar'),
);
});
await templateApp('X64Asar.app', 'x64', async (appPath) => { await templateApp('X64Asar.app', 'x64', async (appPath) => {
await fs.copy( await fs.copy(
path.resolve(asarsDir, 'app.asar'), path.resolve(asarsDir, 'app.asar'),
@@ -41,13 +49,21 @@ export default async () => {
); );
}); });
await templateApp('NoAsar.app', 'arm64', async (appPath) => { await templateApp('Arm64NoAsar.app', 'arm64', async (appPath) => {
await fs.copy( await fs.copy(
path.resolve(asarsDir, 'app'), path.resolve(asarsDir, 'app'),
path.resolve(appPath, 'Contents', 'Resources', 'app'), path.resolve(appPath, 'Contents', 'Resources', 'app'),
); );
}); });
// contains `extra-file.txt`
await templateApp('Arm64NoAsarExtraFile.app', 'arm64', async (appPath) => {
await fs.copy(
path.resolve(asarsDir, 'app2'),
path.resolve(appPath, 'Contents', 'Resources', 'app'),
);
});
await templateApp('X64NoAsar.app', 'x64', async (appPath) => { await templateApp('X64NoAsar.app', 'x64', async (appPath) => {
await fs.copy( await fs.copy(
path.resolve(asarsDir, 'app'), path.resolve(asarsDir, 'app'),

View File

@@ -8,11 +8,13 @@ const appsPath = path.resolve(__dirname, 'fixtures', 'apps');
describe('asar-utils', () => { describe('asar-utils', () => {
describe('detectAsarMode', () => { describe('detectAsarMode', () => {
it('should correctly detect an asar enabled app', async () => { it('should correctly detect an asar enabled app', async () => {
expect(await detectAsarMode(path.resolve(appsPath, 'Asar.app'))).toBe(AsarMode.HAS_ASAR); expect(await detectAsarMode(path.resolve(appsPath, 'Arm64Asar.app'))).toBe(AsarMode.HAS_ASAR);
}); });
it('should correctly detect an app without an asar', async () => { it('should correctly detect an app without an asar', async () => {
expect(await detectAsarMode(path.resolve(appsPath, 'NoAsar.app'))).toBe(AsarMode.NO_ASAR); expect(await detectAsarMode(path.resolve(appsPath, 'Arm64NoAsar.app'))).toBe(
AsarMode.NO_ASAR,
);
}); });
}); });

View File

@@ -10,8 +10,8 @@ describe('file-utils', () => {
let noAsarFiles: AppFile[]; let noAsarFiles: AppFile[];
beforeAll(async () => { beforeAll(async () => {
asarFiles = await getAllAppFiles(path.resolve(appsPath, 'Asar.app')); asarFiles = await getAllAppFiles(path.resolve(appsPath, 'Arm64Asar.app'));
noAsarFiles = await getAllAppFiles(path.resolve(appsPath, 'NoAsar.app')); noAsarFiles = await getAllAppFiles(path.resolve(appsPath, 'Arm64NoAsar.app'));
}); });
it('should correctly identify plist files', async () => { it('should correctly identify plist files', async () => {

BIN
test/fixtures/asars/app2.asar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
erick was here!

2
test/fixtures/asars/app2/index.js vendored Normal file
View File

@@ -0,0 +1,2 @@
console.log('I am an app.asar', process.arch);
process.exit(0);

4
test/fixtures/asars/app2/package.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "app",
"main": "index.js"
}

View File

@@ -2,9 +2,10 @@ import { spawn } from '@malept/cross-spawn-promise';
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
import { makeUniversalApp } from '../src/index'; import { makeUniversalApp } from '../dist/cjs/index';
const appsPath = path.resolve(__dirname, 'fixtures', 'apps'); const appsPath = path.resolve(__dirname, 'fixtures', 'apps');
const appsOutPath = path.resolve(__dirname, 'fixtures', 'apps', 'out');
async function ensureUniversal(app: string) { async function ensureUniversal(app: string) {
const exe = path.resolve(app, 'Contents', 'MacOS', 'Electron'); const exe = path.resolve(app, 'Contents', 'MacOS', 'Electron');
@@ -14,27 +15,147 @@ async function ensureUniversal(app: string) {
expect(result2).toContain('x64'); expect(result2).toContain('x64');
} }
// See `jest.setup.ts` for app fixture setup process
describe('makeUniversalApp', () => { describe('makeUniversalApp', () => {
it('should correctly merge two identical asars', async () => { afterEach(async () => {
const out = path.resolve(appsPath, 'MergedAsar.app'); await fs.emptyDir(appsOutPath);
await makeUniversalApp({ });
x64AppPath: path.resolve(appsPath, 'X64Asar.app'),
arm64AppPath: path.resolve(appsPath, 'Asar.app'), it('throws an error if asar is only detected in one arch', async () => {
outAppPath: out, const out = path.resolve(appsOutPath, 'Error.app');
await expect(
makeUniversalApp({
x64AppPath: path.resolve(appsPath, 'X64Asar.app'),
arm64AppPath: path.resolve(appsPath, 'Arm64NoAsar.app'),
outAppPath: out,
}),
).rejects.toThrow(
'Both the x64 and arm64 versions of your application need to have been built with the same asar settings (enabled vs disabled)',
);
});
it.todo('works for lipo binary resources');
describe('force', () => {
it('throws an error if `out` bundle already exists and `force` is `false`', async () => {
const out = path.resolve(appsOutPath, 'Error.app');
await fs.mkdirp(out);
await expect(
makeUniversalApp({
x64AppPath: path.resolve(appsPath, 'X64Asar.app'),
arm64AppPath: path.resolve(appsPath, 'Arm64Asar.app'),
outAppPath: out,
}),
).rejects.toThrow(/The out path ".*" already exists and force is not set to true/);
}); });
await ensureUniversal(out);
// Only a single asar as they were identical it('packages successfully if `out` bundle already exists and `force` is `true`', async () => {
expect( const out = path.resolve(appsOutPath, 'Error.app');
(await fs.readdir(path.resolve(out, 'Contents', 'Resources'))).filter((p) => await fs.mkdirp(out);
p.endsWith('asar'), await makeUniversalApp({
), x64AppPath: path.resolve(appsPath, 'X64Asar.app'),
).toEqual(['app.asar']); arm64AppPath: path.resolve(appsPath, 'Arm64Asar.app'),
}, 60000); outAppPath: out,
force: true,
});
await ensureUniversal(out);
// Only a single asar as they were identical
expect(
(await fs.readdir(path.resolve(out, 'Contents', 'Resources'))).filter((p) =>
p.endsWith('asar'),
),
).toEqual(['app.asar']);
}, 60000);
});
describe('asar mode', () => {
it('should correctly merge two identical asars', async () => {
const out = path.resolve(appsOutPath, 'MergedAsar.app');
await makeUniversalApp({
x64AppPath: path.resolve(appsPath, 'X64Asar.app'),
arm64AppPath: path.resolve(appsPath, 'Arm64Asar.app'),
outAppPath: out,
});
await ensureUniversal(out);
// Only a single asar as they were identical
expect(
(await fs.readdir(path.resolve(out, 'Contents', 'Resources'))).filter((p) =>
p.endsWith('asar'),
),
).toEqual(['app.asar']);
}, 60000);
it('should create a shim if asars are different between architectures', async () => {
const out = path.resolve(appsOutPath, 'ShimmedAsar.app');
await makeUniversalApp({
x64AppPath: path.resolve(appsPath, 'X64Asar.app'),
arm64AppPath: path.resolve(appsPath, 'Arm64AsarExtraFile.app'),
outAppPath: out,
});
await ensureUniversal(out);
// We have three asars including the arch-agnostic shim
expect(
(await fs.readdir(path.resolve(out, 'Contents', 'Resources')))
.filter((p) => p.endsWith('asar'))
.sort(),
).toEqual(['app.asar', 'app-x64.asar', 'app-arm64.asar'].sort());
}, 60000);
it('should merge two different asars when `mergeASARs` is enabled', async () => {
const out = path.resolve(appsOutPath, 'MergedAsar.app');
await makeUniversalApp({
x64AppPath: path.resolve(appsPath, 'X64Asar.app'),
arm64AppPath: path.resolve(appsPath, 'Arm64AsarExtraFile.app'),
outAppPath: out,
mergeASARs: true,
singleArchFiles: 'extra-file.txt',
});
await ensureUniversal(out);
// Only a single merged asar
expect(
(await fs.readdir(path.resolve(out, 'Contents', 'Resources'))).filter((p) =>
p.endsWith('asar'),
),
).toEqual(['app.asar']);
}, 60000);
it('throws an error if `mergeASARs` is enabled and `singleArchFiles` is missing a unique file', async () => {
const out = path.resolve(appsOutPath, 'Error.app');
await expect(
makeUniversalApp({
x64AppPath: path.resolve(appsPath, 'X64Asar.app'),
arm64AppPath: path.resolve(appsPath, 'Arm64AsarExtraFile.app'),
outAppPath: out,
mergeASARs: true,
singleArchFiles: 'bad-rule',
}),
).rejects.toThrow(/Detected unique file "extra-file\.txt"/);
}, 60000);
it.todo('should not inject ElectronAsarIntegrity into `infoPlistsToIgnore`');
});
describe('no asar mode', () => {
it('should correctly merge two identical app folders', async () => {
const out = path.resolve(appsOutPath, 'MergedNoAsar.app');
await makeUniversalApp({
x64AppPath: path.resolve(appsPath, 'X64NoAsar.app'),
arm64AppPath: path.resolve(appsPath, 'Arm64NoAsar.app'),
outAppPath: out,
});
await ensureUniversal(out);
// Only a single app folder as they were identical
expect(
(await fs.readdir(path.resolve(out, 'Contents', 'Resources'))).filter((p) =>
p.startsWith('app'),
),
).toEqual(['app']);
}, 60000);
it.todo('should shim two different app folders');
});
// TODO: Add tests for // TODO: Add tests for
// * different asar files
// * identical app dirs
// * different app dirs
// * different app dirs with different macho files // * different app dirs with different macho files
// * identical app dirs with universal macho files // * identical app dirs with universal macho files
}); });

View File

@@ -928,11 +928,11 @@ brace-expansion@^2.0.1:
balanced-match "^1.0.0" balanced-match "^1.0.0"
braces@^3.0.2: braces@^3.0.2:
version "3.0.2" version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies: dependencies:
fill-range "^7.0.1" fill-range "^7.1.1"
browserslist@^4.21.9: browserslist@^4.21.9:
version "4.22.1" version "4.22.1"
@@ -1362,10 +1362,10 @@ fb-watchman@^2.0.0:
dependencies: dependencies:
bser "2.1.1" bser "2.1.1"
fill-range@^7.0.1: fill-range@^7.1.1:
version "7.0.1" version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies: dependencies:
to-regex-range "^5.0.1" to-regex-range "^5.0.1"