npm-update-package 0.31.2 → 0.31.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.
Files changed (178) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.js +23 -0
  3. package/.github/renovate.json +15 -0
  4. package/.github/workflows/eslint.yml +14 -0
  5. package/.github/workflows/test.yml +20 -0
  6. package/.husky/pre-commit +4 -0
  7. package/.nvmrc +1 -0
  8. package/dist/package.json +1 -4
  9. package/dist/src/bin.js +24 -0
  10. package/dist/src/core/FailedResult.js +2 -0
  11. package/dist/src/core/OutdatedPackage.js +2 -0
  12. package/dist/src/core/OutdatedPackageProcessor.js +79 -0
  13. package/dist/src/core/OutdatedPackagesProcessor.js +20 -0
  14. package/dist/src/core/SucceededResult.js +2 -0
  15. package/dist/src/core/index.js +17 -0
  16. package/dist/src/file/index.js +13 -0
  17. package/dist/src/file/readFile.js +12 -0
  18. package/dist/src/git/CommitMessageCreator.js +22 -0
  19. package/dist/src/git/Git.js +51 -0
  20. package/dist/src/git/GitRepository.js +41 -0
  21. package/dist/src/git/createBranchName.js +9 -0
  22. package/dist/src/git/index.js +16 -0
  23. package/dist/src/github/GitHub.js +94 -0
  24. package/dist/src/github/branch/finder/BranchFinder.js +12 -0
  25. package/dist/src/github/branch/finder/index.js +13 -0
  26. package/dist/src/github/branch/index.js +13 -0
  27. package/dist/src/github/createGitHub.js +13 -0
  28. package/dist/src/github/createOctokit.js +26 -0
  29. package/dist/src/github/errors/NotFoundError.js +10 -0
  30. package/dist/src/github/errors/index.js +13 -0
  31. package/dist/src/github/index.js +17 -0
  32. package/dist/src/github/label/creator/LabelCreator.js +46 -0
  33. package/dist/src/github/label/creator/index.js +13 -0
  34. package/dist/src/github/label/index.js +13 -0
  35. package/dist/src/github/pull-request/closer/PullRequestCloser.js +21 -0
  36. package/dist/src/github/pull-request/closer/index.js +13 -0
  37. package/dist/src/github/pull-request/creator/PullRequestCreator.js +45 -0
  38. package/dist/src/github/pull-request/creator/PullRequestTitleCreator.js +22 -0
  39. package/dist/src/github/pull-request/creator/createPullRequestBody.js +39 -0
  40. package/dist/src/github/pull-request/creator/index.js +14 -0
  41. package/dist/src/github/pull-request/finder/PullRequestFinder.js +27 -0
  42. package/dist/src/github/pull-request/finder/index.js +13 -0
  43. package/dist/src/github/pull-request/finder/isPullRequestByNpmUpdatePackage.js +7 -0
  44. package/dist/src/github/pull-request/index.js +15 -0
  45. package/dist/src/github/pull-request/metadata/PullRequestMetadata.js +15 -0
  46. package/dist/src/github/pull-request/metadata/createPullRequestMetadata.js +19 -0
  47. package/dist/src/github/pull-request/metadata/extractPullRequestMetadata.js +17 -0
  48. package/dist/src/github/pull-request/metadata/index.js +15 -0
  49. package/dist/src/json/index.js +13 -0
  50. package/dist/src/json/toJSON.js +14 -0
  51. package/dist/src/logger/LogLevel.js +15 -0
  52. package/dist/src/logger/Logger.js +2 -0
  53. package/dist/src/logger/createLogger.js +11 -0
  54. package/dist/src/logger/index.js +15 -0
  55. package/dist/src/main.js +112 -0
  56. package/dist/src/ncu/Ncu.js +71 -0
  57. package/dist/src/ncu/NcuResult.js +6 -0
  58. package/dist/src/ncu/index.js +13 -0
  59. package/dist/src/options/CLIOption.js +2 -0
  60. package/dist/src/options/OptionType.js +10 -0
  61. package/dist/src/options/Options.js +30 -0
  62. package/dist/src/options/cliOptions.js +61 -0
  63. package/dist/src/options/index.js +14 -0
  64. package/dist/src/options/initOptions.js +25 -0
  65. package/dist/src/options/toCommanderOption.js +32 -0
  66. package/dist/src/package-json/PackageMetadata.js +18 -0
  67. package/dist/src/package-json/PackageMetadataDependencies.js +6 -0
  68. package/dist/src/package-json/index.js +14 -0
  69. package/dist/src/package-json/parsePackageJson.js +15 -0
  70. package/dist/src/package-manager/Npm.js +18 -0
  71. package/dist/src/package-manager/PackageManager.js +2 -0
  72. package/dist/src/package-manager/PackageManagerName.js +10 -0
  73. package/dist/src/package-manager/Yarn.js +18 -0
  74. package/dist/src/package-manager/createPackageManager.js +15 -0
  75. package/dist/src/package-manager/index.js +17 -0
  76. package/dist/src/semver/SemVer.js +25 -0
  77. package/dist/src/semver/SemVerLevel.js +11 -0
  78. package/dist/src/semver/compareSemVers.js +16 -0
  79. package/dist/src/semver/index.js +15 -0
  80. package/dist/src/terminal/Terminal.js +14 -0
  81. package/dist/src/terminal/index.js +13 -0
  82. package/jest.config.ts +12 -0
  83. package/lint-staged.config.js +4 -0
  84. package/npm-update-package.code-workspace +10 -0
  85. package/package.json +1 -4
  86. package/src/bin.ts +22 -0
  87. package/src/core/FailedResult.ts +6 -0
  88. package/src/core/OutdatedPackage.ts +11 -0
  89. package/src/core/OutdatedPackageProcessor.ts +136 -0
  90. package/src/core/OutdatedPackagesProcessor.ts +35 -0
  91. package/src/core/SucceededResult.ts +7 -0
  92. package/src/core/index.ts +5 -0
  93. package/src/file/index.ts +1 -0
  94. package/src/file/readFile.ts +6 -0
  95. package/src/git/CommitMessageCreator.test.ts +17 -0
  96. package/src/git/CommitMessageCreator.ts +19 -0
  97. package/src/git/Git.ts +59 -0
  98. package/src/git/GitRepository.test.ts +82 -0
  99. package/src/git/GitRepository.ts +57 -0
  100. package/src/git/createBranchName.test.ts +14 -0
  101. package/src/git/createBranchName.ts +7 -0
  102. package/src/git/index.ts +4 -0
  103. package/src/github/GitHub.ts +198 -0
  104. package/src/github/branch/finder/BranchFinder.test.ts +21 -0
  105. package/src/github/branch/finder/BranchFinder.ts +9 -0
  106. package/src/github/branch/finder/index.ts +1 -0
  107. package/src/github/branch/index.ts +1 -0
  108. package/src/github/createGitHub.test.ts +16 -0
  109. package/src/github/createGitHub.ts +17 -0
  110. package/src/github/createOctokit.test.ts +27 -0
  111. package/src/github/createOctokit.ts +27 -0
  112. package/src/github/errors/NotFoundError.ts +11 -0
  113. package/src/github/errors/index.ts +1 -0
  114. package/src/github/index.ts +5 -0
  115. package/src/github/label/creator/LabelCreator.ts +71 -0
  116. package/src/github/label/creator/index.ts +1 -0
  117. package/src/github/label/index.ts +1 -0
  118. package/src/github/pull-request/closer/PullRequestCloser.test.ts +53 -0
  119. package/src/github/pull-request/closer/PullRequestCloser.ts +21 -0
  120. package/src/github/pull-request/closer/index.ts +1 -0
  121. package/src/github/pull-request/creator/PullRequestCreator.test.ts +98 -0
  122. package/src/github/pull-request/creator/PullRequestCreator.ts +84 -0
  123. package/src/github/pull-request/creator/PullRequestTitleCreator.test.ts +17 -0
  124. package/src/github/pull-request/creator/PullRequestTitleCreator.ts +19 -0
  125. package/src/github/pull-request/creator/createPullRequestBody.test.ts +47 -0
  126. package/src/github/pull-request/creator/createPullRequestBody.ts +35 -0
  127. package/src/github/pull-request/creator/index.ts +2 -0
  128. package/src/github/pull-request/finder/PullRequestFinder.ts +27 -0
  129. package/src/github/pull-request/finder/index.ts +1 -0
  130. package/src/github/pull-request/finder/isPullRequestByNpmUpdatePackage.test.ts +24 -0
  131. package/src/github/pull-request/finder/isPullRequestByNpmUpdatePackage.ts +5 -0
  132. package/src/github/pull-request/index.ts +3 -0
  133. package/src/github/pull-request/metadata/PullRequestMetadata.ts +22 -0
  134. package/src/github/pull-request/metadata/createPullRequestMetadata.test.ts +31 -0
  135. package/src/github/pull-request/metadata/createPullRequestMetadata.ts +21 -0
  136. package/src/github/pull-request/metadata/extractPullRequestMetadata.test.ts +53 -0
  137. package/src/github/pull-request/metadata/extractPullRequestMetadata.ts +21 -0
  138. package/src/github/pull-request/metadata/index.ts +3 -0
  139. package/src/json/index.ts +1 -0
  140. package/src/json/toJSON.test.ts +65 -0
  141. package/src/json/toJSON.ts +13 -0
  142. package/src/logger/LogLevel.ts +13 -0
  143. package/src/logger/Logger.ts +1 -0
  144. package/src/logger/createLogger.ts +10 -0
  145. package/src/logger/index.ts +3 -0
  146. package/src/main.ts +149 -0
  147. package/src/ncu/Ncu.ts +84 -0
  148. package/src/ncu/NcuResult.ts +10 -0
  149. package/src/ncu/index.ts +1 -0
  150. package/src/options/CLIOption.ts +19 -0
  151. package/src/options/OptionType.ts +8 -0
  152. package/src/options/Options.ts +40 -0
  153. package/src/options/cliOptions.ts +60 -0
  154. package/src/options/index.ts +2 -0
  155. package/src/options/initOptions.ts +24 -0
  156. package/src/options/toCommanderOption.ts +35 -0
  157. package/src/package-json/PackageMetadata.ts +24 -0
  158. package/src/package-json/PackageMetadataDependencies.ts +10 -0
  159. package/src/package-json/index.ts +2 -0
  160. package/src/package-json/parsePackageJson.ts +15 -0
  161. package/src/package-manager/Npm.ts +17 -0
  162. package/src/package-manager/PackageManager.ts +5 -0
  163. package/src/package-manager/PackageManagerName.ts +8 -0
  164. package/src/package-manager/Yarn.ts +17 -0
  165. package/src/package-manager/createPackageManager.test.ts +27 -0
  166. package/src/package-manager/createPackageManager.ts +20 -0
  167. package/src/package-manager/index.ts +5 -0
  168. package/src/semver/SemVer.test.ts +17 -0
  169. package/src/semver/SemVer.ts +40 -0
  170. package/src/semver/SemVerLevel.ts +9 -0
  171. package/src/semver/compareSemVers.test.ts +21 -0
  172. package/src/semver/compareSemVers.ts +16 -0
  173. package/src/semver/index.ts +3 -0
  174. package/src/terminal/Terminal.ts +12 -0
  175. package/src/terminal/index.ts +1 -0
  176. package/tsconfig.base.json +7 -0
  177. package/tsconfig.build.json +12 -0
  178. package/tsconfig.json +9 -0
