Skip to content

Commit

Permalink
feat(core): create a GitHub Pull Request from a fork (#45)
Browse files Browse the repository at this point in the history
* made logger global (for read-only) and main interface for pr
  • Loading branch information
TomKristie committed Jul 28, 2020
1 parent d078cce commit 782bced
Show file tree
Hide file tree
Showing 19 changed files with 868 additions and 92 deletions.
7 changes: 6 additions & 1 deletion package.json
Expand Up @@ -9,6 +9,9 @@
},
"repository": "googleapis/code-suggester",
"main": "build/src/index.js",
"module": "build/src/index.js",
"source": "build/src/index.js",
"types": "build/src/index.d.ts",
"files": [
"build/src"
],
Expand All @@ -33,14 +36,15 @@
"prelint": "cd samples; npm link ../; npm i"
},
"dependencies": {
"@octokit/rest": "^18.0.0",
"@octokit/rest": "^18.0.1",
"pino": "^6.3.2"
},
"devDependencies": {
"@types/chai": "^4.2.11",
"@types/mocha": "^8.0.0",
"@types/node": "^14.0.20",
"@types/pino": "^6.3.0",
"@types/proxyquire": "^1.3.28",
"@types/sinon": "^9.0.4",
"c8": "^7.0.1",
"chai": "^4.2.0",
Expand All @@ -59,6 +63,7 @@
"null-loader": "^4.0.0",
"pack-n-play": "^1.0.0-2",
"prettier": "^2.0.0",
"proxyquire": "^2.1.3",
"sinon": "^9.0.2",
"ts-loader": "^8.0.0",
"typescript": "^3.9.5",
Expand Down
49 changes: 49 additions & 0 deletions src/default-options-handler.ts
@@ -0,0 +1,49 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {CreatePullRequest, CreatePullRequestUserOptions} from './types';

const DEFAULT_BRANCH_NAME = 'code-suggestions';
const DEFAULT_PRIMARY_BRANCH = 'master';

/**
* Add defaults to GitHub Pull Request options.
* Preserves the empty string.
* For ESCMAScript, null/undefined values are preserved for required fields.
* Recommended with an object validation function to check empty strings and incorrect types.
* @param {PullRequestUserOptions} options the user-provided github pull request options
* @returns {CreatePullRequest} git hub context with defaults applied
*/
function addPullRequestDefaults(
options: CreatePullRequestUserOptions
): CreatePullRequest {
const pullRequestSettings: CreatePullRequest = {
upstreamOwner: options.upstreamOwner,
upstreamRepo: options.upstreamRepo,
description: options.description,
title: options.title,
message: options.message,
force: options.force || false,
branch:
typeof options.branch === 'string' ? options.branch : DEFAULT_BRANCH_NAME,
primary:
typeof options.primary === 'string'
? options.primary
: DEFAULT_PRIMARY_BRANCH,
maintainersCanModify: options.maintainersCanModify === false ? false : true,
};
return pullRequestSettings;
}

export {addPullRequestDefaults};
9 changes: 3 additions & 6 deletions src/github-handler/branch-handler.ts
Expand Up @@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {Logger, Octokit, RepoDomain} from '../types';
import {logger} from '../logger';
import {Octokit, RepoDomain} from '../types';

const REF_PREFIX = 'refs/heads/';
const DEFAULT_PRIMARY_BRANCH = 'master';
Expand All @@ -28,14 +29,12 @@ function createRef(branchName: string) {
/**
* get branch commit HEAD SHA of a repository
* Throws an error if the branch cannot be found
* @param {Logger} logger The logger instance
* @param {Octokit} octokit The authenticated octokit instance
* @param {RepoDomain} origin The domain information of the remote origin repository
* @param {string} branch the name of the branch
* @returns {Promise<string>} branch commit HEAD SHA
*/
async function getBranchHead(
logger: Logger,
octokit: Octokit,
origin: RepoDomain,
branch: string
Expand All @@ -56,23 +55,21 @@ async function getBranchHead(
/**
* Create a GitHub branch given a remote origin.
* Throws an exception if octokit fails, or if the base branch is invalid
* @param {Logger} logger The logger instance
* @param {Octokit} octokit The authenticated octokit instance
* @param {RepoDomain} origin The domain information of the remote origin repository
* @param {string} name The branch name to create on the origin repository
* @param {string} baseBranch the name of the branch to base the new branch off of. Default is master
* @returns {Promise<string>} the base SHA for subsequent commits to be based off for the origin branch
*/
async function branch(
logger: Logger,
octokit: Octokit,
origin: RepoDomain,
name: string,
baseBranch: string = DEFAULT_PRIMARY_BRANCH
): Promise<string> {
// create branch from primary branch HEAD SHA
try {
const baseSHA = await getBranchHead(logger, octokit, origin, baseBranch);
const baseSHA = await getBranchHead(octokit, origin, baseBranch);
const refData = (
await octokit.git.createRef({
owner: origin.owner,
Expand Down
30 changes: 10 additions & 20 deletions src/github-handler/commit-and-push-handler.ts
Expand Up @@ -16,10 +16,11 @@ import {
Changes,
FileData,
TreeObject,
Logger,
Octokit,
RepoDomain,
BranchDomain,
} from '../types';
import {logger} from '../logger';

/**
* Generate and return a GitHub tree object structure
Expand Down Expand Up @@ -63,7 +64,6 @@ function generateTreeObjects(changes: Changes): TreeObject[] {
* @returns {Promise<string>} the GitHub tree SHA
*/
async function createTree(
logger: Logger,
octokit: Octokit,
origin: RepoDomain,
refHead: string,
Expand Down Expand Up @@ -95,7 +95,6 @@ async function createTree(
* Create a commit with a repo snapshot SHA on top of the reference HEAD
* and resolves with the SHA of the commit.
* Rejects if GitHub V3 API fails with the GitHub error response
* @param {Logger} logger The logger instance
* @param {Octokit} octokit The authenticated octokit instance
* @param {RepoDomain} origin the the remote repository to push changes to
* @param {string} refHead the base of the new commit(s)
Expand All @@ -104,7 +103,6 @@ async function createTree(
* @returns {Promise<string>} the new commit SHA
*/
async function createCommit(
logger: Logger,
octokit: Octokit,
origin: RepoDomain,
refHead: string,
Expand All @@ -127,33 +125,28 @@ async function createCommit(
/**
* Update a reference to a SHA
* Rejects if GitHub V3 API fails with the GitHub error response
* @param {Logger} logger The logger instance
* @param {Octokit} octokit The authenticated octokit instance
* @param {RepoDomain} origin the the remote repository to push changes to
* @param {string} refName the name of the branch ref
* @param {BranchDomain} origin the the remote branch to push changes to
* @param {string} newSha the ref to update the commit HEAD to
* @returns {Promise<void>}
*/
async function updateRef(
logger: Logger,
octokit: Octokit,
origin: RepoDomain,
refName: string,
origin: BranchDomain,
newSha: string
): Promise<void> {
await octokit.git.updateRef({
owner: origin.owner,
repo: origin.repo,
ref: `heads/${refName}`,
ref: `heads/${origin.branch}`,
sha: newSha,
});
logger.info(`Successfully updated reference ${refName} to ${newSha}`);
logger.info(`Successfully updated reference ${origin.branch} to ${newSha}`);
}

/**
* Given a set of changes, apply the commit(s) on top of the given branch's head and upload it to GitHub
* Rejects if GitHub V3 API fails with the GitHub error response
* @param {Logger} logger The logger instance
* @param {Octokit} octokit The authenticated octokit instance
* @param {string} refHead the base of the new commit(s)
* @param {Changes} changes the set of repository changes
Expand All @@ -163,26 +156,23 @@ async function updateRef(
* @returns {Promise<void>}
*/
async function commitAndPush(
logger: Logger,
octokit: Octokit,
refHead: string,
changes: Changes,
origin: RepoDomain,
originBranchName: string,
originBranch: BranchDomain,
commitMessage: string
) {
try {
const tree = generateTreeObjects(changes);
const treeSha = await createTree(logger, octokit, origin, refHead, tree);
const treeSha = await createTree(octokit, originBranch, refHead, tree);
const commitSha = await createCommit(
logger,
octokit,
origin,
originBranch,
refHead,
treeSha,
commitMessage
);
await updateRef(logger, octokit, origin, originBranchName, commitSha);
await updateRef(octokit, originBranch, commitSha);
} catch (err) {
logger.error('Error while creating a tree and updating the ref');
throw err;
Expand Down
5 changes: 2 additions & 3 deletions src/github-handler/fork-handler.ts
Expand Up @@ -12,21 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {Logger, Octokit, RepoDomain} from '../types';
import {Octokit, RepoDomain} from '../types';
import {logger} from '../logger';

/**
* Fork the GitHub owner's repository.
* Returns the fork owner and fork repo if successful. Otherwise throws error.
*
* If fork already exists no new fork is created, no error occurs, and the existing Fork data is returned
* with the `updated_at` + any historical repo changes.
* @param {Logger} logger The logger instance
* @param {Octokit} octokit The authenticated octokit instance
* @param {RepoDomain} upstream upstream repository information
* @returns {Promise<RepoDomain>} the forked repository name, as well as the owner of that fork
*/
async function fork(
logger: Logger,
octokit: Octokit,
upstream: RepoDomain
): Promise<RepoDomain> {
Expand Down
2 changes: 1 addition & 1 deletion src/github-handler/index.ts
@@ -1,4 +1,4 @@
export * from './fork-handler';
export {branch} from './branch-handler';
export {commitAndPush} from './commit-and-push-handler';
export * from './pr-handler';
export * from './pull-request-handler';
Expand Up @@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {BranchDomain, Description, Logger, Octokit, RepoDomain} from '../types';
import {BranchDomain, Description, Octokit, RepoDomain} from '../types';
import {logger} from '../logger';

const DEFAULT_PRIMARY = 'master';

/**
* Create a GitHub PR on the upstream organization's repo
* Throws an error if the GitHub API fails
* @param {Logger} logger The logger instance
* @param {Octokit} octokit The authenticated octokit instance
* @param {RepoDomain} upstream The upstream repository
* @param {BranchDomain} origin The remote origin information that contains the origin branch
Expand All @@ -28,8 +28,7 @@ const DEFAULT_PRIMARY = 'master';
* @param {string} upstreamPrimary The upstream repository's primary branch. Default is master.
* @returns {Promise<void>}
*/
async function openPR(
logger: Logger,
async function openPullRequest(
octokit: Octokit,
upstream: RepoDomain,
origin: BranchDomain,
Expand All @@ -54,4 +53,4 @@ async function openPR(
);
}

export {openPR};
export {openPullRequest};

0 comments on commit 782bced

Please sign in to comment.