zx-bulk-release 1.4.1 → 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,22 @@
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
+
10
+ ## [1.5.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.4.2...v1.5.0) (2022-06-21)
11
+
12
+ ### Features
13
+ * feat: add commit-ish to meta ([9092a4d](https://github.com/semrel-extra/zx-bulk-release/commit/9092a4d41f51fb58a9d7cb1696bb9ee3f07c9133))
14
+
15
+ ## [1.4.2](https://github.com/semrel-extra/zx-bulk-release/compare/v1.4.1...v1.4.2) (2022-06-21)
16
+
17
+ ### Fixes & improvements
18
+ * refactor: use common iface for push internals ([e614e42](https://github.com/semrel-extra/zx-bulk-release/commit/e614e42d8b323749082b94611f4552fdacebe1f5))
19
+
1
20
  ## [1.4.1](https://github.com/semrel-extra/zx-bulk-release/compare/v1.4.0...v1.4.1) (2022-06-21)
2
21
 
3
22
  ### Fixes & improvements
package/package.json CHANGED
@@ -1,23 +1,25 @@
1
1
  {
2
2
  "name": "zx-bulk-release",
3
- "version": "1.4.1",
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})
@@ -2,8 +2,101 @@ import {formatTag, getLatestTag} from './tag.js'
2
2
  import {tempy, ctx, fs, path} from 'zx-extra'
3
3
  import {copydir} from 'git-glob-cp'
4
4
 
5
- const branches = {}
6
- const META_VERSION = '1'
5
+ export const publish = async (pkg) => {
6
+ await pushTag(pkg)
7
+ await pushMeta(pkg)
8
+ await npmPublish(pkg)
9
+ await createGhRelease(pkg)
10
+ }
11
+
12
+ export const pushTag = (pkg) => ctx(async ($) => {
13
+ const {absPath: cwd, name, version} = pkg
14
+ const tag = formatTag({name, version})
15
+ const {gitCommitterEmail, gitCommitterName} = parseEnv($.env)
16
+ $.cwd = cwd
17
+
18
+ console.log(`push release tag ${tag}`)
19
+
20
+ await $`git config user.name ${gitCommitterName}`
21
+ await $`git config user.email ${gitCommitterEmail}`
22
+ await $`git tag -m ${tag} ${tag}`
23
+ await $`git push origin ${tag}`
24
+ })
25
+
26
+ export const pushMeta = (pkg) => ctx(async ($) => {
27
+ console.log('push artifact to branch `meta`')
28
+
29
+ const cwd = pkg.absPath
30
+ const {name, version} = pkg
31
+ const tag = formatTag({name, version})
32
+ const to = '.'
33
+ const branch = 'meta'
34
+ const msg = `chore: release meta ${name} ${version}`
35
+
36
+ $.cwd = cwd
37
+ const hash = (await $`git rev-parse HEAD`).toString().trim()
38
+ const meta = {
39
+ META_VERSION: '1',
40
+ name: pkg.name,
41
+ hash,
42
+ version: pkg.version,
43
+ dependencies: pkg.dependencies,
44
+ devDependencies: pkg.devDependencies,
45
+ peerDependencies: pkg.peerDependencies,
46
+ optionalDependencies: pkg.optionalDependencies,
47
+ }
48
+ const files = [{relpath: `${getArtifactPath(tag)}.json`, contents: meta}]
49
+
50
+ await push({cwd, to, branch, msg, files})
51
+ })
52
+
53
+ export const npmPublish = ({absPath: cwd}) => ctx(async ($) => {
54
+ const {npmRegistry, npmToken, npmConfig} = parseEnv($.env)
55
+ const npmrc = npmConfig ? npmConfig : path.resolve(cwd, '.npmrc')
56
+
57
+ console.log(`publish npm package to ${npmRegistry}`)
58
+ $.cwd = cwd
59
+ if (!npmConfig) {
60
+ await $.raw`echo ${npmRegistry.replace(/https?:/, '')}/:_authToken=${npmToken} >> ${npmrc}`
61
+ }
62
+ await $`npm publish --no-git-tag-version --registry=${npmRegistry} --userconfig ${npmrc} --no-workspaces`
63
+ })
64
+
65
+ export const createGhRelease = (pkg) => ctx(async ($) => {
66
+ console.log('create gh release')
67
+
68
+ const cwd = pkg.absPath
69
+ const {name, version} = pkg
70
+ const {ghUser, ghToken} = parseEnv($.env)
71
+ const {repoName, repoPublicUrl} = await parseRepo(cwd)
72
+
73
+ if (!ghToken || !ghUser) return null
74
+
75
+ const tag = formatTag({name, version})
76
+ const releaseDiffRef = `## [${name}@${version}](${repoPublicUrl}/compare/${pkg.latest.tag?.ref}...${tag}) (${new Date().toISOString().slice(0, 10)})`
77
+ const releaseDetails = Object.values(pkg.changes
78
+ .reduce((acc, {group, subj, short, hash}) => {
79
+ const {commits} = acc[group] || (acc[group] = {commits: [], group})
80
+ const commitRef = `* ${subj}${short ? ` [${short}](${repoPublicUrl}/commit/${hash})` : ''}`
81
+
82
+ commits.push(commitRef)
83
+
84
+ return acc
85
+ }, {}))
86
+ .map(({group, commits}) => `
87
+ ### ${group}
88
+ ${commits.join('\n')}`).join('\n')
89
+
90
+ const releaseNotes = releaseDiffRef + '\n' + releaseDetails + '\n'
91
+ const releaseData = JSON.stringify({
92
+ name: tag,
93
+ tag_name: tag,
94
+ body: releaseNotes
95
+ })
96
+
97
+ $.cwd = cwd
98
+ await $`curl -u ${ghUser}:${ghToken} -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${repoName}/releases -d ${releaseData}`
99
+ })
7
100
 
8
101
  export const getOrigin = (cwd) => ctx(async ($) => {
9
102
  $.cwd = cwd
@@ -16,6 +109,7 @@ export const getOrigin = (cwd) => ctx(async ($) => {
16
109
  : originUrl
17
110
  })
18
111
 
112
+ const branches = {}
19
113
  export const fetch = async ({cwd: _cwd, branch, origin: _origin}) => ctx(async ($) => {
20
114
  let cwd = branches[branch]
21
115
  if (cwd) return cwd
@@ -55,33 +149,6 @@ export const push = async ({cwd, from, to, branch, origin, msg, ignoreFiles, fil
55
149
  await $.raw`git push origin HEAD:refs/heads/${branch}`
56
150
  })
57
151
 
58
- export const publish = async (pkg, env) => ctx(async ($) => {
59
- const {name, version} = pkg.manifest
60
- const tag = formatTag({name, version})
61
- const cwd = pkg.absPath
62
- const {gitCommitterEmail, gitCommitterName, npmRegistry} = parseEnv(env)
63
-
64
- $.cwd = cwd
65
- $.env = env
66
-
67
- console.log(`push release tag ${tag}`)
68
- await $`git config user.name ${gitCommitterName}`
69
- await $`git config user.email ${gitCommitterEmail}`
70
- await $`git tag -m ${tag} ${tag}`
71
- await $`git push origin ${tag}`
72
-
73
- console.log('push artifact to branch `meta`')
74
- await pushMeta(pkg)
75
-
76
- console.log(`publish npm package to ${npmRegistry}`)
77
- const npmrc = path.resolve(cwd, '.npmrc')
78
- await $.raw`echo ${npmRegistry.replace(/https?:/, '')}/:_authToken=${$.env.NPM_TOKEN} >> ${npmrc}`
79
- await $`npm publish --no-git-tag-version --registry=${npmRegistry} --userconfig ${npmrc} --no-workspaces`
80
-
81
- console.log('create gh release')
82
- await createGhRelease(pkg)
83
- })
84
-
85
152
  export const getArtifactPath = (tag) => tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')
86
153
 
87
154
  export const getLatestMeta = async (cwd, tag) => {
@@ -89,33 +156,15 @@ export const getLatestMeta = async (cwd, tag) => {
89
156
 
90
157
  try {
91
158
  const _cwd = await fetch({cwd, branch: 'meta'})
92
- return await fs.readJson(path.resolve(_cwd, getArtifactPath(tag), 'meta.json'))
159
+ return await Promise.any([
160
+ fs.readJson(path.resolve(_cwd, `${getArtifactPath(tag)}.json`)),
161
+ fs.readJson(path.resolve(_cwd, getArtifactPath(tag), 'meta.json'))
162
+ ])
93
163
  } catch {}
94
164
 
95
165
  return null
96
166
  }
97
167
 
98
- export const pushMeta = async (pkg) => {
99
- const cwd = pkg.absPath
100
- const {name, version} = pkg
101
- const tag = formatTag({name, version})
102
- const to = getArtifactPath(tag)
103
- const branch = 'meta'
104
- const msg = `chore: release meta ${name} ${version}`
105
- const meta = {
106
- META_VERSION,
107
- name: pkg.name,
108
- version: pkg.version,
109
- dependencies: pkg.dependencies,
110
- devDependencies: pkg.devDependencies,
111
- peerDependencies: pkg.peerDependencies,
112
- optionalDependencies: pkg.optionalDependencies,
113
- }
114
- const files = [{relpath: 'meta.json', contents: meta}]
115
-
116
- await push({cwd, to, branch, msg, files})
117
- }
118
-
119
168
  export const getLatest = async (cwd, name) => {
120
169
  const tag = await getLatestTag(cwd, name)
121
170
  const meta = await getLatestMeta(cwd, tag?.ref)
@@ -127,11 +176,12 @@ export const getLatest = async (cwd, name) => {
127
176
  }
128
177
 
129
178
  export const parseEnv = (env = process.env) => {
130
- const {GH_USER, GH_USERNAME, GITHUB_USER, GITHUB_USERNAME, GH_TOKEN, GITHUB_TOKEN, NPM_TOKEN, NPM_REGISTRY, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL} = env
179
+ 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
131
180
 
132
181
  return {
133
182
  ghUser: GH_USER || GH_USERNAME || GITHUB_USER || GITHUB_USERNAME,
134
183
  ghToken: GH_TOKEN || GITHUB_TOKEN,
184
+ npmConfig: NPMRC || NPM_USERCONFIG || NPM_CONFIG_USERCONFIG,
135
185
  npmToken: NPM_TOKEN,
136
186
  npmRegistry: NPM_REGISTRY || 'https://registry.npmjs.org',
137
187
  gitCommitterName: GIT_COMMITTER_NAME || 'Semrel Extra Bot',
@@ -151,37 +201,3 @@ export const parseRepo = (cwd, origin) => ctx(async ($) => {
151
201
  repoPublicUrl
152
202
  }
153
203
  })
154
-
155
- export const createGhRelease = (pkg) => ctx(async ($) => {
156
- const cwd = pkg.absPath
157
- const {name, version} = pkg
158
- const {ghUser, ghToken} = parseEnv($.env)
159
- const {repoName, repoPublicUrl} = await parseRepo(cwd)
160
-
161
- if (!ghToken || !ghUser) return null
162
-
163
- const tag = formatTag({name, version})
164
- const releaseDiffRef = `## [${name}@${version}](${repoPublicUrl}/compare/${pkg.latest.tag?.ref}...${tag}) (${new Date().toISOString().slice(0, 10)})`
165
- const releaseDetails = Object.values(pkg.changes
166
- .reduce((acc, {group, subj, short, hash}) => {
167
- const {commits} = acc[group] || (acc[group] = {commits: [], group})
168
- const commitRef = `* ${subj}${short ? ` [${short}](${repoPublicUrl}/commit/${hash})` : ''}`
169
-
170
- commits.push(commitRef)
171
-
172
- return acc
173
- }, {}))
174
- .map(({group, commits}) => `
175
- ### ${group}
176
- ${commits.join('\n')}`).join('\n')
177
-
178
- const releaseNotes = releaseDiffRef + '\n' + releaseDetails + '\n'
179
- const releaseData = JSON.stringify({
180
- name: tag,
181
- tag_name: tag,
182
- body: releaseNotes
183
- })
184
-
185
- $.cwd = cwd
186
- await $`curl -u ${ghUser}:${ghToken} -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${repoName}/releases -d ${releaseData}`
187
- })
@@ -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
 
@@ -121,7 +131,41 @@ test('run()', async () => {
121
131
  const meta = tempy.temporaryDirectory()
122
132
 
123
133
  await $`git clone --single-branch --branch meta --depth 1 ${origin} ${meta}`
124
- assert.is((await fs.readJson(`${meta}/${tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')}/meta.json`)).version, '1.0.1')
134
+ assert.is((await fs.readJson(`${meta}/${tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')}.json`)).version, '1.0.1')
135
+ })
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')
125
169
  })
126
170
 
127
171
  await registry.stop()
@@ -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
+ })