プログレッシブ Web アプリケーション (PWA) は、ネイティブ アプリに似た特性を持つことから、広く注目を集めて受け入れられつつあり、Web ブラウザーとの互換性も進んでいます。PWA をデプロイするときには、セキュリティ上の必須の考慮事項として、安全にホスティングされていなくてはなりません。このため、PWA の機能は安全でない URL、つまり https:// プロトコルを使用しない URL 上では動作しません。今回は、安全な URL に PWA をデプロイする自動デプロイ パイプラインを Firebase 上に作成します。

前提条件

この記事の手順に沿ってチュートリアルを進めるには、いくつかの準備が必要です。

  1. JavaScript の基礎を理解する
  2. Node.js をローカル システムにインストールする
  3. HTTP サーバー モジュールをローカルシステムにグローバルにインストールする (npm install -g http-server)
  4. Firebase アカウントを用意する
  5. CircleCI アカウントを用意する
  6. GitHub アカウントを用意する

これらのインストールとセットアップが済んだら、いざチュートリアルを始めましょう。

デモ アプリケーションをセットアップする

最初のタスクは、Firebase にデプロイするデモ アプリケーションを作成することです。以下のコマンドを実行して、プロジェクト用のディレクトリを作成し、ディレクトリのルートに移動します。

mkdir my-pwa-firebase
cd my-pwa-firebase

次に、アプリケーションのホーム ページを作成します。プロジェクトのルートに index.html という名前のファイルを作成して、以下のコードを貼り付けます。


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="manifest" href="manifest.json" />
    <meta name="theme-color" content="#db4938" />
    <link rel="stylesheet" type="text/css" href="styles.css" media="all" />

    <title>DogVille</title>
  </head>
  <body>
    <h2>
      Welcome to the home of Dogs
    </h2>

    <div class="dog-list">
      <div class="dog-pic"><img width="300px" src="images/dog1.jpg" /></div>
      <div class="dog-pic"><img width="300px" src="images/dog2.jpg" /></div>
      <div class="dog-pic"><img width="300px" src="images/dog3.jpg" /></div>
      <div class="dog-pic"><img width="300px" src="images/dog4.jpg" /></div>
    </div>

    <script src="app.js"></script>
  </body>
</html>

上記のホーム ページのファイルは、3 つのファイルとリンクしています。manifest.json は、PWA の「ホーム画面に追加」機能を設定するために使用します。styles.css は、ページに基本的なスタイルを適用します。app.js は、サービス ワーカー (この後の手順で作成) を読み込みます。ページの本文には「Welcome to the home of Dogs」というタイトルを配置し、その下に犬の画像の一覧を表示しています。お気付きのとおり、今回の例で使用するのは犬のサイトです (愛猫家の皆さん、ごめんなさい)。

プロジェクトのルートの images フォルダーに入っている犬の画像を、index.html で参照しています。ファイル名は images フォルダー内の実際のファイル名に従います。フォルダーを作成したら、犬の写真はこちらから無料でダウンロードできますので、適宜名前を変更してください。

次はスタイルの追加です。プロジェクトのルートに styles.css というファイルを作成し、以下のコードを張り付けます。

body {
  background-color: orange;
}

h2 {
  color: white;
}

このファイルでは、ページの背景色を設定し、タイトルのテキストを白色にしています。それでは、アプリを試してみましょう。プロジェクトのルートで、以下のコマンドを実行してグローバルの http-server モジュールを呼び出し、アプリにサービスを提供するローカル サーバーを起動します。

http-server

ブラウザーで URL を読み込むと、下のような画面が表示されるはずです (犬の画像は各自で異なるでしょう)。

アプリ最初の画面

サービス ワーカーを追加する

PWA の動力源であるサービス ワーカーを追加します。プロジェクトのルートに serviceworker.js という名前のファイルを作成して、以下のコードを貼り付けます。

var cacheName = "sw-v1";
var filesToCache = [
  "./",
  "./index.html",
  "./styles.css",
  "./app.js",
  "./images/dog1.jpg",
  "./images/dog2.jpg",
  "./images/dog3.jpg",
  "./images/dog4.jpg"
];

self.addEventListener("install", function (e) {
  console.log("<ServiceWorker> ---- Install v1");
  e.waitUntil(
    caches.open(cacheName).then(function (cache) {
      console.log("<ServiceWorker> --- Caching app shell");
      return cache.addAll(filesToCache);
    })
  );
});

self.addEventListener("activate", (event) => {
  event.waitUntil(self.clients.claim());
});

