This commit is contained in:
garronej 2021-12-01 13:39:57 +01:00
commit 5297ab00ab
54 changed files with 18162 additions and 0 deletions

View file

@ -0,0 +1,90 @@
export type NpmModuleVersion = {
major: number;
minor: number;
patch: number;
};
export namespace NpmModuleVersion {
export function parse(versionStr: string): NpmModuleVersion {
const match = versionStr.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-beta.([0-9]+))?/);
if( !match ){
throw new Error(`${versionStr} is not a valid NPM version`);
}
return {
"major": parseInt(match[1]),
"minor": parseInt(match[2]),
"patch": parseInt(match[3])
};
};
export function stringify(v: NpmModuleVersion) {
return `${v.major}.${v.minor}.${v.patch}`;
}
/**
*
* v1 < v2 => -1
* v1 === v2 => 0
* v1 > v2 => 1
*
*/
export function compare(v1: NpmModuleVersion, v2: NpmModuleVersion): -1 | 0 | 1 {
const sign = (n: number): -1 | 0 | 1 => n === 0 ? 0 : (n < 0 ? -1 : 1);
if (v1.major === v2.major) {
if (v1.minor === v2.minor) {
return sign(v1.patch - v2.patch);
} else {
return sign(v1.minor - v2.minor);
}
} else {
return sign(v1.major - v2.major);
}
}
export function bumpType(
params: {
versionBehindStr: string;
versionAheadStr: string;
}
): "SAME" | "MAJOR" | "MINOR" | "PATCH" {
const versionAhead = parse(params.versionAheadStr);
const versionBehind = parse(params.versionBehindStr);
if( compare(versionBehind, versionAhead) === 1 ){
throw new Error(`Version regression ${versionBehind} -> ${versionAhead}`);
}
if (versionBehind.major !== versionAhead.major) {
return "MAJOR";
} else if (versionBehind.minor !== versionAhead.minor) {
return "MINOR";
} else if (versionBehind.patch !== versionAhead.patch) {
return "PATCH";
} else {
return "SAME";
}
}
}

View file

@ -0,0 +1,10 @@
import { Octokit } from "@octokit/rest";
export function createOctokit(params: { github_token: string; }) {
const { github_token } = params;
return new Octokit({ ...(github_token !== "" ? { "auth": github_token } : {}) });
}

60
src/tools/gitCommit.ts Normal file
View file

@ -0,0 +1,60 @@
import * as st from "scripting-tools";
export async function gitCommit(
params: {
owner: string;
repo: string;
commitAuthorEmail: string;
performChanges: () => Promise<{ commit: false; } | { commit: true; addAll: boolean; message: string; }>;
github_token: string;
}
) {
const { owner, repo, commitAuthorEmail, performChanges, github_token } = params;
await st.exec(`git clone https://github.com/${owner}/${repo}`);
const cwd = process.cwd();
process.chdir(repo);
const changesResult = await (async () => {
try {
return await performChanges();
} catch (error) {
return error as Error;
}
})()
if (!(changesResult instanceof Error) && changesResult.commit) {
await st.exec(`git config --local user.email "${commitAuthorEmail}"`);
await st.exec(`git config --local user.name "${commitAuthorEmail.split("@")[0]}"`);
if (changesResult.addAll) {
await st.exec(`git add -A`);
}
await st.exec(`git commit -am "${changesResult.message}"`);
await st.exec(`git push "https://${owner}:${github_token}@github.com/${owner}/${repo}.git"`);
}
process.chdir(cwd);
await st.exec(`rm -r ${repo}`);
if (changesResult instanceof Error) {
throw changesResult;
}
}

6
src/tools/is404.ts Normal file
View file

@ -0,0 +1,6 @@
import fetch from "node-fetch";
export function is404(url: string): Promise<boolean> {
return fetch(url).then(({ status }) => status === 404);
}

View file

