zx-bulk-release 1.7.8 → 1.9.1

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,21 @@
1
+ ## [1.9.1](https://github.com/semrel-extra/zx-bulk-release/compare/v1.9.0...v1.9.1) (2022-06-26)
2
+
3
+ ### Fixes & improvements
4
+ * refactor: replace postbuild hook with postupdate ([b7d68a5](https://github.com/semrel-extra/zx-bulk-release/commit/b7d68a5a2b9dec6516204f3fcbbc3c08db9c6d90))
5
+
6
+ ## [1.9.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.8.0...v1.9.0) (2022-06-26)
7
+
8
+ ### Features
9
+ * feat: add gh-pages push ([32130b6](https://github.com/semrel-extra/zx-bulk-release/commit/32130b64a2088cc98b1cae678691cd55c18fe0a3))
10
+
11
+ ## [1.8.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.7.8...v1.8.0) (2022-06-26)
12
+
13
+ ### Features
14
+ * feat: add cosmiconfig ([1fe694d](https://github.com/semrel-extra/zx-bulk-release/commit/1fe694d2c919a029eb4aa5d5f500b287a2035608))
15
+
16
+ ### Fixes & improvements
17
+ * docs: formatting ([d2bac88](https://github.com/semrel-extra/zx-bulk-release/commit/d2bac8893d33f4c9ce6c003c4f7188ea56452023))
18
+
1
19
  ## [1.7.8](https://github.com/semrel-extra/zx-bulk-release/compare/v1.7.7...v1.7.8) (2022-06-26)
2
20
 
3
21
  ### Fixes & improvements
package/README.md CHANGED
@@ -3,25 +3,51 @@
3
3
 
4
4
  🚧 Work in progress. Early access preview
5
5
 
6
+ ## Roadmap
7
+ * [x] [Conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#specification) trigger semantic releases.
8
+ * [x] Predictable [toposort](https://githib.com/semrel-extra/topo)-driven flow.
9
+ * [x] No blocking (no release commits).
10
+ * [ ] Changelogs, docs, bundles go to: release assets and/or meta branch.
11
+ * [x] No extra builds. The required deps are fetched from the pkg registry.
12
+
6
13
  ## Requirements
7
14
  * macOS / linux
8
15
  * Node.js >= 16.0.0
9
- * yarn >= 4.0.0-rc.5
16
+ * npm >=7 / yarn >= 3
10
17
 
11
18
  ## Usage
19
+ ### CLI
12
20
  ```shell
13
21
  GH_TOKEN=ghtoken GH_USER=username NPM_TOKEN=npmtoken npx zx-bulk-release [opts]
14
22
  ```
15
23
 
16
- ## Roadmap
17
- * [x] [Conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#specification) trigger semantic releases.
18
- * [x] Predictable [toposort](https://githib.com/semrel-extra/topo)-driven flow.
19
- * [x] No blocking (no release commits).
20
- * [ ] Changelogs, docs, bundles go to: release assets and/or meta branch.
21
- * [x] No extra builds. The required deps are fetched from the pkg registry.
24
+ ### JS API
25
+ ```js
26
+ import { run } from 'zx-bulk-release'
27
+
28
+ const cwd = '/foo/bar'
29
+ const env = {GH_TOKEN: 'foo', NPM_TOKEN: 'bar'}
30
+ const flags = {dryRun: true}
22
31
 
23
- ## Tags
24
- [Lerna](https://github.com/lerna/lerna) tags (like `@pkg/name@v1.0.0-beta.0`) are suitable for monorepos, but they don’t follow [semver spec](https://semver.org/). Therefore, we propose another contract:
32
+ await run({
33
+ cwd, // Defaults to process.cwd()
34
+ flags, // Defaults to process.env
35
+ env // Defaults to minimist-parsed `process.argv.slice(2)`
36
+ })
37
+ ```
38
+
39
+ ### Config
40
+ Any [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) compliant format: `.releaserc`, `.release.json`, `.release.yaml`, etc.
41
+ ```yaml
42
+ buildCmd: 'yarn build'
43
+ postbuildCmd: 'yarn install'
44
+ testCmd: 'yarn test'
45
+ fetch: true
46
+ ```
47
+
48
+ ## Implementation notes
49
+ ### Tags
50
+ [Lerna](https://github.com/lerna/lerna) tags (like `@pkg/name@v1.0.0-beta.0`) are suitable for monorepos, but they don’t follow [semver spec](https://semver.org/). Therefore, we propose another contract:
25
51
  ```js
26
52
  '2022.6.13-optional-org.pkg-name.v1.0.0-beta.1+sha.1-f0'
27
53
  // date name version format
@@ -32,18 +58,7 @@ Note, [npm-package-name charset](https://www.npmjs.com/package/validate-npm-pack
32
58
  // date name ver b64 format
33
59
  ```
34
60
 
35
- ## JS API
36
- ```js
37
- import { run } from 'zx-bulk-release'
38
-
39
- const cwd = '/foo/bar' // Defaults to process.cwd()
40
- const env = { GH_TOKEN: 'foo', NPM_TOKEN: 'bar' } // Defaults to process.env
41
- const flags = {dryRun: true}
42
-
43
- await run({cwd, flags, env})
44
- ```
45
-
46
- ## env vars
61
+ ### env vars
47
62
  ```js
48
63
  export const parseEnv = (env = process.env) => {
49
64
  const {GH_USER, GH_USERNAME, GITHUB_USER, GITHUB_USERNAME, GH_TOKEN, GITHUB_TOKEN, NPM_TOKEN, NPM_REGISTRY, NPMRC, NPM_USERCONFIG, NPM_CONFIG_USERCONFIG, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL} = env
@@ -61,6 +76,23 @@ export const parseEnv = (env = process.env) => {
61
76
  }
62
77
  ```
63
78
 
79
+ ### Meta
80
+
81
+ Each release projects its result into the `meta` branch.
82
+ `2022-6-26-semrel-extra-zxbr-test-c-1-3-1-f0.json`
83
+ ```json
84
+ {
85
+ "META_VERSION": "1",
86
+ "name": "@semrel-extra/zxbr-test-c",
87
+ "hash": "07b7df33f0159f674c940bd7bbb2652cdaef5207",
88
+ "version": "1.3.1",
89
+ "dependencies": {
90
+ "@semrel-extra/zxbr-test-a": "^1.4.0",
91
+ "@semrel-extra/zxbr-test-d": "~1.2.0"
92
+ }
93
+ }
94
+ ```
95
+
64
96
  ## References
65
97
  * [semrel-extra/zx-semrel](https://github.com/semrel-extra/zx-semrel)
66
98
  * [dhoulb/multi-semantic-release](https://github.com/dhoulb/multi-semantic-release)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zx-bulk-release",
3
- "version": "1.7.8",
3
+ "version": "1.9.1",
4
4
  "description": "zx-based alternative for multi-semantic-release",
5
5
  "type": "module",
6
6
  "exports": "./src/main/js/index.js",
@@ -12,9 +12,10 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@semrel-extra/topo": "^1.4.1",
15
+ "cosmiconfig": "^7.0.1",
15
16
  "git-glob-cp": "^1.7.1",
16
17
  "ini": "^3.0.0",
17
- "zx-extra": "^1.7.0"
18
+ "zx-extra": "^1.7.1"
18
19
  },
19
20
  "devDependencies": {
20
21
  "c8": "^7.11.3",
@@ -9,21 +9,23 @@ export const build = (pkg, packages) => ctx(async ($) => {
9
9
 
10
10
  await traverseDeps(pkg, packages, async (_, {pkg}) => build(pkg, packages))
11
11
 
12
- if (pkg.manifest.scripts?.build && pkg.manifest?.release?.build) {
13
- if (pkg.changes?.length === 0 && pkg.manifest?.release?.fetch) await fetchPkg(pkg)
12
+ const {config} = pkg
13
+ $.cwd = pkg.absPath
14
14
 
15
- if (!pkg.fetched) {
16
- $.cwd = pkg.absPath
15
+ if (config.buildCmd) {
16
+ if (pkg.changes?.length === 0 && config.fetch) await fetchPkg(pkg)
17
17
 
18
- await $`yarn build`
18
+ if (!pkg.fetched) {
19
+ await $.raw`${config.buildCmd}`
19
20
  console.log(`built '${pkg.name}'`)
21
+ }
22
+
23
+ // if (config.postbuildCmd) await $.raw`${config.postbuildCmd}`
20
24
 
21
- if (pkg.manifest.scripts?.test && pkg.manifest?.release?.test) {
22
- await $`yarn test`
23
- console.log(`tested '${pkg.name}'`)
24
- }
25
+ if (!pkg.fetched && config.testCmd) {
26
+ await $.raw`${config.testCmd}`
27
+ console.log(`tested '${pkg.name}'`)
25
28
  }
26
- await $`yarn install`
27
29
  }
28
30
 
29
31
  pkg.built = true
@@ -0,0 +1,27 @@
1
+ import { cosmiconfig } from 'cosmiconfig'
2
+
3
+ const CONFIG_NAME = 'release'
4
+ const CONFIG_FILES = [
5
+ 'package.json',
6
+ `.${CONFIG_NAME}rc`,
7
+ `.${CONFIG_NAME}rc.json`,
8
+ `.${CONFIG_NAME}rc.yaml`,
9
+ `.${CONFIG_NAME}rc.yml`,
10
+ `.${CONFIG_NAME}rc.js`,
11
+ `.${CONFIG_NAME}rc.cjs`,
12
+ `${CONFIG_NAME}.config.js`,
13
+ `${CONFIG_NAME}.config.cjs`
14
+ ]
15
+
16
+ export const defaultConfig = {
17
+ buildCmd: 'yarn build',
18
+ postbuildCmd: 'yarn install',
19
+ testCmd: 'yarn test',
20
+ fetch: true
21
+ }
22
+
23
+ export const getConfig = async (...cwds) =>
24
+ (await Promise.all(cwds.map(
25
+ cwd => cosmiconfig(CONFIG_NAME, { searchPlaces: CONFIG_FILES }).search(cwd).then(r => r?.config)
26
+ ))).find(Boolean) || defaultConfig
27
+
@@ -1,9 +1,10 @@
1
- import {fs, ctx} from 'zx-extra'
1
+ import {fs, $} from 'zx-extra'
2
2
  import {topo} from '@semrel-extra/topo'
3
3
  import {updateDeps} from './deps.js'
4
4
  import {getSemanticChanges, resolvePkgVersion} from './analyze.js'
5
5
  import {publish, getLatest} from './publish.js'
6
6
  import {build} from './build.js'
7
+ import {getConfig} from './config.js'
7
8
 
8
9
  export const run = async ({cwd = process.cwd(), env = process.env, flags = {}} = {}) => {
9
10
  const {packages, queue, root} = await topo({cwd})
@@ -11,6 +12,7 @@ export const run = async ({cwd = process.cwd(), env = process.env, flags = {}} =
11
12
 
12
13
  for (let name of queue) {
13
14
  const pkg = packages[name]
15
+ pkg.config = await getConfig(pkg.absPath, root.absPath)
14
16
  pkg.latest = await getLatest(cwd, name)
15
17
 
16
18
  const semanticChanges = await getSemanticChanges(pkg.absPath, pkg.latest.tag?.ref)
@@ -24,6 +26,7 @@ export const run = async ({cwd = process.cwd(), env = process.env, flags = {}} =
24
26
  if (changes.length === 0) continue
25
27
  console.log(`semantic changes of '${name}'`, changes)
26
28
 
29
+ $.o({cwd: pkg.absPath})`yarn install`
27
30
  pkg.build = await build(pkg, packages, cwd)
28
31
 
29
32
  if (dryRun) continue
@@ -7,6 +7,7 @@ export const publish = async (pkg) => {
7
7
  await pushMeta(pkg)
8
8
  await npmPublish(pkg)
9
9
  await createGhRelease(pkg)
10
+ await ghPages(pkg)
10
11
  }
11
12
 
12
13
  export const pushTag = (pkg) => ctx(async ($) => {
@@ -98,6 +99,22 @@ ${commits.join('\n')}`).join('\n')
98
99
  await $`curl -u ${ghUser}:${ghToken} -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${repoName}/releases -d ${releaseData}`
99
100
  })
100
101
 
102
+ const ghPages = async (pkg) => {
103
+ const {config} = pkg
104
+ if (!config.ghPages) return
105
+
106
+ console.log('publish to gh-pages')
107
+ const [from, branch = 'gh-pages', to = '.'] = config.ghPages.split(' ')
108
+
109
+ await push({
110
+ cwd: path.resolve(pkg.absPath, from),
111
+ from: '.',
112
+ to,
113
+ branch,
114
+ msg: 'docs update'
115
+ })
116
+ }
117
+
101
118
  const branches = {}
102
119
  export const fetch = async ({cwd: _cwd, branch, origin: _origin}) => ctx(async ($) => {
103
120
  let cwd = branches[branch]
@@ -127,14 +144,20 @@ export const push = async ({cwd, from, to, branch, origin, msg, ignoreFiles, fil
127
144
  const _contents = typeof contents === 'string' ? contents : JSON.stringify(contents, null, 2)
128
145
  await fs.outputFile(path.resolve(_cwd, to, relpath), _contents)
129
146
  }
130
- if (from) await copydir({baseFrom: cwd, from, baseTo: _cwd, to, ignoreFiles})
147
+ if (from) await copydir({baseFrom: cwd, from, baseTo: _cwd, to, ignoreFiles, cwd})
131
148
 
132
149
  $.cwd = _cwd
133
150
 
134
151
  await $`git config user.name ${gitCommitterEmail}`
135
152
  await $`git config user.email ${gitCommitterName}`
136
153
  await $`git add .`
137
- await $`git commit -m ${msg}`
154
+ try {
155
+ await $`git commit -m ${msg}`
156
+ } catch {
157
+ console.warn('no changes')
158
+ return
159
+ }
160
+
138
161
  await $.raw`git push origin HEAD:refs/heads/${branch}`
139
162
  })
140
163
 
@@ -49,9 +49,10 @@ const cwd = await createFakeRepo({
49
49
  test: "node ./index.js"
50
50
  },
51
51
  release: {
52
- build: true,
53
- fetch: true,
54
- test: true
52
+ buildCmd: 'yarn build',
53
+ postbuildCmd: 'yarn install',
54
+ testCmd: 'yarn test',
55
+ fetch: true
55
56
  },
56
57
  exports: {
57
58
  '.': {
@@ -107,13 +108,15 @@ const cwd = await createFakeRepo({
107
108
  a: 'workspace:^'
108
109
  },
109
110
  scripts: {
110
- build: 'cp index.js bundle.js',
111
+ build: 'cp index.js bundle.js && mkdir -p docs && echo "# docs" > docs/readme.md',
111
112
  test: "node ./index.js"
112
113
  },
113
114
  release: {
114
- build: true,
115
+ buildCmd: 'yarn build',
116
+ postbuildCmd: 'yarn install',
117
+ testCmd: 'yarn test',
115
118
  fetch: true,
116
- test: true
119
+ ghPages: 'docs gh-pages b'
117
120
  },
118
121
  exports: {
119
122
  '.': {
@@ -207,9 +210,13 @@ test('run()', async () => {
207
210
 
208
211
  const origin = (await $`git remote get-url origin`).toString().trim()
209
212
  const meta = tempy.temporaryDirectory()
213
+ const ghp = tempy.temporaryDirectory()
210
214
 
211
215
  await $`git clone --single-branch --branch meta --depth 1 ${origin} ${meta}`
212
216
  assert.is((await fs.readJson(`${meta}/${tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')}.json`)).version, '1.1.0')
217
+
218
+ await $`git clone --single-branch --branch gh-pages --depth 1 ${origin} ${ghp}`
219
+ assert.is((await fs.readFile(`${ghp}/b/readme.md`, 'utf-8')).trim(), '# docs')
213
220
  })
214
221
 
215
222
  await registry.stop()