self.addEventListener("fetch", function (event) {
  event.respondWith(
    caches.match(event.request).then(function (response) {
      if (response) {
        return response;
      }
      return fetch(event.request);
    })
  );
});

上記のサービス ワーカーのファイルでは、プロジェクトのルートや画像など、すべての静的ファイルをキャッシュします。サービス ワーカーをインストールする install イベントをリッスンし、指定した cacheName を識別子として使用してこれらの静的ファイルのキャッシュを作成します。

次に、activate イベントをリッスンし、新たにインストールされたサービス ワーカーが古いバージョンのものではなく、キャッシュを提供するものとなったタイミングを拾います。

最後に、fetch イベントをリッスンしてリクエストを傍受し、リクエストされたリソースがキャッシュに既に存在するかどうかをチェックします。存在する場合はキャッシュしておいたバージョンを提供し、存在しない場合はリソースを取得するために新しいリクエストを行います。

それでは、サービス ワーカーのファイルをアプリケーションに読み込んでみましょう。プロジェクトのルートに app.js という名前のファイルを作成して、以下のコードを貼り付けます。


if ("serviceWorker" in navigator) {
  window.addEventListener("load", function () {
    navigator.serviceWorker.register("./serviceworker.js").then(
      function (registration) {
        console.log("Hurray! Service workers with scope: ", registration.scope);
      },
      function (err) {
        console.log("Oops! ServiceWorker registration failed: ", err);
      }
    );
  });
}

サービス ワーカーを試してみましょう。アプリケーションがまだ実行中であることを確認してから、アプリケーションが現在読み込まれているブラウザー タブでハード リロード (Ctrl + Shift + R) を行います。次に、ブラウザー コンソールをチェックして、サービス ワーカーのインストールを確認するために出力したコンソール ログ メッセージを見てみます。コンソールには以下のようなメッセージが表示されているはずです。

サービスワーカーのインストール

サービス ワーカーがインストールされてオフライン機能が備わったことを確認するには、http-server サービスを Ctrl + C で停止させてから、ブラウザー上でアプリケーションを更新します。アプリケーションが実行されていないため、通常であればこの時点でオフライン ページが表示されますが、サービス ワーカーという魔法の力によって、アプリケーションのホーム ページが表示されます。また、犬の写真もすべてオフラインで読み込まれていることにもご注目ください。

すばらしいですね!

マニフェスト ファイルを追加する

プロジェクトのルートに manifest.json ファイルを作成して、デモ アプリケーションを完成させましょう。こちらのページでは、アイコンと共にシンプルな manifest.json ファイルを生成できます。

以下は、私の manifest.json ファイルのコードです。 : この私の例では、いくつかのアイコンを削除しています。

