Bump the ktsu group with 8 updates #522
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: .NET Workflow | |
| on: | |
| push: | |
| branches: [main, develop] | |
| paths-ignore: | |
| ["**.md", ".github/ISSUE_TEMPLATE/**", ".github/pull_request_template.md"] | |
| pull_request: | |
| paths-ignore: | |
| ["**.md", ".github/ISSUE_TEMPLATE/**", ".github/pull_request_template.md"] | |
| schedule: | |
| - cron: "0 23 * * *" # Daily at 11 PM UTC | |
| workflow_dispatch: # Allow manual triggers | |
| inputs: | |
| version-bump: | |
| description: 'Version bump type' | |
| required: false | |
| default: 'auto' | |
| type: choice | |
| options: | |
| - auto | |
| - patch | |
| - minor | |
| - major | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| # Default permissions | |
| permissions: | |
| contents: read | |
| env: | |
| DOTNET_VERSION: "10.0" # Only needed for actions/setup-dotnet | |
| jobs: | |
| build: | |
| name: Build, Test & Release | |
| runs-on: windows-latest | |
| timeout-minutes: 20 | |
| permissions: | |
| contents: write # For creating releases and committing metadata | |
| packages: write # For publishing packages | |
| outputs: | |
| version: ${{ steps.pipeline.outputs.version }} | |
| release_hash: ${{ steps.pipeline.outputs.release_hash }} | |
| should_release: ${{ steps.pipeline.outputs.should_release }} | |
| steps: | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v5 | |
| with: | |
| java-version: 17 | |
| distribution: "zulu" # Alternative distribution options are available. | |
| - name: Checkout Repository | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 # Full history for versioning | |
| fetch-tags: true | |
| lfs: true | |
| submodules: recursive | |
| persist-credentials: true | |
| - name: Setup .NET SDK ${{ env.DOTNET_VERSION }} | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }}.x | |
| # setup-dotnet caches the NuGet global packages folder and keys it on a hash of these | |
| # files. The csproj files alone are not enough: this repository uses central package | |
| # management, so Directory.Packages.props is where versions actually live, and global.json | |
| # pins the ktsu SDK versions, which are NuGet packages too. Without them a version bump in | |
| # either file reuses the previous key, and because a cache hit never re-saves, the newly | |
| # downloaded packages would never make it into the cache. | |
| cache: true | |
| cache-dependency-path: | | |
| **/*.csproj | |
| **/Directory.Packages.props | |
| **/global.json | |
| # Ensure NuGet packages directory exists for caching (prevents error when pipeline exits early) | |
| - name: Ensure NuGet cache directory exists | |
| run: New-Item -Path "$env:USERPROFILE\.nuget\packages" -ItemType Directory -Force | |
| shell: pwsh | |
| - name: Cache SonarQube Cloud packages | |
| if: ${{ env.SONAR_TOKEN != '' }} | |
| uses: actions/cache@v6 | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| with: | |
| path: ~\sonar\cache | |
| key: ${{ runner.os }}-sonar | |
| restore-keys: ${{ runner.os }}-sonar | |
| - name: Cache SonarQube Cloud scanner | |
| if: ${{ env.SONAR_TOKEN != '' }} | |
| id: cache-sonar-scanner | |
| uses: actions/cache@v6 | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| with: | |
| path: .\.sonar\scanner | |
| key: ${{ runner.os }}-sonar-scanner | |
| restore-keys: ${{ runner.os }}-sonar-scanner | |
| - name: Install SonarQube Cloud scanner | |
| if: ${{ env.SONAR_TOKEN != '' && steps.cache-sonar-scanner.outputs.cache-hit != 'true' }} | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| shell: pwsh | |
| run: | | |
| New-Item -Path .\.sonar\scanner -ItemType Directory | |
| dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner | |
| # KtsuBuild is cloned and compiled before the SonarQube window opens. The scanner injects its | |
| # Roslyn analyzers into every project compiled between begin and end, so building KtsuBuild | |
| # inside the window made it report KtsuBuild's own code smells against this repository. That is | |
| # a compiler diagnostic rather than a reported issue, so sonar.exclusions cannot suppress it. | |
| - name: Clone KtsuBuild (Latest Tag) | |
| shell: pwsh | |
| run: | | |
| # Sorting by [version] rather than as text, so v1.8.15 wins over v1.8.9. | |
| $tags = git ls-remote --tags https://github.com/ktsu-dev/KtsuBuild.git | | |
| Select-String -Pattern 'refs/tags/(v\d+\.\d+\.\d+)$' | | |
| ForEach-Object { $_.Matches[0].Groups[1].Value } | |
| $latestTag = $tags | Sort-Object { [version]$_.Substring(1) } | Select-Object -Last 1 | |
| if ([string]::IsNullOrEmpty($latestTag)) { | |
| Write-Host "No version tags found, falling back to HEAD" | |
| git clone --depth 1 https://github.com/ktsu-dev/KtsuBuild.git "${{ runner.temp }}/KtsuBuild" | |
| } | |
| else { | |
| Write-Host "Cloning KtsuBuild at tag: $latestTag" | |
| git clone --depth 1 --branch $latestTag https://github.com/ktsu-dev/KtsuBuild.git "${{ runner.temp }}/KtsuBuild" | |
| } | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| - name: Build KtsuBuild CLI | |
| shell: pwsh | |
| run: | | |
| dotnet build "${{ runner.temp }}/KtsuBuild/KtsuBuild.CLI" | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| - name: Begin SonarQube | |
| if: ${{ env.SONAR_TOKEN != '' }} | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| shell: pwsh | |
| run: | | |
| # sonar.projectBaseDir is pinned to the workspace. Scanner for .NET v8 otherwise derives it | |
| # from the projects it sees, and anything compiled outside the workspace widens it to a | |
| # common ancestor, which pulls the runner's own checkouts under _actions and _temp into the | |
| # file scan. Pinning it means only this repository is ever in scope, so no exclusions are | |
| # needed for those directories. | |
| .\.sonar\scanner\dotnet-sonarscanner begin /k:"${{ github.repository_owner }}_${{ github.event.repository.name }}" /o:"${{ github.repository_owner }}" /d:sonar.token="$env:SONAR_TOKEN" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.projectBaseDir="${{ github.workspace }}" /d:sonar.cs.vscoveragexml.reportsPaths="coverage/coverage.xml" /d:sonar.coverage.exclusions="**/*Test*.cs,**/*.Tests.cs,**/*.Tests/**/*,**/obj/**/*,**/*.dll,**/NativeExports.cs" /d:sonar.cs.vstest.reportsPaths="coverage/TestResults/**/*.trx" /d:sonar.exclusions="**/NativeExports.cs" | |
| - name: Run KtsuBuild CI Pipeline | |
| id: pipeline | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| NUGET_API_KEY: ${{ secrets.NUGET_KEY }} | |
| KTSU_PACKAGE_KEY: ${{ secrets.KTSU_PACKAGE_KEY }} | |
| EXPECTED_OWNER: ktsu-dev | |
| run: | | |
| # Run the CI pipeline | |
| $versionBump = "${{ github.event.inputs.version-bump }}" | |
| # Build arguments array - only add --version-bump if explicitly set (for backward compatibility during bootstrap) | |
| $args = @("ci", "--workspace", "${{ github.workspace }}", "--verbose") | |
| if (![string]::IsNullOrEmpty($versionBump) -and $versionBump -ne "auto") { | |
| $args += @("--version-bump", $versionBump) | |
| } | |
| # --no-build because the CLI was compiled before the SonarQube window opened. Recompiling | |
| # it here would pull the scanner's injected analyzers into KtsuBuild's own sources. | |
| & dotnet run --no-build --project "${{ runner.temp }}/KtsuBuild/KtsuBuild.CLI" -- @args | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| # Set outputs for downstream jobs | |
| $version = (Get-Content "${{ github.workspace }}/VERSION.md" -Raw).Trim() | |
| "version=$version" >> $env:GITHUB_OUTPUT | |
| $releaseHash = git rev-parse HEAD | |
| "release_hash=$releaseHash" >> $env:GITHUB_OUTPUT | |
| # Compute should_release (same logic as BuildConfigurationProvider) | |
| $isMain = "${{ github.ref }}" -eq "refs/heads/main" | |
| $isTagged = [bool](git tag --points-at "${{ github.sha }}" 2>$null) | |
| $isFork = "${{ github.event.repository.fork }}" -eq "true" | |
| $isExpectedOwner = "${{ github.repository_owner }}" -eq "ktsu-dev" | |
| $isOfficial = (-not $isFork) -and $isExpectedOwner | |
| $shouldRelease = $isMain -and (-not $isTagged) -and $isOfficial | |
| "should_release=$($shouldRelease.ToString().ToLower())" >> $env:GITHUB_OUTPUT | |
| - name: End SonarQube | |
| if: env.SONAR_TOKEN != '' | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| shell: pwsh | |
| run: | | |
| .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="$env:SONAR_TOKEN" | |
| - name: Upload Coverage Report | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: coverage-report | |
| path: | | |
| ./coverage/* | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| winget: | |
| name: Update Winget Manifests | |
| needs: build | |
| if: needs.build.outputs.should_release == 'true' | |
| runs-on: windows-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout Release Commit | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.build.outputs.release_hash }} | |
| fetch-depth: 0 # Full history for better auto-detection | |
| - name: Setup .NET SDK ${{ env.DOTNET_VERSION }} | |
| uses: actions/setup-dotnet@v6 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }}.x | |
| - name: Clone KtsuBuild (Latest Tag) | |
| shell: pwsh | |
| run: | | |
| # Sorting by [version] rather than as text, so v1.8.15 wins over v1.8.9. | |
| $tags = git ls-remote --tags https://github.com/ktsu-dev/KtsuBuild.git | | |
| Select-String -Pattern 'refs/tags/(v\d+\.\d+\.\d+)$' | | |
| ForEach-Object { $_.Matches[0].Groups[1].Value } | |
| $latestTag = $tags | Sort-Object { [version]$_.Substring(1) } | Select-Object -Last 1 | |
| if ([string]::IsNullOrEmpty($latestTag)) { | |
| Write-Host "No version tags found, falling back to HEAD" | |
| git clone --depth 1 https://github.com/ktsu-dev/KtsuBuild.git "${{ runner.temp }}/KtsuBuild" | |
| } | |
| else { | |
| Write-Host "Cloning KtsuBuild at tag: $latestTag" | |
| git clone --depth 1 --branch $latestTag https://github.com/ktsu-dev/KtsuBuild.git "${{ runner.temp }}/KtsuBuild" | |
| } | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| - name: Update Winget Manifests | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| dotnet run --project "${{ runner.temp }}/KtsuBuild/KtsuBuild.CLI" -- winget generate --version "${{ needs.build.outputs.version }}" --workspace "${{ github.workspace }}" --verbose | |
| - name: Upload Updated Manifests | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: winget-manifests-${{ needs.build.outputs.version }} | |
| path: winget/*.yaml | |
| retention-days: 30 | |
| security: | |
| name: Security Scanning | |
| needs: build | |
| if: needs.build.outputs.should_release == 'true' | |
| runs-on: windows-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| id-token: write # For dependency submission | |
| contents: write # For dependency submission | |
| steps: | |
| - name: Checkout Release Commit | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.build.outputs.release_hash }} | |
| # Pinned to a full commit SHA rather than a tag. Tags are mutable, so a third party action | |
| # referenced by tag can be repointed at different code after review. The trailing comment is | |
| # the convention dependabot reads, so it still offers upgrades and rewrites both parts. | |
| - name: Detect Dependencies | |
| uses: advanced-security/component-detection-dependency-submission-action@31f25a8de68ae5ce2ca274bc28546a78683c15ce # v0.1.4 |