npm-update-package 0.1.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/.eslintignore +3 -0
- package/.eslintrc.js +23 -0
- package/.github/renovate.json +15 -0
- package/.github/workflows/eslint.yml +14 -0
- package/.github/workflows/test.yml +19 -0
- package/.husky/pre-commit +4 -0
- package/.nvmrc +1 -0
- package/LICENSE +22 -0
- package/README.md +43 -0
- package/dist/app.js +8 -0
- package/dist/bin.js +18 -0
- package/dist/enums/LogLevel.js +10 -0
- package/dist/enums/PackageManagerName.js +10 -0
- package/dist/enums/UpdateType.js +11 -0
- package/dist/enums/index.js +12 -0
- package/dist/git/Committer.js +31 -0
- package/dist/git/Git.js +48 -0
- package/dist/git/GitRepository.js +41 -0
- package/dist/git/index.js +7 -0
- package/dist/github/Branch.js +2 -0
- package/dist/github/GitHub.js +22 -0
- package/dist/github/PullRequest.js +2 -0
- package/dist/github/PullRequestCreator.js +31 -0
- package/dist/github/RemoteBranchExistenceChecker.js +17 -0
- package/dist/github/Repository.js +2 -0
- package/dist/github/createGitHub.js +14 -0
- package/dist/github/createOctokit.js +24 -0
- package/dist/github/createPullRequestBody.js +19 -0
- package/dist/github/createPullRequestTitle.js +10 -0
- package/dist/github/index.js +11 -0
- package/dist/logger/Logger.js +2 -0
- package/dist/logger/createLogger.js +11 -0
- package/dist/logger/index.js +5 -0
- package/dist/main.js +83 -0
- package/dist/ncu/Ncu.js +38 -0
- package/dist/ncu/NcuOutdatedPackages.js +2 -0
- package/dist/ncu/NcuOutdatedPackagesConverter.js +26 -0
- package/dist/ncu/index.js +5 -0
- package/dist/ncu/isNcuOutdatedPackages.js +8 -0
- package/dist/ncu/toUpdateType.js +17 -0
- package/dist/options/Options.js +16 -0
- package/dist/options/index.js +5 -0
- package/dist/options/initOptions.js +27 -0
- package/dist/outdated-package-processor/OutdatedPackageProcessor.js +56 -0
- package/dist/outdated-package-processor/createBranchName.js +9 -0
- package/dist/outdated-package-processor/createCommitMessage.js +10 -0
- package/dist/outdated-package-processor/index.js +5 -0
- package/dist/outdated-packages-processor/OutdatedPackagesProcessor.js +19 -0
- package/dist/outdated-packages-processor/index.js +5 -0
- package/dist/package-manager/Npm.js +20 -0
- package/dist/package-manager/PackageManager.js +2 -0
- package/dist/package-manager/Yarn.js +20 -0
- package/dist/package-manager/createPackageManager.js +15 -0
- package/dist/package-manager/index.js +9 -0
- package/dist/read-package-json/Package.js +18 -0
- package/dist/read-package-json/PackageDependencies.js +6 -0
- package/dist/read-package-json/index.js +5 -0
- package/dist/read-package-json/parsePackageJson.js +15 -0
- package/dist/read-package-json/readFile.js +12 -0
- package/dist/read-package-json/readPackageJson.js +11 -0
- package/dist/terminal/Terminal.js +29 -0
- package/dist/terminal/index.js +5 -0
- package/dist/terminal/isExecaReturnValue.js +24 -0
- package/dist/types/OutdatedPackage.js +2 -0
- package/dist/types/Result.js +2 -0
- package/dist/types/index.js +2 -0
- package/dist/values/PackageVersion.js +25 -0
- package/dist/values/index.js +5 -0
- package/jest.config.ts +11 -0
- package/lint-staged.config.js +4 -0
- package/package.json +59 -0
- package/src/app.ts +5 -0
- package/src/bin.ts +19 -0
- package/src/enums/LogLevel.ts +8 -0
- package/src/enums/PackageManagerName.ts +8 -0
- package/src/enums/UpdateType.ts +9 -0
- package/src/enums/index.ts +12 -0
- package/src/git/Committer.ts +49 -0
- package/src/git/Git.ts +55 -0
- package/src/git/GitRepository.test.ts +61 -0
- package/src/git/GitRepository.ts +57 -0
- package/src/git/index.ts +3 -0
- package/src/github/Branch.ts +4 -0
- package/src/github/GitHub.ts +27 -0
- package/src/github/PullRequest.ts +3 -0
- package/src/github/PullRequestCreator.ts +57 -0
- package/src/github/RemoteBranchExistenceChecker.ts +15 -0
- package/src/github/Repository.ts +3 -0
- package/src/github/createGitHub.ts +18 -0
- package/src/github/createOctokit.ts +28 -0
- package/src/github/createPullRequestBody.test.ts +62 -0
- package/src/github/createPullRequestBody.ts +17 -0
- package/src/github/createPullRequestTitle.test.ts +43 -0
- package/src/github/createPullRequestTitle.ts +8 -0
- package/src/github/index.ts +7 -0
- package/src/logger/Logger.ts +1 -0
- package/src/logger/createLogger.ts +10 -0
- package/src/logger/index.ts +2 -0
- package/src/main.ts +105 -0
- package/src/ncu/Ncu.ts +41 -0
- package/src/ncu/NcuOutdatedPackages.ts +6 -0
- package/src/ncu/NcuOutdatedPackagesConverter.ts +25 -0
- package/src/ncu/index.ts +1 -0
- package/src/ncu/isNcuOutdatedPackages.ts +6 -0
- package/src/ncu/toUpdateType.test.ts +21 -0
- package/src/ncu/toUpdateType.ts +18 -0
- package/src/options/Options.ts +24 -0
- package/src/options/index.ts +2 -0
- package/src/options/initOptions.ts +34 -0
- package/src/outdated-package-processor/OutdatedPackageProcessor.ts +101 -0
- package/src/outdated-package-processor/createBranchName.test.ts +14 -0
- package/src/outdated-package-processor/createBranchName.ts +7 -0
- package/src/outdated-package-processor/createCommitMessage.test.ts +43 -0
- package/src/outdated-package-processor/createCommitMessage.ts +8 -0
- package/src/outdated-package-processor/index.ts +1 -0
- package/src/outdated-packages-processor/OutdatedPackagesProcessor.ts +34 -0
- package/src/outdated-packages-processor/index.ts +1 -0
- package/src/package-manager/Npm.ts +19 -0
- package/src/package-manager/PackageManager.ts +4 -0
- package/src/package-manager/Yarn.ts +19 -0
- package/src/package-manager/createPackageManager.ts +21 -0
- package/src/package-manager/index.ts +4 -0
- package/src/read-package-json/Package.ts +24 -0
- package/src/read-package-json/PackageDependencies.ts +10 -0
- package/src/read-package-json/index.ts +2 -0
- package/src/read-package-json/parsePackageJson.ts +13 -0
- package/src/read-package-json/readFile.ts +6 -0
- package/src/read-package-json/readPackageJson.ts +9 -0
- package/src/terminal/Terminal.ts +30 -0
- package/src/terminal/index.ts +1 -0
- package/src/terminal/isExecaReturnValue.ts +30 -0
- package/src/types/OutdatedPackage.ts +9 -0
- package/src/types/Result.ts +7 -0
- package/src/types/index.ts +2 -0
- package/src/values/PackageVersion.test.ts +25 -0
- package/src/values/PackageVersion.ts +40 -0
- package/src/values/index.ts +1 -0
- package/tsconfig.base.json +3 -0
- package/tsconfig.build.json +13 -0
- package/tsconfig.json +9 -0
package/src/main.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Committer,
|
|
3
|
+
Git
|
|
4
|
+
} from './git'
|
|
5
|
+
import {
|
|
6
|
+
createGitHub,
|
|
7
|
+
PullRequestCreator,
|
|
8
|
+
RemoteBranchExistenceChecker
|
|
9
|
+
} from './github'
|
|
10
|
+
import type { Logger } from './logger'
|
|
11
|
+
import { Ncu } from './ncu'
|
|
12
|
+
import type { Options } from './options'
|
|
13
|
+
import { OutdatedPackageProcessor } from './outdated-package-processor'
|
|
14
|
+
import { OutdatedPackagesProcessor } from './outdated-packages-processor'
|
|
15
|
+
import { createPackageManager } from './package-manager'
|
|
16
|
+
import { Terminal } from './terminal'
|
|
17
|
+
|
|
18
|
+
// TODO: add test
|
|
19
|
+
export const main = async ({
|
|
20
|
+
options,
|
|
21
|
+
logger
|
|
22
|
+
}: {
|
|
23
|
+
options: Options
|
|
24
|
+
logger: Logger
|
|
25
|
+
}): Promise<void> => {
|
|
26
|
+
logger.debug(`options=${JSON.stringify(options)}`)
|
|
27
|
+
|
|
28
|
+
const ncu = new Ncu()
|
|
29
|
+
const outdatedPackages = await ncu.check()
|
|
30
|
+
logger.debug(`outdatedPackages=${JSON.stringify(outdatedPackages)}`)
|
|
31
|
+
|
|
32
|
+
if (outdatedPackages.length === 0) {
|
|
33
|
+
logger.info('All packages are up-to-date.')
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
logger.info(`There are ${outdatedPackages.length} outdated packages.`)
|
|
38
|
+
|
|
39
|
+
const terminal = new Terminal()
|
|
40
|
+
const git = new Git(terminal)
|
|
41
|
+
const gitRepo = await git.getRepository()
|
|
42
|
+
logger.debug(`gitRepo=${JSON.stringify(gitRepo)}`)
|
|
43
|
+
|
|
44
|
+
const github = createGitHub({
|
|
45
|
+
repository: gitRepo,
|
|
46
|
+
token: options.githubToken
|
|
47
|
+
})
|
|
48
|
+
const githubRepo = await github.fetchRepository({
|
|
49
|
+
owner: gitRepo.owner,
|
|
50
|
+
repo: gitRepo.name
|
|
51
|
+
})
|
|
52
|
+
logger.debug(`githubRepo=${JSON.stringify(githubRepo)}`)
|
|
53
|
+
|
|
54
|
+
const remoteBranches = await github.fetchBranches({
|
|
55
|
+
owner: gitRepo.owner,
|
|
56
|
+
repo: gitRepo.name
|
|
57
|
+
})
|
|
58
|
+
logger.debug(`remoteBranches=${JSON.stringify(remoteBranches)}`)
|
|
59
|
+
|
|
60
|
+
const remoteBranchExistenceChecker = RemoteBranchExistenceChecker.of(remoteBranches)
|
|
61
|
+
const committer = new Committer({
|
|
62
|
+
git,
|
|
63
|
+
user: {
|
|
64
|
+
name: options.gitUserName,
|
|
65
|
+
email: options.gitUserEmail
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
const packageManager = createPackageManager({
|
|
69
|
+
terminal,
|
|
70
|
+
packageManager: options.packageManager
|
|
71
|
+
})
|
|
72
|
+
const pullRequestCreator = new PullRequestCreator({
|
|
73
|
+
github,
|
|
74
|
+
gitRepo,
|
|
75
|
+
githubRepo,
|
|
76
|
+
logger
|
|
77
|
+
})
|
|
78
|
+
const outdatedPackageProcessor = new OutdatedPackageProcessor({
|
|
79
|
+
committer,
|
|
80
|
+
git,
|
|
81
|
+
ncu,
|
|
82
|
+
packageManager,
|
|
83
|
+
pullRequestCreator,
|
|
84
|
+
remoteBranchExistenceChecker,
|
|
85
|
+
logger
|
|
86
|
+
})
|
|
87
|
+
const outdatedPackagesProcessor = new OutdatedPackagesProcessor({
|
|
88
|
+
outdatedPackageProcessor,
|
|
89
|
+
logger
|
|
90
|
+
})
|
|
91
|
+
const results = await outdatedPackagesProcessor.process(outdatedPackages)
|
|
92
|
+
logger.debug(`results=${JSON.stringify(results)}`)
|
|
93
|
+
|
|
94
|
+
const updatedPackages = results
|
|
95
|
+
.filter(({ updated }) => updated)
|
|
96
|
+
.map(({ outdatedPackage }) => outdatedPackage)
|
|
97
|
+
logger.debug(`updatedPackages=${JSON.stringify(updatedPackages)}`)
|
|
98
|
+
|
|
99
|
+
const skippedPackages = results
|
|
100
|
+
.filter(({ skipped }) => skipped)
|
|
101
|
+
.map(({ outdatedPackage }) => outdatedPackage)
|
|
102
|
+
logger.debug(`skippedPackages=${JSON.stringify(skippedPackages)}`)
|
|
103
|
+
|
|
104
|
+
logger.info(`${updatedPackages.length} packages has updated. ${skippedPackages.length} packages has skipped.`)
|
|
105
|
+
}
|
package/src/ncu/Ncu.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { run } from 'npm-check-updates'
|
|
2
|
+
import type { Options } from 'npm-check-updates/build/src/types'
|
|
3
|
+
import { readPackageJson } from '../read-package-json'
|
|
4
|
+
import type { OutdatedPackage } from '../types'
|
|
5
|
+
import { isNcuOutdatedPackages } from './isNcuOutdatedPackages'
|
|
6
|
+
import { NcuOutdatedPackagesConverter } from './NcuOutdatedPackagesConverter'
|
|
7
|
+
|
|
8
|
+
// TODO: add test
|
|
9
|
+
export class Ncu {
|
|
10
|
+
async check (): Promise<OutdatedPackage[]> {
|
|
11
|
+
return await this.run({
|
|
12
|
+
jsonUpgraded: true
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async update (outdatedPackage: OutdatedPackage): Promise<OutdatedPackage[]> {
|
|
17
|
+
return await this.run({
|
|
18
|
+
jsonUpgraded: true,
|
|
19
|
+
filter: outdatedPackage.name,
|
|
20
|
+
upgrade: true
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private async run (options: Options): Promise<OutdatedPackage[]> {
|
|
25
|
+
const pkg = await readPackageJson('./package.json')
|
|
26
|
+
const currentDependencies = {
|
|
27
|
+
...pkg.dependencies,
|
|
28
|
+
...pkg.devDependencies,
|
|
29
|
+
...pkg.peerDependencies,
|
|
30
|
+
...pkg.optionalDependencies
|
|
31
|
+
}
|
|
32
|
+
const result = await run(options)
|
|
33
|
+
|
|
34
|
+
if (!isNcuOutdatedPackages(result)) {
|
|
35
|
+
throw new Error('result is not NcuOutdatedPackages')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const ncuOutdatedPackagesConverter = new NcuOutdatedPackagesConverter(currentDependencies)
|
|
39
|
+
return ncuOutdatedPackagesConverter.toOutdatedPackages(result)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PackageDependencies } from '../read-package-json'
|
|
2
|
+
import type { OutdatedPackage } from '../types'
|
|
3
|
+
import { PackageVersion } from '../values'
|
|
4
|
+
import type { NcuOutdatedPackages } from './NcuOutdatedPackages'
|
|
5
|
+
import { toUpdateType } from './toUpdateType'
|
|
6
|
+
|
|
7
|
+
// TODO: add test
|
|
8
|
+
export class NcuOutdatedPackagesConverter {
|
|
9
|
+
constructor (private readonly currentDependencies: PackageDependencies) {}
|
|
10
|
+
|
|
11
|
+
toOutdatedPackages (outdatedPackages: NcuOutdatedPackages): OutdatedPackage[] {
|
|
12
|
+
return Object.entries(outdatedPackages)
|
|
13
|
+
.map(([name, newVersion]) => ({
|
|
14
|
+
name,
|
|
15
|
+
currentVersion: PackageVersion.of(this.currentDependencies[name]),
|
|
16
|
+
newVersion: PackageVersion.of(newVersion)
|
|
17
|
+
}))
|
|
18
|
+
.map(({ name, currentVersion, newVersion }) => ({
|
|
19
|
+
name,
|
|
20
|
+
currentVersion,
|
|
21
|
+
newVersion,
|
|
22
|
+
type: toUpdateType(currentVersion, newVersion)
|
|
23
|
+
}))
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/ncu/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Ncu } from './Ncu'
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { NcuOutdatedPackages } from './NcuOutdatedPackages'
|
|
2
|
+
|
|
3
|
+
// TODO: add test
|
|
4
|
+
export const isNcuOutdatedPackages = (value: unknown): value is NcuOutdatedPackages => {
|
|
5
|
+
return typeof value === 'object' && value !== null && Object.values(value).every(value => typeof value === 'string')
|
|
6
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { UpdateType } from '../enums'
|
|
2
|
+
import { PackageVersion } from '../values'
|
|
3
|
+
import { toUpdateType } from './toUpdateType'
|
|
4
|
+
|
|
5
|
+
describe('toUpdateType', () => {
|
|
6
|
+
const currentVersion = PackageVersion.of('1.0.0')
|
|
7
|
+
|
|
8
|
+
describe('if both versions are different', () => {
|
|
9
|
+
it('returns UpdateType', () => {
|
|
10
|
+
expect(toUpdateType(currentVersion, PackageVersion.of('1.0.1'))).toBe(UpdateType.Patch)
|
|
11
|
+
expect(toUpdateType(currentVersion, PackageVersion.of('1.1.1'))).toBe(UpdateType.Minor)
|
|
12
|
+
expect(toUpdateType(currentVersion, PackageVersion.of('2.1.1'))).toBe(UpdateType.Major)
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('if both versions are same', () => {
|
|
17
|
+
it('throws error', () => {
|
|
18
|
+
expect(() => toUpdateType(currentVersion, PackageVersion.of('1.0.0'))).toThrow(Error)
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { UpdateType } from '../enums'
|
|
2
|
+
import type { PackageVersion } from '../values'
|
|
3
|
+
|
|
4
|
+
export const toUpdateType = (currentVersion: PackageVersion, newVersion: PackageVersion): UpdateType => {
|
|
5
|
+
if (currentVersion.major !== newVersion.major) {
|
|
6
|
+
return UpdateType.Major
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if (currentVersion.minor !== newVersion.minor) {
|
|
10
|
+
return UpdateType.Minor
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (currentVersion.patch !== newVersion.patch) {
|
|
14
|
+
return UpdateType.Patch
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
throw new Error('Both versions are same.')
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
intersection,
|
|
3
|
+
literal,
|
|
4
|
+
partial,
|
|
5
|
+
string,
|
|
6
|
+
type,
|
|
7
|
+
union
|
|
8
|
+
} from 'io-ts'
|
|
9
|
+
import type { TypeOf } from 'io-ts'
|
|
10
|
+
|
|
11
|
+
export const Options = intersection([
|
|
12
|
+
type({
|
|
13
|
+
githubToken: string,
|
|
14
|
+
logLevel: union([literal('info'), literal('debug')]),
|
|
15
|
+
packageManager: union([literal('npm'), literal('yarn')])
|
|
16
|
+
}),
|
|
17
|
+
partial({
|
|
18
|
+
gitUserEmail: string,
|
|
19
|
+
gitUserName: string
|
|
20
|
+
})
|
|
21
|
+
])
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
23
|
+
export type Options = TypeOf<typeof Options>
|
|
24
|
+
export const isOptions = Options.is
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Option,
|
|
3
|
+
program
|
|
4
|
+
} from 'commander'
|
|
5
|
+
import { app } from '../app'
|
|
6
|
+
import { isOptions } from './Options'
|
|
7
|
+
import type { Options } from './Options'
|
|
8
|
+
|
|
9
|
+
// TODO: add test
|
|
10
|
+
export const initOptions = (): Options => {
|
|
11
|
+
program
|
|
12
|
+
.version(app.version)
|
|
13
|
+
.option('--git-user-email <value>', 'User email of commit')
|
|
14
|
+
.option('--git-user-name <value>', 'User name of commit')
|
|
15
|
+
.requiredOption('--github-token <value>', 'GitHub token')
|
|
16
|
+
.addOption(
|
|
17
|
+
new Option('--log-level <value>', 'Log level to show')
|
|
18
|
+
.choices(['info', 'debug'])
|
|
19
|
+
.default('info')
|
|
20
|
+
)
|
|
21
|
+
.addOption(
|
|
22
|
+
new Option('--package-manager <value>', 'Package manager of your project')
|
|
23
|
+
.choices(['npm', 'yarn'])
|
|
24
|
+
.default('npm')
|
|
25
|
+
)
|
|
26
|
+
program.parse(process.argv)
|
|
27
|
+
const options = program.opts()
|
|
28
|
+
|
|
29
|
+
if (!isOptions(options)) {
|
|
30
|
+
throw new Error(`Failed to parse command-line options. options=${JSON.stringify(options)}`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return options
|
|
34
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Committer,
|
|
3
|
+
Git
|
|
4
|
+
} from '../git'
|
|
5
|
+
import type {
|
|
6
|
+
PullRequestCreator,
|
|
7
|
+
RemoteBranchExistenceChecker
|
|
8
|
+
} from '../github'
|
|
9
|
+
import type { Logger } from '../logger'
|
|
10
|
+
import type { Ncu } from '../ncu'
|
|
11
|
+
import type { PackageManager } from '../package-manager'
|
|
12
|
+
import type {
|
|
13
|
+
OutdatedPackage,
|
|
14
|
+
Result
|
|
15
|
+
} from '../types'
|
|
16
|
+
import { createBranchName } from './createBranchName'
|
|
17
|
+
import { createCommitMessage } from './createCommitMessage'
|
|
18
|
+
|
|
19
|
+
// TODO: add test
|
|
20
|
+
export class OutdatedPackageProcessor {
|
|
21
|
+
private readonly committer: Committer
|
|
22
|
+
private readonly git: Git
|
|
23
|
+
private readonly ncu: Ncu
|
|
24
|
+
private readonly packageManager: PackageManager
|
|
25
|
+
private readonly pullRequestCreator: PullRequestCreator
|
|
26
|
+
private readonly remoteBranchExistenceChecker: RemoteBranchExistenceChecker
|
|
27
|
+
private readonly logger: Logger
|
|
28
|
+
|
|
29
|
+
constructor ({
|
|
30
|
+
committer,
|
|
31
|
+
git,
|
|
32
|
+
ncu,
|
|
33
|
+
packageManager,
|
|
34
|
+
pullRequestCreator,
|
|
35
|
+
remoteBranchExistenceChecker,
|
|
36
|
+
logger
|
|
37
|
+
}: {
|
|
38
|
+
committer: Committer
|
|
39
|
+
git: Git
|
|
40
|
+
ncu: Ncu
|
|
41
|
+
packageManager: PackageManager
|
|
42
|
+
pullRequestCreator: PullRequestCreator
|
|
43
|
+
remoteBranchExistenceChecker: RemoteBranchExistenceChecker
|
|
44
|
+
logger: Logger
|
|
45
|
+
}) {
|
|
46
|
+
this.committer = committer
|
|
47
|
+
this.git = git
|
|
48
|
+
this.ncu = ncu
|
|
49
|
+
this.packageManager = packageManager
|
|
50
|
+
this.pullRequestCreator = pullRequestCreator
|
|
51
|
+
this.remoteBranchExistenceChecker = remoteBranchExistenceChecker
|
|
52
|
+
this.logger = logger
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Don't run in parallel because it includes file operations.
|
|
57
|
+
*/
|
|
58
|
+
async process (outdatedPackage: OutdatedPackage): Promise<Result> {
|
|
59
|
+
const branchName = createBranchName(outdatedPackage)
|
|
60
|
+
this.logger.debug(`branchName=${branchName}`)
|
|
61
|
+
|
|
62
|
+
if (this.remoteBranchExistenceChecker.check(branchName)) {
|
|
63
|
+
this.logger.info(`Skip ${outdatedPackage.name} because ${branchName} branch already exists on remote.`)
|
|
64
|
+
return {
|
|
65
|
+
outdatedPackage,
|
|
66
|
+
skipped: true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await this.git.createBranch(branchName)
|
|
71
|
+
this.logger.info(`${branchName} branch has created.`)
|
|
72
|
+
|
|
73
|
+
const updatedPackages = await this.ncu.update(outdatedPackage)
|
|
74
|
+
|
|
75
|
+
if (updatedPackages.length !== 1) {
|
|
76
|
+
throw new Error(`Failed to update ${outdatedPackage.name}.`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await this.packageManager.install()
|
|
80
|
+
this.logger.info(`${outdatedPackage.name} has updated from v${outdatedPackage.currentVersion.version} to v${outdatedPackage.newVersion.version}`)
|
|
81
|
+
|
|
82
|
+
await this.git.add(...this.packageManager.packageFiles)
|
|
83
|
+
const message = createCommitMessage(outdatedPackage)
|
|
84
|
+
this.logger.debug(`message=${message}`)
|
|
85
|
+
|
|
86
|
+
await this.committer.commit(message)
|
|
87
|
+
await this.git.push(branchName)
|
|
88
|
+
await this.pullRequestCreator.create({
|
|
89
|
+
outdatedPackage,
|
|
90
|
+
branchName
|
|
91
|
+
})
|
|
92
|
+
await this.git.checkout('-')
|
|
93
|
+
await this.git.removeBranch(branchName)
|
|
94
|
+
this.logger.info(`${branchName} branch has removed.`)
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
outdatedPackage,
|
|
98
|
+
updated: true
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PackageVersion } from '../values'
|
|
2
|
+
import { createBranchName } from './createBranchName'
|
|
3
|
+
|
|
4
|
+
describe('createBranchName', () => {
|
|
5
|
+
it('returns branch name', () => {
|
|
6
|
+
const actual = createBranchName({
|
|
7
|
+
name: '@typescript-eslint/eslint-plugin',
|
|
8
|
+
currentVersion: PackageVersion.of('1.0.0'),
|
|
9
|
+
newVersion: PackageVersion.of('1.2.3'),
|
|
10
|
+
type: 'major'
|
|
11
|
+
})
|
|
12
|
+
expect(actual).toBe('npm-update-package/@typescript-eslint/eslint-plugin/v1.2.3')
|
|
13
|
+
})
|
|
14
|
+
})
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { OutdatedPackage } from '../types'
|
|
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,43 @@
|
|
|
1
|
+
import { UpdateType } from '../enums'
|
|
2
|
+
import { PackageVersion } from '../values'
|
|
3
|
+
import { createCommitMessage } from './createCommitMessage'
|
|
4
|
+
|
|
5
|
+
describe('createCommitMessage', () => {
|
|
6
|
+
const currentVersion = PackageVersion.of('1.0.0')
|
|
7
|
+
|
|
8
|
+
describe('if update type is patch', () => {
|
|
9
|
+
it('returns commit message', () => {
|
|
10
|
+
const actual = createCommitMessage({
|
|
11
|
+
name: '@typescript-eslint/eslint-plugin',
|
|
12
|
+
currentVersion,
|
|
13
|
+
newVersion: PackageVersion.of('1.0.1'),
|
|
14
|
+
type: UpdateType.Patch
|
|
15
|
+
})
|
|
16
|
+
expect(actual).toBe('chore(deps): patch update @typescript-eslint/eslint-plugin to v1.0.1')
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('if update type is minor', () => {
|
|
21
|
+
it('returns commit message', () => {
|
|
22
|
+
const actual = createCommitMessage({
|
|
23
|
+
name: '@typescript-eslint/eslint-plugin',
|
|
24
|
+
currentVersion,
|
|
25
|
+
newVersion: PackageVersion.of('1.1.0'),
|
|
26
|
+
type: UpdateType.Minor
|
|
27
|
+
})
|
|
28
|
+
expect(actual).toBe('chore(deps): minor update @typescript-eslint/eslint-plugin to v1.1.0')
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
describe('if update type is major', () => {
|
|
33
|
+
it('returns commit message', () => {
|
|
34
|
+
const actual = createCommitMessage({
|
|
35
|
+
name: '@typescript-eslint/eslint-plugin',
|
|
36
|
+
currentVersion,
|
|
37
|
+
newVersion: PackageVersion.of('2.0.0'),
|
|
38
|
+
type: UpdateType.Major
|
|
39
|
+
})
|
|
40
|
+
expect(actual).toBe('chore(deps): major update @typescript-eslint/eslint-plugin to v2.0.0')
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { OutdatedPackage } from '../types'
|
|
2
|
+
|
|
3
|
+
export const createCommitMessage = (outdatedPackage: OutdatedPackage): string => {
|
|
4
|
+
const packageName = outdatedPackage.name
|
|
5
|
+
const newVersion = outdatedPackage.newVersion.version
|
|
6
|
+
const updateType = outdatedPackage.type
|
|
7
|
+
return `chore(deps): ${updateType} update ${packageName} to v${newVersion}`
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { OutdatedPackageProcessor } from './OutdatedPackageProcessor'
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Logger } from '../logger'
|
|
2
|
+
import type { OutdatedPackageProcessor } from '../outdated-package-processor'
|
|
3
|
+
import type {
|
|
4
|
+
OutdatedPackage,
|
|
5
|
+
Result
|
|
6
|
+
} from '../types'
|
|
7
|
+
|
|
8
|
+
export class OutdatedPackagesProcessor {
|
|
9
|
+
private readonly outdatedPackageProcessor: OutdatedPackageProcessor
|
|
10
|
+
private readonly logger: Logger
|
|
11
|
+
|
|
12
|
+
constructor ({
|
|
13
|
+
outdatedPackageProcessor,
|
|
14
|
+
logger
|
|
15
|
+
}: {
|
|
16
|
+
outdatedPackageProcessor: OutdatedPackageProcessor
|
|
17
|
+
logger: Logger
|
|
18
|
+
}) {
|
|
19
|
+
this.outdatedPackageProcessor = outdatedPackageProcessor
|
|
20
|
+
this.logger = logger
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async process (outdatedPackages: OutdatedPackage[]): Promise<Result[]> {
|
|
24
|
+
const results: Result[] = []
|
|
25
|
+
|
|
26
|
+
for (const outdatedPackage of outdatedPackages) {
|
|
27
|
+
this.logger.debug(`outdatedPackage=${JSON.stringify(outdatedPackage)}`)
|
|
28
|
+
const result = await this.outdatedPackageProcessor.process(outdatedPackage)
|
|
29
|
+
results.push(result)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return results
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { OutdatedPackagesProcessor } from './OutdatedPackagesProcessor'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Terminal } from '../terminal'
|
|
2
|
+
import type { PackageManager } from './PackageManager'
|
|
3
|
+
|
|
4
|
+
// TODO: add test
|
|
5
|
+
export class Npm implements PackageManager {
|
|
6
|
+
readonly packageFiles = [
|
|
7
|
+
'package.json',
|
|
8
|
+
'package-lock.json'
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
constructor (private readonly terminal: Terminal) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @see https://docs.npmjs.com/cli/v8/commands/npm-install
|
|
15
|
+
*/
|
|
16
|
+
async install (): Promise<void> {
|
|
17
|
+
await this.terminal.run('npm', 'install')
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Terminal } from '../terminal'
|
|
2
|
+
import type { PackageManager } from './PackageManager'
|
|
3
|
+
|
|
4
|
+
// TODO: add test
|
|
5
|
+
export class Yarn implements PackageManager {
|
|
6
|
+
readonly packageFiles = [
|
|
7
|
+
'package.json',
|
|
8
|
+
'yarn.lock'
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
constructor (private readonly terminal: Terminal) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @see https://classic.yarnpkg.com/en/docs/cli/install
|
|
15
|
+
*/
|
|
16
|
+
async install (): Promise<void> {
|
|
17
|
+
await this.terminal.run('yarn', 'install')
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PackageManagerName } from '../enums'
|
|
2
|
+
import type { Terminal } from '../terminal'
|
|
3
|
+
import { Npm } from './Npm'
|
|
4
|
+
import type { PackageManager } from './PackageManager'
|
|
5
|
+
import { Yarn } from './Yarn'
|
|
6
|
+
|
|
7
|
+
// TODO: add test
|
|
8
|
+
export const createPackageManager = ({
|
|
9
|
+
terminal,
|
|
10
|
+
packageManager
|
|
11
|
+
}: {
|
|
12
|
+
terminal: Terminal
|
|
13
|
+
packageManager: PackageManagerName
|
|
14
|
+
}): PackageManager => {
|
|
15
|
+
switch (packageManager) {
|
|
16
|
+
case 'npm':
|
|
17
|
+
return new Npm(terminal)
|
|
18
|
+
case 'yarn':
|
|
19
|
+
return new Yarn(terminal)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
intersection,
|
|
3
|
+
partial,
|
|
4
|
+
string,
|
|
5
|
+
type
|
|
6
|
+
} from 'io-ts'
|
|
7
|
+
import type { TypeOf } from 'io-ts'
|
|
8
|
+
import { PackageDependencies } from './PackageDependencies'
|
|
9
|
+
|
|
10
|
+
export const Package = intersection([
|
|
11
|
+
type({
|
|
12
|
+
name: string,
|
|
13
|
+
version: string
|
|
14
|
+
}),
|
|
15
|
+
partial({
|
|
16
|
+
dependencies: PackageDependencies,
|
|
17
|
+
devDependencies: PackageDependencies,
|
|
18
|
+
peerDependencies: PackageDependencies,
|
|
19
|
+
optionalDependencies: PackageDependencies
|
|
20
|
+
})
|
|
21
|
+
])
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
23
|
+
export type Package = TypeOf<typeof Package>
|
|
24
|
+
export const isPackage = Package.is
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
record,
|
|
3
|
+
string
|
|
4
|
+
} from 'io-ts'
|
|
5
|
+
import type { TypeOf } from 'io-ts'
|
|
6
|
+
|
|
7
|
+
export const PackageDependencies = record(string, string)
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
9
|
+
export type PackageDependencies = TypeOf<typeof PackageDependencies>
|
|
10
|
+
export const isPackageDependencies = PackageDependencies.is
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { isPackage } from './Package'
|
|
2
|
+
import type { Package } from './Package'
|
|
3
|
+
|
|
4
|
+
// TODO: add test
|
|
5
|
+
export const parsePackageJson = (json: string): Package => {
|
|
6
|
+
const parsed: unknown = JSON.parse(json)
|
|
7
|
+
|
|
8
|
+
if (isPackage(parsed)) {
|
|
9
|
+
return parsed
|
|
10
|
+
} else {
|
|
11
|
+
throw new Error(`Failed to parse package.json. json=${json}`)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Package } from './Package'
|
|
2
|
+
import { parsePackageJson } from './parsePackageJson'
|
|
3
|
+
import { readFile } from './readFile'
|
|
4
|
+
|
|
5
|
+
// TODO: add test
|
|
6
|
+
export const readPackageJson = async (filePath: string): Promise<Package> => {
|
|
7
|
+
const json = await readFile(filePath)
|
|
8
|
+
return parsePackageJson(json)
|
|
9
|
+
}
|