Skip to content

Ensure automated backport commits have verified signatures - #2998

Open
asivanadi0 wants to merge 2 commits into
NVIDIA:mainfrom
asivanadi0:fix/verified-backport-commits
Open

asivanadi0 wants to merge 2 commits into
NVIDIA:mainfrom
asivanadi0:fix/verified-backport-commits

Conversation

@asivanadi0

Copy link
Copy Markdown

Description

The cherry-pick workflow creates backport commits with a local git push, so they are unsigned (verified: false). That conflicts with signature verification (#2992) and branch protection that requires signed commits.

After pushing the backport branch, recreate each new commit through the GitHub Git Data API (preserving tree, message, and order), then force-update the backport branch to the signed chain. Same approach as NVIDIA/nvidia-container-toolkit#2012. PR creation and conflict handling are unchanged; no bot GPG/SSH key is required.

Fixes #2997

Checklist

  • No secrets, sensitive information, or unrelated changes
  • Lint checks passing (make lint)
  • Generated assets in-sync (make validate-generated-assets)
  • Go mod artifacts in-sync (make validate-modules)
  • Test cases are added for new code paths

Testing

  • node --check .github/scripts/backport.js
  • Diff against the signed-commit block from nvidia-container-toolkit#2012
  • No dedicated unit tests for this script in-repo; behavioral validation depends on a live backport run once merged

Recreate each cherry-picked commit via the GitHub Git Data API so
GitHub signs the resulting chain. Matches nvidia-container-toolkit NVIDIA#2012.

Signed-off-by: Anpoo Sivanadi <asivanadi0@users.noreply.github.com>
@asivanadi0
asivanadi0 requested a review from a team as a code owner October 2, 2026 00:02
@copy-pr-bot

copy-pr-bot Bot commented Oct 2, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

After pushing the cherry-picked backport branch, the script reads commits relative to the target branch and recreates them through GitHub’s Git Data API. It preserves each commit’s message and tree, chains the commits in oldest-to-newest order, and force-updates the backport branch ref to the final recreated commit.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: 🟡 Moderate · up to be283

Use the fetched target SHA and publish the backport branch only after commit recreation succeeds. Otherwise, concurrent target updates can be reversed in the backport diff, and failed runs can leave existing PRs pointing at unrecreated commits. Resolve these issues before merging unless explicitly accepted.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
.github/scripts/backport.js-106-107 (1)

106-107: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Use the checked-out target SHA for the recreated commit chain.

If the remote target branch advances after git fetch, the local ${targetBranch} and the remote getRef result differ. The Git Data API then creates a commit with a tree based on the older target state and a parent based on the newer state. The resulting backport diff can reverse intervening target changes.

Suggested fix
-    const { data: baseRef } = await github.rest.git.getRef({
-      owner: context.repo.owner,
-      repo: context.repo.repo,
-      ref: `heads/${targetBranch}`
-    });
-    let parentSha = baseRef.object.sha;
+    const targetSha = execSync(`git rev-parse ${targetBranch}`, { encoding: 'utf-8' }).trim();
+    let parentSha = targetSha;
.github/scripts/backport.js-109-137 (1)

109-137: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Keep backportBranch unchanged until API commit recreation succeeds.

The script pushes backportBranch before createCommit and updateRef. If either operation fails, the remote ref remains at the pre-API cherry-picked history. An existing PR using this branch can consume that history before a retry completes.

Push to a temporary ref, recreate the commits, then create or update backportBranch. Delete the temporary ref on both success and failure. Use createRef when the backport branch does not exist; updateRef cannot create it.

Suggested fix
     const backportBranch = `backport-${prNumber}-to-${targetBranch}`;
+    const stagingRef = `${backportBranch}-staging-${context.runId}`;
+    const removeStagingRef = async () => {
+      try {
+        await github.rest.git.deleteRef({
+          owner: context.repo.owner,
+          repo: context.repo.repo,
+          ref: `heads/${stagingRef}`
+        });
+      } catch (error) {
+        if (error.status !== 404) throw error;
+      }
+    };
     try {
...
-    execSync(`git push --force-with-lease origin ${backportBranch}`, { stdio: 'inherit' });
+    execSync(`git push --force-with-lease origin ${backportBranch}:${stagingRef}`, { stdio: 'inherit' });
...
-    core.info(`Repointing ${backportBranch} at signed commit ${parentSha}`);
-    await github.rest.git.updateRef({
-      owner: context.repo.owner,
-      repo: context.repo.repo,
-      ref: `heads/${backportBranch}`,
-      sha: parentSha,
-      force: true
-    });
+    await removeStagingRef();
+    let backportRefExists = true;
+    try {
+      await github.rest.git.getRef({
+        owner: context.repo.owner,
+        repo: context.repo.repo,
+        ref: `heads/${backportBranch}`
+      });
+    } catch (error) {
+      if (error.status === 404) {
+        backportRefExists = false;
+      } else {
+        throw error;
+      }
+    }
+    if (backportRefExists) {
+      await github.rest.git.updateRef({
+        owner: context.repo.owner,
+        repo: context.repo.repo,
+        ref: `heads/${backportBranch}`,
+        sha: parentSha,
+        force: true
+      });
+    } else {
+      await github.rest.git.createRef({
+        owner: context.repo.owner,
+        repo: context.repo.repo,
+        ref: `refs/heads/${backportBranch}`,
+        sha: parentSha
+      });
+    }
...
   } finally {
+    await removeStagingRef().catch(error => {
+      core.warning(`Failed to remove staging ref ${stagingRef}: ${error.message}`);
+    });
     // Clean up: go back to main branch

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: NVIDIA/gpu-operator/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 18daadee-fd8d-46bc-88fd-e447781f1c84

📥 Commits

Reviewing files that changed from the base of the PR and between 3e1873a and be28310.

📒 Files selected for processing (1)
  • .github/scripts/backport.js

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

Address CodeRabbit feedback on the verified-commit recreation path:
- Parent the API-created chain on the checked-out target branch SHA
  (git rev-parse) instead of a remote getRef that can race ahead.
- Push cherry-picks to a per-run staging ref until createCommit succeeds,
  then create or update the backport branch and always clean up staging.

Signed-off-by: Anpoo Sivanadi <sivanadi08@gmail.com>
@asivanadi0

Copy link
Copy Markdown
Author

Addressed the two CodeRabbit findings in .github/scripts/backport.js:

  1. Checked-out target SHA — parent the Git Data API commit chain on git rev-parse ${targetBranch} instead of a remote getRef, so the parent matches the cherry-pick base.
  2. Staging ref — push cherry-picks to ${backportBranch}-staging-${runId} until createCommit succeeds, then create/update backportBranch and always delete the staging ref in finally.

Commit: e70d9307fdf375ca1bbc957dd733ce8f9b204063

@rahulait

rahulait commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Any specific reason we want to have staging branch? I would prefer keeping the code patch same as what we had in toolkit patch to avoid drift in future for same backport.js.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ensure automated backport commits have verified signatures

2 participants