@jphutchins/code-review 0.1.0-alpha.29 → 0.1.0-alpha.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -972,7 +972,7 @@ var pad2 = (n) => String(n).padStart(2, "0");
972
972
  var formatUtc = (d) => `${String(d.getUTCFullYear())}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())} ${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())} UTC`;
973
973
 
974
974
  // src/notice.ts
975
- var isNoticeKind = (s) => s === "security-blocked" || s === "setup-failed" || s === "diff-apply-failed" || s === "no-output";
975
+ var isNoticeKind = (s) => s === "security-blocked" || s === "setup-failed" || s === "checkout-failed" || s === "no-output";
976
976
  var blockquote = (text) => text.replaceAll("\n", "\n> ");
977
977
  var noticeSummary = (kind, reasons) => {
978
978
  switch (kind) {
@@ -984,8 +984,8 @@ The diff was flagged as unsafe to apply and execute:
984
984
  > ${blockquote(reasons)}` : "### \u{1F6D1} Code review skipped by the security gate\n\nThe security triage returned an unsafe verdict without a reason. See workflow logs.";
985
985
  case "setup-failed":
986
986
  return "### \u{1F6E0}\uFE0F Review did not run\n\nThe review job failed before the security triage could run (e.g. dependency install or environment setup). See the workflow logs \u2014 this is an infrastructure failure, not a security verdict.";
987
- case "diff-apply-failed":
988
- return "### \u26A0\uFE0F Could not apply the diff\n\nThe PR diff could not be applied to the checked-out base commit, so the review was skipped rather than run against an unmodified tree. See workflow logs.";
987
+ case "checkout-failed":
988
+ return "### \u26A0\uFE0F Could not check out the PR head\n\nThe PR head commit could not be fetched or checked out (it may have been force-pushed away, or is otherwise unavailable), so the review was skipped rather than run against the wrong tree. See workflow logs.";
989
989
  case "no-output":
990
990
  return "### \u26A0\uFE0F Review did not complete\n\nThe diff passed triage but the review produced no output. See workflow logs.";
991
991
  }
@@ -1953,6 +1953,7 @@ var renderOutputs = (result) => {
1953
1953
  return `pr=${String(result.pr)}
1954
1954
  conclusion=${result.conclusion}
1955
1955
  diff_size=${String(result.diffSize)}
1956
+ stacked=${String(result.stacked)}
1956
1957
  `;
1957
1958
  }
1958
1959
  };
@@ -1975,6 +1976,7 @@ var runGit = (args) => new Promise((resolve3, reject) => {
1975
1976
  var PrMetaCodec = t.type({
1976
1977
  changed_files: t.number,
1977
1978
  base_sha: t.string,
1979
+ base_ref: t.string,
1978
1980
  title: t.string,
1979
1981
  body: t.union([t.string, t.null])
1980
1982
  });
@@ -1995,7 +1997,7 @@ var fetchPrMeta = async (repo, prNumber, ghApi) => {
1995
1997
  const stdout = await ghApi([
1996
1998
  `repos/${repo}/pulls/${String(prNumber)}`,
1997
1999
  "--jq",
1998
- "{changed_files: .changed_files, base_sha: .base.sha, title: .title, body: .body}"
2000
+ "{changed_files: .changed_files, base_sha: .base.sha, base_ref: .base.ref, title: .title, body: .body}"
1999
2001
  ]);
2000
2002
  const decoded = PrMetaCodec.decode(JSON.parse(stdout));
2001
2003
  if (decoded._tag === "Left") {
@@ -2010,6 +2012,53 @@ var fetchApiDiff = async (repo, prNumber, ghApi) => {
2010
2012
  return null;
2011
2013
  }
2012
2014
  };
2015
+ var fetchFullDiff = async (repo, defaultBranch, headSha, ghApi, gitRun) => {
2016
+ try {
2017
+ const diff = await ghApi([
2018
+ `repos/${repo}/compare/${defaultBranch}...${headSha}`,
2019
+ "-H",
2020
+ "Accept: application/vnd.github.v3.diff"
2021
+ ]);
2022
+ if (diff.length > 0) return diff;
2023
+ process.stderr.write(
2024
+ `compare diff for ${defaultBranch}...${headSha} was empty \u2014 falling back to git diff
2025
+ `
2026
+ );
2027
+ } catch (err) {
2028
+ process.stderr.write(`compare diff fetch failed (${errMsg(err)}) \u2014 falling back to git diff
2029
+ `);
2030
+ }
2031
+ await gitRun(["fetch", "origin", headSha]);
2032
+ const base = (await gitRun(["rev-parse", "HEAD"])).trim();
2033
+ return gitRun(["diff", `${base}...${headSha}`]);
2034
+ };
2035
+ var CommitCodec = t.type({
2036
+ sha: t.string,
2037
+ message: t.string,
2038
+ author: t.union([t.string, t.null]),
2039
+ email: t.union([t.string, t.null])
2040
+ });
2041
+ var COMMIT_JQ = ".commits[] | {sha: .sha, message: .commit.message, author: .commit.author.name, email: .commit.author.email}";
2042
+ var fetchCompareCommits = async (repo, defaultBranch, headSha, ghApi) => {
2043
+ const rows = parseJsonl(
2044
+ await ghApi([
2045
+ `repos/${repo}/compare/${defaultBranch}...${headSha}`,
2046
+ "--paginate",
2047
+ "--jq",
2048
+ COMMIT_JQ
2049
+ ])
2050
+ );
2051
+ const decoded = rows.map((row) => CommitCodec.decode(row));
2052
+ const commits = decoded.flatMap((d) => d._tag === "Right" ? [d.right] : []);
2053
+ const dropped = decoded.length - commits.length;
2054
+ if (dropped > 0) {
2055
+ process.stderr.write(
2056
+ `Warning: ${String(dropped)} of ${String(rows.length)} commit row(s) failed to decode \u2014 their messages are NOT in the triage scan though a checkout's git log still exposes them
2057
+ `
2058
+ );
2059
+ }
2060
+ return commits;
2061
+ };
2013
2062
  var COMMENT_JQ = ".[] | {id: .id, body: .body, user: {login: .user.login}, created_at: .created_at, author_association: .author_association}";
2014
2063
  var REVIEW_COMMENT_JQ = ".[] | {body: .body, user: {login: .user.login}, created_at: .created_at, author_association: .author_association, path: .path, line: .line}";
2015
2064
  var REVIEW_JQ = ".[] | {body: .body, user: {login: .user.login}, submitted_at: .submitted_at, author_association: .author_association, state: .state}";
@@ -2131,8 +2180,10 @@ var gather = async (input, ghApi = runGhApi, gitRun = runGit) => {
2131
2180
  }
2132
2181
  const prNumber = resolution.prNumber;
2133
2182
  const meta = await fetchPrMeta(input.repo, prNumber, ghApi);
2134
- const apiDiff = await fetchApiDiff(input.repo, prNumber, ghApi);
2135
- const diff = apiDiff !== null && !(apiDiff.length === 0 && meta.changed_files > 0) ? apiDiff : await (async () => {
2183
+ const stacked = meta.base_ref !== input.defaultBranch;
2184
+ const prDiff = await (async () => {
2185
+ const apiDiff = await fetchApiDiff(input.repo, prNumber, ghApi);
2186
+ if (apiDiff !== null && !(apiDiff.length === 0 && meta.changed_files > 0)) return apiDiff;
2136
2187
  process.stderr.write(
2137
2188
  `PR diff fetch failed or was empty for ${String(meta.changed_files)} changed files \u2014 falling back to git diff
2138
2189
  `
@@ -2140,7 +2191,11 @@ var gather = async (input, ghApi = runGhApi, gitRun = runGit) => {
2140
2191
  await gitRun(["fetch", "origin", input.headSha]);
2141
2192
  return gitRun(["diff", meta.base_sha, input.headSha]);
2142
2193
  })();
2143
- writeFileSync(join(input.outDir, "pr.diff"), diff);
2194
+ const fullDiff = stacked ? await fetchFullDiff(input.repo, input.defaultBranch, input.headSha, ghApi, gitRun) : prDiff;
2195
+ const commits = await fetchCompareCommits(input.repo, input.defaultBranch, input.headSha, ghApi);
2196
+ writeFileSync(join(input.outDir, "full.diff"), fullDiff);
2197
+ writeFileSync(join(input.outDir, "pr.diff"), prDiff);
2198
+ writeFileSync(join(input.outDir, "commits.json"), JSON.stringify(commits));
2144
2199
  writeFileSync(
2145
2200
  join(input.outDir, "pr_context.json"),
2146
2201
  JSON.stringify({ title: meta.title, body: meta.body })
@@ -2177,7 +2232,8 @@ var gather = async (input, ghApi = runGhApi, gitRun = runGit) => {
2177
2232
  kind: "gathered",
2178
2233
  pr: prNumber,
2179
2234
  conclusion: input.conclusion,
2180
- diffSize: Buffer.byteLength(diff, "utf8")
2235
+ diffSize: Buffer.byteLength(prDiff, "utf8"),
2236
+ stacked
2181
2237
  };
2182
2238
  };
2183
2239
 
@@ -3189,7 +3245,7 @@ var noticeCmd = defineCommand({
3189
3245
  args: {
3190
3246
  kind: {
3191
3247
  type: "positional",
3192
- description: "One of: security-blocked, setup-failed, diff-apply-failed, no-output",
3248
+ description: "One of: security-blocked, setup-failed, checkout-failed, no-output",
3193
3249
  required: true
3194
3250
  },
3195
3251
  reasons: {
@@ -3199,7 +3255,7 @@ var noticeCmd = defineCommand({
3199
3255
  },
3200
3256
  run: ({ args }) => {
3201
3257
  const kind = isNoticeKind(args.kind) ? args.kind : fail(
3202
- `Unknown notice kind "${args.kind}" \u2014 expected one of: security-blocked, setup-failed, diff-apply-failed, no-output`
3258
+ `Unknown notice kind "${args.kind}" \u2014 expected one of: security-blocked, setup-failed, checkout-failed, no-output`
3203
3259
  );
3204
3260
  process.stdout.write(`${JSON.stringify(buildNoticeEnvelope(kind, args.reasons), null, 2)}
3205
3261
  `);
@@ -3464,6 +3520,11 @@ var gatherCmd = defineCommand({
3464
3520
  type: "string",
3465
3521
  description: "Head branch to disambiguate the PR when multiple share a commit"
3466
3522
  },
3523
+ "default-branch": {
3524
+ type: "string",
3525
+ description: "The repo's default branch \u2014 the trusted base the review checks out, and the reference for the full (triage) diff; a PR based on any other branch is treated as stacked",
3526
+ required: true
3527
+ },
3467
3528
  "run-id": {
3468
3529
  type: "string",
3469
3530
  description: "CI run id (from workflow_run.id); its failing jobs' logs are downloaded on failure",
@@ -3488,6 +3549,7 @@ var gatherCmd = defineCommand({
3488
3549
  repo: args.repo,
3489
3550
  headSha: args["head-sha"],
3490
3551
  headBranch: args["head-branch"],
3552
+ defaultBranch: args["default-branch"],
3491
3553
  runId: args["run-id"],
3492
3554
  conclusion: args.conclusion,
3493
3555
  botLogin: args["bot-login"] || "github-actions[bot]",