mirror of
https://github.com/softprops/action-gh-release.git
synced 2025-06-28 14:16:02 +00:00
chore: add prettier config (#633)
Signed-off-by: Rui Chen <rui@chenrui.dev>
This commit is contained in:
parent
605f567f95
commit
c8d8e91662
7 changed files with 273 additions and 355 deletions
108
src/github.ts
108
src/github.ts
|
@ -1,9 +1,9 @@
|
|||
import { GitHub } from "@actions/github/lib/utils";
|
||||
import { statSync } from "fs";
|
||||
import { open } from "fs/promises";
|
||||
import { lookup } from "mime-types";
|
||||
import { basename } from "path";
|
||||
import { alignAssetName, Config, isTag, releaseBody } from "./util";
|
||||
import { GitHub } from '@actions/github/lib/utils';
|
||||
import { statSync } from 'fs';
|
||||
import { open } from 'fs/promises';
|
||||
import { lookup } from 'mime-types';
|
||||
import { basename } from 'path';
|
||||
import { alignAssetName, Config, isTag, releaseBody } from './util';
|
||||
|
||||
type GitHub = InstanceType<typeof GitHub>;
|
||||
|
||||
|
@ -27,11 +27,7 @@ export interface Release {
|
|||
}
|
||||
|
||||
export interface Releaser {
|
||||
getReleaseByTag(params: {
|
||||
owner: string;
|
||||
repo: string;
|
||||
tag: string;
|
||||
}): Promise<{ data: Release }>;
|
||||
getReleaseByTag(params: { owner: string; repo: string; tag: string }): Promise<{ data: Release }>;
|
||||
|
||||
createRelease(params: {
|
||||
owner: string;
|
||||
|
@ -44,7 +40,7 @@ export interface Releaser {
|
|||
target_commitish: string | undefined;
|
||||
discussion_category_name: string | undefined;
|
||||
generate_release_notes: boolean | undefined;
|
||||
make_latest: "true" | "false" | "legacy" | undefined;
|
||||
make_latest: 'true' | 'false' | 'legacy' | undefined;
|
||||
}): Promise<{ data: Release }>;
|
||||
|
||||
updateRelease(params: {
|
||||
|
@ -59,13 +55,10 @@ export interface Releaser {
|
|||
prerelease: boolean | undefined;
|
||||
discussion_category_name: string | undefined;
|
||||
generate_release_notes: boolean | undefined;
|
||||
make_latest: "true" | "false" | "legacy" | undefined;
|
||||
make_latest: 'true' | 'false' | 'legacy' | undefined;
|
||||
}): Promise<{ data: Release }>;
|
||||
|
||||
allReleases(params: {
|
||||
owner: string;
|
||||
repo: string;
|
||||
}): AsyncIterableIterator<{ data: Release[] }>;
|
||||
allReleases(params: { owner: string; repo: string }): AsyncIterableIterator<{ data: Release[] }>;
|
||||
}
|
||||
|
||||
export class GitHubReleaser implements Releaser {
|
||||
|
@ -93,11 +86,11 @@ export class GitHubReleaser implements Releaser {
|
|||
target_commitish: string | undefined;
|
||||
discussion_category_name: string | undefined;
|
||||
generate_release_notes: boolean | undefined;
|
||||
make_latest: "true" | "false" | "legacy" | undefined;
|
||||
make_latest: 'true' | 'false' | 'legacy' | undefined;
|
||||
}): Promise<{ data: Release }> {
|
||||
if (
|
||||
typeof params.make_latest === "string" &&
|
||||
!["true", "false", "legacy"].includes(params.make_latest)
|
||||
typeof params.make_latest === 'string' &&
|
||||
!['true', 'false', 'legacy'].includes(params.make_latest)
|
||||
) {
|
||||
params.make_latest = undefined;
|
||||
}
|
||||
|
@ -117,11 +110,11 @@ export class GitHubReleaser implements Releaser {
|
|||
prerelease: boolean | undefined;
|
||||
discussion_category_name: string | undefined;
|
||||
generate_release_notes: boolean | undefined;
|
||||
make_latest: "true" | "false" | "legacy" | undefined;
|
||||
make_latest: 'true' | 'false' | 'legacy' | undefined;
|
||||
}): Promise<{ data: Release }> {
|
||||
if (
|
||||
typeof params.make_latest === "string" &&
|
||||
!["true", "false", "legacy"].includes(params.make_latest)
|
||||
typeof params.make_latest === 'string' &&
|
||||
!['true', 'false', 'legacy'].includes(params.make_latest)
|
||||
) {
|
||||
params.make_latest = undefined;
|
||||
}
|
||||
|
@ -129,10 +122,7 @@ export class GitHubReleaser implements Releaser {
|
|||
return this.github.rest.repos.updateRelease(params);
|
||||
}
|
||||
|
||||
allReleases(params: {
|
||||
owner: string;
|
||||
repo: string;
|
||||
}): AsyncIterableIterator<{ data: Release[] }> {
|
||||
allReleases(params: { owner: string; repo: string }): AsyncIterableIterator<{ data: Release[] }> {
|
||||
const updatedParams = { per_page: 100, ...params };
|
||||
return this.github.paginate.iterator(
|
||||
this.github.rest.repos.listReleases.endpoint.merge(updatedParams),
|
||||
|
@ -149,7 +139,7 @@ export const asset = (path: string): ReleaseAsset => {
|
|||
};
|
||||
|
||||
export const mimeOrDefault = (path: string): string => {
|
||||
return lookup(path) || "application/octet-stream";
|
||||
return lookup(path) || 'application/octet-stream';
|
||||
};
|
||||
|
||||
export const upload = async (
|
||||
|
@ -159,7 +149,7 @@ export const upload = async (
|
|||
path: string,
|
||||
currentAssets: Array<{ id: number; name: string }>,
|
||||
): Promise<any> => {
|
||||
const [owner, repo] = config.github_repository.split("/");
|
||||
const [owner, repo] = config.github_repository.split('/');
|
||||
const { name, mime, size } = asset(path);
|
||||
const currentAsset = currentAssets.find(
|
||||
// note: GitHub renames asset filenames that have special characters, non-alphanumeric characters, and leading or trailing periods. The "List release assets" endpoint lists the renamed filenames.
|
||||
|
@ -169,9 +159,7 @@ export const upload = async (
|
|||
);
|
||||
if (currentAsset) {
|
||||
if (config.input_overwrite_files === false) {
|
||||
console.log(
|
||||
`Asset ${name} already exists and overwrite_files is false...`,
|
||||
);
|
||||
console.log(`Asset ${name} already exists and overwrite_files is false...`);
|
||||
return null;
|
||||
} else {
|
||||
console.log(`♻️ Deleting previously uploaded asset ${name}...`);
|
||||
|
@ -184,18 +172,18 @@ export const upload = async (
|
|||
}
|
||||
console.log(`⬆️ Uploading ${name}...`);
|
||||
const endpoint = new URL(url);
|
||||
endpoint.searchParams.append("name", name);
|
||||
endpoint.searchParams.append('name', name);
|
||||
const fh = await open(path);
|
||||
try {
|
||||
const resp = await github.request({
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
url: endpoint.toString(),
|
||||
headers: {
|
||||
"content-length": `${size}`,
|
||||
"content-type": mime,
|
||||
'content-length': `${size}`,
|
||||
'content-type': mime,
|
||||
authorization: `token ${config.github_token}`,
|
||||
},
|
||||
data: fh.readableWebStream({ type: "bytes" }),
|
||||
data: fh.readableWebStream({ type: 'bytes' }),
|
||||
});
|
||||
const json = resp.data;
|
||||
if (resp.status !== 201) {
|
||||
|
@ -219,25 +207,18 @@ export const release = async (
|
|||
): Promise<Release> => {
|
||||
if (maxRetries <= 0) {
|
||||
console.log(`❌ Too many retries. Aborting...`);
|
||||
throw new Error("Too many retries.");
|
||||
throw new Error('Too many retries.');
|
||||
}
|
||||
|
||||
const [owner, repo] = config.github_repository.split("/");
|
||||
const [owner, repo] = config.github_repository.split('/');
|
||||
const tag =
|
||||
config.input_tag_name ||
|
||||
(isTag(config.github_ref)
|
||||
? config.github_ref.replace("refs/tags/", "")
|
||||
: "");
|
||||
(isTag(config.github_ref) ? config.github_ref.replace('refs/tags/', '') : '');
|
||||
|
||||
const discussion_category_name = config.input_discussion_category_name;
|
||||
const generate_release_notes = config.input_generate_release_notes;
|
||||
try {
|
||||
const _release: Release | undefined = await findTagFromReleases(
|
||||
releaser,
|
||||
owner,
|
||||
repo,
|
||||
tag,
|
||||
);
|
||||
const _release: Release | undefined = await findTagFromReleases(releaser, owner, repo, tag);
|
||||
|
||||
if (_release === undefined) {
|
||||
return await createRelease(
|
||||
|
@ -253,9 +234,7 @@ export const release = async (
|
|||
}
|
||||
|
||||
let existingRelease: Release = _release!;
|
||||
console.log(
|
||||
`Found release ${existingRelease.name} (with id=${existingRelease.id})`,
|
||||
);
|
||||
console.log(`Found release ${existingRelease.name} (with id=${existingRelease.id})`);
|
||||
|
||||
const release_id = existingRelease.id;
|
||||
let target_commitish: string;
|
||||
|
@ -277,23 +256,18 @@ export const release = async (
|
|||
// body parts as a release gets updated. some users will likely want this while
|
||||
// others won't previously this was duplicating content for most which
|
||||
// no one wants
|
||||
const workflowBody = releaseBody(config) || "";
|
||||
const existingReleaseBody = existingRelease.body || "";
|
||||
const workflowBody = releaseBody(config) || '';
|
||||
const existingReleaseBody = existingRelease.body || '';
|
||||
let body: string;
|
||||
if (config.input_append_body && workflowBody && existingReleaseBody) {
|
||||
body = existingReleaseBody + "\n" + workflowBody;
|
||||
body = existingReleaseBody + '\n' + workflowBody;
|
||||
} else {
|
||||
body = workflowBody || existingReleaseBody;
|
||||
}
|
||||
|
||||
const draft =
|
||||
config.input_draft !== undefined
|
||||
? config.input_draft
|
||||
: existingRelease.draft;
|
||||
const draft = config.input_draft !== undefined ? config.input_draft : existingRelease.draft;
|
||||
const prerelease =
|
||||
config.input_prerelease !== undefined
|
||||
? config.input_prerelease
|
||||
: existingRelease.prerelease;
|
||||
config.input_prerelease !== undefined ? config.input_prerelease : existingRelease.prerelease;
|
||||
|
||||
const make_latest = config.input_make_latest;
|
||||
|
||||
|
@ -377,13 +351,11 @@ async function createRelease(
|
|||
const prerelease = config.input_prerelease;
|
||||
const target_commitish = config.input_target_commitish;
|
||||
const make_latest = config.input_make_latest;
|
||||
let commitMessage: string = "";
|
||||
let commitMessage: string = '';
|
||||
if (target_commitish) {
|
||||
commitMessage = ` using commit "${target_commitish}"`;
|
||||
}
|
||||
console.log(
|
||||
`👩🏭 Creating new GitHub release for tag ${tag_name}${commitMessage}...`,
|
||||
);
|
||||
console.log(`👩🏭 Creating new GitHub release for tag ${tag_name}${commitMessage}...`);
|
||||
try {
|
||||
let release = await releaser.createRelease({
|
||||
owner,
|
||||
|
@ -407,16 +379,16 @@ async function createRelease(
|
|||
switch (error.status) {
|
||||
case 403:
|
||||
console.log(
|
||||
"Skip retry — your GitHub token/PAT does not have the required permission to create a release",
|
||||
'Skip retry — your GitHub token/PAT does not have the required permission to create a release',
|
||||
);
|
||||
throw error;
|
||||
|
||||
case 404:
|
||||
console.log("Skip retry - discussion category mismatch");
|
||||
console.log('Skip retry - discussion category mismatch');
|
||||
throw error;
|
||||
|
||||
case 422:
|
||||
console.log("Skip retry - validation failed");
|
||||
console.log('Skip retry - validation failed');
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
|
54
src/main.ts
54
src/main.ts
|
@ -1,24 +1,14 @@
|
|||
import { setFailed, setOutput } from "@actions/core";
|
||||
import { getOctokit } from "@actions/github";
|
||||
import { GitHubReleaser, release, upload } from "./github";
|
||||
import {
|
||||
isTag,
|
||||
parseConfig,
|
||||
paths,
|
||||
unmatchedPatterns,
|
||||
uploadUrl,
|
||||
} from "./util";
|
||||
import { setFailed, setOutput } from '@actions/core';
|
||||
import { getOctokit } from '@actions/github';
|
||||
import { GitHubReleaser, release, upload } from './github';
|
||||
import { isTag, parseConfig, paths, unmatchedPatterns, uploadUrl } from './util';
|
||||
|
||||
import { env } from "process";
|
||||
import { env } from 'process';
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const config = parseConfig(env);
|
||||
if (
|
||||
!config.input_tag_name &&
|
||||
!isTag(config.github_ref) &&
|
||||
!config.input_draft
|
||||
) {
|
||||
if (!config.input_tag_name && !isTag(config.github_ref) && !config.input_draft) {
|
||||
throw new Error(`⚠️ GitHub Releases requires a tag`);
|
||||
}
|
||||
if (config.input_files) {
|
||||
|
@ -44,9 +34,7 @@ async function run() {
|
|||
//new oktokit(
|
||||
throttle: {
|
||||
onRateLimit: (retryAfter, options) => {
|
||||
console.warn(
|
||||
`Request quota exhausted for request ${options.method} ${options.url}`,
|
||||
);
|
||||
console.warn(`Request quota exhausted for request ${options.method} ${options.url}`);
|
||||
if (options.request.retryCount === 0) {
|
||||
// only retries once
|
||||
console.log(`Retrying after ${retryAfter} seconds!`);
|
||||
|
@ -55,9 +43,7 @@ async function run() {
|
|||
},
|
||||
onAbuseLimit: (retryAfter, options) => {
|
||||
// does not retry, only logs a warning
|
||||
console.warn(
|
||||
`Abuse detected for request ${options.method} ${options.url}`,
|
||||
);
|
||||
console.warn(`Abuse detected for request ${options.method} ${options.url}`);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -67,25 +53,15 @@ async function run() {
|
|||
const files = paths(config.input_files);
|
||||
if (files.length == 0) {
|
||||
if (config.input_fail_on_unmatched_files) {
|
||||
throw new Error(
|
||||
`⚠️ ${config.input_files} does not include a valid file.`,
|
||||
);
|
||||
throw new Error(`⚠️ ${config.input_files} does not include a valid file.`);
|
||||
} else {
|
||||
console.warn(
|
||||
`🤔 ${config.input_files} does not include a valid file.`,
|
||||
);
|
||||
console.warn(`🤔 ${config.input_files} does not include a valid file.`);
|
||||
}
|
||||
}
|
||||
const currentAssets = rel.assets;
|
||||
|
||||
const uploadFile = async (path) => {
|
||||
const json = await upload(
|
||||
config,
|
||||
gh,
|
||||
uploadUrl(rel.upload_url),
|
||||
path,
|
||||
currentAssets,
|
||||
);
|
||||
const json = await upload(config, gh, uploadUrl(rel.upload_url), path, currentAssets);
|
||||
if (json) {
|
||||
delete json.uploader;
|
||||
}
|
||||
|
@ -103,12 +79,12 @@ async function run() {
|
|||
}
|
||||
|
||||
const assets = results.filter(Boolean);
|
||||
setOutput("assets", assets);
|
||||
setOutput('assets', assets);
|
||||
}
|
||||
console.log(`🎉 Release ready at ${rel.html_url}`);
|
||||
setOutput("url", rel.html_url);
|
||||
setOutput("id", rel.id.toString());
|
||||
setOutput("upload_url", rel.upload_url);
|
||||
setOutput('url', rel.html_url);
|
||||
setOutput('id', rel.id.toString());
|
||||
setOutput('upload_url', rel.upload_url);
|
||||
} catch (error) {
|
||||
setFailed(error.message);
|
||||
}
|
||||
|
|
60
src/util.ts
60
src/util.ts
|
@ -1,5 +1,5 @@
|
|||
import * as glob from "glob";
|
||||
import { statSync, readFileSync } from "fs";
|
||||
import * as glob from 'glob';
|
||||
import { statSync, readFileSync } from 'fs';
|
||||
|
||||
export interface Config {
|
||||
github_token: string;
|
||||
|
@ -21,11 +21,11 @@ export interface Config {
|
|||
input_discussion_category_name?: string;
|
||||
input_generate_release_notes?: boolean;
|
||||
input_append_body?: boolean;
|
||||
input_make_latest: "true" | "false" | "legacy" | undefined;
|
||||
input_make_latest: 'true' | 'false' | 'legacy' | undefined;
|
||||
}
|
||||
|
||||
export const uploadUrl = (url: string): string => {
|
||||
const templateMarkerPos = url.indexOf("{");
|
||||
const templateMarkerPos = url.indexOf('{');
|
||||
if (templateMarkerPos > -1) {
|
||||
return url.substring(0, templateMarkerPos);
|
||||
}
|
||||
|
@ -34,8 +34,7 @@ export const uploadUrl = (url: string): string => {
|
|||
|
||||
export const releaseBody = (config: Config): string | undefined => {
|
||||
return (
|
||||
(config.input_body_path &&
|
||||
readFileSync(config.input_body_path).toString("utf8")) ||
|
||||
(config.input_body_path && readFileSync(config.input_body_path).toString('utf8')) ||
|
||||
config.input_body
|
||||
);
|
||||
};
|
||||
|
@ -46,7 +45,7 @@ export const parseInputFiles = (files: string): string[] => {
|
|||
return files.split(/\r?\n/).reduce<string[]>(
|
||||
(acc, line) =>
|
||||
acc
|
||||
.concat(line.split(","))
|
||||
.concat(line.split(','))
|
||||
.filter((pat) => pat)
|
||||
.map((pat) => pat.trim()),
|
||||
[],
|
||||
|
@ -55,38 +54,31 @@ export const parseInputFiles = (files: string): string[] => {
|
|||
|
||||
export const parseConfig = (env: Env): Config => {
|
||||
return {
|
||||
github_token: env.GITHUB_TOKEN || env.INPUT_TOKEN || "",
|
||||
github_ref: env.GITHUB_REF || "",
|
||||
github_repository: env.INPUT_REPOSITORY || env.GITHUB_REPOSITORY || "",
|
||||
github_token: env.GITHUB_TOKEN || env.INPUT_TOKEN || '',
|
||||
github_ref: env.GITHUB_REF || '',
|
||||
github_repository: env.INPUT_REPOSITORY || env.GITHUB_REPOSITORY || '',
|
||||
input_name: env.INPUT_NAME,
|
||||
input_tag_name: env.INPUT_TAG_NAME?.trim(),
|
||||
input_body: env.INPUT_BODY,
|
||||
input_body_path: env.INPUT_BODY_PATH,
|
||||
input_files: parseInputFiles(env.INPUT_FILES || ""),
|
||||
input_files: parseInputFiles(env.INPUT_FILES || ''),
|
||||
input_overwrite_files: env.INPUT_OVERWRITE_FILES
|
||||
? env.INPUT_OVERWRITE_FILES == "true"
|
||||
? env.INPUT_OVERWRITE_FILES == 'true'
|
||||
: undefined,
|
||||
input_draft: env.INPUT_DRAFT ? env.INPUT_DRAFT === "true" : undefined,
|
||||
input_preserve_order: env.INPUT_PRESERVE_ORDER
|
||||
? env.INPUT_PRESERVE_ORDER == "true"
|
||||
: undefined,
|
||||
input_prerelease: env.INPUT_PRERELEASE
|
||||
? env.INPUT_PRERELEASE == "true"
|
||||
: undefined,
|
||||
input_fail_on_unmatched_files: env.INPUT_FAIL_ON_UNMATCHED_FILES == "true",
|
||||
input_draft: env.INPUT_DRAFT ? env.INPUT_DRAFT === 'true' : undefined,
|
||||
input_preserve_order: env.INPUT_PRESERVE_ORDER ? env.INPUT_PRESERVE_ORDER == 'true' : undefined,
|
||||
input_prerelease: env.INPUT_PRERELEASE ? env.INPUT_PRERELEASE == 'true' : undefined,
|
||||
input_fail_on_unmatched_files: env.INPUT_FAIL_ON_UNMATCHED_FILES == 'true',
|
||||
input_target_commitish: env.INPUT_TARGET_COMMITISH || undefined,
|
||||
input_discussion_category_name:
|
||||
env.INPUT_DISCUSSION_CATEGORY_NAME || undefined,
|
||||
input_generate_release_notes: env.INPUT_GENERATE_RELEASE_NOTES == "true",
|
||||
input_append_body: env.INPUT_APPEND_BODY == "true",
|
||||
input_discussion_category_name: env.INPUT_DISCUSSION_CATEGORY_NAME || undefined,
|
||||
input_generate_release_notes: env.INPUT_GENERATE_RELEASE_NOTES == 'true',
|
||||
input_append_body: env.INPUT_APPEND_BODY == 'true',
|
||||
input_make_latest: parseMakeLatest(env.INPUT_MAKE_LATEST),
|
||||
};
|
||||
};
|
||||
|
||||
const parseMakeLatest = (
|
||||
value: string | undefined,
|
||||
): "true" | "false" | "legacy" | undefined => {
|
||||
if (value === "true" || value === "false" || value === "legacy") {
|
||||
const parseMakeLatest = (value: string | undefined): 'true' | 'false' | 'legacy' | undefined => {
|
||||
if (value === 'true' || value === 'false' || value === 'legacy') {
|
||||
return value;
|
||||
}
|
||||
return undefined;
|
||||
|
@ -94,26 +86,22 @@ const parseMakeLatest = (
|
|||
|
||||
export const paths = (patterns: string[]): string[] => {
|
||||
return patterns.reduce((acc: string[], pattern: string): string[] => {
|
||||
return acc.concat(
|
||||
glob.sync(pattern).filter((path) => statSync(path).isFile()),
|
||||
);
|
||||
return acc.concat(glob.sync(pattern).filter((path) => statSync(path).isFile()));
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const unmatchedPatterns = (patterns: string[]): string[] => {
|
||||
return patterns.reduce((acc: string[], pattern: string): string[] => {
|
||||
return acc.concat(
|
||||
glob.sync(pattern).filter((path) => statSync(path).isFile()).length == 0
|
||||
? [pattern]
|
||||
: [],
|
||||
glob.sync(pattern).filter((path) => statSync(path).isFile()).length == 0 ? [pattern] : [],
|
||||
);
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const isTag = (ref: string): boolean => {
|
||||
return ref.startsWith("refs/tags/");
|
||||
return ref.startsWith('refs/tags/');
|
||||
};
|
||||
|
||||
export const alignAssetName = (assetName: string): string => {
|
||||
return assetName.replace(/ /g, ".");
|
||||
return assetName.replace(/ /g, '.');
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue