Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 27 additions & 10 deletions src/notarytool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,31 @@ async function getNotarizationLogs(opts: NotarizeOptions, id: string) {
}
}

export function parseNotarytoolOutput(output: string): any {
const rawOut = output.trim();

let jsonOut: string = '';

for (const line of rawOut.split('\n')) {
const trimmedLine = line.trim();
if (trimmedLine.startsWith('{') && trimmedLine.endsWith('}')) {
jsonOut = line;
break;
}
}

d('notarytool produced output:\n', output);

let parsed: any;
try {
parsed = JSON.parse(jsonOut);
} catch (err) {
throw new Error(`Could not parse notarytool output: \n\n${rawOut}`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we as a fallback try parse the entire output as JSON so that anything that worked before due to strange chunking or whatever keeps working.

Super appreciate the test coverage 🙇

}

return parsed;
}

export async function isNotaryToolAvailable(notarytoolPath?: string) {
if (notarytoolPath !== undefined) {
const result = await spawn(notarytoolPath, ['--version']);
Expand Down Expand Up @@ -110,16 +135,8 @@ export async function notarizeAndWaitForNotaryTool(opts: NotarizeOptions) {
];

const result = await runNotaryTool(notarizeArgs, opts.notarytoolPath);
const rawOut = result.output.trim();

let parsed: any;
try {
parsed = JSON.parse(rawOut);
} catch (err) {
throw new Error(
`Failed to notarize via notarytool. Failed with unexpected result: \n\n${rawOut}`,
);
}

const parsed = parseNotarytoolOutput(result.output);

let logOutput: undefined | string;
if (typeof parsed.id === 'string') {
Expand Down
65 changes: 65 additions & 0 deletions test/notarytool.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { describe, expect, it } from 'vitest';
import { parseNotarytoolOutput } from '../src/notarytool.js';

describe('parseNotarytoolOutput', () => {
it('parses valid JSON output', () => {
const output = '{"status": "Accepted", "id": "123"}';
expect(parseNotarytoolOutput(output)).toEqual({
status: 'Accepted',
id: '123',
});
});

it('parses JSON with whitespace', () => {
const output = '\n\n {"status": "Accepted", "id": "456"} \n';
expect(parseNotarytoolOutput(output)).toEqual({
status: 'Accepted',
id: '456',
});
});

it('parses JSON with warnings before it', () => {
const output = 'Warning: Some warning message\n{"status": "Accepted", "id": "789"}';
expect(parseNotarytoolOutput(output)).toEqual({
status: 'Accepted',
id: '789',
});
});

it('parses JSON with warnings after it', () => {
const output = '{"status": "Accepted", "id": "abc"}\nWarning: Some warning message';
expect(parseNotarytoolOutput(output)).toEqual({
status: 'Accepted',
id: 'abc',
});
});

it('parses JSON with warnings before and after it', () => {
const output =
'Warning: First warning\n{"status": "Invalid", "id": "def"}\nWarning: Second warning';
expect(parseNotarytoolOutput(output)).toEqual({
status: 'Invalid',
id: 'def',
});
});

it('throws error for invalid JSON', () => {
const output = 'not json at all';
expect(() => parseNotarytoolOutput(output)).toThrow(
'Could not parse notarytool output: \n\nnot json at all',
);
});

it('throws error for incomplete JSON', () => {
const output = '{"status": "Accepted"';
expect(() => parseNotarytoolOutput(output)).toThrow('Could not parse notarytool output');
});

it('parses nested JSON objects', () => {
const output = '{"status": "Accepted", "data": {"nested": "value"}}';
expect(parseNotarytoolOutput(output)).toEqual({
status: 'Accepted',
data: { nested: 'value' },
});
});
});