Skip to content

Commit

Permalink
Improve error output
Browse files Browse the repository at this point in the history
  • Loading branch information
fregante committed Jul 27, 2024
1 parent e8e2323 commit 69ee941
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 175 deletions.
169 changes: 2 additions & 167 deletions cli.js
Original file line number Diff line number Diff line change
@@ -1,175 +1,10 @@
#!/usr/bin/env node

import path 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';

const cli = meow(`
Usage
$ chrome-webstore-upload [command]
where [command] can be one of
upload, publish
if the command is missing, it will both upload and publish the extension.
Options
--source Path to either a zip file or a directory to be zipped. Defaults to the value of webExt.sourceDir in package.json or the current directory if not specified
--extension-id The ID of the Chrome Extension (environment variable EXTENSION_ID)
--client-id OAuth2 Client ID (environment variable CLIENT_ID)
--client-secret OAuth2 Client Secret (environment variable CLIENT_SECRET)
--refresh-token OAuth2 Refresh Token (environment variable REFRESH_TOKEN)
--auto-publish Can be used with the "upload" command (deprecated, use \`chrome-webstore-upload\` without a command instead)
--trusted-testers Can be used with the "publish" command
--deploy-percentage Can be used with the "publish" command. Defaults to 100
Examples
Upload and publish a new version, using existing environment variables and the default value for --source
$ chrome-webstore-upload
Upload new extension archive to the Chrome Web Store
$ chrome-webstore-upload upload --source my-custom-zip.zip
Publish the last uploaded version (whether it was uploaded via web UI or via CLI)
$ chrome-webstore-upload publish --extension-id elomekmlfonmdhmpmdfldcjgdoacjcba
`, {
importMeta: import.meta,
flags: {
source: {
type: 'string',
},
},
});

if (cli.input.length > 1) {
console.error('Too many parameters');
cli.showHelp(1);
}

const {
apiConfig,
zipPath,
isUpload,
isPublish,
autoPublish,
trustedTesters,
deployPercentage,
} = await createConfig(cli.input[0], cli.flags);

async function doAutoPublish() {
console.log('Fetching token...');

const token = await fetchToken(apiConfig);
console.log(`Uploading ${path.basename(zipPath)}...`);

const uploadResponse = await upload({
apiConfig,
token,
zipPath,
});

if (!isUploadSuccess(uploadResponse)) {
throw uploadResponse;
}

console.log('Publishing...');
const publishResponse = await publish(
{ apiConfig, token },
trustedTesters && 'trustedTesters',
deployPercentage,
);

handlePublishStatus(publishResponse);
}

async function doUpload() {
console.log(`Uploading ${path.basename(zipPath)}`);
const response = await upload({
apiConfig,
zipPath,
});

if (!isUploadSuccess(response)) {
throw response;
}

console.log('Upload completed');
}

async function doPublish() {
console.log('Publishing');

const response = await publish(
{ apiConfig },
trustedTesters && 'trustedTesters',
deployPercentage,
);

handlePublishStatus(response);
}

function errorHandler(error) {
console.log(error?.response?.body ?? error);
process.exitCode = 1;

if (error?.name === 'HTTPError') {
const response = JSON.parse(error?.response?.body ?? '{}');
const { clientId, refreshToken } = apiConfig;
if (response.error_description === 'The OAuth client was not found.') {
console.error(
'Probably the provided client ID is not valid. Try following the guide again',
);
console.error(
'https://github.com/fregante/chrome-webstore-upload-keys',
);
console.error({ clientId });
return;
}

if (response.error_description === 'Bad Request') {
const { clientId } = apiConfig;
console.error(
'Probably the provided refresh token is not valid. Try following the guide again',
);
console.error(
'https://github.com/fregante/chrome-webstore-upload-keys',
);
console.error({ clientId, refreshToken });
return;
}

if (error?.message === 'Response code 400 (Bad Request)') {
// Nothing else to add
return;
}
}

if (error?.itemError?.length > 0) {
for (const itemError of error.itemError) {
console.error('Error: ' + itemError.error_code);
console.error(itemError.error_detail);
}
}
}

async function init() {
if (isUpload && autoPublish) {
await doAutoPublish();
} else if (isUpload) {
await doUpload();
} else if (isPublish) {
await doPublish();
}
}
import { init, errorHandler } from './core.js';

try {
await init();
} catch (error) {
errorHandler(error);
process.exitCode = 1;
}
14 changes: 6 additions & 8 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ import process from 'node:process';
import findSource from './find-source.js';

