こんにちは。
日頃、boiler's Graphics( https://github.com/dhq-boiler/boiler-s-Graphics )の開発に勤しんでいるのですが、ただ勤しむだけではダメで、ひとさじのスパイスというか、工夫が必要だったりします。今回の記事では、boiler's GraphicsのCD/CI、継続的インテグレーションの話を簡単に説明した後、本題のパイプラインから自動ツイートさせる方法を述べます。
azure-pipelines.ymlの作り方
boiler's Graphics では、GitHubのリポジトリにmasterブランチおよびdevelopブランチをプッシュする度に、自動でビルド、テスト、パッケージング、そしてツイートするようになっています。
Azure DevOpsでCD/CIを実現するためには、azure-pipelines.ymlというファイルを書く必要があります。
実は、このazure-pipelines.ymlというファイルはブランチ毎に作ることも可能で、たとえばmasterブランチがプッシュされたら動作するパイプラインとdevelopブランチがプッシュされたら動作するパイプラインというように分けることができます。
この場合、次のようにファイルを分けます。
- azure-pipelines.yml ===============> masterブランチ用
- azure-pipelines_develop.yml =======> developブランチ用
以下のようにしても良いです。
- azure-pipelines_master.yml
- azure-pipelines_develop.yml
名前は自由に決めてもらって構わないです。結局、Azure DevOpsの画面でazure-pipelines.ymlの場所を聞かれるので、そのパイプラインの適切なブランチのazure-pipeline.ymlを選択すればOKです。
Configure your pipelineの画面で"Existing Azure Pipelines YAML file"を選択し、BranchとPathを設定します。
azure-pipelines.yml の内容
boiler's Graphics では2つのazure-pipelines.ymlが存在しています。
- /azure_devops/azure-pipelines.yml ==========> masterブランチ用
- /azure_devops/azure-pipelines_develop.yml ==> developブランチ用
それぞれやっていることが違うのでそれぞれ説明していきたいと思います。
azure-pipelines.yml (masterブランチの場合)
# .NET Desktop
# Build and run tests for .NET Desktop or Windows classic desktop solutions.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net
trigger:
- master
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
disable.coverage.autogenerate: 'true'
steps:
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
restoreSolution: '$(solution)'
- task: VSBuild@1
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: CmdLine@2
displayName: "NUnit & OpenCover"
inputs:
script: >
packages\OpenCover.4.7.1221\tools\OpenCover.Console.exe
-register:Path64
-target:"packages\NUnit.ConsoleRunner.3.12.0\tools\nunit3-console.exe"
-targetargs:"boilersGraphics.Test.dll"
-targetdir:"boilersGraphics.Test\bin\$(buildConfiguration)"
-returntargetcode
-output:"coverage.xml"
-filter:"+[boilersGraphics]* +[TsOperationHistory]* -[*]XamlGeneratedNamespace*"
- task: PublishTestResults@2
displayName: "Publish unit test result"
condition: always()
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
failTaskOnFailedTests: false
- task: reportgenerator@4
condition: always()
inputs:
reports: '**\coverage.xml'
targetdir: 'coveragereport'
- task: PublishCodeCoverageResults@1
condition: always()
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: 'coveragereport\Cobertura.xml'
reportDirectory: 'coveragereport\'
# Archive files
# Compress files into .7z, .tar.gz, or .zip
- task: ArchiveFiles@2
condition: always()
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)\boilersGraphics\bin\Release'
includeRootFolder: false
archiveType: 'zip' # Options: zip, 7z, tar, wim
tarCompression: 'gz' # Optional. Options: gz, bz2, xz, none
archiveFile: '$(Build.ArtifactStagingDirectory)/boilersGraphics_$(Build.BuildId).zip'
replaceExistingArchive: true
#verbose: # Optional
#quiet: # Optional
# GitHub Release
# Create, edit, or delete a GitHub release
- task: GitHubRelease@0
condition: always()
inputs:
gitHubConnection: dhq-boiler-azure-devops
repositoryName: '$(Build.Repository.Name)'
action: 'create' # Options: create, edit, delete
target: '$(Build.SourceVersion)' # Required when action == Create || Action == Edit
tagSource: 'auto' # Required when action == Create# Options: auto, manual
#tagPattern: # Optional
#tag: # Required when action == Edit || Action == Delete || TagSource == Manual
#title: # Optional
#releaseNotesSource: 'file' # Optional. Options: file, input
#releaseNotesFile: # Optional
#releaseNotes: # Optional
assets: '$(Build.ArtifactStagingDirectory)\boilersGraphics_$(Build.BuildId).zip'
assetUploadMode: 'delete' # Optional. Options: delete, replace
isDraft: false # Optional
isPreRelease: false # Optional
addChangeLog: true # Optional
compareWith: 'lastFullRelease' # Required when addChangeLog == True. Options: lastFullRelease, lastRelease, lastReleaseByTag
#releaseTag: # Required when compareWith == LastReleaseByTag
- powershell: |
$CI_BUILD_TAG = git describe --tags
Write-Host "##vso[task.setvariable variable=CI_BUILD_TAG]$CI_BUILD_TAG"
displayName: 'Set the tag name as an environment variable'
- task: CmdLine@2
displayName: "pip install python-oauth2"
inputs:
script: >
pip install twitter
# Python script
# Run a Python file or inline script
- task: PythonScript@0
inputs:
scriptSource: 'inline' # Options: filePath, inline
#scriptPath: azure_devops/python_script/tweet.py # Required when scriptSource == filePath
script: >
from twitter import Twitter, OAuth
access_token = '$(AccessToken)'
access_token_secret = '$(AccessTokenSecret)'
api_key = '$(APIKey)'
api_secret = '$(APIKeySecret)'
t = Twitter(auth = OAuth(access_token, access_token_secret, api_key, api_secret))
text = 'https://github.com/dhq-boiler/boiler-s-Graphics/releases/tag/$(CI_BUILD_TAG)'
statusUpdate = t.statuses.update(status=text)
#arguments: # Optional
#pythonInterpreter: # Optional
#workingDirectory: # Optional
#failOnStderr: false # Optional
trigger で master ブランチを指定してあります。
タスクの概略は以下のとおりです。
- NugetToolInstaller
- NuGetCommand ソリューションをリストアします。
- VSBuild ソリューションをビルドします。
- CmdLine NUnitテストの実行とOpenCoverによるカバレッジ収集
- PublishTestResult ユニットテストの結果を発行します
- reportgenerator カバレッジの結果を発行します
- PublishCodeCoverageResults Cobertura.xmlを発行します(嘘かも。詳細は忘れた)
- ArchiveFiles ビルドした出力ファイル等をzipファイルに圧縮します
- GitHubRelease GitHubのReleaseを発行します
- powershell タグ名を取得して$(CI_BUILD_TAG)に格納します
- CmdLine pythonのtwitterライブラリをインストールします
- PythonScript ビルド結果をツイートします
ここで解説したいのは10, 11, 12です。
12でツイートする内容としてGitHubのリリースページのURLを作り上げる必要があります。
例:https://github.com/dhq-boiler/boiler-s-Graphics/releases/tag/v4.7.2.6
この例においては"v4.7.2.6"という文字列をどこかから取得しないとツイートの内容を構成できません。なので、10でgit describe --tagsをしてその内容を環境変数に格納するというのをやっているのが以下です。
- powershell: |
$CI_BUILD_TAG = git describe --tags
Write-Host "##vso[task.setvariable variable=CI_BUILD_TAG]$CI_BUILD_TAG"
displayName: 'Set the tag name as an environment variable'
次に11ではpythonのtwitterライブラリをインストールします。それだけです。
12ではtwitterライブラリを使ってツイートします。
scriptSourceはinlineにする必要があります。
scriptに複数行のpythonコードを書くために、script: >という書き方をします。
# Python script
# Run a Python file or inline script
- task: PythonScript@0
inputs:
scriptSource: 'inline' # Options: filePath, inline
#scriptPath: azure_devops/python_script/tweet.py # Required when scriptSource == filePath
script: >
from twitter import Twitter, OAuth
access_token = '$(AccessToken)'
access_token_secret = '$(AccessTokenSecret)'
api_key = '$(APIKey)'
api_secret = '$(APIKeySecret)'
t = Twitter(auth = OAuth(access_token, access_token_secret, api_key, api_secret))
text = 'https://github.com/dhq-boiler/boiler-s-Graphics/releases/tag/$(CI_BUILD_TAG)'
statusUpdate = t.statuses.update(status=text)
#arguments: # Optional
#pythonInterpreter: # Optional
#workingDirectory: # Optional
#failOnStderr: false # Optional
script内では一行置きにコードを書いていますが、これはエラー防止の策でこうなっています。書きたくてこんな間抜けなコードを書いているわけではありません。scriptSourceをinlineに設定した理由はscriptSource = filePathにした時別のpythonファイルを参照する方法では、コード中に環境変数を使えないためです。inlineにしているのは、中のコードで環境変数を使うためです。で、ここでは4つの環境変数を使用しています。$(AccessToken)、$(AccessTokenSecret)、$(APIKey)、$(APIKeySecret)の4つです。これらは以下の画面で設定できます。
Keep this value secretにはチェックを入れて下さい。この4つの変数の値はセンシティブな情報です。ファイルに直接書かないで下さい。
最終的には以下のようなツイートが発行されます。
これはmasterブランチを自動ビルドした時に発行されるツイートです。
— boiler's Graphics ビルドステータス (@boilersGraphics) November 4, 2021
azure-pipelines.yml (developブランチの場合)
# .NET Desktop
# Build and run tests for .NET Desktop or Windows classic desktop solutions.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net
trigger:
- develop
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
disable.coverage.autogenerate: 'true'
steps:
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
restoreSolution: '$(solution)'
- task: VSBuild@1
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: CmdLine@2
displayName: "NUnit & OpenCover"
inputs:
script: >
packages\OpenCover.4.7.1221\tools\OpenCover.Console.exe
-register:Path64
-target:"packages\NUnit.ConsoleRunner.3.12.0\tools\nunit3-console.exe"
-targetargs:"boilersGraphics.Test.dll"
-targetdir:"boilersGraphics.Test\bin\$(buildConfiguration)"
-returntargetcode
-output:"coverage.xml"
-filter:"+[boilersGraphics]* +[TsOperationHistory]* -[*]XamlGeneratedNamespace*"
- task: PublishTestResults@2
displayName: "Publish unit test result"
condition: always()
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
failTaskOnFailedTests: false
- task: reportgenerator@4
condition: always()
inputs:
reports: '**\coverage.xml'
targetdir: 'coveragereport'
- task: PublishCodeCoverageResults@1
condition: always()
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: 'coveragereport\Cobertura.xml'
reportDirectory: 'coveragereport\'
# Archive files
# Compress files into .7z, .tar.gz, or .zip
- task: ArchiveFiles@2
condition: always()
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)\boilersGraphics\bin\Release'
includeRootFolder: false
archiveType: 'zip' # Options: zip, 7z, tar, wim
tarCompression: 'gz' # Optional. Options: gz, bz2, xz, none
archiveFile: '$(Build.ArtifactStagingDirectory)/boilersGraphics_$(Build.BuildId).zip'
replaceExistingArchive: true
#verbose: # Optional
#quiet: # Optional
# GitHub Release
# Create, edit, or delete a GitHub release
- task: GitHubRelease@0
condition: always()
inputs:
gitHubConnection: dhq-boiler-azure-devops
repositoryName: '$(Build.Repository.Name)'
action: 'create' # Options: create, edit, delete
target: '$(Build.SourceVersion)' # Required when action == Create || Action == Edit
tagSource: manual # Required when action == Create# Options: auto, manual
#tagPattern: # Optional
tag: unstable-$(Build.BuildNumber) # Required when action == Edit || Action == Delete || TagSource == Manual
#title: # Optional
#releaseNotesSource: 'file' # Optional. Options: file, input
#releaseNotesFile: # Optional
#releaseNotes: # Optional
assets: '$(Build.ArtifactStagingDirectory)\boilersGraphics_$(Build.BuildId).zip'
assetUploadMode: 'delete' # Optional. Options: delete, replace
isDraft: false # Optional
isPreRelease: true # Optional
addChangeLog: true # Optional
compareWith: 'lastFullRelease' # Required when addChangeLog == True. Options: lastFullRelease, lastRelease, lastReleaseByTag
#releaseTag: # Required when compareWith == LastReleaseByTag
- task: CmdLine@2
displayName: "pip install python-oauth2"
inputs:
script: >
pip install twitter
# Python script
# Run a Python file or inline script
- task: PythonScript@0
inputs:
scriptSource: 'inline' # Options: filePath, inline
#scriptPath: azure_devops/python_script/tweet.py # Required when scriptSource == filePath
script: >
from twitter import Twitter, OAuth
access_token = '$(AccessToken)'
access_token_secret = '$(AccessTokenSecret)'
api_key = '$(APIKey)'
api_secret = '$(APIKeySecret)'
t = Twitter(auth = OAuth(access_token, access_token_secret, api_key, api_secret))
text = 'https://github.com/dhq-boiler/boiler-s-Graphics/releases/tag/unstable-$(Build.BuildNumber)'
statusUpdate = t.statuses.update(status=text)
#arguments: # Optional
#pythonInterpreter: # Optional
#workingDirectory: # Optional
#failOnStderr: false # Optional
trigger で develop ブランチを指定してあります。
タスクの概略は以下のとおりです。
- NugetToolInstaller
- NuGetCommand ソリューションをリストアします。
- VSBuild ソリューションをビルドします。
- CmdLine NUnitテストの実行とOpenCoverによるカバレッジ収集
- PublishTestResult ユニットテストの結果を発行します
- reportgenerator カバレッジの結果を発行します
- PublishCodeCoverageResults Cobertura.xmlを発行します(嘘かも。詳細は忘れた)
- ArchiveFiles ビルドした出力ファイル等をzipファイルに圧縮します
- GitHubRelease GitHubのReleaseを発行します**(isPreRelease=true, tag=unstable-$(Build.SourceVersion))**
- powershell タグ名を取得して$(CI_BUILD_TAG)に格納します
- CmdLine pythonのtwitterライブラリをインストールします
- PythonScript ビルド結果をツイートします
masterブランチの時と大きく変わらないのですが、少し変更するところがあります。
developブランチで成果物として固めたいものは変わりがないのですが、GitHubReleaseでのリリース名が”unstable-$(Build.BuildNumber)"となります。
そしてリリースはプレリリースとして発行したいので、isPreRelease: trueにします。
12のツイートでは、ツイート内容のリンクの末尾をunstable-$(Build.BuildNumber)にしています。
これでツイートに正しいURLが貼られます。
こんな風に。
— boiler's Graphics ビルドステータス (@boilersGraphics) November 4, 2021
終わりに
いかがでしたでしょうか。Azure DevOpsはパブリックリポジトリなら無料で利用できる便利なCD/CIサービスなので、皆さんにも使ってもらえるといいなと思っています。
また、締めに宣伝なのですが、現在開発中のboiler's Graphicsも利用者が増えてくれるといいなと思っています。ユーザーの声が聞きたいですが、なかなか機会が得られないので困っています。
宣伝!!!
私はベクターグラフィックスドローイングツールboiler's Graphicsを開発しています。コンピューターグラフィックスツールShadowEyeの開発もしています。ユーザー大募集!!!!