Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add option to use original commit message #96

Merged
merged 2 commits into from
Sep 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ Here are all the inputs [repo-file-sync-action](https://github.com/BetaHuhn/repo
| `ASSIGNEES` | People to assign to the pull request | **No** | N/A |
| `COMMIT_PREFIX` | Prefix for commit message and pull request title | **No** | 🔄 |
| `COMMIT_BODY` | Commit message body. Will be appended to commit message, separated by two line returns. | **No** | '' |
| `ORIGINAL_MESSAGE` | Use original commit message instead. Only works if the file(s) where changed and the action was triggered by pushing a single commit. | **No** | false |
| `COMMIT_EACH_FILE` | Commit each file seperately | **No** | true |
| `GIT_EMAIL` | The e-mail address used to commit the synced files | **Only when using installation token** | the email of the PAT used |
| `GIT_USERNAME` | The username used to commit the synced files | **Only when using installation token** | the username of the PAT used |
Expand Down
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ inputs:
description: |
Overwrite any existing Sync PR with the new changes. Defaults to true
required: false
ORIGINAL_MESSAGE:
description: |
Re-use the original commit message for commits. Works only if the action is triggered by pushing one commit. Defaults to false
required: false
SKIP_PR:
description: |
Skips creating a Pull Request and pushes directly to the default branch. Defaults to false
Expand Down
5 changes: 5 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ try {
type: 'boolean',
default: false
}),
ORIGINAL_MESSAGE: getInput({
key: 'ORIGINAL_MESSAGE',
type: 'boolean',
default: false
}),
BRANCH_PREFIX: getInput({
key: 'BRANCH_PREFIX',
default: 'repo-sync/SOURCE_REPO_NAME'
Expand Down
55 changes: 54 additions & 1 deletion src/git.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { parse } = require('@putout/git-status-porcelain')
const core = require('@actions/core')
const github = require('@actions/github')
const { GitHub, getOctokitOptions } = require('@actions/github/lib/utils')
const { throttling } = require('@octokit/plugin-throttling')
const path = require('path')
Expand Down Expand Up @@ -127,6 +128,58 @@ class Git {
)
}

isOneCommitPush() {
return github.context.eventName === 'push' && github.context.payload.commits.length === 1
}

originalCommitMessage() {
return github.context.payload.commits[0].message
}

parseGitDiffOutput(string) { // parses git diff output and returns a dictionary mapping the file path to the diff output for this file
// split diff into separate entries for separate files. \ndiff --git should be a reliable way to detect the separation, as content of files is always indented
return `\n${ string }`.split('\ndiff --git').slice(1).reduce((resultDict, fileDiff) => {
const lines = fileDiff.split('\n')
const lastHeaderLineIndex = lines.findIndex((line) => line.startsWith('+++'))
const plainDiff = lines.slice(lastHeaderLineIndex + 1).join('\n').trim()
let filePath = ''
if (lines[lastHeaderLineIndex].startsWith('+++ b/')) { // every file except removed files
filePath = lines[lastHeaderLineIndex].slice(6) // remove '+++ b/'
} else { // for removed file need to use header line with filename before deletion
filePath = lines[lastHeaderLineIndex - 1].slice(6) // remove '--- a/'
}
return { ...resultDict, [filePath]: plainDiff }
}, {})
}

async getChangesFromLastCommit(source) { // gets array of git diffs for the source, which either can be a file or a dict
if (this.lastCommitChanges === undefined) {
const diff = await this.github.repos.compareCommits({
mediaType: {
format: 'diff'
},
owner: github.context.payload.repository.owner.name,
repo: github.context.payload.repository.name,
base: github.context.payload.before,
head: github.context.payload.after
})
this.lastCommitChanges = this.parseGitDiffOutput(diff.data)
}
if (source.endsWith('/')) {
return Object.keys(this.lastCommitChanges).filter((filePath) => filePath.startsWith(source)).reduce((result, key) => [ ...result, this.lastCommitChanges[key] ], [])
} else {
return this.lastCommitChanges[source] === undefined ? [] : [ this.lastCommitChanges[source] ]
}
}

async changes(destination) { // gets array of git diffs for the destination, which either can be a file or a dict
const output = await execCmd(
`git diff HEAD ${ destination }`,
this.workingDir
)
return Object.values(this.parseGitDiffOutput(output))
}

async hasChanges() {
const statusOutput = await execCmd(
`git status --porcelain`,
Expand All @@ -142,7 +195,7 @@ class Git {
message += `\n\n${ COMMIT_BODY }`
}
return execCmd(
`git commit -m "${ message }"`,
`git commit -m "${ message.replace(/"/g, '\\"') }"`,
this.workingDir
)
}
Expand Down
5 changes: 4 additions & 1 deletion src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,15 @@ const remove = async (src) => {
return fs.remove(src)
}

const arrayEquals = (array1, array2) => Array.isArray(array1) && Array.isArray(array2) && array1.length === array2.length && array1.every((value, i) => value === array2[i])

module.exports = {
forEach,
dedent,
addTrailingSlash,
pathIsDirectory,
execCmd,
copy,
remove
remove,
arrayEquals
}
19 changes: 14 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const core = require('@actions/core')
const fs = require('fs')

const Git = require('./git')
const { forEach, dedent, addTrailingSlash, pathIsDirectory, copy, remove } = require('./helpers')
const { forEach, dedent, addTrailingSlash, pathIsDirectory, copy, remove, arrayEquals } = require('./helpers')

const {
parseConfig,
Expand All @@ -14,7 +14,8 @@ const {
TMP_DIR,
SKIP_CLEANUP,
OVERWRITE_EXISTING_PR,
SKIP_PR
SKIP_PR,
ORIGINAL_MESSAGE
} = require('./config')

const run = async () => {
Expand Down Expand Up @@ -82,14 +83,15 @@ const run = async () => {
// Use different commit/pr message based on if the source is a directory or file
const directory = isDirectory ? 'directory' : ''
const otherFiles = isDirectory ? 'and copied all sub files/folders' : ''
const useOriginalCommitMessage = ORIGINAL_MESSAGE && git.isOneCommitPush() && arrayEquals(await git.getChangesFromLastCommit(file.source), await git.changes(file.dest))

const message = {
true: {
commit: `${ COMMIT_PREFIX } Synced local '${ file.dest }' with remote '${ file.source }'`,
commit: useOriginalCommitMessage ? git.originalCommitMessage() : `${ COMMIT_PREFIX } Synced local '${ file.dest }' with remote '${ file.source }'`,
pr: `Synced local ${ directory } <code>${ file.dest }</code> with remote ${ directory } <code>${ file.source }</code>`
},
false: {
commit: `${ COMMIT_PREFIX } Created local '${ file.dest }' from remote '${ file.source }'`,
commit: useOriginalCommitMessage ? git.originalCommitMessage() : `${ COMMIT_PREFIX } Created local '${ file.dest }' from remote '${ file.source }'`,
pr: `Created local ${ directory } <code>${ file.dest }</code> ${ otherFiles } from remote ${ directory } <code>${ file.source }</code>`
}
}
Expand Down Expand Up @@ -128,7 +130,14 @@ const run = async () => {
if (hasChanges === true) {
core.debug(`Creating commit for remaining files`)

await git.commit()
let useOriginalCommitMessage = ORIGINAL_MESSAGE && git.isOneCommitPush()
if (useOriginalCommitMessage) {
await forEach(item.files, async (file) => {
useOriginalCommitMessage = useOriginalCommitMessage && arrayEquals(await git.getChangesFromLastCommit(file.source), await git.changes(file.dest))
})
}

await git.commit(useOriginalCommitMessage ? git.originalCommitMessage() : undefined)
modified.push({
dest: git.workingDir
})
Expand Down