fix: support submodules when commit signing (#3354)

* fix: support submodules when commit signing

* create correct tree object for submodule

* update log messages
This commit is contained in:
Peter Evans 2024-09-18 17:46:39 +01:00 committed by GitHub
parent 7a8aeac749
commit 2f38cd26bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 116 additions and 75 deletions

View file

@ -250,11 +250,15 @@ describe('create-or-update-branch tests', () => {
expect(branchCommits.length).toEqual(1) expect(branchCommits.length).toEqual(1)
expect(branchCommits[0].subject).toEqual('Test changes') expect(branchCommits[0].subject).toEqual('Test changes')
expect(branchCommits[0].changes.length).toEqual(3) expect(branchCommits[0].changes.length).toEqual(3)
expect(branchCommits[0].changes).toEqual([ expect(branchCommits[0].changes[0].mode).toEqual('100755')
{mode: '100755', path: UNTRACKED_EXE_FILE, status: 'A'}, expect(branchCommits[0].changes[0].path).toEqual(UNTRACKED_EXE_FILE)
{mode: '100644', path: TRACKED_FILE, status: 'M'}, expect(branchCommits[0].changes[0].status).toEqual('A')
{mode: '100644', path: UNTRACKED_FILE, status: 'A'} expect(branchCommits[0].changes[1].mode).toEqual('100644')
]) expect(branchCommits[0].changes[1].path).toEqual(TRACKED_FILE)
expect(branchCommits[0].changes[1].status).toEqual('M')
expect(branchCommits[0].changes[2].mode).toEqual('100644')
expect(branchCommits[0].changes[2].path).toEqual(UNTRACKED_FILE)
expect(branchCommits[0].changes[2].status).toEqual('A')
}) })
it('tests buildBranchCommits with addition and deletion', async () => { it('tests buildBranchCommits with addition and deletion', async () => {
@ -272,11 +276,15 @@ describe('create-or-update-branch tests', () => {
expect(branchCommits.length).toEqual(1) expect(branchCommits.length).toEqual(1)
expect(branchCommits[0].subject).toEqual('Test changes') expect(branchCommits[0].subject).toEqual('Test changes')
expect(branchCommits[0].changes.length).toEqual(3) expect(branchCommits[0].changes.length).toEqual(3)
expect(branchCommits[0].changes).toEqual([ expect(branchCommits[0].changes[0].mode).toEqual('100644')
{mode: '100644', path: TRACKED_FILE, status: 'D'}, expect(branchCommits[0].changes[0].path).toEqual(TRACKED_FILE)
{mode: '100644', path: UNTRACKED_FILE, status: 'A'}, expect(branchCommits[0].changes[0].status).toEqual('D')
{mode: '100644', path: TRACKED_FILE_NEW_PATH, status: 'A'} expect(branchCommits[0].changes[1].mode).toEqual('100644')
]) expect(branchCommits[0].changes[1].path).toEqual(UNTRACKED_FILE)
expect(branchCommits[0].changes[1].status).toEqual('A')
expect(branchCommits[0].changes[2].mode).toEqual('100644')
expect(branchCommits[0].changes[2].path).toEqual(TRACKED_FILE_NEW_PATH)
expect(branchCommits[0].changes[2].status).toEqual('A')
}) })
it('tests buildBranchCommits with multiple commits', async () => { it('tests buildBranchCommits with multiple commits', async () => {
@ -294,10 +302,13 @@ describe('create-or-update-branch tests', () => {
expect(branchCommits[i].subject).toEqual(`Test changes ${i}`) expect(branchCommits[i].subject).toEqual(`Test changes ${i}`)
expect(branchCommits[i].changes.length).toEqual(2) expect(branchCommits[i].changes.length).toEqual(2)
const untrackedFileStatus = i == 0 ? 'A' : 'M' const untrackedFileStatus = i == 0 ? 'A' : 'M'
expect(branchCommits[i].changes).toEqual([
{mode: '100644', path: TRACKED_FILE, status: 'M'}, expect(branchCommits[i].changes[0].mode).toEqual('100644')
{mode: '100644', path: UNTRACKED_FILE, status: untrackedFileStatus} expect(branchCommits[i].changes[0].path).toEqual(TRACKED_FILE)
]) expect(branchCommits[i].changes[0].status).toEqual('M')
expect(branchCommits[i].changes[1].mode).toEqual('100644')
expect(branchCommits[i].changes[1].path).toEqual(UNTRACKED_FILE)
expect(branchCommits[i].changes[1].status).toEqual(untrackedFileStatus)
} }
}) })

View file

@ -18,9 +18,9 @@ describe('git-command-manager integration tests', () => {
expect(initialCommit.subject).toEqual('initial commit') expect(initialCommit.subject).toEqual('initial commit')
expect(initialCommit.signed).toBeFalsy() expect(initialCommit.signed).toBeFalsy()
expect(initialCommit.changes).toEqual([ expect(initialCommit.changes[0].mode).toEqual('100644')
{mode: '100644', status: 'A', path: 'README_TEMP.md'} expect(initialCommit.changes[0].status).toEqual('A')
]) expect(initialCommit.changes[0].path).toEqual('README_TEMP.md')
expect(emptyCommit.subject).toEqual('empty commit for tests') expect(emptyCommit.subject).toEqual('empty commit for tests')
expect(emptyCommit.tree).toEqual(initialCommit.tree) // empty commits have no tree and reference the parent's expect(emptyCommit.tree).toEqual(initialCommit.tree) // empty commits have no tree and reference the parent's
@ -31,16 +31,18 @@ describe('git-command-manager integration tests', () => {
expect(modifiedCommit.subject).toEqual('add sparkles') expect(modifiedCommit.subject).toEqual('add sparkles')
expect(modifiedCommit.parents[0]).toEqual(emptyCommit.sha) expect(modifiedCommit.parents[0]).toEqual(emptyCommit.sha)
expect(modifiedCommit.signed).toBeFalsy() expect(modifiedCommit.signed).toBeFalsy()
expect(modifiedCommit.changes).toEqual([ expect(modifiedCommit.changes[0].mode).toEqual('100644')
{mode: '100644', status: 'M', path: 'README_TEMP.md'} expect(modifiedCommit.changes[0].status).toEqual('M')
]) expect(modifiedCommit.changes[0].path).toEqual('README_TEMP.md')
expect(headCommit.subject).toEqual('rename readme') expect(headCommit.subject).toEqual('rename readme')
expect(headCommit.parents[0]).toEqual(modifiedCommit.sha) expect(headCommit.parents[0]).toEqual(modifiedCommit.sha)
expect(headCommit.signed).toBeFalsy() expect(headCommit.signed).toBeFalsy()
expect(headCommit.changes).toEqual([ expect(headCommit.changes[0].mode).toEqual('100644')
{mode: '100644', status: 'A', path: 'README.md'}, expect(headCommit.changes[0].status).toEqual('A')
{mode: '100644', status: 'D', path: 'README_TEMP.md'} expect(headCommit.changes[0].path).toEqual('README.md')
]) expect(headCommit.changes[1].mode).toEqual('100644')
expect(headCommit.changes[1].status).toEqual('D')
expect(headCommit.changes[1].path).toEqual('README_TEMP.md')
}) })
}) })

