CI/CDのセットアップ② (GitHub & Azure)

前回からの続きです。今回は「Azure Pipelines」を使用して CI 環境を構築します。前回と同様に、クラウド版の他にローカル環境でも実行できる環境を構築します。GitHub Actions も大規模案件に使えますが、運用の工夫や制約への対応が必要なようです。一方、Azure Pipelines は最初から企業・大規模利用を意識した設計になっている様です。今回は Azure Pipelines の CIについて書かれた記事になっています。

Azureを設定します

まずこちらのサイト様等を参考にして、Azureにアカウントを作成しておきます。(作成するにはマイクロソフトアカウントが必要です。GitHub アカウントでも作成可能です。)

多少の制限がありますが、フリープランなら以下の条件内であれば維持費も無料です。

  • 最初の 5 人のユーザーが無料 (Basic ライセンス)
    組織というユニットに自分も含め5人まで招待(参加)できます。
  • Azure Pipelines:
    ・1 つの Microsoft ホスト型 CI/CD (1 つの同時実行ジョブ、1 か月あたり最大 30 時間無料)
    ・自前でホストするエージェント(self-hosted):1 並行ジョブ(時間制限なし)無料。
  • Azure Boards: 作業項目の追跡とボード
  • Azure Repos: 無制限のプライベート Git リポジトリ
  • Azure Artifacts: 組織あたり2 GiB無料

アカウント作成が終了したら Azure にログインして、最初の画面で「Azure DevOps organizations」を選択します。

項目が表示されていない場合は、上部の検索欄に「devops」と入力して表示されたリストから選択します。

「My Azure DevOps Organizations」リンクをクリックして、組織と呼ばれるプロジェクトの入れ物を作成します。

いくつか画面が続き、以下の画面で組織名を入力して作成します。(すでに誰かが使用している組織名は作成できない様です。)

作成された組織名をクリックします。

初期プロジェクトの作成画面が表示されますので、プロジェクト名を入力して「Create project」ボタンを押します。(組織内には複数のプロジェクトを作成可能です。)

以上で、プロジェクト(リポジトリの入れ物)の作成は終了です。プロジェクトを作成すると、同名の空のリポジトリも自動で作成されます。プロジェクトには複数のリポジトリが作成可能です。

色々な方法でリポジトリを初期化できる様なので、お好きな方法をお試しください。

・Azure Repos でリポジトリを初期化して、VSCodeにダウンロード

プロジェクトを選択して、左側のメニューにある「Repos」をクリックします。

「Initialize」ボタンを押します。

「Clone」ボタンを押します。

「Clone in VS Code」ボタンを押します。違う表示になっていた場合は、ドロップダウンから「VS Code」を選択します。

表示されたメッセージで「Visual Studio Code を開く」を選択します。

ファイルを保存するダイアログが表示されますので、任意の場所にリモートリポジトリのコピーを保存するとVSCodeで編集できる様になります。

後は通常の操作と同じです。コミット後に「変更の同期」ボタンを押すと「Azure Repos」にリポジトリがプッシュされます。

・既にあるローカルリポジトリを Azure Repos にプッシュ

GitHub からダウンロードしてきたリポジトリや、最初からローカル環境で作成したリポジトリを Azure Repos にアップロード(プッシュ)する場合はこちらの方法を使用します。

先程と同じように、まず Azure で空の「プロジェクト」を作成しておきます。既にあるプロジェクトに新しいリポジトリを追加したい場合は、左側のメニューから「Repos」を選択します。上部にあるプロジェクト内のリポジトリを選択するドロップダウンから、「New repository」を選択します。

表示された画面に新しいリポジトリ名を入力します。「Add a README」のチェックをはずして「Create」ボタンを押して、空のリポジトリを作成します。

Azure Repos の場合は VSCode のソース管理画面から 「ブランチの発行」ボタンで GitHub の様にアップロード(プッシュ)出来ないので、初回のみターミナル画面で設定を行う必要があります。

Ctrl + @ でターミナル画面を開いて「git remote -v」と入力します。

PS C:\MAMP\htdocs\azure-projects\myapp> git remote -v
origin  https://github.com/mimura-soft/myapp.git (fetch)
origin  https://github.com/mimura-soft/myapp.git (push)
PS C:\MAMP\htdocs\azure-projects\myapp> 

何か表示されたら、以下のコマンドを実行します。(remove とありますが、リモート(GitHub等)のリポジトリが消去されるわけではないので大丈夫です。ローカル(PC)にあるリポジトリの接続設定が消去されます。)

git remote remove origin

