@fern-api/generator-cli 0.9.43 → 0.9.44

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.
Files changed (3) hide show
  1. package/dist/api.js +20 -0
  2. package/dist/cli.js +72 -19
  3. package/package.json +1 -1
package/dist/api.js CHANGED
@@ -6168,6 +6168,26 @@ var ClonedRepository = class _ClonedRepository {
6168
6168
  const result = await this.git.raw(["rev-parse", `${commitSha}^{tree}`]);
6169
6169
  return result.trim();
6170
6170
  }
6171
+ async getCommitMessage(commitSha) {
6172
+ await this.git.cwd(this.clonePath);
6173
+ const result = await this.git.raw(["log", "-1", "--format=%B", commitSha]);
6174
+ return result.replace(/\n$/, "");
6175
+ }
6176
+ async getCommitParents(commitSha) {
6177
+ await this.git.cwd(this.clonePath);
6178
+ const result = await this.git.raw(["rev-list", "--parents", "-n", "1", commitSha]);
6179
+ const parts = result.trim().split(/\s+/);
6180
+ return parts.slice(1);
6181
+ }
6182
+ /**
6183
+ * Returns the SHAs of commits reachable from HEAD but not from any remote-tracking ref,
6184
+ * ordered oldest-first. These are the commits created locally that have not been pushed.
6185
+ */
6186
+ async getLocalOnlyCommits() {
6187
+ await this.git.cwd(this.clonePath);
6188
+ const result = await this.git.raw(["rev-list", "--reverse", "HEAD", "--not", "--remotes=origin"]);
6189
+ return result.trim().split("\n").filter((line) => line.length > 0);
6190
+ }
6171
6191
  /**
6172
6192
  * Returns true if the working tree has uncommitted changes (staged or unstaged).
6173
6193
  */
package/dist/cli.js CHANGED
@@ -32254,6 +32254,26 @@ var ClonedRepository = class _ClonedRepository {
32254
32254
  const result = await this.git.raw(["rev-parse", `${commitSha}^{tree}`]);
32255
32255
  return result.trim();
32256
32256
  }
32257
+ async getCommitMessage(commitSha) {
32258
+ await this.git.cwd(this.clonePath);
32259
+ const result = await this.git.raw(["log", "-1", "--format=%B", commitSha]);
32260
+ return result.replace(/\n$/, "");
32261
+ }
32262
+ async getCommitParents(commitSha) {
32263
+ await this.git.cwd(this.clonePath);
32264
+ const result = await this.git.raw(["rev-list", "--parents", "-n", "1", commitSha]);
32265
+ const parts = result.trim().split(/\s+/);
32266
+ return parts.slice(1);
32267
+ }
32268
+ /**
32269
+ * Returns the SHAs of commits reachable from HEAD but not from any remote-tracking ref,
32270
+ * ordered oldest-first. These are the commits created locally that have not been pushed.
32271
+ */
32272
+ async getLocalOnlyCommits() {
32273
+ await this.git.cwd(this.clonePath);
32274
+ const result = await this.git.raw(["rev-list", "--reverse", "HEAD", "--not", "--remotes=origin"]);
32275
+ return result.trim().split("\n").filter((line) => line.length > 0);
32276
+ }
32257
32277
  /**
32258
32278
  * Returns true if the working tree has uncommitted changes (staged or unstaged).
32259
32279
  */
