Compare commits

...

4 Commits

Author SHA1 Message Date
CommanderRoot f37edd6984
Merge 55ad59dfaa into 900f2210b1 2026-05-27 00:50:52 +08:00
Yashwanth Anantharaju 900f2210b1
fix: expand merge commit SHA regex and add SHA-256 test cases (#2414)
* fix: expand merge commit SHA regex and add SHA-256 test cases

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test: add checkCommitInfo SHA coverage

Add checkCommitInfo tests for SHA-1 and SHA-256 merge messages and reject invalid 50-character hex merge heads.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* style: fix Prettier formatting in test and source files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-04 13:30:55 -04:00
CommanderRoot 55ad59dfaa
Merge branch 'main' into rm-deprecated-substr 2023-08-26 21:33:44 +02:00
Tobias Speicher dedef103f1
Replace deprecated String.prototype.substr()
String.prototype.substr() is deprecated (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr) so we replace it with slice() which works similarily but isn't deprecated.
Signed-off-by: Tobias Speicher <rootcommander@gmail.com>
2022-03-13 22:03:37 +01:00
8 changed files with 175 additions and 15 deletions

View File

@ -133,6 +133,16 @@ describe('input-helper tests', () => {
expect(settings.commit).toBe('1111111111222222222233333333334444444444') expect(settings.commit).toBe('1111111111222222222233333333334444444444')
}) })
it('sets ref to empty when explicit sha-256', async () => {
inputs.ref =
'1111111111222222222233333333334444444444555555555566666666667777'
const settings: IGitSourceSettings = await inputHelper.getInputs()
expect(settings.ref).toBeFalsy()
expect(settings.commit).toBe(
'1111111111222222222233333333334444444444555555555566666666667777'
)
})
it('sets sha to empty when explicit ref', async () => { it('sets sha to empty when explicit ref', async () => {
inputs.ref = 'refs/heads/some-other-ref' inputs.ref = 'refs/heads/some-other-ref'
const settings: IGitSourceSettings = await inputHelper.getInputs() const settings: IGitSourceSettings = await inputHelper.getInputs()

View File

@ -1,8 +1,12 @@
import * as assert from 'assert' import * as assert from 'assert'
import * as core from '@actions/core'
import * as github from '@actions/github'
import * as refHelper from '../lib/ref-helper' import * as refHelper from '../lib/ref-helper'
import {IGitCommandManager} from '../lib/git-command-manager' import {IGitCommandManager} from '../lib/git-command-manager'
const commit = '1234567890123456789012345678901234567890' const commit = '1234567890123456789012345678901234567890'
const sha256Commit =
'1234567890123456789012345678901234567890123456789012345678901234'
let git: IGitCommandManager let git: IGitCommandManager
describe('ref-helper tests', () => { describe('ref-helper tests', () => {
@ -37,6 +41,12 @@ describe('ref-helper tests', () => {
expect(checkoutInfo.startPoint).toBeFalsy() expect(checkoutInfo.startPoint).toBeFalsy()
}) })
it('getCheckoutInfo sha-256 only', async () => {
const checkoutInfo = await refHelper.getCheckoutInfo(git, '', sha256Commit)
expect(checkoutInfo.ref).toBe(sha256Commit)
expect(checkoutInfo.startPoint).toBeFalsy()
})
it('getCheckoutInfo refs/heads/', async () => { it('getCheckoutInfo refs/heads/', async () => {
const checkoutInfo = await refHelper.getCheckoutInfo( const checkoutInfo = await refHelper.getCheckoutInfo(
git, git,
@ -227,4 +237,142 @@ describe('ref-helper tests', () => {
'+refs/heads/my/branch:refs/remotes/origin/my/branch' '+refs/heads/my/branch:refs/remotes/origin/my/branch'
) )
}) })
describe('checkCommitInfo', () => {
const repositoryOwner = 'some-owner'
const repositoryName = 'some-repo'
const ref = 'refs/pull/123/merge'
const sha1Head = '1111111111222222222233333333334444444444'
const sha1Base = 'aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd'
const sha256Head =
'1111111111222222222233333333334444444444555555555566666666667777'
const sha256Base =
'aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff0000'
let debugSpy: jest.SpyInstance
let getOctokitSpy: jest.SpyInstance
let repoGetSpy: jest.Mock
let originalEventName: string
let originalPayload: unknown
let originalRef: string
let originalSha: string
function setPullRequestContext(
expectedHeadSha: string,
expectedBaseSha: string,
mergeCommit: string
): void {
;(github.context as any).eventName = 'pull_request'
github.context.ref = ref
github.context.sha = mergeCommit
;(github.context as any).payload = {
action: 'synchronize',
after: expectedHeadSha,
number: 123,
pull_request: {
base: {
sha: expectedBaseSha
}
},
repository: {
private: false
}
}
}
beforeEach(() => {
originalEventName = github.context.eventName
originalPayload = github.context.payload
originalRef = github.context.ref
originalSha = github.context.sha
jest.spyOn(github.context, 'repo', 'get').mockReturnValue({
owner: repositoryOwner,
repo: repositoryName
})
debugSpy = jest.spyOn(core, 'debug').mockImplementation(jest.fn())
repoGetSpy = jest.fn(async () => ({}))
getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({
rest: {
repos: {
get: repoGetSpy
}
}
} as any)
})
afterEach(() => {
;(github.context as any).eventName = originalEventName
;(github.context as any).payload = originalPayload
github.context.ref = originalRef
github.context.sha = originalSha
jest.restoreAllMocks()
})
it('returns early for SHA-1 merge commit', async () => {
setPullRequestContext(sha1Head, sha1Base, commit)
await refHelper.checkCommitInfo(
'token',
`Merge ${sha1Head} into ${sha1Base}`,
repositoryOwner,
repositoryName,
ref,
commit
)
expect(getOctokitSpy).not.toHaveBeenCalled()
expect(repoGetSpy).not.toHaveBeenCalled()
})
it('matches SHA-256 merge commit info', async () => {
const actualHeadSha =
'9999999999888888888877777777776666666666555555555544444444443333'
setPullRequestContext(sha256Head, sha256Base, sha256Commit)
await refHelper.checkCommitInfo(
'token',
`Merge ${actualHeadSha} into ${sha256Base}`,
repositoryOwner,
repositoryName,
ref,
sha256Commit
)
expect(getOctokitSpy).toHaveBeenCalledWith(
'token',
expect.objectContaining({
userAgent: expect.stringContaining(
`expected_head_sha=${sha256Head};actual_head_sha=${actualHeadSha}`
)
})
)
expect(repoGetSpy).toHaveBeenCalledWith({
owner: repositoryOwner,
repo: repositoryName
})
expect(debugSpy).toHaveBeenCalledWith(
`Expected head sha ${sha256Head}; actual head sha ${actualHeadSha}`
)
expect(debugSpy).not.toHaveBeenCalledWith('Unexpected message format')
})
it('does not match 50-char hex as a valid merge', async () => {
const invalidHeadSha =
'99999999998888888888777777777766666666665555555555'
setPullRequestContext(sha1Head, sha1Base, commit)
await refHelper.checkCommitInfo(
'token',
`Merge ${invalidHeadSha} into ${sha1Base}`,
repositoryOwner,
repositoryName,
ref,
commit
)
expect(getOctokitSpy).not.toHaveBeenCalled()
expect(repoGetSpy).not.toHaveBeenCalled()
expect(debugSpy).toHaveBeenCalledWith('Unexpected message format')
})
})
}) })

4
dist/index.js vendored
View File

@ -2021,7 +2021,7 @@ function getInputs() {
} }
} }
// SHA? // SHA?
else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) { else if (result.ref.match(/^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$/)) {
result.commit = result.ref; result.commit = result.ref;
result.ref = ''; result.ref = '';
} }
@ -2444,7 +2444,7 @@ function checkCommitInfo(token, commitInfo, repositoryOwner, repositoryName, ref
return; return;
} }
// Extract details from message // Extract details from message
const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/); const match = commitInfo.match(/Merge ([0-9a-f]{40}|[0-9a-f]{64}) into ([0-9a-f]{40}|[0-9a-f]{64})/);
if (!match) { if (!match) {
core.debug('Unexpected message format'); core.debug('Unexpected message format');
return; return;

View File

@ -336,7 +336,7 @@ class GitCommandManager {
line = line.trim() line = line.trim()
if (line.startsWith('ref:') || line.endsWith('HEAD')) { if (line.startsWith('ref:') || line.endsWith('HEAD')) {
return line return line
.substr('ref:'.length, line.length - 'ref:'.length - 'HEAD'.length) .slice('ref:'.length, line.length - 'HEAD'.length)
.trim() .trim()
} }
} }

View File

@ -64,11 +64,11 @@ export async function prepareExistingDirectory(
if (ref) { if (ref) {
ref = ref.startsWith('refs/') ? ref : `refs/heads/${ref}` ref = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`
if (ref.startsWith('refs/heads/')) { if (ref.startsWith('refs/heads/')) {
const upperName1 = ref.toUpperCase().substr('REFS/HEADS/'.length) const upperName1 = ref.toUpperCase().slice('REFS/HEADS/'.length)
const upperName1Slash = `${upperName1}/` const upperName1Slash = `${upperName1}/`
branches = await git.branchList(true) branches = await git.branchList(true)
for (const branch of branches) { for (const branch of branches) {
const upperName2 = branch.substr('origin/'.length).toUpperCase() const upperName2 = branch.slice('origin/'.length).toUpperCase()
const upperName2Slash = `${upperName2}/` const upperName2Slash = `${upperName2}/`
if ( if (
upperName1.startsWith(upperName2Slash) || upperName1.startsWith(upperName2Slash) ||

View File

@ -71,7 +71,7 @@ export async function getInputs(): Promise<IGitSourceSettings> {
} }
} }
// SHA? // SHA?
else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) { else if (result.ref.match(/^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$/)) {
result.commit = result.ref result.commit = result.ref
result.ref = '' result.ref = ''
} }

View File

@ -43,7 +43,7 @@ function updateUsage(
const newReadme: string[] = [] const newReadme: string[] = []
// Append the beginning // Append the beginning
newReadme.push(originalReadme.substr(0, startTokenIndex + startToken.length)) newReadme.push(originalReadme.slice(0, startTokenIndex + startToken.length))
// Build the new usage section // Build the new usage section
newReadme.push('```yaml', `- uses: ${actionReference}`, ' with:') newReadme.push('```yaml', `- uses: ${actionReference}`, ' with:')
@ -68,9 +68,9 @@ function updateUsage(
// Longer than width? Find a space to break apart // Longer than width? Find a space to break apart
let segment: string = description let segment: string = description
if (description.length > width) { if (description.length > width) {
segment = description.substr(0, width + 1) segment = description.slice(0, width + 1)
while (!segment.endsWith(' ') && !segment.endsWith('\n') && segment) { while (!segment.endsWith(' ') && !segment.endsWith('\n') && segment) {
segment = segment.substr(0, segment.length - 1) segment = segment.slice(0, -1)
} }
// Trimmed too much? // Trimmed too much?
@ -84,14 +84,14 @@ function updateUsage(
// Check for newline // Check for newline
const newlineIndex = segment.indexOf('\n') const newlineIndex = segment.indexOf('\n')
if (newlineIndex >= 0) { if (newlineIndex >= 0) {
segment = segment.substr(0, newlineIndex + 1) segment = segment.slice(0, newlineIndex + 1)
} }
// Append segment // Append segment
newReadme.push(` # ${segment}`.trimRight()) newReadme.push(` # ${segment}`.trimRight())
// Remaining // Remaining
description = description.substr(segment.length) description = description.slice(segment.length)
} }
if (input.default !== undefined) { if (input.default !== undefined) {
@ -113,7 +113,7 @@ function updateUsage(
newReadme.push('```') newReadme.push('```')
// Append the end // Append the end
newReadme.push(originalReadme.substr(endTokenIndex)) newReadme.push(originalReadme.slice(endTokenIndex))
// Write the new README // Write the new README
fs.writeFileSync(readmePath, newReadme.join(os.EOL)) fs.writeFileSync(readmePath, newReadme.join(os.EOL))

View File

@ -258,7 +258,9 @@ export async function checkCommitInfo(
} }
// Extract details from message // Extract details from message
const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/) const match = commitInfo.match(
/Merge ([0-9a-f]{40}|[0-9a-f]{64}) into ([0-9a-f]{40}|[0-9a-f]{64})/
)
if (!match) { if (!match) {
core.debug('Unexpected message format') core.debug('Unexpected message format')
return return
@ -304,6 +306,6 @@ function select(obj: any, path: string): any {
return obj[path] return obj[path]
} }
const key = path.substr(0, i) const key = path.slice(0, i)
return select(obj[key], path.substr(i + 1)) return select(obj[key], path.slice(i + 1))
} }