Azure に戻って、左側にある「Repos」をクリックして表示された画面にある Push 用の コマンドをコピーします。

VSCode のターミナル画面にコピーしたコマンドを貼り付けて実行します。途中、Azure へのログイン画面が表示されたらログイン処理を行います。

git remote add origin https://mimura-soft@dev.azure.com/mimura-soft/myapp/_git/myapp
git push -u origin --all

以下の様なメッセージが出れば正常に動作してます。

PS C:\MAMP\htdocs\azure-projects\myapp> git push -u origin --all
Enumerating objects: 105, done.
Counting objects: 100% (105/105), done.
Delta compression using up to 12 threads
Compressing objects: 100% (75/75), done.
Writing objects: 100% (105/105), 61.22 KiB | 3.83 MiB/s, done.
Total 105 (delta 35), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Analyzing objects... (105/105) (10 ms)
remote: Validating commits... (18/18) done (1 ms)
remote: Storing packfile... done (52 ms)
remote: Storing index... done (51 ms)
remote: Updating refs... done (214 ms)
remote: We noticed you're using an older version of Git. For the best experience, upgrade to a newer version.
To https://dev.azure.com/mimura-soft/myapp/_git/myapp
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.
PS C:\MAMP\htdocs\azure-projects\myapp> 

Azure を見てみると、リポジトリが正常にプッシュされている様です。後は通常の操作と同じです。VSCode でコミット後に「変更の同期」ボタンを押すと「Azure Repos」に変更がプッシュされます。

・GitHub から直接インポート

GitHub 等の外部のサービスからリポジトリを直接インポートすることが可能です。外部のリポジトリを「クローン」して Azure に「プッシュ」するという動作を行うので、GitHub の「フォーク」という機能の様に元のリポジトリへの参照は持ちません。(インポートされたリポジトリは、Azure でフォークできる様です。)

左側メニューにある「Repos」をクリックして表示された画面にある、「Import」ボタンをクリックします。

公開されているリポジトリの場合は、「Clone URL」にリポジトリのURLを入力して「Import」ボタンを押すとインポートされます。

GitHub のプライベートリポジトリをインポートするには、パーソナルアクセストークン (PAT) と呼ばれるトークンが必要になります。

PAT を取得するには、GitHub にログインして右上にあるプロフィールアイコンをクリックして、表示されたメニューから「Settings」を選択します。

左サイドバーメニューの一番下にある「Developer settings」をクリックします。

表示された画面の左側にある「Personal access tokens」をクリックして、展開されたメニューから「Tokens (classic)」を選択します。

右上にある「Generate new token」ドロップダウンから、「Generate new token (classic)」を選択します。

必須ではありませんが「Note」欄に何か注釈を入力しておきます。さらに「Select scopes」欄にある「repo」にチェックを入れて、一番下にある「Genarate token」ボタンを押します。

以上で PAT の作成は終了です。この画面 (PAT) は一度しか表示されませんので、ページを保存しておくか PAT を控えておいてください。(上の画面にある「Expiration」に従い、デフォルトでは 30 日間有効です。セキュリティ的にはできるだけ短くしておいた方がいい様です。自分が管理するリポジトリの場合は Azure へのエクスポートが完了したら削除してもいいかもしれません。)

再度、Azure の Repos 画面に戻りインポート画面を表示します。プライベートリポジトリのURLを入力してから、「Requires Authentication」にチェックを入れて、GitHub のユーザー名と先ほど取得した PAT を入力して「Import」ボタンを押します。

少し待つと、GitHub からプライベートリポジトリがインポートされます。

Azure Pipelinesを設定/実行します (クラウド版)

新規に作成した Azure DevOps 組織では、Microsoft-hosted(クラウド)エージェントの並列ジョブ (parallelism) の無料枠が自動的に付与されないので、申請して有効化する必要があります。有効化するには公式フォーム (https://aka.ms/azpipelines-parallelism-request) から「名前」、「メールアドレス」、「組織名」、「Private / Public 種類」を入力して送信します。申請が承認されると Microsoft-hosted エージェントが使えるようになり、パイプラインが実行できる様になります。ただし、申請から使用可能になるまで4~5営業日かかる場合がある様です。

※(2025/09/25 追記)

使用可能かどうかは以下の方法で確認できます。組織のトップページの左下にある「Organization settings」メニューをクリックします。

左側のメニューにある「Parallel jobs」を選択して表示された画面で、「Microsoft-hosted」が「Free tier」等になっていれば使用可能です。

「azure-pipelines.yml」というファイルの 15 行目を Microsoft-hosted エージェントの Ubuntu ホストで実行する様に修正します。

# azure-pipelines.yml

# GitHub Actions の on: push (branches: [main]) に相当
trigger:
  - main

# PR では自動起動させない(必要なければ削除)
pr: none

jobs:
  - job: test_and_build
    displayName: "Test & Build"
    pool:
      # Microsoft が提供する Ubuntu ホストを使用
      vmImage: 'ubuntu-latest'

    steps:
      # checkout に相当
      - checkout: self
        fetchDepth: 0

      # actions/setup-node@v4 に相当(Node.js 24 系を使用)
      - task: UseNode@1
        displayName: "Use Node.js 24.x"
        inputs:
          version: "24.x"
          checkLatest: true

      # npm ci
      - script: npm ci
        displayName: "Install dependencies (npm ci)"

      # npm run lint
      - script: npm run lint
        displayName: "Lint"

      # npm run test:run
      - script: npm run test:run
        displayName: "Test"

      # npm run coverage
      - script: npm run coverage
        displayName: "Coverage"

      # npm run build
      - script: npm run build
        displayName: "Build"

      - task: CopyFiles@2
        displayName: "Stage coverage to artifacts directory"
        inputs:
          SourceFolder: "$(Build.SourcesDirectory)/coverage"
          Contents: "**"
          TargetFolder: "$(Build.ArtifactStagingDirectory)/coverage-html"

      # actions/upload-artifact@v4 に相当(常に成果物アップロード)
      - task: PublishBuildArtifacts@1
        displayName: "Publish coverage HTML as artifact"
        condition: always()
        inputs:
          path: "$(Build.ArtifactStagingDirectory)/coverage-html"
          artifactName: "coverage-html"

正常に実行されたようです。エージェントが「Hosted Agent」になっています。Microsoft-hosted エージェントは月に 1800 分 (30 時間) まで無料で使用できます。

Azure Pipelinesを設定します (ローカル版)

Self-hosted エージェントは Windows と Linux (WSL2) 上で動作するので、先に両方の環境でアプリを起動するところまでご説明します。実際の動かし方は次のセクションをご覧ください。

・Windows 上で Self-hosted エージェントを起動する方法

Azure Pipelines の Self-hosted エージェントを登録する際に必要になるので、先にパーソナルアクセストークン (PAT) を作成しておきます。

Azure DevOps トップ画面の右上にある、「User settings」ボタンをクリックして表示されたメニューから「Personal access tokens」を選択します。

右上にある「New Token」ボタンをクリックします。

表示された画面でトークン名 (Name) に任意の名称を入力します。Scopes で「Custom defined」を選択します。下の項目リストから「Agent pools」項目の「Read & manage」を選択して「Create」ボタンを押します。

以上で PAT の作成は終了です。この画面 (PAT) は一度しか表示されませんので、ページを保存しておくか PAT を控えておいてください。(PAT の有効期限はデフォルトで 30日になっています。有効期限を過ぎると再度 PAT を登録しないと Self-hosted エージェントが起動しなくなる様です。ご都合に合わせて期間を選択してください。)

続いて Self-hosted エージェントのインストールを行っていきます。Azure DevOps トップ画面に戻り、左下にある「Organization settings」をクリックします。

左側にあるサイドメニューから「Agent pools」を選択します。

既にある「Default」エージェントプールを選択します。

右上にある「New agent」ボタンをクリックします。

表示された画面から、「Windows」タブにある「Download」ボタンを押してアプリをダウンロードします。

ダウンロードが完了したら C ドライブ直下に「agent」等の空白を含まないフォルダを作成して、ダウンロードしたアプリを展開します。PowerShell を起動して「C:\agent」フォルダに移動します。(Self-hosted エージェントを Windows のサービスとして登録する場合は PowerShell を管理者権限で起動します。)

エージェント取得ページの説明に従い、「.\config.cmd」コマンドを実行します。16行目のサーバー URL と 18行目のアクセストークン (先ほど作成した PAT) の入力以外はすべてデフォルト (リターンキー) で設定します。

PS C:\WINDOWS\system32> cd C:\agent\
PS C:\agent> .\config.cmd

  ___                      ______ _            _ _
 / _ \                     | ___ (_)          | (_)
/ /_\ \_____   _ _ __ ___  | |_/ /_ _ __   ___| |_ _ __   ___  ___
|  _  |_  / | | | '__/ _ \ |  __/| | '_ \ / _ \ | | '_ \ / _ \/ __|
| | | |/ /| |_| | | |  __/ | |   | | |_) |  __/ | | | | |  __/\__ \
\_| |_/___|\__,_|_|  \___| \_|   |_| .__/ \___|_|_|_| |_|\___||___/
                                   | |
        agent v4.261.0             |_|          (commit 45f3f01)


>> 接続:

サーバー URL の入力 > https://dev.azure.com/mimura-soft
認証の種類 を入力します (PAT の場合は Enter キーを押します) >
個人用アクセス トークン の入力 > ************************************************************************************
サーバーに接続しています...

>> エージェントの登録:

エージェント プール を入力します (default の場合は Enter キーを押します) >
エージェント名 を入力します (DESKTOP-CQEA49P の場合は Enter キーを押します) >
置き換えますか? (Y/N) を入力します (N の場合は Enter キーを押します) >
ツール機能をスキャンしています。
サーバーに接続しています...
ユーザーが正常に追加されました。
エージェント接続をテストしています。
作業フォルダー を入力します (_work の場合は Enter キーを押します) >
2025-09-19 09:32:28Z: 設定が保存されました。
エージェントをサービスとして実行しますか? (Y/N) を入力します (N の場合は Enter キーを押します) >
自動ログオンを構成し、起動時にエージェントを実行しますか? (Y/N) を入力します (N の場合は Enter キーを押します) >
PS C:\agent>

「.\run.cmd」と入力するとアプリが起動します。終了する場合は「Ctrl + C」と y を入力します。

PS C:\agent> .\run.cmd
ツール機能をスキャンしています。
サーバーに接続しています...
2025-09-19 09:48:14Z: ジョブをリッスンしています
・WSL2 上で Self-hosted エージェントを起動する方法

Windows の Self-hosted エージェントの場合と同様に「Default」エージェントプールを選択します。右上にある「New agent」ボタンをクリックします。

表示された画面から、「Linux」タブにあるDownload ボタンの横にあるコピーボタンをクリックして、ダウンロード用のURLをコピーします。

WSL2 を起動して、ターミナル画面で以下の様に入力して「myagent」フォルダにアプリをダウンロードして展開します。(ダウンロードファイルの URL は画面上でマウスを右クリックすると入力できます。)

cd ~
# アプリ用フォルダ作成
mkdir myagent ; cd myagent
# アプリのダウンロード
wget https://download.agent.dev.azure.com/agent/4.261.0/vsts-agent-linux-x64-4.261.0.tar.gz
# 展開します
tar zxvf ./vsts-agent-linux-x64-4.261.0.tar.gz

エージェント取得ページの説明に従い、「./config.sh」コマンドを実行します。サーバー名と PAT トークン以外はすべてデフォルト (リターンキー) で設定できますが、32行目のエージェント名は Windows 用と区別しておいた方がいいかもしれません。

user01@DESKTOP-CQEA49P:~/myagent$ ./config.sh

  ___                      ______ _            _ _
 / _ \                     | ___ (_)          | (_)
/ /_\ \_____   _ _ __ ___  | |_/ /_ _ __   ___| |_ _ __   ___  ___
|  _  |_  / | | | '__/ _ \ |  __/| | '_ \ / _ \ | | '_ \ / _ \/ __|
| | | |/ /| |_| | | |  __/ | |   | | |_) |  __/ | | | | |  __/\__ \
\_| |_/___|\__,_|_|  \___| \_|   |_| .__/ \___|_|_|_| |_|\___||___/
                                   | |
        agent v4.261.0             |_|          (commit 45f3f01)


>> End User License Agreements:

Building sources from a TFVC repository requires accepting the Team Explorer Everywhere End User License Agreement. This step is not required for building sources from Git repositories.

A copy of the Team Explorer Everywhere license agreement can be found at:
  /home/user01/myagent/license.html

Enter (Y/N) Accept the Team Explorer Everywhere license agreement now? (press enter for N) >

>> Connect:

Enter server URL > https://dev.azure.com/mimura-soft
Enter authentication type (press enter for PAT) >
Enter personal access token > ************************************************************************************
Connecting to server ...

>> Register Agent:

Enter agent pool (press enter for default) >
Enter agent name (press enter for DESKTOP-CQEA49P) > WSL2-Ubuntu
Enter replace? (Y/N) (press enter for N) >
Scanning for tool capabilities.
Connecting to the server.
Successfully added the agent
Testing agent connection.
Enter work folder (press enter for _work) >
2025-09-20 05:50:54Z: Settings Saved.
user01@DESKTOP-CQEA49P:~/myagent$

「./run.sh」と入力するとアプリが起動します。終了する場合は「Ctrl + C」を 入力します。

user01@DESKTOP-CQEA49P:~/myagent$ ./run.sh
Scanning for tool capabilities.
Connecting to the server.
2025-09-20 05:58:09Z: Listening for Jobs
Azure Pipelinesを実行します (ローカル版)

