レガシー .NET Framework 4.8 を GitLab CI/CD で自動ビルドする完全ガイド

SVN と CruiseControl.NET+NAnt で運用してきたレガシーな .NET Framework 4.8 プロジェクトを、GitLab CI/CD 上で自動ビルド・自動テストできるようにしたい――そんなときに必要になるのが、Visual Studio をフルインストールせずに msbuild ベースのパイプラインを構成する具体的な手順です。本記事では、移行の全体像から、GitLab Runner の設定例、SDK スタイルプロジェクトへの段階的なモダナイズ戦略まで、現場でそのまま使えるレシピとして整理します。

目次

.NET Framework 4.8 レガシープロジェクトを CI/CD 対応させる全体像

まずは、SVN+CruiseControl.NET+NAnt という古い構成から、GitLab CI/CD に移行する際の「全体像」を押さえておきます。ゴールは次のようなイメージです。

  • ソースコード管理:SVN → GitLab(Git)
  • ビルドエンジン:CruiseControl.NET → GitLab CI
  • ビルドスクリプト:NAnt → PowerShell(あるいはバッチ)+ msbuild
  • ビルド環境:Visual Studio 本体 → Visual Studio Build Tools のみ
  • 将来:旧 csproj → SDK スタイルプロジェクト → .NET 7/8 などへ段階的モダナイズ

このときの典型的な悩みが「Windows VM 上で msbuild が見つからずビルドできない」「NAnt を捨てたいが、どう書き換えればよいか」「GitLab CI の .gitlab-ci.yml をどう書けばよいか」といった点です。そこでまず、主な課題と解決策を一覧にしておきます。

課題解決策・手順備考・補足
msbuild が見つからない / Visual Studio を入れたくない「Visual Studio 2022 Build Tools」(GUI なし・無料)をビルドサーバーや Windows コンテナにインストールし、.NET デスクトップ ビルドツールを有効化する。vs_BuildTools.exe からインストール。これだけで msbuild.exe とテストランナーが揃う。
NAnt 依存を取り除きたいNAnt のビルドスクリプトを PowerShell やバッチに書き換え、msbuild コマンドを直接叩く構成に変更する。複雑な XML スクリプトを脱却し、誰でも読めるスクリプトにすることで保守性が大きく向上。
NuGet パッケージの復元packages.config プロジェクトは nuget.exe restore もしくは msbuild /t:Restore を事前に実行する。.NET Framework 4.x では dotnet restore が効かないケースが多いため注意。
SDK スタイルへ移行したいまずはライブラリプロジェクトから SDK スタイルへ変換する。ASP.NET WebForms は MSBuild.SDK.SystemWeb などの SDK を利用して段階的に移行する。ライブラリは .NET Standard 2.0 化しておくと、将来の .NET 8/9 への移行が楽になる。
GitLab CI へ組み込みたいWindows Runner に Build Tools を事前インストールし、.gitlab-ci.yml から nuget restore → msbuild → テスト → アーティファクト出力の流れを自動化する。自前 VM でも、Microsoft の .NET Framework SDK コンテナでもよい。組織のポリシーに合わせて選択。

既存レガシー環境の棚卸しと移行戦略

いきなり GitLab CI を書き始めるのではなく、まずは「何をどの順番で捨てるか・残すか」を決めておくと失敗しにくくなります。

現状の把握チェックリスト

  • ソース管理は SVN か? ブランチ運用はどうなっているか?
  • CruiseControl.NET の設定 XML 内で、どの NAnt ターゲットが呼ばれているか?
  • NAnt スクリプトではどのタスク(compile / nunit / zip / ftp など)を使っているか?
  • プロジェクトの種類:Class Library / Console / WPF / WinForms / ASP.NET WebForms / WCF など
  • NuGet 管理方式:packages.config か、既に PackageReference を使っているか?

この情報がまとまっていれば、「最初の一歩」は次のように決められます。

  • SVN → GitLab への移行は、現行のリリースブランチ単位で履歴を最低限残す
  • CruiseControl.NET は「見るだけ」にしておき、新 CI が安定してから停止する
  • NAnt スクリプトは、まず「ビルド周り」だけ PowerShell に移植し、デプロイ作業は後で置き換える

一気にすべてをモダン化しようとすると必ず詰まるので、「ビルドが再現できること」を最優先にした段階的移行が現実的です。

ビルド環境の準備:Visual Studio Build Tools と msbuild

.NET Framework 4.8 のレガシープロジェクトを CI でビルドするうえでの最初の壁が、「msbuild がどこにも入っていない」問題です。開発者マシンには Visual Studio 本体が入っていても、ビルドサーバーや GitLab Runner の Windows VM には何も入っていない、というケースが多くあります。