57
dist/index.js vendored
View file

@ -762,12 +762,13 @@ class GitCommandManager {
subject: detailLines[4], subject: detailLines[4],
body: detailLines.slice(5, endOfBodyIndex).join('\n'), body: detailLines.slice(5, endOfBodyIndex).join('\n'),
changes: lines.slice(endOfBodyIndex + 2, -1).map(line => { changes: lines.slice(endOfBodyIndex + 2, -1).map(line => {
const change = line.match(/^:(\d{6}) (\d{6}) \w{40} \w{40} ([AMD])\s+(.*)$/); const change = line.match(/^:(\d{6}) (\d{6}) \w{40} (\w{40}) ([AMD])\s+(.*)$/);
if (change) { if (change) {
return { return {
mode: change[3] === 'D' ? change[1] : change[2], mode: change[4] === 'D' ? change[1] : change[2],
status: change[3], dstSha: change[3],
path: change[4] status: change[4],
path: change[5]
}; };
} }
else { else {
@ -1368,25 +1369,37 @@ class GitHubHelper {
let treeSha = parentCommit.tree; let treeSha = parentCommit.tree;
if (commit.changes.length > 0) { if (commit.changes.length > 0) {
core.info(`Creating tree objects for local commit ${commit.sha}`); core.info(`Creating tree objects for local commit ${commit.sha}`);
const treeObjects = yield Promise.all(commit.changes.map((_a) => __awaiter(this, [_a], void 0, function* ({ path, mode, status }) { const treeObjects = yield Promise.all(commit.changes.map((_a) => __awaiter(this, [_a], void 0, function* ({ path, mode, status, dstSha }) {
let sha = null; if (mode === '160000') {
if (status === 'A' || status === 'M') { // submodule
try { core.info(`Creating tree object for submodule commit at '${path}'`);
const { data: blob } = yield blobCreationLimit(() => this.octokit.rest.git.createBlob(Object.assign(Object.assign({}, repository), { content: utils.readFileBase64([repoPath, path]), encoding: 'base64' }))); return {
sha = blob.sha; path,
} mode,
catch (error) { sha: dstSha,
core.error(`Error creating blob for file '${path}': ${utils.getErrorMessage(error)}`); type: 'commit'
throw error; };
} }
else {
let sha = null;
if (status === 'A' || status === 'M') {
try {
const { data: blob } = yield blobCreationLimit(() => this.octokit.rest.git.createBlob(Object.assign(Object.assign({}, repository), { content: utils.readFileBase64([repoPath, path]), encoding: 'base64' })));
sha = blob.sha;
}
catch (error) {
core.error(`Error creating blob for file '${path}': ${utils.getErrorMessage(error)}`);
throw error;
}
}
core.info(`Creating tree object for blob at '${path}' with status '${status}'`);
return {
path,
mode,
sha,
type: 'blob'
};
} }
core.info(`Created blob for file '${path}'`);
return {
path,
mode,
sha,
type: 'blob'
};
}))); })));
const chunkSize = 100; const chunkSize = 100;
const chunkedTreeObjects = Array.from({ length: Math.ceil(treeObjects.length / chunkSize) }, (_, i) => treeObjects.slice(i * chunkSize, i * chunkSize + chunkSize)); const chunkedTreeObjects = Array.from({ length: Math.ceil(treeObjects.length / chunkSize) }, (_, i) => treeObjects.slice(i * chunkSize, i * chunkSize + chunkSize));

View file

@ -14,6 +14,7 @@ export type Commit = {
body: string body: string
changes: { changes: {
mode: string mode: string
dstSha: string
status: 'A' | 'M' | 'D' status: 'A' | 'M' | 'D'
path: string path: string
}[] }[]
@ -178,13 +179,14 @@ export class GitCommandManager {
body: detailLines.slice(5, endOfBodyIndex).join('\n'), body: detailLines.slice(5, endOfBodyIndex).join('\n'),
changes: lines.slice(endOfBodyIndex + 2, -1).map(line => { changes: lines.slice(endOfBodyIndex + 2, -1).map(line => {
const change = line.match( const change = line.match(
/^:(\d{6}) (\d{6}) \w{40} \w{40} ([AMD])\s+(.*)$/ /^:(\d{6}) (\d{6}) \w{40} (\w{40}) ([AMD])\s+(.*)$/
) )
if (change) { if (change) {
return { return {
mode: change[3] === 'D' ? change[1] : change[2], mode: change[4] === 'D' ? change[1] : change[2],
status: change[3], dstSha: change[3],
path: change[4] status: change[4],
path: change[5]
} }
} else { } else {
unparsedChanges.push(line) unparsedChanges.push(line)

View file

@ -35,7 +35,7 @@ type TreeObject = {
path: string path: string
mode: '100644' | '100755' | '040000' | '160000' | '120000' mode: '100644' | '100755' | '040000' | '160000' | '120000'
sha: string | null sha: string | null
type: 'blob' type: 'blob' | 'commit'
} }
export class GitHubHelper { export class GitHubHelper {
@ -255,31 +255,44 @@ export class GitHubHelper {
if (commit.changes.length > 0) { if (commit.changes.length > 0) {
core.info(`Creating tree objects for local commit ${commit.sha}`) core.info(`Creating tree objects for local commit ${commit.sha}`)
const treeObjects = await Promise.all( const treeObjects = await Promise.all(
commit.changes.map(async ({path, mode, status}) => { commit.changes.map(async ({path, mode, status, dstSha}) => {
let sha: string | null = null if (mode === '160000') {
if (status === 'A' || status === 'M') { // submodule
try { core.info(`Creating tree object for submodule commit at '${path}'`)
const {data: blob} = await blobCreationLimit(() => return <TreeObject>{
this.octokit.rest.git.createBlob({ path,
...repository, mode,
content: utils.readFileBase64([repoPath, path]), sha: dstSha,
encoding: 'base64' type: 'commit'
}) }
) } else {
sha = blob.sha let sha: string | null = null
} catch (error) { if (status === 'A' || status === 'M') {
core.error( try {
`Error creating blob for file '${path}': ${utils.getErrorMessage(error)}` const {data: blob} = await blobCreationLimit(() =>
) this.octokit.rest.git.createBlob({
throw error ...repository,
content: utils.readFileBase64([repoPath, path]),
encoding: 'base64'
})
)
sha = blob.sha
} catch (error) {
core.error(
`Error creating blob for file '${path}': ${utils.getErrorMessage(error)}`
)
throw error
}
}
core.info(
`Creating tree object for blob at '${path}' with status '${status}'`
)
return <TreeObject>{
path,
mode,
sha,
type: 'blob'
} }
}
core.info(`Created blob for file '${path}'`)
return <TreeObject>{
path,
mode,
sha,
type: 'blob'
} }
}) })
) )