@@ -42461,6 +42481,7 @@ ${prBody}`;
42461
42481
 
42462
42482
  // src/pipeline/github/pushSignedCommit.ts
42463
42483
  var MAX_CONCURRENT_PUSH_RETRIES = 3;
42484
+ var MAX_SIGNED_CHAIN_LENGTH = 20;
42464
42485
  async function pushSignedCommit({
42465
42486
  repository,
42466
42487
  octokit,
@@ -42475,25 +42496,19 @@ async function pushSignedCommit({
42475
42496
  const tempRef = `refs/temp/fern-${Date.now()}`;
42476
42497
  let tempRefPushed = false;
42477
42498
  try {
42478
- let [localHeadSha, treeSha, message, parents] = await Promise.all([
42479
- repository.getHeadSha(),
42480
- repository.getHeadTreeHash(),
42481
- repository.getHeadCommitMessage(),
42482
- repository.getHeadParents()
42483
- ]);
42499
+ let localHeadSha = await repository.getHeadSha();
42484
42500
  await repository.pushObjectToRef(localHeadSha, tempRef);
42485
42501
  tempRefPushed = true;
42486
42502
  for (let attempt2 = 0; attempt2 < MAX_CONCURRENT_PUSH_RETRIES; attempt2++) {
42487
- const identityFields = author != null ? { author, committer: author } : {};
42488
- const { data: signedCommit } = await octokit.git.createCommit({
42503
+ const signedSha = await signLocalCommitChain({
42504
+ repository,
42505
+ octokit,
42489
42506
  owner,
42490
42507
  repo,
42491
- message,
42492
- tree: treeSha,
42493
- parents,
42494
- ...identityFields
42508
+ localHeadSha,
42509
+ author,
42510
+ logger
42495
42511
  });
42496
- const signedSha = signedCommit.sha;
42497
42512
  try {
42498
42513
  await upsertBranchRef({ octokit, owner, repo, branch, sha: signedSha, force });
42499
42514
  logger.debug(`Updated refs/heads/${branch} to signed commit ${signedSha}`);
@@ -42508,12 +42523,7 @@ async function pushSignedCommit({
42508
42523
  `Non-fast-forward on refs/heads/${branch} (attempt ${attempt2 + 1}/${MAX_CONCURRENT_PUSH_RETRIES}); rebasing locally and retrying.`
42509
42524
  );
42510
42525
  await repository.pullWithRebase(branch);
42511
- [localHeadSha, treeSha, message, parents] = await Promise.all([
42512
- repository.getHeadSha(),
42513
- repository.getHeadTreeHash(),
42514
- repository.getHeadCommitMessage(),
42515
- repository.getHeadParents()
42516
- ]);
42526
+ localHeadSha = await repository.getHeadSha();
42517
42527
  await repository.pushObjectToRef(localHeadSha, tempRef, { force: true });
42518
42528
  } else {
42519
42529
  throw err;
@@ -42533,6 +42543,49 @@ async function pushSignedCommit({
42533
42543
  }
42534
42544
  }
42535
42545
  }
42546
+ async function signLocalCommitChain({
42547
+ repository,
42548
+ octokit,
42549
+ owner,
42550
+ repo,
42551
+ localHeadSha,
42552
+ author,
42553
+ logger
42554
+ }) {
42555
+ let chain = await repository.getLocalOnlyCommits();
42556
+ if (chain.length === 0 || chain[chain.length - 1] !== localHeadSha) {
42557
+ chain = [localHeadSha];
42558
+ } else if (chain.length > MAX_SIGNED_CHAIN_LENGTH) {
42559
+ logger.warn(
42560
+ `Local commit chain has ${chain.length} commits (limit ${MAX_SIGNED_CHAIN_LENGTH}); signing HEAD only.`
42561
+ );
42562
+ chain = [localHeadSha];
42563
+ }
42564
+ const identityFields = author != null ? { author, committer: author } : {};
42565
+ const signedShaByLocalSha = /* @__PURE__ */ new Map();
42566
+ let signedSha = localHeadSha;
42567
+ for (const sha of chain) {
42568
+ const [treeSha, message, parents] = await Promise.all([
42569
+ repository.getCommitTreeHash(sha),
42570
+ repository.getCommitMessage(sha),
42571
+ repository.getCommitParents(sha)
42572
+ ]);
42573
+ const { data: signedCommit } = await octokit.git.createCommit({
42574
+ owner,
42575
+ repo,
42576
+ message,
42577
+ tree: treeSha,
42578
+ parents: parents.map((parent) => signedShaByLocalSha.get(parent) ?? parent),
42579
+ ...identityFields
42580
+ });
42581
+ signedShaByLocalSha.set(sha, signedCommit.sha);
42582
+ signedSha = signedCommit.sha;
42583
+ }
42584
+ if (chain.length > 1) {
42585
+ logger.debug(`Signed ${chain.length} local commits via the GitHub API`);
42586
+ }
42587
+ return signedSha;
42588
+ }
42536
42589
  async function upsertBranchRef({
42537
42590
  octokit,
42538
42591
  owner,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fern-api/generator-cli",
3
- "version": "0.9.43",
3
+ "version": "0.9.44",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fern-api/fern.git",