Visual Studio 2022 Build Tools を入れる

Visual Studio 本体ではなく、ビルド専用の「Visual Studio Build Tools」をインストールすることで、次のものが使えるようになります。

  • msbuild.exe(32bit/64bit)
  • C# コンパイラ(csc.exe)
  • テスト実行ツール(vstest.console.exe など)
  • 一部の NuGet 関連ツール

インストール時に必ずチェックしておきたいワークロード/コンポーネントの例です。

  • .NET デスクトップ開発ツール(.NET Framework 4.x のビルドに必要)
  • MSBuild ツール
  • NuGet パッケージ マネージャー
  • 必要であれば C++ ビルドツール(C++/CLI との混在プロジェクトがある場合)

インストールが完了したら、コマンドプロンプトや PowerShell から次のように確認します。

where msbuild

正しくインストールされていれば、例えば次のようなパスが表示されます。

C:\Program Files\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\MSBuild.exe

64bit 版を使いたい場合は MSBuild\Current\Bin\amd64 配下の msbuild.exe を使います。ソリューションの規模が大きい場合は 64bit 版を使うことでメモリ不足エラーを回避しやすくなります。

PATH への追加とバージョン固定

GitLab Runner で msbuild を呼び出すときに、毎回フルパスを書くのは面倒なので、ビルドサーバーの環境変数に msbuild のパスを足しておくと便利です。

setx PATH "%PATH%;C:\Program Files\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin"

ただし、将来別バージョンの Build Tools を追加インストールする可能性がある場合は、あえて PATH には追加せず、スクリプト側でフルパスを指定する方が「どのバージョンでビルドしているか」が明確になります。

NAnt スクリプトを PowerShell+msbuild に置き換える

次のステップは、CruiseControl.NET から呼ばれている NAnt スクリプトを、よりシンプルな PowerShell スクリプトに置き換えることです。ここでは、よくある NAnt の構成を例に変換してみます。

典型的な NAnt スクリプトの例

<project name="SampleApp" default="build">
  <property name="configuration" value="Release" />
  <target name="clean">
    <delete dir="build" />
  </target>

  <target name="restore-packages">
    <exec program="nuget.exe" commandline="restore SampleApp.sln" />
  </target>

  <target name="build" depends="clean, restore-packages">
    <exec program="msbuild.exe"
          commandline="SampleApp.sln /p:Configuration=${configuration}" />
  </target>
</project>

これを PowerShell に置き換えると次のようになります。

param(
    [string]$Configuration = "Release"
)

$ErrorActionPreference = "Stop"

$solution = "SampleApp.sln"
$msbuild = "C:\Program Files\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\MSBuild.exe"
$nuget   = ".\tools\nuget.exe"  # リポジトリ内に格納しておく前提

Write-Host "=== Clean ==="
if (Test-Path "build") {
    Remove-Item "build" -Recurse -Force
}

Write-Host "=== NuGet Restore ==="
& $nuget restore $solution

Write-Host "=== MSBuild ==="
& $msbuild $solution /p:Configuration=$Configuration /m

Write-Host "Build completed."

ポイントは次の通りです。

  • NAnt のターゲットを、そのまま PowerShell の処理ブロックとして書き直す
  • nuget.exe や msbuild.exe のパスは変数にまとめておく(後から差し替えやすくする)
  • エラー時には即座に停止するよう $ErrorActionPreference = "Stop" を設定
  • 最初はリポジトリ直下に build.ps1 として置き、ローカルでも GitLab CI でも同じスクリプトを使う

この PowerShell スクリプトが安定して動くようになれば、CruiseControl.NET 側から NAnt の代わりに build.ps1 を呼ぶこともできますし、GitLab CI からも同じスクリプトを使い回せます。

NuGet パッケージの復元戦略(packages.config と PackageReference)

.NET Framework 4.8 プロジェクトでは、NuGet パッケージの管理方法が混在していることがよくあります。CI に組み込む前に、各プロジェクトがどちらの方式かを確認しておきましょう。

packages.config 方式の場合

古いプロジェクトでは、ソリューション直下に packages フォルダがあり、各プロジェクトに packages.config が置かれている形が一般的です。この場合、CI では次のいずれかの方法で復元します。

  • nuget.exe を使う
nuget.exe restore SampleApp.sln
  • msbuild の Restore ターゲットを使う
msbuild SampleApp.sln /t:Restore

どちらでもよいですが、既に nuget.exe を運用で使っているのであれば、まずは nuget.exe を CI にもそのまま持ち込む方が安全です。

