mirror of
https://forgejo.stefka.eu/jiriks74/create-pull-request.git
synced 2025-01-18 16:01:06 +01:00
Convert action to typescript
This commit is contained in:
parent
40e70b8f7b
commit
4ba9ca3d10
19 changed files with 12517 additions and 4466 deletions
192
src/create-or-update-branch.ts
Normal file
192
src/create-or-update-branch.ts
Normal file
|
@ -0,0 +1,192 @@
|
|||
import * as core from '@actions/core'
|
||||
import {GitCommandManager} from './git-command-manager'
|
||||
import {v4 as uuidv4} from 'uuid'
|
||||
|
||||
const CHERRYPICK_EMPTY =
|
||||
'The previous cherry-pick is now empty, possibly due to conflict resolution.'
|
||||
|
||||
export async function tryFetch(
|
||||
git: GitCommandManager,
|
||||
branch: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
await git.fetch([`${branch}:refs/remotes/origin/${branch}`])
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if branch2 is ahead of branch1
|
||||
async function isAhead(
|
||||
git: GitCommandManager,
|
||||
branch1: string,
|
||||
branch2: string
|
||||
): Promise<boolean> {
|
||||
const result = await git.revList(
|
||||
[`${branch1}...${branch2}`],
|
||||
['--right-only', '--count']
|
||||
)
|
||||
return Number(result) > 0
|
||||
}
|
||||
|
||||
// Return true if branch2 is behind branch1
|
||||
async function isBehind(
|
||||
git: GitCommandManager,
|
||||
branch1: string,
|
||||
branch2: string
|
||||
): Promise<boolean> {
|
||||
const result = await git.revList(
|
||||
[`${branch1}...${branch2}`],
|
||||
['--left-only', '--count']
|
||||
)
|
||||
return Number(result) > 0
|
||||
}
|
||||
|
||||
// Return true if branch2 is even with branch1
|
||||
async function isEven(
|
||||
git: GitCommandManager,
|
||||
branch1: string,
|
||||
branch2: string
|
||||
): Promise<boolean> {
|
||||
return (
|
||||
!(await isAhead(git, branch1, branch2)) &&
|
||||
!(await isBehind(git, branch1, branch2))
|
||||
)
|
||||
}
|
||||
|
||||
async function hasDiff(
|
||||
git: GitCommandManager,
|
||||
branch1: string,
|
||||
branch2: string
|
||||
): Promise<boolean> {
|
||||
const result = await git.diff([`${branch1}..${branch2}`])
|
||||
return result.length > 0
|
||||
}
|
||||
|
||||
function splitLines(multilineString: string): string[] {
|
||||
return multilineString
|
||||
.split('\n')
|
||||
.map(s => s.trim())
|
||||
.filter(x => x !== '')
|
||||
}
|
||||
|
||||
export async function createOrUpdateBranch(
|
||||
git: GitCommandManager,
|
||||
commitMessage: string,
|
||||
baseInput: string,
|
||||
branch: string
|
||||
): Promise<CreateOrUpdateBranchResult> {
|
||||
// Get the working base. This may or may not be the actual base.
|
||||
const workingBase = await git.symbolicRef('HEAD', ['--short'])
|
||||
// If the base is not specified it is assumed to be the working base.
|
||||
const base = baseInput ? baseInput : workingBase
|
||||
|
||||
// Set the default return values
|
||||
const result: CreateOrUpdateBranchResult = {
|
||||
action: 'none',
|
||||
base: base,
|
||||
hasDiffWithBase: false
|
||||
}
|
||||
|
||||
// Save the working base changes to a temporary branch
|
||||
const tempBranch = uuidv4()
|
||||
await git.checkout(tempBranch, 'HEAD')
|
||||
// Commit any uncomitted changes
|
||||
if (await git.isDirty(true)) {
|
||||
core.info('Uncommitted changes found. Adding a commit.')
|
||||
await git.exec(['add', '-A'])
|
||||
await git.commit(['-m', commitMessage])
|
||||
}
|
||||
|
||||
// Perform fetch and reset the working base
|
||||
// Commits made during the workflow will be removed
|
||||
await git.fetch([`${workingBase}:${workingBase}`], 'origin', ['--force'])
|
||||
|
||||
// If the working base is not the base, rebase the temp branch commits
|
||||
if (workingBase != base) {
|
||||
core.info(
|
||||
`Rebasing commits made to branch '${workingBase}' on to base branch '${base}'`
|
||||
)
|
||||
// Checkout the actual base
|
||||
await git.fetch([`${base}:${base}`], 'origin', ['--force'])
|
||||
await git.checkout(base)
|
||||
// Cherrypick commits from the temporary branch starting from the working base
|
||||
const commits = await git.revList(
|
||||
[`${workingBase}..${tempBranch}`, '.'],
|
||||
['--reverse']
|
||||
)
|
||||
for (const commit of splitLines(commits)) {
|
||||
const result = await git.cherryPick(
|
||||
['--strategy=recursive', '--strategy-option=theirs', commit],
|
||||
true
|
||||
)
|
||||
if (result.exitCode != 0 && !result.stderr.includes(CHERRYPICK_EMPTY)) {
|
||||
throw new Error(`Unexpected error: ${result.stderr}`)
|
||||
}
|
||||
}
|
||||
// Reset the temp branch to the working index
|
||||
await git.checkout(tempBranch, 'HEAD')
|
||||
// Reset the base
|
||||
await git.fetch([`${base}:${base}`], 'origin', ['--force'])
|
||||
}
|
||||
|
||||
// Try to fetch the pull request branch
|
||||
if (!(await tryFetch(git, branch))) {
|
||||
// The pull request branch does not exist
|
||||
core.info(`Pull request branch '${branch}' does not exist yet.`)
|
||||
// Create the pull request branch
|
||||
await git.checkout(branch, 'HEAD')
|
||||
// Check if the pull request branch is ahead of the base
|
||||
result.hasDiffWithBase = await isAhead(git, base, branch)
|
||||
if (result.hasDiffWithBase) {
|
||||
result.action = 'created'
|
||||
core.info(`Created branch '${branch}'`)
|
||||
} else {
|
||||
core.info(
|
||||
`Branch '${branch}' is not ahead of base '${base}' and will not be created`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// The pull request branch exists
|
||||
core.info(
|
||||
`Pull request branch '${branch}' already exists as remote branch 'origin/${branch}'`
|
||||
)
|
||||
// Checkout the pull request branch
|
||||
await git.checkout(branch)
|
||||
|
||||
if (await hasDiff(git, branch, tempBranch)) {
|
||||
// If the branch differs from the recreated temp version then the branch is reset
|
||||
// For changes on base this action is similar to a rebase of the pull request branch
|
||||
core.info(`Resetting '${branch}'`)
|
||||
// Alternatively, git switch -C branch tempBranch
|
||||
await git.checkout(branch, tempBranch)
|
||||
}
|
||||
|
||||
// Check if the pull request branch has been updated
|
||||
// If the branch was reset or updated it will be ahead
|
||||
// It may be behind if a reset now results in no diff with the base
|
||||
if (!(await isEven(git, `origin/${branch}`, branch))) {
|
||||
result.action = 'updated'
|
||||
core.info(`Updated branch '${branch}'`)
|
||||
} else {
|
||||
core.info(
|
||||
`Branch '${branch}' is even with its remote and will not be updated`
|
||||
)
|
||||
}
|
||||
|
||||
// Check if the pull request branch is ahead of the base
|
||||
result.hasDiffWithBase = await isAhead(git, base, branch)
|
||||
}
|
||||
|
||||
// Delete the temporary branch
|
||||
await git.exec(['branch', '--delete', '--force', tempBranch])
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
interface CreateOrUpdateBranchResult {
|
||||
action: string
|
||||
base: string
|
||||
hasDiffWithBase: boolean
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue