@@ -17464,6 +17464,7 @@ class Git {
17464
17464
await this.clone()
17465
17465
await this.setIdentity()
17466
17466
await this.getBaseBranch()
17467
+ await this.getLastCommitSha()
17467
17468
17468
17469
if (FORK) {
17469
17470
const forkUrl = `https://${ GITHUB_TOKEN }@github.com/${ FORK }/${ this.repo.name }.git`
@@ -17501,13 +17502,11 @@ class Git {
17501
17502
let email = GIT_EMAIL
17502
17503
17503
17504
if (email === undefined) {
17504
- if (IS_INSTALLATION_TOKEN) {
17505
- core.setFailed('When using an installation token you must provide GIT_EMAIL and GIT_USERNAME')
17506
- process.exit(1)
17505
+ if (!IS_INSTALLATION_TOKEN) {
17506
+ const { data } = await this.github.users.getAuthenticated()
17507
+ email = data.email
17508
+ username = data.login
17507
17509
}
17508
- const { data } = await this.github.users.getAuthenticated()
17509
- email = data.email
17510
- username = data.login
17511
17510
}
17512
17511
17513
17512
core.debug(`Setting git user to email: ${ email }, username: ${ username }`)
@@ -17528,7 +17527,7 @@ class Git {
17528
17527
async createPrBranch() {
17529
17528
const prefix = BRANCH_PREFIX.replace('SOURCE_REPO_NAME', GITHUB_REPOSITORY.split('/')[1])
17530
17529
17531
- let newBranch = path.join(prefix, this.repo.branch)
17530
+ let newBranch = path.join(prefix, this.repo.branch).replace(/\\/g, '/')
17532
17531
17533
17532
if (OVERWRITE_EXISTING_PR === false) {
17534
17533
newBranch += `-${ Math.round((new Date()).getTime() / 1000) }`
@@ -17546,7 +17545,7 @@ class Git {
17546
17545
17547
17546
async add(file) {
17548
17547
return execCmd(
17549
- `git add -f ${ file }`,
17548
+ `git add -f " ${ file }" `,
17550
17549
this.workingDir
17551
17550
)
17552
17551
}
@@ -17595,6 +17594,21 @@ class Git {
17595
17594
}
17596
17595
}
17597
17596
17597
+ async getBlobContent(objectSha) {
17598
+ return await execCmd(
17599
+ `git show ${ objectSha }`,
17600
+ this.workingDir,
17601
+ false // Do not trim the result
17602
+ )
17603
+ }
17604
+
17605
+ async getLastCommitSha() {
17606
+ this.lastCommitSha = await execCmd(
17607
+ `git rev-parse HEAD`,
17608
+ this.workingDir
17609
+ )
17610
+ }
17611
+
17598
17612
async changes(destination) { // gets array of git diffs for the destination, which either can be a file or a dict
17599
17613
const output = await execCmd(
17600
17614
`git diff HEAD ${ destination }`,
@@ -17623,6 +17637,96 @@ class Git {
17623
17637
)
17624
17638
}
17625
17639
17640
+ // Creates a tree object with all the blobs of each commit
17641
+ async getTree(commitSha) {
17642
+ const output = await execCmd(
17643
+ `git ls-tree -r --full-tree ${ commitSha }`,
17644
+ this.workingDir
17645
+ )
17646
+
17647
+ const tree = []
17648
+ for (const treeObject of output.split('\n')) {
17649
+ const [ mode, type, sha ] = treeObject.split(/\s/)
17650
+ const treeObjectPath = treeObject.split('\t')[1]
17651
+
17652
+ const treeEntry = {
17653
+ mode,
17654
+ type,
17655
+ content: await this.getBlobContent(sha),
17656
+ path: treeObjectPath
17657
+ }
17658
+ tree.push(treeEntry)
17659
+ }
17660
+ return tree
17661
+ }
17662
+
17663
+ // Gets the commit list in chronological order
17664
+ async getCommitsToPush() {
17665
+ const output = await execCmd(
17666
+ `git log --format=%H --reverse ${ this.baseBranch }..HEAD`,
17667
+ this.workingDir
17668
+ )
17669
+
17670
+ const commits = output.split('\n')
17671
+ return commits
17672
+ }
17673
+
17674
+ async getCommitMessage(commitSha) {
17675
+ return await execCmd(
17676
+ `git log -1 --format=%B ${ commitSha }`,
17677
+ this.workingDir
17678
+ )
17679
+ }
17680
+
17681
+ // Returns an array of objects with the git tree and the commit, one entry for each pending commit to push
17682
+ async getCommitsDataToPush() {
17683
+ const commitsToPush = await this.getCommitsToPush()
17684
+
17685
+ const commitsData = []
17686
+ for (const commitSha of commitsToPush) {
17687
+ const commitData = {
17688
+ commitMessage: await this.getCommitMessage(commitSha),
17689
+ tree: await this.getTree(commitSha)
17690
+ }
17691
+ commitsData.push(commitData)
17692
+ }
17693
+ return commitsData
17694
+ }
17695
+
17696
+ // A wrapper for running all the flow to generate all the pending commits using the GitHub API
17697
+ async createGithubVerifiedCommits() {
17698
+ const commitsData = await this.getCommitsDataToPush()
17699
+
17700
+ // Creates the PR branch if doesn't exists
17701
+ try {
17702
+ await this.github.git.createRef({
17703
+ owner: this.repo.user,
17704
+ repo: this.repo.name,
17705
+ sha: this.lastCommitSha,
17706
+ ref: 'refs/heads/' + this.prBranch
17707
+ })
17708
+
17709
+ core.debug(`Created new branch ${ this.prBranch }`)
17710
+ } catch (error) {
17711
+ // If the branch exists ignores the error
17712
+ if (error.message !== 'Reference already exists') throw error
17713
+ }
17714
+
17715
+ for (const commitData of commitsData) {
17716
+ await this.createGithubTreeAndCommit(commitData.tree, commitData.commitMessage)
17717
+ }
17718
+
17719
+ core.debug(`Updating branch ${ this.prBranch } ref`)
17720
+ await this.github.git.updateRef({
17721
+ owner: this.repo.user,
17722
+ repo: this.repo.name,
17723
+ ref: `heads/${ this.prBranch }`,
17724
+ sha: this.lastCommitSha,
17725
+ force: true
17726
+ })
17727
+ core.debug(`Commit using GitHub API completed`)
17728
+ }
17729
+
17626
17730
async status() {
17627
17731
return execCmd(
17628
17732
`git status`,
@@ -17637,6 +17741,9 @@ class Git {
17637
17741
this.workingDir
17638
17742
)
17639
17743
}
17744
+ if (IS_INSTALLATION_TOKEN) {
17745
+ return await this.createGithubVerifiedCommits()
17746
+ }
17640
17747
return execCmd(
17641
17748
`git push ${ this.gitUrl } --force`,
17642
17749
this.workingDir
@@ -17738,6 +17845,32 @@ class Git {
17738
17845
assignees: assignees
17739
17846
})
17740
17847
}
17848
+
17849
+ async createGithubTreeAndCommit(tree, commitMessage) {
17850
+ core.debug(`Creating a GitHub tree`)
17851
+ let treeSha
17852
+ try {
17853
+ const request = await this.github.git.createTree({
17854
+ owner: this.repo.user,
17855
+ repo: this.repo.name,
17856
+ tree
17857
+ })
17858
+ treeSha = request.data.sha
17859
+ } catch (error) {
17860
+ error.message = `Cannot create a new GitHub Tree: ${ error.message }`
17861
+ throw error
17862
+ }
17863
+
17864
+ core.debug(`Creating a commit for the GitHub tree`)
17865
+ const request = await this.github.git.createCommit({
17866
+ owner: this.repo.user,
17867
+ repo: this.repo.name,
17868
+ message: commitMessage,
17869
+ parents: [ this.lastCommitSha ],
17870
+ tree: treeSha
17871
+ })
17872
+ this.lastCommitSha = request.data.sha
17873
+ }
17741
17874
}
17742
17875
17743
17876
module.exports = Git
@@ -17788,7 +17921,7 @@ const dedent = function(templateStrings, ...values) {
17788
17921
return string
17789
17922
}
17790
17923
17791
- const execCmd = (command, workingDir) => {
17924
+ const execCmd = (command, workingDir, trimResult = true ) => {
17792
17925
core.debug(`EXEC: "${ command }" IN ${ workingDir }`)
17793
17926
return new Promise((resolve, reject) => {
17794
17927
exec(
@@ -17797,7 +17930,9 @@ const execCmd = (command, workingDir) => {
17797
17930
cwd: workingDir
17798
17931
},
17799
17932
function(error, stdout) {
17800
- error ? reject(error) : resolve(stdout.trim())
17933
+ error ? reject(error) : resolve(
17934
+ trimResult ? stdout.trim() : stdout
17935
+ )
17801
17936
}
17802
17937
)
17803
17938
})
0 commit comments