PackageReference 方式の場合

最近の Visual Studio で「パッケージ管理形式を変更」したプロジェクトは、csproj 内で <PackageReference> を使う形になっています。この場合は、次のように msbuild だけで復元とビルドを行うことができます。

msbuild SampleApp.sln /t:Restore,Build /p:Configuration=Release

.NET Framework 4.8 でも PackageReference は利用可能なので、packages.config の多いソリューションでも、少しずつ PackageReference へ移行していくと CI の設定がシンプルになります。

GitLab Runner(Windows)の準備と構成パターン

.NET Framework 4.8 は Windows 専用ランタイムのため、GitLab CI でも Windows Runner が必要になります。代表的な構成パターンは次の 2 つです。

パターン概要メリットデメリット
自前 Windows VM + Shell Executorオンプレやクラウドの Windows Server / Windows 11 に Runner をインストールし、PowerShell スクリプトを実行する。既存資産(ファイル共有、IIS など)との連携が楽。トラブルシュートもしやすい。イメージの再現性が低く、「あのサーバーだけ動く」状態になりやすい。
Windows コンテナ + Docker ExecutorVisual Studio Build Tools や .NET Framework SDK 入りの Windows コンテナイメージを用意し、その上でビルドを行う。イメージを固定すれば、どこでも同じ環境でビルドできる。スケールアウトも容易。Windows コンテナの扱いに慣れが必要。イメージビルドのメンテナンスが増える。

まずは「自前 Windows VM + Shell Executor」で始め、運用が安定してきたら Windows コンテナ化を検討する、という順序がおすすめです。

Windows Runner の登録イメージ

GitLab Runner をインストールしたら、管理者権限の PowerShell で次のように登録します。

gitlab-runner register `
  --url https://gitlab.example.com/ `
  --registration-token XXXXXXXXXXXX `
  --executor shell `
  --description "windows-build-runner" `
  --tag-list "windows,dotnet48" `
  --non-interactive

これで、.gitlab-ci.yml 側から tags: ["windows", "dotnet48"] を指定することで、この Runner 上でジョブが実行できるようになります。

GitLab CI の基本構成:.gitlab-ci.yml の例

ここから、実際に .gitlab-ci.yml をどのように書くかを見ていきます。最もシンプルな例として、次のようなステージ構成を考えます。

ステージ役割主な処理
restoreNuGet パッケージの復元nuget.exe restore / msbuild /t:Restore
buildソリューションのビルドmsbuild /p:Configuration=Release
test単体テストの実行vstest.console.exe / MSTest など
packアーティファクトの作成zip 作成、nuget pack、配布物フォルダへの集約など

.gitlab-ci.yml サンプル(Windows Shell Executor 向け)

stages:
  - restore
  - build
  - test
  - pack

variables:
  CONFIGURATION: "Release"
  SOLUTION_FILE: "SampleApp.sln"

default:
  tags:
    - "windows"
    - "dotnet48"
  before_script:
    - 'echo Running on %COMPUTERNAME%'
    - 'echo Using configuration: %CONFIGURATION%'

restore:
  stage: restore
  script:
    - 'powershell -ExecutionPolicy Bypass -File .\build\restore.ps1 -Solution %SOLUTION_FILE%'
  artifacts:
    paths:
      - packages/
    expire_in: 1h

build:
  stage: build
  script:
    - 'powershell -ExecutionPolicy Bypass -File .\build\build.ps1 -Solution %SOLUTION_FILE% -Configuration %CONFIGURATION%'
  artifacts:
    paths:
      - src/**/bin/%CONFIGURATION%/
    expire_in: 1 week

test:
  stage: test
  script:
    - 'powershell -ExecutionPolicy Bypass -File .\build\test.ps1'
  artifacts:
    when: always
    paths:
      - TestResults/
    expire_in: 1 week

pack:
  stage: pack
  script:
    - 'powershell -ExecutionPolicy Bypass -File .\build\pack.ps1'
  artifacts:
    paths:
      - artifacts/
    expire_in: 1 month
  only:
    - main
    - tags

ここでは、実際のビルドロジックはすべて PowerShell スクリプト(build\restore.ps1 など)に寄せ、.gitlab-ci.yml からはそれらを呼び出すだけにしています。この構造にしておくと、ローカル開発環境でも同じ PowerShell スクリプトを叩くことで、「CI と同じ手順でビルド・テストする」ことができます。

テスト自動化:vstest.console.exe や MSTest の活用

