Skip to content

Commit 3f407ac

Browse files
authored
Apply container release procedures from ServiceControl to ServicePulse (#1894)
* Apply container release procedures from ServiceControl to ServicePulse * Also do Major.Minor tag * Readme as well * Throw if releasing a lower-versioned patch on the same minor * Apply version check to all release jobs
1 parent 4caa61f commit 3f407ac

File tree

4 files changed

+209
-1
lines changed

4 files changed

+209
-1
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
name: Validate version
2+
description: Validate that provided version can be released
3+
inputs:
4+
version:
5+
description: Full version
6+
required: true
7+
outputs:
8+
major:
9+
description: Major version
10+
value: ${{ steps.validate.outputs.major }}
11+
minor:
12+
description: Minor version
13+
value: ${{ steps.validate.outputs.minor }}
14+
patch:
15+
description: Patch version
16+
value: ${{ steps.validate.outputs.patch }}
17+
prerelease:
18+
description: Prerelease label
19+
value: ${{ steps.validate.outputs.prerelease }}
20+
container-tags:
21+
description: Tags to be added for container releases (comma-separated)
22+
value: ${{ steps.validate.outputs.container-tags }}
23+
latest:
24+
description: Evaluates to `true` if the version should be flagged as the new latest version
25+
value: ${{ steps.validate.outputs.latest }}
26+
runs:
27+
using: composite
28+
steps:
29+
- name: Validate versions
30+
id: validate
31+
shell: pwsh
32+
run: |
33+
$version = "${{ inputs.version }}"
34+
35+
Write-Output "Received version: $version"
36+
$isMatch = $version -match '^(?<Major>\d+)\.(?<Minor>\d+)\.(?<Patch>\d+)(-(?<PrereleaseLabel>[\w\-\.]+))?$'
37+
if ( -not $isMatch)
38+
{
39+
throw "Invalid version $version"
40+
exit 1
41+
}
42+
43+
Write-Output "Version $version is valid."
44+
45+
$major = [int]$Matches.Major
46+
$minor = [int]$Matches.Minor
47+
$patch = [int]$Matches.Patch
48+
$prereleaseLabel = $Matches.PrereleaseLabel
49+
$isPrerelease = -not -not $prereleaseLabel
50+
51+
echo "major=$major" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf-8 -Append
52+
echo "minor=$minor" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf-8 -Append
53+
echo "patch=$patch" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf-8 -Append
54+
echo "prerelease=$prereleaseLabel" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf-8 -Append
55+
56+
# Get highest-versioned releases
57+
$releases = Invoke-WebRequest -Uri https://api.github.com/repos/particular/ServicePulse/releases -UseBasicParsing |
58+
ConvertFrom-Json -ErrorAction Stop
59+
$releasedVersions = $releases |
60+
where Draft -eq $false |
61+
where Prerelease -eq $false |
62+
where Name -match '^\d+\.\d+\.\d+$' | # Ignore prerelease tags
63+
select -ExpandProperty tag_name
64+
65+
$latestVersion = $releasedVersions |
66+
Sort-Object { [System.Version]$_ } -Descending |
67+
select -First 1
68+
Write-Output "Latest released version on GitHub = $latestVersion"
69+
70+
$latestForMajor = $releasedVersions |
71+
where { ([System.Version]$_).Major -eq $major } |
72+
Sort-Object { [System.Version]$_ } -Descending |
73+
select -First 1
74+
Write-Output "Latest released v$major version on GitHub = $latestForMajor"
75+
76+
$latestForMinor = $releasedVersions |
77+
where { ([System.Version]$_).Major -eq $major -and ([System.Version]$_).Minor -eq $minor } |
78+
Sort-Object { [System.Version]$_ } -Descending |
79+
select -First 1
80+
Write-Output "Latest released v$major.$minor version on GitHub = $latestForMinor"
81+
82+
$isLatest = $false
83+
if ( -not $isPrerelease ) {
84+
$vNew = [System.Version]$version
85+
$vPrev = [System.Version]$latestVersion
86+
$vPrevForMajor = [System.Version]$latestForMajor
87+
$vPrevForMinor = [System.Version]$latestForMinor
88+
89+
if ($vNew -ge $vPrev) {
90+
$isLatest = $true
91+
}
92+
93+
# For new majors, vNew < null evaluates to false, no exception thrown
94+
# Logic would not be appropriate if support policy allows backports to previous minors
95+
if ($vNew -lt $vPrevForMajor) {
96+
throw "Releasing a version $version that is not the latest version for the major would break container tagging and is not supported. (Latest v$major version is $latestForMajor)"
97+
}
98+
99+
# For new minors, vNew < null evaluates to false, no exception thrown
100+
if ($vNew -lt $vPrevForMinor) {
101+
throw "Releasing a version $version that is not the latest version for the minor would break container tagging and is not supported. (Latest v$major.$minor version is $latestForMinor)"
102+
}
103+
}
104+
105+
Write-Output "Major = $major, Minor = $minor, Patch = $patch, PrereleaseLabel = $prereleaseLabel, IsPrerelease = $isPrerelease, IsLatest = $isLatest"
106+
107+
Write-Output "Determining container tags..."
108+
$tags = @($version)
109+
110+
if ( -not $isPrerelease ) {
111+
$tags += "$major.$minor"
112+
$tags += "$major"
113+
if ($isLatest) {
114+
$tags += 'latest'
115+
}
116+
}
117+
118+
Write-Output "Tags to apply:"
119+
$tags | ForEach-Object { Write-Output " * '$_'" }
120+
121+
$tagsDelimited = $tags -Join ','
122+
echo "container-tags=$tagsDelimited" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf-8 -Append
123+
echo "latest=$($isLatest.ToString().ToLower())" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf-8 -Append
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Push container images
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
version:
6+
description: Full version of container image to push. Normally, this should exactly match the tag name.
7+
required: true
8+
type: string
9+
jobs:
10+
push:
11+
runs-on: ubuntu-latest
12+
name: Push
13+
defaults:
14+
run:
15+
shell: pwsh
16+
steps:
17+
- name: Checkout
18+
uses: actions/[email protected]
19+
- name: Validate build version
20+
id: validate
21+
uses: ./.github/actions/validate-version
22+
with:
23+
version: ${{ inputs.version }}
24+
- name: Log in to GitHub container registry
25+
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
26+
- name: Login to Docker Hub
27+
uses: docker/[email protected]
28+
with:
29+
username: ${{ secrets.DOCKERHUB_USERNAME }}
30+
password: ${{ secrets.DOCKERHUB_TOKEN }}
31+
- name: Publish to Docker Hub
32+
run: |
33+
$containers = @('servicepulse')
34+
$tags = "${{ steps.validate.outputs.container-tags }}" -Split ','
35+
$sourceTag = "${{ inputs.version }}"
36+
37+
foreach ($tag in $tags)
38+
{
39+
foreach($name in $containers)
40+
{
41+
Write-Output "::group::Pushing $($name):$($tag)"
42+
$cmd = "docker buildx imagetools create --tag particular/$($name):$($tag) ghcr.io/particular/$($name):$($sourceTag)"
43+
Write-Output "Command: $cmd"
44+
Invoke-Expression $cmd
45+
Write-Output "::endgroup::"
46+
}
47+
}
48+
- name: Update Docker Hub Description
49+
if: ${{ steps.validate.outputs.latest == 'true' }}
50+
uses: peter-evans/[email protected]
51+
with:
52+
username: ${{ secrets.DOCKERHUB_USERNAME }}
53+
password: ${{ secrets.DOCKERHUB_TOKEN }}
54+
repository: particular/servicepulse
55+
readme-filepath: ./src/Container/README.md

.github/workflows/release.yml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ jobs:
3939
# .NET Build and sign
4040
- name: Build
4141
run: dotnet build src --configuration Release
42+
- name: Validate build version
43+
if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
44+
uses: ./.github/actions/validate-version
45+
with:
46+
version: ${{ env.MinVerVersion }}
4247
- name: Sign NuGet packages
4348
uses: Particular/[email protected]
4449
with:
@@ -99,6 +104,10 @@ jobs:
99104
linux-container:
100105
if: ${{ github.actor != 'dependabot[bot]' }}
101106
runs-on: ubuntu-22.04
107+
name: linux-container
108+
defaults:
109+
run:
110+
shell: bash
102111
steps:
103112
- name: Check for secrets
104113
env:
@@ -113,6 +122,11 @@ jobs:
113122
run: dotnet tool install --global minver-cli
114123
- name: Determine version
115124
run: echo "MinVerVersion=$(minver)" >> $GITHUB_ENV
125+
- name: Validate build version
126+
if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
127+
uses: ./.github/actions/validate-version
128+
with:
129+
version: ${{ env.MinVerVersion }}
116130
- name: Set up Node.js
117131
uses: actions/[email protected]
118132
with:
@@ -138,10 +152,22 @@ jobs:
138152
env:
139153
TAG_NAME: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || env.MinVerVersion }}
140154
run: |
141-
docker buildx build --push --tag ghcr.io/particular/servicepulse:${{ env.TAG_NAME }} --file src/Container/Dockerfile \
155+
docker buildx build --push --tag ghcr.io/particular/servicepulse:${{ env.TAG_NAME }} \
156+
--file src/Container/Dockerfile \
142157
--build-arg NGINX_TAGORDIGEST="@${{ env.NGINX_DIGEST }}" \
143158
--build-arg VERSION=${{ env.MinVerVersion }} \
144159
--build-arg GITHUB_SHA=${{ github.sha }} \
145160
--build-arg GITHUB_REF_NAME=${{ github.ref.name }} \
161+
--annotation "index:org.opencontainers.image.title=ServicePulse" \
162+
--annotation "index:org.opencontainers.image.description=ServicePulse provides real-time production monitoring for distributed applications. It monitors the health of a system's endpoints, detects processing errors, sends failed messages for reprocessing, and ensures the specific environment's needs are met, all in one consolidated dashboard." \
163+
--annotation "index:org.opencontainers.image.created=$(date '+%FT%TZ')" \
164+
--annotation "index:org.opencontainers.image.revision=${{ github.sha }}" \
165+
--annotation "index:org.opencontainers.image.authors=Particular Software" \
166+
--annotation "index:org.opencontainers.image.vendor=Particular Software" \
167+
--annotation "index:org.opencontainers.image.version=${{ env.MinVerVersion }}" \
168+
--annotation "index:org.opencontainers.image.source=https://github.com/${{ github.repository }}/tree/${{ github.sha }}" \
169+
--annotation "index:org.opencontainers.image.url=https://hub.docker.com/r/particular/servicepulse" \
170+
--annotation "index:org.opencontainers.image.documentation=https://docs.particular.net/servicepulse/" \
171+
--annotation "index:org.opencontainers.image.base.name=nginx@${{ env.NGINX_DIGEST }}" \
146172
--platform linux/arm64,linux/arm,linux/amd64 .
147173
docker buildx imagetools inspect ghcr.io/particular/servicepulse:${{ env.TAG_NAME }}

src/Container/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ If `particular/servicepulse:1.30.1` is the latest release in the version 1 major
6363

6464
The major version tag is never added to images pushed to [the GitHub Container Registry](https://github.com/Particular/ServicePulse/pkgs/container/servicepulse).
6565

66+
#### Minor version tag
67+
68+
The latest release within a minor version will be tagged with `{major}.{minor}` on images pushed to Docker Hub. This allows users to target the latest patch within a specific minor version.
69+
6670
## Built With
6771

6872
This image is built from the stable Alpine version of the [nginx official Docker image](https://hub.docker.com/_/nginx/).

0 commit comments

Comments
 (0)