Skip to content

Commit dd2324f

Browse files
fix: use showFileAtRefBase64 to read per-commit file contents (#3744)
* GitCommandManager: add a function to get a file's contents at a specific revision * use showFileAtRef instead of readFileBase64 * Teach GitCommandManager.exec about an object of exec parameters so we can add more * Encode the showFiletRef output as base64 out of the gate * Fix missing async for function * Use Buffer.concat to avoid issues with partial data streams * formatting --------- Co-authored-by: gustavderdrache <alex.ford@determinate.systems>
1 parent 367180c commit dd2324f

6 files changed

+71
-56
lines changed

dist/index.js

+31-28
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ var WorkingBaseType;
6767
})(WorkingBaseType || (exports.WorkingBaseType = WorkingBaseType = {}));
6868
function getWorkingBaseAndType(git) {
6969
return __awaiter(this, void 0, void 0, function* () {
70-
const symbolicRefResult = yield git.exec(['symbolic-ref', 'HEAD', '--short'], true);
70+
const symbolicRefResult = yield git.exec(['symbolic-ref', 'HEAD', '--short'], { allowAllExitCodes: true });
7171
if (symbolicRefResult.exitCode == 0) {
7272
// A ref is checked out
7373
return [symbolicRefResult.stdout.trim(), WorkingBaseType.Branch];
@@ -194,7 +194,7 @@ function createOrUpdateBranch(git, commitMessage, base, branch, branchRemoteName
194194
else {
195195
aopts.push('-A');
196196
}
197-
yield git.exec(aopts, true);
197+
yield git.exec(aopts, { allowAllExitCodes: true });
198198
const popts = ['-m', commitMessage];
199199
if (signoff) {
200200
popts.push('--signoff');
@@ -517,7 +517,7 @@ function createPullRequest(inputs) {
517517
// Create signed commits via the GitHub API
518518
const stashed = yield git.stashPush(['--include-untracked']);
519519
yield git.checkout(inputs.branch);
520-
const pushSignedCommitsResult = yield ghBranch.pushSignedCommits(result.branchCommits, result.baseCommit, repoPath, branchRepository, inputs.branch);
520+
const pushSignedCommitsResult = yield ghBranch.pushSignedCommits(git, result.branchCommits, result.baseCommit, repoPath, branchRepository, inputs.branch);
521521
outputs.set('pull-request-head-sha', pushSignedCommitsResult.sha);
522522
outputs.set('pull-request-commits-verified', pushSignedCommitsResult.verified.toString());
523523
yield git.checkout('-');
@@ -704,7 +704,7 @@ class GitCommandManager {
704704
if (options) {
705705
args.push(...options);
706706
}
707-
return yield this.exec(args, allowAllExitCodes);
707+
return yield this.exec(args, { allowAllExitCodes: allowAllExitCodes });
708708
});
709709
}
710710
commit(options_1) {
@@ -716,7 +716,7 @@ class GitCommandManager {
716716
if (options) {
717717
args.push(...options);
718718
}
719-
return yield this.exec(args, allowAllExitCodes);
719+
return yield this.exec(args, { allowAllExitCodes: allowAllExitCodes });
720720
});
721721
}
722722
config(configKey, configValue, globalConfig, add) {
@@ -738,7 +738,7 @@ class GitCommandManager {
738738
'--get-regexp',
739739
configKey,
740740
configValue
741-
], true);
741+
], { allowAllExitCodes: true });
742742
return output.exitCode === 0;
743743
});
744744
}
@@ -835,7 +835,7 @@ class GitCommandManager {
835835
if (options) {
836836
args.push(...options);
837837
}
838-
const output = yield this.exec(args, true);
838+
const output = yield this.exec(args, { allowAllExitCodes: true });
839839
return output.exitCode === 1;
840840
});
841841
}
@@ -892,6 +892,13 @@ class GitCommandManager {
892892
return output.stdout.trim();
893893
});
894894
}
895+
showFileAtRefBase64(ref, path) {
896+
return __awaiter(this, void 0, void 0, function* () {
897+
const args = ['show', `${ref}:${path}`];
898+
const output = yield this.exec(args, { encoding: 'base64' });
899+
return output.stdout.trim();
900+
});
901+
}
895902
stashPush(options) {
896903
return __awaiter(this, void 0, void 0, function* () {
897904
const args = ['stash', 'push'];
@@ -939,13 +946,13 @@ class GitCommandManager {
939946
'--unset',
940947
configKey,
941948
configValue
942-
], true);
949+
], { allowAllExitCodes: true });
943950
return output.exitCode === 0;
944951
});
945952
}
946953
tryGetRemoteUrl() {
947954
return __awaiter(this, void 0, void 0, function* () {
948-
const output = yield this.exec(['config', '--local', '--get', 'remote.origin.url'], true);
955+
const output = yield this.exec(['config', '--local', '--get', 'remote.origin.url'], { allowAllExitCodes: true });
949956
if (output.exitCode !== 0) {
950957
return '';
951958
}
@@ -957,30 +964,34 @@ class GitCommandManager {
957964
});
958965
}
959966
exec(args_1) {
960-
return __awaiter(this, arguments, void 0, function* (args, allowAllExitCodes = false) {
967+
return __awaiter(this, arguments, void 0, function* (args, { encoding = 'utf8', allowAllExitCodes = false } = {}) {
961968
const result = new GitOutput();
962969
const env = {};
963970
for (const key of Object.keys(process.env)) {
964971
env[key] = process.env[key];
965972
}
966973
const stdout = [];
974+
let stdoutLength = 0;
967975
const stderr = [];
976+
let stderrLength = 0;
968977
const options = {
969978
cwd: this.workingDirectory,
970979
env,
971980
ignoreReturnCode: allowAllExitCodes,
972981
listeners: {
973982
stdout: (data) => {
974-
stdout.push(data.toString());
983+
stdout.push(data);
984+
stdoutLength += data.length;
975985
},
976986
stderr: (data) => {
977-
stderr.push(data.toString());
987+
stderr.push(data);
988+
stderrLength += data.length;
978989
}
979990
}
980991
};
981992
result.exitCode = yield exec.exec(`"${this.gitPath}"`, args, options);
982-
result.stdout = stdout.join('');
983-
result.stderr = stderr.join('');
993+
result.stdout = Buffer.concat(stdout, stdoutLength).toString(encoding);
994+
result.stderr = Buffer.concat(stderr, stderrLength).toString(encoding);
984995
return result;
985996
});
986997
}
@@ -1400,21 +1411,21 @@ class GitHubHelper {
14001411
return pull;
14011412
});
14021413
}
1403-
pushSignedCommits(branchCommits, baseCommit, repoPath, branchRepository, branch) {
1414+
pushSignedCommits(git, branchCommits, baseCommit, repoPath, branchRepository, branch) {
14041415
return __awaiter(this, void 0, void 0, function* () {
14051416
let headCommit = {
14061417
sha: baseCommit.sha,
14071418
tree: baseCommit.tree,
14081419
verified: false
14091420
};
14101421
for (const commit of branchCommits) {
1411-
headCommit = yield this.createCommit(commit, headCommit, repoPath, branchRepository);
1422+
headCommit = yield this.createCommit(git, commit, headCommit, repoPath, branchRepository);
14121423
}
14131424
yield this.createOrUpdateRef(branchRepository, branch, headCommit.sha);
14141425
return headCommit;
14151426
});
14161427
}
1417-
createCommit(commit, parentCommit, repoPath, branchRepository) {
1428+
createCommit(git, commit, parentCommit, repoPath, branchRepository) {
14181429
return __awaiter(this, void 0, void 0, function* () {
14191430
const repository = this.parseRepository(branchRepository);
14201431
// In the case of an empty commit, the tree references the parent's tree
@@ -1436,7 +1447,9 @@ class GitHubHelper {
14361447
let sha = null;
14371448
if (status === 'A' || status === 'M') {
14381449
try {
1439-
const { data: blob } = yield blobCreationLimit(() => this.octokit.rest.git.createBlob(Object.assign(Object.assign({}, repository), { content: utils.readFileBase64([repoPath, path]), encoding: 'base64' })));
1450+
const { data: blob } = yield blobCreationLimit(() => __awaiter(this, void 0, void 0, function* () {
1451+
return this.octokit.rest.git.createBlob(Object.assign(Object.assign({}, repository), { content: yield git.showFileAtRefBase64(commit.sha, path), encoding: 'base64' }));
1452+
}));
14401453
sha = blob.sha;
14411454
}
14421455
catch (error) {
@@ -1763,7 +1776,6 @@ exports.randomString = randomString;
17631776
exports.parseDisplayNameEmail = parseDisplayNameEmail;
17641777
exports.fileExistsSync = fileExistsSync;
17651778
exports.readFile = readFile;
1766-
exports.readFileBase64 = readFileBase64;
17671779
exports.getErrorMessage = getErrorMessage;
17681780
const core = __importStar(__nccwpck_require__(7484));
17691781
const fs = __importStar(__nccwpck_require__(9896));
@@ -1853,15 +1865,6 @@ function fileExistsSync(path) {
18531865
function readFile(path) {
18541866
return fs.readFileSync(path, 'utf-8');
18551867
}
1856-
function readFileBase64(pathParts) {
1857-
const resolvedPath = path.resolve(...pathParts);
1858-
if (fs.lstatSync(resolvedPath).isSymbolicLink()) {
1859-
return fs
1860-
.readlinkSync(resolvedPath, { encoding: 'buffer' })
1861-
.toString('base64');
1862-
}
1863-
return fs.readFileSync(resolvedPath).toString('base64');
1864-
}
18651868
/* eslint-disable @typescript-eslint/no-explicit-any */
18661869
function hasErrorCode(error) {
18671870
return typeof (error && error.code) === 'string';

src/create-or-update-branch.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export async function getWorkingBaseAndType(
1919
): Promise<[string, WorkingBaseType]> {
2020
const symbolicRefResult = await git.exec(
2121
['symbolic-ref', 'HEAD', '--short'],
22-
true
22+
{allowAllExitCodes: true}
2323
)
2424
if (symbolicRefResult.exitCode == 0) {
2525
// A ref is checked out
@@ -200,7 +200,7 @@ export async function createOrUpdateBranch(
200200
} else {
201201
aopts.push('-A')
202202
}
203-
await git.exec(aopts, true)
203+
await git.exec(aopts, {allowAllExitCodes: true})
204204
const popts = ['-m', commitMessage]
205205
if (signoff) {
206206
popts.push('--signoff')

src/create-pull-request.ts

+1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
211211
const stashed = await git.stashPush(['--include-untracked'])
212212
await git.checkout(inputs.branch)
213213
const pushSignedCommitsResult = await ghBranch.pushSignedCommits(
214+
git,
214215
result.branchCommits,
215216
result.baseCommit,
216217
repoPath,

src/git-command-manager.ts

+31-13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ export type Commit = {
2121
unparsedChanges: string[]
2222
}
2323

24+
export type ExecOpts = {
25+
allowAllExitCodes?: boolean
26+
encoding?: 'utf8' | 'base64'
27+
}
28+
2429
export class GitCommandManager {
2530
private gitPath: string
2631
private workingDirectory: string
@@ -66,7 +71,7 @@ export class GitCommandManager {
6671
args.push(...options)
6772
}
6873

69-
return await this.exec(args, allowAllExitCodes)
74+
return await this.exec(args, {allowAllExitCodes: allowAllExitCodes})
7075
}
7176

7277
async commit(
@@ -82,7 +87,7 @@ export class GitCommandManager {
8287
args.push(...options)
8388
}
8489

85-
return await this.exec(args, allowAllExitCodes)
90+
return await this.exec(args, {allowAllExitCodes: allowAllExitCodes})
8691
}
8792

8893
async config(
@@ -113,7 +118,7 @@ export class GitCommandManager {
113118
configKey,
114119
configValue
115120
],
116-
true
121+
{allowAllExitCodes: true}
117122
)
118123
return output.exitCode === 0
119124
}
@@ -222,7 +227,7 @@ export class GitCommandManager {
222227
if (options) {
223228
args.push(...options)
224229
}
225-
const output = await this.exec(args, true)
230+
const output = await this.exec(args, {allowAllExitCodes: true})
226231
return output.exitCode === 1
227232
}
228233

@@ -278,6 +283,12 @@ export class GitCommandManager {
278283
return output.stdout.trim()
279284
}
280285

286+
async showFileAtRefBase64(ref: string, path: string): Promise<string> {
287+
const args = ['show', `${ref}:${path}`]
288+
const output = await this.exec(args, {encoding: 'base64'})
289+
return output.stdout.trim()
290+
}
291+
281292
async stashPush(options?: string[]): Promise<boolean> {
282293
const args = ['stash', 'push']
283294
if (options) {
@@ -326,15 +337,15 @@ export class GitCommandManager {
326337
configKey,
327338
configValue
328339
],
329-
true
340+
{allowAllExitCodes: true}
330341
)
331342
return output.exitCode === 0
332343
}
333344

334345
async tryGetRemoteUrl(): Promise<string> {
335346
const output = await this.exec(
336347
['config', '--local', '--get', 'remote.origin.url'],
337-
true
348+
{allowAllExitCodes: true}
338349
)
339350

340351
if (output.exitCode !== 0) {
@@ -349,34 +360,41 @@ export class GitCommandManager {
349360
return stdout
350361
}
351362

352-
async exec(args: string[], allowAllExitCodes = false): Promise<GitOutput> {
363+
async exec(
364+
args: string[],
365+
{encoding = 'utf8', allowAllExitCodes = false}: ExecOpts = {}
366+
): Promise<GitOutput> {
353367
const result = new GitOutput()
354368

355369
const env = {}
356370
for (const key of Object.keys(process.env)) {
357371
env[key] = process.env[key]
358372
}
359373

360-
const stdout: string[] = []
361-
const stderr: string[] = []
374+
const stdout: Buffer[] = []
375+
let stdoutLength = 0
376+
const stderr: Buffer[] = []
377+
let stderrLength = 0
362378

363379
const options = {
364380
cwd: this.workingDirectory,
365381
env,
366382
ignoreReturnCode: allowAllExitCodes,
367383
listeners: {
368384
stdout: (data: Buffer) => {
369-
stdout.push(data.toString())
385+
stdout.push(data)
386+
stdoutLength += data.length
370387
},
371388
stderr: (data: Buffer) => {
372-
stderr.push(data.toString())
389+
stderr.push(data)
390+
stderrLength += data.length
373391
}
374392
}
375393
}
376394

377395
result.exitCode = await exec.exec(`"${this.gitPath}"`, args, options)
378-
result.stdout = stdout.join('')
379-
result.stderr = stderr.join('')
396+
result.stdout = Buffer.concat(stdout, stdoutLength).toString(encoding)
397+
result.stderr = Buffer.concat(stderr, stderrLength).toString(encoding)
380398
return result
381399
}
382400
}

src/github-helper.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as core from '@actions/core'
22
import {Inputs} from './create-pull-request'
3-
import {Commit} from './git-command-manager'
3+
import {Commit, GitCommandManager} from './git-command-manager'
44
import {Octokit, OctokitOptions, throttleOptions} from './octokit-client'
55
import pLimit from 'p-limit'
66
import * as utils from './utils'
@@ -220,6 +220,7 @@ export class GitHubHelper {
220220
}
221221

222222
async pushSignedCommits(
223+
git: GitCommandManager,
223224
branchCommits: Commit[],
224225
baseCommit: Commit,
225226
repoPath: string,
@@ -233,6 +234,7 @@ export class GitHubHelper {
233234
}
234235
for (const commit of branchCommits) {
235236
headCommit = await this.createCommit(
237+
git,
236238
commit,
237239
headCommit,
238240
repoPath,
@@ -244,6 +246,7 @@ export class GitHubHelper {
244246
}
245247

246248
private async createCommit(
249+
git: GitCommandManager,
247250
commit: Commit,
248251
parentCommit: CommitResponse,
249252
repoPath: string,
@@ -269,10 +272,10 @@ export class GitHubHelper {
269272
let sha: string | null = null
270273
if (status === 'A' || status === 'M') {
271274
try {
272-
const {data: blob} = await blobCreationLimit(() =>
275+
const {data: blob} = await blobCreationLimit(async () =>
273276
this.octokit.rest.git.createBlob({
274277
...repository,
275-
content: utils.readFileBase64([repoPath, path]),
278+
content: await git.showFileAtRefBase64(commit.sha, path),
276279
encoding: 'base64'
277280
})
278281
)

0 commit comments

Comments
 (0)