Files
action-gh-release/src/util.ts
T

222 lines
6.5 KiB
TypeScript
Raw Normal View History

2025-06-11 11:34:43 -04:00
import * as glob from 'glob';
import { statSync, readFileSync } from 'fs';
2026-03-15 00:09:15 -04:00
import { homedir } from 'os';
import * as pathLib from 'path';
2019-09-09 17:10:07 +09:00
export interface Config {
2019-09-09 21:20:59 +09:00
github_token: string;
github_ref: string;
github_repository: string;
// user provided
input_name?: string;
2020-01-09 15:06:19 +09:00
input_tag_name?: string;
2020-12-20 14:44:30 -05:00
input_repository?: string;
2019-09-09 21:20:59 +09:00
input_body?: string;
input_body_path?: string;
input_files?: string[];
input_working_directory?: string;
input_overwrite_files?: boolean;
2019-09-09 21:20:59 +09:00
input_draft?: boolean;
2024-11-11 21:14:02 +01:00
input_preserve_order?: boolean;
2019-09-17 23:30:36 +09:00
input_prerelease?: boolean;
2020-06-24 23:11:41 -07:00
input_fail_on_unmatched_files?: boolean;
2021-05-03 02:43:58 +02:00
input_target_commitish?: string;
2021-08-08 02:07:02 -04:00
input_discussion_category_name?: string;
input_generate_release_notes?: boolean;
input_previous_tag?: string;
2022-01-23 00:40:31 +08:00
input_append_body?: boolean;
2025-06-11 11:34:43 -04:00
input_make_latest: 'true' | 'false' | 'legacy' | undefined;
2019-09-09 17:10:07 +09:00
}
export const errorMessage = (error: unknown): string => {
if (error instanceof Error) {
return error.message;
}
if (
typeof error === 'object' &&
error !== null &&
'message' in error &&
typeof error.message === 'string'
) {
return error.message;
}
if (error === null || error === undefined) {
return 'Unknown error';
}
return String(error);
};
2021-08-08 00:28:01 -04:00
export const uploadUrl = (url: string): string => {
2025-06-11 11:34:43 -04:00
const templateMarkerPos = url.indexOf('{');
2021-08-08 00:28:01 -04:00
if (templateMarkerPos > -1) {
return url.substring(0, templateMarkerPos);
}
return url;
};
export const releaseBody = (config: Config): string | undefined => {
if (config.input_body_path) {
try {
const contents = readFileSync(config.input_body_path, 'utf8');
return contents;
} catch (err: any) {
console.warn(
`⚠️ Failed to read body_path "${config.input_body_path}" (${err?.code ?? 'ERR'}). Falling back to 'body' input.`,
);
}
}
return config.input_body;
};
2019-09-09 17:10:07 +09:00
type Env = { [key: string]: string | undefined };
const smartSplit = (input: string): string[] => {
const result: string[] = [];
let current = '';
let braceDepth = 0;
for (const ch of input) {
if (ch === '{') {
braceDepth++;
}
if (ch === '}') {
braceDepth--;
}
if (ch === ',' && braceDepth === 0) {
if (current.trim()) {
result.push(current.trim());
}
current = '';
} else {
current += ch;
}
}
if (current.trim()) {
result.push(current.trim());
}
return result;
};
export const parseInputFiles = (files: string): string[] => {
return files
.split(/\r?\n/)
.flatMap((line) => smartSplit(line))
.filter((pat) => pat.trim() !== '');
};
const parseToken = (env: Env): string => {
const inputToken = env.INPUT_TOKEN?.trim();
if (inputToken) {
return inputToken;
}
return env.GITHUB_TOKEN?.trim() || '';
};
2019-09-09 17:10:07 +09:00
export const parseConfig = (env: Env): Config => {
return {
github_token: parseToken(env),
2025-06-11 11:34:43 -04:00
github_ref: env.GITHUB_REF || '',
github_repository: env.INPUT_REPOSITORY || env.GITHUB_REPOSITORY || '',
2019-09-09 17:10:07 +09:00
input_name: env.INPUT_NAME,
2026-03-15 00:05:22 -04:00
input_tag_name: normalizeTagName(env.INPUT_TAG_NAME?.trim()),
2019-09-09 17:10:07 +09:00
input_body: env.INPUT_BODY,
input_body_path: env.INPUT_BODY_PATH,
2025-06-11 11:34:43 -04:00
input_files: parseInputFiles(env.INPUT_FILES || ''),
input_working_directory: env.INPUT_WORKING_DIRECTORY || undefined,
input_overwrite_files: env.INPUT_OVERWRITE_FILES
2025-06-11 11:34:43 -04:00
? env.INPUT_OVERWRITE_FILES == 'true'
: undefined,
2025-06-11 11:34:43 -04:00
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,
2025-06-11 11:34:43 -04:00
input_discussion_category_name: env.INPUT_DISCUSSION_CATEGORY_NAME || undefined,
input_generate_release_notes: env.INPUT_GENERATE_RELEASE_NOTES == 'true',
input_previous_tag: env.INPUT_PREVIOUS_TAG?.trim() || undefined,
2025-06-11 11:34:43 -04:00
input_append_body: env.INPUT_APPEND_BODY == 'true',
input_make_latest: parseMakeLatest(env.INPUT_MAKE_LATEST),
2019-09-09 21:20:59 +09:00
};
};
2019-09-09 17:10:07 +09:00
2025-06-11 11:34:43 -04:00
const parseMakeLatest = (value: string | undefined): 'true' | 'false' | 'legacy' | undefined => {
if (value === 'true' || value === 'false' || value === 'legacy') {
return value;
}
return undefined;
};
export const normalizeGlobPattern = (
pattern: string,
platform: NodeJS.Platform = process.platform,
): string => {
if (platform === 'win32') {
return pattern.replace(/\\/g, '/');
}
return pattern;
};
2026-03-15 00:09:15 -04:00
export const expandHomePattern = (pattern: string, homeDirectory: string = homedir()): string => {
if (pattern === '~') {
return homeDirectory;
}
if (pattern.startsWith('~/') || pattern.startsWith('~\\')) {
return pathLib.join(homeDirectory, pattern.slice(2));
}
return pattern;
};
export const normalizeFilePattern = (
pattern: string,
platform: NodeJS.Platform = process.platform,
homeDirectory: string = homedir(),
): string => {
return normalizeGlobPattern(expandHomePattern(pattern, homeDirectory), platform);
};
export const paths = (patterns: string[], cwd?: string): string[] => {
2019-09-09 17:10:07 +09:00
return patterns.reduce((acc: string[], pattern: string): string[] => {
2026-03-15 00:09:15 -04:00
const matches = glob.sync(normalizeFilePattern(pattern), { cwd, dot: true, absolute: false });
const resolved = matches
2026-03-15 00:09:15 -04:00
.map((p) => (cwd && !pathLib.isAbsolute(p) ? pathLib.join(cwd, p) : p))
.filter((p) => {
try {
return statSync(p).isFile();
} catch {
return false;
}
});
return acc.concat(resolved);
2019-09-09 21:20:59 +09:00
}, []);
};
2019-09-09 17:10:07 +09:00
export const unmatchedPatterns = (patterns: string[], cwd?: string): string[] => {
2020-06-24 23:11:41 -07:00
return patterns.reduce((acc: string[], pattern: string): string[] => {
2026-03-15 00:09:15 -04:00
const matches = glob.sync(normalizeFilePattern(pattern), { cwd, dot: true, absolute: false });
const files = matches.filter((p) => {
try {
2026-03-15 00:09:15 -04:00
const full = cwd && !pathLib.isAbsolute(p) ? pathLib.join(cwd, p) : p;
return statSync(full).isFile();
} catch {
return false;
}
});
return acc.concat(files.length == 0 ? [pattern] : []);
2020-06-24 23:11:41 -07:00
}, []);
};
2019-09-09 17:10:07 +09:00
export const isTag = (ref: string): boolean => {
2025-06-11 11:34:43 -04:00
return ref.startsWith('refs/tags/');
2019-09-09 21:20:59 +09:00
};
2026-03-15 00:05:22 -04:00
export const normalizeTagName = (tag: string | undefined): string | undefined => {
if (!tag) {
return tag;
}
return isTag(tag) ? tag.replace('refs/tags/', '') : tag;
};
export const alignAssetName = (assetName: string): string => {
2025-06-11 11:34:43 -04:00
return assetName.replace(/ /g, '.');
};