@ -0,0 +1,55 @@
import { getCommonOriginFactory } from "./getCommonOrigin";
import { listCommitFactory } from "./listCommit";
import type { Commit } from "./getCommitAsyncIterable";
import type { Octokit } from "@octokit/rest";
/** Take two branch that have a common origin and list all the
* commit that have been made on the branch that is ahead since it
* has been forked from the branch that is behind.
* From the older to the newest.
* */
export function getCommitAheadFactory(
params: { octokit: Octokit; }
) {
const { octokit } = params;
const { getCommonOrigin } = getCommonOriginFactory({ octokit });
const { listCommit } = listCommitFactory({ octokit });
async function getCommitAhead(
params: {
owner: string;
repo: string;
branchBehind: string;
branchAhead: string;
}
): Promise<{ commits: Commit[]; }> {
const { owner, repo, branchBehind, branchAhead } = params;
const { sha } = await getCommonOrigin({
owner,
repo,
"branch1": branchBehind,
"branch2": branchAhead
});
const commits = await listCommit({
owner,
repo,
"branch": branchAhead,
sha
});
return { commits };
}
return { getCommitAhead };
}

View file

@ -0,0 +1,100 @@
import type { AsyncReturnType } from "evt/dist/tools/typeSafety/AsyncReturnType";
import type { Octokit } from "@octokit/rest";
/*
.sha
.commit.message
.author.type
.author.type !== "User"
*/
/** Alias for the non exported ReposListCommitsResponseData type alias */
export type Commit = AsyncReturnType<Octokit["repos"]["listCommits"]>["data"][number];
const per_page = 30;
/** Iterate over the commits of a repo's branch */
export function getCommitAsyncIterableFactory(params: { octokit: Octokit; }) {
const { octokit } = params;
function getCommitAsyncIterable(
params: {
owner: string;
repo: string;
branch: string;
}
): AsyncIterable<Commit> {
const { owner, repo, branch } = params;
let commits: Commit[] = [];
let page = 0;
let isLastPage: boolean | undefined = undefined;
const getReposListCommitsResponseData = (params: { page: number }) =>
octokit.repos.listCommits({
owner,
repo,
per_page,
"page": params.page,
"sha": branch
}).then(({ data }) => data)
;
return {
[Symbol.asyncIterator]() {
return {
"next": async ()=> {
if (commits.length === 0) {
if (isLastPage) {
return { "done": true, "value": undefined };
}
page++;
commits = await getReposListCommitsResponseData({ page });
if (commits.length === 0) {
return { "done": true, "value": undefined };
}
isLastPage =
commits.length !== per_page ||
(await getReposListCommitsResponseData({ "page": page + 1 })).length === 0
;
}
const [commit, ...rest] = commits;
commits = rest;
return {
"value": commit,
"done": false
};
}
};
}
};
}
return { getCommitAsyncIterable };
}

View file

@ -0,0 +1,90 @@
import { getCommitAsyncIterableFactory } from "./getCommitAsyncIterable";
import type { Octokit } from "@octokit/rest";
/** Return the sha of the first common commit between two branches */
export function getCommonOriginFactory(params: { octokit: Octokit; }) {
const { octokit } = params;
const { getCommitAsyncIterable } = getCommitAsyncIterableFactory({ octokit });
async function getCommonOrigin(
params: {
owner: string;
repo: string;
branch1: string;
branch2: string;
}
): Promise<{ sha: string; }> {
const { owner, repo, branch1, branch2 } = params;
const [
commitAsyncIterable1,
commitAsyncIterable2
] = ([branch1, branch2] as const)
.map(branch => getCommitAsyncIterable({ owner, repo, branch }))
;
let shas1: string[] = [];
let shas2: string[] = [];
while (true) {
const [
itRes1,
itRes2
] =
await Promise.all(
([commitAsyncIterable1, commitAsyncIterable2] as const)
.map(
commitAsyncIterable => commitAsyncIterable[Symbol.asyncIterator]()
.next()
)
)
;
let sha1: string | undefined = undefined;
if (!itRes1.done) {
sha1 = itRes1.value.sha;
shas1.push(sha1);
}
let sha2: string | undefined = undefined;
if (!itRes2.done) {
sha2 = itRes2.value.sha;
shas2.push(sha2);
}
if (!!sha1 && shas2.includes(sha1)) {
return { "sha": sha1 };
}
if (!!sha2 && shas1.includes(sha2)) {
return { "sha": sha2 };
}
if (itRes1.done && itRes2.done) {
throw new Error("No common origin");
}
}
}
return { getCommonOrigin };
}

