diff --git a/README.ZH-CN.md b/README.ZH-CN.md
index 94ecffe..5d91237 100644
--- a/README.ZH-CN.md
+++ b/README.ZH-CN.md
@@ -138,6 +138,20 @@ pr-checker -v
pr-checker -h
````
+#### -m | --mode
+使用 rebase 模式 或者 merge 模式 , 默认值是 rebase 模式
+
+> 在 `rebase` 模式中, 你可以选择仓库或这直接对所有你所提交的 `pr` 进行 `rebase` 操作
+它将调用 `/repos/${repoName}/pulls/${prNumber}/update-branch`.
+
+>在 `merge` 模式中,你可以对你所拥有的仓库(`fork` 的仓库除外)进行 `merge` 操作
+它将调用 `/repos/${repoName}/pulls/${prNumber}/merge`.
+一个典型的引用场景就是批量处理`dependabot`的 `pr` (加入到 `merge queue` 的功能还未完成)
+
+```` shell
+pr-checker run -m merge | rebase
+````
+
## 快照
diff --git a/README.md b/README.md
index 439fd4a..7159cd9 100644
--- a/README.md
+++ b/README.md
@@ -134,6 +134,20 @@ pr-checker -v
#### -h | --help
Display help message
+#### -m | --mode
+Use `rebase` mode or `merge` mode, the default value is `rebase` mode
+
+> In `rebase` mode, you can choose a repository or directly `rebase` all your submitted `pr`
+It will call `/repos/${repoName}/pulls/${prNumber}/update-branch`.
+
+>In `merge` mode, you can `merge` on repositories you own (except `fork` repositories)
+It will call `/repos/${repoName}/pulls/${prNumber}/merge`.
+A typical usage scenario is batch processing `pr` of `dependabot` (the function of adding to `merge queue` has not yet been completed)
+
+```` shell
+pr-checker run -m merge | rebase
+````
+
```` shell
pr-checker -h
````
diff --git a/packages/cli/core/index.ts b/packages/cli/core/index.ts
index b0cd78e..840a688 100644
--- a/packages/cli/core/index.ts
+++ b/packages/cli/core/index.ts
@@ -26,21 +26,27 @@ async function initCli() {
// get git config
cli.option('-g, --get', 'get git config')
- cli.command('run', 'check your pr').action(async() => {
- if (!storage.token) {
- log('error', 'use `pr-checker -t ` to set your token')
- process.exit(1)
- }
+ cli.command('run', 'check your pr')
+ // use rebase or merge
+ .option('-m , --mode ', 'use rebase or merge')
+ .action(async(options) => {
+ if (!storage.token) {
+ log('error', 'use `pr-checker -t ` to set your token')
+ process.exit(1)
+ }
- if (!storage.username) {
- log('info', 'You have not set a username, '
+ if (!storage.username) {
+ log('info', 'You have not set a username, '
+ 'it has been automatically set for you according to the token')
- const { login } = await getUserName(storage.token)
- storage.username = login
- await saveStorage()
- }
- await handleSelect(storage as Storage)
- })
+ const { login } = await getUserName(storage.token)
+ storage.username = login
+ await saveStorage()
+ }
+
+ const { m, mode } = options
+ const lastMode = m || mode || 'rebase'
+ await handleSelect(storage as Storage, lastMode)
+ })
cli.help()
cli.version(version)
return cli
diff --git a/packages/cli/core/select/handle-select.ts b/packages/cli/core/select/handle-select.ts
index e06ef28..349b89c 100644
--- a/packages/cli/core/select/handle-select.ts
+++ b/packages/cli/core/select/handle-select.ts
@@ -8,14 +8,14 @@ import type { IPRCheckRes, IPRInfo, IPRListItem } from '@pr-checker/utils/git-ap
import type { Storage } from '../store/storage'
declare type IPRSelect = Record
-export async function handleSelect(store: Storage) {
+export async function handleSelect(store: Storage, mode: 'merge' | 'rebase') {
// select type ( all Repo ?)
const isAllRepo = await promptsRun(typeOption)
let updateRes = []
const spinner = ora({ text: 'Loading Repo......', color: 'blue' }).start()
const githubApi = new GitApi(store.token, store.username!)
- const prList = await githubApi.getPRList()
+ const prList = mode === 'rebase' ? (await githubApi.getSubmitPRList()) : (await githubApi.getAllRepoPRList())
spinner.stop()
const repoList = Object.keys(prList)
@@ -26,12 +26,12 @@ export async function handleSelect(store: Storage) {
const prl = prList[selectRepo.RepoSelect as keyof typeof prList] as IPRListItem[]
// check pr
log('info', `Checking PR by ${selectRepo.RepoSelect}......`)
- const prListByRepo = await checkPR(prl, githubApi)
+ const prListByRepo = await checkPR(prl, githubApi, mode)
// select pr
const prSelectRes = await promptsRun(createPrOption(prListByRepo as IPRCheckRes[]))
// update pr
log('info', `Update PR by ${selectRepo.RepoSelect}......`)
- updateRes = await updatePR(prl, prSelectRes as IPRSelect, githubApi)
+ updateRes = await updatePR(prl, prSelectRes as IPRSelect, githubApi, mode)
} else {
const prl = [] as IPRListItem[]
repoList.forEach((val: string) => {
@@ -41,12 +41,12 @@ export async function handleSelect(store: Storage) {
})
// check pr
log('info', 'Checking PR......')
- const prListByRepo = await checkPR(prl, githubApi)
+ const prListByRepo = await checkPR(prl, githubApi, mode)
// select pr
const prSelectRes = await promptsRun(createPrOption(prListByRepo as IPRCheckRes[]))
// update pr
log('info', 'Update PR......')
- updateRes = await updatePR(prl, prSelectRes as IPRSelect, githubApi)
+ updateRes = await updatePR(prl, prSelectRes as IPRSelect, githubApi, mode)
}
spinner.succeed()
@@ -54,7 +54,7 @@ export async function handleSelect(store: Storage) {
await printUpdateRes(updateRes as IPRCheckRes[])
}
-async function checkPR(prl: IPRListItem[], githubApi: GitApi) {
+async function checkPR(prl: IPRListItem[], githubApi: GitApi, mode: 'merge' | 'rebase') {
if (prl.length === 0) {
log('error', 'Please select a pr to check')
process.exit()
@@ -63,7 +63,7 @@ async function checkPR(prl: IPRListItem[], githubApi: GitApi) {
// get pr detail data
const prInfo = await githubApi.getPRByRepo(prl[i].number, prl[i].repo, prl[i].title)
// need update pr ?
- const res = await githubApi.needUpdate(prl[i].repo, prInfo as IPRInfo)
+ const res = await githubApi.needUpdate(prl[i].repo, prInfo as IPRInfo, mode)
log('success', `✔ Check PR #${prl[i].number} completed`)
return {
...prInfo,
@@ -73,12 +73,19 @@ async function checkPR(prl: IPRListItem[], githubApi: GitApi) {
return prListByRepo
}
-async function updatePR(prl: IPRListItem[],
+async function updatePR(
+ prl: IPRListItem[],
prSelectRes: IPRSelect,
- githubApi: GitApi) {
+ githubApi: GitApi,
+ mode: 'merge' | 'rebase') {
const updateRes = await Promise.all(createRunList(prl.length, async(i: number) => {
if (prSelectRes.prSelect[i] && prSelectRes.prSelect[i].isNeedUpdate) {
- await githubApi.updatePR(prSelectRes.prSelect[i].number, prSelectRes.prSelect[i].repo)
+ if (mode === 'rebase')
+ await githubApi.rebasePR(prSelectRes.prSelect[i].number, prSelectRes.prSelect[i].repo)
+
+ if (mode === 'merge')
+ await githubApi.mergePR(prSelectRes.prSelect[i].number, prSelectRes.prSelect[i].repo)
+
log('success', `✔ Update PR #${prl[i].number} completed`)
}
return {
diff --git a/utils/git-api.ts b/utils/git-api.ts
index 23c5c90..70d92f6 100644
--- a/utils/git-api.ts
+++ b/utils/git-api.ts
@@ -1,5 +1,5 @@
import { Octokit } from '@octokit/core'
-import { log } from '@pr-checker/utils'
+import { createRunList, isEmptyObj, log } from '@pr-checker/utils'
export declare interface IPRListItem {
title: string
number: number
@@ -36,15 +36,18 @@ export class GitApi {
}
/**
- * 获取账户下所有 pr
+ * 获取账户下所有提交 pr
* @param username
*/
- async getPRList(username: string = this.owner) {
+ async getSubmitPRList(username: string = this.owner) {
try {
const { data } = await this.octokit.request('GET /search/issues', {
q: `is:pr is:open author:${username}`,
per_page: 1000,
})
+ if (!data.items || (data.items && data.items.length === 0))
+ log('error', 'You don\'t have any pull requests that are open')
+
const res = {} as Record
data.items.forEach((val: any) => {
const repo = val.repository_url.split('repos/')[1]
@@ -56,8 +59,47 @@ export class GitApi {
id: val.id,
})
})
- if (!data.items || (data.items && data.items.length === 0))
- log('error', 'You don\'t have any pull requests that are open')
+ return res
+ } catch (error: any) {
+ if (error.status === 401)
+ log('error', 'Your token is invalid or does not match your username')
+
+ log('error', error)
+ return {}
+ }
+ }
+
+ async getAllRepoPRList(username: string = this.owner) {
+ try {
+ const { data } = await this.octokit.request('GET /user/repos', {
+ type: 'owner',
+ per_page: 1000,
+ })
+ const notForkRepoList = data.filter(v => !v.fork)
+ const res = {} as Record
+ await Promise.all(createRunList(notForkRepoList.length, async(i: number) => {
+ const { data: prData } = await this.octokit.request(
+ 'GET /repos/{owner}/{repo}/pulls',
+ {
+ owner: username,
+ repo: notForkRepoList[i].name,
+ per_page: 1000,
+ })
+
+ prData.forEach((val: any) => {
+ const repo = notForkRepoList[i].full_name
+ if (!res[repo]) res[repo] = []
+ res[repo].push({
+ title: val.title,
+ number: val.number,
+ repo,
+ id: val.id,
+ })
+ })
+ }))
+
+ if (isEmptyObj(res))
+ log('error', 'You don\'t have any pull requests')
return res
} catch (error: any) {
@@ -65,7 +107,7 @@ export class GitApi {
log('error', 'Your token is invalid or does not match your username')
log('error', error)
- return []
+ return {}
}
}
@@ -108,8 +150,9 @@ export class GitApi {
* Determine whether pr wants to synchronize the upstream Repo
* @param repo_name upstream Repo name
* @param pr_info The getPRByRepo function returns the result
+ * @param mode
*/
- async needUpdate(repo_name: string, pr_info: IPRInfo) {
+ async needUpdate(repo_name: string, pr_info: IPRInfo, mode: 'rebase' | 'merge') {
try {
if (pr_info.mergeable_state === 'dirty')
return { isNeedUpdate: false, reason: 'code conflict' }
@@ -122,7 +165,8 @@ export class GitApi {
// Comparison of the default branch hash of
// the fork warehouse and the upstream warehouse hash
const cur_sha = data[0].sha
- if (pr_info.sha !== cur_sha
+ const matchedSha = (mode === 'rebase' && pr_info.sha !== cur_sha) || mode === 'merge'
+ if (matchedSha
&& pr_info.mergeable
&& !pr_info.merged)
return { isNeedUpdate: true, reason: '--' }
@@ -142,7 +186,7 @@ export class GitApi {
* @param pull_number
* @param repo_name
*/
- async updatePR(pull_number: number, repo_name: string) {
+ async rebasePR(pull_number: number, repo_name: string) {
try {
const res = await this.octokit.request(
`PUT /repos/${repo_name}/pulls/{pull_number}/update-branch`,
@@ -156,6 +200,21 @@ export class GitApi {
return {}
}
}
+
+ async mergePR(pull_number: number, repo_name: string) {
+ try {
+ const res = await this.octokit.request(
+ `PUT /repos/${repo_name}/pulls/{pull_number}/merge`,
+ {
+ pull_number,
+ },
+ )
+ return res.data.message
+ } catch (error: any) {
+ log('error', error)
+ return {}
+ }
+ }
}
export async function getUserName(token: string) {
diff --git a/utils/index.ts b/utils/index.ts
index 40e3400..60ab8fb 100644
--- a/utils/index.ts
+++ b/utils/index.ts
@@ -3,7 +3,7 @@ export * from './git-api'
export function createRunList(
taskNum: number,
- taskFunc: (index: number) => Promise>) {
+ taskFunc: (index: number) => Promise | void>) {
const taskList = [] as Array>
for (let i = 0; i < taskNum; i++) {
taskList.push(new Promise((resolve) => {