Skip to content
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
5 changes: 3 additions & 2 deletions __snapshots__/github.js

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class GitHubAPIError extends Error {
this.body = GitHubAPIError.parseErrorBody(requestError);
this.name = GitHubAPIError.name;
this.cause = requestError;
this.stack = requestError.stack;
}

static parseErrorBody(
Expand Down Expand Up @@ -81,3 +82,12 @@ export class DuplicateReleaseError extends GitHubAPIError {
this.name = DuplicateReleaseError.name;
}
}

export class FileNotFoundError extends Error {
path: string;
constructor(path: string) {
super(`Failed to find file: ${path}`);
this.path = path;
this.name = FileNotFoundError.name;
}
}
131 changes: 15 additions & 116 deletions src/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ import {Update} from './update';
import {Release} from './release';
import {ROOT_PROJECT_PATH} from './manifest';
import {signoffCommitMessage} from './util/signoff-commit-message';
import {
RepositoryFileCache,
GitHubFileContents,
DEFAULT_FILE_MODE,
} from './util/file-cache';

// Extract some types from the `request` package.
type RequestBuilderType = typeof request;
type DefaultFunctionType = RequestBuilderType['defaults'];
type RequestFunctionType = ReturnType<DefaultFunctionType>;
type RequestOptionsType = Parameters<DefaultFunctionType>[0];
export interface OctokitAPIs {
graphql: Function;
request: RequestFunctionType;
Expand All @@ -61,12 +65,6 @@ interface GitHubCreateOptions {
token?: string;
}

export interface GitHubFileContents {
sha: string;
content: string;
parsedContent: string;
}

type CommitFilter = (commit: Commit) => boolean;

interface GraphQLCommit {
Expand Down Expand Up @@ -176,12 +174,14 @@ export class GitHub {
private octokit: OctokitType;
private request: RequestFunctionType;
private graphql: Function;
private fileCache: RepositoryFileCache;

private constructor(options: GitHubOptions) {
this.repository = options.repository;
this.octokit = options.octokitAPIs.octokit;
this.request = options.octokitAPIs.request;
this.graphql = options.octokitAPIs.graphql;
this.fileCache = new RepositoryFileCache(this.octokit, this.repository);
}

/**
Expand Down Expand Up @@ -754,80 +754,6 @@ export class GitHub {
);
}

/**
* Fetch the contents of a file with the Contents API
*
* @param {string} path The path to the file in the repository
* @param {string} branch The branch to fetch from
* @returns {GitHubFileContents}
* @throws {GitHubAPIError} on other API errors
*/
getFileContentsWithSimpleAPI = wrapAsync(
async (
path: string,
ref: string,
isBranch = true
): Promise<GitHubFileContents> => {
ref = isBranch ? fullyQualifyBranchRef(ref) : ref;
const options: RequestOptionsType = {
owner: this.repository.owner,
repo: this.repository.repo,
path,
ref,
};
const resp = await this.request(
'GET /repos/:owner/:repo/contents/:path',
options
);
return {
parsedContent: Buffer.from(resp.data.content, 'base64').toString(
'utf8'
),
content: resp.data.content,
sha: resp.data.sha,
};
}
);

/**
* Fetch the contents of a file using the Git data API
*
* @param {string} path The path to the file in the repository
* @param {string} branch The branch to fetch from
* @returns {GitHubFileContents}
* @throws {GitHubAPIError} on other API errors
*/
getFileContentsWithDataAPI = wrapAsync(
async (path: string, branch: string): Promise<GitHubFileContents> => {
const repoTree = await this.octokit.git.getTree({
owner: this.repository.owner,
repo: this.repository.repo,
tree_sha: branch,
});

const blobDescriptor = repoTree.data.tree.find(
tree => tree.path === path
);
if (!blobDescriptor) {
throw new Error(`Could not find requested path: ${path}`);
}

const resp = await this.octokit.git.getBlob({
owner: this.repository.owner,
repo: this.repository.repo,
file_sha: blobDescriptor.sha!,
});

return {
parsedContent: Buffer.from(resp.data.content, 'base64').toString(
'utf8'
),
content: resp.data.content,
sha: resp.data.sha,
};
}
);

/**
* Fetch the contents of a file
*
Expand All @@ -841,14 +767,7 @@ export class GitHub {
branch: string
): Promise<GitHubFileContents> {
logger.debug(`Fetching ${path} from branch ${branch}`);
try {
return await this.getFileContentsWithSimpleAPI(path, branch);
} catch (err) {
if (err.status === 403) {
return await this.getFileContentsWithDataAPI(path, branch);
}
throw err;
}
return await this.fileCache.getFileContents(path, branch);
}

async getFileJson<T>(path: string, branch: string): Promise<T> {
Expand Down Expand Up @@ -1108,19 +1027,12 @@ export class GitHub {
): Promise<Changes> {
const changes = new Map();
for (const update of updates) {
let content;
let content: GitHubFileContents | undefined;
try {
if (update.cachedFileContents) {
// we already loaded the file contents earlier, let's not
// hit GitHub again.
content = {data: update.cachedFileContents};
} else {
const fileContent = await this.getFileContentsOnBranch(
update.path,
defaultBranch
);
content = {data: fileContent};
}
content = await this.getFileContentsOnBranch(
update.path,
defaultBranch
);
} catch (err) {
if (err.status !== 404) throw err;
// if the file is missing and create = false, just continue
Expand All @@ -1131,13 +1043,13 @@ export class GitHub {
}
}
const contentText = content
? Buffer.from(content.data.content, 'base64').toString('utf8')
? Buffer.from(content.content, 'base64').toString('utf8')
: undefined;
const updatedContent = update.updater.updateContent(contentText);
if (updatedContent) {
changes.set(update.path, {
content: updatedContent,
mode: '100644',
mode: content?.mode || DEFAULT_FILE_MODE,
});
}
}
Expand Down Expand Up @@ -1361,19 +1273,6 @@ export class GitHub {
}
}

// Takes a potentially unqualified branch name, and turns it
// into a fully qualified ref.
//
// e.g. main -> refs/heads/main
function fullyQualifyBranchRef(refName: string): string {
let final = refName;
if (final.indexOf('/') < 0) {
final = `refs/heads/${final}`;
}

return final;
}

/**
* Normalize a provided prefix by removing leading and trailing
* slashes.
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/dart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import * as yaml from 'js-yaml';
// pubspec
import {PubspecYaml} from '../updaters/dart/pubspec-yaml';
import {BaseStrategy, BuildUpdatesOptions} from './base';
import {GitHubFileContents} from '../github';
import {GitHubFileContents} from '../util/file-cache';
import {Update} from '../update';

export class Dart extends BaseStrategy {
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/helm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {GitHubFileContents} from '../github';
import {GitHubFileContents} from '../util/file-cache';

// Generic
import {Changelog} from '../updaters/changelog';
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/java-yoshi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {Version, VersionsMap} from '../version';
import {JavaUpdate} from '../updaters/java/java-update';
import {BaseStrategy, BuildUpdatesOptions, BaseStrategyOptions} from './base';
import {Changelog} from '../updaters/changelog';
import {GitHubFileContents} from '../github';
import {GitHubFileContents} from '../util/file-cache';
import {JavaSnapshot} from '../versioning-strategies/java-snapshot';
import {GitHubAPIError, MissingRequiredFileError} from '../errors';
import {Commit, ConventionalCommit} from '../commit';
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/krm-blueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {GitHubFileContents} from '../github';
import {GitHubFileContents} from '../util/file-cache';

// Generic
import {Changelog} from '../updaters/changelog';
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {PackageLockJson} from '../updaters/node/package-lock-json';
import {SamplesPackageJson} from '../updaters/node/samples-package-json';
import {Changelog} from '../updaters/changelog';
import {PackageJson} from '../updaters/node/package-json';
import {GitHubFileContents} from '../github';
import {GitHubFileContents} from '../util/file-cache';

export class Node extends BaseStrategy {
private pkgJsonContents?: GitHubFileContents;
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/ocaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {GitHubFileContents} from '../github';
import {GitHubFileContents} from '../util/file-cache';

// Generic
import {Changelog} from '../updaters/changelog';
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/php-yoshi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {TagName} from '../util/tag-name';
import {PullRequestTitle} from '../util/pull-request-title';
import {BranchName} from '../util/branch-name';
import {PullRequestBody} from '../util/pull-request-body';
import {GitHubFileContents} from '../github';
import {GitHubFileContents} from '../util/file-cache';

const CHANGELOG_SECTIONS = [
{type: 'feat', section: 'Features'},
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/rust.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {GitHubFileContents} from '../github';
import {GitHubFileContents} from '../util/file-cache';

// Generic
import {Changelog} from '../updaters/changelog';
Expand Down
2 changes: 1 addition & 1 deletion src/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {GitHubFileContents} from './github';
import {GitHubFileContents} from './util/file-cache';

/**
* An update is a collection of data that represents changes to
Expand Down
Loading