Skip to content

Commit 2d54ab8

Browse files
committed
fix: improve error messages when there's a generic google error (#95)
BREAKING CHANGE: the `verifyConditions` step now requires a `extensionId` to be passed in its configs. See the README for an updated config example.
1 parent c8189a3 commit 2d54ab8

File tree

6 files changed

+174
-40
lines changed

6 files changed

+174
-40
lines changed

README.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ This package export the following plugins:
4040

4141
Verify the presence of the authentication parameters, which are set via environment variables (see [Chrome webstore authentication][chrome-authentication]).
4242

43+
#### `verifyConditions` parameters
44+
45+
- `extensionId`: **REQUIRED** parameter. The `extension id` from the webstore. For example: If the url of your extension is [https://chrome.google.com/webstore/detail/webplayer-hotkeys-shortcu/ikmkicnmahfdilneilgibeppbnolgkaf](https://chrome.google.com/webstore/detail/webplayer-hotkeys-shortcu/ikmkicnmahfdilneilgibeppbnolgkaf), then the last portion, `ikmkicnmahfdilneilgibeppbnolgkaf`, will be the `extension id`. You can also take this ID on the [developers dashboard](https://chrome.google.com/webstore/developer/dashboard), under the name `Item ID` located inside the `More info` dialog. This is used so that we can confirm that the credentials are working for the extension you are trying to publish.
46+
4347
### `prepare`
4448

4549
Writes the correct version to the `manifest.json` and creates a `zip` file with everything inside the `dist` folder.
@@ -58,14 +62,13 @@ This plugin requires some parameters to be set, so be sure to check below and fi
5862

5963
Uploads the generated zip file to the webstore and publishes a new release.
6064

65+
Unfortunately, due to Google's restrictions, this plugin can only publish extensions that already exists on the store, so you will have to at least make a draft release for yourself, so the plugin can create a proper release for the first time. You can create a draft release with just a minimum `manifest.json` with version `0.0.1` compressed in a zip file.
66+
If you decide to make the draft, make sure to fill all the required fields on the drafts page, otherwise the publish will fail with a `400` status code (Bad request).
67+
6168
#### `publish` parameters
6269

6370
- `extensionId`: **REQUIRED** parameter. The `extension id` from the webstore. For example: If the url of your extension is [https://chrome.google.com/webstore/detail/webplayer-hotkeys-shortcu/ikmkicnmahfdilneilgibeppbnolgkaf](https://chrome.google.com/webstore/detail/webplayer-hotkeys-shortcu/ikmkicnmahfdilneilgibeppbnolgkaf), then the last portion, `ikmkicnmahfdilneilgibeppbnolgkaf`, will be the `extension id`. You can also take this ID on the [developers dashboard](https://chrome.google.com/webstore/developer/dashboard), under the name `Item ID` located inside the `More info` dialog.
6471

65-
Unfortunately, due to Google's restrictions, this plugin can only publish extensions that already exists on the store, so you will have to at least make a draft release for yourself, so the plugin can create a proper release for the first time. You can create a draft release with just a minimum `manifest.json` with version `0.0.1` compressed in a zip file.
66-
67-
If you decide to make the draft, make sure to fill all the required fields on the drafts page, otherwise the publishing will fail with a `400` status code (Bad request).
68-
6972
- `asset`: **REQUIRED** parameter. The zip file that will be published to the chrome webstore.
7073

7174
- `target`: Valid options are:
@@ -85,7 +88,13 @@ A basic configuration file example is available below:
8588

8689
```json
8790
{
88-
"verifyConditions": ["semantic-release-chrome", "@semantic-release/github"],
91+
"verifyConditions": [
92+
{
93+
"path": "semantic-release-chrome",
94+
"extensionId": "mppjhhbajcciljocgbadbhbgphjfdmhj"
95+
},
96+
"@semantic-release/github"
97+
],
8998
"prepare": [
9099
{
91100
"path": "semantic-release-chrome",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
},
4949
"dependencies": {
5050
"@semantic-release/error": "3.0.0",
51+
"aggregate-error": "4.0.1",
5152
"archiver": "5.3.1",
5253
"chrome-webstore-upload": "1.0.0",
5354
"fs-extra": "10.1.0"

src/getEsModule.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const modulesCache: { [keyof: string]: any } = {}
2+
3+
async function getEsModule(moduleName: string) {
4+
if (modulesCache[moduleName]) {
5+
return modulesCache[moduleName]
6+
}
7+
8+
const module = (await import(moduleName)).default
9+
modulesCache[moduleName] = module
10+
11+
return module
12+
}
13+
14+
export default getEsModule

src/publish.ts

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,10 @@ import { createReadStream } from 'fs-extra'
33
import { Context } from 'semantic-release'
44

55
import type PluginConfig from './@types/pluginConfig'
6+
import getEsModule from './getEsModule'
67

78
const errorWhitelist = ['PUBLISHED_WITH_FRICTION_WARNING']
89

9-
const modulesCache: { [keyof: string]: any } = {}
10-
11-
const getEsModule = async (module: string) => {
12-
if (modulesCache[module]) {
13-
return modulesCache[module]
14-
}
15-
16-
const esModule = await import(module)
17-
modulesCache[module] = esModule.default || esModule
18-
19-
return modulesCache[module]
20-
}
21-
2210
const publish = async (
2311
{ extensionId, target, asset }: PluginConfig,
2412
{ logger }: Context,
@@ -43,49 +31,103 @@ const publish = async (
4331
)
4432
}
4533

46-
const webStore = await (
47-
await getEsModule('chrome-webstore-upload')
48-
)({
34+
const chromeWebstoreUpload = (await getEsModule(
35+
'chrome-webstore-upload',
36+
)) as typeof import('chrome-webstore-upload')['default']
37+
38+
const webStore = (await chromeWebstoreUpload({
4939
clientId,
5040
clientSecret,
5141
extensionId,
5242
refreshToken,
53-
})
43+
})) as typeof import('chrome-webstore-upload')['default']
44+
45+
logger.log('Creating zip file...')
5446

5547
const zipFile = createReadStream(asset)
56-
const uploadRes = await webStore.uploadExisting(zipFile)
48+
const errorMessage = `
49+
50+
[ERROR] Semantic Release Chrome
51+
52+
Unfortunately we can't tell for sure what's the reason for that, but usually this happens when there's something wrong or missing with the configs.
53+
Make sure to check:
54+
55+
* The extensionId is correctly set on the plugin config
56+
* The environment variables GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REFRESH_TOKEN are correctly set (Double check the authentication guide: https://github.com/GabrielDuarteM/semantic-release-chrome/blob/master/Authentication.md)
57+
* Go to https://chrome.google.com/webstore/devconsole, click on the extension you are publishing, and verify that there's no errors there (for example, on the "Why can't I submit" button modal, or on any of the other tabs there)
58+
59+
`
60+
let uploadRes
61+
62+
logger.log('Uploading zip file to Google Web Store...')
63+
try {
64+
uploadRes = await webStore.uploadExisting(zipFile)
65+
} catch (err) {
66+
throw new SemanticReleaseError(
67+
`Error uploading extension to Google Web Store. ${errorMessage} Error details:\n\n${err}`,
68+
err as string,
69+
)
70+
}
71+
72+
const AggregateError = (await getEsModule(
73+
'aggregate-error',
74+
)) as typeof import('aggregate-error')['default']
5775

58-
if (uploadRes.uploadState === 'FAILURE') {
76+
if (uploadRes?.uploadState === 'FAILURE') {
5977
const errors: SemanticReleaseError[] = []
78+
6079
uploadRes.itemError.forEach((err: any) => {
6180
const semanticError = new SemanticReleaseError(
6281
err.error_detail,
6382
err.error_code,
6483
)
84+
6585
errors.push(semanticError)
6686
})
67-
throw new AggregateError(errors).errors
87+
88+
throw new AggregateError(errors)
6889
}
6990

91+
logger.log(`Successfully uploaded extension to Google Web Store`)
92+
7093
if (target !== 'draft') {
71-
const publishRes = await webStore.publish(target || 'default')
94+
logger.log('Publishing extension to Google Web Store...')
7295

73-
if (!publishRes.status.includes('OK')) {
96+
let publishRes
97+
98+
try {
99+
publishRes = await webStore.publish(target || 'default')
100+
} catch (err) {
101+
throw new SemanticReleaseError(
102+
err as string,
103+
`Error publishing extension to Google Web Store. ${errorMessage} Error details:\n\n`,
104+
)
105+
}
106+
107+
if (!publishRes?.status.includes('OK')) {
74108
const errors: SemanticReleaseError[] = []
109+
75110
for (let i = 0; i < publishRes.status.length; i += 1) {
76111
const code = publishRes.status[i]
77112
const message = publishRes.statusDetail[i]
113+
78114
if (errorWhitelist.includes(code)) {
79115
logger.log(`${code}: ${message}`)
80116
} else {
81117
const err = new SemanticReleaseError(message, code)
118+
82119
errors.push(err)
83120
}
84121
}
122+
85123
if (errors.length > 0) {
86-
throw new AggregateError(errors).errors
124+
throw new AggregateError(errors)
87125
}
88126
}
127+
128+
logger.log(`Successfully published extension to Google Web Store`)
129+
} else {
130+
logger.log(`Target option is set to "draft", skipping publish step`)
89131
}
90132

91133
return {

src/verifyConditions.ts

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,78 @@
11
import SemanticReleaseError from '@semantic-release/error'
2+
import { Context } from 'semantic-release'
3+
import PluginConfig from './@types/pluginConfig'
4+
import getEsModule from './getEsModule'
25

36
const configMessage = 'Check the README.md for config info.'
47

5-
const createErrorPATH = (param: string, code: string) =>
8+
const createErrorEnvFile = (param: string, code: string) =>
69
new SemanticReleaseError(
7-
`No ${param} specified inside PATH. ${configMessage}`,
10+
`Environment variable not found: ${param}. ${configMessage}`,
811
code,
912
)
1013

11-
const verifyConditions = () => {
12-
const {
13-
GOOGLE_CLIENT_ID,
14-
GOOGLE_CLIENT_SECRET,
15-
GOOGLE_REFRESH_TOKEN,
16-
} = process.env
14+
const verifyConditions = async (
15+
{ extensionId }: PluginConfig,
16+
{ logger }: Context,
17+
) => {
18+
const { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKEN } =
19+
process.env
1720
const errors: Error[] = []
1821

1922
if (!GOOGLE_CLIENT_ID) {
20-
errors.push(createErrorPATH('GOOGLE_CLIENT_ID', 'EGOOGLECLIENTID'))
23+
errors.push(createErrorEnvFile('GOOGLE_CLIENT_ID', 'EGOOGLECLIENTID'))
2124
}
2225

2326
if (!GOOGLE_CLIENT_SECRET) {
24-
errors.push(createErrorPATH('GOOGLE_CLIENT_SECRET', 'EGOOGLECLIENTSECRET'))
27+
errors.push(
28+
createErrorEnvFile('GOOGLE_CLIENT_SECRET', 'EGOOGLECLIENTSECRET'),
29+
)
2530
}
2631

2732
if (!GOOGLE_REFRESH_TOKEN) {
28-
errors.push(createErrorPATH('GOOGLE_REFRESH_TOKEN', 'EGOOGLEREFRESHTOKEN'))
33+
errors.push(
34+
createErrorEnvFile('GOOGLE_REFRESH_TOKEN', 'EGOOGLEREFRESHTOKEN'),
35+
)
36+
}
37+
38+
if (!extensionId) {
39+
errors.push(
40+
new SemanticReleaseError(
41+
"Option 'extensionId' was not included in the verifyConditions config. Check the README.md for config info.",
42+
'ENOEXTENSIONID',
43+
),
44+
)
2945
}
3046

3147
if (errors.length > 0) {
32-
throw new AggregateError(errors).errors
48+
const AggregateError = (await getEsModule(
49+
'aggregate-error',
50+
)) as typeof import('aggregate-error')['default']
51+
52+
throw new AggregateError(errors)
53+
}
54+
55+
const chromeWebstoreUpload = (await getEsModule(
56+
'chrome-webstore-upload',
57+
)) as typeof import('chrome-webstore-upload')['default']
58+
59+
const webStore = await chromeWebstoreUpload({
60+
extensionId,
61+
clientId: GOOGLE_CLIENT_ID,
62+
clientSecret: GOOGLE_CLIENT_SECRET,
63+
refreshToken: GOOGLE_REFRESH_TOKEN,
64+
})
65+
66+
let mainErrorMsg: string | undefined
67+
68+
try {
69+
logger.log('Verifying chrome webstore credentials...')
70+
await webStore.get()
71+
logger.log('Chrome webstore credentials seem to be valid.')
72+
} catch (e) {
73+
mainErrorMsg =
74+
'\n[semantic-release-chrome] Could not connect to Chrome Web Store with the provided credentials. Please check if they are correct.'
75+
throw new Error(mainErrorMsg)
3376
}
3477
}
3578

yarn.lock

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,6 +1624,14 @@ agentkeepalive@^4.2.1:
16241624
depd "^1.1.2"
16251625
humanize-ms "^1.2.1"
16261626

1627+
aggregate-error@4.0.1:
1628+
version "4.0.1"
1629+
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-4.0.1.tgz#25091fe1573b9e0be892aeda15c7c66a545f758e"
1630+
integrity sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==
1631+
dependencies:
1632+
clean-stack "^4.0.0"
1633+
indent-string "^5.0.0"
1634+
16271635
aggregate-error@^3.0.0:
16281636
version "3.1.0"
16291637
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
@@ -2035,6 +2043,13 @@ clean-stack@^2.0.0:
20352043
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
20362044
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
20372045

2046+
clean-stack@^4.0.0:
2047+
version "4.2.0"
2048+
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-4.2.0.tgz#c464e4cde4ac789f4e0735c5d75beb49d7b30b31"
2049+
integrity sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==
2050+
dependencies:
2051+
escape-string-regexp "5.0.0"
2052+
20382053
cli-columns@^4.0.0:
20392054
version "4.0.0"
20402055
resolved "https://registry.yarnpkg.com/cli-columns/-/cli-columns-4.0.0.tgz#9fe4d65975238d55218c41bd2ed296a7fa555646"
@@ -2456,6 +2471,11 @@ escalade@^3.1.1:
24562471
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
24572472
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
24582473

2474+
escape-string-regexp@5.0.0:
2475+
version "5.0.0"
2476+
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
2477+
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
2478+
24592479
escape-string-regexp@^1.0.5:
24602480
version "1.0.5"
24612481
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -3067,6 +3087,11 @@ indent-string@^4.0.0:
30673087
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
30683088
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
30693089

3090+
indent-string@^5.0.0:
3091+
version "5.0.0"
3092+
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5"
3093+
integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==
3094+
30703095
infer-owner@^1.0.4:
30713096
version "1.0.4"
30723097
resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"

0 commit comments

Comments
 (0)