Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release automation #2062

Merged
merged 6 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions .github/workflows/after-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
name: after-release

on:
release:
types: [ published ]

permissions: {}

env:
RELEASE_DATE: ${{ github.event.release.published_at }}
RELEASE_NOTES: ${{ github.event.release.body }}
RELEASE_URL: ${{ github.event.release.html_url }}
RELEASE_VERSION: ${{ github.event.release.tag_name }}

jobs:

update-changelog:
runs-on: [ ubuntu-latest ]
if: github.event_name == 'release'

concurrency:
group: '${{ github.workflow }}-changelog'
cancel-in-progress: false

steps:

- name: Generate GitHub application token
id: generate-application-token
uses: peter-murray/workflow-application-token-action@dc0413987a085fa17d19df9e47d4677cf81ffef3 # v3.0.0
with:
application_id: ${{ secrets.POLLY_UPDATER_BOT_APP_ID }}
application_private_key: ${{ secrets.POLLY_UPDATER_BOT_KEY }}
permissions: 'contents:write, pull_requests:write'

- name: Checkout code
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
token: ${{ steps.generate-application-token.outputs.token }}

- name: Update Polly version
shell: pwsh
run: ./eng/bump-version.ps1 ${env:RELEASE_VERSION}

- name: Update CHANGELOG
shell: pwsh
run: ./eng/update-changelog.ps1 ${env:RELEASE_VERSION} ${env:RELEASE_NOTES} ${env:GITHUB_SERVER_URL}

- name: Update public API baselines
shell: pwsh
run: ./eng/update-baselines.ps1

- name: Push changes to GitHub
id: push-changes
shell: pwsh
env:
GIT_COMMIT_USER_EMAIL: '138034000+polly-updater-bot[bot]@users.noreply.github.com'
GIT_COMMIT_USER_NAME: 'polly-updater-bot[bot]'
run: |
$gitStatus = (git status --porcelain)

if ([string]::IsNullOrEmpty($gitStatus)) {
throw "No changes to commit."
}

git config color.diff always
git --no-pager diff

$branchName = "changelog-${env:RELEASE_VERSION}"

git config user.email ${env:GIT_COMMIT_USER_EMAIL} | Out-Null
git config user.name ${env:GIT_COMMIT_USER_NAME} | Out-Null
git fetch origin --no-tags | Out-Null
git rev-parse --verify --quiet "remotes/origin/${branchName}" | Out-Null

if ($LASTEXITCODE -eq 0) {
Write-Host "Branch ${branchName} already exists."
exit 0
}

git checkout -b $branchName
git add .
git commit -m "Update CHANGELOG`n`nUpdate CHANGELOG and samples for v${env:RELEASE_VERSION}."
git push -u origin $branchName

"branch-name=${branchName}" >> $env:GITHUB_OUTPUT
"updated-version=true" >> $env:GITHUB_OUTPUT

- name: Create pull request
if: steps.push-changes.outputs.updated-version == 'true'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
BASE_BRANCH: ${{ github.event.repository.default_branch }}
HEAD_BRANCH: ${{ steps.push-changes.outputs.branch-name }}
with:
github-token: ${{ steps.generate-application-token.outputs.token }}
script: |
const version = process.env.RELEASE_VERSION;
const { repo, owner } = context.repo;

const releaseUrl = process.env.RELEASE_URL;
const workflowUrl = `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;

const { data: pr } = await github.rest.pulls.create({
title: 'Bump version and update CHANGELOG',
owner,
repo,
head: process.env.HEAD_BRANCH,
base: process.env.BASE_BRANCH,
draft: true,
body: [
`Update CHANGELOG and samples version for [v${version}](${releaseUrl}).`,
'',
`This pull request was generated by [GitHub Actions](${workflowUrl}).`
].join('\n')
});

core.notice(`Created pull request ${owner}/${repo}#${pr.number}: ${pr.html_url}`);

update-milestone:
runs-on: [ ubuntu-latest ]

concurrency:
group: '${{ github.workflow }}-milestone'
cancel-in-progress: false

permissions:
issues: write

steps:

- name: Close milestone
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const { repo, owner } = context.repo;

const { data: milestones } = await github.rest.issues.listMilestones({
owner,
repo,
state: 'open',
});

const milestone = milestones.find((p) => p.title === process.env.RELEASE_VERSION);

if (!milestone) {
return;
}

try {
await github.rest.issues.updateMilestone({
owner,
repo,
milestone_number: milestone.number,
state: 'closed',
due_on: process.env.RELEASE_DATE,
});
} catch (error) {
// Ignore
}
71 changes: 71 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: release

on:
workflow_dispatch:
inputs:
version:
description: 'The version to create the release for.'
required: true
type: string

permissions: {}

jobs:
release:
runs-on: [ ubuntu-latest ]

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

permissions:
contents: write
issues: write

steps:

- name: Checkout code
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2

- name: Create release
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
VERSION: ${{ inputs.version }}
with:
script: |
const { repo, owner } = context.repo;
const draft = true;
let version = process.env.VERSION;

if (version.startsWith('v')) {
version = version.slice(1);
}

const tag_name = version;
const name = tag_name;

const { data: notes } = await github.rest.repos.generateReleaseNotes({
owner,
repo,
tag_name,
target_commitish: process.env.DEFAULT_BRANCH,
});

