Skip to content
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

Split fs / node-specific functionality into modules, and add more tests for different file sizes. #7

Merged
merged 5 commits into from
Jul 27, 2024
Merged
Changes from 1 commit
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
Next Next commit
Split core and fs functionality into separate files
s0 committed Jul 27, 2024
commit 8728081b2380ebe11c8275b5de12d500a5b82a0e
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -31,8 +31,6 @@
"format:check": "prettier --check \"**/*.{ts,tsx,md}\"",
"format:fix": "prettier --write \"**/*.{ts,tsx,md}\"",
"lint": "eslint . --max-warnings 0",
"test": "jest",
"test:watch": "jest --watch",
"test:integration": "jest --config jest.integration.config.cjs"
},
"devDependencies": {
100 changes: 100 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import type {
CommitMessage,
FileChanges,
} from "./github/graphql/generated/types";
import {
createCommitOnBranchQuery,
createRefMutation,
getRepositoryMetadata,
GitHubClient,
} from "./github/graphql/queries";
import type { CreateCommitOnBranchMutationVariables } from "./github/graphql/generated/operations";
import type { Logger } from "./logging";

export type CommitFilesResult = {
refId: string | null;
};

export type CommitFilesFromBase64Args = {
octokit: GitHubClient;
owner: string;
repository: string;
branch: string;
/**
* The current commit that the target branch is at
*/
baseBranch: string;
/**
* The commit message
*/
message: CommitMessage;
fileChanges: FileChanges;
log?: Logger;
};

export const commitFilesFromBase64 = async ({
octokit,
owner,
repository,
branch,
baseBranch,
message,
fileChanges,
log,
}: CommitFilesFromBase64Args): Promise<CommitFilesResult> => {
const repositoryNameWithOwner = `${owner}/${repository}`;
const baseRef = `refs/heads/${baseBranch}`;

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

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

const oid = info.ref?.target?.oid;

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

log?.debug(`Creating branch ${branch} from commit ${oid}}`);
const refId = await createRefMutation(octokit, {
input: {
repositoryId: info.id,
name: `refs/heads/${branch}`,
oid,
},
});

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

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

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

await log?.debug(`Creating commit on branch ${branch}`);
const createCommitMutation: CreateCommitOnBranchMutationVariables = {
input: {
branch: {
id: refIdStr,
},
expectedHeadOid: oid,
message,
fileChanges,
},
};
log?.debug(JSON.stringify(createCommitMutation, null, 2));

const result = await createCommitOnBranchQuery(octokit, createCommitMutation);
return {
refId: result.createCommitOnBranch?.ref?.id ?? null,
};
};
127 changes: 22 additions & 105 deletions src/fs.ts
Original file line number Diff line number Diff line change
@@ -1,126 +1,43 @@
import { promises as fs } from "fs";
import * as path from "path";
import type {
CommitMessage,
FileAddition,
FileDeletion,
} from "./github/graphql/generated/types";
import {
createCommitOnBranchQuery,
createRefMutation,
getRepositoryMetadata,
GitHubClient,
} from "./github/graphql/queries";
import type { CreateCommitOnBranchMutationVariables } from "./github/graphql/generated/operations";
import type { Logger } from "./logging";

export const commitFilesFromDirectory = async (args: {
octokit: GitHubClient;
import type { FileAddition } from "./github/graphql/generated/types";
import { CommitFilesFromBase64Args, CommitFilesResult } from "./core";
import { commitFilesFromBuffers } from "./node";

export type CommitFilesFromDirectoryArgs = Omit<
CommitFilesFromBase64Args,
"fileChanges"
> & {
/**
* The root of the github repository.
* The directory to consider the root of the repository when calculating
* file paths
*/
workingDirectory?: string;
owner: string;
repository: string;
branch: string;
/**
* The current commit that the target branch is at
*/
baseBranch: string;
/**
* The commit message
*/
message: CommitMessage;
fileChanges: {
/**
* File paths (relative to the repository root)
*/
additions?: string[];
deletions?: string[];
};
log?: Logger;
}) => {
const {
octokit,
workingDirectory = process.cwd(),
owner,
repository,
branch,
baseBranch,
message,
fileChanges,
log,
} = args;
const repositoryNameWithOwner = `${owner}/${repository}`;
const baseRef = `refs/heads/${baseBranch}`;
};

export const commitFilesFromDirectory = async ({
workingDirectory = process.cwd(),
fileChanges,
...otherArgs
}: CommitFilesFromDirectoryArgs): Promise<CommitFilesResult> => {
const additions: FileAddition[] = await Promise.all(
(fileChanges.additions || []).map(async (p) => {
const fileContents = await fs.readFile(path.join(workingDirectory, p));
const base64Contents = Buffer.from(fileContents).toString("base64");
return {
path: p,
contents: base64Contents,
contents: await fs.readFile(path.join(workingDirectory, p)),
};
}),
);

const deletions: FileDeletion[] =
fileChanges.deletions?.map((p) => ({
path: p,
})) ?? [];

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

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

const oid = info.ref?.target?.oid;

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

log?.debug(`Creating branch ${branch} from commit ${oid}}`);
const refId = await createRefMutation(octokit, {
input: {
repositoryId: info.id,
name: `refs/heads/${branch}`,
oid,
return commitFilesFromBuffers({
...otherArgs,
fileChanges: {
additions,
deletions: fileChanges.deletions,
},
});

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

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

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

await log?.debug(`Creating commit on branch ${args.branch}`);
const createCommitMutation: CreateCommitOnBranchMutationVariables = {
input: {
branch: {
id: refIdStr,
},
expectedHeadOid: oid,
message,
fileChanges: {
additions,
deletions,
},
},
};
log?.debug(JSON.stringify(createCommitMutation, null, 2));

const result = await createCommitOnBranchQuery(octokit, createCommitMutation);
return result.createCommitOnBranch?.ref?.id ?? null;
};
34 changes: 34 additions & 0 deletions src/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
commitFilesFromBase64,
CommitFilesFromBase64Args,
CommitFilesResult,
} from "./core";

export type CommitFilesFromBuffersArgs = Omit<
CommitFilesFromBase64Args,
"fileChanges"
> & {
fileChanges: {
additions?: Array<{
path: string;
contents: Buffer;
}>;
deletions?: string[];
};
};

export const commitFilesFromBuffers = async ({
fileChanges,
...otherArgs
}: CommitFilesFromBuffersArgs): Promise<CommitFilesResult> => {
return commitFilesFromBase64({
...otherArgs,
fileChanges: {
additions: fileChanges.additions?.map(({ path, contents }) => ({
path,
contents: contents.toString("base64"),
})),
deletions: fileChanges.deletions?.map((path) => ({ path })),
},
});
};
2 changes: 1 addition & 1 deletion tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts", "src/fs.ts"],
entry: ["src/index.ts", "src/core.ts", "src/fs.ts", "src/node.ts"],
format: ["cjs", "esm"],
dts: true,
});