From c8d8e91662b5e1f5381235b7b86c78e3b07cb440 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 11 Jun 2025 11:34:43 -0400 Subject: [PATCH] chore: add prettier config (#633) Signed-off-by: Rui Chen --- .prettierignore | 16 +++ .prettierrc.js | 11 ++ __tests__/github.test.ts | 144 ++++++++---------------- __tests__/util.test.ts | 235 ++++++++++++++++++++------------------- src/github.ts | 108 +++++++----------- src/main.ts | 54 +++------ src/util.ts | 60 ++++------ 7 files changed, 273 insertions(+), 355 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.js diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ceb484e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,16 @@ +# Build outputs +dist/ +lib/ +coverage/ + +# Dependencies +node_modules/ + +# Misc +.github/ +*.log +.DS_Store +__tests__/release.txt + +# Package files +package-lock.json diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..d27b0d2 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,11 @@ +/** + * @type {import('prettier').Config} + */ +module.exports = { + trailingComma: 'all', + tabWidth: 2, + semi: true, + singleQuote: true, + printWidth: 100, + bracketSpacing: true, +}; diff --git a/__tests__/github.test.ts b/__tests__/github.test.ts index 361db08..3fcc592 100644 --- a/__tests__/github.test.ts +++ b/__tests__/github.test.ts @@ -1,62 +1,56 @@ -import { - asset, - findTagFromReleases, - mimeOrDefault, - Release, - Releaser, -} from "../src/github"; +import { asset, findTagFromReleases, mimeOrDefault, Release, Releaser } from '../src/github'; -import { assert, describe, it } from "vitest"; +import { assert, describe, it } from 'vitest'; -describe("github", () => { - describe("mimeOrDefault", () => { - it("returns a specific mime for common path", async () => { - assert.equal(mimeOrDefault("foo.tar.gz"), "application/gzip"); +describe('github', () => { + describe('mimeOrDefault', () => { + it('returns a specific mime for common path', async () => { + assert.equal(mimeOrDefault('foo.tar.gz'), 'application/gzip'); }); - it("returns default mime for uncommon path", async () => { - assert.equal(mimeOrDefault("foo.uncommon"), "application/octet-stream"); + it('returns default mime for uncommon path', async () => { + assert.equal(mimeOrDefault('foo.uncommon'), 'application/octet-stream'); }); }); - describe("asset", () => { - it("derives asset info from a path", async () => { - const { name, mime, size } = asset("tests/data/foo/bar.txt"); - assert.equal(name, "bar.txt"); - assert.equal(mime, "text/plain"); + describe('asset', () => { + it('derives asset info from a path', async () => { + const { name, mime, size } = asset('tests/data/foo/bar.txt'); + assert.equal(name, 'bar.txt'); + assert.equal(mime, 'text/plain'); assert.equal(size, 10); }); }); - describe("findTagFromReleases", () => { - const owner = "owner"; - const repo = "repo"; + describe('findTagFromReleases', () => { + const owner = 'owner'; + const repo = 'repo'; const mockRelease: Release = { id: 1, upload_url: `https://api.github.com/repos/${owner}/${repo}/releases/1/assets`, html_url: `https://github.com/${owner}/${repo}/releases/tag/v1.0.0`, - tag_name: "v1.0.0", - name: "Test Release", - body: "Test body", - target_commitish: "main", + tag_name: 'v1.0.0', + name: 'Test Release', + body: 'Test body', + target_commitish: 'main', draft: false, prerelease: false, assets: [], } as const; const mockReleaser: Releaser = { - getReleaseByTag: () => Promise.reject("Not implemented"), - createRelease: () => Promise.reject("Not implemented"), - updateRelease: () => Promise.reject("Not implemented"), + getReleaseByTag: () => Promise.reject('Not implemented'), + createRelease: () => Promise.reject('Not implemented'), + updateRelease: () => Promise.reject('Not implemented'), allReleases: async function* () { yield { data: [mockRelease] }; }, } as const; - describe("when the tag_name is not an empty string", () => { - const targetTag = "v1.0.0"; + describe('when the tag_name is not an empty string', () => { + const targetTag = 'v1.0.0'; - it("finds a matching release in first batch of results", async () => { + it('finds a matching release in first batch of results', async () => { const targetRelease = { ...mockRelease, owner, @@ -67,7 +61,7 @@ describe("github", () => { ...mockRelease, owner, repo, - tag_name: "v1.0.1", + tag_name: 'v1.0.1', }; const releaser = { @@ -78,17 +72,12 @@ describe("github", () => { }, }; - const result = await findTagFromReleases( - releaser, - owner, - repo, - targetTag, - ); + const result = await findTagFromReleases(releaser, owner, repo, targetTag); assert.deepStrictEqual(result, targetRelease); }); - it("finds a matching release in second batch of results", async () => { + it('finds a matching release in second batch of results', async () => { const targetRelease = { ...mockRelease, owner, @@ -99,7 +88,7 @@ describe("github", () => { ...mockRelease, owner, repo, - tag_name: "v1.0.1", + tag_name: 'v1.0.1', }; const releaser = { @@ -110,21 +99,16 @@ describe("github", () => { }, }; - const result = await findTagFromReleases( - releaser, - owner, - repo, - targetTag, - ); + const result = await findTagFromReleases(releaser, owner, repo, targetTag); assert.deepStrictEqual(result, targetRelease); }); - it("returns undefined when a release is not found in any batch", async () => { + it('returns undefined when a release is not found in any batch', async () => { const otherRelease = { ...mockRelease, owner, repo, - tag_name: "v1.0.1", + tag_name: 'v1.0.1', }; const releaser = { ...mockReleaser, @@ -134,17 +118,12 @@ describe("github", () => { }, }; - const result = await findTagFromReleases( - releaser, - owner, - repo, - targetTag, - ); + const result = await findTagFromReleases(releaser, owner, repo, targetTag); assert.strictEqual(result, undefined); }); - it("returns undefined when no releases are returned", async () => { + it('returns undefined when no releases are returned', async () => { const releaser = { ...mockReleaser, allReleases: async function* () { @@ -152,21 +131,16 @@ describe("github", () => { }, }; - const result = await findTagFromReleases( - releaser, - owner, - repo, - targetTag, - ); + const result = await findTagFromReleases(releaser, owner, repo, targetTag); assert.strictEqual(result, undefined); }); }); - describe("when the tag_name is an empty string", () => { - const emptyTag = ""; + describe('when the tag_name is an empty string', () => { + const emptyTag = ''; - it("finds a matching release in first batch of results", async () => { + it('finds a matching release in first batch of results', async () => { const targetRelease = { ...mockRelease, owner, @@ -177,7 +151,7 @@ describe("github", () => { ...mockRelease, owner, repo, - tag_name: "v1.0.1", + tag_name: 'v1.0.1', }; const releaser = { @@ -188,17 +162,12 @@ describe("github", () => { }, }; - const result = await findTagFromReleases( - releaser, - owner, - repo, - emptyTag, - ); + const result = await findTagFromReleases(releaser, owner, repo, emptyTag); assert.deepStrictEqual(result, targetRelease); }); - it("finds a matching release in second batch of results", async () => { + it('finds a matching release in second batch of results', async () => { const targetRelease = { ...mockRelease, owner, @@ -209,7 +178,7 @@ describe("github", () => { ...mockRelease, owner, repo, - tag_name: "v1.0.1", + tag_name: 'v1.0.1', }; const releaser = { @@ -220,21 +189,16 @@ describe("github", () => { }, }; - const result = await findTagFromReleases( - releaser, - owner, - repo, - emptyTag, - ); + const result = await findTagFromReleases(releaser, owner, repo, emptyTag); assert.deepStrictEqual(result, targetRelease); }); - it("returns undefined when a release is not found in any batch", async () => { + it('returns undefined when a release is not found in any batch', async () => { const otherRelease = { ...mockRelease, owner, repo, - tag_name: "v1.0.1", + tag_name: 'v1.0.1', }; const releaser = { ...mockReleaser, @@ -244,17 +208,12 @@ describe("github", () => { }, }; - const result = await findTagFromReleases( - releaser, - owner, - repo, - emptyTag, - ); + const result = await findTagFromReleases(releaser, owner, repo, emptyTag); assert.strictEqual(result, undefined); }); - it("returns undefined when no releases are returned", async () => { + it('returns undefined when no releases are returned', async () => { const releaser = { ...mockReleaser, allReleases: async function* () { @@ -262,12 +221,7 @@ describe("github", () => { }, }; - const result = await findTagFromReleases( - releaser, - owner, - repo, - emptyTag, - ); + const result = await findTagFromReleases(releaser, owner, repo, emptyTag); assert.strictEqual(result, undefined); }); diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index 2e87a41..87e0ad3 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -7,44 +7,48 @@ import { releaseBody, unmatchedPatterns, uploadUrl, -} from "../src/util"; +} from '../src/util'; -import { assert, describe, expect, it } from "vitest"; +import { assert, describe, expect, it } from 'vitest'; -describe("util", () => { - describe("uploadUrl", () => { - it("strips template", () => { +describe('util', () => { + describe('uploadUrl', () => { + it('strips template', () => { assert.equal( uploadUrl( - "https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets{?name,label}", + 'https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets{?name,label}', ), - "https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets", + 'https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets', ); }); }); - describe("parseInputFiles", () => { - it("parses empty strings", () => { - assert.deepStrictEqual(parseInputFiles(""), []); + describe('parseInputFiles', () => { + it('parses empty strings', () => { + assert.deepStrictEqual(parseInputFiles(''), []); }); - it("parses comma-delimited strings", () => { - assert.deepStrictEqual(parseInputFiles("foo,bar"), ["foo", "bar"]); + it('parses comma-delimited strings', () => { + assert.deepStrictEqual(parseInputFiles('foo,bar'), ['foo', 'bar']); }); - it("parses newline and comma-delimited (and then some)", () => { - assert.deepStrictEqual( - parseInputFiles("foo,bar\nbaz,boom,\n\ndoom,loom "), - ["foo", "bar", "baz", "boom", "doom", "loom"], - ); + it('parses newline and comma-delimited (and then some)', () => { + assert.deepStrictEqual(parseInputFiles('foo,bar\nbaz,boom,\n\ndoom,loom '), [ + 'foo', + 'bar', + 'baz', + 'boom', + 'doom', + 'loom', + ]); }); }); - describe("releaseBody", () => { - it("uses input body", () => { + describe('releaseBody', () => { + it('uses input body', () => { assert.equal( - "foo", + 'foo', releaseBody({ - github_ref: "", - github_repository: "", - github_token: "", - input_body: "foo", + github_ref: '', + github_repository: '', + github_token: '', + input_body: 'foo', input_body_path: undefined, input_draft: false, input_prerelease: false, @@ -60,15 +64,15 @@ describe("util", () => { }), ); }); - it("uses input body path", () => { + it('uses input body path', () => { assert.equal( - "bar", + 'bar', releaseBody({ - github_ref: "", - github_repository: "", - github_token: "", + github_ref: '', + github_repository: '', + github_token: '', input_body: undefined, - input_body_path: "__tests__/release.txt", + input_body_path: '__tests__/release.txt', input_draft: false, input_prerelease: false, input_preserve_order: undefined, @@ -83,15 +87,15 @@ describe("util", () => { }), ); }); - it("defaults to body path when both body and body path are provided", () => { + it('defaults to body path when both body and body path are provided', () => { assert.equal( - "bar", + 'bar', releaseBody({ - github_ref: "", - github_repository: "", - github_token: "", - input_body: "foo", - input_body_path: "__tests__/release.txt", + github_ref: '', + github_repository: '', + github_token: '', + input_body: 'foo', + input_body_path: '__tests__/release.txt', input_draft: false, input_prerelease: false, input_preserve_order: undefined, @@ -107,8 +111,8 @@ describe("util", () => { ); }); }); - describe("parseConfig", () => { - it("parses basic config", () => { + describe('parseConfig', () => { + it('parses basic config', () => { assert.deepStrictEqual( parseConfig({ // note: inputs declared in actions.yml, even when declared not required, @@ -117,13 +121,13 @@ describe("util", () => { // as an empty string !== undefined in terms of what we pass to the api // so we cover that in a test case here to ensure undefined values are actually // resolved as undefined and not empty strings - INPUT_TARGET_COMMITISH: "", - INPUT_DISCUSSION_CATEGORY_NAME: "", + INPUT_TARGET_COMMITISH: '', + INPUT_DISCUSSION_CATEGORY_NAME: '', }), { - github_ref: "", - github_repository: "", - github_token: "", + github_ref: '', + github_repository: '', + github_token: '', input_append_body: false, input_body: undefined, input_body_path: undefined, @@ -143,15 +147,15 @@ describe("util", () => { ); }); - it("parses basic config with commitish", () => { + it('parses basic config with commitish', () => { assert.deepStrictEqual( parseConfig({ - INPUT_TARGET_COMMITISH: "affa18ef97bc9db20076945705aba8c516139abd", + INPUT_TARGET_COMMITISH: 'affa18ef97bc9db20076945705aba8c516139abd', }), { - github_ref: "", - github_repository: "", - github_token: "", + github_ref: '', + github_repository: '', + github_token: '', input_append_body: false, input_body: undefined, input_body_path: undefined, @@ -163,22 +167,22 @@ describe("util", () => { input_name: undefined, input_tag_name: undefined, input_fail_on_unmatched_files: false, - input_target_commitish: "affa18ef97bc9db20076945705aba8c516139abd", + input_target_commitish: 'affa18ef97bc9db20076945705aba8c516139abd', input_discussion_category_name: undefined, input_generate_release_notes: false, input_make_latest: undefined, }, ); }); - it("supports discussion category names", () => { + it('supports discussion category names', () => { assert.deepStrictEqual( parseConfig({ - INPUT_DISCUSSION_CATEGORY_NAME: "releases", + INPUT_DISCUSSION_CATEGORY_NAME: 'releases', }), { - github_ref: "", - github_repository: "", - github_token: "", + github_ref: '', + github_repository: '', + github_token: '', input_append_body: false, input_body: undefined, input_body_path: undefined, @@ -191,22 +195,22 @@ describe("util", () => { input_tag_name: undefined, input_fail_on_unmatched_files: false, input_target_commitish: undefined, - input_discussion_category_name: "releases", + input_discussion_category_name: 'releases', input_generate_release_notes: false, input_make_latest: undefined, }, ); }); - it("supports generating release notes", () => { + it('supports generating release notes', () => { assert.deepStrictEqual( parseConfig({ - INPUT_GENERATE_RELEASE_NOTES: "true", + INPUT_GENERATE_RELEASE_NOTES: 'true', }), { - github_ref: "", - github_repository: "", - github_token: "", + github_ref: '', + github_repository: '', + github_token: '', input_append_body: false, input_body: undefined, input_body_path: undefined, @@ -226,19 +230,19 @@ describe("util", () => { ); }); - it("prefers GITHUB_TOKEN over token input for backwards compatibility", () => { + it('prefers GITHUB_TOKEN over token input for backwards compatibility', () => { assert.deepStrictEqual( parseConfig({ - INPUT_DRAFT: "false", - INPUT_PRERELEASE: "true", - INPUT_PRESERVE_ORDER: "true", - GITHUB_TOKEN: "env-token", - INPUT_TOKEN: "input-token", + INPUT_DRAFT: 'false', + INPUT_PRERELEASE: 'true', + INPUT_PRESERVE_ORDER: 'true', + GITHUB_TOKEN: 'env-token', + INPUT_TOKEN: 'input-token', }), { - github_ref: "", - github_repository: "", - github_token: "env-token", + github_ref: '', + github_repository: '', + github_token: 'env-token', input_append_body: false, input_body: undefined, input_body_path: undefined, @@ -257,17 +261,17 @@ describe("util", () => { }, ); }); - it("uses input token as the source of GITHUB_TOKEN by default", () => { + it('uses input token as the source of GITHUB_TOKEN by default', () => { assert.deepStrictEqual( parseConfig({ - INPUT_DRAFT: "false", - INPUT_PRERELEASE: "true", - INPUT_TOKEN: "input-token", + INPUT_DRAFT: 'false', + INPUT_PRERELEASE: 'true', + INPUT_TOKEN: 'input-token', }), { - github_ref: "", - github_repository: "", - github_token: "input-token", + github_ref: '', + github_repository: '', + github_token: 'input-token', input_append_body: false, input_body: undefined, input_body_path: undefined, @@ -286,16 +290,16 @@ describe("util", () => { }, ); }); - it("parses basic config with draft and prerelease", () => { + it('parses basic config with draft and prerelease', () => { assert.deepStrictEqual( parseConfig({ - INPUT_DRAFT: "false", - INPUT_PRERELEASE: "true", + INPUT_DRAFT: 'false', + INPUT_PRERELEASE: 'true', }), { - github_ref: "", - github_repository: "", - github_token: "", + github_ref: '', + github_repository: '', + github_token: '', input_append_body: false, input_body: undefined, input_body_path: undefined, @@ -314,15 +318,15 @@ describe("util", () => { }, ); }); - it("parses basic config where make_latest is passed", () => { + it('parses basic config where make_latest is passed', () => { assert.deepStrictEqual( parseConfig({ - INPUT_MAKE_LATEST: "false", + INPUT_MAKE_LATEST: 'false', }), { - github_ref: "", - github_repository: "", - github_token: "", + github_ref: '', + github_repository: '', + github_token: '', input_append_body: false, input_body: undefined, input_body_path: undefined, @@ -337,19 +341,19 @@ describe("util", () => { input_target_commitish: undefined, input_discussion_category_name: undefined, input_generate_release_notes: false, - input_make_latest: "false", + input_make_latest: 'false', }, ); }); - it("parses basic config with append_body", () => { + it('parses basic config with append_body', () => { assert.deepStrictEqual( parseConfig({ - INPUT_APPEND_BODY: "true", + INPUT_APPEND_BODY: 'true', }), { - github_ref: "", - github_repository: "", - github_token: "", + github_ref: '', + github_repository: '', + github_token: '', input_append_body: true, input_body: undefined, input_body_path: undefined, @@ -369,46 +373,43 @@ describe("util", () => { ); }); }); - describe("isTag", () => { - it("returns true for tags", async () => { - assert.equal(isTag("refs/tags/foo"), true); + describe('isTag', () => { + it('returns true for tags', async () => { + assert.equal(isTag('refs/tags/foo'), true); }); - it("returns false for other kinds of refs", async () => { - assert.equal(isTag("refs/heads/master"), false); + it('returns false for other kinds of refs', async () => { + assert.equal(isTag('refs/heads/master'), false); }); }); - describe("paths", () => { - it("resolves files given a set of paths", async () => { - assert.deepStrictEqual( - paths(["tests/data/**/*", "tests/data/does/not/exist/*"]), - ["tests/data/foo/bar.txt"], - ); + describe('paths', () => { + it('resolves files given a set of paths', async () => { + assert.deepStrictEqual(paths(['tests/data/**/*', 'tests/data/does/not/exist/*']), [ + 'tests/data/foo/bar.txt', + ]); }); }); - describe("unmatchedPatterns", () => { + describe('unmatchedPatterns', () => { it("returns the patterns that don't match any files", async () => { assert.deepStrictEqual( - unmatchedPatterns(["tests/data/**/*", "tests/data/does/not/exist/*"]), - ["tests/data/does/not/exist/*"], + unmatchedPatterns(['tests/data/**/*', 'tests/data/does/not/exist/*']), + ['tests/data/does/not/exist/*'], ); }); }); - describe("replaceSpacesWithDots", () => { - it("replaces all spaces with dots", () => { - expect(alignAssetName("John Doe.bla")).toBe("John.Doe.bla"); + describe('replaceSpacesWithDots', () => { + it('replaces all spaces with dots', () => { + expect(alignAssetName('John Doe.bla')).toBe('John.Doe.bla'); }); - it("handles names with multiple spaces", () => { - expect(alignAssetName("John William Doe.bla")).toBe( - "John.William.Doe.bla", - ); + it('handles names with multiple spaces', () => { + expect(alignAssetName('John William Doe.bla')).toBe('John.William.Doe.bla'); }); - it("returns the same string if there are no spaces", () => { - expect(alignAssetName("JohnDoe")).toBe("JohnDoe"); + it('returns the same string if there are no spaces', () => { + expect(alignAssetName('JohnDoe')).toBe('JohnDoe'); }); }); }); diff --git a/src/github.ts b/src/github.ts index e54959b..9fc103d 100644 --- a/src/github.ts +++ b/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; @@ -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 => { - 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 => { 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; } diff --git a/src/main.ts b/src/main.ts index eb25cde..b645a18 100644 --- a/src/main.ts +++ b/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); } diff --git a/src/util.ts b/src/util.ts index 85099ad..c6e2b4d 100644 --- a/src/util.ts +++ b/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( (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, '.'); };