講義を DX する(完結編)

Dec 1st, 2022 lifehack git github typescript deno

はじめに

これまでの尽力によって,講義資料問題は解決した.

だが,私には致命的な見落としがあった---

致命的な見落とし

コストである.

結論,クラスの資料をデプロイする毎にコストがかかり続け,クラスが増える毎にコストが増え続ける.

これは中長期的な運用を行う上で致命的と言わざるを得ない.

_人人人人人人人人人人人人人人人人人_
> 膨らみ続ける自腹コストとか無理 <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^

方針

デプロイ先を Vercel から Deno Deploy に変更する.

Vercel はコストが増え続けるため,デプロイ先を変更するしかない.新たなデプロイ先は下記の条件を満たす必要がある.

「Deno Deploy」は Deno が公式で提供しているホスティングサービスである.Deno 向けのデプロイ環境であり,エントリーポイントとなる ts ファイルを指定してデプロイする.

つまり,ユーザからリクエストが送られてくると指定した ts ファイルが必ず動作する.

一方,現在講義資料作成に使用している「mdbook」は HTML ファイルをビルドし,ファイルに直接リクエストを送ることで画面に内容を表示している.

...ということは,ts ファイルに「リクエスト先のファイル名を取得して」「サーバ上にある該当ファイルを読み込み」「レスポンスとして返す」処理を記述すればいけるんじゃね??

しかも...

_人人人人_
> 無料 <
 ̄Y^Y^Y^Y^ ̄

実装

実装のポイントは以下の 2 つ.

ts ファイルの実装

まず,プロジェクト直下に server.ts を作成する.

コードは Deno のサーバ立ち上げ(テンプレ)に処理を加えた.

ポイントは以下のとおり.

import { serve } from "https://deno.land/std@0.120.0/http/server.ts";

async function handleRequest(request: Request): Promise<Response> {
  // 🔽 リクエストのURLを取得する.
  const { pathname } = new URL(request.url);

  // 🔽 ファイルの種類とheaderの種類を対応させたテーブルを作成.
  const contentType = {
    html: "text/html; charset=UTF-8",
    css: "text/css",
    js: "text/javascript",
    json: "application/json",
    pdf: "application/pdf",
    jpg: "image/jpeg",
    jpeg: "image/jpeg",
    JPG: "image/jpeg",
    JPEG: "image/jpeg",
    png: "image/png",
    PNG: "image/png",
    gif: "image/gif",
    bmp: "image/bmp",
    svg: "image/svg+xml",
    zip: "application/zip",
  };

  const getFilePath = async (pathname) => {
    // 🔽 ファイルのデータ読み取り処理.リクエストが `/` の場合とファイルが見つからない場合を条件分岐.
    try {
      return await Deno.readFile(
        pathname === "/" ? "./index.html" : `.${pathname}`
      );
    } catch (error) {
      return await Deno.readFile("./404.html");
    }
  };

  const getHeader = (pathname, contentType) => {
    // 🔽 headerはリクエストの拡張子に応じてテーブルから指定.
    try {
      return {
        headers: {
          "content-type": contentType[pathname.split(".")[1]],
        },
      };
    } catch (error) {
      return {
        headers: {
          "content-type": "text/html",
        },
      };
    }
  };

  return new Response(
    await getFilePath(pathname),
    getHeader(pathname, contentType)
  );
}

serve(handleRequest);

GitHub Actions の処理を追加

ts ファイルは完成したので,GitHub Actions でのビルド時に生成ディレクトリ(ここでは book)にコピーする必要がある.

単純にシェルコマンドを追記すれば OK.これで,server.ts がデプロイブランチのルートディレクトリに配置される.

name: github pages

on:
  push:
    branches:
      - class01-main
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2

      - name: Setup mdBook
        uses: peaceiris/actions-mdbook@v1
        with:
          mdbook-version: "0.4.8"
          # mdbook-version: 'latest'

      # mdファイルのビルド
      - run: mdbook build

      # 🔽 `server.ts` を `book` ディレクトリにコピー.
      - run: |
          cp server.ts ./book
          mkdir ./book/samples
          cd samples
          find . \! -name '*.zip' -type d -exec zip -r {}.zip {} \;
          mv *.zip ../book/samples

      # ビルドされたファイル群をgh-pagesブランチにデプロイ
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        if: github.ref == 'refs/heads/class01-main'
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./book
          publish_branch: class01-deploy

デプロイ

Deno Deploy から GUI 操作でデプロイできる.リポジトリとブランチを指定し,エントリーポイントとなるファイルを指定すれば秒で完了する.

ちなみに URL は下記のようになる.

Vercel の URL

https://hogeapp.vercel.app/

Deno Deploy の URL

https://hogeapp.deno.dev/

Vercel よりも更に短くなって良き.

結果

これらにより,講義のフローは以下のようになった.

  1. マスタデータを作成しておく.マスタデータには「Deno Deploy 実行用の ts ファイル」「ts ファイルをコピーするコマンドを書いた yml ファイル」を含めておく.
  2. 講師は,クラスが開講するタイミングで main ブランチからクラス用のブランチ(class0n-main,class0n-develop)を作成する.
  3. 各クラス用ブランチの yml ファイルを編集し,GitHub Actions でビルドできるよう設定して実行する.
  4. Deno Deploy でクラス用のプロジェクトを作成し,各クラスのデプロイ用ブランチ(class0n-deploy)の server.ts を参照するよう設定する.

Vercel の場合と比較してパフォーマンス低下(lighthouse のスコアで 78 → 65)があったののの,動けばええやろの精神で良いことにした.

体感それほど変わらないし.講義資料のファイルなので速度が求められる場面はほぼないと言っていい.

まとめ

今回,デプロイ環境を Vercel から Deno Deploy に移すことでコスト問題を解決した.

【前回まで】

【今回】

最低でも年間$240 のコスト削減を成し遂げた.年間 ¥12,000 のワインを 2 本もらえると考えると,極めて意義の大きな仕事であった(作業時間は 2 時間程度).

Deno Deploy は現在 Beta 3 なのだが,このままの体系で運用してくれることを切に願うッ..!

今度こそ本当に以上だ(`・ω・)b