Compare commits
10 commits
548d90536f
...
714e9be377
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
714e9be377 | ||
|
|
35dbaca7d5 | ||
|
|
8101ce8ff4 | ||
|
|
a636b8113c | ||
|
|
0db6c3cf7b | ||
|
|
36e042a736 | ||
|
|
6284ea5854 | ||
|
|
03266d3789 | ||
|
|
404696dda5 | ||
|
|
0e209053e0 |
7 changed files with 539 additions and 1131 deletions
|
|
@ -1,7 +1,8 @@
|
||||||
import {
|
import {
|
||||||
createOrUpdateBranch,
|
createOrUpdateBranch,
|
||||||
tryFetch,
|
tryFetch,
|
||||||
getWorkingBaseAndType
|
getWorkingBaseAndType,
|
||||||
|
buildBranchFileChanges
|
||||||
} from '../lib/create-or-update-branch'
|
} from '../lib/create-or-update-branch'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import {GitCommandManager} from '../lib/git-command-manager'
|
import {GitCommandManager} from '../lib/git-command-manager'
|
||||||
|
|
@ -229,6 +230,80 @@ describe('create-or-update-branch tests', () => {
|
||||||
expect(workingBaseType).toEqual('commit')
|
expect(workingBaseType).toEqual('commit')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('tests buildBranchFileChanges with no diff', async () => {
|
||||||
|
await git.checkout(BRANCH, BASE)
|
||||||
|
const branchFileChanges = await buildBranchFileChanges(git, BASE, BRANCH)
|
||||||
|
expect(branchFileChanges.additions.length).toEqual(0)
|
||||||
|
expect(branchFileChanges.deletions.length).toEqual(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('tests buildBranchFileChanges with addition and modification', async () => {
|
||||||
|
await git.checkout(BRANCH, BASE)
|
||||||
|
const changes = await createChanges()
|
||||||
|
await git.exec(['add', '-A'])
|
||||||
|
await git.commit(['-m', 'Test changes'])
|
||||||
|
|
||||||
|
const branchFileChanges = await buildBranchFileChanges(git, BASE, BRANCH)
|
||||||
|
|
||||||
|
expect(branchFileChanges.additions).toEqual([
|
||||||
|
{
|
||||||
|
path: TRACKED_FILE,
|
||||||
|
contents: Buffer.from(changes.tracked, 'binary').toString('base64')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: UNTRACKED_FILE,
|
||||||
|
contents: Buffer.from(changes.untracked, 'binary').toString('base64')
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(branchFileChanges.deletions.length).toEqual(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('tests buildBranchFileChanges with addition and deletion', async () => {
|
||||||
|
await git.checkout(BRANCH, BASE)
|
||||||
|
const changes = await createChanges()
|
||||||
|
const TRACKED_FILE_NEW_PATH = 'c/tracked-file.txt'
|
||||||
|
const filepath = path.join(REPO_PATH, TRACKED_FILE_NEW_PATH)
|
||||||
|
await fs.promises.mkdir(path.dirname(filepath), {recursive: true})
|
||||||
|
await fs.promises.rename(path.join(REPO_PATH, TRACKED_FILE), filepath)
|
||||||
|
await git.exec(['add', '-A'])
|
||||||
|
await git.commit(['-m', 'Test changes'])
|
||||||
|
|
||||||
|
const branchFileChanges = await buildBranchFileChanges(git, BASE, BRANCH)
|
||||||
|
|
||||||
|
expect(branchFileChanges.additions).toEqual([
|
||||||
|
{
|
||||||
|
path: UNTRACKED_FILE,
|
||||||
|
contents: Buffer.from(changes.untracked, 'binary').toString('base64')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: TRACKED_FILE_NEW_PATH,
|
||||||
|
contents: Buffer.from(changes.tracked, 'binary').toString('base64')
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(branchFileChanges.deletions).toEqual([{path: TRACKED_FILE}])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('tests buildBranchFileChanges with binary files', async () => {
|
||||||
|
await git.checkout(BRANCH, BASE)
|
||||||
|
const filename = 'c/untracked-binary-file'
|
||||||
|
const filepath = path.join(REPO_PATH, filename)
|
||||||
|
const binaryData = Buffer.from([0x00, 0xff, 0x10, 0x20])
|
||||||
|
await fs.promises.mkdir(path.dirname(filepath), {recursive: true})
|
||||||
|
await fs.promises.writeFile(filepath, binaryData)
|
||||||
|
await git.exec(['add', '-A'])
|
||||||
|
await git.commit(['-m', 'Test changes'])
|
||||||
|
|
||||||
|
const branchFileChanges = await buildBranchFileChanges(git, BASE, BRANCH)
|
||||||
|
|
||||||
|
expect(branchFileChanges.additions).toEqual([
|
||||||
|
{
|
||||||
|
path: filename,
|
||||||
|
contents: binaryData.toString('base64')
|
||||||
|
}
|
||||||
|
])
|
||||||
|
expect(branchFileChanges.deletions.length).toEqual(0)
|
||||||
|
})
|
||||||
|
|
||||||
it('tests no changes resulting in no new branch being created', async () => {
|
it('tests no changes resulting in no new branch being created', async () => {
|
||||||
const commitMessage = uuidv4()
|
const commitMessage = uuidv4()
|
||||||
const result = await createOrUpdateBranch(
|
const result = await createOrUpdateBranch(
|
||||||
|
|
|
||||||
1118
dist/index.js
vendored
1118
dist/index.js
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,7 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {GitCommandManager} from './git-command-manager'
|
import {GitCommandManager} from './git-command-manager'
|
||||||
import {v4 as uuidv4} from 'uuid'
|
import {v4 as uuidv4} from 'uuid'
|
||||||
|
import * as utils from './utils'
|
||||||
|
|
||||||
const CHERRYPICK_EMPTY =
|
const CHERRYPICK_EMPTY =
|
||||||
'The previous cherry-pick is now empty, possibly due to conflict resolution.'
|
'The previous cherry-pick is now empty, possibly due to conflict resolution.'
|
||||||
|
|
@ -47,6 +48,38 @@ export async function tryFetch(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function buildBranchFileChanges(
|
||||||
|
git: GitCommandManager,
|
||||||
|
base: string,
|
||||||
|
branch: string
|
||||||
|
): Promise<BranchFileChanges> {
|
||||||
|
const branchFileChanges: BranchFileChanges = {
|
||||||
|
additions: [],
|
||||||
|
deletions: []
|
||||||
|
}
|
||||||
|
const changedFiles = await git.getChangedFiles([
|
||||||
|
'--diff-filter=AM',
|
||||||
|
`${base}..${branch}`
|
||||||
|
])
|
||||||
|
const deletedFiles = await git.getChangedFiles([
|
||||||
|
'--diff-filter=D',
|
||||||
|
`${base}..${branch}`
|
||||||
|
])
|
||||||
|
const repoPath = git.getWorkingDirectory()
|
||||||
|
for (const file of changedFiles) {
|
||||||
|
branchFileChanges.additions!.push({
|
||||||
|
path: file,
|
||||||
|
contents: utils.readFileBase64([repoPath, file])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for (const file of deletedFiles) {
|
||||||
|
branchFileChanges.deletions!.push({
|
||||||
|
path: file
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return branchFileChanges
|
||||||
|
}
|
||||||
|
|
||||||
// Return the number of commits that branch2 is ahead of branch1
|
// Return the number of commits that branch2 is ahead of branch1
|
||||||
async function commitsAhead(
|
async function commitsAhead(
|
||||||
git: GitCommandManager,
|
git: GitCommandManager,
|
||||||
|
|
@ -110,11 +143,22 @@ function splitLines(multilineString: string): string[] {
|
||||||
.filter(x => x !== '')
|
.filter(x => x !== '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BranchFileChanges {
|
||||||
|
additions: {
|
||||||
|
path: string
|
||||||
|
contents: string
|
||||||
|
}[]
|
||||||
|
deletions: {
|
||||||
|
path: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
interface CreateOrUpdateBranchResult {
|
interface CreateOrUpdateBranchResult {
|
||||||
action: string
|
action: string
|
||||||
base: string
|
base: string
|
||||||
hasDiffWithBase: boolean
|
hasDiffWithBase: boolean
|
||||||
headSha: string
|
headSha: string
|
||||||
|
branchFileChanges?: BranchFileChanges
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createOrUpdateBranch(
|
export async function createOrUpdateBranch(
|
||||||
|
|
@ -289,6 +333,9 @@ export async function createOrUpdateBranch(
|
||||||
result.hasDiffWithBase = await isAhead(git, base, branch)
|
result.hasDiffWithBase = await isAhead(git, base, branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the branch file changes
|
||||||
|
result.branchFileChanges = await buildBranchFileChanges(git, base, branch)
|
||||||
|
|
||||||
// Get the pull request branch SHA
|
// Get the pull request branch SHA
|
||||||
result.headSha = await git.revParse('HEAD')
|
result.headSha = await git.revParse('HEAD')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,4 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as fs from 'fs'
|
|
||||||
import {graphql} from '@octokit/graphql'
|
|
||||||
import type {
|
|
||||||
Repository,
|
|
||||||
Ref,
|
|
||||||
Commit,
|
|
||||||
FileChanges
|
|
||||||
} from '@octokit/graphql-schema'
|
|
||||||
import {
|
import {
|
||||||
createOrUpdateBranch,
|
createOrUpdateBranch,
|
||||||
getWorkingBaseAndType,
|
getWorkingBaseAndType,
|
||||||
|
|
@ -204,207 +196,13 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||||
`Pushing pull request branch to '${branchRemoteName}/${inputs.branch}'`
|
`Pushing pull request branch to '${branchRemoteName}/${inputs.branch}'`
|
||||||
)
|
)
|
||||||
if (inputs.signCommit) {
|
if (inputs.signCommit) {
|
||||||
core.info(`Use API to push a signed commit`)
|
await githubHelper.pushSignedCommit(
|
||||||
const graphqlWithAuth = graphql.defaults({
|
branchRepository,
|
||||||
headers: {
|
inputs.branch,
|
||||||
authorization: 'token ' + inputs.token
|
inputs.base,
|
||||||
}
|
inputs.commitMessage,
|
||||||
})
|
result.branchFileChanges
|
||||||
|
|
||||||
let repoOwner = process.env.GITHUB_REPOSITORY!.split('/')[0]
|
|
||||||
if (inputs.pushToFork) {
|
|
||||||
const forkName = await githubHelper.getRepositoryParent(
|
|
||||||
baseRemote.repository
|
|
||||||
)
|
|
||||||
if (!forkName) {
|
|
||||||
repoOwner = forkName!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const repoName = process.env.GITHUB_REPOSITORY!.split('/')[1]
|
|
||||||
|
|
||||||
core.debug(`repoOwner: '${repoOwner}', repoName: '${repoName}'`)
|
|
||||||
const refQuery = `
|
|
||||||
query GetRefId($repoName: String!, $repoOwner: String!, $branchName: String!) {
|
|
||||||
repository(owner: $repoOwner, name: $repoName){
|
|
||||||
id
|
|
||||||
ref(qualifiedName: $branchName){
|
|
||||||
id
|
|
||||||
name
|
|
||||||
prefix
|
|
||||||
target{
|
|
||||||
id
|
|
||||||
oid
|
|
||||||
commitUrl
|
|
||||||
commitResourcePath
|
|
||||||
abbreviatedOid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
let branchRef = await graphqlWithAuth<{repository: Repository}>(
|
|
||||||
refQuery,
|
|
||||||
{
|
|
||||||
repoOwner: repoOwner,
|
|
||||||
repoName: repoName,
|
|
||||||
branchName: inputs.branch
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
core.debug(
|
|
||||||
`Fetched information for branch '${inputs.branch}' - '${JSON.stringify(branchRef)}'`
|
|
||||||
)
|
|
||||||
|
|
||||||
// if the branch does not exist, then first we need to create the branch from base
|
|
||||||
if (branchRef.repository.ref == null) {
|
|
||||||
core.debug(`Branch does not exist - '${inputs.branch}'`)
|
|
||||||
branchRef = await graphqlWithAuth<{repository: Repository}>(
|
|
||||||
refQuery,
|
|
||||||
{
|
|
||||||
repoOwner: repoOwner,
|
|
||||||
repoName: repoName,
|
|
||||||
branchName: inputs.base
|
|
||||||
}
|
|
||||||
)
|
|
||||||
core.debug(
|
|
||||||
`Fetched information for base branch '${inputs.base}' - '${JSON.stringify(branchRef)}'`
|
|
||||||
)
|
|
||||||
|
|
||||||
core.info(
|
|
||||||
`Creating new branch '${inputs.branch}' from '${inputs.base}', with ref '${JSON.stringify(branchRef.repository.ref!.target!.oid)}'`
|
|
||||||
)
|
|
||||||
if (branchRef.repository.ref != null) {
|
|
||||||
core.debug(`Send request for creating new branch`)
|
|
||||||
const newBranchMutation = `
|
|
||||||
mutation CreateNewBranch($branchName: String!, $oid: GitObjectID!, $repoId: ID!) {
|
|
||||||
createRef(input: {
|
|
||||||
name: $branchName,
|
|
||||||
oid: $oid,
|
|
||||||
repositoryId: $repoId
|
|
||||||
}) {
|
|
||||||
ref {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
prefix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
const newBranch = await graphqlWithAuth<{createRef: {ref: Ref}}>(
|
|
||||||
newBranchMutation,
|
|
||||||
{
|
|
||||||
repoId: branchRef.repository.id,
|
|
||||||
oid: branchRef.repository.ref.target!.oid,
|
|
||||||
branchName: 'refs/heads/' + inputs.branch
|
|
||||||
}
|
|
||||||
)
|
|
||||||
core.debug(
|
|
||||||
`Created new branch '${inputs.branch}': '${JSON.stringify(newBranch.createRef.ref)}'`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
core.info(
|
|
||||||
`Hash ref of branch '${inputs.branch}' is '${JSON.stringify(branchRef.repository.ref!.target!.oid)}'`
|
|
||||||
)
|
|
||||||
|
|
||||||
// switch to input-branch for reading updated file contents
|
|
||||||
await git.checkout(inputs.branch)
|
|
||||||
|
|
||||||
const changedFiles = await git.getChangedFiles(
|
|
||||||
branchRef.repository.ref!.target!.oid,
|
|
||||||
['--diff-filter=M']
|
|
||||||
)
|
|
||||||
const deletedFiles = await git.getChangedFiles(
|
|
||||||
branchRef.repository.ref!.target!.oid,
|
|
||||||
['--diff-filter=D']
|
|
||||||
)
|
|
||||||
const fileChanges = <FileChanges>{additions: [], deletions: []}
|
|
||||||
|
|
||||||
core.debug(`Changed files: '${JSON.stringify(changedFiles)}'`)
|
|
||||||
core.debug(`Deleted files: '${JSON.stringify(deletedFiles)}'`)
|
|
||||||
|
|
||||||
for (const file of changedFiles) {
|
|
||||||
core.debug(`Reading contents of file: '${file}'`)
|
|
||||||
fileChanges.additions!.push({
|
|
||||||
path: file,
|
|
||||||
contents: fs.readFileSync(file).toString('base64')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const file of deletedFiles) {
|
|
||||||
core.debug(`Marking file as deleted: '${file}'`)
|
|
||||||
fileChanges.deletions!.push({
|
|
||||||
path: file
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const pushCommitMutation = `
|
|
||||||
mutation PushCommit(
|
|
||||||
$repoNameWithOwner: String!,
|
|
||||||
$branchName: String!,
|
|
||||||
$headOid: GitObjectID!,
|
|
||||||
$commitMessage: String!,
|
|
||||||
$fileChanges: FileChanges
|
|
||||||
) {
|
|
||||||
createCommitOnBranch(input: {
|
|
||||||
branch: {
|
|
||||||
repositoryNameWithOwner: $repoNameWithOwner,
|
|
||||||
branchName: $branchName,
|
|
||||||
}
|
|
||||||
fileChanges: $fileChanges
|
|
||||||
message: {
|
|
||||||
headline: $commitMessage
|
|
||||||
}
|
|
||||||
expectedHeadOid: $headOid
|
|
||||||
}){
|
|
||||||
clientMutationId
|
|
||||||
ref{
|
|
||||||
id
|
|
||||||
name
|
|
||||||
prefix
|
|
||||||
}
|
|
||||||
commit{
|
|
||||||
id
|
|
||||||
abbreviatedOid
|
|
||||||
oid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
const pushCommitVars = {
|
|
||||||
branchName: inputs.branch,
|
|
||||||
repoNameWithOwner: repoOwner + '/' + repoName,
|
|
||||||
headOid: branchRef.repository.ref!.target!.oid,
|
|
||||||
commitMessage: inputs.commitMessage,
|
|
||||||
fileChanges: fileChanges
|
|
||||||
}
|
|
||||||
|
|
||||||
const pushCommitVarsWithoutContents = {
|
|
||||||
...pushCommitVars,
|
|
||||||
fileChanges: {
|
|
||||||
...pushCommitVars.fileChanges,
|
|
||||||
additions: pushCommitVars.fileChanges.additions?.map(addition => {
|
|
||||||
const {contents, ...rest} = addition
|
|
||||||
return rest
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
core.debug(
|
|
||||||
`Push commit with payload: '${JSON.stringify(pushCommitVarsWithoutContents)}'`
|
|
||||||
)
|
|
||||||
|
|
||||||
const commit = await graphqlWithAuth<{
|
|
||||||
createCommitOnBranch: {ref: Ref; commit: Commit}
|
|
||||||
}>(pushCommitMutation, pushCommitVars)
|
|
||||||
|
|
||||||
core.debug(`Pushed commit - '${JSON.stringify(commit)}'`)
|
|
||||||
core.info(
|
|
||||||
`Pushed commit with hash - '${commit.createCommitOnBranch.commit.oid}' on branch - '${commit.createCommitOnBranch.ref.name}'`
|
|
||||||
)
|
|
||||||
|
|
||||||
// switch back to previous branch/state since we are done with reading the changed file contents
|
|
||||||
await git.checkout('-')
|
|
||||||
} else {
|
} else {
|
||||||
await git.push([
|
await git.push([
|
||||||
'--force-with-lease',
|
'--force-with-lease',
|
||||||
|
|
|
||||||
|
|
@ -166,12 +166,11 @@ export class GitCommandManager {
|
||||||
return output.exitCode === 1
|
return output.exitCode === 1
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChangedFiles(ref: string, options?: string[]): Promise<string[]> {
|
async getChangedFiles(options?: string[]): Promise<string[]> {
|
||||||
const args = ['diff', '--name-only']
|
const args = ['diff', '--name-only']
|
||||||
if (options) {
|
if (options) {
|
||||||
args.push(...options)
|
args.push(...options)
|
||||||
}
|
}
|
||||||
args.push(ref)
|
|
||||||
const output = await this.exec(args)
|
const output = await this.exec(args)
|
||||||
return output.stdout.split('\n').filter(filename => filename != '')
|
return output.stdout.split('\n').filter(filename => filename != '')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {Inputs} from './create-pull-request'
|
import {Inputs} from './create-pull-request'
|
||||||
import {Octokit, OctokitOptions} from './octokit-client'
|
import {Octokit, OctokitOptions} from './octokit-client'
|
||||||
|
import type {
|
||||||
|
Repository as TempRepository,
|
||||||
|
Ref,
|
||||||
|
Commit,
|
||||||
|
FileChanges
|
||||||
|
} from '@octokit/graphql-schema'
|
||||||
|
import {BranchFileChanges} from './create-or-update-branch'
|
||||||
import * as utils from './utils'
|
import * as utils from './utils'
|
||||||
|
|
||||||
const ERROR_PR_REVIEW_TOKEN_SCOPE =
|
const ERROR_PR_REVIEW_TOKEN_SCOPE =
|
||||||
|
|
@ -184,4 +191,204 @@ export class GitHubHelper {
|
||||||
|
|
||||||
return pull
|
return pull
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async pushSignedCommit(
|
||||||
|
branchRepository: string,
|
||||||
|
branch: string,
|
||||||
|
base: string,
|
||||||
|
commitMessage: string,
|
||||||
|
branchFileChanges?: BranchFileChanges
|
||||||
|
): Promise<void> {
|
||||||
|
core.info(`Use API to push a signed commit`)
|
||||||
|
|
||||||
|
const [repoOwner, repoName] = branchRepository.split('/')
|
||||||
|
core.debug(`repoOwner: '${repoOwner}', repoName: '${repoName}'`)
|
||||||
|
const refQuery = `
|
||||||
|
query GetRefId($repoName: String!, $repoOwner: String!, $branchName: String!) {
|
||||||
|
repository(owner: $repoOwner, name: $repoName){
|
||||||
|
id
|
||||||
|
ref(qualifiedName: $branchName){
|
||||||
|
id
|
||||||
|
name
|
||||||
|
prefix
|
||||||
|
target{
|
||||||
|
id
|
||||||
|
oid
|
||||||
|
commitUrl
|
||||||
|
commitResourcePath
|
||||||
|
abbreviatedOid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
let branchRef = await this.octokit.graphql<{repository: TempRepository}>(
|
||||||
|
refQuery,
|
||||||
|
{
|
||||||
|
repoOwner: repoOwner,
|
||||||
|
repoName: repoName,
|
||||||
|
branchName: branch
|
||||||
|
}
|
||||||
|
)
|
||||||
|
core.debug(
|
||||||
|
`Fetched information for branch '${branch}' - '${JSON.stringify(branchRef)}'`
|
||||||
|
)
|
||||||
|
|
||||||
|
const branchExists = branchRef.repository.ref != null
|
||||||
|
|
||||||
|
// if the branch does not exist, then first we need to create the branch from base
|
||||||
|
if (!branchExists) {
|
||||||
|
core.debug(`Branch does not exist - '${branch}'`)
|
||||||
|
branchRef = await this.octokit.graphql<{repository: TempRepository}>(
|
||||||
|
refQuery,
|
||||||
|
{
|
||||||
|
repoOwner: repoOwner,
|
||||||
|
repoName: repoName,
|
||||||
|
branchName: base
|
||||||
|
}
|
||||||
|
)
|
||||||
|
core.debug(
|
||||||
|
`Fetched information for base branch '${base}' - '${JSON.stringify(branchRef)}'`
|
||||||
|
)
|
||||||
|
|
||||||
|
core.info(
|
||||||
|
`Creating new branch '${branch}' from '${base}', with ref '${JSON.stringify(branchRef.repository.ref!.target!.oid)}'`
|
||||||
|
)
|
||||||
|
if (branchRef.repository.ref != null) {
|
||||||
|
core.debug(`Send request for creating new branch`)
|
||||||
|
const newBranchMutation = `
|
||||||
|
mutation CreateNewBranch($branchName: String!, $oid: GitObjectID!, $repoId: ID!) {
|
||||||
|
createRef(input: {
|
||||||
|
name: $branchName,
|
||||||
|
oid: $oid,
|
||||||
|
repositoryId: $repoId
|
||||||
|
}) {
|
||||||
|
ref {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const newBranch = await this.octokit.graphql<{createRef: {ref: Ref}}>(
|
||||||
|
newBranchMutation,
|
||||||
|
{
|
||||||
|
repoId: branchRef.repository.id,
|
||||||
|
oid: branchRef.repository.ref.target!.oid,
|
||||||
|
branchName: 'refs/heads/' + branch
|
||||||
|
}
|
||||||
|
)
|
||||||
|
core.debug(
|
||||||
|
`Created new branch '${branch}': '${JSON.stringify(newBranch.createRef.ref)}'`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core.info(
|
||||||
|
`Hash ref of branch '${branch}' is '${JSON.stringify(branchRef.repository.ref!.target!.oid)}'`
|
||||||
|
)
|
||||||
|
|
||||||
|
const fileChanges = <FileChanges>{
|
||||||
|
additions: branchFileChanges!.additions,
|
||||||
|
deletions: branchFileChanges!.deletions
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushCommitMutation = `
|
||||||
|
mutation PushCommit(
|
||||||
|
$repoNameWithOwner: String!,
|
||||||
|
$branchName: String!,
|
||||||
|
$headOid: GitObjectID!,
|
||||||
|
$commitMessage: String!,
|
||||||
|
$fileChanges: FileChanges
|
||||||
|
) {
|
||||||
|
createCommitOnBranch(input: {
|
||||||
|
branch: {
|
||||||
|
repositoryNameWithOwner: $repoNameWithOwner,
|
||||||
|
branchName: $branchName,
|
||||||
|
}
|
||||||
|
fileChanges: $fileChanges
|
||||||
|
message: {
|
||||||
|
headline: $commitMessage
|
||||||
|
}
|
||||||
|
expectedHeadOid: $headOid
|
||||||
|
}){
|
||||||
|
clientMutationId
|
||||||
|
ref{
|
||||||
|
id
|
||||||
|
name
|
||||||
|
prefix
|
||||||
|
}
|
||||||
|
commit{
|
||||||
|
id
|
||||||
|
abbreviatedOid
|
||||||
|
oid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const pushCommitVars = {
|
||||||
|
branchName: branch,
|
||||||
|
repoNameWithOwner: repoOwner + '/' + repoName,
|
||||||
|
headOid: branchRef.repository.ref!.target!.oid,
|
||||||
|
commitMessage: commitMessage,
|
||||||
|
fileChanges: fileChanges
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushCommitVarsWithoutContents = {
|
||||||
|
...pushCommitVars,
|
||||||
|
fileChanges: {
|
||||||
|
...pushCommitVars.fileChanges,
|
||||||
|
additions: pushCommitVars.fileChanges.additions?.map(addition => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const {contents, ...rest} = addition
|
||||||
|
return rest
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.debug(
|
||||||
|
`Push commit with payload: '${JSON.stringify(pushCommitVarsWithoutContents)}'`
|
||||||
|
)
|
||||||
|
|
||||||
|
const commit = await this.octokit.graphql<{
|
||||||
|
createCommitOnBranch: {ref: Ref; commit: Commit}
|
||||||
|
}>(pushCommitMutation, pushCommitVars)
|
||||||
|
|
||||||
|
core.debug(`Pushed commit - '${JSON.stringify(commit)}'`)
|
||||||
|
core.info(
|
||||||
|
`Pushed commit with hash - '${commit.createCommitOnBranch.commit.oid}' on branch - '${commit.createCommitOnBranch.ref.name}'`
|
||||||
|
)
|
||||||
|
|
||||||
|
if (branchExists) {
|
||||||
|
// The branch existed so update the branch ref to point to the new commit
|
||||||
|
// This is the same behavior as force pushing the branch
|
||||||
|
core.info(
|
||||||
|
`Updating branch '${branch}' to commit '${commit.createCommitOnBranch.commit.oid}'`
|
||||||
|
)
|
||||||
|
const updateBranchMutation = `
|
||||||
|
mutation UpdateBranch($branchId: ID!, $commitOid: GitObjectID!) {
|
||||||
|
updateRef(input: {
|
||||||
|
refId: $branchId,
|
||||||
|
oid: $commitOid,
|
||||||
|
force: true
|
||||||
|
}) {
|
||||||
|
ref {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const updatedBranch = await this.octokit.graphql<{updateRef: {ref: Ref}}>(
|
||||||
|
updateBranchMutation,
|
||||||
|
{
|
||||||
|
branchId: branchRef.repository.ref!.id,
|
||||||
|
commitOid: commit.createCommitOnBranch.commit.oid
|
||||||
|
}
|
||||||
|
)
|
||||||
|
core.debug(`Updated branch - '${JSON.stringify(updatedBranch)}'`)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,10 @@ export function readFile(path: string): string {
|
||||||
return fs.readFileSync(path, 'utf-8')
|
return fs.readFileSync(path, 'utf-8')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function readFileBase64(pathParts: string[]): string {
|
||||||
|
return fs.readFileSync(path.resolve(...pathParts)).toString('base64')
|
||||||
|
}
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
function hasErrorCode(error: any): error is {code: string} {
|
function hasErrorCode(error: any): error is {code: string} {
|
||||||
return typeof (error && error.code) === 'string'
|
return typeof (error && error.code) === 'string'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue