はじめに
この記事はGiteaを使ったCI/CDパイプラインの構築方法を展開する連載の一つである。
連載の各記事へは以下からジャンプできる。
- Giteaインストール編
- アプリケーション構築編
- テスト編
- ビルド編
- デプロイ編
CI/CDパイプラインでデプロイする
この記事では以下の赤枠部である、デプロイの自動実行をやってみよう。
アプリケーションのデプロイ方法は多種多様である。クラウド環境にデプロイするかもしれないし、オンプレミス環境にデプロイするかもしれない。
今回は例としてSSHを使ってオンプレミス環境のサーバーにデプロイする想定で進めるが、デプロイ手順がCLIで完結するなら今回紹介する方法の応用で環境を問わずデプロイを自動化できる。
Workflowにデプロイのジョブを追加する
.gitea/workflows/workflow.ymlにdeployのジョブを追加する。
このジョブはサーバーへSSHで接続し、Dockerイメージをコンテナとして実行する。
name: Workflow
on:
push:
branches:
- main # mainブランチに対して実行する
env:
NODE_VERSION: "24.14.1" # 使用するNode.jsのバージョンを定義する
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: npm ci
- name: Run typecheck
run: npm run typecheck
test:
needs: typecheck # typecheckが通ったら実行する
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: npm ci
- name: Run test
run: npm run test
build:
runs-on: ubuntu-latest
needs: [typecheck, test] # typecheckとtestが通ったら実行する
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# Docker BuildxにHTTPのコンテナレジストリへのpushを許可する
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
buildkitd-config-inline: |
[registry."${{ vars.REGISTRY_ENDPOINT }}"]
http = true
insecure = true
# コンテナレジストリへの認証情報を設定する
- name: Log in to Gitea container registry
run: |
mkdir -p ~/.docker
AUTH=$(echo -n "${{ gitea.actor }}:${{ secrets.PERSONAL_ACCESS_TOKEN }}" | base64 -w 0)
echo "{\"auths\":{\"${{ vars.REGISTRY_ENDPOINT }}\":{\"auth\":\"$AUTH\"}}}" > ~/.docker/config.json
- name: Build and push
run: |
IMAGE_NAME=$(node -p "require('./package.json').name")
TAG_NAME=$(node -p "require('./package.json').version")
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag ${{ vars.REGISTRY_ENDPOINT }}/${{ gitea.repository_owner }}/$IMAGE_NAME:$TAG_NAME \
--tag ${{ vars.REGISTRY_ENDPOINT }}/${{ gitea.repository_owner }}/$IMAGE_NAME:latest \
--cache-from type=registry,ref=${{ vars.REGISTRY_ENDPOINT }}/${{ gitea.repository_owner }}/$IMAGE_NAME:buildcache \
--cache-to type=registry,ref=${{ vars.REGISTRY_ENDPOINT }}/${{ gitea.repository_owner }}/$IMAGE_NAME:buildcache,mode=max \
--provenance=false \
--push \
.
+ deploy:
+ runs-on: ubuntu-latest
+ needs: [typecheck, test, build]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v5
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ env.NODE_VERSION }}
+ - name: Prepare SSH Key
+ run: |
+ # ジョブ実行用のコンテナにSSHの設定をしてやる必要がある
+ mkdir ~/.ssh && chmod 0700 ~/.ssh
+ echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
+ chmod 0400 ~/.ssh/id_rsa
+ ssh-keyscan -p22 -H ${{ vars.DEPLOYMENT_HOST }} > ~/.ssh/known_hosts
+ - name: Deploy
+ run: |
+ IMAGE_NAME=$(node -p "require('./package.json').name")
+ ssh ${{ vars.DEPLOYMENT_USER }}@${{ vars.DEPLOYMENT_HOST }} "
+ docker pull ${{ vars.REGISTRY_ENDPOINT }}/${{ gitea.repository_owner }}/$IMAGE_NAME:latest &&
+ docker stop $IMAGE_NAME;
+ docker rm $IMAGE_NAME;
+ docker run -d --name $IMAGE_NAME -p 3030:3000 ${{ vars.REGISTRY_ENDPOINT }}/${{ gitea.repository_owner }}/$IMAGE_NAME:latest
+ "
deployのジョブでは以下のことを行っている。
- リポジトリをチェックアウトする
- Node.js環境を用意する
- SSHの設定を行う
- デプロイ先へSSH接続し、Dockerイメージのプルを行ったあとコンテナとして実行する
リポジトリのSettingsにあるActions-Variablesからデプロイ先サーバーのIPアドレスとユーザー名を追加する。これはSSHの接続先になる。
リポジトリのSettingsにあるActions-Secretsからデプロイ先サーバーのSSHの秘密鍵を登録する。(秘密鍵の内容は別途デプロイ先サーバーにSSH接続し、cat ~/.ssh/id_rsaなどを実行することで確認できる)
末尾には改行を入れること。これをしないと接続に失敗する。
デプロイ先サーバーからGiteaに登録されたDockerイメージを参照できるようにする
前回の記事に書いたとおり、Dockerはデフォルトの設定ではHTTPのレジストリと通信しようとするとエラーになる。これはデプロイ先サーバーでも同様である。
デプロイ先サーバーの/etc/docker/daemon.jsonに以下の設定をマージする。
{
"insecure-registries": ["xxx.xxx.xxx.xxx:yyyy"]
}
Dockerデーモンを再起動する。
sudo systemctl restart docker.service docker.socket
GiteaへプッシュしたときCI/CDパイプラインが実行されることを確認する。
Webブラウザでhttp://${デプロイ先サーバーのIPアドレス}:3030にアクセスしたとき、1+1の結果が返ってきたらデプロイに成功している。
仕様変更に対応する
ここまで長い道のりを辿ってCI/CDパイプラインを構築してきた。
機能追加や仕様変更でその効果を実感することができる。
足し算を1+1から1+2に変更しよう。
import { Hono } from "hono";
import { sum } from "./sum.js";
export const app = new Hono();
/**
* 足し算API
*/
app.get("/sum", (c) => {
+ return c.json({ result: sum(1, 2) });
});
バージョンも上げておこう。
{
"name": "cicd-example",
"type": "module",
+ "version": "1.1.0",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"typecheck": "tsc --noemit",
"test": "vitest"
},
"dependencies": {
"@hono/node-server": "^1.19.14",
"hono": "^4.12.17"
},
"devDependencies": {
"@types/node": "^20.11.17",
"tsx": "^4.7.1",
"typescript": "^5.8.3",
"vitest": "^4.1.5"
}
}
リポジトリへプッシュする。
$ git add -A
$ git commit -m "足し算を変更した"
$ git push
CI/CDパイプラインが自動実行される。内容を確認すると、テストで失敗していることがわかる。
テストのジョブを詳しく見るとAPIの戻り値が期待値と異なることがわかる。テストの期待値の更新を忘れていたようだ。
テストが失敗しているのでDockerイメージのビルドやデプロイは行われていない。
テストコードの期待値を更新する。
import { describe, expect, test } from "vitest";
import { app } from "./app.js";
describe("足し算API", () => {
test("正しい足し算の結果を得られること", async () => {
const response = await app.request("/sum");
// HTTPステータスコードが200であること
expect(response.status).toBe(200);
// 足し算の結果が期待値と一致すること
const body = await response.json();
+ expect(body).toEqual({ result: 3 });
});
});
再度リポジトリへプッシュする。
$ git add src/app.test.ts
$ git commit -m "足し算のテストコードを見直した"
$ git push
CI/CDパイプラインが自動実行される。今度はテストが通り、ビルドもデプロイも成功する。
Webブラウザでhttp://${デプロイ先サーバーのIPアドレス}:3030にアクセスしたとき、1+2の結果が返ってくることから、最新のアプリケーションがデプロイされていることがわかる。
振り返り
- Gitea ActionsのCI/CDパイプラインでアプリケーションをデプロイすることができた
リポジトリ
最終形のリポジトリを以下に展開する。
おわりに
Giteaを使ったCI/CDパイプラインを構築し、テスト・ビルド・デプロイを自動化することができた。
今回の記事では題材にTypeScriptのWebアプリケーションを選んだが、学んだことを応用すればPythonでの開発であってもビルドがWindows向けのバイナリであってもCI/CDパイプラインを構築できる。
自動化によって浮いた時間は開発にも使えるし、別分野の学習にも使える。素晴らしいことだ。