const body = notes.body
.split('\n')
.filter((line) => !line.includes(' @dependabot '))
.filter((line) => !line.includes(' @github-actions '))
.filter((line) => !line.includes(' @polly-updater-bot '))
.join('\n');

const { data: release } = await github.rest.repos.createRelease({
owner,
repo,
tag_name,
name,
body,
draft,
});

core.notice(`Created release ${release.name}: ${release.html_url}`);
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<!-- markdownlint-disable MD034 -->

<!-- next-release -->

## 8.3.1

* Add example for chaos engineering by [@martintmk](https://github.com/martintmk) in https://github.com/App-vNext/Polly/pull/1956
Expand Down
40 changes: 40 additions & 0 deletions eng/bump-version.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#! /usr/bin/env pwsh
param(
[Parameter(Mandatory = $true)][string] $ReleaseVersion
)

$ErrorActionPreference = "Stop"

$repo = Join-Path $PSScriptRoot ".."
$properties = Join-Path $repo "Directory.Packages.props"

$xml = [xml](Get-Content $properties)
$pollyVersion = $xml.SelectSingleNode('Project/PropertyGroup/PollyVersion')

if ($ReleaseVersion.StartsWith("v")) {
$ReleaseVersion = $ReleaseVersion.Substring(1)
}

$version = [System.Version]::new($ReleaseVersion)
$releasedVersion = $version.ToString()

Write-Host "Bumping version from $($pollyVersion.InnerText) to $releasedVersion"

$pollyVersion.InnerText = $releasedVersion

$settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = New-Object System.Text.UTF8Encoding($false)
$settings.Indent = $true
$settings.OmitXmlDeclaration = $true

try {
$writer = [System.Xml.XmlWriter]::Create($properties, $settings)
$xml.Save($writer)
} finally {
if ($writer) {
$writer.Flush()
$writer.Dispose()
"" >> $properties
}
$writer = $null
}
56 changes: 56 additions & 0 deletions eng/update-baselines.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#! /usr/bin/env pwsh
param()

$ErrorActionPreference = "Stop"

$encoding = New-Object System.Text.UTF8Encoding($true)
$releasedName = "PublicAPI.Shipped.txt"
$unshippedName = "PublicAPI.Unshipped.txt"

$repo = Join-Path $PSScriptRoot ".."
$src = Join-Path $repo "src"
$files = Get-ChildItem -Path $src -Filter $unshippedName -Recurse

# See https://github.com/dotnet/roslyn-analyzers/blob/b07c100bfc66013a8444172d00cfa04c9ceb5a97/src/PublicApiAnalyzers/Core/CodeFixes/DeclarePublicApiFix.cs#L373-L390
$comparer = {
param(
[string]$x,
[string]$y
)
$comparison = [System.StringComparer]::OrdinalIgnoreCase.Compare($x, $y)
if ($comparison -eq 0) {
$comparison = [System.StringComparer]::Ordinal.Compare($x, $y)
}
return $comparison;
}

foreach ($file in $files) {
$directory = [System.IO.Path]::GetDirectoryName($file)
$changesPath = Join-Path $directory $unshippedName
$baselinePath = Join-Path $directory $releasedName

$baseline = Get-Content $baselinePath
$baseline = [System.Collections.Generic.List[string]]$baseline

$additions = Get-Content $changesPath
$additions = [System.Collections.Generic.List[string]]$additions

$edited = $false

# Skip the "#nullable enable" header
$index = (($additions.Count -gt 0) -And ($additions[0] -eq "#nullable enable")) ? 1 : 0
while ($additions.Count -gt $index) {
$addition = $additions[$index]
$additions.RemoveAt($index)
$baseline.Add($addition)
$edited = $true
}

if ($edited) {
$additions.Sort($comparer)
$baseline.Sort($comparer)

$additions | Set-Content $changesPath -Encoding $encoding
$baseline | Set-Content $baselinePath -Encoding $encoding
}
}
49 changes: 49 additions & 0 deletions eng/update-changelog.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#! /usr/bin/env pwsh
param(
[Parameter(Mandatory = $true)][string] $ReleaseVersion,
[Parameter(Mandatory = $true)][string] $ReleaseNotesText,
[Parameter(Mandatory = $true)][string] $GitHubServerUrl
)

$ErrorActionPreference = "Stop"

if ($ReleaseVersion.StartsWith("v")) {
$ReleaseVersion = $ReleaseVersion.Substring(1)
}

Write-Host "Updating CHANGELOG for v$ReleaseVersion"

$repo = Join-Path $PSScriptRoot ".."
$changelog = Join-Path $repo "CHANGELOG.md"

$lines = Get-Content $changelog
$lines = [System.Collections.Generic.List[string]]$lines

$entry = [System.Collections.Generic.List[string]]@(
"",
"## ${ReleaseVersion}",
""
)

$releaseNotes = $ReleaseNotesText.Split("`n") | Select-Object -Skip 1
foreach ($line in $releaseNotes) {
if ($line -eq "") {
continue
}
if ($line.StartsWith("##")) {
break
}
if (!$line.StartsWith("* ")) {
continue
}

# Update the user's login to link to their GitHub profile
$line = $line -Replace "\@(([a-zA-Z0-9\-]+))", ('[@$1](' + $GitHubServerUrl + '/$1)')

$entry.Add($line)
}

$index = $lines.IndexOf("<!-- next-release -->")
$lines.InsertRange($index + 1, $entry)

$lines | Set-Content $changelog