View file

@ -0,0 +1,48 @@
import { listTagsFactory } from "./listTags";
import type { Octokit } from "@octokit/rest";
import { NpmModuleVersion } from "../NpmModuleVersion";
export function getLatestSemVersionedTagFactory(params: { octokit: Octokit; }) {
const { octokit } = params;
async function getLatestSemVersionedTag(
params: {
owner: string;
repo: string;
}
): Promise<{
tag: string;
version: NpmModuleVersion;
} | undefined> {
const { owner, repo } = params;
const semVersionedTags: { tag: string; version: NpmModuleVersion; }[] = [];
const { listTags } = listTagsFactory({ octokit });
for await (const tag of listTags({ owner, repo })) {
const match = tag.match(/^v?([0-9]+\.[0-9]+\.[0-9]+)$/);
if (!match) {
continue;
}
semVersionedTags.push({
tag,
"version": NpmModuleVersion.parse( match[1])
});
}
return semVersionedTags
.sort(({ version: vX }, { version: vY }) => NpmModuleVersion.compare(vY, vX))[0];
};
return { getLatestSemVersionedTag };
}

View file

@ -0,0 +1,93 @@
import type { AsyncReturnType } from "evt/dist/tools/typeSafety/AsyncReturnType";
import type { Octokit } from "@octokit/rest";
/** Alias for the non exported PullsListResponseData type alias */
export type PullRequest = AsyncReturnType<Octokit["pulls"]["list"]>["data"][number];
const per_page = 99;
export function getPullRequestAsyncIterableFactory(params: { octokit: Octokit; }) {
const { octokit } = params;
function getPullRequestAsyncIterable(
params: {
owner: string;
repo: string;
state: "open" | "closed" | "all"
}
): AsyncIterable<PullRequest> {
const { owner, repo, state } = params;
let pullRequests: PullRequest[] = [];
let page = 0;
let isLastPage: boolean | undefined = undefined;
const getPullsListResponseData = (params: { page: number }) =>
octokit.pulls.list({
owner,
repo,
state,
per_page,
"page": params.page
}).then(({ data }) => data)
;
return {
[Symbol.asyncIterator]() {
return {
"next": async ()=> {
if (pullRequests.length === 0) {
if (isLastPage) {
return { "done": true, "value": undefined };
}
page++;
pullRequests = await getPullsListResponseData({ page });
if (pullRequests.length === 0) {
return { "done": true, "value": undefined };
}
isLastPage =
pullRequests.length !== per_page ||
(await getPullsListResponseData({ "page": page + 1 })).length === 0
;
}
const [pullRequest, ...rest] = pullRequests;
pullRequests = rest;
return {
"value": pullRequest,
"done": false
};
}
};
}
};
}
return { getPullRequestAsyncIterable };
}

View file

@ -0,0 +1,4 @@
export { getCommitAheadFactory as getChangeLogFactory } from "./getCommitAhead";
export { Commit, getCommitAsyncIterableFactory } from "./getCommitAsyncIterable";
export { getCommonOriginFactory } from "./getCommonOrigin";
export { listCommitFactory } from "./listCommit";

View file

