mirror of
https://github.com/garronej/ts-ci.git
synced 2025-11-30 21:43:05 +00:00
Follow up from https://github.com/garronej/github_actions_toolkit
This commit is contained in:
commit
5297ab00ab
54 changed files with 18162 additions and 0 deletions
33
src/bin/generateActionYml.ts
Normal file
33
src/bin/generateActionYml.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { inputNames, getInputDescription, getInputDefault } from "../inputHelper";
|
||||
import { outputNames, getOutputDescription } from "../outputHelper";
|
||||
|
||||
const projectRoot = path.join(__dirname, "..", "..");
|
||||
|
||||
const packageJsonParsed = require(path.join(projectRoot, "package.json"));
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(projectRoot, "action.yml"),
|
||||
Buffer.from([
|
||||
`name: '${packageJsonParsed["name"]}'`,
|
||||
`description: '${packageJsonParsed["description"]}'`,
|
||||
`author: '${packageJsonParsed["author"]}'`,
|
||||
`inputs:`,
|
||||
...inputNames.map((inputName, i) => [
|
||||
` ${inputName}:`,
|
||||
` required: ${i === 0 ? "true" : "false"}`,
|
||||
` description: '${getInputDescription(inputName).replace(/'/g,"''")}'`,
|
||||
...[getInputDefault(inputName)].filter(x=>x!==undefined).map(s=>` default: '${s}'`)
|
||||
].join("\n")),
|
||||
`outputs:`,
|
||||
...outputNames.map((outputName, i) => [
|
||||
` ${outputName}:`,
|
||||
` description: '${getOutputDescription(outputName).replace(/'/g,"''")}'`
|
||||
].join("\n")),
|
||||
`runs:`,
|
||||
` using: 'node12'`,
|
||||
` main: 'dist/index.js'`
|
||||
].join("\n"), "utf8")
|
||||
);
|
||||
47
src/dispatch_event.ts
Normal file
47
src/dispatch_event.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { getActionParamsFactory } from "./inputHelper";
|
||||
import { createOctokit } from "./tools/createOctokit";
|
||||
|
||||
export const { getActionParams } = getActionParamsFactory({
|
||||
"inputNameSubset": [
|
||||
"owner",
|
||||
"repo",
|
||||
"event_type",
|
||||
"client_payload_json",
|
||||
"github_token"
|
||||
] as const
|
||||
});
|
||||
|
||||
export type Params = ReturnType<typeof getActionParams>;
|
||||
|
||||
|
||||
type CoreLike = {
|
||||
debug: (message: string) => void;
|
||||
};
|
||||
|
||||
export async function action(
|
||||
_actionName: "dispatch_event",
|
||||
params: Params,
|
||||
core: CoreLike
|
||||
) {
|
||||
|
||||
const { owner, repo, event_type, client_payload_json, github_token } = params;
|
||||
|
||||
core.debug(JSON.stringify({ _actionName, params }));
|
||||
|
||||
|
||||
const octokit = createOctokit({ github_token });
|
||||
|
||||
await octokit.repos.createDispatchEvent({
|
||||
owner,
|
||||
repo,
|
||||
event_type,
|
||||
|
||||
...(!!client_payload_json ?
|
||||
{ "client_payload": JSON.parse(client_payload_json) } :
|
||||
{}
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
61
src/get_package_json_version.ts
Normal file
61
src/get_package_json_version.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
import fetch from "node-fetch";
|
||||
const urlJoin: typeof import("path").join = require("url-join");
|
||||
import { setOutputFactory } from "./outputHelper";
|
||||
import { NpmModuleVersion } from "./tools/NpmModuleVersion";
|
||||
|
||||
import { getActionParamsFactory } from "./inputHelper";
|
||||
|
||||
export const { getActionParams } = getActionParamsFactory({
|
||||
"inputNameSubset": [
|
||||
"owner",
|
||||
"repo",
|
||||
"branch",
|
||||
"compare_to_version"
|
||||
] as const
|
||||
});
|
||||
|
||||
export type Params = ReturnType<typeof getActionParams>;
|
||||
|
||||
type CoreLike = {
|
||||
debug: (message: string) => void;
|
||||
};
|
||||
|
||||
export const { setOutput } = setOutputFactory<"version" | "compare_result">();
|
||||
|
||||
export async function action(
|
||||
_actionName: "get_package_json_version",
|
||||
params: Params,
|
||||
core: CoreLike
|
||||
): Promise<Parameters<typeof setOutput>[0]> {
|
||||
|
||||
core.debug(JSON.stringify(params));
|
||||
|
||||
const { owner, repo, branch, compare_to_version } = params;
|
||||
|
||||
const version = await fetch(
|
||||
urlJoin(
|
||||
"https://raw.github.com",
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
"package.json"
|
||||
)
|
||||
)
|
||||
.then(res => res.text())
|
||||
.then(text => JSON.parse(text))
|
||||
.then(({ version }) => version as string)
|
||||
.catch(() => "")
|
||||
;
|
||||
|
||||
core.debug(`Version on ${owner}/${repo}#${branch} is ${version}`);
|
||||
|
||||
return {
|
||||
version,
|
||||
"compare_result": NpmModuleVersion.compare(
|
||||
NpmModuleVersion.parse(version || "0.0.0"),
|
||||
NpmModuleVersion.parse(compare_to_version)
|
||||
).toString()
|
||||
};
|
||||
|
||||
}
|
||||
129
src/inputHelper.ts
Normal file
129
src/inputHelper.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
|
||||
import * as core from '@actions/core';
|
||||
|
||||
export const inputNames = [
|
||||
"action_name",
|
||||
"owner",
|
||||
"repo",
|
||||
"event_type",
|
||||
"client_payload_json",
|
||||
"branch",
|
||||
"exclude_commit_from_author_names_json",
|
||||
"module_name",
|
||||
"compare_to_version",
|
||||
"input_string",
|
||||
"search_value",
|
||||
"replace_value",
|
||||
"should_webhook_be_enabled",
|
||||
"github_token"
|
||||
] as const;
|
||||
|
||||
export const availableActions = [
|
||||
"get_package_json_version",
|
||||
"dispatch_event",
|
||||
"update_changelog",
|
||||
"sync_package_and_package_lock_version",
|
||||
"setup_repo_webhook_for_deno_land_publishing",
|
||||
"is_well_formed_and_available_module_name",
|
||||
"string_replace",
|
||||
"tell_if_project_uses_npm_or_yarn",
|
||||
"is_package_json_version_upgraded"
|
||||
] as const;
|
||||
|
||||
|
||||
export function getInputDescription(inputName: typeof inputNames[number]): string {
|
||||
switch(inputName){
|
||||
case "action_name": return [
|
||||
`Action to run, one of: `,
|
||||
availableActions.map(s=>`"${s}"`).join(", ")
|
||||
].join("");
|
||||
case "owner": return [
|
||||
"Repository owner, example: 'garronej',",
|
||||
"github.repository_owner"
|
||||
].join("");
|
||||
case "repo": return [
|
||||
"Repository name, example: ",
|
||||
"'evt', github.event.repository.name"
|
||||
].join("");
|
||||
case "event_type": return [
|
||||
"see: https://developer.github.com/v3/",
|
||||
"repos/#create-a-repository-dispatch-event"
|
||||
].join("");
|
||||
case "client_payload_json": return [
|
||||
"Example '{\"p\":\"foo\"}' see: https://developer.github.com/v3/",
|
||||
"repos/#create-a-repository-dispatch-event"
|
||||
].join("");
|
||||
case "branch": return "Example: default ( can also be a sha )";
|
||||
case "exclude_commit_from_author_names_json": return [
|
||||
"For update_changelog, do not includes commit from user ",
|
||||
`certain committer in the CHANGELOG.md, ex: '["denoify_ci"]'`
|
||||
].join("");
|
||||
case "module_name": return [
|
||||
`A candidate module name, Example: lodash`
|
||||
].join("");
|
||||
case "compare_to_version": return [
|
||||
`For get_package_json_version, a version against which comparing the result`,
|
||||
`if found version more recent than compare_to_version compare_result is 1`,
|
||||
`if found version is equal to compare_to_version compare_result is 0`,
|
||||
`if found version is older to compare_to_version compare_result -1`,
|
||||
`Example: 0.1.3`
|
||||
].join(" ");
|
||||
case "input_string": return `For string_replace, the string to replace`;
|
||||
case "search_value": return `For string_replace, Example '-' ( Will be used as arg for RegExp constructor )`;
|
||||
case "replace_value": return `For string_replace, Example '_'`;
|
||||
case "should_webhook_be_enabled": return `true|false, Should the create webhook be enabled, with setup_repo_webhook_for_deno_land_publishing`;
|
||||
case "github_token": return "GitHub Personal access token";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function getInputDefault(inputName: typeof inputNames[number]): string | undefined {
|
||||
|
||||
switch(inputName){
|
||||
case "owner": return "${{github.repository_owner}}";
|
||||
case "repo": return "${{github.event.repository.name}}";
|
||||
case "branch": return "${{ github.sha }}";
|
||||
case "github_token": return "${{ github.token }}";
|
||||
case "exclude_commit_from_author_names_json": return '["actions"]';
|
||||
case "should_webhook_be_enabled": return "true";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const getInput = (inputName: typeof inputNames[number]) => {
|
||||
|
||||
if (inputNames.indexOf(inputName) < 0) {
|
||||
throw new Error(`${inputName} expected`);
|
||||
}
|
||||
|
||||
return core.getInput(inputName);
|
||||
|
||||
}
|
||||
|
||||
|
||||
export function getActionParamsFactory<U extends typeof inputNames[number]>(
|
||||
params: {
|
||||
inputNameSubset: readonly U[]
|
||||
}
|
||||
) {
|
||||
|
||||
const { inputNameSubset } = params;
|
||||
|
||||
function getActionParams() {
|
||||
|
||||
const params: Record<U, string> = {} as any;
|
||||
|
||||
inputNameSubset.forEach(inputName => params[inputName] = getInput(inputName));
|
||||
|
||||
return params;
|
||||
|
||||
};
|
||||
|
||||
return { getActionParams };
|
||||
|
||||
}
|
||||
|
||||
export function getActionName(): typeof availableActions[number] {
|
||||
return getInput("action_name") as any;
|
||||
}
|
||||
100
src/is_package_json_version_upgraded.ts
Normal file
100
src/is_package_json_version_upgraded.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
|
||||
import fetch from "node-fetch";
|
||||
const urlJoin: typeof import("path").join = require("url-join");
|
||||
import { setOutputFactory } from "./outputHelper";
|
||||
import { NpmModuleVersion } from "./tools/NpmModuleVersion";
|
||||
import { getActionParamsFactory } from "./inputHelper";
|
||||
import { createOctokit } from "./tools/createOctokit";
|
||||
import { getLatestSemVersionedTagFactory } from "./tools/octokit-addons/getLatestSemVersionedTag";
|
||||
|
||||
export const { getActionParams } = getActionParamsFactory({
|
||||
"inputNameSubset": [
|
||||
"owner",
|
||||
"repo",
|
||||
"branch",
|
||||
"github_token"
|
||||
] as const
|
||||
});
|
||||
|
||||
export type Params = ReturnType<typeof getActionParams>;
|
||||
|
||||
type CoreLike = {
|
||||
debug: (message: string) => void;
|
||||
};
|
||||
|
||||
export const { setOutput } = setOutputFactory<"from_version" | "to_version" | "is_upgraded_version">();
|
||||
|
||||
export async function action(
|
||||
_actionName: "is_package_json_version_upgraded",
|
||||
params: Params,
|
||||
core: CoreLike
|
||||
): Promise<Parameters<typeof setOutput>[0]> {
|
||||
|
||||
core.debug(JSON.stringify(params));
|
||||
|
||||
const { owner, repo, branch, github_token } = params;
|
||||
|
||||
const to_version = await getPackageJsonVersion({ owner, repo, branch });
|
||||
|
||||
if( to_version === undefined ){
|
||||
throw new Error("No version in package.json on ${owner}/${repo}#${branch} (or repo is private)");
|
||||
}
|
||||
|
||||
core.debug(`Version on ${owner}/${repo}#${branch} is ${NpmModuleVersion.stringify(to_version)}`);
|
||||
|
||||
const octokit = createOctokit({ github_token });
|
||||
|
||||
const { getLatestSemVersionedTag } = getLatestSemVersionedTagFactory({ octokit });
|
||||
|
||||
const { version: from_version } = await getLatestSemVersionedTag({ owner, repo })
|
||||
.then(wrap => wrap === undefined ? { "version": NpmModuleVersion.parse("0.0.0") } : wrap);
|
||||
|
||||
core.debug(`Last version was ${NpmModuleVersion.stringify(from_version)}`);
|
||||
|
||||
const is_upgraded_version = NpmModuleVersion.compare(
|
||||
to_version,
|
||||
from_version
|
||||
) === 1 ? "true" : "false";
|
||||
|
||||
core.debug(`Is version upgraded: ${is_upgraded_version}`);
|
||||
|
||||
return {
|
||||
"to_version": NpmModuleVersion.stringify(to_version),
|
||||
"from_version": NpmModuleVersion.stringify(from_version),
|
||||
is_upgraded_version
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//TODO: Find a way to make it work with private repo
|
||||
async function getPackageJsonVersion(params: {
|
||||
owner: string;
|
||||
repo: string;
|
||||
branch: string;
|
||||
}): Promise<NpmModuleVersion | undefined> {
|
||||
|
||||
const { owner, repo, branch } = params;
|
||||
|
||||
const version = await fetch(
|
||||
urlJoin(
|
||||
`https://raw.github.com`,
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
"package.json"
|
||||
)
|
||||
)
|
||||
.then(res => res.text())
|
||||
.then(text => JSON.parse(text))
|
||||
.then(({ version }) => version as string)
|
||||
.catch(()=> undefined)
|
||||
;
|
||||
|
||||
if( version === undefined){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return NpmModuleVersion.parse(version);
|
||||
|
||||
}
|
||||
|
||||
58
src/is_well_formed_and_available_module_name.ts
Normal file
58
src/is_well_formed_and_available_module_name.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
|
||||
import { setOutputFactory } from "./outputHelper";
|
||||
import { getActionParamsFactory } from "./inputHelper";
|
||||
import { is404 } from "./tools/is404";
|
||||
|
||||
import validate_npm_package_name from "validate-npm-package-name";
|
||||
|
||||
|
||||
export const { getActionParams } = getActionParamsFactory({
|
||||
"inputNameSubset": [
|
||||
"module_name"
|
||||
] as const
|
||||
});
|
||||
|
||||
export type Params = ReturnType<typeof getActionParams>;
|
||||
|
||||
type CoreLike = {
|
||||
debug: (message: string) => void;
|
||||
};
|
||||
|
||||
export const { setOutput } = setOutputFactory<
|
||||
"is_valid_node_module_name" |
|
||||
"is_valid_deno_module_name" |
|
||||
"is_available_on_npm" |
|
||||
"is_available_on_deno_land"
|
||||
>();
|
||||
|
||||
export async function action(
|
||||
_actionName: "is_well_formed_and_available_module_name",
|
||||
params: Params,
|
||||
core: CoreLike
|
||||
): Promise<Parameters<typeof setOutput>[0]> {
|
||||
|
||||
const { module_name } = params;
|
||||
|
||||
const { validForNewPackages } = validate_npm_package_name(module_name);
|
||||
|
||||
const validForDenoPackages = validForNewPackages && module_name.indexOf("-") < 0
|
||||
|
||||
return {
|
||||
"is_valid_node_module_name": validForNewPackages ? "true" : "false",
|
||||
"is_available_on_npm":
|
||||
!validForNewPackages ?
|
||||
"false"
|
||||
:
|
||||
(await is404(`https://www.npmjs.com/package/${module_name}`)) ?
|
||||
"true" : "false",
|
||||
"is_valid_deno_module_name": validForDenoPackages ? "true" : "false",
|
||||
"is_available_on_deno_land":
|
||||
!validForDenoPackages ?
|
||||
"false"
|
||||
:
|
||||
(await is404(`https://deno.land/x/${module_name}/`)) ?
|
||||
"true" : "false"
|
||||
};
|
||||
|
||||
}
|
||||
113
src/main.ts
Normal file
113
src/main.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
import * as core from '@actions/core'
|
||||
import * as get_package_json_version from "./get_package_json_version";
|
||||
import * as dispatch_event from "./dispatch_event";
|
||||
import * as sync_package_and_package_lock_version from "./sync_package_and_package_lock_version";
|
||||
import * as setup_repo_webhook_for_deno_land_publishing from "./setup_repo_webhook_for_deno_land_publishing";
|
||||
import * as is_well_formed_and_available_module_name from "./is_well_formed_and_available_module_name";
|
||||
import * as tell_if_project_uses_npm_or_yarn from "./tell_if_project_uses_npm_or_yarn";
|
||||
import * as string_replace from "./string_replace";
|
||||
import * as is_package_json_version_upgraded from "./is_package_json_version_upgraded";
|
||||
import { getActionName } from "./inputHelper";
|
||||
import * as update_changelog from "./update_changelog";
|
||||
|
||||
async function run(): Promise<null> {
|
||||
|
||||
const action_name = getActionName();
|
||||
|
||||
switch (action_name) {
|
||||
case "get_package_json_version":
|
||||
get_package_json_version.setOutput(
|
||||
await get_package_json_version.action(
|
||||
action_name,
|
||||
get_package_json_version.getActionParams(),
|
||||
core
|
||||
)
|
||||
);
|
||||
return null;
|
||||
case "dispatch_event":
|
||||
await dispatch_event.action(
|
||||
action_name,
|
||||
dispatch_event.getActionParams(),
|
||||
core
|
||||
);
|
||||
return null;
|
||||
case "update_changelog":
|
||||
await update_changelog.action(
|
||||
action_name,
|
||||
update_changelog.getActionParams(),
|
||||
core
|
||||
);
|
||||
return null;
|
||||
case "sync_package_and_package_lock_version":
|
||||
await sync_package_and_package_lock_version.action(
|
||||
action_name,
|
||||
sync_package_and_package_lock_version.getActionParams(),
|
||||
core
|
||||
);
|
||||
return null;
|
||||
case "setup_repo_webhook_for_deno_land_publishing":
|
||||
setup_repo_webhook_for_deno_land_publishing.setOutput(
|
||||
await setup_repo_webhook_for_deno_land_publishing.action(
|
||||
action_name,
|
||||
setup_repo_webhook_for_deno_land_publishing.getActionParams(),
|
||||
core
|
||||
)
|
||||
);
|
||||
return null;
|
||||
case "is_well_formed_and_available_module_name":
|
||||
is_well_formed_and_available_module_name.setOutput(
|
||||
await is_well_formed_and_available_module_name.action(
|
||||
action_name,
|
||||
is_well_formed_and_available_module_name.getActionParams(),
|
||||
core
|
||||
)
|
||||
);
|
||||
return null;
|
||||
case "string_replace":
|
||||
string_replace.setOutput(
|
||||
await string_replace.action(
|
||||
action_name,
|
||||
string_replace.getActionParams(),
|
||||
core
|
||||
)
|
||||
);
|
||||
return null;
|
||||
case "tell_if_project_uses_npm_or_yarn":
|
||||
tell_if_project_uses_npm_or_yarn.setOutput(
|
||||
await tell_if_project_uses_npm_or_yarn.action(
|
||||
action_name,
|
||||
tell_if_project_uses_npm_or_yarn.getActionParams(),
|
||||
core
|
||||
)
|
||||
);
|
||||
return null;
|
||||
case "is_package_json_version_upgraded":
|
||||
is_package_json_version_upgraded.setOutput(
|
||||
await is_package_json_version_upgraded.action(
|
||||
action_name,
|
||||
is_package_json_version_upgraded.getActionParams(),
|
||||
core
|
||||
)
|
||||
);
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
if (1 === 0 + 1) {
|
||||
throw new Error(`${action_name} Not supported by this toolkit`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(async () => {
|
||||
|
||||
try {
|
||||
|
||||
await run()
|
||||
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
51
src/outputHelper.ts
Normal file
51
src/outputHelper.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
import * as core from '@actions/core'
|
||||
import { objectKeys } from "evt/dist/tools/typeSafety/objectKeys";
|
||||
|
||||
export const outputNames = [
|
||||
"version",
|
||||
"is_valid_node_module_name",
|
||||
"is_valid_deno_module_name",
|
||||
"is_available_on_npm",
|
||||
"is_available_on_deno_land",
|
||||
"was_already_published",
|
||||
"compare_result",
|
||||
"replace_result",
|
||||
"was_hook_created",
|
||||
"npm_or_yarn",
|
||||
"from_version",
|
||||
"to_version",
|
||||
"is_upgraded_version"
|
||||
] as const;
|
||||
|
||||
|
||||
export function getOutputDescription(inputName: typeof outputNames[number]): string {
|
||||
switch (inputName) {
|
||||
case "version": return "Output of get_package_json_version";
|
||||
case "is_valid_node_module_name": return "true|false";
|
||||
case "is_valid_deno_module_name": return "true|false";
|
||||
case "is_available_on_npm": return "true|false";
|
||||
case "is_available_on_deno_land": return "true|false";
|
||||
case "was_already_published": return "true|false";
|
||||
case "compare_result": return "1|0|-1";
|
||||
case "replace_result": return "Output of string_replace";
|
||||
case "was_hook_created": return "true|false";
|
||||
case "npm_or_yarn": return "npm|yarn";
|
||||
case "from_version": return "Output of is_package_json_version_upgraded, string";
|
||||
case "to_version": return "Output of is_package_json_version_upgraded, string";
|
||||
case "is_upgraded_version": return "Output of is_package_json_version_upgraded, true|false";
|
||||
}
|
||||
}
|
||||
|
||||
export function setOutputFactory<U extends typeof outputNames[number]>() {
|
||||
|
||||
function setOutput(outputs: Record<U, string>): void {
|
||||
|
||||
objectKeys(outputs)
|
||||
.forEach(outputName => core.setOutput(outputName, outputs[outputName]));
|
||||
|
||||
};
|
||||
|
||||
return { setOutput };
|
||||
|
||||
}
|
||||
56
src/setup_repo_webhook_for_deno_land_publishing.ts
Normal file
56
src/setup_repo_webhook_for_deno_land_publishing.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
import { setOutputFactory } from "./outputHelper";
|
||||
import { getActionParamsFactory } from "./inputHelper";
|
||||
import { createOctokit } from "./tools/createOctokit";
|
||||
|
||||
export const { getActionParams } = getActionParamsFactory({
|
||||
"inputNameSubset": [
|
||||
"owner",
|
||||
"repo",
|
||||
"should_webhook_be_enabled",
|
||||
"github_token"
|
||||
] as const
|
||||
});
|
||||
|
||||
export type Params = ReturnType<typeof getActionParams>;
|
||||
|
||||
type CoreLike = {
|
||||
debug: (message: string) => void;
|
||||
warning: (message: string) => void;
|
||||
};
|
||||
|
||||
|
||||
export const { setOutput } = setOutputFactory<"was_hook_created">();
|
||||
|
||||
export async function action(
|
||||
_actionName: "setup_repo_webhook_for_deno_land_publishing",
|
||||
params: Params,
|
||||
core: CoreLike
|
||||
): Promise<Parameters<typeof setOutput>[0]> {
|
||||
|
||||
const { owner, repo, should_webhook_be_enabled, github_token } = params;
|
||||
|
||||
const octokit = createOctokit({ github_token });
|
||||
|
||||
try {
|
||||
|
||||
await octokit.repos.createWebhook({
|
||||
owner,
|
||||
repo,
|
||||
"active": should_webhook_be_enabled === "true",
|
||||
"events": ["create"],
|
||||
"config": {
|
||||
"url": `https://api.deno.land/webhook/gh/${repo}?subdir=deno_dist%252F`,
|
||||
"content_type": "json"
|
||||
}
|
||||
});
|
||||
|
||||
} catch {
|
||||
|
||||
return { "was_hook_created": "false" };
|
||||
|
||||
}
|
||||
|
||||
return { "was_hook_created": "true" };
|
||||
|
||||
}
|
||||
39
src/string_replace.ts
Normal file
39
src/string_replace.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
import { setOutputFactory } from "./outputHelper";
|
||||
|
||||
import { getActionParamsFactory } from "./inputHelper";
|
||||
|
||||
export const { getActionParams } = getActionParamsFactory({
|
||||
"inputNameSubset": [
|
||||
"input_string",
|
||||
"search_value",
|
||||
"replace_value"
|
||||
] as const
|
||||
});
|
||||
|
||||
export type Params = ReturnType<typeof getActionParams>;
|
||||
|
||||
type CoreLike = {
|
||||
debug: (message: string) => void;
|
||||
};
|
||||
|
||||
export const { setOutput } = setOutputFactory<"replace_result">();
|
||||
|
||||
export async function action(
|
||||
_actionName: "string_replace",
|
||||
params: Params,
|
||||
core: CoreLike
|
||||
): Promise<Parameters<typeof setOutput>[0]> {
|
||||
|
||||
core.debug(JSON.stringify(params));
|
||||
|
||||
const { input_string, search_value, replace_value } = params;
|
||||
|
||||
return {
|
||||
"replace_result": input_string.replace(
|
||||
new RegExp(search_value, "g"),
|
||||
replace_value
|
||||
)
|
||||
};
|
||||
|
||||
}
|
||||
92
src/sync_package_and_package_lock_version.ts
Normal file
92
src/sync_package_and_package_lock_version.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import { getActionParamsFactory } from "./inputHelper";
|
||||
import * as st from "scripting-tools";
|
||||
import * as fs from "fs";
|
||||
import { gitCommit } from "./tools/gitCommit";
|
||||
|
||||
export const { getActionParams } = getActionParamsFactory({
|
||||
"inputNameSubset": [
|
||||
"owner",
|
||||
"repo",
|
||||
"branch",
|
||||
"github_token"
|
||||
] as const
|
||||
});
|
||||
|
||||
export type Params = ReturnType<typeof getActionParams>;
|
||||
|
||||
type CoreLike = {
|
||||
debug: (message: string) => void;
|
||||
};
|
||||
|
||||
export async function action(
|
||||
_actionName: "sync_package_and_package_lock_version",
|
||||
params: Params,
|
||||
core: CoreLike
|
||||
) {
|
||||
|
||||
core.debug(JSON.stringify(params));
|
||||
|
||||
const { owner, repo, branch, github_token } = params;
|
||||
|
||||
await gitCommit({
|
||||
owner,
|
||||
repo,
|
||||
github_token,
|
||||
"commitAuthorEmail": "actions@github.com",
|
||||
"performChanges": async () => {
|
||||
|
||||
await st.exec(`git checkout ${branch}`);
|
||||
|
||||
const { version } = JSON.parse(
|
||||
fs.readFileSync("package.json")
|
||||
.toString("utf8")
|
||||
);
|
||||
|
||||
if (!fs.existsSync("package-lock.json")) {
|
||||
core.debug(`No package-lock.json tracked by ${owner}/${repo}#${branch}`);
|
||||
return { "commit": false };
|
||||
}
|
||||
|
||||
const packageLockJsonRaw = fs.readFileSync("package-lock.json")
|
||||
.toString("utf8")
|
||||
;
|
||||
|
||||
const packageLockJsonParsed = JSON.parse(
|
||||
packageLockJsonRaw
|
||||
);
|
||||
|
||||
if (packageLockJsonParsed.version === version) {
|
||||
core.debug("Nothing to do, version in package.json and package-lock.json are the same");
|
||||
return { "commit": false };
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
"package-lock.json",
|
||||
Buffer.from(
|
||||
JSON.stringify(
|
||||
(() => {
|
||||
packageLockJsonParsed.version = version;
|
||||
packageLockJsonParsed.packages[""].version = version;
|
||||
return packageLockJsonParsed;
|
||||
})(),
|
||||
null,
|
||||
packageLockJsonRaw
|
||||
.replace(/\t/g, " ")
|
||||
.match(/^(\s*)\"version\"/m)![1].length
|
||||
) + packageLockJsonRaw.match(/}([\r\n]*)$/)![1],
|
||||
"utf8"
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
"commit": true,
|
||||
"addAll": false,
|
||||
"message": "Sync package.json and package.lock version"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
50
src/tell_if_project_uses_npm_or_yarn.ts
Normal file
50
src/tell_if_project_uses_npm_or_yarn.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
import fetch from "node-fetch";
|
||||
const urlJoin: typeof import("path").join = require("url-join");
|
||||
import { setOutputFactory } from "./outputHelper";
|
||||
|
||||
import { getActionParamsFactory } from "./inputHelper";
|
||||
|
||||
export const { getActionParams } = getActionParamsFactory({
|
||||
"inputNameSubset": [
|
||||
"owner",
|
||||
"repo",
|
||||
"branch"
|
||||
] as const
|
||||
});
|
||||
|
||||
export type Params = ReturnType<typeof getActionParams>;
|
||||
|
||||
type CoreLike = {
|
||||
debug: (message: string) => void;
|
||||
};
|
||||
|
||||
export const { setOutput } = setOutputFactory<"npm_or_yarn">();
|
||||
|
||||
export async function action(
|
||||
_actionName: "tell_if_project_uses_npm_or_yarn",
|
||||
params: Params,
|
||||
core: CoreLike
|
||||
): Promise<Parameters<typeof setOutput>[0]> {
|
||||
|
||||
core.debug(JSON.stringify(params));
|
||||
|
||||
const { owner, repo } = params;
|
||||
|
||||
const branch = params.branch.split("/").reverse()[0];
|
||||
|
||||
const npm_or_yarn = await fetch(
|
||||
urlJoin(
|
||||
"https://raw.github.com",
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
"yarn.lock"
|
||||
)
|
||||
).then(res => res.status === 404 ? "npm" : "yarn")
|
||||
|
||||
core.debug(`Version on ${owner}/${repo}#${branch} is using ${npm_or_yarn}`);
|
||||
|
||||
return { npm_or_yarn };
|
||||
|
||||
}
|
||||
17
src/test/dispatch_event.ts
Normal file
17
src/test/dispatch_event.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
|
||||
|
||||
import { action } from "../dispatch_event";
|
||||
|
||||
action(
|
||||
"dispatch_event",
|
||||
{
|
||||
"owner": "garronej",
|
||||
"repo": "test-repo",
|
||||
"event_type": "example-event",
|
||||
"client_payload_json": JSON.stringify({ "foo": "bar" }),
|
||||
"github_token": ""
|
||||
},
|
||||
{ "debug": console.log }
|
||||
);
|
||||
|
||||
22
src/test/get_package_json_version.ts
Normal file
22
src/test/get_package_json_version.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
|
||||
|
||||
import { action } from "../get_package_json_version";
|
||||
|
||||
(async () => {
|
||||
|
||||
const repo = "denoify";
|
||||
|
||||
const result = await action("get_package_json_version", {
|
||||
"owner": "garronej",
|
||||
repo,
|
||||
"branch": "aa5da60301bea4cf0e80e98a4579f7076b544a44",
|
||||
"compare_to_version": "0.0.0"
|
||||
}, { "debug": console.log });
|
||||
|
||||
console.log(result);
|
||||
|
||||
|
||||
})();
|
||||
|
||||
|
||||
20
src/test/is_package_json_version_upgraded.ts
Normal file
20
src/test/is_package_json_version_upgraded.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
|
||||
import { action } from "../is_package_json_version_upgraded";
|
||||
|
||||
(async () => {
|
||||
|
||||
const repo = "keycloakify-demo-app";
|
||||
|
||||
const result = await action("is_package_json_version_upgraded", {
|
||||
"owner": "InseeFrLab",
|
||||
repo,
|
||||
"branch": "4fc0ccb46bdb3912e0a215ca3ae45aed458ea6a4",
|
||||
"github_token": ""
|
||||
}, { "debug": console.log });
|
||||
|
||||
console.log(result);
|
||||
|
||||
})();
|
||||
|
||||
|
||||
33
src/test/is_well_formed_and_available_module_name.ts
Normal file
33
src/test/is_well_formed_and_available_module_name.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
|
||||
|
||||
|
||||
import { action } from "../is_well_formed_and_available_module_name";
|
||||
|
||||
(async () => {
|
||||
|
||||
|
||||
for (const module_name of [
|
||||
"congenial_pancake",
|
||||
"evt",
|
||||
"_okay",
|
||||
"mai-oui-c-clair"
|
||||
]) {
|
||||
|
||||
console.log({ module_name });
|
||||
|
||||
const resp = await action(
|
||||
"is_well_formed_and_available_module_name",
|
||||
{ module_name },
|
||||
{ "debug": console.log }
|
||||
);
|
||||
|
||||
console.log(resp);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
})();
|
||||
|
||||
|
||||
27
src/test/setup_repo_webhook_for_deno_land_publishing.ts
Normal file
27
src/test/setup_repo_webhook_for_deno_land_publishing.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
import { action } from "../setup_repo_webhook_for_deno_land_publishing";
|
||||
import * as st from "scripting-tools";
|
||||
|
||||
(async () => {
|
||||
|
||||
st.enableCmdTrace();
|
||||
|
||||
const repo = "reimagined_octo_winner_kay";
|
||||
|
||||
const resp = await action(
|
||||
"setup_repo_webhook_for_deno_land_publishing",
|
||||
{
|
||||
"owner": "garronej",
|
||||
"repo": "reimagined_octo_winner_kay",
|
||||
"should_webhook_be_enabled": "false",
|
||||
"github_token": ""
|
||||
},
|
||||
{
|
||||
"debug": console.log,
|
||||
"warning": console.warn
|
||||
}
|
||||
);
|
||||
|
||||
console.log(resp);
|
||||
|
||||
})();
|
||||
17
src/test/string_replace.ts
Normal file
17
src/test/string_replace.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
import { action } from "../string_replace";
|
||||
import * as st from "scripting-tools";
|
||||
|
||||
(async () => {
|
||||
|
||||
const { replace_result } = await action("string_replace", {
|
||||
"input_string": "octo-computing-machine",
|
||||
"search_value": "-",
|
||||
"replace_value": "_"
|
||||
},
|
||||
{ "debug": console.log }
|
||||
);
|
||||
|
||||
console.log({ replace_result });
|
||||
|
||||
})();
|
||||
20
src/test/sync_package_and_package_lock_version.ts
Normal file
20
src/test/sync_package_and_package_lock_version.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
|
||||
import { action } from "../sync_package_and_package_lock_version";
|
||||
|
||||
(async () => {
|
||||
|
||||
const repo = "literate-sniffle";
|
||||
|
||||
await action("sync_package_and_package_lock_version", {
|
||||
"owner": "garronej",
|
||||
repo,
|
||||
"branch": "master",
|
||||
"github_token": ""
|
||||
},
|
||||
{ "debug": console.log }
|
||||
);
|
||||
|
||||
})();
|
||||
|
||||
|
||||
17
src/test/tell_if_project_uses_npm_or_yarn.ts
Normal file
17
src/test/tell_if_project_uses_npm_or_yarn.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
|
||||
import { action } from "../tell_if_project_uses_npm_or_yarn";
|
||||
|
||||
(async () => {
|
||||
|
||||
const repo = "powerhooks";
|
||||
|
||||
await action("tell_if_project_uses_npm_or_yarn", {
|
||||
"owner": "garronej",
|
||||
repo,
|
||||
"branch": "refs/heads/master"
|
||||
},
|
||||
{ "debug": console.log }
|
||||
);
|
||||
|
||||
})();
|
||||
25
src/test/update_changelog.ts
Normal file
25
src/test/update_changelog.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
import { action } from "../update_changelog";
|
||||
import * as st from "scripting-tools";
|
||||
|
||||
(async () => {
|
||||
|
||||
st.enableCmdTrace();
|
||||
|
||||
const repo = "sturdy_umbrella";
|
||||
|
||||
await action("update_changelog", {
|
||||
"owner": "garronej",
|
||||
repo,
|
||||
"branch": "dev",
|
||||
"exclude_commit_from_author_names_json": JSON.stringify(["denoify_ci"]),
|
||||
"github_token": ""
|
||||
},
|
||||
{
|
||||
"debug": console.log,
|
||||
"warning": console.log
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
})();
|
||||
90
src/tools/NpmModuleVersion.ts
Normal file
90
src/tools/NpmModuleVersion.ts
Normal 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";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
10
src/tools/createOctokit.ts
Normal file
10
src/tools/createOctokit.ts
Normal 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
60
src/tools/gitCommit.ts
Normal 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
6
src/tools/is404.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
import fetch from "node-fetch";
|
||||
|
||||
export function is404(url: string): Promise<boolean> {
|
||||
return fetch(url).then(({ status }) => status === 404);
|
||||
}
|
||||
55
src/tools/octokit-addons/getCommitAhead.ts
Normal file
55
src/tools/octokit-addons/getCommitAhead.ts
Normal 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 };
|
||||
|
||||
|
||||
|
||||
}
|
||||
100
src/tools/octokit-addons/getCommitAsyncIterable.ts
Normal file
100
src/tools/octokit-addons/getCommitAsyncIterable.ts
Normal 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 };
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
90
src/tools/octokit-addons/getCommonOrigin.ts
Normal file
90
src/tools/octokit-addons/getCommonOrigin.ts
Normal 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 };
|
||||
|
||||
|
||||
}
|
||||
48
src/tools/octokit-addons/getLatestSemVersionedTag.ts
Normal file
48
src/tools/octokit-addons/getLatestSemVersionedTag.ts
Normal 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 };
|
||||
|
||||
}
|
||||
93
src/tools/octokit-addons/getPullRequestAsyncIterable.ts
Normal file
93
src/tools/octokit-addons/getPullRequestAsyncIterable.ts
Normal 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 };
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
4
src/tools/octokit-addons/index.ts
Normal file
4
src/tools/octokit-addons/index.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export { getCommitAheadFactory as getChangeLogFactory } from "./getCommitAhead";
|
||||
export { Commit, getCommitAsyncIterableFactory } from "./getCommitAsyncIterable";
|
||||
export { getCommonOriginFactory } from "./getCommonOrigin";
|
||||
export { listCommitFactory } from "./listCommit";
|
||||
51
src/tools/octokit-addons/listCommit.ts
Normal file
51
src/tools/octokit-addons/listCommit.ts
Normal 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 };
|
||||
|
||||
}
|
||||
82
src/tools/octokit-addons/listTags.ts
Normal file
82
src/tools/octokit-addons/listTags.ts
Normal 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 };
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
22
src/tools/test/octokit-addons/getChangeLog.ts
Normal file
22
src/tools/test/octokit-addons/getChangeLog.ts
Normal 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));
|
||||
|
||||
})();
|
||||
28
src/tools/test/octokit-addons/getCommitAsyncIterable.ts
Normal file
28
src/tools/test/octokit-addons/getCommitAsyncIterable.ts
Normal 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");
|
||||
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
23
src/tools/test/octokit-addons/getCommonOrigin.ts
Normal file
23
src/tools/test/octokit-addons/getCommonOrigin.ts
Normal 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 });
|
||||
|
||||
})();
|
||||
|
||||
|
||||
21
src/tools/test/octokit-addons/listCommit.ts
Normal file
21
src/tools/test/octokit-addons/listCommit.ts
Normal 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));
|
||||
|
||||
})();
|
||||
192
src/update_changelog.ts
Normal file
192
src/update_changelog.ts
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
|
||||
import { getActionParamsFactory } from "./inputHelper";
|
||||
import * as st from "scripting-tools";
|
||||
import { getCommitAheadFactory } from "./tools/octokit-addons/getCommitAhead";
|
||||
import * as get_package_json_version from "./get_package_json_version";
|
||||
import * as fs from "fs";
|
||||
import { NpmModuleVersion } from "./tools/NpmModuleVersion";
|
||||
import { gitCommit } from "./tools/gitCommit";
|
||||
import { getLatestSemVersionedTagFactory } from "./tools/octokit-addons/getLatestSemVersionedTag";
|
||||
import { createOctokit } from "./tools/createOctokit";
|
||||
|
||||
export const { getActionParams } = getActionParamsFactory({
|
||||
"inputNameSubset": [
|
||||
"owner",
|
||||
"repo",
|
||||
"branch",
|
||||
"exclude_commit_from_author_names_json",
|
||||
"github_token"
|
||||
] as const
|
||||
});
|
||||
|
||||
export type Params = ReturnType<typeof getActionParams>;
|
||||
|
||||
type CoreLike = {
|
||||
debug: (message: string) => void;
|
||||
warning: (message: string)=> void;
|
||||
};
|
||||
|
||||
export async function action(
|
||||
_actionName: "update_changelog",
|
||||
params: Params,
|
||||
core: CoreLike
|
||||
) {
|
||||
|
||||
const {
|
||||
owner,
|
||||
repo,
|
||||
github_token
|
||||
} = params;
|
||||
|
||||
const branch = params.branch.split("/").reverse()[0];
|
||||
|
||||
core.debug(`params: ${JSON.stringify(params)}`);
|
||||
|
||||
const exclude_commit_from_author_names: string[] =
|
||||
JSON.parse(params.exclude_commit_from_author_names_json)
|
||||
;
|
||||
|
||||
|
||||
const octokit = createOctokit({ github_token });
|
||||
|
||||
const { getCommitAhead } = getCommitAheadFactory({ octokit });
|
||||
|
||||
const { getLatestSemVersionedTag } = getLatestSemVersionedTagFactory({ octokit });
|
||||
|
||||
const { tag: branchBehind } = (await getLatestSemVersionedTag({ owner, repo })) ?? {};
|
||||
|
||||
if( branchBehind === undefined ){
|
||||
|
||||
core.warning(`It's the first release, not editing the CHANGELOG.md`);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const { commits } = await getCommitAhead({
|
||||
owner,
|
||||
repo,
|
||||
branchBehind,
|
||||
"branchAhead": branch
|
||||
}).catch(() => ({ "commits": undefined }));
|
||||
|
||||
if( commits === undefined ){
|
||||
|
||||
core.warning(`${branchBehind} probably does not exist`);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const [
|
||||
branchBehindVersion,
|
||||
branchAheadVersion
|
||||
] = await Promise.all(
|
||||
([branchBehind, branch] as const)
|
||||
.map(branch =>
|
||||
get_package_json_version.action(
|
||||
"get_package_json_version",
|
||||
{
|
||||
owner,
|
||||
repo,
|
||||
branch,
|
||||
"compare_to_version": "0.0.0"
|
||||
},
|
||||
core
|
||||
).then(({ version }) => version)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
const bumpType = NpmModuleVersion.bumpType({
|
||||
"versionAheadStr": branchAheadVersion,
|
||||
"versionBehindStr": branchBehindVersion || "0.0.0"
|
||||
});
|
||||
|
||||
if( bumpType === "SAME" ){
|
||||
|
||||
core.warning(`Version on ${branch} and ${branchBehind} are the same, not editing CHANGELOG.md`);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
await gitCommit({
|
||||
owner,
|
||||
repo,
|
||||
github_token,
|
||||
"commitAuthorEmail": "actions@github.com",
|
||||
"performChanges": async () => {
|
||||
|
||||
await st.exec(`git checkout ${branch}`);
|
||||
|
||||
const { changelogRaw } = updateChangelog({
|
||||
"changelogRaw":
|
||||
fs.existsSync("CHANGELOG.md") ?
|
||||
fs.readFileSync("CHANGELOG.md")
|
||||
.toString("utf8")
|
||||
: "",
|
||||
"version": branchAheadVersion,
|
||||
bumpType,
|
||||
"body": commits
|
||||
.reverse()
|
||||
.filter(({ commit }) => !exclude_commit_from_author_names.includes(commit.author.name))
|
||||
.map(({ commit }) => commit.message)
|
||||
.filter(message => !/changelog/i.test(message))
|
||||
.filter(message => !/^Merge branch /.test(message))
|
||||
.filter(message => !/^GitBook: /.test(message))
|
||||
.map(message => `- ${message} `)
|
||||
.join("\n")
|
||||
});
|
||||
|
||||
core.debug(`CHANGELOG.md: ${changelogRaw}`);
|
||||
|
||||
fs.writeFileSync(
|
||||
"CHANGELOG.md",
|
||||
Buffer.from(changelogRaw, "utf8")
|
||||
);
|
||||
|
||||
return {
|
||||
"commit": true,
|
||||
"addAll": true,
|
||||
"message": `Update changelog v${branchAheadVersion}`
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function updateChangelog(
|
||||
params: {
|
||||
changelogRaw: string;
|
||||
version: string;
|
||||
bumpType: "MAJOR" | "MINOR" | "PATCH";
|
||||
body: string;
|
||||
}
|
||||
): { changelogRaw: string; } {
|
||||
|
||||
const { body, version, bumpType } = params;
|
||||
|
||||
const dateString = (() => {
|
||||
|
||||
const now = new Date();
|
||||
|
||||
return new Date(now.getTime() - (now.getTimezoneOffset() * 60000))
|
||||
.toISOString()
|
||||
.split("T")[0];
|
||||
|
||||
})();
|
||||
|
||||
const changelogRaw = [
|
||||
`${bumpType === "MAJOR" ? "#" : (bumpType === "MINOR" ? "##" : "###")}`,
|
||||
` **${version}** (${dateString}) \n \n`,
|
||||
`${body} \n \n`,
|
||||
params.changelogRaw
|
||||
].join("");
|
||||
|
||||
|
||||
return { changelogRaw };
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue