@jsenv/package-publish 1.7.0 → 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/{LICENSE → license}
RENAMED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/package-publish",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
4
4
|
"description": "Publish package to one or many registry.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "https://github.com/jsenv/
|
|
13
|
+
"url": "https://github.com/jsenv/workflow",
|
|
14
|
+
"directory": "packages/jsenv-package-publish"
|
|
14
15
|
},
|
|
15
16
|
"engines": {
|
|
16
17
|
"node": ">=16.13.0"
|
|
@@ -32,27 +33,16 @@
|
|
|
32
33
|
"/main.js"
|
|
33
34
|
],
|
|
34
35
|
"scripts": {
|
|
35
|
-
"eslint": "
|
|
36
|
-
"importmap": "node ./script/importmap/importmap.mjs",
|
|
36
|
+
"eslint": "npx eslint . --ext=.js,.mjs",
|
|
37
37
|
"test": "node --experimental-json-modules ./script/test/test.mjs",
|
|
38
38
|
"test-with-coverage": "npm run test -- --coverage",
|
|
39
39
|
"prettier": "prettier --write ."
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@jsenv/filesystem": "
|
|
42
|
+
"@jsenv/filesystem": "3.1.0",
|
|
43
43
|
"@jsenv/logger": "4.0.1",
|
|
44
|
-
"
|
|
44
|
+
"node-fetch": "2.6.7",
|
|
45
|
+
"@jsenv/log": "1.5.0",
|
|
45
46
|
"semver": "7.3.5"
|
|
46
|
-
},
|
|
47
|
-
"devDependencies": {
|
|
48
|
-
"@jsenv/assert": "2.4.1",
|
|
49
|
-
"@jsenv/core": "25.3.0",
|
|
50
|
-
"@jsenv/eslint-config": "16.0.9",
|
|
51
|
-
"@jsenv/github-release-package": "1.3.0",
|
|
52
|
-
"@jsenv/importmap-eslint-resolver": "5.2.5",
|
|
53
|
-
"@jsenv/importmap-node-module": "5.1.0",
|
|
54
|
-
"eslint": "8.7.0",
|
|
55
|
-
"eslint-plugin-import": "2.25.4",
|
|
56
|
-
"prettier": "2.5.1"
|
|
57
47
|
}
|
|
58
48
|
}
|
package/{README.md → readme.md}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Package publish [](https://www.npmjs.com/package/@jsenv/package-publish)
|
|
1
|
+
# Package publish [](https://www.npmjs.com/package/@jsenv/package-publish)
|
|
2
2
|
|
|
3
3
|
Publish package to one or many registry.
|
|
4
4
|
|
|
@@ -13,8 +13,6 @@ Screenshot taken inside a github workflow when the package.json version is alrea
|
|
|
13
13
|
|
|
14
14
|
Screenshot taken inside a github workflow when the package.json version is not published: 
|
|
15
15
|
|
|
16
|
-
This package is using itself to be published on NPM. It is done during ["publish package" step](https://github.com/jsenv/package-publish/blob/0170a5c859c4732203ff2f3e70b85e705396ccc7/.github/workflows/main.yml#L70-L74) in GitHub worflow.
|
|
17
|
-
|
|
18
16
|
# Installation
|
|
19
17
|
|
|
20
18
|
```console
|
|
@@ -31,7 +29,7 @@ _publishPackage_ is an async function publishing a package on one or many regist
|
|
|
31
29
|
import { publishPackage } from "@jsenv/package-publish"
|
|
32
30
|
|
|
33
31
|
const publishReport = await publishPackage({
|
|
34
|
-
|
|
32
|
+
rootDirectoryUrl: new URL('./', import.meta.url)
|
|
35
33
|
registriesConfig: {
|
|
36
34
|
"https://registry.npmjs.org": {
|
|
37
35
|
token: process.env.NPM_TOKEN,
|
|
@@ -43,9 +41,9 @@ const publishReport = await publishPackage({
|
|
|
43
41
|
})
|
|
44
42
|
```
|
|
45
43
|
|
|
46
|
-
##
|
|
44
|
+
## rootDirectoryUrl
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
_rootDirectoryUrl_ parameter is a string leading to a directory containing the package.json.
|
|
49
47
|
|
|
50
48
|
This parameter is **required**.
|
|
51
49
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// https://github.com/npm/registry-issue-archive/issues/34
|
|
3
3
|
// https://stackoverflow.com/questions/53212849/querying-information-about-specific-version-of-scoped-npm-package
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import nodeFetch from "node-fetch"
|
|
6
6
|
|
|
7
7
|
export const fetchLatestInRegistry = async ({
|
|
8
8
|
registryUrl,
|
|
@@ -10,13 +10,17 @@ export const fetchLatestInRegistry = async ({
|
|
|
10
10
|
token,
|
|
11
11
|
}) => {
|
|
12
12
|
const requestUrl = `${registryUrl}/${packageName}`
|
|
13
|
-
const response = await
|
|
13
|
+
const response = await nodeFetch(requestUrl, {
|
|
14
14
|
method: "GET",
|
|
15
15
|
headers: {
|
|
16
16
|
// "user-agent": "jsenv",
|
|
17
17
|
accept:
|
|
18
18
|
"application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
|
|
19
|
-
...(token
|
|
19
|
+
...(token
|
|
20
|
+
? {
|
|
21
|
+
authorization: `token ${token}`,
|
|
22
|
+
}
|
|
23
|
+
: {}),
|
|
20
24
|
},
|
|
21
25
|
})
|
|
22
26
|
const responseStatus = response.status
|
package/src/internal/publish.js
CHANGED
|
@@ -4,152 +4,170 @@ import {
|
|
|
4
4
|
urlToFileSystemPath,
|
|
5
5
|
readFile,
|
|
6
6
|
writeFile,
|
|
7
|
+
removeEntry,
|
|
7
8
|
} from "@jsenv/filesystem"
|
|
9
|
+
import { UNICODE } from "@jsenv/log"
|
|
8
10
|
|
|
9
11
|
import { setNpmConfig } from "./setNpmConfig.js"
|
|
10
12
|
|
|
11
13
|
export const publish = async ({
|
|
12
14
|
logger,
|
|
15
|
+
packageSlug,
|
|
13
16
|
logNpmPublishOutput,
|
|
14
|
-
|
|
17
|
+
rootDirectoryUrl,
|
|
15
18
|
registryUrl,
|
|
16
19
|
token,
|
|
17
20
|
}) => {
|
|
18
|
-
|
|
19
|
-
const promises = []
|
|
20
|
-
const previousValue = process.env.NODE_AUTH_TOKEN
|
|
21
|
-
const restoreProcessEnv = () => {
|
|
22
|
-
process.env.NODE_AUTH_TOKEN = previousValue
|
|
23
|
-
}
|
|
24
|
-
process.env.NODE_AUTH_TOKEN = token
|
|
25
|
-
const projectPackageFileUrl = resolveUrl(
|
|
26
|
-
"./package.json",
|
|
27
|
-
projectDirectoryUrl,
|
|
28
|
-
)
|
|
29
|
-
const projectPackageString = await readFile(projectPackageFileUrl)
|
|
30
|
-
const restoreProjectPackageFile = () =>
|
|
31
|
-
writeFile(projectPackageFileUrl, projectPackageString)
|
|
32
|
-
const projectPackageObject = JSON.parse(projectPackageString)
|
|
33
|
-
projectPackageObject.publishConfig =
|
|
34
|
-
projectPackageObject.publishConfig || {}
|
|
35
|
-
projectPackageObject.publishConfig.registry = registryUrl
|
|
36
|
-
promises.push(
|
|
37
|
-
writeFile(
|
|
38
|
-
projectPackageFileUrl,
|
|
39
|
-
JSON.stringify(projectPackageObject, null, " "),
|
|
40
|
-
),
|
|
41
|
-
)
|
|
42
|
-
const projectNpmConfigFileUrl = resolveUrl("./.npmrc", projectDirectoryUrl)
|
|
43
|
-
let projectNpmConfigString
|
|
21
|
+
const getResult = async () => {
|
|
44
22
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} else {
|
|
50
|
-
throw e
|
|
23
|
+
const promises = []
|
|
24
|
+
const previousValue = process.env.NODE_AUTH_TOKEN
|
|
25
|
+
const restoreProcessEnv = () => {
|
|
26
|
+
process.env.NODE_AUTH_TOKEN = previousValue
|
|
51
27
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
28
|
+
process.env.NODE_AUTH_TOKEN = token
|
|
29
|
+
const rootPackageFileUrl = resolveUrl("./package.json", rootDirectoryUrl)
|
|
30
|
+
const rootPackageString = await readFile(rootPackageFileUrl)
|
|
31
|
+
const restorePackageFile = () =>
|
|
32
|
+
writeFile(rootPackageFileUrl, rootPackageString)
|
|
33
|
+
const packageObject = JSON.parse(rootPackageString)
|
|
34
|
+
packageObject.publishConfig = packageObject.publishConfig || {}
|
|
35
|
+
packageObject.publishConfig.registry = registryUrl
|
|
36
|
+
promises.push(
|
|
37
|
+
writeFile(
|
|
38
|
+
rootPackageFileUrl,
|
|
39
|
+
JSON.stringify(packageObject, null, " "),
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
const npmConfigFileUrl = resolveUrl("./.npmrc", rootDirectoryUrl)
|
|
43
|
+
let restoreNpmConfigFile
|
|
44
|
+
let projectNpmConfigString
|
|
45
|
+
try {
|
|
46
|
+
projectNpmConfigString = await readFile(npmConfigFileUrl)
|
|
47
|
+
restoreNpmConfigFile = () =>
|
|
48
|
+
writeFile(npmConfigFileUrl, projectNpmConfigString)
|
|
49
|
+
} catch (e) {
|
|
50
|
+
if (e.code === "ENOENT") {
|
|
51
|
+
restoreNpmConfigFile = () => removeEntry(npmConfigFileUrl)
|
|
52
|
+
projectNpmConfigString = ""
|
|
53
|
+
} else {
|
|
54
|
+
throw e
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
promises.push(
|
|
58
|
+
writeFile(
|
|
59
|
+
npmConfigFileUrl,
|
|
60
|
+
setNpmConfig(projectNpmConfigString, {
|
|
61
|
+
[computeRegistryTokenKey(registryUrl)]: token,
|
|
62
|
+
[computeRegistryKey(packageObject.name)]: registryUrl,
|
|
63
|
+
}),
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
await Promise.all(promises)
|
|
67
|
+
try {
|
|
68
|
+
return await new Promise((resolve, reject) => {
|
|
69
|
+
const command = exec(
|
|
70
|
+
"npm publish",
|
|
71
|
+
{
|
|
72
|
+
cwd: urlToFileSystemPath(rootDirectoryUrl),
|
|
73
|
+
stdio: "silent",
|
|
74
|
+
},
|
|
75
|
+
(error) => {
|
|
76
|
+
if (error) {
|
|
77
|
+
// publish conflict generally occurs because servers
|
|
78
|
+
// returns 200 after npm publish
|
|
79
|
+
// but returns previous version if asked immediatly
|
|
80
|
+
// after for the last published version.
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
// TODO: ideally we should catch 404 error returned from npm
|
|
83
|
+
// it happens it the token is not allowed to publish
|
|
84
|
+
// a repository. And when we detect this we display a more useful message
|
|
85
|
+
// suggesting the token rights are insufficient to publish the package
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
87
|
+
// npm publish conclit
|
|
88
|
+
if (error.message.includes("EPUBLISHCONFLICT")) {
|
|
89
|
+
resolve({
|
|
90
|
+
success: true,
|
|
91
|
+
reason: "already-published",
|
|
92
|
+
})
|
|
93
|
+
} else if (
|
|
94
|
+
error.message.includes("Cannot publish over existing version")
|
|
95
|
+
) {
|
|
96
|
+
resolve({
|
|
97
|
+
success: true,
|
|
98
|
+
reason: "already-published",
|
|
99
|
+
})
|
|
100
|
+
} else if (
|
|
101
|
+
error.message.includes(
|
|
102
|
+
"You cannot publish over the previously published versions",
|
|
103
|
+
)
|
|
104
|
+
) {
|
|
105
|
+
resolve({
|
|
106
|
+
success: true,
|
|
107
|
+
reason: "already-published",
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
// github publish conflict
|
|
111
|
+
else if (
|
|
112
|
+
error.message.includes(
|
|
113
|
+
"ambiguous package version in package.json",
|
|
114
|
+
)
|
|
115
|
+
) {
|
|
116
|
+
resolve({
|
|
117
|
+
success: true,
|
|
118
|
+
reason: "already-published",
|
|
119
|
+
})
|
|
120
|
+
} else {
|
|
121
|
+
reject(error)
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
114
124
|
resolve({
|
|
115
125
|
success: true,
|
|
116
|
-
reason: "
|
|
126
|
+
reason: "published",
|
|
117
127
|
})
|
|
118
|
-
} else {
|
|
119
|
-
reject(error)
|
|
120
128
|
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
if (logNpmPublishOutput) {
|
|
132
|
+
command.stdout.on("data", (data) => {
|
|
133
|
+
logger.debug(data)
|
|
134
|
+
})
|
|
135
|
+
command.stderr.on("data", (data) => {
|
|
136
|
+
// debug because this output is part of
|
|
137
|
+
// the error message generated by a failing npm publish
|
|
138
|
+
logger.debug(data)
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
} finally {
|
|
143
|
+
await Promise.all([
|
|
144
|
+
restoreProcessEnv(),
|
|
145
|
+
restorePackageFile(),
|
|
146
|
+
restoreNpmConfigFile(),
|
|
147
|
+
])
|
|
148
|
+
}
|
|
149
|
+
} catch (e) {
|
|
150
|
+
return {
|
|
151
|
+
success: false,
|
|
152
|
+
reason: e,
|
|
153
|
+
}
|
|
146
154
|
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
155
|
+
}
|
|
156
|
+
const { success, reason } = await getResult()
|
|
157
|
+
if (success) {
|
|
158
|
+
if (reason === "already-published") {
|
|
159
|
+
logger.info(
|
|
160
|
+
`${UNICODE.INFO} ${packageSlug} was already published on ${registryUrl}`,
|
|
161
|
+
)
|
|
162
|
+
} else {
|
|
163
|
+
logger.info(`${UNICODE.OK} ${packageSlug} published on ${registryUrl}`)
|
|
151
164
|
}
|
|
165
|
+
} else {
|
|
166
|
+
logger.error(`${UNICODE.FAILURE} error when publishing ${packageSlug} in ${registryUrl}
|
|
167
|
+
--- error stack ---
|
|
168
|
+
${reason.stack}`)
|
|
152
169
|
}
|
|
170
|
+
return { success, reason }
|
|
153
171
|
}
|
|
154
172
|
|
|
155
173
|
const computeRegistryTokenKey = (registryUrl) => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { resolveUrl, urlToFileSystemPath, readFile } from "@jsenv/filesystem"
|
|
2
2
|
|
|
3
|
-
export const readProjectPackage = async ({
|
|
4
|
-
const packageFileUrl = resolveUrl("./package.json",
|
|
3
|
+
export const readProjectPackage = async ({ rootDirectoryUrl }) => {
|
|
4
|
+
const packageFileUrl = resolveUrl("./package.json", rootDirectoryUrl)
|
|
5
5
|
let packageObject
|
|
6
6
|
try {
|
|
7
|
-
const packageString = await readFile(packageFileUrl)
|
|
7
|
+
const packageString = await readFile(packageFileUrl, { as: "string" })
|
|
8
8
|
try {
|
|
9
9
|
packageObject = JSON.parse(packageString)
|
|
10
10
|
} catch (e) {
|
package/src/publishPackage.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
|
|
16
16
|
export const publishPackage = async ({
|
|
17
17
|
logLevel,
|
|
18
|
-
|
|
18
|
+
rootDirectoryUrl,
|
|
19
19
|
registriesConfig,
|
|
20
20
|
logNpmPublishOutput = true,
|
|
21
21
|
updateProcessExitCode = true,
|
|
@@ -23,17 +23,17 @@ export const publishPackage = async ({
|
|
|
23
23
|
const logger = createLogger({ logLevel })
|
|
24
24
|
logger.debug(
|
|
25
25
|
`publishPackage(${JSON.stringify(
|
|
26
|
-
{
|
|
26
|
+
{ rootDirectoryUrl, logLevel, registriesConfig },
|
|
27
27
|
null,
|
|
28
28
|
" ",
|
|
29
29
|
)})`,
|
|
30
30
|
)
|
|
31
|
-
|
|
31
|
+
rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl)
|
|
32
32
|
assertRegistriesConfig(registriesConfig)
|
|
33
33
|
|
|
34
34
|
logger.debug(`reading project package.json`)
|
|
35
35
|
const packageInProject = await readProjectPackage({
|
|
36
|
-
|
|
36
|
+
rootDirectoryUrl,
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
const { name: packageName, version: packageVersion } = packageInProject
|
|
@@ -138,29 +138,15 @@ ${actionReason.stack}`)
|
|
|
138
138
|
|
|
139
139
|
const { success, reason } = await publish({
|
|
140
140
|
logger,
|
|
141
|
+
packageSlug: `${packageName}@${packageVersion}`,
|
|
141
142
|
logNpmPublishOutput,
|
|
142
|
-
|
|
143
|
+
rootDirectoryUrl,
|
|
143
144
|
registryUrl,
|
|
144
145
|
...registriesConfig[registryUrl],
|
|
145
146
|
})
|
|
146
147
|
registryReport.actionResult = { success, reason }
|
|
147
|
-
if (success) {
|
|
148
|
-
|
|
149
|
-
logger.info(
|
|
150
|
-
`${packageName}@${packageVersion} was already published on ${registryUrl}`,
|
|
151
|
-
)
|
|
152
|
-
} else {
|
|
153
|
-
logger.info(
|
|
154
|
-
`${packageName}@${packageVersion} published on ${registryUrl}`,
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
logger.error(`error when publishing ${packageName}@${packageVersion} in ${registryUrl}
|
|
159
|
-
--- error stack ---
|
|
160
|
-
${reason.stack}`)
|
|
161
|
-
if (updateProcessExitCode) {
|
|
162
|
-
process.exitCode = 1
|
|
163
|
-
}
|
|
148
|
+
if (!success && updateProcessExitCode) {
|
|
149
|
+
process.exitCode = 1
|
|
164
150
|
}
|
|
165
151
|
}, Promise.resolve())
|
|
166
152
|
|