Skip to content

Allow for force pushing for existing branches #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 23, 2024
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
102 changes: 75 additions & 27 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
createRefMutation,
getRepositoryMetadata,
GitHubClient,
updateRefMutation,
} from "./github/graphql/queries.js";
import type {
CreateCommitOnBranchMutationVariables,
Expand Down Expand Up @@ -38,6 +39,11 @@ export type CommitFilesFromBase64Args = {
* The current branch, tag or commit that the new branch should be based on.
*/
base: GitBase;
/**
* Push the commit even if the branch exists and does not match what was
* specified as the base.
*/
force?: boolean;
/**
* The commit message
*/
Expand All @@ -58,7 +64,8 @@ const getBaseRef = (base: GitBase): string => {

const getOidFromRef = (
base: GitBase,
ref: (GetRepositoryMetadataQuery["repository"] & Record<never, never>)["ref"],
ref: (GetRepositoryMetadataQuery["repository"] &
Record<never, never>)["baseRef"],
) => {
if ("commit" in base) {
return base.commit;
Expand All @@ -81,26 +88,29 @@ export const commitFilesFromBase64 = async ({
repository,
branch,
base,
force = false,
message,
fileChanges,
log,
}: CommitFilesFromBase64Args): Promise<CommitFilesResult> => {
const repositoryNameWithOwner = `${owner}/${repository}`;
const baseRef = getBaseRef(base);
const targetRef = `refs/heads/${branch}`;

log?.debug(`Getting repo info ${repositoryNameWithOwner}`);
const info = await getRepositoryMetadata(octokit, {
owner,
name: repository,
ref: baseRef,
baseRef,
targetRef,
});
log?.debug(`Repo info: ${JSON.stringify(info, null, 2)}`);

if (!info) {
throw new Error(`Repository ${repositoryNameWithOwner} not found`);
}

if (!info.ref) {
if (!info.baseRef) {
throw new Error(`Ref ${baseRef} not found`);
}

Expand All @@ -109,39 +119,77 @@ export const commitFilesFromBase64 = async ({
* The commit oid to base the new commit on.
*
* Used both to create / update the new branch (if necessary),
* and th ensure no changes have been made as we push the new commit.
* and to ensure no changes have been made as we push the new commit.
*/
const baseOid = getOidFromRef(base, info.ref);
const baseOid = getOidFromRef(base, info.baseRef);

let refId: string;

if ("branch" in base && base.branch === branch) {
log?.debug(`Committing to the same branch as base: ${branch} (${baseOid})`);
// Get existing branch refId
refId = info.ref.id;
refId = info.baseRef.id;
} else {
// Create branch as not committing to same branch
// TODO: detect if branch already exists, and overwrite if so
log?.debug(`Creating branch ${branch} from commit ${baseOid}}`);
const refIdCreation = await createRefMutation(octokit, {
input: {
repositoryId,
name: `refs/heads/${branch}`,
oid: baseOid,
},
});

log?.debug(
`Created branch with refId ${JSON.stringify(refIdCreation, null, 2)}`,
);

const refIdStr = refIdCreation.createRef?.ref?.id;

if (!refIdStr) {
throw new Error(`Failed to create branch ${branch}`);
// Determine if the branch needs to be created or not
if (info.targetBranch?.target?.oid) {
// Branch already exists, check if it matches the base
if (info.targetBranch.target.oid !== baseOid) {
if (force) {
log?.debug(
`Branch ${branch} exists but does not match base ${baseOid}, forcing update to base`,
);
const refIdUpdate = await updateRefMutation(octokit, {
input: {
refId: info.targetBranch.id,
oid: baseOid,
},
});

log?.debug(
`Updated branch with refId ${JSON.stringify(refIdUpdate, null, 2)}`,
);

const refIdStr = refIdUpdate.updateRef?.ref?.id;

if (!refIdStr) {
throw new Error(`Failed to create branch ${branch}`);
}

refId = refIdStr;
} else {
throw new Error(
`Branch ${branch} exists already and does not match base ${baseOid}, force is set to false`,
);
}
} else {
log?.debug(
`Branch ${branch} already exists and matches base ${baseOid}`,
);
refId = info.targetBranch.id;
}
} else {
// Create branch as it does not exist yet
log?.debug(`Creating branch ${branch} from commit ${baseOid}}`);
const refIdCreation = await createRefMutation(octokit, {
input: {
repositoryId,
name: `refs/heads/${branch}`,
oid: baseOid,
},
});

log?.debug(
`Created branch with refId ${JSON.stringify(refIdCreation, null, 2)}`,
);

const refIdStr = refIdCreation.createRef?.ref?.id;

if (!refIdStr) {
throw new Error(`Failed to create branch ${branch}`);
}

refId = refIdStr;
}

refId = refIdStr;
}

await log?.debug(`Creating commit on branch ${branch}`);
Expand Down
33 changes: 31 additions & 2 deletions src/github/graphql/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ import type {
DeleteRefMutationVariables,
GetRepositoryMetadataQuery,
GetRepositoryMetadataQueryVariables,
UpdateRefMutation,
UpdateRefMutationVariables,
} from "./generated/operations.js";

const GET_REPOSITORY_METADATA = /* GraphQL */ `
query getRepositoryMetadata($owner: String!, $name: String!, $ref: String!) {
query getRepositoryMetadata(
$owner: String!
$name: String!
$baseRef: String!
$targetRef: String!
) {
repository(owner: $owner, name: $name) {
id
ref(qualifiedName: $ref) {
baseRef: ref(qualifiedName: $baseRef) {
id
target {
oid
Expand All @@ -28,6 +35,12 @@ const GET_REPOSITORY_METADATA = /* GraphQL */ `
}
}
}
targetBranch: ref(qualifiedName: $targetRef) {
id
target {
oid
}
}
}
}
`;
Expand All @@ -42,6 +55,16 @@ const CREATE_REF = /* GraphQL */ `
}
`;

const UPDATE_REF = /* GraphQL */ `
mutation updateRef($input: UpdateRefInput!) {
updateRef(input: $input) {
ref {
id
}
}
}
`;

const DELETE_REF = /* GraphQL */ `
mutation deleteRef($input: DeleteRefInput!) {
deleteRef(input: $input) {
Expand Down Expand Up @@ -77,6 +100,12 @@ export const createRefMutation = async (
): Promise<CreateRefMutation> =>
await o.graphql<CreateRefMutation>(CREATE_REF, v);

export const updateRefMutation = async (
o: GitHubClient,
v: UpdateRefMutationVariables,
): Promise<UpdateRefMutation> =>
await o.graphql<UpdateRefMutation>(UPDATE_REF, v);

export const deleteRefMutation = async (
o: GitHubClient,
v: DeleteRefMutationVariables,
Expand Down
4 changes: 3 additions & 1 deletion src/test/integration/jest.globalTeardown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ module.exports = async (_: unknown, projectConfig: Config) => {
}
console.log(`Deleting directory: ${directory}`);

await fs.rm(directory, { recursive: true });
await fs.rm(directory, { recursive: true }).catch((err) => {
console.error(`Error deleting directory: ${err}`);
});
};
Loading