GitHub CI/CD で VPS へ自動デプロイするまで
はじめに
2025年の今回初めてGitHubのCI/CDを使ってみてとても便利だったので、やり方・手順を忘備録として残しました。
今回対象となるプロジェクトは個人で開発している小さなjsライブラリプロジェクトです。
GitHubで管理・公開しているこのnu.jsにタグをつけてプッシュした時点で、以下の処理をCI/CDで自動化させます。
- バージョンタグを取得する
- コンテンツをビルドする
- プレースホルダーに①を埋め込む
- 公開用のVPSサーバーへSSHで接続
- rsyncでコンテンツを同期する
と、こんな感じです。シェル一発でもできる作業なので、わざわざCI/CDで実現しなくても良さそうではありますが、実際やってみるとこれが結構便利で楽しかったです。なるほど、CI/CDというのはGitHub上にLinuxを立ち上げて、pushなどをトリガーにしてシェルを実行させるような仕組みなのですね! 仮想のmacOSなども実行できるので、iOSアプリのリリース作業もできそうです。ちょっと今まで使ってこなかったのが、損した気分になるほどCI/CDって便利かもです。
CI/CDとは
CI/CDは、ソフトウェアを「こまめに作って、こまめに届ける」ための自動化の仕組みで、まさに先に示した通りです。
CI(Continuous Integration)は、開発者がコードをpushするたびに、自動でビルド・テスト・静的解析を実行して、早い段階で不具合を見つけるしくみ。
CD(Continuous Delivery / Deployment)はテストを通った成果物を「いつでも本番に出せる状態」に自動で用意する(ステージング配置やアーティファクト化まで)。テスト通過後に「本番へ自動リリース」までやる。
こうすることで、早期にバグ検知、手作業ミス削減、リリース頻度向上、レビューと承認の見える化できます。
手順① サーバ側:デプロイ鍵を用意
ここからは実際に個人プロジェクトのデプロイをCI/CDで自動化させた手順をご紹介します。
ローカル(macOS)で鍵を作って、サーバーの
authorized_keys
に登録します。
# macOS 側で
ssh-keygen -t ed25519 -f ~/.ssh/nujs_deploy -C "nu-js deploy" -N ""
# cat 方式
cat ~/.ssh/nujs_deploy.pub | ssh xxx@example.com 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
手順② GitHub Secrets を登録
リポジトリ Settings → Secrets and variables → Actions に以下を追加します。
SSH_HOST
:example.com
SSH_USER
:xxx
SSH_PORT
:22
(省略可)SSH_PRIVATE_KEY
:~/.ssh/nujs_deploy
の 中身(秘密鍵テキスト)REMOTE_PATH
:/home/xxx/somewhere
注意点としては Environment secrets
ではなく
Repository secrets
側に設定するようにしてください。
手順③ ワークフローを作成(.github/workflows/deploy.yml)
ここまで準備ができましたら、いよいよ GitHub Actions を設定します。冒頭で説明した通り、大まかに次のような流れを実現してます。
- バージョンタグを取得する
- コンテンツをビルドする
- プレースホルダーに①を埋め込む
- 公開用のVPSサーバーへSSHで接続
- rsyncでコンテンツを同期する
タグ v*
を push
した時と、手動実行(workflow_dispatch)でこの Actions
が走ります。バージョンはタグ名があれば優先して採用し、手動入力 or
日付でも埋めるようになってます。
name: deploy-nu-js
on:
workflow_dispatch:
inputs:
version:
description: 'VERSION(例: v2025.8.11.1)未入力なら実行時刻で生成'
required: false
push:
tags:
- 'v*'
jobs:
buildAndDeploy:
runs-on: ubuntu-latest
env:
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_PORT: ${{ secrets.SSH_PORT }}
REMOTE_PATH: ${{ secrets.REMOTE_PATH }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Decide version string
id: ver
shell: bash
run: |
if [ "${{ github.ref_type }}" = "tag" ] && [ -n "${{ github.ref_name }}" ]; then
V="${{ github.ref_name }}"
elif [ -n "${{ inputs.version }}" ]; then
V="${{ inputs.version }}"
else
V="v$(date +%Y.%m.%d.%H%M)"
fi
echo "version=$V" >> "$GITHUB_OUTPUT"
- name: Build (same as your script)
shell: bash
run: |
set -e
find ./ -name '.DS_Store' -delete -print || true
echo "👉 build/ ディレクトリを再作成..."
rm -rf build
mkdir -p build/src build/static build/sample build/lib
echo "📁 必要なファイルをコピー中..."
cp index.html build/
cp -R src build/
cp -R static build/
cp -R sample build/
cp -R lib build/
# バージョン埋め込み(Linux sed は -i 拡張子不要)
sed -i "s/__VERSION__/${{ steps.ver.outputs.version }}/g" "build/index.html"
# 入力の事前チェック(空なら明示的に fail)
- name: Preflight check
shell: bash
run: |
[ -n "${{ secrets.SSH_HOST }}" ] || { echo "❌ SSH_HOST is empty. Set repository secret or variable."; exit 1; }
[ -n "${{ secrets.SSH_USER }}" ] || { echo "❌ SSH_USER is empty."; exit 1; }
[ -n "${{ secrets.SSH_PRIVATE_KEY || '' }}" ] || { echo "❌ SSH_PRIVATE_KEY is empty."; exit 1; }
[ -n "${{ secrets.REMOTE_PATH }}" ] || { echo "❌ REMOTE_PATH is empty."; exit 1; }
- name: Setup SSH key
shell: bash
run: |
set -e
install -m 700 -d ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_deploy
chmod 600 ~/.ssh/id_deploy
# known_hosts 登録(ホスト鍵検証)
ssh-keyscan -p "${SSH_PORT:-22}" "${SSH_HOST}" >> ~/.ssh/known_hosts
- name: Prepare remote directory
shell: bash
run: |
ssh -i ~/.ssh/id_deploy -p "${SSH_PORT:-22}" \
-o StrictHostKeyChecking=yes \
"${SSH_USER}@${SSH_HOST}" "mkdir -p '${REMOTE_PATH}'"
- name: Deploy (rsync over SSH)
shell: bash
run: |
rsync -avu --delete --exclude='.sass-cache' \
-e "ssh -i ~/.ssh/id_deploy -p ${SSH_PORT:-22} -o StrictHostKeyChecking=yes" \
build/ "${SSH_USER}@${SSH_HOST}:${REMOTE_PATH}"
- name: Done
run: echo "Done🎉 https://apppppp.com/kit/nu-js/"
使い方
完成した GitHub Actions が動くか試してみましょう。
タグのプッシュがトリガーで発火する設定なので、以下のように実行してみます。
git tag v2025.8.31.1 && git push origin v2025.8.31.1
公開サーバーへアクセスすると、最新が反映されてました。CI/CDがはじめての場合、結構感動しますね!
手動実行も可能です。Actions
→ deploy-nu-js
→ Run workflow
でプロンプトが表示されますので
version
を自由入力して実行できます。