Skip to content
Merged
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
22 changes: 2 additions & 20 deletions source/cli.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#!/usr/bin/env node

import nodePath from 'node:path';
import process from 'node:process';
import meow from 'meow';
import createConfig from './config.js';
import { upload, publish, fetchToken } from './wrapper.js';
import { isUploadSuccess, handlePublishStatus } from './util.js';
import { handleError } from './error-handler.js';

const cli = meow(`
Usage
Expand Down Expand Up @@ -121,24 +121,6 @@ async function doPublish() {
handlePublishStatus(response);
}

function errorHandler(error) {
process.exitCode = 1;

// Handle CWSError from chrome-webstore-upload package
if (error?.name === 'CWSError') {
console.error(`❌ ${error.message}`);
console.error('Does the dev console require changes?');
console.error('https://chrome.google.com/webstore/devconsole');
console.error('');
console.error('Did you follow the guide to generate the keys?');
console.error('https://github.com/fregante/chrome-webstore-upload-keys');
return;
}

// Fallback for unknown error types
console.log(error);
}

async function init() {
if (isUpload && autoPublish) {
await doAutoPublish();
Expand All @@ -152,5 +134,5 @@ async function init() {
try {
await init();
} catch (error) {
errorHandler(error);
handleError(error);
}
81 changes: 81 additions & 0 deletions source/error-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import process from 'node:process';

export function extractDetailLines(details) {
const detailItems = Array.isArray(details)
? details
: (details ? [details] : []);
const lines = [];

for (const detail of detailItems) {
if (typeof detail === 'string') {
const message = detail.trim();

if (message) {
lines.push(message);
}

continue;
}

if (!detail || typeof detail !== 'object') {
continue;
}

if (Array.isArray(detail.fieldViolations)) {
for (const violation of detail.fieldViolations) {
if (!violation || typeof violation !== 'object') {
continue;
}

const field = typeof violation.field === 'string' ? violation.field.trim() : '';
const description = typeof violation.description === 'string' ? violation.description.trim() : '';
const reason = typeof violation.reason === 'string' ? violation.reason.trim() : '';
const message = [description, reason].filter(Boolean).join(' ');

if (!message) {
continue;
}

lines.push(field ? `${field}: ${message}` : message);
}

continue;
}

if (typeof detail.message === 'string') {
lines.push(detail.message);
continue;
}

if (typeof detail.reason === 'string') {
lines.push(detail.reason);
Comment on lines +45 to +51
}
}

return [...new Set(lines)];
}

export function handleError(error) {
process.exitCode = 1;

if (error?.name === 'CWSError') {
console.error(`❌ ${error.message}`);

const detailLines = extractDetailLines(error.details);
if (detailLines.length > 0) {
console.error('Details:');
for (const line of detailLines) {
console.error(`- ${line}`);
}
}
Comment on lines +64 to +70

console.error('Does the dev console require changes?');
console.error('https://chrome.google.com/webstore/devconsole');
console.error('');
console.error('Did you follow the guide to generate the keys?');
console.error('https://github.com/fregante/chrome-webstore-upload-keys');
return;
}

console.log(error);
}
109 changes: 82 additions & 27 deletions source/error-handler.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import process from 'node:process';
import test from 'ava';
import { stubConsoleLog } from '../test/helpers/stubs.js';
import { extractDetailLines, handleError } from './error-handler.js';

test('CWSError displays message with ❌ prefix and helpful links', t => {
const logs = [];
Expand All @@ -26,15 +27,7 @@ test('CWSError displays message with ❌ prefix and helpful links', t => {

process.exitCode = 0;

// Simulate error handling
if (error?.name === 'CWSError') {
console.error(`❌ ${error.message}`);
console.error('Does the dev console require changes?');
console.error('https://chrome.google.com/webstore/devconsole');
console.error('');
console.error('Did you follow the guide to generate the keys?');
console.error('https://github.com/fregante/chrome-webstore-upload-keys');
}
handleError(error);

restoreLog();
restoreError();
Expand All @@ -46,6 +39,9 @@ test('CWSError displays message with ❌ prefix and helpful links', t => {
t.is(errors[3], '');
t.is(errors[4], 'Did you follow the guide to generate the keys?');
t.is(errors[5], 'https://github.com/fregante/chrome-webstore-upload-keys');

t.is(process.exitCode, 1);
process.exitCode = 0;
});

test('CWSError with publish-related message shows both links', t => {
Expand All @@ -68,15 +64,7 @@ test('CWSError with publish-related message shows both links', t => {

process.exitCode = 0;

// Simulate error handling
if (error?.name === 'CWSError') {
console.error(`❌ ${error.message}`);
console.error('Does the dev console require changes?');
console.error('https://chrome.google.com/webstore/devconsole');
console.error('');
console.error('Did you follow the guide to generate the keys?');
console.error('https://github.com/fregante/chrome-webstore-upload-keys');
}
handleError(error);

restoreError();

Expand All @@ -87,6 +75,9 @@ test('CWSError with publish-related message shows both links', t => {
t.is(errors[3], '');
t.is(errors[4], 'Did you follow the guide to generate the keys?');
t.is(errors[5], 'https://github.com/fregante/chrome-webstore-upload-keys');

t.is(process.exitCode, 1);
process.exitCode = 0;
});

test('CWSError with any message shows both links', t => {
Expand All @@ -109,15 +100,7 @@ test('CWSError with any message shows both links', t => {

process.exitCode = 0;

// Simulate error handling
if (error?.name === 'CWSError') {
console.error(`❌ ${error.message}`);
console.error('Does the dev console require changes?');
console.error('https://chrome.google.com/webstore/devconsole');
console.error('');
console.error('Did you follow the guide to generate the keys?');
console.error('https://github.com/fregante/chrome-webstore-upload-keys');
}
handleError(error);

restoreError();

Expand All @@ -128,5 +111,77 @@ test('CWSError with any message shows both links', t => {
t.is(errors[3], '');
t.is(errors[4], 'Did you follow the guide to generate the keys?');
t.is(errors[5], 'https://github.com/fregante/chrome-webstore-upload-keys');

t.is(process.exitCode, 1);
process.exitCode = 0;
});

test('extractDetailLines returns BadRequest field violations', t => {
const details = [
{
'@type': 'type.googleapis.com/google.rpc.BadRequest',
fieldViolations: [
{
field: 'media',
description: 'The manifest has an invalid version: 0.0.0.',
reason: 'PKG_MANIFEST_PARSE_ERROR',
},
],
},
];

t.deepEqual(extractDetailLines(details), [
'media: The manifest has an invalid version: 0.0.0. PKG_MANIFEST_PARSE_ERROR',
]);
});

test('extractDetailLines returns localized messages and reasons', t => {
const details = [
{
'@type': 'type.googleapis.com/google.rpc.LocalizedMessage',
message: 'The uploaded package was invalid.',
},
{
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
reason: 'INVALID_PACKAGE',
},
];

t.deepEqual(extractDetailLines(details), [
'The uploaded package was invalid.',
'INVALID_PACKAGE',
]);
});

test('extractDetailLines ignores empty entries', t => {
const details = [
null,
{
'@type': 'type.googleapis.com/google.rpc.BadRequest',
fieldViolations: [
{
field: 'media',
description: ' ',
},
],
},
{
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
},
];

t.deepEqual(extractDetailLines(details), []);
});

test('extractDetailLines includes non-empty string details', t => {
const details = [
' Additional detail line ',
'',
'Second line',
];

t.deepEqual(extractDetailLines(details), [
'Additional detail line',
'Second line',
]);
});