Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/patch-use-git-cat-file-for-blob-read.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 12 additions & 12 deletions actions/setup/js/push_signed_commits.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,20 @@ function unquoteCPath(s) {
}

/**
* Read a blob from a specific commit as a base64-encoded string using
* `git show <sha>:<path>`. The raw bytes emitted by git are collected via
* the `exec.exec` stdout listener so that binary files are not corrupted by
* any UTF-8 decoding layer (unlike `exec.getExecOutput` which always passes
* stdout through a `StringDecoder('utf8')`).
* Read a blob object as a base64-encoded string using `git cat-file blob <blobHash>`.
* The raw bytes emitted by git are collected via the `exec.exec` stdout
* listener so that binary files are not corrupted by any UTF-8 decoding
* layer (unlike `exec.getExecOutput` which always passes stdout through a
* `StringDecoder('utf8')`).
*
* @param {string} sha - Commit SHA to read the blob from
* @param {string} filePath - Repo-relative path of the file
* @param {string} blobHash - Object hash of the blob (from `git diff-tree --raw` dstHash field)
* @param {string} cwd - Working directory of the local git checkout
* @returns {Promise<string>} Base64-encoded file contents
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Clean refactor: using blobHash directly instead of sha + filePath is more precise and avoids potential path-quoting issues. The function signature change is clear and well-documented.

async function readBlobAsBase64(sha, filePath, cwd) {
async function readBlobAsBase64(blobHash, cwd) {
/** @type {Buffer[]} */
const chunks = [];
await exec.exec("git", ["show", `${sha}:${filePath}`], {
await exec.exec("git", ["cat-file", "blob", blobHash], {
cwd,
silent: true,
listeners: {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice simplification! By accepting blobHash directly instead of (sha, filePath), this function now does exactly one thing: reads a blob by its object hash. This is more robust since it avoids any path resolution ambiguity.

Expand Down Expand Up @@ -200,6 +199,7 @@ async function pushSignedCommits({ githubClient, owner, repo, branch, baseRef, c
}
const srcMode = modeFields[0]; // source file mode (e.g. 100644, 100755, 120000, 160000)
const dstMode = modeFields[1]; // destination file mode (e.g. 100644, 100755, 120000, 160000)
const dstHash = modeFields[3]; // destination blob hash (object ID of the file in this commit)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good extraction of dstHash from modeFields[3] — it's already available from the git diff-tree --raw output and reusing it avoids the need to compose sha:filePath strings at the call sites.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good addition of dstHash from modeFields[3]. This aligns with the git diff-tree --raw output format and makes the blob hash available for the refactored readBlobAsBase64 call below.

const status = modeFields[4]; // A=Added, M=Modified, D=Deleted, R=Renamed, C=Copied

const paths = line.slice(tabIdx + 1).split("\t");
Expand Down Expand Up @@ -231,7 +231,7 @@ async function pushSignedCommits({ githubClient, owner, repo, branch, baseRef, c
if (dstMode === "100755") {
core.warning(`pushSignedCommits: executable bit on ${renamedPath} will be lost in signed commit (GitHub GraphQL does not support mode 100755)`);
}
additions.push({ path: renamedPath, contents: await readBlobAsBase64(sha, renamedPath, cwd) });
additions.push({ path: renamedPath, contents: await readBlobAsBase64(dstHash, cwd) });
} else if (status && status.startsWith("C")) {
// Copy: source path is kept (no deletion), only the destination path is added
const copiedPath = unquoteCPath(paths[1]);
Expand All @@ -250,7 +250,7 @@ async function pushSignedCommits({ githubClient, owner, repo, branch, baseRef, c
if (dstMode === "100755") {
core.warning(`pushSignedCommits: executable bit on ${copiedPath} will be lost in signed commit (GitHub GraphQL does not support mode 100755)`);
}
additions.push({ path: copiedPath, contents: await readBlobAsBase64(sha, copiedPath, cwd) });
additions.push({ path: copiedPath, contents: await readBlobAsBase64(dstHash, cwd) });
} else {
// Added or Modified
if (dstMode === "160000") {
Expand All @@ -264,7 +264,7 @@ async function pushSignedCommits({ githubClient, owner, repo, branch, baseRef, c
if (dstMode === "100755") {
core.warning(`pushSignedCommits: executable bit on ${filePath} will be lost in signed commit (GitHub GraphQL does not support mode 100755)`);
}
additions.push({ path: filePath, contents: await readBlobAsBase64(sha, filePath, cwd) });
additions.push({ path: filePath, contents: await readBlobAsBase64(dstHash, cwd) });
}
}

Expand Down