diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index 6f26e1058a..42c2ce11db 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -7,21 +7,69 @@ on: required: true description: 'Release tag' type: string + release: - types: [published] + types: + - published + + issue_comment: + types: + - created jobs: automation: uses: ./.github/workflows/automation.yml secrets: inherit - post-release: + push-packages-and-publish-release: + runs-on: ubuntu-latest + + needs: automation + + if: | + github.event_name == 'issue_comment' + && github.event.issue.pull_request + && github.event.issue.locked == true + && github.event.comment.user.login != needs.automation.outputs.username + && contains(github.event.comment.body, '/PushPackages') + && startsWith(github.event.issue.title, '[repo] Prepare release ') + && github.event.issue.pull_request.merged_at + && needs.automation.outputs.enabled + + env: + GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }} + + steps: + - name: check out code + uses: actions/checkout@v4 + with: + token: ${{ secrets[needs.automation.outputs.token-secret-name] }} + ref: ${{ github.event.repository.default_branch }} + + - name: Push packages and publish release + shell: pwsh + env: + NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} + run: | + Import-Module .\build\scripts\post-release.psm1 + + PushPackagesPublishReleaseUnlockAndPostNoticeOnPrepareReleasePullRequest ` + -gitRepository '${{ github.repository }}' ` + -pullRequestNumber '${{ github.event.issue.number }}' ` + -botUserName '${{ needs.automation.outputs.username }}' ` + -commentUserName '${{ github.event.comment.user.login }}' ` + -artifactDownloadPath '${{ github.workspace }}/artifacts' ` + -pushToNuget '${{ secrets.NUGET_TOKEN != '' }}' + + post-release-published: runs-on: ubuntu-latest needs: - automation - if: needs.automation.outputs.enabled + if: | + needs.automation.outputs.enabled + && (github.event_name == 'release' || github.event_name == 'workflow_dispatch') env: GH_TOKEN: ${{ secrets[needs.automation.outputs.token-secret-name] }} diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 087081efbb..1225f098e9 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -89,7 +89,7 @@ jobs: -pullRequestNumber '${{ github.event.pull_request.number }}' ` -botUserName '${{ needs.automation.outputs.username }}' - create-release-tag-unlock-pr-post-notice: + create-release-tag-pr-post-notice: runs-on: ubuntu-latest needs: automation @@ -127,3 +127,4 @@ jobs: -botUserName '${{ needs.automation.outputs.username }}' ` -gitUserName '${{ needs.automation.outputs.username }}' ` -gitUserEmail '${{ needs.automation.outputs.email }}' + diff --git a/.github/workflows/publish-packages-1.0.yml b/.github/workflows/publish-packages-1.0.yml index 553b8bbfc6..7553bd6662 100644 --- a/.github/workflows/publish-packages-1.0.yml +++ b/.github/workflows/publish-packages-1.0.yml @@ -53,7 +53,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ github.ref_name }}-packages - path: '**/bin/**/*.*nupkg' + path: 'src/**/*.*nupkg' - name: Publish MyGet env: @@ -61,7 +61,7 @@ jobs: if: env.MYGET_TOKEN_EXISTS == 'true' # Skip MyGet publish if run on a fork without the secret run: | nuget setApiKey ${{ secrets.MYGET_TOKEN }} -Source https://www.myget.org/F/opentelemetry/api/v2/package - nuget push **/bin/**/*.nupkg -Source https://www.myget.org/F/opentelemetry/api/v2/package + nuget push src/**/*.nupkg -Source https://www.myget.org/F/opentelemetry/api/v2/package post-build: runs-on: ubuntu-latest diff --git a/build/RELEASING.md b/build/RELEASING.md index d25f13b135..f9075727bd 100644 --- a/build/RELEASING.md +++ b/build/RELEASING.md @@ -1,6 +1,7 @@ # Release process -**Only for Maintainers.** +**Note: Approvers (collaborators) can perform much of the release process but +Maintainers (admins) are needed to merge PRs and for the push to NuGet.** 1. Decide the component(s) and tag name (version name) to be released. We use [MinVer](https://github.com/adamralph/minver) to do versioning, which @@ -40,15 +41,13 @@ * `OpenTelemetry.Shims.OpenTracing` - Defined by spec (stable but incomplete implementation) - * As of the `1.9.0` release cycle instrumentation packages and core - unstable packages always depend on the stable versions of core - packages. Before releasing a non-core component ensure the - `OTelLatestStableVer` property in `Directory.Packages.props` has been - updated to the latest stable core version. + * As of the `1.9.0` release cycle core unstable packages always depend on + the stable versions of core packages. Before releasing a non-core + component ensure the `OTelLatestStableVer` property in + `Directory.Packages.props` has been updated to the latest stable core + version. - 2. Prepare for release - - Run the [Prepare for a + 2. Run the [Prepare for a release](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/prepare-release.yml) workflow. Specify the `tag-prefix` and the `version` for the release. Make sure to run the workflow on the branch being released. This is typically @@ -91,11 +90,10 @@ comment and lock the PR. Post a comment with "/CreateReleaseTag" in the body. This will tell the [Prepare for a release](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/prepare-release.yml) - workflow to push the tag for the merge commit of the PR and to call the - [Build, pack, and publish to + workflow to push the tag for the merge commit of the PR which will trigger + the [Build, pack, and publish to MyGet](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/publish-packages-1.0.yml) - workflow. Once packages are available a comment will be posted on the PR - opened in step 2 with a link to the artifacts. + workflow.
Instructions for pushing tags manually @@ -127,51 +125,76 @@ 5. :stop_sign: Wait for the [Build, pack, and publish to MyGet](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/publish-packages-1.0.yml) - workflow to complete. + workflow to complete. When complete a trigger will automatically add a + comment on the PR opened by [Prepare for a + release](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/prepare-release.yml) + workflow in step 2. Use MyGet or download the packages using the provided + link to validate locally everything works. After validation has been + performed have a maintainer post a comment with "/PushPackages" in the body. + This will trigger the [Complete + release](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/prepare-release.yml) + workflow to push the packages to NuGet and publish the draft release created + by the [Build, pack, and publish to + MyGet](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/publish-packages-1.0.yml) + workflow. Comments will automatically be added on the PR opened by [Prepare + for a + release](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/prepare-release.yml) + workflow in step 2 as the process is run and the PR will be unlocked. - 6. Validate locally everything works using the MyGet packages pushed from the - release. Basic sanity checks :) +
+ Instructions for pushing packages to NuGet manually - 7. Download the artifacts from the drop attached to the workflow run. The - artifacts archive (`.zip`) contains all the NuGet packages (`.nupkg`) and - symbols (`.snupkg`) from the build which were pushed to MyGet. + 1. The [Build, pack, and publish to + MyGet](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/publish-packages-1.0.yml) + workflow pushes the packages to MyGet and attaches them as artifacts on + the workflow run. - 8. Extract the artifacts from the archive (`.zip`) into a local folder. + 2. Validate locally everything works using the packages pushed to MyGet or + downloaded from the drop attached to the workflow run. Basic sanity + checks :) - 9. Download latest [nuget.exe](https://www.nuget.org/downloads) into the same - folder from Step 8. + 3. Download the artifacts from the drop attached to the workflow run. The + artifacts archive (`.zip`) contains all the NuGet packages (`.nupkg`) and + symbols (`.snupkg`) from the build which were pushed to MyGet. -10. Create or regenerate an API key from nuget.org (only maintainers have - access). When creating API keys make sure it is set to expire in 1 day or - less. + 4. Extract the artifacts from the archive (`.zip`) into a local folder. -11. Run the following commands from PowerShell from local folder used in Step 8: + 5. Download latest [nuget.exe](https://www.nuget.org/downloads) into the + same folder from step 4. - ```powershell - .\nuget.exe setApiKey + 6. Create or regenerate an API key from nuget.org (only maintainers have + access). When creating API keys make sure it is set to expire in 1 day or + less. - get-childitem -Recurse | where {$_.extension -eq ".nupkg"} | foreach ($_) {.\nuget.exe push $_.fullname -Source https://api.nuget.org/v3/index.json} - ``` + 7. Run the following commands from PowerShell from local folder used in step + 4: -12. Validate that the package(s) are uploaded. Packages are available - immediately to maintainers on nuget.org but aren't publicly visible until - scanning completes. This process usually takes a few minutes. + ```powershell + .\nuget.exe setApiKey -13. Open the - [Releases](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - page on the GitHub repository. The [Build, pack, and publish to - MyGet](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/publish-packages-1.0.yml) - workflow creates a draft release for the tag which was pushed. Edit the - draft Release and click `Publish release`. + get-childitem -Recurse | where {$_.extension -eq ".nupkg"} | foreach ($_) {.\nuget.exe push $_.fullname -Source https://api.nuget.org/v3/index.json} + ``` + + 8. Validate that the package(s) are uploaded. Packages are available + immediately to maintainers on nuget.org but aren't publicly visible until + scanning completes. This process usually takes a few minutes. + + 9. Open the + [Releases](https://github.com/open-telemetry/opentelemetry-dotnet/releases) + page on the GitHub repository. The [Build, pack, and publish to + MyGet](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/publish-packages-1.0.yml) + workflow creates a draft release for the tag which was pushed. Edit the + draft Release and click `Publish release`. +
-14. If a new stable version of the core packages was released, a PR should have + 6. If a new stable version of the core packages was released, a PR should have been automatically created by the [Complete release](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/post-release.yml) workflow to update the `OTelLatestStableVer` property in `Directory.Packages.props` to the just released stable version. Merge that PR once the build passes (this requires the packages be available on NuGet). -15. The [Complete + 7. The [Complete release](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/post-release.yml) workflow should have invoked the [Core version update](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/actions/workflows/core-version-update.yml) @@ -180,6 +203,6 @@ repository which opens a PR to update dependencies. Verify this PR was opened successfully. -16. Post an announcement in the [Slack + 8. Post an announcement in the [Slack channel](https://cloud-native.slack.com/archives/C01N3BC2W7Q). Note any big or interesting new features as part of the announcement. diff --git a/build/scripts/post-release.psm1 b/build/scripts/post-release.psm1 index 51010213cd..fc72bc208a 100644 --- a/build/scripts/post-release.psm1 +++ b/build/scripts/post-release.psm1 @@ -137,6 +137,8 @@ function TryPostPackagesReadyNoticeOnPrepareReleasePullRequest { $body = @" The packages for [$tag](https://github.com/$gitRepository/releases/tag/$tag) are now available: $packagesUrl. + +Once these packages have been validated have a maintainer post a comment with "/PushPackages" in the body if you would like me to push to NuGet. "@ $pullRequestNumber = $pr.number @@ -150,6 +152,88 @@ The packages for [$tag](https://github.com/$gitRepository/releases/tag/$tag) are Export-ModuleMember -Function TryPostPackagesReadyNoticeOnPrepareReleasePullRequest +function PushPackagesPublishReleaseUnlockAndPostNoticeOnPrepareReleasePullRequest { + param( + [Parameter(Mandatory=$true)][string]$gitRepository, + [Parameter(Mandatory=$true)][string]$pullRequestNumber, + [Parameter(Mandatory=$true)][string]$botUserName, + [Parameter(Mandatory=$true)][string]$commentUserName, + [Parameter(Mandatory=$true)][string]$artifactDownloadPath, + [Parameter(Mandatory=$true)][string]$pushToNuget + ) + + $prViewResponse = gh pr view $pullRequestNumber --json author,title,comments | ConvertFrom-Json + + if ($prViewResponse.author.login -ne $botUserName) + { + throw 'PR author was unexpected' + } + + $match = [regex]::Match($prViewResponse.title, '^\[repo\] Prepare release (.*)$') + if ($match.Success -eq $false) + { + throw 'Could not parse tag from PR title' + } + + $tag = $match.Groups[1].Value + + $commentUserPermission = gh api "repos/$gitRepository/collaborators/$commentUserName/permission" | ConvertFrom-Json + if ($commentUserPermission.permission -ne 'admin') + { + gh pr comment $pullRequestNumber ` + --body "I'm sorry @$commentUserName but you don't have permission to push packages. Only maintainers can push to NuGet." + return + } + + $foundComment = $false + $packagesUrl = '' + foreach ($comment in $prViewResponse.comments) + { + if ($comment.author.login -eq $botUserName -and $comment.body.StartsWith("The packages for [$tag](https://github.com/$gitRepository/releases/tag/$tag) are now available:")) + { + $foundComment = $true + break + } + } + + if ($foundComment -eq $false) + { + throw 'Could not find package push comment on pr' + } + + gh release download $tag ` + -p "$tag-packages.zip" ` + -D "$artifactDownloadPath" + + Expand-Archive -LiteralPath "$artifactDownloadPath/$tag-packages.zip" -DestinationPath "$artifactDownloadPath\" + + if ($pushToNuget -eq 'true') + { + gh pr comment $pullRequestNumber ` + --body "I am uploading the packages for ``$tag`` to NuGet and then I will publish the release." + + nuget push "$artifactDownloadPath/**/*.nupkg" -Source https://api.nuget.org/v3/index.json -ApiKey "$env.NUGET_TOKEN" -SymbolApiKey "$env.NUGET_TOKEN" + + if ($LASTEXITCODE -gt 0) + { + gh pr comment $pullRequestNumber ` + --body "Something went wrong uploading the packages for ``$tag`` to NuGet." + + throw 'nuget push failure' + } + } + else { + gh pr comment $pullRequestNumber ` + --body "I am publishing the release without uploading the packages to NuGet because a token wasn't configured." + } + + gh release edit $tag --draft=false + + gh pr unlock $pullRequestNumber +} + +Export-ModuleMember -Function PushPackagesPublishReleaseUnlockAndPostNoticeOnPrepareReleasePullRequest + function CreateStableVersionUpdatePullRequest { param( [Parameter(Mandatory=$true)][string]$gitRepository, diff --git a/build/scripts/prepare-release.psm1 b/build/scripts/prepare-release.psm1 index 78a8754980..bc7809eb9e 100644 --- a/build/scripts/prepare-release.psm1 +++ b/build/scripts/prepare-release.psm1 @@ -100,7 +100,7 @@ function LockPullRequestAndPostNoticeToCreateReleaseTag { @" I noticed this PR was merged. -Post a comment with "/CreateReleaseTag" in the body if you would like me to create the release tag ``$tag`` for [the merge commit](https://github.com/$gitRepository/commit/$commit) and then trigger the package workflow. +Post a comment with "/CreateReleaseTag" in the body if you would like me to create the release tag ``$tag`` for [the merge commit](https://github.com/$gitRepository/commit/$commit) which will trigger the package workflow. "@ gh pr comment $pullRequestNumber --body $body @@ -161,8 +161,6 @@ function CreateReleaseTagAndPostNoticeOnPullRequest { throw 'git push failure' } - gh pr unlock $pullRequestNumber - $body = @" I just pushed the [$tag](https://github.com/$gitRepository/releases/tag/$tag) tag.