CI/CD では、ビルドが通るだけでなく、自動テストも一緒に回しておくことが重要です。.NET Framework 4.8 では次のようなテストランナーがよく使われます。

  • MSTest(.testproj)
  • NUnit
  • xUnit.net

Visual Studio Build Tools を入れていれば、通常 vstest.console.exe が利用可能です。例えば、テスト DLL をまとめて実行する PowerShell スクリプトは次のように書けます。

$testDlls = Get-ChildItem ".\test" -Recurse -Filter "*Tests.dll"

$vstest = "C:\Program Files\Microsoft Visual Studio\2022\BuildTools\Common7\IDE\Extensions\TestPlatform\vstest.console.exe"
$resultsDir = "TestResults"

if (!(Test-Path $resultsDir)) {
    New-Item -ItemType Directory -Path $resultsDir | Out-Null
}

foreach ($dll in $testDlls) {
    Write-Host "Running tests in $($dll.FullName)"
    & $vstest $dll.FullName `
        /Logger:trx `
        /ResultsDirectory:$resultsDir
}

出力された TRX ファイルは GitLab CI のジョブログからダウンロードできるように、artifacts.paths に追加しておきます。将来的に GitLab のテストレポート機能と連携したい場合は、TRX を JUnit 形式に変換するスクリプトを追加することもできます。

成果物(アーティファクト)管理と配布

レガシーな .NET Framework プロジェクトでは、ビルド成果物の配布方法も多様です。ファイルサーバーへコピー、IIS への WebDeploy、セットアップ EXE の発行、NuGet パッケージ化など、現状の運用を整理しつつ、GitLab CI のアーティファクト機能と組み合わせていきます。

代表的な成果物パターン

種類用途CI/CD での扱い
bin/Release 一式オンプレサーバーへの手動デプロイ、ファイルコピーzip に固めて artifacts として保存。運用担当者がダウンロードして適用。
NuGet パッケージ社内共通ライブラリ、SDK などnuget pack または msbuild /t:Pack で nupkg を作成し、社内 NuGet フィードへ push。
WebDeploy パッケージASP.NET Web アプリの自動デプロイmsbuild の Web 配置ターゲットを利用して zip を生成し、必要に応じてステージング環境へ自動適用。

最初から自動デプロイまでやろうとせず、まずは「誰がビルドしても同じ成果物が得られる」状態を目指すのがおすすめです。そのうえで、ステージング環境への自動デプロイ、本番環境への半自動デプロイなどを段階的に足していきます。

旧式 csproj から SDK スタイルへの段階的移行

将来的に .NET 7/8 など新しい .NET へ移行したい場合、避けて通れないのが「SDK スタイルプロジェクト」への変換です。ただし、ASP.NET WebForms をいきなり .NET 7 に載せ替えるのは現実的ではないため、まずはライブラリから SDK スタイルへ移行するのがポイントです。

ライブラリプロジェクトの SDK スタイル化

クラスライブラリ(.csproj)の場合、次のような方針で SDK スタイルへ移行できます。

  1. 対象プロジェクトを Visual Studio 2022 で開く
  2. 「プロジェクトの移行」機能や拡張機能を利用して SDK スタイルに変換する
  3. 不要になった <Compile Include="..." /> などの項目を削除し、SDK スタイル標準の構成に整える

SDK スタイルの csproj は、非常にシンプルです。

&lt;Project Sdk="Microsoft.NET.Sdk"&gt;
  &lt;PropertyGroup&gt;
    &lt;TargetFramework&gt;net48&lt;/TargetFramework&gt;
    &lt;GenerateAssemblyInfo&gt;false&lt;/GenerateAssemblyInfo&gt;
  &lt;/PropertyGroup&gt;
&lt;/Project&gt;

この段階では、TargetFramework をあえて net48 のままにしておき、「ビルド結果は変えずにプロジェクト形式だけを変えた状態」を作ることが重要です。CI が安定して動くのを確認してから、別ブランチで .NET Standard 2.0 などへの移行を検討すると、リスクを小さくできます。

ASP.NET WebForms / System.Web プロジェクトの注意点

ASP.NET WebForms や古い MVC(System.Web ベース)のアプリは、そのままでは汎用の SDK スタイルに変換できません。この場合、次のようなアプローチを取ります。

  • まずはライブラリ層(ビジネスロジック)を SDK スタイル化し、UI 層(WebForms)は旧式のまま残す
  • Web プロジェクトの csproj は、専用の MSBuild SDK(例:SystemWeb 向け SDK)を利用して SDK スタイル風にする
  • UI 層を .NET 7/8 の MVC / Razor Pages / Blazor などへ載せ替える場合は、別システム並みの工数がかかる前提で計画する

