mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-05-16 06:49:09 +02:00
fix mulitple bugs
This commit is contained in:
+49
-8
@@ -6,7 +6,7 @@ import ConferenceModel from "./model/conference/conferences.model";
|
||||
import AnonymousError from "./AnonymousError";
|
||||
import { IAnonymizedPullRequestDocument } from "./model/anonymizedPullRequests/anonymizedPullRequests.types";
|
||||
import config from "../config";
|
||||
import got from "got";
|
||||
import got, { HTTPError } from "got";
|
||||
import { octokit } from "./GitHubUtils";
|
||||
import { ContentAnonimizer } from "./anonymize-utils";
|
||||
|
||||
@@ -57,20 +57,61 @@ export default class PullRequest {
|
||||
const [owner, repo] = this._model.source.repositoryFullName.split("/");
|
||||
const pull_number = this._model.source.pullRequestId;
|
||||
|
||||
const [prInfo, comments, diff] = await Promise.all([
|
||||
oct.rest.pulls.get({
|
||||
let prInfo;
|
||||
try {
|
||||
prInfo = await oct.rest.pulls.get({
|
||||
owner,
|
||||
repo,
|
||||
pull_number,
|
||||
}),
|
||||
oct.paginate("GET /repos/{owner}/{repo}/issues/{issue_number}/comments", {
|
||||
});
|
||||
} catch (err) {
|
||||
if ((err as { status?: number }).status === 404) {
|
||||
throw new AnonymousError("pull_request_not_found", {
|
||||
httpStatus: 404,
|
||||
object: this,
|
||||
cause: err as Error,
|
||||
});
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
const commentsPromise = oct
|
||||
.paginate("GET /repos/{owner}/{repo}/issues/{issue_number}/comments", {
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
issue_number: pull_number,
|
||||
per_page: 100,
|
||||
}),
|
||||
got(`https://github.com/${owner}/${repo}/pull/${pull_number}.diff`),
|
||||
]);
|
||||
})
|
||||
.catch((err): Array<{
|
||||
body?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
user?: { login?: string } | null;
|
||||
}> => {
|
||||
if ((err as { status?: number }).status === 404) {
|
||||
console.warn(
|
||||
"[WARN] Failed to fetch PR comments (404), continuing without them",
|
||||
`${owner}/${repo}#${pull_number}`
|
||||
);
|
||||
return [];
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
||||
const diffPromise = got(
|
||||
`https://github.com/${owner}/${repo}/pull/${pull_number}.diff`
|
||||
).catch((err) => {
|
||||
if (err instanceof HTTPError && err.response.statusCode === 404) {
|
||||
console.warn(
|
||||
"[WARN] Failed to fetch PR diff (404), continuing without it",
|
||||
`${owner}/${repo}#${pull_number}`
|
||||
);
|
||||
return { body: "" };
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
||||
const [comments, diff] = await Promise.all([commentsPromise, diffPromise]);
|
||||
|
||||
this._model.pullRequest = {
|
||||
diff: diff.body,
|
||||
|
||||
@@ -3,6 +3,10 @@ import { Readable } from "stream";
|
||||
import AnonymizedFile from "../AnonymizedFile";
|
||||
import { SourceBase } from "./Source";
|
||||
import { IFile } from "../model/files/files.types";
|
||||
import { octokit } from "../GitHubUtils";
|
||||
import { isConnected } from "../../server/database";
|
||||
import RepositoryModel from "../model/repositories/repositories.model";
|
||||
import AnonymizedRepositoryModel from "../model/anonymizedRepositories/anonymizedRepositories.model";
|
||||
|
||||
export interface GitHubBaseData {
|
||||
getToken: () => string | Promise<string>;
|
||||
@@ -25,3 +29,64 @@ export default abstract class GitHubBase implements SourceBase {
|
||||
|
||||
abstract getFiles(progress?: (status: string) => void): Promise<IFile[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* On a 404 from a commit/tree/zip lookup, probe `repos.get` to determine
|
||||
* whether the repository itself is gone, was renamed, or only the commit is
|
||||
* missing. When a rename is detected (via the cached numeric GitHub repo id
|
||||
* on `RepositoryModel.externalId`), the model's `name` is updated in place so
|
||||
* subsequent lookups succeed.
|
||||
*/
|
||||
export async function classifyGitHubMissError(
|
||||
err: unknown,
|
||||
data: GitHubBaseData
|
||||
): Promise<"repo_not_found" | "commit_not_found" | "repo_renamed"> {
|
||||
const status = (err as { status?: number }).status;
|
||||
if (status !== 404) return "repo_not_found";
|
||||
const oct = octokit(await data.getToken());
|
||||
try {
|
||||
await oct.repos.get({
|
||||
owner: data.organization,
|
||||
repo: data.repoName,
|
||||
});
|
||||
return "commit_not_found";
|
||||
} catch {
|
||||
// Repo no longer exists at owner/repo. Try to recover via the cached
|
||||
// numeric GitHub id — if the repo was renamed, GET /repositories/{id}
|
||||
// resolves to its new full_name. See #409.
|
||||
if (!isConnected) return "repo_not_found";
|
||||
const dbModel = await RepositoryModel.findOne({
|
||||
name: data.organization + "/" + data.repoName,
|
||||
});
|
||||
const ghId =
|
||||
typeof dbModel?.externalId === "string" &&
|
||||
dbModel.externalId.startsWith("gh_")
|
||||
? dbModel.externalId.slice(3)
|
||||
: null;
|
||||
if (!dbModel || !ghId) return "repo_not_found";
|
||||
try {
|
||||
const r = await oct.request("GET /repositories/{id}", { id: ghId });
|
||||
const newName = (r?.data as { full_name?: string } | undefined)
|
||||
?.full_name;
|
||||
if (newName && newName !== dbModel.name) {
|
||||
const oldName = dbModel.name;
|
||||
dbModel.name = newName;
|
||||
await dbModel.save();
|
||||
// Propagate the rename to every anonymized repo that referenced
|
||||
// the old source name, so subsequent lookups (admin diagnostic,
|
||||
// streaming, download, update cron) all hit the correct GitHub
|
||||
// location without the user having to recreate the configuration.
|
||||
await AnonymizedRepositoryModel.updateMany(
|
||||
{ "source.repositoryName": oldName },
|
||||
{ $set: { "source.repositoryName": newName } }
|
||||
);
|
||||
data.organization = newName.split("/")[0];
|
||||
data.repoName = newName.split("/")[1];
|
||||
return "repo_renamed";
|
||||
}
|
||||
return "repo_not_found";
|
||||
} catch {
|
||||
return "repo_not_found";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ import { Readable } from "stream";
|
||||
import { OctokitResponse } from "@octokit/types";
|
||||
|
||||
import storage from "../storage";
|
||||
import GitHubBase, { GitHubBaseData } from "./GitHubBase";
|
||||
import GitHubBase, {
|
||||
GitHubBaseData,
|
||||
classifyGitHubMissError,
|
||||
} from "./GitHubBase";
|
||||
import { FILE_TYPE } from "../storage/Storage";
|
||||
import { octokit } from "../GitHubUtils";
|
||||
import AnonymousError from "../AnonymousError";
|
||||
@@ -30,7 +33,8 @@ export default class GitHubDownload extends GitHubBase {
|
||||
try {
|
||||
response = await this.getZipUrl();
|
||||
} catch (error) {
|
||||
throw new AnonymousError("repo_not_found", {
|
||||
const code = await classifyGitHubMissError(error, this.data);
|
||||
throw new AnonymousError(code, {
|
||||
httpStatus: (error as { status?: number }).status || 404,
|
||||
object: this.data,
|
||||
cause: error as Error,
|
||||
|
||||
@@ -55,12 +55,35 @@ export class GitHubRepository {
|
||||
}
|
||||
) {
|
||||
const oct = octokit(opt.accessToken);
|
||||
const commit = await oct.repos.getCommit({
|
||||
owner: this.owner,
|
||||
repo: this.repo,
|
||||
ref: sha,
|
||||
});
|
||||
return commit.data;
|
||||
try {
|
||||
const commit = await oct.repos.getCommit({
|
||||
owner: this.owner,
|
||||
repo: this.repo,
|
||||
ref: sha,
|
||||
});
|
||||
return commit.data;
|
||||
} catch (error) {
|
||||
const status = (error as { status?: number }).status;
|
||||
if (status === 404) {
|
||||
// Distinguish: does the repo itself still exist?
|
||||
let repoExists = false;
|
||||
try {
|
||||
await oct.repos.get({ owner: this.owner, repo: this.repo });
|
||||
repoExists = true;
|
||||
} catch {
|
||||
repoExists = false;
|
||||
}
|
||||
throw new AnonymousError(
|
||||
repoExists ? "commit_not_found" : "repo_not_found",
|
||||
{
|
||||
httpStatus: 404,
|
||||
cause: error as Error,
|
||||
object: this,
|
||||
}
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async branches(opt: {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import AnonymizedFile from "../AnonymizedFile";
|
||||
import GitHubBase, { GitHubBaseData } from "./GitHubBase";
|
||||
import GitHubBase, {
|
||||
GitHubBaseData,
|
||||
classifyGitHubMissError,
|
||||
} from "./GitHubBase";
|
||||
import storage from "../storage";
|
||||
import * as path from "path";
|
||||
import got from "got";
|
||||
@@ -274,7 +277,8 @@ export default class GitHubStream extends GitHubBase {
|
||||
});
|
||||
}
|
||||
if (status === 404) {
|
||||
throw new AnonymousError("repo_not_found", {
|
||||
const code = await classifyGitHubMissError(error, this.data);
|
||||
throw new AnonymousError(code, {
|
||||
httpStatus: 404,
|
||||
object: this.data,
|
||||
cause: error as Error,
|
||||
|
||||
Reference in New Issue
Block a user