Skip to content

Commit

Permalink
Release automation (#2062)
Browse files Browse the repository at this point in the history
  • Loading branch information
martincostello committed Apr 25, 2024
1 parent b89ce23 commit 20cf892
Show file tree
Hide file tree
Showing 6 changed files with 377 additions and 0 deletions.
159 changes: 159 additions & 0 deletions .github/workflows/after-release.yml
@@ -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
@@ -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
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
@@ -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
@@ -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
@@ -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

0 comments on commit 20cf892

Please sign in to comment.