bulk-release 2.11.6 → 2.12.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## [2.12.0](https://github.com/semrel-extra/zx-bulk-release/compare/v2.11.7...v2.12.0) (2023-09-19)
2
+
3
+ ### Features
4
+ * feat: provide `tagFormat` configuration ([c405454](https://github.com/semrel-extra/zx-bulk-release/commit/c4054545da55b430e427ab8b8684a305a1dd8360))
5
+
6
+ ## [2.11.7](https://github.com/semrel-extra/zx-bulk-release/compare/v2.11.6...v2.11.7) (2023-09-19)
7
+
8
+ ### Fixes & improvements
9
+ * perf: up deps ([ae0d80a](https://github.com/semrel-extra/zx-bulk-release/commit/ae0d80ac82b3fc49c2fc046b4b52bc17ea24f466))
10
+ * perf: add `npmPersist` helper with debug notice ([3790f4d](https://github.com/semrel-extra/zx-bulk-release/commit/3790f4dbed97e52bc937bedfacac69ce4acb5529))
11
+
1
12
  ## [2.11.6](https://github.com/semrel-extra/zx-bulk-release/compare/v2.11.5...v2.11.6) (2023-09-19)
2
13
 
3
14
  ### Fixes & improvements
package/README.md CHANGED
@@ -199,10 +199,19 @@ Note, [npm-package-name charset](https://www.npmjs.com/package/validate-npm-pack
199
199
  '2022.6.13-examplecom.v1.0.0.ZXhhbXBsZS5jb20-f1'
200
200
  // date name ver b64 format
201
201
  ```
202
+ Anyway, it's still possible to override the default config by `tagFormat` option:
203
+
204
+ | tagFormat | Example |
205
+ |-----------|------------------------------------------------------|
206
+ | f0 | 2022.6.22-qiwi.pijma-native.v1.0.0-beta.0+foo.bar-f0 |
207
+ | f1 | 2022.6.13-examplecom.v1.0.0.ZXhhbXBsZS5jb20-f1 |
208
+ | lerna | @qiwi/pijma-ssr@1.1.12 |
209
+ | pure | 1.2.3-my.package |
210
+
202
211
 
203
212
  ### Meta
204
213
 
205
- Each release stores its result into the `meta` branch.
214
+ Each release pushes its result to the `meta` branch.
206
215
  `2022-6-26-semrel-extra-zxbr-test-c-1-3-1-f0.json`
207
216
  ```json
208
217
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bulk-release",
3
3
  "alias": "bulk-release",
4
- "version": "2.11.6",
4
+ "version": "2.12.0",
5
5
  "description": "zx-based alternative for multi-semantic-release",
6
6
  "type": "module",
7
7
  "exports": {
@@ -23,15 +23,15 @@
23
23
  "docs": "mkdir -p docs && cp ./README.md ./docs/README.md"
24
24
  },
25
25
  "dependencies": {
26
- "@semrel-extra/topo": "^1.13.0",
27
- "cosmiconfig": "^8.2.0",
26
+ "@semrel-extra/topo": "^1.14.0",
27
+ "cosmiconfig": "^8.3.6",
28
28
  "queuefy": "^1.2.1",
29
- "zx-extra": "^2.5.4"
29
+ "zx-extra": "^2.5.5"
30
30
  },
31
31
  "devDependencies": {
32
- "c8": "^8.0.0",
32
+ "c8": "^8.0.1",
33
33
  "uvu": "^0.5.6",
34
- "verdaccio": "^5.25.0"
34
+ "verdaccio": "^5.26.2"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
@@ -22,9 +22,15 @@ export const analyze = async (pkg) => {
22
22
  )
23
23
  pkg.preversion = pre && pkg.version
24
24
  pkg.manifest.version = pkg.version
25
- pkg.tag = releaseType ? formatTag({name: pkg.name, version: pkg.version}) : null
25
+ pkg.tag = releaseType ? formatTag({name: pkg.name, version: pkg.version, format: pkg.config.tagFormat}) : null
26
26
 
27
- log({pkg})('semantic changes', changes, 'nextVersion', pkg.version, 'latestVersion', latestVersion)
27
+ log({pkg})(
28
+ 'semantic changes', changes,
29
+ 'releaseType', releaseType,
30
+ 'prevVersion', latestVersion,
31
+ 'nextVersion', pkg.version,
32
+ 'nextTag', pkg.tag
33
+ )
28
34
  }
29
35
 
30
36
  export const releaseSeverityOrder = ['major', 'minor', 'patch']
@@ -22,9 +22,8 @@ export const pushChangelog = queuefy(async (pkg) => {
22
22
  })
23
23
 
24
24
  export const formatReleaseNotes = async (pkg) => {
25
- const {name, version, absPath: cwd, config: {ghBasicAuth: basicAuth}} = pkg
25
+ const {name, version, tag = formatTag({name, version}), absPath: cwd, config: {ghBasicAuth: basicAuth}} = pkg
26
26
  const {repoPublicUrl} = await getRepo(cwd, {basicAuth})
27
- const tag = formatTag({name, version})
28
27
  const releaseDiffRef = `## [${name}@${version}](${repoPublicUrl}/compare/${pkg.latest.tag?.ref}...${tag}) (${new Date().toISOString().slice(0, 10)})`
29
28
  const releaseDetails = Object.values(pkg.changes
30
29
  .reduce((acc, {group, subj, short, hash}) => {
package/src/main/js/gh.js CHANGED
@@ -14,9 +14,8 @@ export const ghRelease = async (pkg) => {
14
14
  log({pkg})('create gh release')
15
15
 
16
16
  const now = Date.now()
17
- const {name, version, absPath: cwd} = pkg
17
+ const {name, version, absPath: cwd, tag = formatTag({name, version})} = pkg
18
18
  const {repoName} = await getRepo(cwd, {basicAuth})
19
- const tag = formatTag({name, version})
20
19
  const releaseNotes = await formatReleaseNotes(pkg)
21
20
  const releaseData = JSON.stringify({
22
21
  name: tag,
@@ -8,8 +8,7 @@ import {fetchRepo, pushCommit, getTags as getGitTags, pushTag} from './git.js'
8
8
  import {fetchManifest} from './npm.js'
9
9
 
10
10
  export const pushReleaseTag = async (pkg) => {
11
- const {name, version, config: {gitCommitterEmail, gitCommitterName}} = pkg
12
- const tag = formatTag({name, version})
11
+ const {name, version, tag = formatTag({name, version}),config: {gitCommitterEmail, gitCommitterName}} = pkg
13
12
  const cwd = pkg.context.git.root
14
13
 
15
14
  pkg.context.git.tag = tag
@@ -21,8 +20,7 @@ export const pushReleaseTag = async (pkg) => {
21
20
  export const pushMeta = queuefy(async (pkg) => {
22
21
  log({pkg})('push artifact to branch \'meta\'')
23
22
 
24
- const {name, version, absPath: cwd, config: {gitCommitterEmail, gitCommitterName, ghBasicAuth: basicAuth}} = pkg
25
- const tag = formatTag({name, version})
23
+ const {name, version, tag = formatTag({name, version}), absPath: cwd, config: {gitCommitterEmail, gitCommitterName, ghBasicAuth: basicAuth}} = pkg
26
24
  const to = '.'
27
25
  const branch = 'meta'
28
26
  const msg = `chore: release meta ${name} ${version}`
@@ -53,7 +51,10 @@ export const getLatest = async (pkg) => {
53
51
  }
54
52
  }
55
53
 
54
+ const isSafeName = n => /^(@?[a-z0-9-]+\/)?[a-z0-9-]+$/.test(n)
55
+
56
56
  const f0 = {
57
+ name: 'f0',
57
58
  parse(tag) {
58
59
  if (!tag.endsWith('-f0')) return null
59
60
 
@@ -66,10 +67,10 @@ const f0 = {
66
67
  const date = parseDateTag(_date)
67
68
  const name = _name.includes('.') ? `@${_name.replace('.', '/')}` : _name
68
69
 
69
- return {date, name, version, format: 'f0', ref: tag}
70
+ return {date, name, version, format: this.name, ref: tag}
70
71
  },
71
72
  format({name, date = new Date(), version}) {
72
- if (!/^(@?[a-z0-9-]+\/)?[a-z0-9-]+$/.test(name) || !semver.valid(version)) return null
73
+ if (!isSafeName(name) || !semver.valid(version)) return null
73
74
 
74
75
  const d = formatDateTag(date)
75
76
  const n = name.replace('@', '').replace('/', '.')
@@ -79,6 +80,7 @@ const f0 = {
79
80
  }
80
81
 
81
82
  const f1 = {
83
+ name: 'f1',
82
84
  parse(tag) {
83
85
  if (!tag.endsWith('-f1')) return null
84
86
 
@@ -91,7 +93,7 @@ const f1 = {
91
93
  const date = parseDateTag(_date)
92
94
  const name = Buffer.from(b64, 'base64url').toString('utf8')
93
95
 
94
- return {date, name, version, format: 'f1', ref: tag}
96
+ return {date, name, version, format: this.name, ref: tag}
95
97
  },
96
98
  format({name, date = new Date(), version}) {
97
99
  if (!semver.valid(version)) return null
@@ -105,35 +107,72 @@ const f1 = {
105
107
  }
106
108
 
107
109
  const lerna = {
110
+ name: 'lerna',
108
111
  parse(tag) {
109
112
  const pattern = /^(@?[a-z0-9-]+(?:\/[a-z0-9-]+)?)@(v?\d+\.\d+\.\d+.*)/
110
113
  const [, name, version] = pattern.exec(tag) || []
111
114
 
112
115
  if (!semver.valid(version)) return null
113
116
 
114
- return {name, version, format: 'lerna', ref: tag}
117
+ return {name, version, format: this.name, ref: tag}
115
118
  },
116
- // format({name, version}) {
117
- // if (!semver.valid(version)) return null
118
- //
119
- // return `${name}@${version}`
120
- // }
119
+ format({name, version}) {
120
+ if (!semver.valid(version)) return null
121
+
122
+ return `${name}@${version}`
123
+ }
121
124
  }
122
125
 
123
- // TODO
124
- // const variants = [f0, f1]
125
- // export const parseTag = (tag) => {
126
- // for (const variant of variants) {
127
- // const parsed = variant.parse(tag)
128
- // if (parsed) return parsed
129
- // }
130
- //
131
- // return null
132
- // }
126
+ const pure = {
127
+ name: 'pure',
128
+ parse(tag) {
129
+ if (tag.endsWith('-f0') || tag.endsWith('-f1')) {
130
+ return null
131
+ }
132
+ const parsed = semver.parse(tag) || {}
133
+ const {prerelease} = parsed
134
+ if (!prerelease?.length) {
135
+ return null
136
+ }
137
+
138
+ const [n, o = ''] = prerelease.reverse()
139
+ const name = o === 'x' ? n : `@${o}/${n}`
140
+ const version = tag.slice(0, -1 - n.length - (o ? o.length + 1 : 0))
141
+
142
+ return {format: this.name, ref: tag, name, version}
143
+ },
144
+ format({name, version}) {
145
+ const parsed = semver.parse(version)
146
+ if (!parsed || !isSafeName(name)) {
147
+ return null
148
+ }
149
+ const {prerelease} = parsed
150
+ const [n, o] = name.slice(name[0] === '@' ? 1 : 0).split('/').reverse()
151
+ const extra = prerelease.length
152
+ ? '.' + [o || 'x', n].join('.')
153
+ : '-' + [o, n].filter(Boolean).join('.')
154
+ return version + extra
155
+ }
156
+ }
157
+
158
+ const getFormatter = (tagFormat) => {
159
+ if (!tagFormat) {
160
+ return {
161
+ parse() {},
162
+ format() {}
163
+ }
164
+ }
165
+ const formatter = [f0, f1, pure, lerna].find(f => f.name === tagFormat)
166
+ if (!formatter) {
167
+ throw new Error(`Unsupported tag format: ${tagFormat}`)
168
+ }
169
+
170
+ return formatter
171
+ }
133
172
 
134
- export const parseTag = (tag) => f0.parse(tag) || f1.parse(tag) || lerna.parse(tag) || null
173
+ export const parseTag = (tag) => f0.parse(tag) || f1.parse(tag) || lerna.parse(tag) || pure.parse(tag) || null
135
174
 
136
- export const formatTag = (tag) => f0.format(tag) || f1.format(tag) || null
175
+ export const formatTag = (tag, tagFormat = tag.format) => getFormatter(tagFormat).format(tag) || f0.format(tag) || f1.format(tag) || null
137
176
 
138
177
  export const getTags = async (cwd, ref = '') =>
139
178
  (await getGitTags(cwd, ref))
@@ -38,6 +38,18 @@ export const fetchManifest = async (pkg, {nothrow} = {}) => {
38
38
  }
39
39
  }
40
40
 
41
+ export const npmPersist = async (pkg) => {
42
+ const {name, version, manifest, manifestAbsPath} = pkg
43
+ log({pkg})(`updating ${manifestAbsPath} inners: ${name} ${version}`)
44
+ await fs.writeJson(manifestAbsPath, manifest, {spaces: 2})
45
+ }
46
+
47
+ export const npmRestore = async (pkg) => {
48
+ const {manifestRaw, manifestAbsPath} = pkg
49
+ log({pkg})(`rolling back ${manifestAbsPath} inners to manifestRaw`)
50
+ await fs.writeFile(manifestAbsPath, manifestRaw, {encoding: 'utf8'})
51
+ }
52
+
41
53
  export const npmPublish = async (pkg) => {
42
54
  const {absPath: cwd, name, version, manifest, config: {npmPublish, npmRegistry, npmToken, npmConfig, npmProvenance}} = pkg
43
55
 
@@ -1,6 +1,6 @@
1
1
  import os from 'node:os'
2
2
  import {createRequire} from 'node:module'
3
- import {$, fs, within} from 'zx-extra'
3
+ import {$, within} from 'zx-extra'
4
4
  import {queuefy} from 'queuefy'
5
5
  import {analyze} from './analyze.js'
6
6
  import {pushChangelog} from './changelog.js'
@@ -10,7 +10,7 @@ import {ghPages, ghRelease} from './gh.js'
10
10
  import {getRoot, getSha, unsetUserConfig} from './git.js'
11
11
  import {log, createReport} from './log.js'
12
12
  import {getLatest, pushMeta, pushReleaseTag} from './meta.js'
13
- import {fetchPkg, npmPublish} from './npm.js'
13
+ import {fetchPkg, npmPersist, npmPublish, npmRestore} from './npm.js'
14
14
  import {memoizeBy, tpl} from './util.js'
15
15
 
16
16
  export const run = async ({cwd = process.cwd(), env, flags = {}} = {}) => within(async () => {
@@ -153,7 +153,7 @@ const publish = memoizeBy(async (pkg, run = runCmd) => within(async () => {
153
153
  throw new Error('package.json version not synced')
154
154
  }
155
155
 
156
- await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
156
+ await npmPersist(pkg)
157
157
 
158
158
  if (pkg.context.flags.snapshot) {
159
159
  await Promise.all([
@@ -176,7 +176,5 @@ const publish = memoizeBy(async (pkg, run = runCmd) => within(async () => {
176
176
 
177
177
  const clean = async (cwd, packages) => {
178
178
  await unsetUserConfig(cwd)
179
- await Promise.all(Object.values(packages).map(({manifestPath, manifestRaw}) =>
180
- fs.writeFile(manifestPath, manifestRaw, {encoding: 'utf8'})
181
- ))
179
+ await Promise.all(Object.values(packages).map(npmRestore))
182
180
  }