zx-bulk-release 1.5.0 → 1.6.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,12 @@
1
+ ## [1.6.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.5.0...v1.6.0) (2022-06-25)
2
+
3
+ ### Features
4
+ * feat: add lazy builder ([4c59a0e](https://github.com/semrel-extra/zx-bulk-release/commit/4c59a0e61aea19cccd66228f34100d733bc3d3d8))
5
+ * feat: provide pkg building ([04ac6ce](https://github.com/semrel-extra/zx-bulk-release/commit/04ac6cee878aa8cfa329b16348735ba809544bea))
6
+
7
+ ### Fixes & improvements
8
+ * refactor: reuse `traverseDeps` ([626de1c](https://github.com/semrel-extra/zx-bulk-release/commit/626de1c9250bce831aa9ec6298077bd5b91e06b0))
9
+
1
10
  ## [1.5.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.4.2...v1.5.0) (2022-06-21)
2
11
 
3
12
  ### Features
package/package.json CHANGED
@@ -1,23 +1,25 @@
1
1
  {
2
2
  "name": "zx-bulk-release",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "zx-based alternative for multi-semantic-release",
5
5
  "type": "module",
6
6
  "exports": "./src/main/js/index.js",
7
7
  "bin": "./src/main/js/cli.js",
8
8
  "scripts": {
9
9
  "test": "NPM_REGISTRY='http://localhost:4873' NPM_TOKEN='mRv6eIuiaggXGb9ZDFCtBA==' c8 uvu ./src/test -i fixtures -i utils && c8 report -r lcov",
10
+ "test:it": "NPM_REGISTRY='http://localhost:4873' NPM_TOKEN='mRv6eIuiaggXGb9ZDFCtBA==' node ./src/test/js/integration.test.js",
10
11
  "docs": "mkdir -p docs && cp ./README.md ./docs/README.md"
11
12
  },
12
13
  "dependencies": {
13
14
  "@semrel-extra/topo": "^1.4.1",
14
- "git-glob-cp": "^1.5.1",
15
+ "git-glob-cp": "^1.7.1",
16
+ "ini": "^3.0.0",
15
17
  "zx-extra": "^1.7.0"
16
18
  },
17
19
  "devDependencies": {
18
20
  "c8": "^7.11.3",
19
- "uvu": "^0.5.3",
20
- "verdaccio": "^5.13.0"
21
+ "uvu": "^0.5.4",
22
+ "verdaccio": "^5.13.1"
21
23
  },
22
24
  "publishConfig": {
23
25
  "access": "public"
@@ -0,0 +1,52 @@
1
+ import {ctx, tempy, fs} from 'zx-extra'
2
+ import {copy} from 'git-glob-cp'
3
+ import ini from 'ini'
4
+ import {traverseDeps} from './deps.js'
5
+ import {parseEnv} from './publish.js'
6
+
7
+ export const build = (pkg, packages) => ctx(async ($) => {
8
+ if (pkg.built) return true
9
+
10
+ await traverseDeps(pkg, packages, async (_, {pkg}) => build(pkg, packages))
11
+
12
+ if (pkg.manifest.scripts?.build && pkg.manifest?.release?.build) {
13
+ if (pkg.changes?.length === 0 && pkg.manifest?.release?.fetch) await fetchPkg(pkg)
14
+
15
+ if (!pkg.fetched) {
16
+ $.cwd = pkg.absPath
17
+ await $`npm run build`
18
+ console.log(`built '${pkg.name}'`)
19
+ }
20
+ }
21
+
22
+ pkg.built = true
23
+
24
+ return true
25
+ })
26
+
27
+ const fetchPkg = (pkg) => ctx(async ($) => {
28
+ try {
29
+ const cwd = pkg.absPath
30
+ const {npmRegistry} = parseEnv($.env)
31
+ const temp = tempy.temporaryDirectory()
32
+ const npmrc = ini.parse(await fs.readFile(`${cwd}/.npmrc`, 'utf8'))
33
+ const token = getAuthToken(npmRegistry, npmrc)
34
+ const auth = `Authorization: Bearer ${token}`
35
+ const tarball = getTarballUrl(npmRegistry, pkg.name, pkg.version)
36
+
37
+ await $`wget --header=${auth} -qO- ${tarball} | tar xvz -C ${temp}`
38
+ await copy({from: ['**/*', '!package.json'], to: cwd, cwd: `${temp}/package`})
39
+
40
+ pkg.fetched = true
41
+ console.log(`fetched '${pkg.name}@${pkg.version}'`)
42
+ } catch (e) {
43
+ console.log(`fetching '${pkg.name}@${pkg.version}' failed`, e)
44
+ }
45
+ })
46
+
47
+ // NOTE registry-auth-token does not work with localhost:4873
48
+ const getAuthToken = (registry, npmrc) =>
49
+ (Object.entries(npmrc).find(([reg]) => reg.startsWith(registry.replace(/^https?/, ''))) || [])[1]
50
+
51
+ // $`npm view ${name}@${version} dist.tarball`
52
+ const getTarballUrl = (registry, name, version) => `${registry}/${name}/-/${name.replace(/^.+(%2f|\/)/,'')}-${version}.tgz`
@@ -2,9 +2,8 @@ import {semver} from 'zx-extra'
2
2
 
3
3
  export const depScopes = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']
4
4
 
5
- export const updateDeps = async (pkg, packages) => {
5
+ export const traverseDeps = async (pkg, packages, fn) => {
6
6
  const {manifest} = pkg
7
- const changes = []
8
7
 
9
8
  for (let scope of depScopes) {
10
9
  const deps = manifest[scope]
@@ -13,24 +12,32 @@ export const updateDeps = async (pkg, packages) => {
13
12
  for (let [name, version] of Object.entries(deps)) {
14
13
  if (!packages[name]) continue
15
14
 
16
- const prev = pkg.latest.meta?.[scope]?.[name]
17
- const actual = packages[name]?.version
18
- const next = resolveVersion(version, actual, prev)
15
+ await fn(pkg, {name, version, deps, scope, pkg: packages[name]})
16
+ }
17
+ }
18
+ }
19
19
 
20
- pkg[scope] = {...pkg[scope], [name]: next || version}
20
+ export const updateDeps = async (pkg, packages) => {
21
+ const changes = []
21
22
 
22
- if (!next) continue
23
+ await traverseDeps(pkg, packages, async (_, {name, version, deps, scope, pkg: dep}) => {
24
+ const prev = pkg.latest.meta?.[scope]?.[name]
25
+ const actual = dep?.version
26
+ const next = resolveVersion(version, actual, prev)
23
27
 
24
- deps[name] = next
28
+ pkg[scope] = {...pkg[scope], [name]: next || version}
25
29
 
26
- changes.push({
27
- group: 'Dependencies',
28
- releaseType: 'patch',
29
- change: 'perf',
30
- subj: `perf: ${name} updated to ${next}`,
31
- })
32
- }
33
- }
30
+ if (!next) return
31
+
32
+ deps[name] = next
33
+
34
+ changes.push({
35
+ group: 'Dependencies',
36
+ releaseType: 'patch',
37
+ change: 'perf',
38
+ subj: `perf: ${name} updated to ${next}`,
39
+ })
40
+ })
34
41
 
35
42
  return changes
36
43
  }
@@ -3,8 +3,10 @@ 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
+ import {build} from './build.js'
6
7
 
7
8
  export const run = async ({cwd = process.cwd(), env = process.env, flags = {}} = {}) => {
9
+ console.log('Run zx bulk release')
8
10
  const {packages, queue} = await topo({cwd})
9
11
  const dryRun = flags['dry-run'] || flags.dryRun
10
12
 
@@ -23,6 +25,7 @@ export const run = async ({cwd = process.cwd(), env = process.env, flags = {}} =
23
25
  if (changes.length === 0) continue
24
26
  console.log(`semantic changes of '${name}'`, changes)
25
27
 
28
+ pkg.build = await build(pkg, packages)
26
29
  if (dryRun) continue
27
30
 
28
31
  await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
@@ -4,7 +4,7 @@ import * as assert from 'uvu/assert'
4
4
  import {ctx, tempy, fs} from 'zx-extra'
5
5
  import {run} from '../../main/js/index.js'
6
6
  import {formatTag} from '../../main/js/tag.js'
7
- import {createFakeRepo, createNpmRegistry} from './test-utils.js'
7
+ import {addCommits, createFakeRepo, createNpmRegistry} from './test-utils.js'
8
8
 
9
9
  const test = suite('integration')
10
10
 
@@ -32,7 +32,14 @@ const cwd = await createFakeRepo({
32
32
  {
33
33
  relpath: './packages/a/package.json',
34
34
  contents: {
35
- name: 'a'
35
+ name: 'a',
36
+ scripts: {
37
+ build: 'echo "building a"'
38
+ },
39
+ release: {
40
+ build: true,
41
+ fetch: true
42
+ }
36
43
  }
37
44
  }
38
45
  ]
@@ -74,6 +81,10 @@ const cwd = await createFakeRepo({
74
81
  name: 'b',
75
82
  dependencies: {
76
83
  a: '1.0.0'
84
+ },
85
+ release: {
86
+ build: true,
87
+ fetch: true
77
88
  }
78
89
  }
79
90
  }
@@ -110,7 +121,6 @@ test('run()', async () => {
110
121
 
111
122
  assert.is(digestA['dist-tags'].latest, '1.0.1')
112
123
  assert.is(digestA.dist.tarball, 'http://localhost:4873/a/-/a-1.0.1.tgz')
113
-
114
124
  assert.is(digestB['dist-tags'].latest, '1.0.0')
115
125
  assert.is(digestB.dist.tarball, 'http://localhost:4873/b/-/b-1.0.0.tgz')
116
126
 
@@ -124,6 +134,40 @@ test('run()', async () => {
124
134
  assert.is((await fs.readJson(`${meta}/${tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')}.json`)).version, '1.0.1')
125
135
  })
126
136
 
137
+ await addCommits({cwd, commits: [
138
+ {
139
+ msg: 'feat(b): add feat',
140
+ files: [
141
+ {
142
+ relpath: './packages/b/feat.txt',
143
+ contents: 'new feat'
144
+ }
145
+ ]
146
+ }
147
+ ]})
148
+
149
+ await run({cwd})
150
+
151
+ await ctx(async ($) => {
152
+ $.cwd = cwd
153
+ const digestA = JSON.parse((await $`npm view a --registry=${registry.address} --json`).toString())
154
+ const digestB = JSON.parse((await $`npm view b --registry=${registry.address} --json`).toString())
155
+
156
+ assert.is(digestA['dist-tags'].latest, '1.0.1')
157
+ assert.is(digestA.dist.tarball, 'http://localhost:4873/a/-/a-1.0.1.tgz')
158
+ assert.is(digestB['dist-tags'].latest, '1.1.0')
159
+ assert.is(digestB.dist.tarball, 'http://localhost:4873/b/-/b-1.1.0.tgz')
160
+
161
+ const tag = formatTag({name: 'b', version: '1.1.0'})
162
+ assert.is((await $`git for-each-ref refs/tags/${tag} --format='%(contents)'`).toString().trim(), tag)
163
+
164
+ const origin = (await $`git remote get-url origin`).toString().trim()
165
+ const meta = tempy.temporaryDirectory()
166
+
167
+ await $`git clone --single-branch --branch meta --depth 1 ${origin} ${meta}`
168
+ assert.is((await fs.readJson(`${meta}/${tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')}.json`)).version, '1.1.0')
169
+ })
170
+
127
171
  await registry.stop()
128
172
  })
129
173
 
@@ -30,20 +30,7 @@ export const createFakeRepo = async ({cwd = tempy.temporaryDirectory(), commits
30
30
  $.cwd = cwd
31
31
  await $`git init`
32
32
 
33
- for (let {msg, files, name = 'Semrel-extra Bot', email = 'semrel-extra-bot@hotmail.com', tags = []} of commits) {
34
- await $`git config user.name ${name}`
35
- await $`git config user.email ${email}`
36
- for (let {relpath, contents} of files) {
37
- const _contents = typeof contents === 'string' ? contents : JSON.stringify(contents, null, 2)
38
- await fs.outputFile(path.resolve(cwd, relpath), _contents)
39
- }
40
- await $`git add .`
41
- await $`git commit -m ${msg}`
42
-
43
- for (let tag of tags) {
44
- await $`git tag ${tag} -m ${tag}`
45
- }
46
- }
33
+ await addCommits({cwd, commits})
47
34
 
48
35
  const bare = tempy.temporaryDirectory()
49
36
  await $`git init --bare ${bare}`
@@ -51,3 +38,25 @@ export const createFakeRepo = async ({cwd = tempy.temporaryDirectory(), commits
51
38
 
52
39
  return cwd
53
40
  })
41
+
42
+ export const addCommits = async ({cwd, commits = []}) => ctx(async ($) => {
43
+ $.cwd = cwd
44
+
45
+ for (let {msg, files, name = 'Semrel-extra Bot', email = 'semrel-extra-bot@hotmail.com', tags = []} of commits) {
46
+ await $`git config user.name ${name}`
47
+ await $`git config user.email ${email}`
48
+ for (let {relpath, contents} of files) {
49
+ const _contents = typeof contents === 'string' ? contents : JSON.stringify(contents, null, 2)
50
+ const file = path.resolve(cwd, relpath)
51
+ await fs.outputFile(file, _contents)
52
+
53
+ await $`git add ${file}`
54
+ }
55
+
56
+ await $`git commit -m ${msg}`
57
+
58
+ for (let tag of tags) {
59
+ await $`git tag ${tag} -m ${tag}`
60
+ }
61
+ }
62
+ })