Conversation
3207d65 to
30976e0
Compare
87768ff to
c28be95
Compare
079aa5d to
4c2b81c
Compare
e6b8af8 to
05ee8dc
Compare
81beb13 to
45cdbc6
Compare
2559dc9 to
5380682
Compare
Generated-by: update-generated-files-action
https://github.com/int128/update-generated-files-action/actions/runs/20670825090 Generated-by: update-generated-files-action
Auto-generated-by: update-generated-files-action; https://github.com/int128/update-generated-files-action/actions/runs/20688728759
Auto-generated-by: update-generated-files-action; https://github.com/int128/update-generated-files-action/actions/runs/23083230797
Auto-generated-by: update-generated-files-action; https://github.com/int128/update-generated-files-action/actions/runs/23086918292
There was a problem hiding this comment.
Pull request overview
This PR introduces a new “commit signing” step that rewrites the current commit via the GitHub REST API and integrates that step into both pull-request and non-PR event flows.
Changes:
- Add
signCurrentCommithelper that recreates the current commit via Octokit and swaps HEAD to the new commit. - Invoke signing in
handlePullRequestEventandhandleOtherEvent, and threadoctokitinto PR-event handling. - Add
git.deleteRefhelper and update tests to mock the new signing module / new function signature.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
src/sign.ts |
New helper that recreates the current commit through the GitHub API and updates local checkout. |
src/pull_request_event.ts |
Passes Octokit through and signs commits during PR handling (including a merge-fallback path). |
src/other_event.ts |
Signs commits in the non-PR flow before attempting fast-forward update / PR creation. |
src/run.ts |
Updates PR-event call to pass octokit. |
src/git.ts |
Adds deleteRef helper used by signing cleanup. |
tests/pull_request_event.test.ts |
Updates tests for the new handlePullRequestEvent signature and mocks signing. |
tests/other_event.test.ts |
Mocks signing to avoid side effects in existing tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export const deleteRef = async (ref: string, context: Context) => | ||
| await exec.exec('git', [...gitTokenConfigFlags(context), 'push', 'origin', '--quiet', '--delete', ref], { | ||
| env: { | ||
| ...process.env, |
There was a problem hiding this comment.
deleteRef is used for best-effort cleanup, but it currently always throws on non-zero exit codes (e.g., ref already deleted / missing permissions), which can fail the whole run during cleanup. Consider adding an option to ignore return codes (or handling common non-fatal cases) so callers like signing cleanup can avoid masking the main error.
| export const deleteRef = async (ref: string, context: Context) => | |
| await exec.exec('git', [...gitTokenConfigFlags(context), 'push', 'origin', '--quiet', '--delete', ref], { | |
| env: { | |
| ...process.env, | |
| export const deleteRef = async (ref: string, context: Context, options?: exec.ExecOptions) => | |
| await exec.exec('git', [...gitTokenConfigFlags(context), 'push', 'origin', '--quiet', '--delete', ref], { | |
| ...options, | |
| env: { | |
| ...process.env, | |
| ...options?.env, |
| export const signCurrentCommit = async (context: Context, octokit: Octokit) => { | ||
| const unsignedCommitSHA = await git.getCurrentSHA() | ||
| const signingBranch = `signing--${unsignedCommitSHA}` | ||
| await git.push({ localRef: unsignedCommitSHA, remoteRef: `refs/heads/${signingBranch}`, dryRun: false }, context) |
There was a problem hiding this comment.
signCurrentCommit always performs a real remote push (dryRun: false) and then deletes the remote ref, which breaks the action’s dry-run contract (it will still create/delete branches and mutate remote state). Consider threading a dryRun flag into signCurrentCommit and making it a no-op (or warning-only) when dryRun is enabled, and ensure any cleanup steps also respect dryRun.
| export const signCurrentCommit = async (context: Context, octokit: Octokit) => { | |
| const unsignedCommitSHA = await git.getCurrentSHA() | |
| const signingBranch = `signing--${unsignedCommitSHA}` | |
| await git.push({ localRef: unsignedCommitSHA, remoteRef: `refs/heads/${signingBranch}`, dryRun: false }, context) | |
| export const signCurrentCommit = async (context: Context, octokit: Octokit, dryRun = false) => { | |
| if (dryRun) { | |
| console.warn('Dry-run enabled: skipping signCurrentCommit because it would push and delete remote refs.') | |
| return | |
| } | |
| const unsignedCommitSHA = await git.getCurrentSHA() | |
| const signingBranch = `signing--${unsignedCommitSHA}` | |
| await git.push({ localRef: unsignedCommitSHA, remoteRef: `refs/heads/${signingBranch}`, dryRun }, context) |
| repo: context.repo.repo, | ||
| message: unsignedCommit.message, | ||
| tree: unsignedCommit.tree.sha, | ||
| parents: unsignedCommit.parents.map((parent) => parent.sha), |
There was a problem hiding this comment.
The commit created via octokit.rest.git.createCommit does not set author/committer. That will typically change the resulting commit metadata (and potentially timestamps) compared to the locally created commit, which can be surprising and may break expectations about attribution. Consider copying author and committer fields from the fetched unsignedCommit (or otherwise explicitly setting them) when creating the new commit.
| parents: unsignedCommit.parents.map((parent) => parent.sha), | |
| parents: unsignedCommit.parents.map((parent) => parent.sha), | |
| ...(unsignedCommit.author | |
| ? { | |
| author: { | |
| name: unsignedCommit.author.name, | |
| email: unsignedCommit.author.email, | |
| date: unsignedCommit.author.date, | |
| }, | |
| } | |
| : {}), | |
| ...(unsignedCommit.committer | |
| ? { | |
| committer: { | |
| name: unsignedCommit.committer.name, | |
| email: unsignedCommit.committer.email, | |
| date: unsignedCommit.committer.date, | |
| }, | |
| } | |
| : {}), |
| await git.fetch({ refs: [signedCommit.sha], depth: 2 }, context) | ||
| await git.checkout(signedCommit.sha) | ||
| } finally { | ||
| await git.deleteRef(`refs/heads/${signingBranch}`, context) |
There was a problem hiding this comment.
If any operation inside the try fails, an error thrown by git.deleteRef in the finally block will override the original failure and make debugging harder. Consider wrapping the cleanup in its own try/catch and logging a warning on cleanup failure so the primary error is preserved.
| await git.deleteRef(`refs/heads/${signingBranch}`, context) | |
| try { | |
| await git.deleteRef(`refs/heads/${signingBranch}`, context) | |
| } catch (error) { | |
| console.warn(`Failed to delete temporary signing branch refs/heads/${signingBranch}`, error) | |
| } |
| }) | ||
| } | ||
|
|
||
| await core.group(`Signing the commit`, () => signCurrentCommit(context, octokit)) |
There was a problem hiding this comment.
Signing is executed unconditionally before the dryRun short-circuit. With inputs.dryRun === true, this will still perform remote mutations (branch create/update/delete and REST API calls) even though later steps try to avoid pushing. Consider skipping signing when dryRun is enabled, or refactoring so signing respects dryRun end-to-end.
| await core.group(`Signing the commit`, () => signCurrentCommit(context, octokit)) | |
| if (inputs.dryRun) { | |
| core.warning(`[dry-run] Skipping commit signing.`) | |
| } else { | |
| await core.group(`Signing the commit`, () => signCurrentCommit(context, octokit)) | |
| } |
| await core.group(`Committing the workspace changes`, async () => { | ||
| await git.commit([inputs.commitMessage, inputs.commitMessageFooter, getAutoGeneratedTrailer(context)]) | ||
| }) | ||
| await core.group(`Signing the commit`, () => signCurrentCommit(context, octokit)) |
There was a problem hiding this comment.
signCurrentCommit is called even when inputs.dryRun is true, which will still mutate remote state (temporary branch, updateRef, delete) despite the rest of the flow supporting dry-run. Consider gating signing behind !inputs.dryRun (or making signing itself dry-run aware).
| await core.group(`Signing the commit`, () => signCurrentCommit(context, octokit)) | |
| if (inputs.dryRun) { | |
| core.warning(`[dry-run] Skipping commit signing`) | |
| } else { | |
| await core.group(`Signing the commit`, () => signCurrentCommit(context, octokit)) | |
| } |
| export const signCurrentCommit = async (context: Context, octokit: Octokit) => { | ||
| const unsignedCommitSHA = await git.getCurrentSHA() | ||
| const signingBranch = `signing--${unsignedCommitSHA}` | ||
| await git.push({ localRef: unsignedCommitSHA, remoteRef: `refs/heads/${signingBranch}`, dryRun: false }, context) | ||
| try { | ||
| const { data: unsignedCommit } = await octokit.rest.git.getCommit({ |
There was a problem hiding this comment.
New signing behavior in signCurrentCommit is not covered by tests. Since this is a new side-effectful workflow step (git push/delete + Octokit REST calls), consider adding a dedicated Vitest suite that mocks git and octokit.rest.git to assert the expected sequence and ensure error/cleanup behavior is correct (including dry-run).
No description provided.