@@ -0,0 +1,136 @@
1
+ import {
2
+ left,
3
+ right,
4
+ type Either
5
+ } from 'fp-ts/lib/Either'
6
+ import {
7
+ createBranchName,
8
+ type CommitMessageCreator,
9
+ type Git
10
+ } from '../git'
11
+ import type {
12
+ BranchFinder,
13
+ PullRequestCloser,
14
+ PullRequestCreator,
15
+ PullRequestFinder
16
+ } from '../github'
17
+ import type { Logger } from '../logger'
18
+ import type { Ncu } from '../ncu'
19
+ import type { PackageManager } from '../package-manager'
20
+ import type { FailedResult } from './FailedResult'
21
+ import type { OutdatedPackage } from './OutdatedPackage'
22
+ import type { SucceededResult } from './SucceededResult'
23
+
24
+ // TODO: add test
25
+ export class OutdatedPackageProcessor {
26
+ private readonly git: Git
27
+ private readonly ncu: Ncu
28
+ private readonly packageManager: PackageManager
29
+ private readonly pullRequestCreator: PullRequestCreator
30
+ private readonly branchFinder: BranchFinder
31
+ private readonly logger: Logger
32
+ private readonly commitMessageCreator: CommitMessageCreator
33
+ private readonly pullRequestFinder: PullRequestFinder
34
+ private readonly pullRequestCloser: PullRequestCloser
35
+
36
+ constructor ({
37
+ git,
38
+ ncu,
39
+ packageManager,
40
+ pullRequestCreator,
41
+ branchFinder,
42
+ logger,
43
+ commitMessageCreator,
44
+ pullRequestFinder,
45
+ pullRequestCloser
46
+ }: {
47
+ git: Git
48
+ ncu: Ncu
49
+ packageManager: PackageManager
50
+ pullRequestCreator: PullRequestCreator
51
+ branchFinder: BranchFinder
52
+ logger: Logger
53
+ commitMessageCreator: CommitMessageCreator
54
+ pullRequestFinder: PullRequestFinder
55
+ pullRequestCloser: PullRequestCloser
56
+ }) {
57
+ this.git = git
58
+ this.ncu = ncu
59
+ this.packageManager = packageManager
60
+ this.pullRequestCreator = pullRequestCreator
61
+ this.branchFinder = branchFinder
62
+ this.logger = logger
63
+ this.commitMessageCreator = commitMessageCreator
64
+ this.pullRequestFinder = pullRequestFinder
65
+ this.pullRequestCloser = pullRequestCloser
66
+ }
67
+
68
+ /**
69
+ * Don't run in parallel because it includes file operations.
70
+ */
71
+ async process (outdatedPackage: OutdatedPackage): Promise<Either<FailedResult, SucceededResult>> {
72
+ const branchName = createBranchName(outdatedPackage)
73
+ this.logger.debug(`branchName=${branchName}`)
74
+
75
+ if (this.branchFinder.findByName(branchName) !== undefined) {
76
+ this.logger.info(`Skip ${outdatedPackage.name} because ${branchName} branch already exists on remote.`)
77
+ return right({
78
+ outdatedPackage,
79
+ skipped: true
80
+ })
81
+ }
82
+
83
+ await this.git.createBranch(branchName)
84
+ this.logger.info(`${branchName} branch has created.`)
85
+
86
+ try {
87
+ try {
88
+ const updatedPackages = await this.ncu.update(outdatedPackage)
89
+
90
+ if (updatedPackages.length !== 1) {
91
+ throw new Error(`Failed to update ${outdatedPackage.name}.`)
92
+ }
93
+
94
+ await this.packageManager.install()
95
+ } catch (error) {
96
+ this.logger.error(error)
97
+ return left({
98
+ outdatedPackage,
99
+ error
100
+ })
101
+ }
102
+
103
+ this.logger.info(`${outdatedPackage.name} has updated from v${outdatedPackage.currentVersion.version} to v${outdatedPackage.newVersion.version}`)
104
+
105
+ await this.git.add(this.packageManager.packageFile, this.packageManager.lockFile)
106
+ const message = this.commitMessageCreator.create(outdatedPackage)
107
+ this.logger.debug(`message=${message}`)
108
+
109
+ await this.git.commit(message)
110
+ await this.git.push(branchName)
111
+
112
+ const pullRequest = await this.pullRequestCreator.create({
113
+ outdatedPackage,
114
+ branchName
115
+ })
116
+ this.logger.info(`Pull request for ${outdatedPackage.name} has created. ${pullRequest.html_url}`)
117
+
118
+ const pullRequests = this.pullRequestFinder.findByPackageName(outdatedPackage.name)
119
+ this.logger.debug(`pullRequests=${JSON.stringify(pullRequests)}`)
120
+
121
+ await Promise.all(pullRequests.map(async (pullRequest) => {
122
+ await this.pullRequestCloser.close(pullRequest)
123
+ this.logger.info(`Pull request for ${outdatedPackage.name} has closed. ${pullRequest.html_url}`)
124
+ }))
125
+ return right({
126
+ outdatedPackage,
127
+ created: true
128
+ })
129
+ } finally {
130
+ await this.git.restore(this.packageManager.packageFile, this.packageManager.lockFile)
131
+ await this.git.switch('-')
132
+ await this.git.removeBranch(branchName)
133
+ this.logger.info(`${branchName} branch has removed.`)
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,35 @@
1
+ import type { Either } from 'fp-ts/lib/Either'
2
+ import type { Logger } from '../logger'
3
+ import type { FailedResult } from './FailedResult'
4
+ import type { OutdatedPackage } from './OutdatedPackage'
5
+ import type { OutdatedPackageProcessor } from './OutdatedPackageProcessor'
6
+ import type { SucceededResult } from './SucceededResult'
7
+
8
+ // TODO: add test
9
+ export class OutdatedPackagesProcessor {
10
+ private readonly outdatedPackageProcessor: OutdatedPackageProcessor
11
+ private readonly logger: Logger
12
+
13
+ constructor ({
14
+ outdatedPackageProcessor,
15
+ logger
16
+ }: {
17
+ outdatedPackageProcessor: OutdatedPackageProcessor
18
+ logger: Logger
19
+ }) {
20
+ this.outdatedPackageProcessor = outdatedPackageProcessor
21
+ this.logger = logger
22
+ }
23
+
24
+ async process (outdatedPackages: OutdatedPackage[]): Promise<Array<Either<FailedResult, SucceededResult>>> {
25
+ const results: Array<Either<FailedResult, SucceededResult>> = []
26
+
27
+ for (const outdatedPackage of outdatedPackages) {
28
+ this.logger.debug(`outdatedPackage=${JSON.stringify(outdatedPackage)}`)
29
+ const result = await this.outdatedPackageProcessor.process(outdatedPackage)
30
+ results.push(result)
31
+ }
32
+
33
+ return results
34
+ }
35
+ }
@@ -0,0 +1,7 @@
1
+ import type { OutdatedPackage } from './OutdatedPackage'
2
+
3
+ export interface SucceededResult {
4
+ outdatedPackage: OutdatedPackage
5
+ created?: boolean
6
+ skipped?: boolean
7
+ }
@@ -0,0 +1,5 @@
1
+ export * from './FailedResult'
2
+ export * from './OutdatedPackage'
3
+ export * from './OutdatedPackageProcessor'
4
+ export * from './OutdatedPackagesProcessor'
5
+ export * from './SucceededResult'
@@ -0,0 +1 @@
1
+ export * from './readFile'
@@ -0,0 +1,6 @@
1
+ import fs from 'fs'
2
+
3
+ // TODO: add test
4
+ export const readFile = async (path: string): Promise<string> => {
5
+ return await fs.promises.readFile(path, 'utf8')
6
+ }
@@ -0,0 +1,17 @@
1
+ import { SemVer } from '../semver'
2
+ import { CommitMessageCreator } from './CommitMessageCreator'
3
+
4
+ describe('CommitMessageCreator', () => {
5
+ describe('create', () => {
6
+ it('returns commit message', () => {
7
+ const commitMessageCreator = new CommitMessageCreator('chore(deps): {{level}} update {{{packageName}}} from {{currentVersion}} to v{{newVersion}}')
8
+ const actual = commitMessageCreator.create({
9
+ name: '@npm-update-package/example',
10
+ currentVersion: SemVer.of('1.0.0'),
11
+ newVersion: SemVer.of('2.0.0'),
12
+ level: 'major'
13
+ })
14
+ expect(actual).toBe('chore(deps): major update @npm-update-package/example from 1.0.0 to v2.0.0')
15
+ })
16
+ })
17
+ })
@@ -0,0 +1,19 @@
1
+ import { render } from 'mustache'
2
+ import type { OutdatedPackage } from '../core'
3
+
4
+ export class CommitMessageCreator {
5
+ constructor (private readonly template: string) {}
6
+
7
+ create (outdatedPackage: OutdatedPackage): string {
8
+ const packageName = outdatedPackage.name
9
+ const currentVersion = outdatedPackage.currentVersion.version
10
+ const newVersion = outdatedPackage.newVersion.version
11
+ const level = outdatedPackage.level
12
+ return render(this.template, {
13
+ packageName,
14
+ currentVersion,
15
+ newVersion,
16
+ level
17
+ })
18
+ }
19
+ }
package/src/git/Git.ts ADDED
@@ -0,0 +1,59 @@
1
+ import type { Terminal } from '../terminal'
2
+ import { GitRepository } from './GitRepository'
3
+
4
+ // TODO: add test
5
+ export class Git {
6
+ constructor (private readonly terminal: Terminal) {}
7
+
8
+ async add (...files: string[]): Promise<void> {
9
+ await this.terminal.run('git', 'add', ...files)
10
+ }
11
+
12
+ async commit (message: string): Promise<void> {
13
+ await this.terminal.run('git', 'commit', '--message', message)
14
+ }
15
+
16
+ async createBranch (branchName: string): Promise<void> {
17
+ await this.terminal.run('git', 'checkout', '-b', branchName)
18
+ }
19
+
20
+ async getConfig (key: string): Promise<string> {
21
+ const { stdout } = await this.terminal.run('git', 'config', key)
22
+ return stdout.trim()
23
+ }
24
+
25
+ async getCurrentBranch (): Promise<string> {
26
+ const { stdout } = await this.terminal.run('git', 'rev-parse', '--abbrev-ref', 'HEAD')
27
+ return stdout.trim()
28
+ }
29
+
30
+ async getRemoteUrl (): Promise<string> {
31
+ const { stdout } = await this.terminal.run('git', 'remote', 'get-url', '--push', 'origin')
32
+ return stdout.trim()
33
+ }
34
+
35
+ async getRepository (): Promise<GitRepository> {
36
+ const url = await this.getRemoteUrl()
37
+ return GitRepository.of(url)
38
+ }
39
+
40
+ async push (branchName: string): Promise<void> {
41
+ await this.terminal.run('git', 'push', 'origin', branchName)
42
+ }
43
+
44
+ async removeBranch (branchName: string): Promise<void> {
45
+ await this.terminal.run('git', 'branch', '-D', branchName)
46
+ }
47
+
48
+ async restore (...files: string[]): Promise<void> {
49
+ await this.terminal.run('git', 'checkout', ...files)
50
+ }
51
+
52
+ async setConfig (key: string, value: string): Promise<void> {
53
+ await this.terminal.run('git', 'config', key, value)
54
+ }
55
+
56
+ async switch (branchName: string): Promise<void> {
57
+ await this.terminal.run('git', 'checkout', branchName)
58
+ }
59
+ }
@@ -0,0 +1,82 @@
1
+ import { GitRepository } from './GitRepository'
2
+
3
+ describe('GitRepository', () => {
4
+ describe('of', () => {
5
+ describe('returns new GitRepository instance if URL is valid', () => {
6
+ type TestCase = [
7
+ string,
8
+ {
9
+ host: string
10
+ owner: string
11
+ name: string
12
+ apiEndPoint: string
13
+ isGitHubDotCom: boolean
14
+ }
15
+ ]
16
+ const cases: TestCase[] = [
17
+ [
18
+ 'https://github.com/npm-update-package/npm-update-package.git',
19
+ {
20
+ host: 'github.com',
21
+ owner: 'npm-update-package',
22
+ name: 'npm-update-package',
23
+ apiEndPoint: 'https://api.github.com',
24
+ isGitHubDotCom: true
25
+ }
26
+ ],
27
+ [
28
+ 'git@github.com:npm-update-package/npm-update-package.git',
29
+ {
30
+ host: 'github.com',
31
+ owner: 'npm-update-package',
32
+ name: 'npm-update-package',
33
+ apiEndPoint: 'https://api.github.com',
34
+ isGitHubDotCom: true
35
+ }
36
+ ],
37
+ [
38
+ 'https://git.example.com/npm-update-package/npm-update-package.git',
39
+ {
40
+ host: 'git.example.com',
41
+ owner: 'npm-update-package',
42
+ name: 'npm-update-package',
43
+ apiEndPoint: 'https://git.example.com/api/v3',
44
+ isGitHubDotCom: false
45
+ }
46
+ ],
47
+ [
48
+ 'git@git.example.com:npm-update-package/npm-update-package.git',
49
+ {
50
+ host: 'git.example.com',
51
+ owner: 'npm-update-package',
52
+ name: 'npm-update-package',
53
+ apiEndPoint: 'https://git.example.com/api/v3',
54
+ isGitHubDotCom: false
55
+ }
56
+ ]
57
+ ]
58
+
59
+ it.each<TestCase>(cases)('url=%p', (url, expected) => {
60
+ const actual = GitRepository.of(url)
61
+ expect(actual).toBeInstanceOf(GitRepository)
62
+ expect(actual.host).toBe(expected.host)
63
+ expect(actual.owner).toBe(expected.owner)
64
+ expect(actual.name).toBe(expected.name)
65
+ expect(actual.apiEndPoint).toBe(expected.apiEndPoint)
66
+ expect(actual.isGitHubDotCom).toBe(expected.isGitHubDotCom)
67
+ })
68
+ })
69
+
70
+ describe('throws error if URL is invalid', () => {
71
+ type TestCase = string
72
+ const cases: TestCase[] = [
73
+ '',
74
+ 'https://example.com/'
75
+ ]
76
+
77
+ it.each<TestCase>(cases)('url=%p', (url) => {
78
+ expect(() => GitRepository.of(url)).toThrow(Error)
79
+ })
80
+ })
81
+ })
82
+ })
@@ -0,0 +1,57 @@
1
+ import parse from 'parse-github-url'
2
+
3
+ export class GitRepository {
4
+ readonly host: string
5
+ readonly owner: string
6
+ readonly name: string
7
+
8
+ private constructor ({
9
+ host,
10
+ owner,
11
+ name
12
+ }: {
13
+ host: string
14
+ owner: string
15
+ name: string
16
+ }) {
17
+ this.host = host
18
+ this.owner = owner
19
+ this.name = name
20
+ }
21
+
22
+ static of (url: string): GitRepository {
23
+ const parsed = parse(url)
24
+
25
+ if (parsed === null) {
26
+ throw new Error(`Failed to parse url. url=${url}`)
27
+ }
28
+
29
+ const {
30
+ host,
31
+ owner,
32
+ name
33
+ } = parsed
34
+
35
+ if (host === null || owner === null || name === null) {
36
+ throw new Error(`Failed to parse url. url=${url}`)
37
+ }
38
+
39
+ return new GitRepository({
40
+ host,
41
+ owner,
42
+ name
43
+ })
44
+ }
45
+
46
+ get apiEndPoint (): string {
47
+ if (this.isGitHubDotCom) {
48
+ return 'https://api.github.com'
49
+ } else {
50
+ return `https://${this.host}/api/v3`
51
+ }
52
+ }
53
+
54
+ get isGitHubDotCom (): boolean {
55
+ return this.host === 'github.com'
56
+ }
57
+ }
@@ -0,0 +1,14 @@
1
+ import { SemVer } from '../semver'
2
+ import { createBranchName } from './createBranchName'
3
+
4
+ describe('createBranchName', () => {
5
+ it('returns branch name', () => {
6
+ const actual = createBranchName({
7
+ name: '@npm-update-package/example',
8
+ currentVersion: SemVer.of('1.0.0'),
9
+ newVersion: SemVer.of('1.2.3'),
10
+ level: 'major'
11
+ })
12
+ expect(actual).toBe('npm-update-package/@npm-update-package/example/v1.2.3')
13
+ })
14
+ })
@@ -0,0 +1,7 @@
1
+ import type { OutdatedPackage } from '../core'
2
+
3
+ export const createBranchName = (outdatedPackage: OutdatedPackage): string => {
4
+ const packageName = outdatedPackage.name
5
+ const newVersion = outdatedPackage.newVersion.version
6
+ return `npm-update-package/${packageName}/v${newVersion}`
7
+ }
@@ -0,0 +1,4 @@
1
+ export * from './CommitMessageCreator'
2
+ export * from './createBranchName'
3
+ export * from './Git'
4
+ export * from './GitRepository'
@@ -0,0 +1,198 @@
1
+ import type {
2
+ Octokit,
3
+ RestEndpointMethodTypes
4
+ } from '@octokit/rest'
5
+ import type { ValuesType } from 'utility-types'
6
+
7
+ export type Branch = ValuesType<RestEndpointMethodTypes['repos']['listBranches']['response']['data']>
8
+ export type CreatedPullRequest = RestEndpointMethodTypes['pulls']['create']['response']['data']
9
+ export type Label = RestEndpointMethodTypes['issues']['getLabel']['response']['data']
10
+ export type PullRequest = ValuesType<RestEndpointMethodTypes['pulls']['list']['response']['data']>
11
+ export type Repository = RestEndpointMethodTypes['repos']['get']['response']['data']
12
+
13
+ // TODO: add test
14
+ export class GitHub {
15
+ constructor (private readonly octokit: Octokit) {}
16
+
17
+ async addLabels ({
18
+ owner,
19
+ repo,
20
+ issueNumber,
21
+ labels
22
+ }: {
23
+ owner: string
24
+ repo: string
25
+ issueNumber: number
26
+ labels: string[]
27
+ }): Promise<void> {
28
+ await this.octokit.issues.addLabels({
29
+ owner,
30
+ repo,
31
+ issue_number: issueNumber,
32
+ labels
33
+ })
34
+ }
35
+
36
+ async closePullRequest ({
37
+ owner,
38
+ repo,
39
+ pullNumber
40
+ }: {
41
+ owner: string
42
+ repo: string
43
+ pullNumber: number
44
+ }): Promise<void> {
45
+ await this.octokit.pulls.update({
46
+ owner,
47
+ repo,
48
+ pull_number: pullNumber,
49
+ state: 'closed'
50
+ })
51
+ }
52
+
53
+ async createLabel ({
54
+ owner,
55
+ repo,
56
+ name,
57
+ description,
58
+ color
59
+ }: {
60
+ owner: string
61
+ repo: string
62
+ name: string
63
+ description?: string
64
+ color?: string
65
+ }): Promise<void> {
66
+ await this.octokit.issues.createLabel({
67
+ owner,
68
+ repo,
69
+ name,
70
+ description,
71
+ color
72
+ })
73
+ }
74
+
75
+ async createPullRequest ({
76
+ owner,
77
+ repo,
78
+ baseBranch,
79
+ headBranch,
80
+ title,
81
+ body
82
+ }: {
83
+ owner: string
84
+ repo: string
85
+ baseBranch: string
86
+ headBranch: string
87
+ title: string
88
+ body: string
89
+ }): Promise<CreatedPullRequest> {
90
+ const { data } = await this.octokit.pulls.create({
91
+ owner,
92
+ repo,
93
+ base: baseBranch,
94
+ head: headBranch,
95
+ title,
96
+ body
97
+ })
98
+ return data
99
+ }
100
+
101
+ async deleteBranch ({
102
+ owner,
103
+ repo,
104
+ branch
105
+ }: {
106
+ owner: string
107
+ repo: string
108
+ branch: string
109
+ }): Promise<void> {
110
+ await this.octokit.git.deleteRef({
111
+ owner,
112
+ repo,
113
+ ref: `heads/${branch}`
114
+ })
115
+ }
116
+
117
+ // TODO: fetch all branches with page option
118
+ async fetchBranches ({
119
+ owner,
120
+ repo
121
+ }: {
122
+ owner: string
123
+ repo: string
124
+ }): Promise<Branch[]> {
125
+ const { data } = await this.octokit.repos.listBranches({
126
+ owner,
127
+ repo,
128
+ per_page: 100
129
+ })
130
+ return data
131
+ }
132
+
133
+ async fetchLabel ({
134
+ owner,
135
+ repo,
136
+ name
137
+ }: {
138
+ owner: string
139
+ repo: string
140
+ name: string
141
+ }): Promise<Label> {
142
+ const { data } = await this.octokit.issues.getLabel({
143
+ owner,
144
+ repo,
145
+ name
146
+ })
147
+ return data
148
+ }
149
+
150
+ // TODO: fetch all pull requests with page option
151
+ async fetchPullRequests ({
152
+ owner,
153
+ repo
154
+ }: {
155
+ owner: string
156
+ repo: string
157
+ }): Promise<PullRequest[]> {
158
+ const { data } = await this.octokit.pulls.list({
159
+ owner,
160
+ repo,
161
+ per_page: 100
162
+ })
163
+ return data
164
+ }
165
+
166
+ async fetchRepository ({
167
+ owner,
168
+ repo
169
+ }: {
170
+ owner: string
171
+ repo: string
172
+ }): Promise<Repository> {
173
+ const { data } = await this.octokit.repos.get({
174
+ owner,
175
+ repo
176
+ })
177
+ return data
178
+ }
179
+
180
+ async requestReviewers ({
181
+ owner,
182
+ repo,
183
+ pullNumber,
184
+ reviewers
185
+ }: {
186
+ owner: string
187
+ repo: string
188
+ pullNumber: number
189
+ reviewers: string[]
190
+ }): Promise<void> {
191
+ await this.octokit.pulls.requestReviewers({
192
+ owner,
193
+ repo,
194
+ pull_number: pullNumber,
195
+ reviewers
196
+ })
197
+ }
198
+ }
@@ -0,0 +1,21 @@
1
+ import type { Branch } from '../../GitHub'
2
+ import { BranchFinder } from './BranchFinder'
3
+
4
+ describe('BranchFinder', () => {
5
+ describe('findByName', () => {
6
+ const branch = {
7
+ name: 'test_branch_name'
8
+ } as unknown as Branch
9
+ const branchFinder = new BranchFinder([branch])
10
+
11
+ it('returns branch if exists', () => {
12
+ const actual = branchFinder.findByName('test_branch_name')
13
+ expect(actual).toBe(branch)
14
+ })
15
+
16
+ it('returns undefined if does not exists', () => {
17
+ const actual = branchFinder.findByName('not_test_branch_name')
18
+ expect(actual).toBeUndefined()
19
+ })
20
+ })
21
+ })
@@ -0,0 +1,9 @@
1
+ import type { Branch } from '../../GitHub'
2
+
3
+ export class BranchFinder {
4
+ constructor (private readonly branches: Branch[]) {}
5
+
6
+ findByName (branchName: string): Branch | undefined {
7
+ return this.branches.find(({ name }) => name === branchName)
8
+ }
9
+ }