はまったりひらめいたり…とか…

Angularや.NETやAzureやその他色々。

Azure DevOpsのパイプラインで `version` という名前の変数を作るとNuGetパッケージ生成で面倒くさいことになった話

はじめに

2021年10月時点の情報です。

参照時期によっては動作が変わっている可能性がありますのでご注意ください。

なにをしようとしていたか

.NETのライブラリプロジェクトをビルドしてNuGetパッケージをビルドしようとしています。

ライブラリのバージョンはPrefix/SuffixをCIコマンド上で指定して外からバージョン情報を変更できるようにしたいと考えています。

そこで、Powershellで取得した値をversion変数に格納し、その値をmsbuildタスクのmsbuildArgumentsで使おうとしていました。

しかし、このバージョン指定が全く意図したとおりに動きません。

下記は検証を踏まえて作成した再現可能な必要最低限のymlとcsprojファイルの構成なります。

  • csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <AssemblyVersion>1.2.3.4</AssemblyVersion>
    <FileVersion>1.2.3.4</FileVersion>
    <VersionPrefix>1.2.3</VersionPrefix>
  </PropertyGroup>
</Project>
  • CIのyml
trigger:
  branches:
    include:
    - master

variables:
  vmImageOfBuild: windows-latest
  version: '1.2.34' #👈👈👈👈👈👈👈👈👈👈👈👈👈👈👈👈👈👈👈👈

stages:
  - stage: pre_build
    displayName: PreBuild
    jobs:
      - job: build
        pool:
          vmImage: $(vmImageOfBuild)
        steps:
        - task: NuGetCommand@2
          inputs:
            command: 'restore'
            feedsToUse: 'config'
            includeNuGetOrg: true
            restoreSolution: 'SampleLibrary/*.sln'
        - task: MSBuild@1
          displayName: 'Build PreProd'
          inputs:
            solution: '**/SampleLibrary.csproj'
            configuration: Release

何が起きるか

csprojで指定したバージョンでNuGetパッケージが生成されてほしいところですが

CIのyamlで作成したversion変数の1.2.34でNuGetパッケージがリリースされます。タスクでプロパティなどに指定はしていません。まじか。

f:id:TakasDev:20211017155054p:plain

色々試してみる

VersionPrefixはどうなるのか

csprojの<version></version>でバージョン指定するとそこで指定されたバージョンでNuGetパッケージが発行されます。

ってことは、同じようにVersionPrefixを指定したら変わるのでは?ということで試してみました。

variables:
  vmImageOfBuild: windows-latest
  # version: '1.2.34'
  versionprefix: '3.2.1'

これはcsprojで設定したバージョンで生成してくれました。

f:id:TakasDev:20211017160007p:plain

versionprefix / versionPrefix / VersionPrefix など試してみましたが変わらなかったので、影響があるのはversion変数だけのようです。

msbuildタスクだけなのか

おそらく一番良く使うdotnetタスクでも同様の現象は発生するのでしょうか?

csprojの構成はそのままに、ymlを下記のように変更してみました。

trigger:
  branches:
    include:
    - master

variables:
  vmImageOfBuild: windows-latest
  version: '1.2.34-dev.0'

stages:
  - stage: pre_build
    displayName: PreBuild
    jobs:
      - job: build
        pool:
          vmImage: $(vmImageOfBuild)
        steps:
        - task: DotNetCoreCLI@2
          inputs:
            command: 'restore'
            projects: '**/*.csproj'
        - task: DotNetCoreCLI@2
          inputs:
            command: 'build'
            projects: '**/*.csproj'

結果、パッケージのバージョンは書き換わりました。タスクは関係なく書き換わりそうです。

f:id:TakasDev:20211017161706p:plain

csprojでバージョン指定しなければどうなるだろうか

csprojのXML内からバージョン指定部分をごっそり消して下記のような構成にしてみました。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

</Project>

f:id:TakasDev:20211017162004p:plain

この状態でも書き換わったので、csprojの構成内容に関わらずversionを指定するとライブラリのバージョンが書き換わるようです。

まとめ

事前定義済み変数でもなさそうですし 、そもそもタスクでプロパティ指定していないのにVariable指定で勝手に書き換わるというのもどうにもバグ臭いです。

まぁPrefix/Sufixを外部から指定する方法面倒くさいっちゃ面倒くさいので楽にできるのはありがたいのですが、さすがにこれは…

フィードバックは投げますが、事前定義済み変数など触りの部分しか調べていないので、これが仕様とわかる何かがあれば教えていただきたいです🙇‍♂️

と、いうわけで現状version変数を使用する場合は注意しましょう。というか使わないほうが無難かと思います。

他の変数でも同様の現象が発生する可能性も(めちゃ低いとは思いますが)ありますので、どうしても意図と違う動作から抜け出せないときは変数の名前を変更してみると、なにかヒントが得られるかもしれませんね。

パイプライン結果、検証で使用したソースコードなどは下記から確認可能です。

dev.azure.com