{
  "name": "my-dogsite-pwa",
  "short_name": "my-dogsite-pwa",
  "theme_color": "#000000",
  "background_color": "#ffffff",
  "display": "standalone",
  "orientation": "portrait",
  "scope": "/index.html",
  "start_url": "/index.html",
  "icons": [
    {
      "src": "icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

上記のファイルでは、アプリケーションの nameshort name、指定の orientation、アプリ バーの theme_color、スプラッシュ スクリーンの background_color を定義します。

注: short_name を設定することをお勧めします。これはアプリ ランチャーまたは新しいタブ ページに表示される名前を指定する任意の項目です。指定しない場合は name が使用され、12 文字を超える部分が切り捨てられます。

icons 配列には、PWA で使用するアイコンを指すアイコン オブジェクト定義のコレクションを追加します。icons フォルダーを作成し、マニフェスト ファイルで指定しているアイコンを忘れずに格納してください。

Firebase へのデプロイメントをセットアップする

これで PWA が完成したので、Firebase にデプロイする準備に取りかかりましょう。まず Firebase ツールがインストールされている必要があります。インストールされているかどうかを確認するには、以下のコマンドを実行します。

firebase

これにより、Firebase コマンドの一覧が CLI に返されます。返されない場合は、以下のコマンドを実行してインストールします。

npm install -g firebase-tools

また、お使いの firebase-tools がバージョン 8 以前の場合も、上のコマンドを実行する必要があります。お使いの firebase-tools バージョンを確認するには、以下のコマンドを実行します。

firebase --version

Firebase で PWA プロジェクトをホスティングするよう設定するには、Firebase プロジェクトを作成します。Firebase コンソールにアクセスして、新しい Firebase プロジェクトを作成します。

[Add Project (プロジェクトを追加)] をクリックし、最初に表示されるページにプロジェクトの名前を入力します。

FirebaseのPWAプロジェクトの追加

[Continue (続行)] をクリックし、Google アナリティクスの追加に関する次ページで、[Enable Google Analytics for this project (このプロジェクトで Google アナリティクスを有効にする)] トグル ボタンをオフにします。今回はデモ プロジェクトのため、分析は必要ありません。

[Create Project (プロジェクトを作成)] をクリックします。プロジェクトのセットアップが完了するのを待ってから、[Continue (続行)] をクリックしてプロジェクト ダッシュボードに移動します。

これでプロジェクトの作成が終わったので、今度はこの作成したプロジェクトを使用して、Firebase で PWA がホスティングされるよう設定します。デフォルトのブラウザーで Firebase にログインしたまま、CLI で以下のコマンドを実行します。

firebase login:ci

このコマンドは、現在ログインしているブラウザーにリダイレクトして Firebase にログインします。認証プロセスが完了したら、画面の「✔ Success! Use this token to login on a CI server」の行の直下に Firebase トークンが表示されます。このトークンは、チュートリアルの後半で必要になるので、安全に保存しておいてください。

次に、PWA プロジェクトのルートで以下のコマンドを実行して、Firebase プロジェクトの設定を初期化します。

firebase init

画面に「

? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices.

(このフォルダーに設定したい Firebase CLI 機能は何ですか? スペースキーを押して機能を選択し、Enter キーを押して選択を確定してください。)」というプロンプトが表示されます。矢印キーで [Hosting (ホスティング)] オプションに移動し、スペースキーで選択して Enter キーで次へ進みます。

プロジェクト機能の選択

次のプロンプトでは、ローカル プロジェクトを Firebase アカウントの Firebase プロジェクトに関連付けます。ここでは、既存のプロジェクトを使用するか、新しいプロジェクトを作成するかを選択します。[Use an existing project (既存のプロジェクトを使用)] を選択し、Enter キーを押して次に進みます。この選択により、ローカルの Firebase プロジェクトが CLI ツールに読み込まれ、次のプロンプトでそれを選択できるようになります。先ほど Firebase コンソールで作成したプロジェクトを選択します。

Enter キーを押して選択を確定します。

プロジェクトの選択

次のプロンプトでは、プロジェクトのフォルダーをたずねられ、デフォルトで public というフォルダーが提示されます。PWA プロジェクトはすべてをルートで処理するため、/ だけ入力し、Enter キーを押して進めます。

次のプロンプトでは「? Configure as a single-page app (rewrite all urls to /index.html)? (シングルページ アプリとして構成 (すべての URL を /index.html に書き換え) しますか?)」と聞かれます。アプリケーション全体が index.html 内に存在しているため、y と入力して Enter キーを押します。このプロンプトは、シングルページ アプリケーションと従来のマルチ ページ アプリケーションを区別するための重要な設定です。これにより、Firebase Hosting でのアプリの処理方法が決まります。

次のプロンプトでは、既に index.html ファイルがあることが検知された場合に上書きするかどうかをたずねられます。ここは N を入力して、Enter キーを押します。これでセットアップが完了し、このアプリケーションのプロジェクト ID が保存された .firebaserc ファイル、先ほどのセットアップ プロセスで指定したオプションとその他のデフォルト設定の詳細が記された firebase.json ファイル、Firebase 用の標準の .gitignore ファイルが作成されます。

これで、デプロイ パイプラインの作成に進むことができます。

CD パイプラインを構築する

自動デプロイのパイプラインをセットアップするには、以下の手順を実行します。

  1. CircleCI アカウントに接続されているリモートリポジトリ (今回は GitHub) に PWA プロジェクトをプッシュします。
  2. アプリケーションを CircleCI の新規プロジェクトとして追加します。
  3. Firebase トークンを環境変数として CircleCI プロジェクトに追加します。
  4. プロジェクト内にローカルに firebase-tools をインストールします。
  5. パイプラインを構成するファイルを作成します。
  6. プロジェクトの変更をリポジトリにプッシュして、デプロイを開始します。

さっそく始めましょう。以下のコマンドを実行して、package.json ファイルのスキャフォールディングをすばやく行います。

npm init -y

プロジェクト フォルダーを git リポジトリとして初期化し、リモートリポジトリにプッシュします。

次に、PWA プロジェクトのリポジトリを CircleCI プロジェクトとして設定します。

CircleCI コンソールで、[Add Projects (プロジェクトの追加)] ページに移動します。

プロジェクトの追加

[Set Up Project (プロジェクトをセットアップ)] をクリックすると、下の画面が開きます。

ビルドの開始

セットアップ ページで [Start Building (ビルドを開始)] をクリックします。ビルドを開始する前に、提供されている CircleCI 設定ファイルをダウンロードして別のブランチに配置するか、手動で設定するかを選択するよう求められます。

手動でビルドの開始

[Add Manually (手動で追加)] を選択します。すると、設定ファイルがセットアップされていることを確認する別のダイアログが表示されます。

設定ファイルの確認

[Start Building (ビルドを開始)] をクリックして、セットアップを完了します。これで、すぐにパイプラインがトリガーされます。この時点ではまだパイプラインの設定ファイルを追加していないため、ビルドは失敗します。

次のステップとして、先ほど作成した CircleCI プロジェクトの環境変数として Firebase トークンを追加します。[Pipelines (パイプライン)] ページで、PWA プロジェクトを選択した状態で [Project Settings (プロジェクト設定)] をクリックします。

プロジェクトの設定画面

設定ページのサイド メニューから [Environment Variables (環境変数)] を選択します。環境変数のページで [Add Environment Variable (環境変数を追加)] をクリックします。ダイアログ ボックスが開いたら、Name* フィールドに FIREBASE_TOKENValue* フィールドに前の手順で CLI から取得した Firebase トークンを入力します。[Submit (送信)] をクリックしてプロセスを完了します。これで、トークン変数が登録されました。

プロジェクトの設定

ローカル システムの PWA プロジェクトに戻ります。以下のコマンドを実行して、プロジェクトのルートに firebase-tools をインストールし、package.json に開発用依存関係として登録します。

npm install -D firebase-tools

そのプロセスが完了したら、デプロイの設定ファイルを作成します。PWA プロジェクトのルートに、.circleci という名前のフォルダーを作成し、その中に config.yml というファイルを作成します。その config.yml ファイルに、以下のコードを貼り付けます。

version: 2
jobs:
  build:
    docker:
      - image: circleci/node:10.16.0
    working_directory: ~/repo
    steps:
      - checkout
      # Download and cache dependencies
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "package.json" }}
            - v1-dependencies-
      - run:
          name: Install Dependencies
          command: npm install
      - save_cache:
          key: v1-npm-deps-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules
      - run:
          name: Deploy to Firebase
          command: ./node_modules/.bin/firebase deploy --token "$FIREBASE_TOKEN" --only hosting

上記の設定ファイルでは、まずリモートリポジトリからプロジェクトをチェックアウトしています。次に、依存関係をインストールしてキャッシュしてから、ローカルから firebase-tools を実行して、Firebase トークンを使ってアプリケーションをデプロイします。

さて、いよいよ本番です。変更をコミットして、リポジトリにプッシュすることによってデプロイ スクリプトをトリガーし、アプリケーションを Firebase のホスティング環境にデプロイしましょう。

ビルドの成功画面

ビルドをクリックすると、プロジェクトのバックグラウンド処理を確認できます。

ビルドプロセスの画面

Deploy to Firebase セクションで、デプロイしたアプリケーションの URL を確認できます。このチュートリアルでの URL は https://my-dog-site-pwa.web.app です。皆さんのアプリケーションをブラウザーに読み込んで、確認してみてください。

アプリのリリース

ご覧のように上の画面では、アドレスバーに Firebase の URL が読み込まれており、サービス ワーカーがインストールされていることを示すコンソール メッセージが表示されています。ネットワークをオフにしてこのページを更新すると、通常のオフライン画面ではなく、犬の写真がすべて読み込まれてスタイルが適用されたアプリケーションが表示されるはずです。

まとめ

https:// URL の SSL 証明書を設定することは、ほとんどの開発者にとって面白いとは言えないタスクです。それが理由で PWA の採用に及び腰になるケースもあります。しかし、この記事のチュートリアルでお見せしたように、CircleCI と Firebase を使用すれば、PWA を安全にホスティングできる継続的インテグレーション & 継続的デプロイメントのパイプラインを作成、自動化することができます。

すばらしいコーディングができますように!


Fikayo Adepoju は、Web とモバイル テクノロジー、DevOps に精通した LinkedIn Learning (Lynda.com) 講師、フルスタック開発者、テクニカル ライター、テクニカル コンテンツ クリエイターです。スケーラブルな分散アプリケーション開発については 10 年以上の経験を持っています。 CircleCI、Twilio、Auth0、The New Stack のブログで 40 以上の記事を執筆するほか、個人の Medium ページでも情報を発信しており、役立つ知識を多くの開発者に広めることに専心しています。 また、Udemy で動画形式のコース (英語) も開講しています。ぜひご覧ください。

さんの他の投稿を読む Fikayo Adepoju