Skip to content

Commit d9e9120

Browse files
committed
Use a temp branch strategy
1 parent 8aab6ff commit d9e9120

2 files changed

Lines changed: 110 additions & 28 deletions

File tree

src/core.ts

Lines changed: 105 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
import {
22
createCommitOnBranchQuery,
3-
createRefMutation,
43
getRepositoryMetadata,
5-
updateRefMutation,
64
} from "./github/graphql/queries.js";
7-
import type {
8-
CreateCommitOnBranchMutationVariables,
9-
GetRepositoryMetadataQuery,
10-
} from "./github/graphql/generated/operations.js";
5+
import type { GetRepositoryMetadataQuery } from "./github/graphql/generated/operations.js";
116
import {
127
CommitFilesFromBase64Args,
138
CommitFilesResult,
@@ -47,6 +42,18 @@ const getOidFromRef = (
4742
return ref.target.oid;
4843
};
4944

45+
const isAlreadyExistingRefError = (
46+
error: unknown,
47+
) =>
48+
typeof error === "object" &&
49+
error !== null &&
50+
"status" in error &&
51+
"message" in error &&
52+
typeof error.status === "number" &&
53+
typeof error.message === "string" &&
54+
error.status === 422 &&
55+
error.message.includes("Reference already exists");
56+
5057
const createCommit = async ({
5158
octokit,
5259
refId,
@@ -121,7 +128,6 @@ export const commitFilesFromBase64 = async ({
121128
const baseOid = getOidFromRef(base, info.baseRef);
122129
const targetOid = info.targetBranch?.target?.oid ?? null;
123130
const sameBranchBase = "branch" in base && base.branch === branch;
124-
const repositoryId = info.id;
125131

126132
let mode: "create" | "update" | "force-update";
127133

@@ -140,39 +146,110 @@ export const commitFilesFromBase64 = async ({
140146
);
141147
}
142148

143-
let refId: string;
149+
if (mode === "force-update") {
150+
// Use a stable temp branch name so a later run can recover and reuse it
151+
// if an earlier run failed before cleanup completed.
152+
const tempBranch = `changesets-ghcommit-temp/${branch}`;
144153

145-
if (mode === "create") {
146-
const createdRef = await createRefMutation(octokit, {
147-
input: {
148-
repositoryId,
149-
name: `refs/heads/${branch}`,
150-
oid: baseOid,
151-
},
154+
let tempRefId: string;
155+
156+
try {
157+
const createdTempRef = await octokit.rest.git.createRef({
158+
owner,
159+
repo,
160+
ref: `refs/heads/${tempBranch}`,
161+
sha: baseOid,
162+
});
163+
164+
const refIdStr = createdTempRef.data.node_id;
165+
166+
if (!refIdStr) {
167+
throw new Error(`Failed to create temporary branch ${tempBranch}`);
168+
}
169+
170+
tempRefId = refIdStr;
171+
} catch (error) {
172+
if (!isAlreadyExistingRefError(error)) {
173+
throw error;
174+
}
175+
176+
const updatedTempRef = await octokit.rest.git.updateRef({
177+
owner,
178+
repo,
179+
ref: `heads/${tempBranch}`,
180+
sha: baseOid,
181+
force: true,
182+
});
183+
184+
const refIdStr = updatedTempRef.data.node_id;
185+
186+
if (!refIdStr) {
187+
throw new Error(`Failed to update temporary branch ${tempBranch}`);
188+
}
189+
190+
tempRefId = refIdStr;
191+
}
192+
193+
await log?.debug(`Creating commit on branch ${tempBranch}`);
194+
const tempCommit = await createCommit({
195+
octokit,
196+
refId: tempRefId,
197+
baseOid,
198+
message,
199+
fileChanges,
152200
});
153201

154-
const refIdStr = createdRef.createRef?.ref?.id;
202+
const tempRefTarget = tempCommit.createCommitOnBranch?.ref?.target;
203+
const tempHeadOid =
204+
tempRefTarget?.__typename === "Commit" ? tempRefTarget.oid : null;
155205

156-
if (!refIdStr) {
157-
throw new Error(`Failed to create branch ${branch}`);
206+
if (!tempHeadOid) {
207+
throw new Error(
208+
`Failed to determine head commit of temporary branch ${tempBranch}`,
209+
);
158210
}
159211

160-
refId = refIdStr;
161-
} else if (mode === "force-update") {
162-
const updatedRef = await updateRefMutation(octokit, {
163-
input: {
164-
refId: sameBranchBase ? resolvedBaseRef!.id : info.targetBranch!.id,
165-
oid: baseOid,
166-
force: true,
167-
},
212+
const updatedTargetRef = await octokit.rest.git.updateRef({
213+
owner,
214+
repo,
215+
ref: `heads/${branch}`,
216+
sha: tempHeadOid,
217+
force: true,
168218
});
169219

170-
const refIdStr = updatedRef.updateRef?.ref?.id;
220+
const updatedTargetRefId = updatedTargetRef.data.node_id;
171221

172-
if (!refIdStr) {
222+
if (!updatedTargetRefId) {
173223
throw new Error(`Failed to update branch ${branch}`);
174224
}
175225

226+
await octokit.rest.git.deleteRef({
227+
owner,
228+
repo,
229+
ref: `heads/${tempBranch}`,
230+
});
231+
232+
return {
233+
refId: updatedTargetRefId,
234+
};
235+
}
236+
237+
let refId: string;
238+
239+
if (mode === "create") {
240+
const createdRef = await octokit.rest.git.createRef({
241+
owner,
242+
repo,
243+
ref: `refs/heads/${branch}`,
244+
sha: baseOid,
245+
});
246+
247+
const refIdStr = createdRef.data.node_id;
248+
249+
if (!refIdStr) {
250+
throw new Error(`Failed to create branch ${branch}`);
251+
}
252+
176253
refId = refIdStr;
177254
} else {
178255
refId = sameBranchBase ? resolvedBaseRef!.id : info.targetBranch!.id;

src/github/graphql/queries.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ const CREATE_COMMIT_ON_BRANCH = /* GraphQL */ `
8080
createCommitOnBranch(input: $input) {
8181
ref {
8282
id
83+
target {
84+
... on Commit {
85+
oid
86+
}
87+
}
8388
}
8489
}
8590
}

0 commit comments

Comments
 (0)