From bdbb129ca8b4b39af3cb541a9b33e6fdb46a5b4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 19:53:27 +0900 Subject: [PATCH 01/10] build(deps): Bump denoland/setup-deno from 1.3.0 to 1.4.0 (#39) Bumps denoland/setup-deno from 1.3.0 to 1.4.0. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd2120b..982e4e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: ๐Ÿฆ• Setup Deno - uses: denoland/setup-deno@5fae568d37c3b73449009674875529a984555dd1 # v1.3.0 + uses: denoland/setup-deno@916edb9a40fd86d1ff57b758807ebe57242f7407 # v1.4.0 with: deno-version: v1.x From 6687934ab08a2774525b560752360b8ae6bca386 Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Sat, 24 Aug 2024 15:37:11 +0900 Subject: [PATCH 02/10] refactor(server): Rewrite with @hono/hono (#41) Use Hono, a more useful web framework. --- deno.json | 6 +- deno.lock | 146 ++++-------------- .../{middlewares => middleware}/content.ts | 13 +- src/libs/middleware/content_test.ts | 31 ++++ src/libs/middleware/redirect.ts | 12 ++ src/libs/middleware/redirect_test.ts | 21 +++ src/libs/middlewares/content_test.ts | 35 ----- src/libs/middlewares/redirect.ts | 30 ---- src/libs/middlewares/redirect_test.ts | 32 ---- src/libs/mod.ts | 4 +- src/libs/test_utils.ts | 3 + src/libs/utils/env.ts | 18 +++ src/router.ts | 14 -- src/router_test.ts | 30 ---- src/server.ts | 40 ++--- src/server_test.ts | 61 ++++++++ 16 files changed, 206 insertions(+), 290 deletions(-) rename src/libs/{middlewares => middleware}/content.ts (60%) create mode 100644 src/libs/middleware/content_test.ts create mode 100644 src/libs/middleware/redirect.ts create mode 100644 src/libs/middleware/redirect_test.ts delete mode 100644 src/libs/middlewares/content_test.ts delete mode 100644 src/libs/middlewares/redirect.ts delete mode 100644 src/libs/middlewares/redirect_test.ts delete mode 100644 src/router.ts delete mode 100644 src/router_test.ts create mode 100644 src/server_test.ts diff --git a/deno.json b/deno.json index 3601d89..cf3dbd1 100644 --- a/deno.json +++ b/deno.json @@ -5,16 +5,14 @@ "run": "deno run --env='.env' --allow-env='REPOSITORY_OWNER,REPOSITORY_NAME,REPOSITORY_PATH,GITHUB_TOKEN' --allow-net='0.0.0.0,api.github.com'", "start": "deno task run ./src/server.ts", "dev": "deno task run --watch ./src/server.ts", - "test": "deno test --allow-env='REPOSITORY_OWNER,REPOSITORY_NAME,REPOSITORY_PATH,GITHUB_TOKEN' --allow-net='api.github.com' --parallel --shuffle", + "test": "deno test --allow-env='REPOSITORY_OWNER,REPOSITORY_NAME,REPOSITORY_PATH,GITHUB_TOKEN' --allow-net='0.0.0.0,api.github.com' --parallel --shuffle", "cov": "deno task test --coverage && deno coverage --lcov > coverage.lcov" }, "imports": { - "@oak/oak": "jsr:@oak/oak@16.1.0", + "@hono/hono": "jsr:@hono/hono@4.5.8", "@octokit/rest": "https://esm.sh/@octokit/rest@21.0.1", "@std/assert": "jsr:@std/assert@1.0.2", - "@std/fmt": "jsr:@std/fmt@1.0.0", "@std/http": "jsr:@std/http@1.0.2", - "@std/http/user-agent": "jsr:@std/http@~0.223/user-agent", "@std/path": "jsr:@std/path@1.0.2" }, "deploy": { diff --git a/deno.lock b/deno.lock index 035607a..69b5ed0 100644 --- a/deno.lock +++ b/deno.lock @@ -2,66 +2,22 @@ "version": "3", "packages": { "specifiers": { - "jsr:@oak/commons@0.11": "jsr:@oak/commons@0.11.0", - "jsr:@oak/oak@16.1.0": "jsr:@oak/oak@16.1.0", - "jsr:@std/assert@0.223": "jsr:@std/assert@0.223.0", - "jsr:@std/assert@0.226": "jsr:@std/assert@0.226.0", + "jsr:@hono/hono@4.5.8": "jsr:@hono/hono@4.5.8", "jsr:@std/assert@1.0.2": "jsr:@std/assert@1.0.2", - "jsr:@std/assert@^0.223.0": "jsr:@std/assert@0.223.0", - "jsr:@std/assert@^0.224.0": "jsr:@std/assert@0.224.0", - "jsr:@std/bytes@0.223": "jsr:@std/bytes@0.223.0", - "jsr:@std/bytes@0.224": "jsr:@std/bytes@0.224.0", - "jsr:@std/bytes@^0.223.0": "jsr:@std/bytes@0.223.0", - "jsr:@std/crypto@0.223": "jsr:@std/crypto@0.223.0", - "jsr:@std/crypto@0.224": "jsr:@std/crypto@0.224.0", - "jsr:@std/encoding@1.0.0-rc.2": "jsr:@std/encoding@1.0.0-rc.2", - "jsr:@std/encoding@^0.223.0": "jsr:@std/encoding@0.223.0", - "jsr:@std/fmt@1.0.0": "jsr:@std/fmt@1.0.0", - "jsr:@std/http@0.223": "jsr:@std/http@0.223.0", - "jsr:@std/http@0.224": "jsr:@std/http@0.224.5", + "jsr:@std/cli@^1.0.3": "jsr:@std/cli@1.0.3", + "jsr:@std/encoding@^1.0.1": "jsr:@std/encoding@1.0.2", + "jsr:@std/fmt@^1.0.0": "jsr:@std/fmt@1.0.0", "jsr:@std/http@1.0.2": "jsr:@std/http@1.0.2", - "jsr:@std/http@~0.223": "jsr:@std/http@0.223.0", "jsr:@std/internal@^1.0.1": "jsr:@std/internal@1.0.1", - "jsr:@std/io@0.223": "jsr:@std/io@0.223.0", - "jsr:@std/media-types@0.223": "jsr:@std/media-types@0.223.0", - "jsr:@std/media-types@0.224": "jsr:@std/media-types@0.224.1", - "jsr:@std/path@0.223": "jsr:@std/path@0.223.0", + "jsr:@std/media-types@^1.0.2": "jsr:@std/media-types@1.0.2", + "jsr:@std/net@^1.0.0": "jsr:@std/net@1.0.0", "jsr:@std/path@1.0.2": "jsr:@std/path@1.0.2", - "npm:path-to-regexp@6.2.1": "npm:path-to-regexp@6.2.1" + "jsr:@std/path@^1.0.2": "jsr:@std/path@1.0.2", + "jsr:@std/streams@^1.0.1": "jsr:@std/streams@1.0.2" }, "jsr": { - "@oak/commons@0.11.0": { - "integrity": "07702bfe5c07cd8144c422022994da1f9fea466b185824f4be63a2b1b1a65125", - "dependencies": [ - "jsr:@std/assert@0.226", - "jsr:@std/bytes@0.224", - "jsr:@std/crypto@0.224", - "jsr:@std/http@0.224", - "jsr:@std/media-types@0.224" - ] - }, - "@oak/oak@16.1.0": { - "integrity": "ab21506555fffeb08dc8f45ff5d28607e8087949ff58bd2964b27df65994480b", - "dependencies": [ - "jsr:@oak/commons@0.11", - "jsr:@std/assert@0.223", - "jsr:@std/bytes@0.223", - "jsr:@std/crypto@0.223", - "jsr:@std/http@0.223", - "jsr:@std/io@0.223", - "jsr:@std/media-types@0.223", - "jsr:@std/path@0.223", - "npm:path-to-regexp@6.2.1" - ] - }, - "@std/assert@0.223.0": { - "integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24" - }, - "@std/assert@0.224.0": { - "integrity": "8643233ec7aec38a940a8264a6e3eed9bfa44e7a71cc6b3c8874213ff401967f" - }, - "@std/assert@0.226.0": { - "integrity": "0dfb5f7c7723c18cec118e080fec76ce15b4c31154b15ad2bd74822603ef75b3" + "@hono/hono@4.5.8": { + "integrity": "60f5b4c61edae2016022d6087b4fc381f378d337f046f56e00a3a3512c7e9c16" }, "@std/assert@1.0.2": { "integrity": "ccacec332958126deaceb5c63ff8b4eaf9f5ed0eac9feccf124110435e59e49c", @@ -69,79 +25,41 @@ "jsr:@std/internal@^1.0.1" ] }, - "@std/bytes@0.223.0": { - "integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8" - }, - "@std/bytes@0.224.0": { - "integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49" - }, - "@std/crypto@0.223.0": { - "integrity": "1aa9555ff56b09e197ad988ea200f84bc6781fd4fd83f3a156ee44449af93000", - "dependencies": [ - "jsr:@std/assert@^0.223.0", - "jsr:@std/encoding@^0.223.0" - ] - }, - "@std/crypto@0.224.0": { - "integrity": "154ef3ff08ef535562ef1a718718c5b2c5fc3808f0f9100daad69e829bfcdf2d", - "dependencies": [ - "jsr:@std/assert@^0.224.0" - ] - }, - "@std/encoding@0.223.0": { - "integrity": "2b5615a75e00337ce113f34cf2f9b8c18182c751a8dcc8b1a2c2fc0e117bef00" + "@std/cli@1.0.3": { + "integrity": "9a0488b5d2e58d29dce106a941eecec7181fae996bf0d2225563f1ca7e4b100c" }, - "@std/encoding@1.0.0-rc.2": { - "integrity": "160d7674a20ebfbccdf610b3801fee91cf6e42d1c106dd46bbaf46e395cd35ef" + "@std/encoding@1.0.2": { + "integrity": "7ed640c777e3275550e2cd937c440acdbebfdcd2d13ef67052f0536bf43e707f" }, "@std/fmt@1.0.0": { "integrity": "8a95c9fdbb61559418ccbc0f536080cf43341655e1444f9d375a66886ceaaa3d" }, - "@std/http@0.223.0": { - "integrity": "15ab8a0c5a7e9d5be017a15b01600f20f66602ceec48b378939fa24fcec522aa", - "dependencies": [ - "jsr:@std/assert@^0.223.0", - "jsr:@std/encoding@^0.223.0" - ] - }, - "@std/http@0.224.5": { - "integrity": "b03b5d1529f6c423badfb82f6640f9f2557b4034cd7c30655ba5bb447ff750a4", + "@std/http@1.0.2": { + "integrity": "e28d612e2b5e0a40704e8c64d9a5d55cf8c6accf6d798cd1cfb43aad02d1e4fe", "dependencies": [ - "jsr:@std/encoding@1.0.0-rc.2" + "jsr:@std/cli@^1.0.3", + "jsr:@std/encoding@^1.0.1", + "jsr:@std/fmt@^1.0.0", + "jsr:@std/media-types@^1.0.2", + "jsr:@std/net@^1.0.0", + "jsr:@std/path@^1.0.2", + "jsr:@std/streams@^1.0.1" ] }, - "@std/http@1.0.2": { - "integrity": "e28d612e2b5e0a40704e8c64d9a5d55cf8c6accf6d798cd1cfb43aad02d1e4fe" - }, "@std/internal@1.0.1": { "integrity": "6f8c7544d06a11dd256c8d6ba54b11ed870aac6c5aeafff499892662c57673e6" }, - "@std/io@0.223.0": { - "integrity": "2d8c3c2ab3a515619b90da2c6ff5ea7b75a94383259ef4d02116b228393f84f1", - "dependencies": [ - "jsr:@std/bytes@^0.223.0" - ] + "@std/media-types@1.0.2": { + "integrity": "abb78dc8f7d88141cba8c4d60fc1e8b421e5c7b0d7c84f2f708bc666cad46784" }, - "@std/media-types@0.223.0": { - "integrity": "84684680c2eb6bc6d9369c6d6f26a49decaf2c7603ff531862dda575d9d6776e" - }, - "@std/media-types@0.224.1": { - "integrity": "9e69a5daed37c5b5c6d3ce4731dc191f80e67f79bed392b0957d1d03b87f11e1" - }, - "@std/path@0.223.0": { - "integrity": "593963402d7e6597f5a6e620931661053572c982fc014000459edc1f93cc3989", - "dependencies": [ - "jsr:@std/assert@^0.223.0" - ] + "@std/net@1.0.0": { + "integrity": "31de7a45f074edee8296d77fde85fef53fed9baa6f3122a38d796af08ecb3fc9" }, "@std/path@1.0.2": { "integrity": "a452174603f8c620bd278a380c596437a9eef50c891c64b85812f735245d9ec7" - } - }, - "npm": { - "path-to-regexp@6.2.1": { - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", - "dependencies": {} + }, + "@std/streams@1.0.2": { + "integrity": "187c3c875675221f5355807a735e51b0f8769caade2cbca6d7f4fa710ea4ace4" } } }, @@ -165,11 +83,9 @@ }, "workspace": { "dependencies": [ - "jsr:@oak/oak@16.1.0", + "jsr:@hono/hono@4.5.8", "jsr:@std/assert@1.0.2", - "jsr:@std/fmt@1.0.0", "jsr:@std/http@1.0.2", - "jsr:@std/http@~0.223", "jsr:@std/path@1.0.2" ] } diff --git a/src/libs/middlewares/content.ts b/src/libs/middleware/content.ts similarity index 60% rename from src/libs/middlewares/content.ts rename to src/libs/middleware/content.ts index 8fdbd43..217a147 100644 --- a/src/libs/middlewares/content.ts +++ b/src/libs/middleware/content.ts @@ -1,12 +1,11 @@ -import type { RouterContext } from "@oak/oak"; import { Octokit } from "@octokit/rest"; +import type { StatusCode } from "@std/http"; import { getRepository, githubToken } from "../utils/env.ts"; -export async function getContent( - ctx: RouterContext, +export async function getContent( ref: string | undefined = undefined, -): Promise { +): Promise<[string, StatusCode]> { const octokit = new Octokit({ auth: githubToken }); const repository = getRepository(); @@ -19,10 +18,8 @@ export async function getContent( ref: ref, }); - ctx.response.status = status; - ctx.response.body = data; + return [data.toString(), status]; } catch (error) { - ctx.response.status = error.status; - ctx.response.body = `โš ๏ธ ${ctx.response.status}: ${error.message}`; + return [`โš ๏ธ ${error.status}: ${error.message}`, error.status]; } } diff --git a/src/libs/middleware/content_test.ts b/src/libs/middleware/content_test.ts new file mode 100644 index 0000000..c8ccd0d --- /dev/null +++ b/src/libs/middleware/content_test.ts @@ -0,0 +1,31 @@ +import { assertEquals, assertExists, assertStringIncludes } from "@std/assert"; +import { STATUS_CODE } from "@std/http/status"; + +import { getContent } from "./content.ts"; +import { exportRepo, testRef, testRepo, unknownRepo } from "../test_utils.ts"; + +Deno.test("Get Content", async (t: Deno.TestContext) => { + await t.step("normal", async () => { + exportRepo(testRepo); + const [data, status] = await getContent(); + + assertExists(data); + assertEquals(status, STATUS_CODE.OK); + }); + + await t.step("with ref", async () => { + exportRepo(testRepo); + const [data, status] = await getContent(testRef); + + assertExists(data); + assertEquals(status, STATUS_CODE.OK); + }); + + await t.step("not found", async () => { + exportRepo(unknownRepo); + const [data, status] = await getContent(); + + assertStringIncludes(data, `โš ๏ธ ${STATUS_CODE.NotFound}:`); + assertEquals(status, STATUS_CODE.NotFound); + }); +}); diff --git a/src/libs/middleware/redirect.ts b/src/libs/middleware/redirect.ts new file mode 100644 index 0000000..98567d6 --- /dev/null +++ b/src/libs/middleware/redirect.ts @@ -0,0 +1,12 @@ +import type { UserAgent } from "@std/http/user-agent"; + +import { getGitHubUrl, getRepository } from "../utils/env.ts"; + +export function redirect( + userAgent: UserAgent, + ref: string = "master", +): URL | null { + const url = getGitHubUrl(getRepository(), ref); + + return userAgent?.browser.name ? url : null; +} diff --git a/src/libs/middleware/redirect_test.ts b/src/libs/middleware/redirect_test.ts new file mode 100644 index 0000000..7027342 --- /dev/null +++ b/src/libs/middleware/redirect_test.ts @@ -0,0 +1,21 @@ +import { assertEquals } from "@std/assert"; + +import { redirect } from "./redirect.ts"; +import { exportRepo, testRef, testRepo, testUserAgent } from "../test_utils.ts"; +import { getGitHubUrl } from "../utils/env.ts"; + +Deno.test("Redirect Detection", async (t: Deno.TestContext) => { + await t.step("normal", () => { + exportRepo(testRepo); + const url: URL | null = redirect(testUserAgent); + + assertEquals(url, getGitHubUrl(testRepo)); + }); + + await t.step("with ref", () => { + exportRepo(testRepo); + const url: URL | null = redirect(testUserAgent, testRef); + + assertEquals(url, getGitHubUrl(testRepo, testRef)); + }); +}); diff --git a/src/libs/middlewares/content_test.ts b/src/libs/middlewares/content_test.ts deleted file mode 100644 index b065e8f..0000000 --- a/src/libs/middlewares/content_test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { type RouterContext, testing } from "@oak/oak"; -import { assertEquals, assertStringIncludes } from "@std/assert"; -import { STATUS_CODE } from "@std/http/status"; - -import { getContent } from "./content.ts"; -import { exportRepo, testRef, testRepo, unknownRepo } from "../test_utils.ts"; - -Deno.test("Get Content", async (t: Deno.TestContext) => { - const ctx: RouterContext = testing.createMockContext(); - - await t.step("normal", async () => { - exportRepo(testRepo); - await getContent(ctx); - - assertEquals(ctx.response.status, STATUS_CODE.OK); - }); - - await t.step("with ref", async () => { - exportRepo(testRepo); - await getContent(ctx, testRef); - - assertEquals(ctx.response.status, STATUS_CODE.OK); - }); - - await t.step("not found", async () => { - exportRepo(unknownRepo); - await getContent(ctx); - - assertEquals(ctx.response.status, STATUS_CODE.NotFound); - assertStringIncludes( - ctx.response.body!.toString(), - `โš ๏ธ ${STATUS_CODE.NotFound}:`, - ); - }); -}); diff --git a/src/libs/middlewares/redirect.ts b/src/libs/middlewares/redirect.ts deleted file mode 100644 index 956ac11..0000000 --- a/src/libs/middlewares/redirect.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { RouterContext } from "@oak/oak"; -import { STATUS_CODE } from "@std/http/status"; -import { UserAgent } from "@std/http/user-agent"; -import { join } from "@std/path"; - -import { getRepository } from "../utils/env.ts"; - -export function redirect( - ctx: RouterContext, - userAgent: UserAgent, - ref: string = "master", -): boolean { - const repository = getRepository(); - const url = new URL( - join( - repository.owner, - repository.name, - "blob", - ref, - repository.path, - ), - "https://github.com", - ); - - if (!userAgent?.browser.name) return false; - - ctx.response.status = STATUS_CODE.PermanentRedirect; - ctx.response.redirect(url); - return true; -} diff --git a/src/libs/middlewares/redirect_test.ts b/src/libs/middlewares/redirect_test.ts deleted file mode 100644 index cb945fb..0000000 --- a/src/libs/middlewares/redirect_test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { type RouterContext, testing } from "@oak/oak"; -import { assertEquals } from "@std/assert"; -import { STATUS_CODE } from "@std/http/status"; -import { UserAgent } from "@std/http/user-agent"; - -import { redirect } from "./redirect.ts"; -import { exportRepo, testRef, testRepo } from "../test_utils.ts"; - -Deno.test("Redirect Detection", async (t: Deno.TestContext) => { - const ctx: RouterContext = testing.createMockContext(); - exportRepo(testRepo); - - await t.step("normal", () => { - redirect(ctx, new UserAgent("Chrome/1.2.3")); - - assertEquals(ctx.response.status, STATUS_CODE.PermanentRedirect); - assertEquals( - ctx.response.headers.get("location"), - `https://github.com/${testRepo.owner}/${testRepo.name}/blob/master/${testRepo.path}`, - ); - }); - - await t.step("with ref", () => { - redirect(ctx, new UserAgent("Chrome/1.2.3"), testRef); - - assertEquals(ctx.response.status, STATUS_CODE.PermanentRedirect); - assertEquals( - ctx.response.headers.get("location"), - `https://github.com/${testRepo.owner}/${testRepo.name}/blob/${testRef}/${testRepo.path}`, - ); - }); -}); diff --git a/src/libs/mod.ts b/src/libs/mod.ts index a7769af..44fff98 100644 --- a/src/libs/mod.ts +++ b/src/libs/mod.ts @@ -1,2 +1,2 @@ -export * from "./middlewares/content.ts"; -export * from "./middlewares/redirect.ts"; +export * from "./middleware/content.ts"; +export * from "./middleware/redirect.ts"; diff --git a/src/libs/test_utils.ts b/src/libs/test_utils.ts index 4362005..1669993 100644 --- a/src/libs/test_utils.ts +++ b/src/libs/test_utils.ts @@ -1,3 +1,4 @@ +import { UserAgent } from "@std/http/user-agent"; import type { Repository } from "./types.ts"; export const testRepo: Repository = { @@ -14,6 +15,8 @@ export const unknownRepo: Repository = { export const testRef = "v1.0.0"; +export const testUserAgent = new UserAgent("Chrome/1.2.3"); + export function exportRepo(repository: Repository) { Deno.env.set("REPOSITORY_OWNER", repository.owner); Deno.env.set("REPOSITORY_NAME", repository.name); diff --git a/src/libs/utils/env.ts b/src/libs/utils/env.ts index 56b33b0..6bc9eff 100644 --- a/src/libs/utils/env.ts +++ b/src/libs/utils/env.ts @@ -1,3 +1,5 @@ +import { join } from "@std/path"; + import type { Repository } from "../types.ts"; export function getRepository(): Repository { @@ -17,3 +19,19 @@ export function getRepository(): Repository { } export const githubToken: string | undefined = Deno.env.get("GITHUB_TOKEN"); + +export function getGitHubUrl( + repository: Repository, + ref: string = "master", +): URL { + return new URL( + join( + repository.owner, + repository.name, + "blob", + ref, + repository.path, + ), + "https://github.com", + ); +} diff --git a/src/router.ts b/src/router.ts deleted file mode 100644 index a9f7f51..0000000 --- a/src/router.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Router, type RouterContext } from "@oak/oak"; -import { getContent, redirect } from "./libs/mod.ts"; - -export const router = new Router(); -router - .get("/", async (ctx: RouterContext) => { - ctx.response.type = "text/plain"; - if (!redirect(ctx, ctx.request.userAgent)) await getContent(ctx); - }) - .get("/:ref", async (ctx: RouterContext) => { - const ref: string | undefined = ctx.params.ref; - ctx.response.type = "text/plain"; - if (!redirect(ctx, ctx.request.userAgent, ref)) await getContent(ctx, ref); - }); diff --git a/src/router_test.ts b/src/router_test.ts deleted file mode 100644 index 3f01af3..0000000 --- a/src/router_test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { type RouterContext, testing } from "@oak/oak"; -import { assertEquals } from "@std/assert"; -import { STATUS_CODE } from "@std/http/status"; - -import { router } from "./router.ts"; -import { exportRepo, testRef, testRepo } from "./libs/test_utils.ts"; - -Deno.test("Serve", async (t: Deno.TestContext) => { - await t.step("/", async () => { - const ctx: RouterContext = testing.createMockContext({ - method: "GET", - path: "/", - }); - exportRepo(testRepo); - await router.routes()(ctx, () => Promise.resolve()); - - assertEquals(ctx.response.status, STATUS_CODE.OK); - }); - - await t.step("/:ref", async () => { - const ctx: RouterContext = testing.createMockContext({ - method: "GET", - path: `/${testRef}`, - }); - exportRepo(testRepo); - await router.routes()(ctx, () => Promise.resolve()); - - assertEquals(ctx.response.status, STATUS_CODE.OK); - }); -}); diff --git a/src/server.ts b/src/server.ts index b5089f2..6ee77a1 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,25 +1,25 @@ -import { Application } from "@oak/oak"; -import type { ApplicationListenEvent } from "@oak/oak/application"; -import { yellow } from "@std/fmt/colors"; +import { type Context, Hono } from "@hono/hono"; +import { STATUS_CODE } from "@std/http/status"; +import { UserAgent } from "@std/http/user-agent"; -import { router } from "./router.ts"; +import { getContent, redirect } from "./libs/mod.ts"; -const app = new Application(); +export const app = new Hono(); +app + .get("/:ref?", async (ctx: Context) => { + const ref: string = ctx.req.param("ref"); -app.use(router.routes()); -app.use(router.allowedMethods()); - -app.addEventListener( - "listen", - ({ secure, hostname, port }: ApplicationListenEvent) => { - console.log( - `๐Ÿ”” listening: ${ - yellow( - `${secure ? "https" : "http"}://${hostname ?? "localhost"}:${port}`, - ) - }`, + const url: URL | null = redirect( + new UserAgent(ctx.req.header("User-Agent") ?? ""), + ref, ); - }, -); + if (url) return ctx.redirect(url.toString(), STATUS_CODE.PermanentRedirect); + + const [data, status] = await getContent(ref); + return ctx.text(data, status); + }) + .get("*", (ctx: Context) => { + return ctx.redirect("/", STATUS_CODE.SeeOther); + }); -await app.listen({ port: 8080 }); +Deno.serve(app.fetch); diff --git a/src/server_test.ts b/src/server_test.ts new file mode 100644 index 0000000..6bb8d85 --- /dev/null +++ b/src/server_test.ts @@ -0,0 +1,61 @@ +import { assertEquals } from "@std/assert"; +import { STATUS_CODE } from "@std/http/status"; + +import { app } from "./server.ts"; +import { + exportRepo, + testRef, + testRepo, + testUserAgent, +} from "./libs/test_utils.ts"; +import { getGitHubUrl } from "./libs/utils/env.ts"; + +Deno.test("Serve", async (t: Deno.TestContext) => { + await t.step("/", async () => { + exportRepo(testRepo); + const res: Response = await app.request("/"); + + assertEquals(res.status, STATUS_CODE.OK); + }); + + await t.step("/ (Redirect)", async () => { + exportRepo(testRepo); + const res: Response = await app.request("/", { + headers: { "User-Agent": testUserAgent.toString() }, + }); + + assertEquals( + res.headers.get("Location"), + getGitHubUrl(testRepo).toString(), + ); + assertEquals(res.status, STATUS_CODE.PermanentRedirect); + }); + + await t.step("/:ref", async () => { + exportRepo(testRepo); + const res: Response = await app.request(`/${testRef}`); + + assertEquals(res.status, STATUS_CODE.OK); + }); + + await t.step("/:ref (Redirect)", async () => { + exportRepo(testRepo); + const res: Response = await app.request(`/${testRef}`, { + headers: { "User-Agent": testUserAgent.toString() }, + }); + + assertEquals( + res.headers.get("Location"), + getGitHubUrl(testRepo, testRef).toString(), + ); + assertEquals(res.status, STATUS_CODE.PermanentRedirect); + }); + + await t.step("*", async () => { + exportRepo(testRepo); + const res: Response = await app.request("/anything/else"); + + assertEquals(res.headers.get("Location"), "/"); + assertEquals(res.status, STATUS_CODE.SeeOther); + }); +}); From 0811423257d15fa04546abbfbb0a0721063bb5ed Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Sun, 25 Aug 2024 11:48:31 +0900 Subject: [PATCH 03/10] refactor(struct): Refactor the file structure (#42) Use spread syntax to clear the returning and flat the libs directory. --- src/libs/{middleware => }/content.ts | 2 +- src/libs/{middleware => }/content_test.ts | 2 +- src/libs/{utils => }/env.ts | 20 +------------------- src/libs/{utils => }/env_test.ts | 2 +- src/libs/mod.ts | 4 ++-- src/libs/{middleware => }/redirect.ts | 5 +++-- src/libs/{middleware => }/redirect_test.ts | 10 +++++----- src/libs/test_utils.ts | 4 ---- src/libs/utils.ts | 19 +++++++++++++++++++ src/server.ts | 11 +++++------ src/server_test.ts | 2 +- 11 files changed, 39 insertions(+), 42 deletions(-) rename src/libs/{middleware => }/content.ts (91%) rename src/libs/{middleware => }/content_test.ts (91%) rename src/libs/{utils => }/env.ts (59%) rename src/libs/{utils => }/env_test.ts (90%) rename src/libs/{middleware => }/redirect.ts (65%) rename src/libs/{middleware => }/redirect_test.ts (54%) create mode 100644 src/libs/utils.ts diff --git a/src/libs/middleware/content.ts b/src/libs/content.ts similarity index 91% rename from src/libs/middleware/content.ts rename to src/libs/content.ts index 217a147..41a21d0 100644 --- a/src/libs/middleware/content.ts +++ b/src/libs/content.ts @@ -1,7 +1,7 @@ import { Octokit } from "@octokit/rest"; import type { StatusCode } from "@std/http"; -import { getRepository, githubToken } from "../utils/env.ts"; +import { getRepository, githubToken } from "./env.ts"; export async function getContent( ref: string | undefined = undefined, diff --git a/src/libs/middleware/content_test.ts b/src/libs/content_test.ts similarity index 91% rename from src/libs/middleware/content_test.ts rename to src/libs/content_test.ts index c8ccd0d..fe05eba 100644 --- a/src/libs/middleware/content_test.ts +++ b/src/libs/content_test.ts @@ -2,7 +2,7 @@ import { assertEquals, assertExists, assertStringIncludes } from "@std/assert"; import { STATUS_CODE } from "@std/http/status"; import { getContent } from "./content.ts"; -import { exportRepo, testRef, testRepo, unknownRepo } from "../test_utils.ts"; +import { exportRepo, testRef, testRepo, unknownRepo } from "./test_utils.ts"; Deno.test("Get Content", async (t: Deno.TestContext) => { await t.step("normal", async () => { diff --git a/src/libs/utils/env.ts b/src/libs/env.ts similarity index 59% rename from src/libs/utils/env.ts rename to src/libs/env.ts index 6bc9eff..0443502 100644 --- a/src/libs/utils/env.ts +++ b/src/libs/env.ts @@ -1,6 +1,4 @@ -import { join } from "@std/path"; - -import type { Repository } from "../types.ts"; +import type { Repository } from "./types.ts"; export function getRepository(): Repository { const repository: Repository = { @@ -19,19 +17,3 @@ export function getRepository(): Repository { } export const githubToken: string | undefined = Deno.env.get("GITHUB_TOKEN"); - -export function getGitHubUrl( - repository: Repository, - ref: string = "master", -): URL { - return new URL( - join( - repository.owner, - repository.name, - "blob", - ref, - repository.path, - ), - "https://github.com", - ); -} diff --git a/src/libs/utils/env_test.ts b/src/libs/env_test.ts similarity index 90% rename from src/libs/utils/env_test.ts rename to src/libs/env_test.ts index 33c72f0..0a1d47c 100644 --- a/src/libs/utils/env_test.ts +++ b/src/libs/env_test.ts @@ -1,7 +1,7 @@ import { assertEquals } from "@std/assert"; import { getRepository } from "./env.ts"; -import { clearRepo, exportRepo, testRepo } from "../test_utils.ts"; +import { clearRepo, exportRepo, testRepo } from "./test_utils.ts"; Deno.test("Get Repository Env", async (t: Deno.TestContext) => { await t.step("Normal", () => { diff --git a/src/libs/mod.ts b/src/libs/mod.ts index 44fff98..7311f3e 100644 --- a/src/libs/mod.ts +++ b/src/libs/mod.ts @@ -1,2 +1,2 @@ -export * from "./middleware/content.ts"; -export * from "./middleware/redirect.ts"; +export * from "./content.ts"; +export * from "./redirect.ts"; diff --git a/src/libs/middleware/redirect.ts b/src/libs/redirect.ts similarity index 65% rename from src/libs/middleware/redirect.ts rename to src/libs/redirect.ts index 98567d6..c4f6c6b 100644 --- a/src/libs/middleware/redirect.ts +++ b/src/libs/redirect.ts @@ -1,8 +1,9 @@ import type { UserAgent } from "@std/http/user-agent"; -import { getGitHubUrl, getRepository } from "../utils/env.ts"; +import { getRepository } from "./env.ts"; +import { getGitHubUrl } from "./utils.ts"; -export function redirect( +export function checkRedirect( userAgent: UserAgent, ref: string = "master", ): URL | null { diff --git a/src/libs/middleware/redirect_test.ts b/src/libs/redirect_test.ts similarity index 54% rename from src/libs/middleware/redirect_test.ts rename to src/libs/redirect_test.ts index 7027342..0054c3c 100644 --- a/src/libs/middleware/redirect_test.ts +++ b/src/libs/redirect_test.ts @@ -1,20 +1,20 @@ import { assertEquals } from "@std/assert"; -import { redirect } from "./redirect.ts"; -import { exportRepo, testRef, testRepo, testUserAgent } from "../test_utils.ts"; -import { getGitHubUrl } from "../utils/env.ts"; +import { checkRedirect } from "./redirect.ts"; +import { exportRepo, testRef, testRepo, testUserAgent } from "./test_utils.ts"; +import { getGitHubUrl } from "./utils.ts"; Deno.test("Redirect Detection", async (t: Deno.TestContext) => { await t.step("normal", () => { exportRepo(testRepo); - const url: URL | null = redirect(testUserAgent); + const url: URL | null = checkRedirect(testUserAgent); assertEquals(url, getGitHubUrl(testRepo)); }); await t.step("with ref", () => { exportRepo(testRepo); - const url: URL | null = redirect(testUserAgent, testRef); + const url: URL | null = checkRedirect(testUserAgent, testRef); assertEquals(url, getGitHubUrl(testRepo, testRef)); }); diff --git a/src/libs/test_utils.ts b/src/libs/test_utils.ts index 1669993..5df5cb1 100644 --- a/src/libs/test_utils.ts +++ b/src/libs/test_utils.ts @@ -6,15 +6,12 @@ export const testRepo: Repository = { name: "deno", path: "README.md", }; - export const unknownRepo: Repository = { owner: "unknown-owner", name: "unknown-repo", path: "unknown-path", }; - export const testRef = "v1.0.0"; - export const testUserAgent = new UserAgent("Chrome/1.2.3"); export function exportRepo(repository: Repository) { @@ -22,7 +19,6 @@ export function exportRepo(repository: Repository) { Deno.env.set("REPOSITORY_NAME", repository.name); Deno.env.set("REPOSITORY_PATH", repository.path); } - export function clearRepo() { Deno.env.delete("REPOSITORY_OWNER"); Deno.env.delete("REPOSITORY_NAME"); diff --git a/src/libs/utils.ts b/src/libs/utils.ts new file mode 100644 index 0000000..50137e5 --- /dev/null +++ b/src/libs/utils.ts @@ -0,0 +1,19 @@ +import { join } from "@std/path"; + +import type { Repository } from "./types.ts"; + +export function getGitHubUrl( + repository: Repository, + ref: string = "master", +): URL { + return new URL( + join( + repository.owner, + repository.name, + "blob", + ref, + repository.path, + ), + "https://github.com", + ); +} diff --git a/src/server.ts b/src/server.ts index 6ee77a1..f780497 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,21 +2,20 @@ import { type Context, Hono } from "@hono/hono"; import { STATUS_CODE } from "@std/http/status"; import { UserAgent } from "@std/http/user-agent"; -import { getContent, redirect } from "./libs/mod.ts"; +import { checkRedirect, getContent } from "./libs/mod.ts"; export const app = new Hono(); app .get("/:ref?", async (ctx: Context) => { const ref: string = ctx.req.param("ref"); - - const url: URL | null = redirect( + const url: URL | null = checkRedirect( new UserAgent(ctx.req.header("User-Agent") ?? ""), ref, ); - if (url) return ctx.redirect(url.toString(), STATUS_CODE.PermanentRedirect); - const [data, status] = await getContent(ref); - return ctx.text(data, status); + return url + ? ctx.redirect(url.toString(), STATUS_CODE.PermanentRedirect) + : ctx.text(...await getContent(ref)); }) .get("*", (ctx: Context) => { return ctx.redirect("/", STATUS_CODE.SeeOther); diff --git a/src/server_test.ts b/src/server_test.ts index 6bb8d85..9340a1e 100644 --- a/src/server_test.ts +++ b/src/server_test.ts @@ -8,7 +8,7 @@ import { testRepo, testUserAgent, } from "./libs/test_utils.ts"; -import { getGitHubUrl } from "./libs/utils/env.ts"; +import { getGitHubUrl } from "./libs/utils.ts"; Deno.test("Serve", async (t: Deno.TestContext) => { await t.step("/", async () => { From f9f56fd029a32cdeed760b68aa01ad7be5095780 Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Sun, 25 Aug 2024 11:51:48 +0900 Subject: [PATCH 04/10] chore(meta): Delete merged branch is not potentially unused (#43) Rules can manage branches without any third-party apps. --- .github/delete-merged-branch-config.yml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .github/delete-merged-branch-config.yml diff --git a/.github/delete-merged-branch-config.yml b/.github/delete-merged-branch-config.yml deleted file mode 100644 index 2ded14b..0000000 --- a/.github/delete-merged-branch-config.yml +++ /dev/null @@ -1,2 +0,0 @@ -exclude: [dev] -delete_closed_pr: true From bd02cba635216591f3ff56f121a2e4b609694206 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 08:41:05 +0900 Subject: [PATCH 05/10] chore(deps): Bump pre-commit hook (#44) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - github.com/crate-ci/typos: v1.23.6 โ†’ v1.24.1 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0346ed6..7d5b90d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: args: [--whitespaces-count, "2"] - repo: https://github.com/crate-ci/typos - rev: v1.23.6 + rev: v1.24.1 hooks: - id: typos From cc8d3cc6023676076ab1025fda2e97e66ff7cdf9 Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Tue, 27 Aug 2024 20:58:51 +0900 Subject: [PATCH 06/10] style(test): Rename test file postfix (#45) The TS test file name usually ends with `.test.ts`, removing the JSON schema. --- deno.json | 1 - src/libs/{content_test.ts => content.test.ts} | 0 src/libs/{env_test.ts => env.test.ts} | 0 src/libs/{redirect_test.ts => redirect.test.ts} | 0 src/libs/test_utils.ts | 1 + src/{server_test.ts => server.test.ts} | 0 6 files changed, 1 insertion(+), 1 deletion(-) rename src/libs/{content_test.ts => content.test.ts} (100%) rename src/libs/{env_test.ts => env.test.ts} (100%) rename src/libs/{redirect_test.ts => redirect.test.ts} (100%) rename src/{server_test.ts => server.test.ts} (100%) diff --git a/deno.json b/deno.json index cf3dbd1..0691f9f 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,4 @@ { - "$schema": "https://deno.land/x/deno/cli/schemas/config-file.v1.json", "fmt": { "exclude": ["LICENSE", ".github/**/*.md"] }, "tasks": { "run": "deno run --env='.env' --allow-env='REPOSITORY_OWNER,REPOSITORY_NAME,REPOSITORY_PATH,GITHUB_TOKEN' --allow-net='0.0.0.0,api.github.com'", diff --git a/src/libs/content_test.ts b/src/libs/content.test.ts similarity index 100% rename from src/libs/content_test.ts rename to src/libs/content.test.ts diff --git a/src/libs/env_test.ts b/src/libs/env.test.ts similarity index 100% rename from src/libs/env_test.ts rename to src/libs/env.test.ts diff --git a/src/libs/redirect_test.ts b/src/libs/redirect.test.ts similarity index 100% rename from src/libs/redirect_test.ts rename to src/libs/redirect.test.ts diff --git a/src/libs/test_utils.ts b/src/libs/test_utils.ts index 5df5cb1..2cfd09d 100644 --- a/src/libs/test_utils.ts +++ b/src/libs/test_utils.ts @@ -1,4 +1,5 @@ import { UserAgent } from "@std/http/user-agent"; + import type { Repository } from "./types.ts"; export const testRepo: Repository = { diff --git a/src/server_test.ts b/src/server.test.ts similarity index 100% rename from src/server_test.ts rename to src/server.test.ts From 275c5af47298f4a4ae4233e503c508e33fce1c87 Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:10:03 +0900 Subject: [PATCH 07/10] docs(jsdoc): Add document comments to all exported codes (#46) Developers can notice what the code means. --- src/libs/content.ts | 27 +++++++++++++++++++++ src/libs/env.ts | 18 ++++++++++++++ src/libs/redirect.ts | 32 ++++++++++++++++++++++++ src/libs/test_utils.ts | 55 ++++++++++++++++++++++++++++++++++++++++++ src/libs/types.ts | 16 ++++++++++++ src/libs/utils.ts | 17 +++++++++++++ src/server.ts | 14 +++++++++++ 7 files changed, 179 insertions(+) diff --git a/src/libs/content.ts b/src/libs/content.ts index 41a21d0..5494d85 100644 --- a/src/libs/content.ts +++ b/src/libs/content.ts @@ -3,6 +3,33 @@ import type { StatusCode } from "@std/http"; import { getRepository, githubToken } from "./env.ts"; +/** + * Get the content of the repository. + * @internal + * + * @param ref The name of the branch, tag or commit hash + * @returns The content of the repository and the status code + * + * @example Use the default branch + * ```ts + * const [content, status] = await getContent(); + * ``` + * @example Use a specific branch + * ```ts + * const branch = "main"; + * const [content, status] = await getContent(branch); + * ``` + * @example Use a specific tag + * ```ts + * const tag = "v1.0.0"; + * const [content, status] = await getContent(tag); + * ``` + * @example Use a specific commit + * ```ts + * const commit = "a1b2c3d4e5f6"; + * const [content, status] = await getContent(commit); + * ``` + */ export async function getContent( ref: string | undefined = undefined, ): Promise<[string, StatusCode]> { diff --git a/src/libs/env.ts b/src/libs/env.ts index 0443502..2016570 100644 --- a/src/libs/env.ts +++ b/src/libs/env.ts @@ -1,5 +1,15 @@ import type { Repository } from "./types.ts"; +/** + * Get the repository details from the environment variables. + * + * @returns {Repository} The repository type + * + * @example + * ```ts + * const repository = getRepository(); + * ``` + */ export function getRepository(): Repository { const repository: Repository = { owner: Deno.env.get("REPOSITORY_OWNER") ?? "", @@ -16,4 +26,12 @@ export function getRepository(): Repository { return repository; } +/** + * The GitHub token from the environment variables. + * + * @example + * ```ts + * const token = githubToken; + * ``` + */ export const githubToken: string | undefined = Deno.env.get("GITHUB_TOKEN"); diff --git a/src/libs/redirect.ts b/src/libs/redirect.ts index c4f6c6b..fb479d4 100644 --- a/src/libs/redirect.ts +++ b/src/libs/redirect.ts @@ -3,6 +3,38 @@ import type { UserAgent } from "@std/http/user-agent"; import { getRepository } from "./env.ts"; import { getGitHubUrl } from "./utils.ts"; +/** + * Check if accessed from a browser and return the GitHub URL. + * @internal + * + * @param userAgent The user agent accessed from + * @param ref="master" The branch, tag, or commit hash + * @returns The GitHub repository URL or null + * + * @example Use the default branch + * ```ts + * const userAgent = new UserAgent("Chrome/1.2.3"); + * const url: URL | null = checkRedirect(userAgent); + * ``` + * @example Use a specific branch + * ```ts + * const userAgent = new UserAgent("Chrome/1.2.3"); + * const branch = "main"; + * const url: URL | null = checkRedirect(userAgent, branch); + * ``` + * @example Use a specific tag + * ```ts + * const userAgent = new UserAgent("Chrome/1.2.3"); + * const tag = "v1.0.0"; + * const url: URL | null = checkRedirect(userAgent, tag); + * ``` + * @example Use a specific commit + * ```ts + * const userAgent = new UserAgent("Chrome/1.2.3"); + * const commit = "a1b2c3d4e5f6"; + * const url: URL | null = checkRedirect(userAgent, commit); + * ``` + */ export function checkRedirect( userAgent: UserAgent, ref: string = "master", diff --git a/src/libs/test_utils.ts b/src/libs/test_utils.ts index 2cfd09d..fe55859 100644 --- a/src/libs/test_utils.ts +++ b/src/libs/test_utils.ts @@ -2,24 +2,79 @@ import { UserAgent } from "@std/http/user-agent"; import type { Repository } from "./types.ts"; +/** + * Sample repository for test. + * + * @example + * ```ts + * const repository = testRepo; + * ``` + */ export const testRepo: Repository = { owner: "denoland", name: "deno", path: "README.md", }; + +/** + * Sample unknown repository for test. + * + * @example + * ```ts + * const repository = unknownRepo; + * ``` + */ export const unknownRepo: Repository = { owner: "unknown-owner", name: "unknown-repo", path: "unknown-path", }; + +/** + * Sample version reference for test. + * + * @example + * ```ts + * const ref = testRef; + * ``` + */ export const testRef = "v1.0.0"; + +/** + * Sample user agent for test. + * + * @example + * ```ts + * const userAgent = testUserAgent; + * ``` + */ export const testUserAgent = new UserAgent("Chrome/1.2.3"); +/** + * Export the repository details to the environment variables. + * + * @param repository The repository type + * + * @example + * ```ts + * const repository = new Repository("5ouma", "reproxy", "src/server.ts"); + * exportRepo(repository); + * ``` + */ export function exportRepo(repository: Repository) { Deno.env.set("REPOSITORY_OWNER", repository.owner); Deno.env.set("REPOSITORY_NAME", repository.name); Deno.env.set("REPOSITORY_PATH", repository.path); } + +/** + * Clear the repository details from the environment variables. + * + * @example + * ```ts + * clearRepo(); + * ``` + */ export function clearRepo() { Deno.env.delete("REPOSITORY_OWNER"); Deno.env.delete("REPOSITORY_NAME"); diff --git a/src/libs/types.ts b/src/libs/types.ts index d2dfbcf..3575123 100644 --- a/src/libs/types.ts +++ b/src/libs/types.ts @@ -1,3 +1,19 @@ +/** + * Repository type. + * + * @property owner The repository owner + * @property name The repository name + * @property path The content path from the root of the repository + * + * @example + * ```ts + * const repository: Repository = { + * owner: "5ouma", + * name: "reproxy", + * path: "src/server.ts", + * }; + * ``` + */ export type Repository = { owner: string; name: string; diff --git a/src/libs/utils.ts b/src/libs/utils.ts index 50137e5..a619489 100644 --- a/src/libs/utils.ts +++ b/src/libs/utils.ts @@ -2,6 +2,23 @@ import { join } from "@std/path"; import type { Repository } from "./types.ts"; +/** + * Get the GitHub URL of the repository. + * + * @param repository The repository type + * @param ref="master" The reference to use + * @returns The GitHub repository URL + * + * @example + * ```ts + * const repository: Repository = { + * owner: "5ouma", + * name: "reproxy", + * path: "src/server.ts", + * }; + * const url: URL = getGitHubUrl(repository); + * ``` + */ export function getGitHubUrl( repository: Repository, ref: string = "master", diff --git a/src/server.ts b/src/server.ts index f780497..4efc307 100644 --- a/src/server.ts +++ b/src/server.ts @@ -4,6 +4,20 @@ import { UserAgent } from "@std/http/user-agent"; import { checkRedirect, getContent } from "./libs/mod.ts"; +/** + * The Hono application for this project. + * + * @example Access without a user agent + * ```ts + * const res: Response = await app.request("/"); + * ``` + * @example Access with a user agent + * ```ts + * const res: Response = await app.request("/", { + * headers: { "User-Agent": new UserAgent("Chrome/1.2.3").toString() }, + * }); + * ``` + */ export const app = new Hono(); app .get("/:ref?", async (ctx: Context) => { From aa2def04be47c2e64fbff1a5e8689dccb8ebd3e1 Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Fri, 30 Aug 2024 08:49:34 +0900 Subject: [PATCH 08/10] feat(jsr): Publish to JSR (#47) Stop using the dev branch to publish this project to JSR. --- .github/CONTRIBUTING.md | 32 +----- .github/branch-switcher.yml | 2 - .github/dependabot.yml | 1 - .github/workflows/deps-update.yml | 3 - .github/workflows/release.yml | 43 ++++++++ .github/workflows/test.yml | 1 - .pre-commit-config.yaml | 1 - .github/README.md => README.md | 48 ++++----- deno.json | 22 ++-- deno.lock | 160 +++++++++++++++++++++------- src/{server.test.ts => app.test.ts} | 2 +- src/app.ts | 36 +++++++ src/server.ts | 36 +------ 13 files changed, 238 insertions(+), 149 deletions(-) delete mode 100644 .github/branch-switcher.yml create mode 100644 .github/workflows/release.yml rename .github/README.md => README.md (75%) rename src/{server.test.ts => app.test.ts} (97%) create mode 100644 src/app.ts diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index dd9e450..ef05e58 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -10,7 +10,6 @@ I would recommend reading this guideline for a better development experience.
- [๐Ÿ’ฌ Commit Message](#-commit-message) -- [๐ŸŽ‹ Pull Requests Branch](#-pull-requests-branch) - [โ“ Pull Requests Title](#-pull-requests-title) - [๐Ÿชต Commit Log](#-commit-log) @@ -30,35 +29,6 @@ Body
-## ๐ŸŽ‹ Pull Requests Branch - -I use the [`dev`] branch for the temporary merging. -When it's time to release, I'll merge the [`dev`] branch into the [`main`] branch. -
-For this reason, please fork the [`dev`] branch and open a PR to it. - -[`main`]: https://github.com/5ouma/rproxy/tree/main -[`dev`]: https://github.com/5ouma/rproxy/tree/dev - -```mermaid -flowchart LR - subgraph PR Branches - feature1 - feature2 - feature3 - end - - subgraph Origin Branches - dev - main - end - - feature1 & feature2 & feature3 -- Squash Merge --> dev - dev -- Merge --> main -``` - -
- ## โ“ Pull Requests Title You don't need to add any prefixes like `feature` or `bug fix` @@ -70,5 +40,5 @@ Please give a clear title. ## ๐Ÿชต Commit Log -I do squash merge to the dev branch to keep the commit history clean. +I do squash merge to the main branch to keep the commit history clean. When merging your Pull Request, I'll add the Conventional Commits type and scope. diff --git a/.github/branch-switcher.yml b/.github/branch-switcher.yml deleted file mode 100644 index a4aaeea..0000000 --- a/.github/branch-switcher.yml +++ /dev/null @@ -1,2 +0,0 @@ -preferredBranch: dev -exclude: [branch: dev] diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 113875d..58bc340 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,5 +4,4 @@ updates: directory: / schedule: interval: daily - target-branch: dev labels: [] diff --git a/.github/workflows/deps-update.yml b/.github/workflows/deps-update.yml index 70bb511..22ac219 100644 --- a/.github/workflows/deps-update.yml +++ b/.github/workflows/deps-update.yml @@ -16,14 +16,11 @@ jobs: steps: - name: ๐Ÿšš Checkout Repository uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - ref: dev - name: ๐Ÿฆ• Update Dependencies uses: hasundue/molt-action@2042116c4f16e14c08c98130f1470c19c5cbfd2f # v1.0.2 with: author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - base: dev branch: deps-deno commit-prefix: "chore(deps):" labels: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9f07f00 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,43 @@ +name: ๐Ÿš€ Release + +on: + push: + branches: + - main + paths: + - deno.json + +permissions: + contents: write + id-token: write + +jobs: + Release: + runs-on: Ubuntu-Latest + + steps: + - name: ๐Ÿšš Checkout Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: ๐Ÿ“ฆ Get the Package Version + uses: notiz-dev/github-action-json-property@a5a9c668b16513c737c3e1f8956772c99c73f6e8 # v0.2.0 + id: version + with: + path: deno.json + prop_path: version + + - name: ๐Ÿ” Detect the Version has Changed + id: version-has-changed + run: | + tag="$(git tag -l | sort -V | tail -n 1)" + if [[ "$tag" == "v${{ steps.version.outputs.prop }}" ]]; then + echo "stop=true" | tee -a "$GITHUB_OUTPUT" + fi + + - name: ๐Ÿš€ Rrelease a New Version + if: ${{ steps.version-has-changed.outputs.stop != 'true' }} + run: gh release create "v${{ steps.version.outputs.prop }}" --generate-notes + + - name: โฌ†๏ธ Publish to JSR + if: ${{ steps.version-has-changed.outputs.stop != 'true' }} + run: npx jsr publish diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 982e4e7..a48cba0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - dev paths: - "**.ts" - deno.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7d5b90d..7bb61e5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,6 @@ ci: chore(fix): Auto fixes from pre-commit.com hooks For more information, see https://pre-commit.ci. - autoupdate_branch: dev autoupdate_commit_msg: "chore(deps): Bump pre-commit hook" repos: diff --git a/.github/README.md b/README.md similarity index 75% rename from .github/README.md rename to README.md index 0dccdba..fb73894 100644 --- a/.github/README.md +++ b/README.md @@ -5,12 +5,15 @@ **๐Ÿšš Deliver any files in the GitHub repository** [![GitHub Release](https://img.shields.io/github/v/release/5ouma/reproxy?style=flat-square)](https://github.com/5ouma/reproxy/releases) +[![JSR](https://jsr.io/badges/@5ouma/reproxy?style=flat-square)](https://jsr.io/@5ouma/opmlreproxy) +
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/5ouma/reproxy?style=flat-square) ![GitHub repo size](https://img.shields.io/github/repo-size/5ouma/reproxy?style=flat-square) [![GitHub last commit](https://img.shields.io/github/last-commit/5ouma/reproxy?style=flat-square)](https://github.com/5ouma/reproxy/commit/HEAD) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/5ouma/reproxy?style=flat-square)](https://github.com/5ouma/reproxy/commits/main)
[![Test](https://img.shields.io/github/actions/workflow/status/5ouma/reproxy/test.yml?label=test&style=flat-square)](https://github.com/5ouma/reproxy/actions/workflows/test.yml) +[![Release](https://img.shields.io/github/actions/workflow/status/5ouma/reproxy/release.yml?label=release&style=flat-square)](https://github.com/5ouma/reproxy/actions/workflows/release.yml) [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/5ouma/reproxy/main.svg?style=flat-square)](https://results.pre-commit.ci/latest/github/5ouma/reproxy/main) [![codecov](https://codecov.io/github/5ouma/reproxy/graph/badge.svg?token=OQB55KXJIL)](https://codecov.io/github/5ouma/reproxy) @@ -34,35 +37,30 @@ To do this, simply add the ref name to the sub-directory. ## ๐Ÿ”ง Setup -You can host your file on [Deno Deploy](https://deno.com/deploy). - -### ๐Ÿ’ช Manual Deployment - -1. Clone this repository - - ```sh - git clone https://github.com/5ouma/reproxy.git - ``` +### ๐Ÿ’ป On Local -2. Copy the [`.env.tmpl`](../.env.tmpl) to `.env` and edit as you prefer +1. Copy the [`.env.tmpl`](./.env.tmpl) to `.env` and edit as you prefer > [๐ŸŒ Environment Variables](#-environment-variables) -3. Install [Deno](https://deno.com) and [deployctl](https://docs.deno.com/deploy/manual/deployctl). - -4. Deploy to Deno Deploy +2. Run this command ```sh - deployctl deploy --prod --env-file='.env' + deno run -A jsr:@5ouma/reproxy/serve ```
-### โš™๏ธ Automatic Deployment +### ๐Ÿฆ• Use [Deno Deploy](https://deno.com/deploy) -1. [Fork this repository](https://github.com/5ouma/reproxy/fork) +1. [Create a new playground](https://dash.deno.com) -2. [Create a new project](https://dash.deno.com/new_project) with your forked repository +2. Replace the default code with this + + ```ts + import { app } from "jsr:@5ouma/reproxy"; + Deno.serve(app.fetch); + ``` 3. Set the environment variables (_Don't forget!!_) @@ -79,13 +77,11 @@ You can host your file on [Deno Deploy](https://deno.com/deploy). git clone https://github.com/5ouma/reproxy.git ``` -2. Copy the [`.env.template`](../.env.template) to `.env` and edit as you prefer +2. Copy the [`.env.tmpl`](./.env.tmpl) to `.env` and edit as you prefer > [๐ŸŒ Environment Variables](#-environment-variables) -3. Install [Deno](https://deno.com) - -4. Run the [`server.ts`](../src/server.ts) via these task runners +3. Run the [`server.ts`](./src/server.ts) via these task runners ```sh # For production @@ -103,10 +99,10 @@ You can host your file on [Deno Deploy](https://deno.com/deploy). | Name | Required | | :----------------: | :------: | -| `REPOSITORY_OWNER` | true | -| `REPOSITORY_NAME` | true | -| `REPOSITORY_PATH` | true | -| [`GITHUB_TOKEN`] | false | +| `REPOSITORY_OWNER` | yes | +| `REPOSITORY_NAME` | yes | +| `REPOSITORY_PATH` | yes | +| [`GITHUB_TOKEN`] | no | > [!NOTE] > You need to add [`GITHUB_TOKEN`] if you want to: @@ -134,5 +130,5 @@ You can host your file on [Deno Deploy](https://deno.com/deploy). I happily welcome your contributions! Before you contribute, -I would recommend reading [CONTRIBUTING.md](./CONTRIBUTING.md) +I would recommend reading [CONTRIBUTING.md](./.github/CONTRIBUTING.md) for a better development experience. diff --git a/deno.json b/deno.json index 0691f9f..a928518 100644 --- a/deno.json +++ b/deno.json @@ -1,22 +1,24 @@ { - "fmt": { "exclude": ["LICENSE", ".github/**/*.md"] }, + "name": "@5ouma/reproxy", + "version": "1.0.0", + "exports": { ".": "./src/app.ts", "./serve": "./src/server.ts" }, + "publish": { + "include": ["LICENSE", "README.md", "deno.json", "src/"], + "exclude": ["**/*.test.ts", "src/libs/test_utils.ts"] + }, + "fmt": { "exclude": ["LICENSE", "README.md", ".github/**/*.md"] }, "tasks": { "run": "deno run --env='.env' --allow-env='REPOSITORY_OWNER,REPOSITORY_NAME,REPOSITORY_PATH,GITHUB_TOKEN' --allow-net='0.0.0.0,api.github.com'", - "start": "deno task run ./src/server.ts", - "dev": "deno task run --watch ./src/server.ts", + "start": "deno task run src/server.ts", + "dev": "deno task run --watch src/server.ts", "test": "deno test --allow-env='REPOSITORY_OWNER,REPOSITORY_NAME,REPOSITORY_PATH,GITHUB_TOKEN' --allow-net='0.0.0.0,api.github.com' --parallel --shuffle", "cov": "deno task test --coverage && deno coverage --lcov > coverage.lcov" }, "imports": { "@hono/hono": "jsr:@hono/hono@4.5.8", - "@octokit/rest": "https://esm.sh/@octokit/rest@21.0.1", "@std/assert": "jsr:@std/assert@1.0.2", "@std/http": "jsr:@std/http@1.0.2", - "@std/path": "jsr:@std/path@1.0.2" - }, - "deploy": { - "project": "reproxy", - "include": ["./deno.json", "./src/"], - "entrypoint": "./src/server.ts" + "@std/path": "jsr:@std/path@1.0.2", + "@octokit/rest": "npm:@octokit/rest@21.0.1" } } diff --git a/deno.lock b/deno.lock index 69b5ed0..fc1639e 100644 --- a/deno.lock +++ b/deno.lock @@ -4,16 +4,17 @@ "specifiers": { "jsr:@hono/hono@4.5.8": "jsr:@hono/hono@4.5.8", "jsr:@std/assert@1.0.2": "jsr:@std/assert@1.0.2", - "jsr:@std/cli@^1.0.3": "jsr:@std/cli@1.0.3", - "jsr:@std/encoding@^1.0.1": "jsr:@std/encoding@1.0.2", - "jsr:@std/fmt@^1.0.0": "jsr:@std/fmt@1.0.0", + "jsr:@std/cli@^1.0.3": "jsr:@std/cli@1.0.4", + "jsr:@std/encoding@^1.0.1": "jsr:@std/encoding@1.0.3", + "jsr:@std/fmt@^1.0.0": "jsr:@std/fmt@1.0.1", "jsr:@std/http@1.0.2": "jsr:@std/http@1.0.2", - "jsr:@std/internal@^1.0.1": "jsr:@std/internal@1.0.1", - "jsr:@std/media-types@^1.0.2": "jsr:@std/media-types@1.0.2", - "jsr:@std/net@^1.0.0": "jsr:@std/net@1.0.0", + "jsr:@std/internal@^1.0.1": "jsr:@std/internal@1.0.2", + "jsr:@std/media-types@^1.0.2": "jsr:@std/media-types@1.0.3", + "jsr:@std/net@^1.0.0": "jsr:@std/net@1.0.1", "jsr:@std/path@1.0.2": "jsr:@std/path@1.0.2", "jsr:@std/path@^1.0.2": "jsr:@std/path@1.0.2", - "jsr:@std/streams@^1.0.1": "jsr:@std/streams@1.0.2" + "jsr:@std/streams@^1.0.1": "jsr:@std/streams@1.0.3", + "npm:@octokit/rest@21.0.1": "npm:@octokit/rest@21.0.1_@octokit+core@6.1.2" }, "jsr": { "@hono/hono@4.5.8": { @@ -25,14 +26,14 @@ "jsr:@std/internal@^1.0.1" ] }, - "@std/cli@1.0.3": { - "integrity": "9a0488b5d2e58d29dce106a941eecec7181fae996bf0d2225563f1ca7e4b100c" + "@std/cli@1.0.4": { + "integrity": "79ca75add572a99a8ba93ae37ccbd8d43fb4e2b635a8a7ebebb4f2d092048764" }, - "@std/encoding@1.0.2": { - "integrity": "7ed640c777e3275550e2cd937c440acdbebfdcd2d13ef67052f0536bf43e707f" + "@std/encoding@1.0.3": { + "integrity": "5dbc2d7f5aa6062de7e19862ea856ac7a0dcce0b6fb46bb7b332d3bdcd4408b7" }, - "@std/fmt@1.0.0": { - "integrity": "8a95c9fdbb61559418ccbc0f536080cf43341655e1444f9d375a66886ceaaa3d" + "@std/fmt@1.0.1": { + "integrity": "ef76c37faa7720faa8c20fd8cc74583f9b1e356dfd630c8714baa716a45856ab" }, "@std/http@1.0.2": { "integrity": "e28d612e2b5e0a40704e8c64d9a5d55cf8c6accf6d798cd1cfb43aad02d1e4fe", @@ -46,47 +47,126 @@ "jsr:@std/streams@^1.0.1" ] }, - "@std/internal@1.0.1": { - "integrity": "6f8c7544d06a11dd256c8d6ba54b11ed870aac6c5aeafff499892662c57673e6" + "@std/internal@1.0.2": { + "integrity": "f4cabe2021352e8bfc24e6569700df87bf070914fc38d4b23eddd20108ac4495" }, - "@std/media-types@1.0.2": { - "integrity": "abb78dc8f7d88141cba8c4d60fc1e8b421e5c7b0d7c84f2f708bc666cad46784" + "@std/media-types@1.0.3": { + "integrity": "b12d30a7852f7578f4d210622df713bbfd1cbdd9b4ec2eaf5c1845ab70bab159" }, - "@std/net@1.0.0": { - "integrity": "31de7a45f074edee8296d77fde85fef53fed9baa6f3122a38d796af08ecb3fc9" + "@std/net@1.0.1": { + "integrity": "27fe136ab7ae81f425df90aad190bae2c87d2aef3d1a49cbc0efc1daa287d794" }, "@std/path@1.0.2": { "integrity": "a452174603f8c620bd278a380c596437a9eef50c891c64b85812f735245d9ec7" }, - "@std/streams@1.0.2": { - "integrity": "187c3c875675221f5355807a735e51b0f8769caade2cbca6d7f4fa710ea4ace4" + "@std/streams@1.0.3": { + "integrity": "d62e645ab981cee2c3d03040eb03cf387fc6bceef6d4564f3ed485a43741a81f" + } + }, + "npm": { + "@octokit/auth-token@5.1.1": { + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", + "dependencies": {} + }, + "@octokit/core@6.1.2": { + "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "dependencies": { + "@octokit/auth-token": "@octokit/auth-token@5.1.1", + "@octokit/graphql": "@octokit/graphql@8.1.1", + "@octokit/request": "@octokit/request@9.1.3", + "@octokit/request-error": "@octokit/request-error@6.1.4", + "@octokit/types": "@octokit/types@13.5.0", + "before-after-hook": "before-after-hook@3.0.2", + "universal-user-agent": "universal-user-agent@7.0.2" + } + }, + "@octokit/endpoint@10.1.1": { + "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "dependencies": { + "@octokit/types": "@octokit/types@13.5.0", + "universal-user-agent": "universal-user-agent@7.0.2" + } + }, + "@octokit/graphql@8.1.1": { + "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "dependencies": { + "@octokit/request": "@octokit/request@9.1.3", + "@octokit/types": "@octokit/types@13.5.0", + "universal-user-agent": "universal-user-agent@7.0.2" + } + }, + "@octokit/openapi-types@22.2.0": { + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", + "dependencies": {} + }, + "@octokit/plugin-paginate-rest@11.3.3_@octokit+core@6.1.2": { + "integrity": "sha512-o4WRoOJZlKqEEgj+i9CpcmnByvtzoUYC6I8PD2SA95M+BJ2x8h7oLcVOg9qcowWXBOdcTRsMZiwvM3EyLm9AfA==", + "dependencies": { + "@octokit/core": "@octokit/core@6.1.2", + "@octokit/types": "@octokit/types@13.5.0" + } + }, + "@octokit/plugin-request-log@5.3.1_@octokit+core@6.1.2": { + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "dependencies": { + "@octokit/core": "@octokit/core@6.1.2" + } + }, + "@octokit/plugin-rest-endpoint-methods@13.2.4_@octokit+core@6.1.2": { + "integrity": "sha512-gusyAVgTrPiuXOdfqOySMDztQHv6928PQ3E4dqVGEtOvRXAKRbJR4b1zQyniIT9waqaWk/UDaoJ2dyPr7Bk7Iw==", + "dependencies": { + "@octokit/core": "@octokit/core@6.1.2", + "@octokit/types": "@octokit/types@13.5.0" + } + }, + "@octokit/request-error@6.1.4": { + "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", + "dependencies": { + "@octokit/types": "@octokit/types@13.5.0" + } + }, + "@octokit/request@9.1.3": { + "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", + "dependencies": { + "@octokit/endpoint": "@octokit/endpoint@10.1.1", + "@octokit/request-error": "@octokit/request-error@6.1.4", + "@octokit/types": "@octokit/types@13.5.0", + "universal-user-agent": "universal-user-agent@7.0.2" + } + }, + "@octokit/rest@21.0.1_@octokit+core@6.1.2": { + "integrity": "sha512-RWA6YU4CqK0h0J6tfYlUFnH3+YgBADlxaHXaKSG+BVr2y4PTfbU2tlKuaQoQZ83qaTbi4CUxLNAmbAqR93A6mQ==", + "dependencies": { + "@octokit/core": "@octokit/core@6.1.2", + "@octokit/plugin-paginate-rest": "@octokit/plugin-paginate-rest@11.3.3_@octokit+core@6.1.2", + "@octokit/plugin-request-log": "@octokit/plugin-request-log@5.3.1_@octokit+core@6.1.2", + "@octokit/plugin-rest-endpoint-methods": "@octokit/plugin-rest-endpoint-methods@13.2.4_@octokit+core@6.1.2" + } + }, + "@octokit/types@13.5.0": { + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", + "dependencies": { + "@octokit/openapi-types": "@octokit/openapi-types@22.2.0" + } + }, + "before-after-hook@3.0.2": { + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "dependencies": {} + }, + "universal-user-agent@7.0.2": { + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "dependencies": {} } } }, - "remote": { - "https://esm.sh/@octokit/rest@21.0.1": "f46bfa9d68ef9c77b0c65f19523d73f2e1512f0b563920dcdb56ecc099f2ce24", - "https://esm.sh/v135/@octokit/auth-token@5.1.0/denonext/auth-token.mjs": "24c22015e586d621821b0acb1b5b82d32c95d1813d264ce8fa3f769ba8806e8c", - "https://esm.sh/v135/@octokit/core@6.1.2/denonext/core.mjs": "2629f0f304f0a8313644ed4ddedbf1c7fbce06e80aadc6f718f6a4b4f401f9d7", - "https://esm.sh/v135/@octokit/endpoint@10.0.0/denonext/endpoint.mjs": "8d6f16f0b272fc7a20582637a3ffbd29c14f4bf629fafda05c33cf214b0dd716", - "https://esm.sh/v135/@octokit/endpoint@10.1.0/denonext/endpoint.mjs": "cb307fd4d62518987144d31210f929e67d644c7bcad866cdfde547a528d48e12", - "https://esm.sh/v135/@octokit/graphql@8.1.0/denonext/graphql.mjs": "a84701c7a52ca92d4b201f7502c3b048bfb3c0be9a131ed04bc736ccab160aba", - "https://esm.sh/v135/@octokit/plugin-paginate-rest@11.3.3/denonext/plugin-paginate-rest.mjs": "c82a962edb386933cf0a112d972d5349fa50a2a2401ec472f6292dba403ad218", - "https://esm.sh/v135/@octokit/plugin-request-log@5.3.1/denonext/plugin-request-log.mjs": "f9e3386dbcedf6c635812f004f2681f82402254f091c9753f694eb64790fabfe", - "https://esm.sh/v135/@octokit/plugin-rest-endpoint-methods@13.2.4/denonext/plugin-rest-endpoint-methods.mjs": "28cb28ddc2367113e9134415a9611be9be6210cdd9f5dc210f3bd9d49f41302d", - "https://esm.sh/v135/@octokit/request-error@6.0.2/denonext/request-error.mjs": "d25f73ba0f75f4ed9830caa96bffc7e36a73e51c0777caa8f436af3ede1cb116", - "https://esm.sh/v135/@octokit/request-error@6.1.0/denonext/request-error.mjs": "271a5a47ff4c05bf414443880c68fcc4c75e4da7c0e2caab1a4998fe4b6961b3", - "https://esm.sh/v135/@octokit/request@9.0.1/denonext/request.mjs": "150c8b8f650bcb2567a3e30906871234b1f449cd3765af418d15e08767bea449", - "https://esm.sh/v135/@octokit/request@9.1.0/denonext/request.mjs": "cc64353d55ef0f89428c9d752e57adad51e8dc7e03884f6086f8b953f781ae3e", - "https://esm.sh/v135/@octokit/rest@21.0.1/denonext/rest.mjs": "d9c2ff439438e475c71118f715f3afb62e8b372e7c0a859fddf2e81216b80751", - "https://esm.sh/v135/before-after-hook@3.0.2/denonext/before-after-hook.mjs": "cf2363bb9be543fadd912087b89d461082fd2ee66a9a3cc81fbeeea11aea8c32", - "https://esm.sh/v135/universal-user-agent@7.0.2/denonext/universal-user-agent.mjs": "c95431a8f6a7593e78b18ba137d1ae8fb8e2c7ebbcfc55c3d2cf1c9667ae8554" - }, + "remote": {}, "workspace": { "dependencies": [ "jsr:@hono/hono@4.5.8", "jsr:@std/assert@1.0.2", "jsr:@std/http@1.0.2", - "jsr:@std/path@1.0.2" + "jsr:@std/path@1.0.2", + "npm:@octokit/rest@21.0.1" ] } } diff --git a/src/server.test.ts b/src/app.test.ts similarity index 97% rename from src/server.test.ts rename to src/app.test.ts index 9340a1e..2fd146c 100644 --- a/src/server.test.ts +++ b/src/app.test.ts @@ -1,7 +1,7 @@ import { assertEquals } from "@std/assert"; import { STATUS_CODE } from "@std/http/status"; -import { app } from "./server.ts"; +import { app } from "./app.ts"; import { exportRepo, testRef, diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..9c76648 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,36 @@ +import { type Context, Hono } from "@hono/hono"; +import { STATUS_CODE } from "@std/http/status"; +import { UserAgent } from "@std/http/user-agent"; + +import { checkRedirect, getContent } from "./libs/mod.ts"; + +/** + * The Hono application for this project. + * + * @example Access without a user agent + * ```ts + * const res: Response = await app.request("/"); + * ``` + * @example Access with a user agent + * ```ts + * const res: Response = await app.request("/", { + * headers: { "User-Agent": new UserAgent("Chrome/1.2.3").toString() }, + * }); + * ``` + */ +export const app: Hono = new Hono(); +app + .get("/:ref?", async (ctx: Context) => { + const ref: string = ctx.req.param("ref"); + const url: URL | null = checkRedirect( + new UserAgent(ctx.req.header("User-Agent") ?? ""), + ref, + ); + + return url + ? ctx.redirect(url.toString(), STATUS_CODE.PermanentRedirect) + : ctx.text(...await getContent(ref)); + }) + .get("*", (ctx: Context) => { + return ctx.redirect("/", STATUS_CODE.SeeOther); + }); diff --git a/src/server.ts b/src/server.ts index 4efc307..add4720 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,38 +1,8 @@ -import { type Context, Hono } from "@hono/hono"; -import { STATUS_CODE } from "@std/http/status"; -import { UserAgent } from "@std/http/user-agent"; - -import { checkRedirect, getContent } from "./libs/mod.ts"; - /** - * The Hono application for this project. - * - * @example Access without a user agent - * ```ts - * const res: Response = await app.request("/"); - * ``` - * @example Access with a user agent - * ```ts - * const res: Response = await app.request("/", { - * headers: { "User-Agent": new UserAgent("Chrome/1.2.3").toString() }, - * }); - * ``` + * This file is the entry point for the server. + * @module */ -export const app = new Hono(); -app - .get("/:ref?", async (ctx: Context) => { - const ref: string = ctx.req.param("ref"); - const url: URL | null = checkRedirect( - new UserAgent(ctx.req.header("User-Agent") ?? ""), - ref, - ); - return url - ? ctx.redirect(url.toString(), STATUS_CODE.PermanentRedirect) - : ctx.text(...await getContent(ref)); - }) - .get("*", (ctx: Context) => { - return ctx.redirect("/", STATUS_CODE.SeeOther); - }); +import { app } from "./app.ts"; Deno.serve(app.fetch); From da0fc45e475e031bc6167ebe264df685f25963c3 Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:32:35 +0900 Subject: [PATCH 09/10] ci(lint): Lint JSDoc (#49) Check the document comments are correct. --- .github/workflows/test.yml | 3 +++ src/app.ts | 1 + src/libs/content.ts | 2 +- src/libs/redirect.ts | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a48cba0..61710ae 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,6 +33,9 @@ jobs: - name: ๐Ÿงน Lint Check run: deno lint + - name: ๐Ÿ“š Lint the JSDoc + run: deno doc --lint ./src + - name: ๐Ÿ“ Format Check run: deno fmt --check diff --git a/src/app.ts b/src/app.ts index 9c76648..3ec021c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,5 @@ import { type Context, Hono } from "@hono/hono"; +export type { Hono }; import { STATUS_CODE } from "@std/http/status"; import { UserAgent } from "@std/http/user-agent"; diff --git a/src/libs/content.ts b/src/libs/content.ts index 5494d85..bdaaf74 100644 --- a/src/libs/content.ts +++ b/src/libs/content.ts @@ -1,11 +1,11 @@ import { Octokit } from "@octokit/rest"; import type { StatusCode } from "@std/http"; +export type { StatusCode }; import { getRepository, githubToken } from "./env.ts"; /** * Get the content of the repository. - * @internal * * @param ref The name of the branch, tag or commit hash * @returns The content of the repository and the status code diff --git a/src/libs/redirect.ts b/src/libs/redirect.ts index fb479d4..386837a 100644 --- a/src/libs/redirect.ts +++ b/src/libs/redirect.ts @@ -1,11 +1,11 @@ import type { UserAgent } from "@std/http/user-agent"; +export type { UserAgent }; import { getRepository } from "./env.ts"; import { getGitHubUrl } from "./utils.ts"; /** * Check if accessed from a browser and return the GitHub URL. - * @internal * * @param userAgent The user agent accessed from * @param ref="master" The branch, tag, or commit hash From dec4942e6be0d99a49e81143e7a20d208aa06aa4 Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Fri, 30 Aug 2024 12:46:12 +0900 Subject: [PATCH 10/10] ci(release): Add GitHub token to the env GitHub CLI needs the token to run. --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9f07f00..f04828a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,8 @@ jobs: - name: ๐Ÿš€ Rrelease a New Version if: ${{ steps.version-has-changed.outputs.stop != 'true' }} run: gh release create "v${{ steps.version.outputs.prop }}" --generate-notes + env: + GH_TOKEN: ${{ github.token }} - name: โฌ†๏ธ Publish to JSR if: ${{ steps.version-has-changed.outputs.stop != 'true' }}