mirror of
https://github.com/bendtherules/github-delete-unused-repos.git
synced 2026-08-18 13:52:59 +00:00
Convert detection logic into class + Remove unused sample files
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
import { Greeter } from '../src/greeter';
|
||||
|
||||
test('Should greet with message', () => {
|
||||
const greeter = new Greeter('friend');
|
||||
expect(greeter.greet()).toBe('Bonjour, friend!');
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import * as Octokit from '@octokit/rest';
|
||||
import assert = require('assert');
|
||||
import Bottleneck from 'bottleneck';
|
||||
|
||||
// +++ General init +++
|
||||
const octokit = new Octokit() as OctokitMod;
|
||||
|
||||
// Add rate limiter
|
||||
@@ -20,6 +21,10 @@ octokit.authenticate({
|
||||
secret: '2228539a48032f0622d6c12a66f56253d0a30d60',
|
||||
});
|
||||
|
||||
// --- End General init ---
|
||||
|
||||
// +++ All typescript definitions +++
|
||||
|
||||
interface RequestOptions {
|
||||
method: string;
|
||||
url: string;
|
||||
@@ -73,7 +78,7 @@ interface ResponseWithMetaLink {
|
||||
|
||||
interface ResponseWithDataArrayAndMeta<T>
|
||||
extends ResponseWithDataArray<T>,
|
||||
ResponseWithMetaLink {}
|
||||
ResponseWithMetaLink { }
|
||||
|
||||
interface ResponseFromGetUserRepo extends ResponseWithMetaLink {
|
||||
data: RepoFromGetUserRepo[];
|
||||
@@ -163,207 +168,27 @@ interface RepoNameWithParentRepo {
|
||||
|
||||
interface RepoNameWithBranchesAndParent
|
||||
extends RepoNameWithBranches,
|
||||
RepoNameWithParentRepo {}
|
||||
|
||||
const username = 'rousan';
|
||||
RepoNameWithParentRepo { }
|
||||
|
||||
interface ObjectWithPerPage {
|
||||
per_page?: number;
|
||||
}
|
||||
|
||||
async function paginate<TFirstParam extends ObjectWithPerPage, TDataElement>(
|
||||
method: (
|
||||
args: TFirstParam
|
||||
) => Promise<ResponseWithDataArrayAndMeta<TDataElement>>,
|
||||
args: TFirstParam
|
||||
): Promise<ResponseWithDataArray<TDataElement>> {
|
||||
// Set per_page
|
||||
args.per_page = 100;
|
||||
// --- End all typescript definitions ---
|
||||
|
||||
let response: ResponseWithDataArrayAndMeta<TDataElement> = await method(args);
|
||||
class GithubDetectUnusedRepos {
|
||||
private username: string;
|
||||
|
||||
// Concat all data
|
||||
let { data } = response;
|
||||
while (octokit.hasNextPage(response)) {
|
||||
response = await octokit.getNextPage(response);
|
||||
data = data.concat(response.data);
|
||||
constructor(username: string) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchRepoNameWithBranches(
|
||||
repoName: string
|
||||
): Promise<RepoNameWithBranches> {
|
||||
const params: Octokit.ReposGetBranchesParams = {
|
||||
owner: username,
|
||||
repo: repoName,
|
||||
};
|
||||
|
||||
const branchesResponse: ResponseWithDataArray<
|
||||
BranchFromGetBranches
|
||||
> = await paginate((tmpFirstParam: Octokit.ReposGetBranchesParams): Promise<
|
||||
ResponseFromGetBranches
|
||||
> => {
|
||||
return (octokit.repos.getBranches(tmpFirstParam) as any) as Promise<
|
||||
ResponseFromGetBranches
|
||||
>;
|
||||
}, params);
|
||||
|
||||
return {
|
||||
repoName,
|
||||
branches: branchesResponse.data,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchRepoNameWithParentRepo(
|
||||
forkedRepoName: string
|
||||
): Promise<RepoNameWithParentRepo> {
|
||||
const responseRepoDetails: ResponseFromGetRepo = await octokit.repos.get({
|
||||
owner: username,
|
||||
repo: forkedRepoName,
|
||||
});
|
||||
|
||||
const repoDetails = responseRepoDetails.data;
|
||||
|
||||
return {
|
||||
repoName: forkedRepoName,
|
||||
parentRepo: repoDetails.parent,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchForkBranchIsNotAhead(
|
||||
parentRepoOwner: string,
|
||||
parentRepoName: string,
|
||||
forkedRepoOwner: string,
|
||||
forkedRepoName: string,
|
||||
forkedBranchName: string,
|
||||
parentBranchName?: string
|
||||
): Promise<boolean> {
|
||||
let commitObject: ResponseFromCompareCommits;
|
||||
|
||||
if (parentBranchName === undefined) {
|
||||
parentBranchName = forkedBranchName;
|
||||
}
|
||||
|
||||
try {
|
||||
commitObject = ((await octokit.repos.compareCommits({
|
||||
// both owner and repo can be either parent or forked, for this usecase
|
||||
owner: parentRepoOwner,
|
||||
repo: parentRepoName,
|
||||
base: `${parentRepoOwner}:${parentBranchName}`,
|
||||
head: `${forkedRepoOwner}:${forkedBranchName}`,
|
||||
})) as any) as ResponseFromCompareCommits;
|
||||
} catch {
|
||||
const fallBackParentBranchName = 'master';
|
||||
|
||||
if (parentBranchName !== fallBackParentBranchName) {
|
||||
return fetchForkBranchIsNotAhead(
|
||||
parentRepoOwner,
|
||||
parentRepoName,
|
||||
forkedRepoOwner,
|
||||
forkedRepoName,
|
||||
forkedBranchName,
|
||||
fallBackParentBranchName
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (commitObject.data.ahead_by === 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Async determine if parent repo contains all commits which are
|
||||
// currently set as HEAD in each branch of the original repo
|
||||
async function fetchNoneOfForkBranchesIsAhead(
|
||||
repoInfo: RepoNameWithBranchesAndParent
|
||||
): Promise<RepoNameWithUnusedFlag> {
|
||||
const allPromiseBranchUnused = repoInfo.branches.map(tmpBranch => {
|
||||
return fetchForkBranchIsNotAhead(
|
||||
repoInfo.parentRepo.owner.login,
|
||||
repoInfo.parentRepo.name,
|
||||
username,
|
||||
repoInfo.repoName,
|
||||
tmpBranch.name
|
||||
);
|
||||
});
|
||||
|
||||
const allBranchUnused = await Promise.all(allPromiseBranchUnused);
|
||||
|
||||
const everyBranchUnused = allBranchUnused.every(
|
||||
tmpBoolean => tmpBoolean === true
|
||||
);
|
||||
|
||||
return {
|
||||
repoName: repoInfo.repoName,
|
||||
unused: everyBranchUnused,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchUserIsNotContributor(
|
||||
repoOwner: string,
|
||||
repoName: string
|
||||
): Promise<RepoNameWithUnusedFlag> {
|
||||
const params: Octokit.ReposGetContributorsParams = {
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
anon: '0',
|
||||
};
|
||||
|
||||
const responseFromGetContributors: ResponseWithDataArray<
|
||||
OwnerFromGetContributors
|
||||
> = await paginate(
|
||||
async (
|
||||
tmpFirstParam: Octokit.ReposGetContributorsParams
|
||||
): Promise<ResponseWithDataArrayAndMeta<OwnerFromGetContributors>> => {
|
||||
// Modify getContributors to return empty contributor data array instead of undefined for empty repos
|
||||
const response = await ((octokit.repos.getContributors(
|
||||
tmpFirstParam
|
||||
) as any) as Promise<ResponseFromGetContributors>);
|
||||
|
||||
let dataNormalized = response.data;
|
||||
if (dataNormalized === undefined) {
|
||||
dataNormalized = [];
|
||||
}
|
||||
|
||||
const responseNormalized: ResponseWithDataArrayAndMeta<
|
||||
OwnerFromGetContributors
|
||||
> = {
|
||||
data: dataNormalized,
|
||||
meta: response.meta,
|
||||
};
|
||||
|
||||
return responseNormalized;
|
||||
},
|
||||
params
|
||||
);
|
||||
|
||||
const contributors = responseFromGetContributors.data;
|
||||
|
||||
const matchingContributor = contributors.find(
|
||||
tmpContributor => tmpContributor.login === username
|
||||
);
|
||||
|
||||
return {
|
||||
// tslint:disable-next-line:object-literal-shorthand
|
||||
repoName: repoName,
|
||||
unused: matchingContributor === undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchUnusedForkedRepos() {
|
||||
public async fetchUnusedForkedRepos() {
|
||||
const params: Octokit.ReposGetForUserParams = {
|
||||
username,
|
||||
this.username,
|
||||
};
|
||||
|
||||
const repos: ResponseWithDataArray<RepoFromGetUserRepo> = await paginate(
|
||||
const repos: ResponseWithDataArray<RepoFromGetUserRepo> = await this.paginate(
|
||||
(
|
||||
tmpFirstParam: Octokit.ReposGetForUserParams
|
||||
): Promise<ResponseFromGetUserRepo> => {
|
||||
@@ -382,12 +207,12 @@ export async function fetchUnusedForkedRepos() {
|
||||
console.log(forkedRepoNames);
|
||||
|
||||
const allPromiseRepoNameWithBranches = forkedRepoNames.map(repoName => {
|
||||
return fetchRepoNameWithBranches(repoName);
|
||||
return this.fetchRepoNameWithBranches(repoName);
|
||||
});
|
||||
|
||||
const allPromiseRepoNameWithParentRepo = forkedRepoNames.map(
|
||||
forkedRepoName => {
|
||||
return fetchRepoNameWithParentRepo(forkedRepoName);
|
||||
return this.fetchRepoNameWithParentRepo(forkedRepoName);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -440,19 +265,19 @@ export async function fetchUnusedForkedRepos() {
|
||||
{
|
||||
const allPromiseRepoWithFlagFromCommit = allRepoNameWithBranchesAndParent.map(
|
||||
(repoInfo: RepoNameWithBranchesAndParent) => {
|
||||
return fetchNoneOfForkBranchesIsAhead(repoInfo);
|
||||
return this.fetchNoneOfForkBranchesIsAhead(repoInfo);
|
||||
}
|
||||
);
|
||||
|
||||
const allPromiseRepoWithFlagFromForkContrib = allRepoNameWithBranchesAndParent.map(
|
||||
({ repoName }) => {
|
||||
return fetchUserIsNotContributor(username, repoName);
|
||||
return this.fetchUserIsNotContributor(this.username, repoName);
|
||||
}
|
||||
);
|
||||
|
||||
const allPromiseRepoWithFlagFromParentContrib = allRepoNameWithBranchesAndParent.map(
|
||||
({ repoName, parentRepo }) => {
|
||||
return fetchUserIsNotContributor(parentRepo.owner.login, repoName);
|
||||
return this.fetchUserIsNotContributor(parentRepo.owner.login, repoName);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -512,10 +337,202 @@ export async function fetchUnusedForkedRepos() {
|
||||
.map(tmp => tmp.repoName);
|
||||
|
||||
return unusedRepoNames;
|
||||
}
|
||||
|
||||
private async paginate<TFirstParam extends ObjectWithPerPage, TDataElement>(
|
||||
method: (
|
||||
args: TFirstParam
|
||||
) => Promise<ResponseWithDataArrayAndMeta<TDataElement>>,
|
||||
args: TFirstParam
|
||||
): Promise<ResponseWithDataArray<TDataElement>> {
|
||||
// Set per_page
|
||||
args.per_page = 100;
|
||||
|
||||
let response: ResponseWithDataArrayAndMeta<TDataElement> = await method(args);
|
||||
|
||||
// Concat all data
|
||||
let { data } = response;
|
||||
while (octokit.hasNextPage(response)) {
|
||||
response = await octokit.getNextPage(response);
|
||||
data = data.concat(response.data);
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
private async fetchRepoNameWithBranches(
|
||||
repoName: string
|
||||
): Promise<RepoNameWithBranches> {
|
||||
const params: Octokit.ReposGetBranchesParams = {
|
||||
owner: this.username,
|
||||
repo: repoName,
|
||||
};
|
||||
|
||||
const branchesResponse: ResponseWithDataArray<
|
||||
BranchFromGetBranches
|
||||
> = await this.paginate((tmpFirstParam: Octokit.ReposGetBranchesParams): Promise<
|
||||
ResponseFromGetBranches
|
||||
> => {
|
||||
return (octokit.repos.getBranches(tmpFirstParam) as any) as Promise<
|
||||
ResponseFromGetBranches
|
||||
>;
|
||||
}, params);
|
||||
|
||||
return {
|
||||
repoName,
|
||||
branches: branchesResponse.data,
|
||||
};
|
||||
}
|
||||
|
||||
private async fetchRepoNameWithParentRepo(
|
||||
forkedRepoName: string
|
||||
): Promise<RepoNameWithParentRepo> {
|
||||
const responseRepoDetails: ResponseFromGetRepo = await octokit.repos.get({
|
||||
owner: this.username,
|
||||
repo: forkedRepoName,
|
||||
});
|
||||
|
||||
const repoDetails = responseRepoDetails.data;
|
||||
|
||||
return {
|
||||
repoName: forkedRepoName,
|
||||
parentRepo: repoDetails.parent,
|
||||
};
|
||||
}
|
||||
|
||||
private async fetchForkBranchIsNotAhead(
|
||||
parentRepoOwner: string,
|
||||
parentRepoName: string,
|
||||
forkedRepoOwner: string,
|
||||
forkedRepoName: string,
|
||||
forkedBranchName: string,
|
||||
parentBranchName?: string
|
||||
): Promise<boolean> {
|
||||
let commitObject: ResponseFromCompareCommits;
|
||||
|
||||
if (parentBranchName === undefined) {
|
||||
parentBranchName = forkedBranchName;
|
||||
}
|
||||
|
||||
try {
|
||||
commitObject = ((await octokit.repos.compareCommits({
|
||||
// both owner and repo can be either parent or forked, for this usecase
|
||||
owner: parentRepoOwner,
|
||||
repo: parentRepoName,
|
||||
base: `${parentRepoOwner}:${parentBranchName}`,
|
||||
head: `${forkedRepoOwner}:${forkedBranchName}`,
|
||||
})) as any) as ResponseFromCompareCommits;
|
||||
} catch {
|
||||
const fallBackParentBranchName = 'master';
|
||||
|
||||
if (parentBranchName !== fallBackParentBranchName) {
|
||||
return this.fetchForkBranchIsNotAhead(
|
||||
parentRepoOwner,
|
||||
parentRepoName,
|
||||
forkedRepoOwner,
|
||||
forkedRepoName,
|
||||
forkedBranchName,
|
||||
fallBackParentBranchName
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (commitObject.data.ahead_by === 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Async determine if parent repo contains all commits which are
|
||||
// currently set as HEAD in each branch of the original repo
|
||||
private async fetchNoneOfForkBranchesIsAhead(
|
||||
repoInfo: RepoNameWithBranchesAndParent
|
||||
): Promise<RepoNameWithUnusedFlag> {
|
||||
const allPromiseBranchUnused = repoInfo.branches.map(tmpBranch => {
|
||||
return this.fetchForkBranchIsNotAhead(
|
||||
repoInfo.parentRepo.owner.login,
|
||||
repoInfo.parentRepo.name,
|
||||
this.username,
|
||||
repoInfo.repoName,
|
||||
tmpBranch.name
|
||||
);
|
||||
});
|
||||
|
||||
const allBranchUnused = await Promise.all(allPromiseBranchUnused);
|
||||
|
||||
const everyBranchUnused = allBranchUnused.every(
|
||||
tmpBoolean => tmpBoolean === true
|
||||
);
|
||||
|
||||
return {
|
||||
repoName: repoInfo.repoName,
|
||||
unused: everyBranchUnused,
|
||||
};
|
||||
}
|
||||
|
||||
private async fetchUserIsNotContributor(
|
||||
repoOwner: string,
|
||||
repoName: string
|
||||
): Promise<RepoNameWithUnusedFlag> {
|
||||
const params: Octokit.ReposGetContributorsParams = {
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
anon: '0',
|
||||
};
|
||||
|
||||
const responseFromGetContributors: ResponseWithDataArray<
|
||||
OwnerFromGetContributors
|
||||
> = await this.paginate(
|
||||
async (
|
||||
tmpFirstParam: Octokit.ReposGetContributorsParams
|
||||
): Promise<ResponseWithDataArrayAndMeta<OwnerFromGetContributors>> => {
|
||||
// Modify getContributors to return empty contributor data array instead of undefined for empty repos
|
||||
const response = await ((octokit.repos.getContributors(
|
||||
tmpFirstParam
|
||||
) as any) as Promise<ResponseFromGetContributors>);
|
||||
|
||||
let dataNormalized = response.data;
|
||||
if (dataNormalized === undefined) {
|
||||
dataNormalized = [];
|
||||
}
|
||||
|
||||
const responseNormalized: ResponseWithDataArrayAndMeta<
|
||||
OwnerFromGetContributors
|
||||
> = {
|
||||
data: dataNormalized,
|
||||
meta: response.meta,
|
||||
};
|
||||
|
||||
return responseNormalized;
|
||||
},
|
||||
params
|
||||
);
|
||||
|
||||
const contributors = responseFromGetContributors.data;
|
||||
|
||||
const matchingContributor = contributors.find(
|
||||
tmpContributor => tmpContributor.login === this.username
|
||||
);
|
||||
|
||||
return {
|
||||
// tslint:disable-next-line:object-literal-shorthand
|
||||
repoName: repoName,
|
||||
unused: matchingContributor === undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function runMain() {
|
||||
fetchUnusedForkedRepos().then(unusedRepoNames => {
|
||||
|
||||
|
||||
export function runMain(username: string = 'bendtherules') {
|
||||
new GithubDetectUnusedRepos(username)
|
||||
.fetchUnusedForkedRepos()
|
||||
.then(unusedRepoNames => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log(unusedRepoNames);
|
||||
});
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
export class Greeter {
|
||||
private greeting: string;
|
||||
|
||||
constructor(message: string) {
|
||||
this.greeting = message;
|
||||
}
|
||||
|
||||
public greet(): string {
|
||||
return `Bonjour, ${this.greeting}!`;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
import 'babel-polyfill';
|
||||
import { Greeter } from './greeter';
|
||||
// tslint:disable-next-line:ordered-imports
|
||||
import { fetchUnusedForkedRepos, runMain } from './gh';
|
||||
export * from './greeter';
|
||||
|
||||
// tslint:disable-next-line:only-arrow-functions
|
||||
(function(currentWindow: Window) {
|
||||
@@ -13,7 +11,6 @@ export * from './greeter';
|
||||
|
||||
// assign Greeter to global
|
||||
const myWindow = currentWindow as any;
|
||||
myWindow.Greeter = Greeter;
|
||||
myWindow.runMain = runMain;
|
||||
// we can use Greeter , execute following in console
|
||||
// var greet = new Greeter('myName');
|
||||
|
||||
Reference in New Issue
Block a user