とりあえず動作テストを行いたいので、プロジェクトファイル直下に「azure-pipelines.yml」というファイルを作成して以下の内容を書き込みます。作成が完了したらコミットしてプッシュしておきます。(YAML ファイルの名称は任意に変更可能です。設定を行えば配置場所も変更可能です。)

# main ブランチに push されたら実行
trigger:
  - main

pool:
  # Microsoft が提供する Ubuntu ホストを使用
  # vmImage: 'ubuntu-latest'

  # Self-hosted エージェントを使用
  name: "Default"

steps:
  - script: echo "Hello!"
    displayName: "Say Hello"

Azure DevOps のプロジェクト画面から左側にある「Pipelines」メニューを選択して、表示された画面にある「Create Pipeline」ボタンをクリックします。

「Azure Repos Git」をクリックします。

「myapp」リポジトリを選択します。

先ほど追加した YAML ファイルのプレビューが表示されますので「Run」ボタンをクリックします。

「パーミッションが必要」というエラーが出た場合は、「View」ボタンをクリックします。

表示された画面で「Permit」ボタンをクリックします。ダイアログが表示されますので「Permit」ボタンをクリックします。

エラーが消えているのを確認してから「Job」をクリックします。

正常にワークフロー (Pipeline) が実行されたようです。

Self-hosted エージェントも特に問題ない様です。

user01@DESKTOP-CQEA49P:~/myagent$ ./run.sh
Scanning for tool capabilities.
Connecting to the server.
2025-09-21 01:50:27Z: Listening for Jobs
2025-09-21 02:00:42Z: Running job: Job
2025-09-21 02:00:52Z: Job Job completed with result: Succeeded

準備ができましたので、前回実行した GitHub Actions のYAMLファイルを実行してみます。GitHub と Azure の YAML ファイルには互換性が無いので、ChatGPT で Azure 用に変換してもらいました。

GitHub の YAML にはありませんでしたが、カバレッジのレポート等のビルド成果物 (アーティファクト) を Azure にアップロードするために 52~57行目の設定を追加しました。これはカバレッジデータ (coverage) はソースフォルダ (_work/1/s 等) に作成されますが、Azure にアップロードする場合はアーティファクトステージングフォルダ (_work/1/a 等) が参照されるため必要なファイルをコピーしています。

# azure-pipelines.yml

# GitHub Actions の on: push (branches: [main]) に相当
trigger:
  - main

# PR では自動起動させない(必要なければ削除)
pr: none

jobs:
  - job: test_and_build
    displayName: "Test & Build"
    pool:
      # Microsoft が提供する Ubuntu ホストを使用
      # vmImage: 'ubuntu-latest'

      # Self-hosted エージェントを使用
      name: "Default"

    steps:
      # checkout に相当
      - checkout: self
        fetchDepth: 0

      # actions/setup-node@v4 に相当(Node.js 24 系を使用)
      - task: UseNode@1
        displayName: "Use Node.js 24.x"
        inputs:
          version: "24.x"
          checkLatest: true

      # npm ci
      - script: npm ci
        displayName: "Install dependencies (npm ci)"

      # npm run lint
      - script: npm run lint
        displayName: "Lint"

      # npm run test:run
      - script: npm run test:run
        displayName: "Test"

      # npm run coverage
      - script: npm run coverage
        displayName: "Coverage"

      # npm run build
      - script: npm run build
        displayName: "Build"

      - task: CopyFiles@2
        displayName: "Stage coverage to artifacts directory"
        inputs:
          SourceFolder: "$(Build.SourcesDirectory)/coverage"
          Contents: "**"
          TargetFolder: "$(Build.ArtifactStagingDirectory)/coverage-html"

      # actions/upload-artifact@v4 に相当(常に成果物アップロード)
      - task: PublishBuildArtifacts@1
        displayName: "Publish coverage HTML as artifact"
        condition: always()
        inputs:
          path: "$(Build.ArtifactStagingDirectory)/coverage-html"
          artifactName: "coverage-html"

正常に実行されたようです。

生成されたアーティファクトをダウンロードするには Job 画面のルート項目 (Test & Build 等) をクリックして、右画面にある「artifact」リンクをクリックします。

表示されたリストの右側にあるボタンを押して表示されたメニューから、「Download atrifacts」を選択すると ZIP 形式でダウンロードできます。

とりあえず今回はここまでとします。次回は、GitHub と Azure で AWS の S3 というサービスにデプロイ (CD) してみたいと思います。

以上です。よろしかったらお試しください。