Skip to content

Commit

Permalink
Temporarily adds custom branch corresponding to open PR. Adding `gene…
Browse files Browse the repository at this point in the history
…rateOrderingConfig` and unit test for generating an ordering file that contains explicit unpack flag per file
  • Loading branch information
mmaietta committed Feb 7, 2025
1 parent d76ca76 commit 62fdc56
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 48 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"typescript": "^5.2.2"
},
"dependencies": {
"@electron/asar": "^3.2.7",
"@electron/asar": "https://github.com/mmaietta/asar.git#asar-ordering-properties",
"@malept/cross-spawn-promise": "^2.0.0",
"debug": "^4.3.1",
"dir-compare": "^4.2.0",
Expand Down
94 changes: 52 additions & 42 deletions src/asar-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import asar from '@electron/asar';
import asar, { FileProperties } from '@electron/asar';
import { execFileSync } from 'child_process';
import crypto from 'crypto';
import fs from 'fs-extra';
Expand Down Expand Up @@ -59,7 +59,7 @@ export const generateAsarIntegrity = (asarPath: string) => {
};
};

function toRelativePath(file: string): string {
export function toRelativePath(file: string): string {
return file.replace(/^\//, '');
}

Expand All @@ -84,32 +84,13 @@ export const mergeASARs = async ({
}: MergeASARsOptions): Promise<void> => {
d(`merging ${x64AsarPath} and ${arm64AsarPath}`);

const x64Files = new Set(asar.listPackage(x64AsarPath).map(toRelativePath));
const arm64Files = new Set(asar.listPackage(arm64AsarPath).map(toRelativePath));

//
// Build set of unpacked directories and files
//

const unpackedFiles = new Set<string>();

function buildUnpacked(a: string, fileList: Set<string>): void {
for (const file of fileList) {
const stat = asar.statFile(a, file);

if (!('unpacked' in stat) || !stat.unpacked) {
continue;
}

if ('files' in stat) {
continue;
}
unpackedFiles.add(file);
}
}
const x64Dir = await fs.mkdtemp(path.join(os.tmpdir(), 'x64-'));
const arm64Dir = await fs.mkdtemp(path.join(os.tmpdir(), 'arm64-'));

buildUnpacked(x64AsarPath, x64Files);
buildUnpacked(arm64AsarPath, arm64Files);
const x64Files = new Set(asar.listPackage(x64AsarPath, { isPack: false }).map(toRelativePath));
const arm64Files = new Set(
asar.listPackage(arm64AsarPath, { isPack: false }).map(toRelativePath),
);

//
// Build list of files/directories unique to each asar
Expand All @@ -128,6 +109,16 @@ export const mergeASARs = async ({
}
}

//
// Build set of unpacked directories and files and output as explicit ordering file
// Note: We don't know the ordering of unique files, so we exclude them from the ordering file?
//

const ordering = await generateOrderingConfig(
{ asarPath: x64AsarPath, files: x64Files },
{ asarPath: arm64AsarPath, files: arm64Files },
);

//
// Find common bindings with different content
//
Expand Down Expand Up @@ -168,9 +159,6 @@ export const mergeASARs = async ({
// Extract both
//

const x64Dir = await fs.mkdtemp(path.join(os.tmpdir(), 'x64-'));
const arm64Dir = await fs.mkdtemp(path.join(os.tmpdir(), 'arm64-'));

try {
d(`extracting ${x64AsarPath} to ${x64Dir}`);
asar.extractAll(x64AsarPath, x64Dir);
Expand Down Expand Up @@ -203,21 +191,43 @@ export const mergeASARs = async ({

d(`creating archive at ${outputAsarPath}`);

const resolvedUnpack = Array.from(unpackedFiles).map((file) => path.join(x64Dir, file));

let unpack: string | undefined;
if (resolvedUnpack.length > 1) {
unpack = `{${resolvedUnpack.join(',')}}`;
} else if (resolvedUnpack.length === 1) {
unpack = resolvedUnpack[0];
}

await asar.createPackageWithOptions(x64Dir, outputAsarPath, {
unpack,
});
await asar.createPackageWithOptions(x64Dir, outputAsarPath, { ordering });

d('done merging');
} finally {
await Promise.all([fs.remove(x64Dir), fs.remove(arm64Dir)]);
}
};

type ArchSpecificOptions = {
asarPath: string;
files: Set<string>;
};

export async function generateOrderingConfig(x64: ArchSpecificOptions, arm64: ArchSpecificOptions) {
const ordering: Record<string, FileProperties['properties']> = {};

function buildUnpacked(asarPath: string, fileList: Set<string>): void {
for (const file of fileList) {
const stat = asar.statFile(asarPath, file);

let unpack = 'unpacked' in stat && stat.unpacked;

if ('files' in stat) {
continue;
}

ordering[file] = { unpack };
}
}

buildUnpacked(x64.asarPath, x64.files);
buildUnpacked(arm64.asarPath, arm64.files);

const data = Object.entries(ordering).reduce((prev, curr) => {
return `${prev}${curr[0]}:${JSON.stringify(curr[1])}\n`;
}, '');
const orderingFile = path.join(os.tmpdir(), 'ordering.txt');
await fs.writeFile(orderingFile, data);
return orderingFile;
}
10 changes: 10 additions & 0 deletions test/__snapshots__/asar-utils.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`asar-utils generateOrderingConfig should properly read \`unpacked\` flag and generate ordering config 1`] = `
"dir1/file1.txt:{"unpack":false}
dir2/file2.png:{"unpack":true}
dir2/file3.txt:{"unpack":true}
emptyfile.txt:{"unpack":false}
file0.txt:{"unpack":false}
"
`;
33 changes: 32 additions & 1 deletion test/asar-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import * as path from 'path';
import fs from 'fs-extra';

import { AsarMode, detectAsarMode, generateAsarIntegrity } from '../src/asar-utils';
import {
AsarMode,
detectAsarMode,
generateAsarIntegrity,
generateOrderingConfig,
mergeASARs,
toRelativePath,
} from '../src/asar-utils';
import asar from '@electron/asar';

const asarsPath = path.resolve(__dirname, 'fixtures', 'asars');
const appsPath = path.resolve(__dirname, 'fixtures', 'apps');
const appsOutPath = path.resolve(__dirname, 'fixtures', 'apps', 'out');

describe('asar-utils', () => {
describe('detectAsarMode', () => {
Expand All @@ -25,4 +35,25 @@ describe('asar-utils', () => {
);
});
});

describe('generateOrderingConfig', () => {
it('should properly read `unpacked` flag and generate ordering config', async () => {
const identicalArchAsar = path.resolve(asarsPath, 'app-with-unpacked-flags.asar');
const files = new Set(
asar.listPackage(identicalArchAsar, { isPack: false }).map(toRelativePath),
);
const ordering = await generateOrderingConfig(
{
asarPath: identicalArchAsar,
files,
},
{
asarPath: identicalArchAsar,
files,
},
);
const orderingConfig = await fs.readFile(ordering, 'utf-8');
expect(orderingConfig).toMatchSnapshot();
});
});
});
Binary file added test/fixtures/asars/app-with-unpacked-flags.asar
Binary file not shown.
7 changes: 3 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,9 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==

"@electron/asar@^3.2.7":
version "3.2.7"
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.7.tgz#bb8117dc6fd0c06a922ae7fb1c0e2d433e35a6e5"
integrity sha512-8FaSCAIiZGYFWyjeevPQt+0e9xCK9YmJ2Rjg5SXgdsXon6cRnU0Yxnbe6CvJbQn26baifur2Y2G5EBayRIsjyg==
"@electron/asar@https://github.com/mmaietta/asar.git#asar-ordering-properties":
version "0.0.0-development"
resolved "https://github.com/mmaietta/asar.git#96707bfb45c5bd1dbca44de0c9fa0b4285561808"
dependencies:
commander "^5.0.0"
glob "^7.1.6"
Expand Down

0 comments on commit 62fdc56

Please sign in to comment.