@ -0,0 +1,51 @@
import type { Octokit } from "@octokit/rest";
import { getCommitAsyncIterableFactory } from "./getCommitAsyncIterable";
import type { Commit } from "./getCommitAsyncIterable";
/** Return the list of commit since given sha (excluded)
* ordered from the oldest to the newest */
export function listCommitFactory(
params: { octokit: Octokit }
) {
const { octokit } = params;
const { getCommitAsyncIterable } = getCommitAsyncIterableFactory({ octokit });
async function listCommit(
params: {
owner: string;
repo: string;
branch: string;
sha: string;
}
): Promise<Commit[]> {
const { owner, repo, branch, sha } = params;
const commitAsyncIterable = getCommitAsyncIterable({
owner,
repo,
branch
});
const commits: Commit[]= [];
for await (const commit of commitAsyncIterable) {
if( commit.sha === sha ){
break;
}
commits.push(commit);
}
return commits.reverse();
}
return { listCommit };
}

View file

@ -0,0 +1,82 @@
import type { Octokit } from "@octokit/rest";
const per_page = 99;
export function listTagsFactory(params: { octokit: Octokit; }) {
const { octokit } = params;
const octokit_repo_listTags = (
async (
params: {
owner: string;
repo: string;
per_page: number;
page: number;
}
) => {
return octokit.repos.listTags(params);
}
);
async function* listTags(
params: {
owner: string;
repo: string;
}
): AsyncGenerator<string> {
const { owner, repo } = params;
let page = 1;
while (true) {
const resp = await octokit_repo_listTags({
owner,
repo,
per_page,
"page": page++
});
for (const branch of resp.data.map(({ name }) => name)) {
yield branch;
}
if (resp.data.length < 99) {
break;
}
}
}
/** Returns the same "latest" tag as deno.land/x, not actually the latest though */
async function getLatestTag(
params: {
owner: string;
repo: string;
}
): Promise<string | undefined> {
const { owner, repo } = params;
const itRes = await listTags({ owner, repo }).next();
if (itRes.done) {
return undefined;
}
return itRes.value;
}
return { listTags, getLatestTag };
}

View file

@ -0,0 +1,22 @@
import { getCommitAheadFactory } from "../../octokit-addons/getCommitAhead";
import { Octokit } from "@octokit/rest";
(async ()=>{
const octokit = new Octokit();
const { getCommitAhead } = getCommitAheadFactory({ octokit });
const { commits } = await getCommitAhead({
"owner": "garronej",
"repo": "test-repo",
"branchBehind": "garronej-patch-1",
"branchAhead": "master"
});
const messages = commits.map(({ commit })=> commit.message );
console.log(JSON.stringify(messages, null, 2));
})();

View file

@ -0,0 +1,28 @@
import { getCommitAsyncIterableFactory } from "../../octokit-addons/getCommitAsyncIterable";
import { createOctokit } from "../../createOctokit";
(async function () {
const octokit = createOctokit({ "github_token": "" });
const { getCommitAsyncIterable } = getCommitAsyncIterableFactory({ octokit });
const commitAsyncIterable = getCommitAsyncIterable({
"owner": "garronej",
"repo": "test-repo",
"branch": "master"
});
for await (const commit of commitAsyncIterable) {
console.log(commit.commit.message);
}
console.log("done");
})();

View file

@ -0,0 +1,23 @@
import { getCommonOriginFactory } from "../../octokit-addons/getCommonOrigin";
import { Octokit } from "@octokit/rest";
(async function () {
const octokit = new Octokit();
const { getCommonOrigin } = getCommonOriginFactory({ octokit });
const { sha } = await getCommonOrigin({
"owner": "garronej",
"repo": "test-repo",
"branch1": "master",
"branch2": "garronej-patch-1"
});
console.log({ sha });
})();

View file

@ -0,0 +1,21 @@
import { listCommitFactory } from "../../octokit-addons/listCommit";
import { createOctokit } from "../../createOctokit";
(async ()=>{
const octokit = createOctokit({ "github_token": "" });
const { listCommit } = listCommitFactory({ octokit });
const commits= await listCommit({
"owner": "garronej",
"repo": "supreme_tribble",
"branch": "dev",
"sha": "84792f5719d0812e6917051b5c6331891187ca20"
});
console.log(JSON.stringify(commits, null, 2));
})();