Merge pull request #76 from routersys/dev/ComputeWeave.ConstantBuffer… #12
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: リリース | |
| on: | |
| push: | |
| tags: | |
| - "v[0-9]+.[0-9]+.[0-9]+" | |
| - "v[0-9]+.[0-9]+.[0-9]+-*" | |
| concurrency: | |
| group: computeweave-release | |
| queue: max | |
| permissions: | |
| contents: read | |
| env: | |
| DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "1" | |
| DOTNET_NOLOGO: "1" | |
| jobs: | |
| validate: | |
| name: バージョン検証とテスト | |
| runs-on: windows-2022 | |
| defaults: | |
| run: | |
| shell: bash | |
| outputs: | |
| version: ${{ steps.parse.outputs.version }} | |
| version_prefix: ${{ steps.parse.outputs.version_prefix }} | |
| version_suffix: ${{ steps.parse.outputs.version_suffix }} | |
| is_prerelease: ${{ steps.parse.outputs.is_prerelease }} | |
| # ComputeWeave.Dxc が参照する環境変数。 | |
| # https://github.com/actions/runner-images/issues/6531 への対処。 | |
| env: | |
| CI_RUNNER_DOTNET_TEST_PLATFORM: x64 | |
| steps: | |
| - name: ソースコードを取得 | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: リリース元とバージョン番号を検証 | |
| id: parse | |
| env: | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| TAG: ${{ github.ref_name }} | |
| run: | | |
| if [ "${GITHUB_REF_TYPE}" != "tag" ]; then | |
| echo "タグ以外からリリースすることはできません。" >&2 | |
| exit 1 | |
| fi | |
| VERSION="${TAG#v}" | |
| PREFIX="${VERSION%%-*}" | |
| if [[ "${VERSION}" == *"-"* ]]; then | |
| SUFFIX="${VERSION#*-}" | |
| IS_PRERELEASE=true | |
| else | |
| SUFFIX="" | |
| IS_PRERELEASE=false | |
| fi | |
| if [[ ! "${PREFIX}" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then | |
| echo "タグのバージョン接頭辞が正しくありません。" >&2 | |
| exit 1 | |
| fi | |
| IFS='.' read -r MAJOR MINOR PATCH <<< "${PREFIX}" | |
| for COMPONENT in "${MAJOR}" "${MINOR}" "${PATCH}"; do | |
| if [ "${#COMPONENT}" -gt 5 ] || { [ "${#COMPONENT}" -eq 5 ] && (( 10#${COMPONENT} > 65534 )); }; then | |
| echo "タグのバージョン接頭辞がアセンブリバージョンの範囲を超えています。" >&2 | |
| exit 1 | |
| fi | |
| done | |
| if [ "${IS_PRERELEASE}" = "true" ] && [[ ! "${SUFFIX}" =~ ^[0-9a-z-]+(\.[0-9a-z-]+)*$ ]]; then | |
| echo "タグのバージョン接尾辞が正しくありません。" >&2 | |
| exit 1 | |
| fi | |
| if [ -n "${SUFFIX}" ]; then | |
| IFS='.' read -ra IDENTIFIERS <<< "${SUFFIX}" | |
| for IDENTIFIER in "${IDENTIFIERS[@]}"; do | |
| if [[ "${IDENTIFIER}" =~ ^[0-9]+$ ]] && [[ ! "${IDENTIFIER}" =~ ^(0|[1-9][0-9]*)$ ]]; then | |
| echo "数値のプレリリース識別子に先頭の0は使用できません。" >&2 | |
| exit 1 | |
| fi | |
| done | |
| fi | |
| if [ "${#VERSION}" -gt 64 ]; then | |
| echo "バージョンは64文字以内で指定してください。" >&2 | |
| exit 1 | |
| fi | |
| if [ "${TAG}" != "v${VERSION}" ]; then | |
| echo "リリースタグが正しくありません。" >&2 | |
| exit 1 | |
| fi | |
| git check-ref-format "refs/tags/${TAG}" | |
| git check-ref-format --branch "${DEFAULT_BRANCH}" | |
| git fetch --no-tags --force origin "refs/heads/${DEFAULT_BRANCH}:refs/remotes/origin/${DEFAULT_BRANCH}" | |
| if ! git merge-base --is-ancestor HEAD "refs/remotes/origin/${DEFAULT_BRANCH}"; then | |
| echo "リリース対象は既定ブランチに含まれていません。" >&2 | |
| exit 1 | |
| fi | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "version_prefix=${PREFIX}" >> "$GITHUB_OUTPUT" | |
| echo "version_suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" | |
| echo "is_prerelease=${IS_PRERELEASE}" >> "$GITHUB_OUTPUT" | |
| - name: 抽出したバージョン番号を確認 | |
| env: | |
| TAG: ${{ github.ref_name }} | |
| VERSION: ${{ steps.parse.outputs.version }} | |
| VERSION_PREFIX: ${{ steps.parse.outputs.version_prefix }} | |
| VERSION_SUFFIX: ${{ steps.parse.outputs.version_suffix }} | |
| IS_PRERELEASE: ${{ steps.parse.outputs.is_prerelease }} | |
| run: | | |
| echo "タグ : ${TAG}" | |
| echo "バージョン : ${VERSION}" | |
| echo "接頭辞 : ${VERSION_PREFIX}" | |
| echo "接尾辞 : ${VERSION_SUFFIX}" | |
| echo "プレリリース : ${IS_PRERELEASE}" | |
| - name: .NET SDK をセットアップ | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.x" | |
| - name: ソリューションをビルド | |
| env: | |
| VERSION_PREFIX: ${{ steps.parse.outputs.version_prefix }} | |
| VERSION_SUFFIX: ${{ steps.parse.outputs.version_suffix }} | |
| run: | | |
| dotnet build ComputeWeave.sln -c Release -p:Platform=x64 \ | |
| "-p:VersionPrefix=${VERSION_PREFIX}" \ | |
| "-p:VersionSuffix=${VERSION_SUFFIX}" | |
| - name: ソースジェネレーターのテストを実行 | |
| run: dotnet test tests/ComputeWeave.Tests.SourceGenerators/ComputeWeave.Tests.SourceGenerators.csproj -c Release -p:Platform=x64 | |
| - name: 内部実装のテストを実行 | |
| run: dotnet test tests/ComputeWeave.Tests.Internals/ComputeWeave.Tests.Internals.csproj -c Release -p:Platform=x64 | |
| - name: 本体のテストを実行 | |
| run: dotnet test tests/ComputeWeave.Tests/ComputeWeave.Tests.csproj -c Release -p:Platform=x64 | |
| # D2D1 の本体のテストは、ハードウェアが取れなければ WARP へ退避する。アダプターの無い runner でも走る。 | |
| - name: D2D1の本体のテストを実行 | |
| run: dotnet test tests/ComputeWeave.D2D1.Tests/ComputeWeave.D2D1.Tests.csproj -c Release -p:Platform=x64 | |
| - name: D2D1のソースジェネレーターのテストを実行 | |
| run: dotnet test tests/ComputeWeave.D2D1.Tests.SourceGenerators/ComputeWeave.D2D1.Tests.SourceGenerators.csproj -c Release -p:Platform=x64 | |
| - name: D2D1のアセンブリ属性のテストを実行 | |
| run: dotnet test tests/ComputeWeave.D2D1.Tests.AssemblyLevelAttributes/ComputeWeave.D2D1.Tests.AssemblyLevelAttributes.csproj -c Release -p:Platform=x64 | |
| # 最上位ステートメントのテストは Exe でありビルドだけでは実行時の不一致を検出できないため、実際に走らせる。 | |
| - name: 最上位ステートメントのテストを実行 | |
| run: dotnet run --project tests/ComputeWeave.Tests.GlobalStatements/ComputeWeave.Tests.GlobalStatements.csproj -c Release -p:Platform=x64 | |
| # 発行と実行を繰り返すため CI では回さない。同梱ライブラリが実際に配られるかを見る唯一の試験なので、出荷前に通す。 | |
| - name: ネイティブライブラリ解決のテストを実行 | |
| run: dotnet test tests/ComputeWeave.Tests.NativeLibrariesResolver/ComputeWeave.Tests.NativeLibrariesResolver.csproj -c Release -p:Platform=x64 | |
| # デバイス消失のテストは実デバイスを破棄するため、他のテストとは別のプロセスで実行する。 | |
| validate-device-lost: | |
| name: デバイス消失のテスト | |
| runs-on: windows-2022 | |
| defaults: | |
| run: | |
| shell: bash | |
| env: | |
| CI_RUNNER_DOTNET_TEST_PLATFORM: x64 | |
| steps: | |
| - name: ソースコードを取得 | |
| uses: actions/checkout@v4 | |
| - name: .NET SDK をセットアップ | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.x" | |
| - name: デバイス消失のテストを実行 | |
| run: dotnet test tests/ComputeWeave.Tests.DeviceLost/ComputeWeave.Tests.DeviceLost.csproj -c Release -p:Platform=x64 | |
| pack: | |
| name: NuGet パッケージの作成 | |
| runs-on: windows-2022 | |
| needs: [validate, validate-device-lost] | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: ソースコードを取得 | |
| uses: actions/checkout@v4 | |
| - name: .NET SDK をセットアップ | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.x" | |
| - name: パッケージを作成 | |
| env: | |
| VERSION_PREFIX: ${{ needs.validate.outputs.version_prefix }} | |
| VERSION_SUFFIX: ${{ needs.validate.outputs.version_suffix }} | |
| run: | | |
| for project in \ | |
| src/ComputeWeave.Core/ComputeWeave.Core.csproj \ | |
| src/ComputeWeave/ComputeWeave.csproj \ | |
| src/ComputeWeave.Dxc/ComputeWeave.Dxc.csproj \ | |
| src/ComputeWeave.D3D12MemoryAllocator/ComputeWeave.D3D12MemoryAllocator.csproj \ | |
| src/ComputeWeave.D2D1/ComputeWeave.D2D1.csproj | |
| do | |
| dotnet pack "$project" -c Release \ | |
| "-p:VersionPrefix=${VERSION_PREFIX}" \ | |
| "-p:VersionSuffix=${VERSION_SUFFIX}" | |
| done | |
| - name: 作成されたパッケージ一覧を確認 | |
| run: ls -la ./artifacts/ | |
| - name: パッケージ内容を検証 | |
| shell: pwsh | |
| env: | |
| EXPECTED_VERSION: ${{ needs.validate.outputs.version }} | |
| EXPECTED_COMMIT: ${{ github.sha }} | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| $expectedPackageIds = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal) | |
| [void]$expectedPackageIds.Add('ComputeWeave.Core') | |
| [void]$expectedPackageIds.Add('ComputeWeave') | |
| [void]$expectedPackageIds.Add('ComputeWeave.Dxc') | |
| [void]$expectedPackageIds.Add('ComputeWeave.D3D12MemoryAllocator') | |
| [void]$expectedPackageIds.Add('ComputeWeave.D2D1') | |
| function Test-PackageSet([string]$filter, [string]$description) { | |
| $actualPackageIds = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal) | |
| $packageFiles = @(Get-ChildItem -LiteralPath artifacts -File -Filter $filter) | |
| if ($packageFiles.Count -ne $expectedPackageIds.Count) { | |
| throw "$descriptionは$($expectedPackageIds.Count)個でなければなりません。" | |
| } | |
| foreach ($packageFile in $packageFiles) { | |
| $archive = [System.IO.Compression.ZipFile]::OpenRead($packageFile.FullName) | |
| try { | |
| $nuspecEntries = @($archive.Entries | Where-Object { $_.FullName -like '*.nuspec' -and $_.FullName -notlike '*/*' }) | |
| if ($nuspecEntries.Count -ne 1) { | |
| throw "$($packageFile.Name)のnuspecを一意に特定できません。" | |
| } | |
| $reader = [System.IO.StreamReader]::new($nuspecEntries[0].Open()) | |
| try { | |
| [xml]$document = $reader.ReadToEnd() | |
| } | |
| finally { | |
| $reader.Dispose() | |
| } | |
| } | |
| finally { | |
| $archive.Dispose() | |
| } | |
| $namespaceManager = [System.Xml.XmlNamespaceManager]::new($document.NameTable) | |
| $namespaceManager.AddNamespace('n', $document.DocumentElement.NamespaceURI) | |
| $metadata = $document.SelectSingleNode('/n:package/n:metadata', $namespaceManager) | |
| $packageId = $metadata.SelectSingleNode('n:id', $namespaceManager).InnerText | |
| $packageVersion = $metadata.SelectSingleNode('n:version', $namespaceManager).InnerText | |
| $repositoryCommit = $metadata.SelectSingleNode('n:repository', $namespaceManager).GetAttribute('commit') | |
| if (!$expectedPackageIds.Contains($packageId) -or !$actualPackageIds.Add($packageId)) { | |
| throw "$($packageFile.Name)のパッケージIDが正しくありません。" | |
| } | |
| if ($packageVersion -cne $env:EXPECTED_VERSION) { | |
| throw "$($packageFile.Name)のバージョンが正しくありません。" | |
| } | |
| if ($repositoryCommit -cne $env:EXPECTED_COMMIT) { | |
| throw "$($packageFile.Name)のコミットが正しくありません。" | |
| } | |
| } | |
| if (!$actualPackageIds.SetEquals($expectedPackageIds)) { | |
| throw "必要な$descriptionが揃っていません。" | |
| } | |
| } | |
| Test-PackageSet '*.nupkg' 'NuGetパッケージ' | |
| Test-PackageSet '*.snupkg' 'NuGetシンボルパッケージ' | |
| # 上の検証はパッケージの同一性を見る。積荷が揃っていることは別に確かめる。 | |
| - name: パッケージの積荷を検証 | |
| run: pwsh build/verify-package-payload.ps1 | |
| # 文書だけの変更では CI が走らないため、同梱される README を出荷前にここで確かめる。 | |
| - name: Markdown の表を検証 | |
| run: pwsh build/verify-markdown-tables.ps1 | |
| - name: パッケージをアーティファクトとして保存 | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: nuget-packages | |
| path: artifacts/ | |
| retention-days: 90 | |
| if-no-files-found: error | |
| publish-nuget: | |
| name: NuGet.org へ公開 | |
| runs-on: ubuntu-latest | |
| needs: [validate, pack] | |
| environment: nuget-release | |
| permissions: | |
| id-token: write | |
| packages: write | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: .NET SDK をセットアップ | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.x" | |
| - name: パッケージをダウンロード | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: nuget-packages | |
| path: ./artifacts | |
| - name: ダウンロードしたファイルを確認 | |
| run: ls -la ./artifacts/ | |
| - name: NuGet Trusted Publishing でログイン | |
| uses: NuGet/login@v1 | |
| id: nuget-login | |
| with: | |
| user: routersys | |
| - name: NuGet.org へパッケージを発行 | |
| env: | |
| ALLOW_DUPLICATES: ${{ github.run_attempt > 1 }} | |
| EXPECTED_COMMIT: ${{ github.sha }} | |
| NUGET_API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }} | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: | | |
| get_package_status() { | |
| curl --location --silent --show-error --output /dev/null --write-out '%{http_code}' \ | |
| "${PACKAGE_BASE}$1/${VERSION}/$1.nuspec" | |
| } | |
| get_package_commit() { | |
| curl --fail --location --silent --show-error "${PACKAGE_BASE}$1/${VERSION}/$1.nuspec" \ | |
| | python -c 'import sys, xml.etree.ElementTree as ET; root = ET.parse(sys.stdin).getroot(); print(next((node.get("commit", "") for node in root.iter() if node.tag.rsplit("}", 1)[-1] == "repository"), ""))' | |
| } | |
| PACKAGE_BASE=$(curl --fail --location --silent --show-error https://api.nuget.org/v3/index.json \ | |
| | python -c 'import json, sys; resources = json.load(sys.stdin)["resources"]; print(next(item["@id"] for item in resources if item["@type"] == "PackageBaseAddress/3.0.0"))') | |
| for PACKAGE_ID in computeweave.core computeweave computeweave.dxc computeweave.d3d12memoryallocator computeweave.d2d1; do | |
| PACKAGE_STATUS=$(get_package_status "${PACKAGE_ID}") | |
| if [ "${PACKAGE_STATUS}" = "404" ]; then | |
| continue | |
| fi | |
| if [ "${PACKAGE_STATUS}" != "200" ]; then | |
| echo "NuGet.orgのパッケージ状態を確認できませんでした。" >&2 | |
| exit 1 | |
| fi | |
| if [ "${ALLOW_DUPLICATES}" != "true" ]; then | |
| echo "同じバージョンのパッケージがNuGet.orgに存在します。" >&2 | |
| exit 1 | |
| fi | |
| PACKAGE_COMMIT=$(get_package_commit "${PACKAGE_ID}") | |
| if [ "${PACKAGE_COMMIT}" != "${EXPECTED_COMMIT}" ]; then | |
| echo "NuGet.orgのパッケージは別のコミットから作成されています。" >&2 | |
| exit 1 | |
| fi | |
| done | |
| if [ "${ALLOW_DUPLICATES}" = "true" ]; then | |
| dotnet nuget push ./artifacts/*.nupkg \ | |
| --api-key "${NUGET_API_KEY}" \ | |
| --source https://api.nuget.org/v3/index.json \ | |
| --skip-duplicate | |
| else | |
| dotnet nuget push ./artifacts/*.nupkg \ | |
| --api-key "${NUGET_API_KEY}" \ | |
| --source https://api.nuget.org/v3/index.json | |
| fi | |
| if [ "${ALLOW_DUPLICATES}" = "true" ]; then | |
| for ATTEMPT in $(seq 1 60); do | |
| ALL_PACKAGES_VERIFIED=true | |
| for PACKAGE_ID in computeweave.core computeweave computeweave.dxc computeweave.d3d12memoryallocator computeweave.d2d1; do | |
| PACKAGE_STATUS=$(get_package_status "${PACKAGE_ID}") | |
| if [ "${PACKAGE_STATUS}" = "404" ]; then | |
| ALL_PACKAGES_VERIFIED=false | |
| continue | |
| fi | |
| if [ "${PACKAGE_STATUS}" != "200" ]; then | |
| echo "NuGet.orgのパッケージ状態を確認できませんでした。" >&2 | |
| exit 1 | |
| fi | |
| PACKAGE_COMMIT=$(get_package_commit "${PACKAGE_ID}") | |
| if [ "${PACKAGE_COMMIT}" != "${EXPECTED_COMMIT}" ]; then | |
| echo "NuGet.orgのパッケージは別のコミットから作成されています。" >&2 | |
| exit 1 | |
| fi | |
| done | |
| if [ "${ALL_PACKAGES_VERIFIED}" = "true" ]; then | |
| exit 0 | |
| fi | |
| sleep 10 | |
| done | |
| echo "NuGet.orgで全パッケージを確認できませんでした。" >&2 | |
| exit 1 | |
| fi | |
| - name: GitHub Packages へも発行 | |
| env: | |
| GITHUB_PACKAGES_API_KEY: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| dotnet nuget push ./artifacts/*.nupkg \ | |
| --api-key "${GITHUB_PACKAGES_API_KEY}" \ | |
| --source https://nuget.pkg.github.com/routersys/index.json \ | |
| --skip-duplicate | |
| continue-on-error: true | |
| github-release: | |
| name: GitHub Release の作成 | |
| runs-on: ubuntu-latest | |
| needs: [validate, pack, publish-nuget] | |
| permissions: | |
| contents: write | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: ソースコードを取得 | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: パッケージをダウンロード | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: nuget-packages | |
| path: ./artifacts | |
| - name: コミット履歴からリリースノートを生成 | |
| env: | |
| CURRENT_TAG: ${{ github.ref_name }} | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: | | |
| CURRENT_SHA=$(git rev-parse HEAD) | |
| PREV=$(git tag --merged "${CURRENT_SHA}" --sort=-version:refname \ | |
| | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \ | |
| | grep -Fxv "${CURRENT_TAG}" \ | |
| | head -n 1 || true) | |
| if [ -n "${PREV}" ]; then | |
| LOG=$(git log "${PREV}..${CURRENT_SHA}" --pretty=format:"- %s (%h)" --no-merges --) | |
| else | |
| LOG=$(git log -n 30 --pretty=format:"- %s (%h)" --no-merges) | |
| fi | |
| { | |
| echo "### 変更内容" | |
| printf '%s\n' "${LOG:-変更履歴を取得できませんでした。}" | |
| echo "" | |
| echo "### インストール" | |
| echo '```sh' | |
| echo "dotnet add package ComputeWeave --version ${VERSION}" | |
| echo '```' | |
| echo "" | |
| echo "### NuGet" | |
| echo "https://www.nuget.org/packages/ComputeWeave/${VERSION}" | |
| } > release-notes.md | |
| - name: GitHub Release を作成してパッケージを添付 | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| name: ${{ github.ref_name }} | |
| body_path: release-notes.md | |
| prerelease: ${{ needs.validate.outputs.is_prerelease }} | |
| fail_on_unmatched_files: true | |
| files: | | |
| artifacts/*.nupkg | |
| artifacts/*.snupkg |