From 8a0d319d2a5fdac363f11c092ff83561234eeb0a Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 8 Dec 2024 19:56:33 -0700 Subject: [PATCH 1/5] Move artifact publishing into a composite action --- .github/actions/publish-artifacts/action.yaml | 67 +++++++++++++++++++ .github/workflows/build.yml | 60 +---------------- 2 files changed, 70 insertions(+), 57 deletions(-) create mode 100644 .github/actions/publish-artifacts/action.yaml diff --git a/.github/actions/publish-artifacts/action.yaml b/.github/actions/publish-artifacts/action.yaml new file mode 100644 index 0000000..94b7426 --- /dev/null +++ b/.github/actions/publish-artifacts/action.yaml @@ -0,0 +1,67 @@ +name: Publish artifacts +description: Publish artifacts + +runs: + using: composite + steps: + - name: 📥 Collect artifacts + run: azure-pipelines/artifacts/_stage_all.ps1 + shell: pwsh + if: always() + +# TODO: replace this hard-coded list with a loop that utilizes the NPM package at +# https://github.com/actions/toolkit/tree/main/packages/artifact (or similar) to push the artifacts. + + - name: 📢 Upload project.assets.json files + if: always() + uses: actions/upload-artifact@v4 + with: + name: projectAssetsJson-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/projectAssetsJson + continue-on-error: true + - name: 📢 Upload variables + uses: actions/upload-artifact@v4 + with: + name: variables-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/Variables + continue-on-error: true + - name: 📢 Upload build_logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: build_logs-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/build_logs + continue-on-error: true + - name: 📢 Upload test_logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: test_logs-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/test_logs + continue-on-error: true + - name: 📢 Upload testResults + if: always() + uses: actions/upload-artifact@v4 + with: + name: testResults-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/testResults + continue-on-error: true + - name: 📢 Upload coverageResults + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverageResults-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/coverageResults + continue-on-error: true + - name: 📢 Upload symbols + uses: actions/upload-artifact@v4 + with: + name: symbols-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/symbols + continue-on-error: true + - name: 📢 Upload deployables + uses: actions/upload-artifact@v4 + with: + name: deployables-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/deployables + if: always() diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b1fb60..9d87be9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,7 @@ env: jobs: build: + name: 🏭 Build runs-on: ${{ matrix.os }} strategy: @@ -57,63 +58,8 @@ jobs: - name: ⚙ Update pipeline variables based on build outputs run: azure-pipelines/variables/_pipelines.ps1 shell: pwsh - - name: 📥 Collect artifacts - run: azure-pipelines/artifacts/_stage_all.ps1 - shell: pwsh - if: always() - - name: 📢 Upload project.assets.json files - if: always() - uses: actions/upload-artifact@v4 - with: - name: projectAssetsJson-${{ runner.os }} - path: ${{ runner.temp }}/_artifacts/projectAssetsJson - continue-on-error: true - - name: 📢 Upload variables - uses: actions/upload-artifact@v4 - with: - name: variables-${{ runner.os }} - path: ${{ runner.temp }}/_artifacts/Variables - continue-on-error: true - - name: 📢 Upload build_logs - if: always() - uses: actions/upload-artifact@v4 - with: - name: build_logs-${{ runner.os }} - path: ${{ runner.temp }}/_artifacts/build_logs - continue-on-error: true - - name: 📢 Upload test_logs - if: always() - uses: actions/upload-artifact@v4 - with: - name: test_logs-${{ runner.os }} - path: ${{ runner.temp }}/_artifacts/test_logs - continue-on-error: true - - name: 📢 Upload testResults - if: always() - uses: actions/upload-artifact@v4 - with: - name: testResults-${{ runner.os }} - path: ${{ runner.temp }}/_artifacts/testResults - continue-on-error: true - - name: 📢 Upload coverageResults - if: always() - uses: actions/upload-artifact@v4 - with: - name: coverageResults-${{ runner.os }} - path: ${{ runner.temp }}/_artifacts/coverageResults - continue-on-error: true - - name: 📢 Upload symbols - uses: actions/upload-artifact@v4 - with: - name: symbols-${{ runner.os }} - path: ${{ runner.temp }}/_artifacts/symbols - continue-on-error: true - - name: 📢 Upload deployables - uses: actions/upload-artifact@v4 - with: - name: deployables-${{ runner.os }} - path: ${{ runner.temp }}/_artifacts/deployables - if: always() + - name: 📢 Publish artifacts + uses: ./.github/actions/publish-artifacts - name: 📢 Publish code coverage results to codecov.io run: ./azure-pipelines/publish-CodeCov.ps1 -CodeCovToken "${{ env.codecov_token }}" -PathToCodeCoverage "${{ runner.temp }}/_artifacts/coverageResults" -Name "${{ runner.os }} Coverage Results" -Flags "${{ runner.os }}" shell: pwsh From 904ff78e41c6c0b5c508080012583a063d4e0eef Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 8 Dec 2024 19:59:31 -0700 Subject: [PATCH 2/5] Make the CI workflow dispatchable --- .github/workflows/build.yml | 3 ++- .github/workflows/libtemplate-update.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9d87be9..5334bac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: CI +name: 🏭 Build on: push: @@ -6,6 +6,7 @@ on: - main - validate/* pull_request: + workflow_dispatch: env: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true diff --git a/.github/workflows/libtemplate-update.yml b/.github/workflows/libtemplate-update.yml index 0501d5e..f78c7e0 100644 --- a/.github/workflows/libtemplate-update.yml +++ b/.github/workflows/libtemplate-update.yml @@ -1,4 +1,4 @@ -name: Library.Template update +name: ⛜ Library.Template update # PREREQUISITE: This workflow requires the repo to be configured to allow workflows to create pull requests. # Visit https://github.com/USER/REPO/settings/actions From 9bbf8a32612f990db1c8972293af5a972febb406 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Mon, 9 Dec 2024 11:17:13 -0700 Subject: [PATCH 3/5] Fix docfx build on private repos --- .github/workflows/docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1820866..70b779b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,6 +10,7 @@ permissions: actions: read pages: write id-token: write + contents: read # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. From 4fa9e72acb9f30a3cdb4f15c60681fc2b71273f0 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Mon, 9 Dec 2024 11:17:23 -0700 Subject: [PATCH 4/5] Fix code coverage merge on GitHub Actions --- azure-pipelines/artifacts/coverageResults.ps1 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/azure-pipelines/artifacts/coverageResults.ps1 b/azure-pipelines/artifacts/coverageResults.ps1 index a6c8f42..8c68216 100644 --- a/azure-pipelines/artifacts/coverageResults.ps1 +++ b/azure-pipelines/artifacts/coverageResults.ps1 @@ -3,14 +3,16 @@ $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") $coverageFiles = @(Get-ChildItem "$RepoRoot/test/*.cobertura.xml" -Recurse | Where {$_.FullName -notlike "*/In/*" -and $_.FullName -notlike "*\In\*" }) # Prepare code coverage reports for merging on another machine -if ($env:SYSTEM_DEFAULTWORKINGDIRECTORY) { - Write-Host "Substituting $env:SYSTEM_DEFAULTWORKINGDIRECTORY with `"{reporoot}`"" +$repoRoot = $env:SYSTEM_DEFAULTWORKINGDIRECTORY +if (!$repoRoot) { $repoRoot = $env:GITHUB_WORKSPACE } +if ($repoRoot) { + Write-Host "Substituting $repoRoot with `"{reporoot}`"" $coverageFiles |% { - $content = Get-Content -LiteralPath $_ |% { $_ -Replace [regex]::Escape($env:SYSTEM_DEFAULTWORKINGDIRECTORY), "{reporoot}" } + $content = Get-Content -LiteralPath $_ |% { $_ -Replace [regex]::Escape($repoRoot), "{reporoot}" } Set-Content -LiteralPath $_ -Value $content -Encoding UTF8 } } else { - Write-Warning "coverageResults: Azure Pipelines not detected. Machine-neutral token replacement skipped." + Write-Warning "coverageResults: Cloud build not detected. Machine-neutral token replacement skipped." } if (!((Test-Path $RepoRoot\bin) -and (Test-Path $RepoRoot\obj))) { return } From 89afbfc38b5726d311954e4902e8f2c75f62f535 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Mon, 9 Dec 2024 11:17:37 -0700 Subject: [PATCH 5/5] Add release workflow --- .github/workflows/release.yml | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d6f5cc4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,87 @@ +name: 🎁 Release + +on: + release: + types: [published] + workflow_dispatch: + inputs: + ship_run_id: + description: ID of the GitHub workflow run to ship + required: true + +run-name: ${{ github.ref_name }} + +permissions: + actions: read + contents: write + +jobs: + release: + runs-on: ubuntu-22.04 + steps: + - name: ⚙️ Initialization + shell: pwsh + run: | + if ('${{ secrets.NUGET_API_KEY }}') { + echo "NUGET_API_KEY_DEFINED=true" >> $GITHUB_ENV + } + + - name: 🔎 Search for build of ${{ github.ref }} + shell: pwsh + id: findrunid + env: + GH_TOKEN: ${{ github.token }} + run: | + if ('${{ inputs.ship_run_id }}') { + $runid = '${{ inputs.ship_run_id }}' + } else { + $restApiRoot = '/repos/${{ github.repository }}' + + # Resolve the tag reference to a commit sha + $resolvedRef = gh api ` + -H "Accept: application/vnd.github+json" ` + -H "X-GitHub-Api-Version: 2022-11-28" ` + $restApiRoot/git/ref/tags/${{ github.ref_name }} ` + | ConvertFrom-Json + $commitSha = $resolvedRef.object.sha + + Write-Host "Resolved ${{ github.ref_name }} to $commitSha" + + $releases = gh run list -R ${{ github.repository }} -c $commitSha -w .github/workflows/build.yml -s success --json databaseId,startedAt ` + | ConvertFrom-Json | Sort-Object startedAt -Descending + + if ($releases.length -eq 0) { + Write-Error "No successful builds found for ${{ github.ref }}." + } elseif ($releases.length -gt 1) { + Write-Warning "More than one successful run found for ${{ github.ref }}. Artifacts from the most recent successful run will ship." + } + + $runid = $releases[0].databaseId + } + + Write-Host "Using artifacts from run-id: $runid" + + Echo "runid=$runid" >> $env:GITHUB_OUTPUT + + - name: 🔻 Download deployables artifacts + uses: actions/download-artifact@v4 + with: + name: deployables-Linux + path: ${{ runner.temp }}/deployables + run-id: ${{ steps.findrunid.outputs.runid }} + github-token: ${{ github.token }} + + - name: 💽 Upload artifacts to release + shell: pwsh + if: ${{ github.event.release.assets_url }} != '' + env: + GH_TOKEN: ${{ github.token }} + run: | + Get-ChildItem '${{ runner.temp }}/deployables' |% { + Write-Host "Uploading $($_.Name) to release..." + gh release -R ${{ github.repository }} upload "${{ github.ref_name }}" $_.FullName + } + + - name: 🚀 Push NuGet packages + run: dotnet nuget push ${{ runner.temp }}/deployables/*.nupkg --source https://api.nuget.org/v3/index.json -k '${{ secrets.NUGET_API_KEY }}' + if: ${{ env.NUGET_API_KEY_DEFINED == 'true' }}