プログレッシブ Web アプリケーション (PWA) は、ネイティブ アプリに似た特性を持つことから、広く注目を集めて受け入れられつつあり、Web ブラウザーとの互換性も進んでいます。PWA をデプロイするときには、セキュリティ上の必須の考慮事項として、安全にホスティングされていなくてはなりません。このため、PWA の機能は安全でない URL、つまり https://
プロトコルを使用しない URL 上では動作しません。今回は、安全な URL に PWA をデプロイする自動デプロイ パイプラインを Firebase 上に作成します。
前提条件
この記事の手順に沿ってチュートリアルを進めるには、いくつかの準備が必要です。
- JavaScript の基礎を理解する
- Node.js をローカル システムにインストールする
- HTTP サーバー モジュールをローカルシステムにグローバルにインストールする (
npm install -g http-server
) - Firebase アカウントを用意する
- CircleCI アカウントを用意する
- 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"
}
]
}
上記のファイルでは、アプリケーションの name
、short 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
(プロジェクトを追加)] をクリックし、最初に表示されるページにプロジェクトの名前を入力します。
[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 パイプラインを構築する
自動デプロイのパイプラインをセットアップするには、以下の手順を実行します。
- CircleCI アカウントに接続されているリモートリポジトリ (今回は GitHub) に PWA プロジェクトをプッシュします。
- アプリケーションを CircleCI の新規プロジェクトとして追加します。
- Firebase トークンを環境変数として CircleCI プロジェクトに追加します。
- プロジェクト内にローカルに
firebase-tools
をインストールします。 - パイプラインを構成するファイルを作成します。
- プロジェクトの変更をリポジトリにプッシュして、デプロイを開始します。
さっそく始めましょう。以下のコマンドを実行して、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_TOKEN
、Value*
フィールドに前の手順で 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 を安全にホスティングできる継続的インテグレーション & 継続的デプロイメントのパイプラインを作成、自動化することができます。
すばらしいコーディングができますように!