CI 観点では、「ビルド方法が安定しているか」が最重要なので、SDK スタイル化は小さな単位で実施し、毎回 GitLab CI のパイプラインを通して確認するのが安全です。

トラブルシューティングのチェックリスト

実際に GitLab CI で .NET Framework 4.8 のビルドを回し始めると、さまざまなエラーに出会います。よくある原因と対処のヒントを整理しておきます。

症状原因の例対処のヒント
msbuild が見つからないBuild Tools がインストールされていない / PATH が通っていないビルドサーバーに Visual Studio 2022 Build Tools をインストールし、where msbuild で確認。必要ならフルパスで指定。
ビルド時にメモリ不足で落ちる巨大ソリューションを 32bit msbuild でビルドしている64bit msbuild(amd64 ディレクトリ)を使用し、/m オプションの並列度を調整する。
NuGet パッケージが見つからないpackages フォルダが存在しない / プロキシ越しの接続に失敗nuget.exe を使って明示的に restore する。社内 NuGet サーバーを使う場合は nuget.config をリポジトリに含める。
ローカルではビルド成功、CI では失敗CI 環境にだけ足りない SDK・コンポーネントがあるBuild Tools のワークロードを見直し、ローカルと CI をできるだけ同一構成にする。「ローカルで Build Tools だけでビルドする」テストを行う。
Web プロジェクトの発行時にエラーUseWPP_CopyWebApplication などの MSBuild プロパティの違い既存の発行プロファイル(.pubxml)の設定を確認し、CI の msbuild パラメータと揃える。

段階的モダナイズのロードマップ例

最後に、レガシー .NET Framework 4.8 プロジェクトを、GitLab CI/CD と SDK スタイルへ段階的にモダナイズするためのロードマップ例をまとめます。

フェーズ 1:ビルド環境の近代化

  • ビルドサーバーに Visual Studio 2022 Build Tools をインストール
  • ローカルで NAnt → PowerShell+msbuild のビルドスクリプトを作成し、CruiseControl.NET でも置き換え
  • NuGet 復元をスクリプト化し、誰が実行しても同じ成果物になる状態を作る

フェーズ 2:GitLab CI/CD への移行

  • GitLab Runner(Windows)を用意し、Shell Executor で登録
  • .gitlab-ci.yml を作成し、restore → build → test → pack のパイプラインを構築
  • CruiseControl.NET を「バックアップ」として残しつつ、GitLab CI を新しい標準にしていく

フェーズ 3:SDK スタイル・テストの強化

  • クラスライブラリから順に SDK スタイル(net48)へ変換
  • テストプロジェクトも SDK スタイル化し、vstest でまとめて実行できるよう整理
  • カバレッジ計測や静的解析ツールをパイプラインに組み込む

フェーズ 4:.NET 7/8 など新ランタイムへの移行

  • ビジネスロジック層を .NET Standard 2.0 / .NET 6+ に移行
  • UI 層(WebForms など)を新しいアーキテクチャへリプレース(別プロジェクト扱い)
  • GitLab CI/CD で .NET Framework パイプラインと .NET 7/8 パイプラインを並行運用

このようにフェーズを区切っておくことで、「今日は Build Tools を入れるところまで」「今月はライブラリ 3 本だけ SDK スタイル化する」といった小さなマイルストーンを設定しやすくなります。結果として、日々の開発を止めることなく、レガシー資産を少しずつモダンな世界へ引き上げていくことができます。

まとめ:レガシーでも CI/CD は十分に構築できる

.NET Framework 4.8 のレガシープロジェクトでも、次のポイントさえ押さえておけば、GitLab CI/CD 上で安定した自動ビルド環境を構築できます。

  • Visual Studio Build Tools を使うことで、Visual Studio 本体なしで msbuild を動かせる
  • NAnt スクリプトは PowerShell+msbuild に書き換え、CI・ローカル共通のビルドスクリプトにする
  • NuGet 復元は packages.config / PackageReference それぞれに合った方法を選ぶ
  • GitLab Runner(Windows)と .gitlab-ci.yml を組み合わせて、restore → build → test → pack の流れを自動化する
  • SDK スタイル化はライブラリから少しずつ進め、CI を通して安全に移行を確認する

レガシー環境だからといって、CI/CD をあきらめる必要はありません。むしろ、壊したくない大事なシステムだからこそ、「誰がビルドしても同じ結果が得られる」パイプラインを先に整えることが、長期的なリスクを減らす一番の近道になります。本記事の内容をベースに、自社の事情に合わせた .NET Framework 4.8 向け GitLab CI/CD パイプラインを設計してみてください。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次