build.yml: 버전 검증 로직 SemVer 조합 방식으로 변경 #404
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: TableCloth Build Pipeline | |
| permissions: | |
| contents: read | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - develop | |
| tags: | |
| - 'v*' | |
| paths: | |
| - '.github/workflows/build.yml' | |
| - 'docs/**' | |
| - 'src/**' | |
| - 'tools/**' | |
| pull_request: | |
| paths: | |
| - '.github/workflows/**' | |
| - 'docs/**' | |
| - 'src/**' | |
| - 'tools/**' | |
| jobs: | |
| validate-version: | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Validate version matches tag | |
| run: | | |
| # Extract version from tag (remove 'v' prefix) | |
| TAG_VERSION="${GITHUB_REF#refs/tags/v}" | |
| # Extract individual version components from Directory.Build.props | |
| VERSION_MAJOR=$(grep -oP '(?<=<TableClothVersionMajor>)[^<]+' src/Directory.Build.Props) | |
| VERSION_MINOR=$(grep -oP '(?<=<TableClothVersionMinor>)[^<]+' src/Directory.Build.Props) | |
| VERSION_PATCH=$(grep -oP '(?<=<TableClothVersionPatch>)[^<]+' src/Directory.Build.Props) | |
| # Combine to form SemVer | |
| PROPS_VERSION="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" | |
| echo "Tag version: $TAG_VERSION" | |
| echo "Props version: $PROPS_VERSION" | |
| if [ "$TAG_VERSION" != "$PROPS_VERSION" ]; then | |
| echo "::error::Version mismatch! Tag: v$TAG_VERSION does not match Directory.Build.props: $PROPS_VERSION" | |
| echo "::error::Please update Directory.Build.props to match the tag version before releasing." | |
| exit 1 | |
| fi | |
| echo "? Version validated: $TAG_VERSION" | |
| shell: bash | |
| build: | |
| needs: [validate-version] | |
| if: always() && (needs.validate-version.result == 'success' || needs.validate-version.result == 'skipped') | |
| strategy: | |
| matrix: | |
| configuration: [Debug, Release] | |
| platform: [x64, arm64] | |
| include: | |
| - platform: x64 | |
| runner: windows-latest | |
| - platform: arm64 | |
| runner: windows-11-arm | |
| # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on | |
| runs-on: ${{ matrix.runner }} | |
| env: | |
| Spork_Project_Path: src\Spork\Spork.csproj | |
| TableCloth_Project_Path: src\TableCloth\TableCloth.csproj | |
| TableCloth_Core_Project_Path: src\TableCloth.Core\TableCloth.Core.csproj | |
| Spork_Test_Project_Path: src\Spork.Test\Spork.Test.csproj | |
| TableCloth_Test_Project_Path: src\TableCloth.Test\TableCloth.Test.csproj | |
| Platform: ${{ matrix.platform }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| # Install the .NET Core workload | |
| - name: Install .NET Core | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: 10.0.x | |
| # Install Velopack CLI | |
| - name: Install Velopack CLI | |
| run: dotnet tool install -g vpk | |
| # Run tests for Spork.Test | |
| - name: Run tests for Spork.Test | |
| run: dotnet test $env:Spork_Test_Project_Path -c:$env:Configuration | |
| env: | |
| Configuration: ${{ matrix.configuration }} | |
| # Run tests for TableCloth.Test | |
| - name: Run tests for TableCloth.Test | |
| run: dotnet test $env:TableCloth_Test_Project_Path -c:$env:Configuration | |
| env: | |
| Configuration: ${{ matrix.configuration }} | |
| # Build TableCloth.Core project | |
| - name: Build TableCloth.Core project | |
| run: dotnet build $env:TableCloth_Core_Project_Path -c:$env:Configuration | |
| env: | |
| Configuration: ${{ matrix.configuration }} | |
| # Build Spork project | |
| - name: Build Spork project | |
| run: dotnet build $env:Spork_Project_Path -c:$env:Configuration | |
| env: | |
| Configuration: ${{ matrix.configuration }} | |
| # Publish TableCloth project | |
| - name: Publish TableCloth project | |
| run: dotnet publish $env:TableCloth_Project_Path -r win-${{ env.Platform }} --self-contained -p:PublishSingleFile=true -p:PublishReadyToRun=true -c:$env:Configuration -o publish | |
| env: | |
| Configuration: ${{ matrix.configuration }} | |
| # Get version from assembly | |
| - name: Get version | |
| id: get_version | |
| run: | | |
| $version = (Get-Item "publish\TableCloth.exe").VersionInfo.ProductVersion | |
| # Remove any suffix after + sign for semver compatibility | |
| $version = $version -replace '\+.*$', '' | |
| # Convert 4-part version (e.g., 1.14.0.0) to 3-part SemVer2 (e.g., 1.14.0) | |
| $versionParts = $version -split '\.' | |
| if ($versionParts.Count -ge 3) { | |
| $version = "$($versionParts[0]).$($versionParts[1]).$($versionParts[2])" | |
| } | |
| echo "VERSION=$version" >> $env:GITHUB_OUTPUT | |
| shell: pwsh | |
| # Update app.manifest version placeholders | |
| - name: Update manifest versions | |
| run: | | |
| $version = "${{ steps.get_version.outputs.VERSION }}.0" | |
| $manifests = @( | |
| "src\Spork\app.manifest", | |
| "src\TableCloth\app.manifest" | |
| ) | |
| foreach ($manifest in $manifests) { | |
| if (Test-Path $manifest) { | |
| (Get-Content $manifest) -replace 'version="0\.0\.0\.0"', "version=`"$version`"" | Set-Content $manifest | |
| Write-Host "Updated $manifest to version $version" | |
| } | |
| } | |
| shell: pwsh | |
| # Create Velopack release package | |
| - name: Create Velopack package | |
| run: | | |
| vpk pack ` | |
| --packId TableCloth ` | |
| --packVersion ${{ steps.get_version.outputs.VERSION }} ` | |
| --packDir publish ` | |
| --mainExe TableCloth.exe ` | |
| --outputDir releases | |
| shell: pwsh | |
| env: | |
| Configuration: ${{ matrix.configuration }} | |
| # Upload the Velopack package | |
| - name: Upload build artifacts (Velopack) | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: Velopack Package Nightly (${{ matrix.platform }}, ${{ matrix.configuration }}) | |
| path: "releases/*" | |
| if-no-files-found: error | |
| retention-days: 5 |