Skip to content

Commit

Permalink
[repo] NuGet push release automation (#5675)
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeBlanch authored Jun 7, 2024
1 parent 90871e0 commit 84cd83a
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 51 deletions.
54 changes: 51 additions & 3 deletions .github/workflows/post-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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] }}
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/prepare-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -127,3 +127,4 @@ jobs:
-botUserName '${{ needs.automation.outputs.username }}' `
-gitUserName '${{ needs.automation.outputs.username }}' `
-gitUserEmail '${{ needs.automation.outputs.email }}'
4 changes: 2 additions & 2 deletions .github/workflows/publish-packages-1.0.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: ${{ github.ref_name }}-packages
path: '**/bin/**/*.*nupkg'
path: 'src/**/*.*nupkg'

- name: Publish MyGet
env:
MYGET_TOKEN_EXISTS: ${{ secrets.MYGET_TOKEN != '' }}
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
Expand Down
107 changes: 65 additions & 42 deletions build/RELEASING.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
<details>
<summary>Instructions for pushing tags manually</summary>
Expand Down Expand Up @@ -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 :)
<details>
<summary>Instructions for pushing packages to NuGet manually</summary>

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 <actual api key>
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 <actual api key>
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`.
</details>
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)
Expand All @@ -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.
84 changes: 84 additions & 0 deletions build/scripts/post-release.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
4 changes: 1 addition & 3 deletions build/scripts/prepare-release.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 84cd83a

Please sign in to comment.