export default async function getConfig(command, flags) {
const apiConfig = {
extensionId: flags.extensionId || process.env.EXTENSION_ID,
clientId: flags.clientId || process.env.CLIENT_ID,
clientSecret: flags.clientSecret || process.env.CLIENT_SECRET,
refreshToken: flags.refreshToken || process.env.REFRESH_TOKEN,
};

return {
apiConfig,
apiConfig: {
extensionId: flags.extensionId || process.env.EXTENSION_ID,
clientId: flags.clientId || process.env.CLIENT_ID,
clientSecret: flags.clientSecret || process.env.CLIENT_SECRET,
refreshToken: flags.refreshToken || process.env.REFRESH_TOKEN,
},
zipPath: await findSource(flags.source),
isUpload: command === 'upload' || !command,
isPublish: command === 'publish',
Expand Down
161 changes: 161 additions & 0 deletions core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import path from 'node:path';
import meow from 'meow';
import { upload, publish, fetchToken } from './wrapper.js';
import {
isUploadSuccess,
handlePublishStatus,
} from './util.js';
import createConfig from './config.js';

let config;

async function doAutoPublish({ apiConfig, trustedTesters, deployPercentage, zipPath }) {
console.log('Fetching token...');

const token = await fetchToken(apiConfig);
console.log(`Uploading ${path.basename(zipPath)}...`);

const uploadResponse = await upload({
apiConfig,
token,
zipPath,
});

if (!isUploadSuccess(uploadResponse)) {
throw uploadResponse;
}

console.log('Publishing...');
const publishResponse = await publish(
{ apiConfig, token },
trustedTesters && 'trustedTesters',
deployPercentage,
);
handlePublishStatus(publishResponse);
}

async function doUpload({ apiConfig, zipPath }) {
console.log(`Uploading ${path.basename(zipPath)}`);
const response = await upload({
apiConfig,
zipPath,
});

if (!isUploadSuccess(response)) {
throw response;
}

console.log('Upload completed');
}

async function doPublish({ apiConfig, trustedTesters, deployPercentage }) {
console.log('Publishing');

const response = await publish(
{ apiConfig },
trustedTesters && 'trustedTesters',
deployPercentage,
);
handlePublishStatus(response);
}

export function errorHandler(error) {
console.log('❌', error?.response?.body ?? error?.message ?? error);

if (error?.name === 'HTTPError') {
const response = JSON.parse(error?.response?.body ?? '{}');
const { clientId, refreshToken } = config.apiConfig;
if (response.error_description === 'The OAuth client was not found.') {
console.error(
'Probably the provided client ID is not valid. Try following the guide again',
);
console.error(
'https://github.com/fregante/chrome-webstore-upload-keys',
);
console.error({ clientId });
return;
}

if (response.error_description === 'Bad Request') {
const { clientId } = config.apiConfig;
console.error(
'Probably the provided refresh token is not valid. Try following the guide again',
);
console.error(
'https://github.com/fregante/chrome-webstore-upload-keys',
);
console.error({ clientId, refreshToken });
return;
}

if (error?.message === 'Response code 400 (Bad Request)') {
// Nothing else to add
return;
}
}

if (error?.itemError?.length > 0) {
for (const itemError of error.itemError) {
console.error('Error: ' + itemError.error_code);
console.error(itemError.error_detail);
}
}
}

export async function init() {
const cli = meow(`
Usage
$ chrome-webstore-upload [command]
where [command] can be one of
upload, publish
if the command is missing, it will both upload and publish the extension.
Options
--source Path to either a zip file or a directory to be zipped. Defaults to the value of webExt.sourceDir in package.json or the current directory if not specified
--extension-id The ID of the Chrome Extension (environment variable EXTENSION_ID)
--client-id OAuth2 Client ID (environment variable CLIENT_ID)
--client-secret OAuth2 Client Secret (environment variable CLIENT_SECRET)
--refresh-token OAuth2 Refresh Token (environment variable REFRESH_TOKEN)
--auto-publish Can be used with the "upload" command (deprecated, use \`chrome-webstore-upload\` without a command instead)
--trusted-testers Can be used with the "publish" command
--deploy-percentage Can be used with the "publish" command. Defaults to 100
Examples
Upload and publish a new version, using existing environment variables and the default value for --source
$ chrome-webstore-upload
Upload new extension archive to the Chrome Web Store
$ chrome-webstore-upload upload --source my-custom-zip.zip
Publish the last uploaded version (whether it was uploaded via web UI or via CLI)
$ chrome-webstore-upload publish --extension-id elomekmlfonmdhmpmdfldcjgdoacjcba
`, {
importMeta: import.meta,
flags: {
source: {
type: 'string',
},
},
});

if (cli.input.length > 1) {
console.error('Too many parameters');
cli.showHelp(1);
}

const command = cli.input[0];

config = await createConfig(command, cli.flags);

const { isUpload, isPublish, autoPublish } = config;

if (isUpload && autoPublish) {
await doAutoPublish(config);
} else if (isUpload) {
await doUpload(config);
} else if (isPublish) {
await doPublish(config);
}
}

0 comments on commit 69ee941

Please sign in to comment.