Skip to content

Commit 570f613

Browse files
author
Jani Giannoudis
committed
ci: add release workflow
1 parent 9526e88 commit 570f613

1 file changed

Lines changed: 216 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
name: Build & Publish NuGet
2+
3+
on:
4+
repository_dispatch:
5+
types: [release]
6+
workflow_dispatch:
7+
inputs:
8+
version:
9+
description: 'Package version (e.g. 1.5.0 or 1.5.0-beta.1)'
10+
required: true
11+
dry_run:
12+
description: 'Dry run (draft release, no commit, no latest tag)'
13+
type: boolean
14+
default: false
15+
16+
permissions:
17+
contents: write
18+
packages: write
19+
20+
env:
21+
DOTNET_VERSION: '10.0.x'
22+
23+
jobs:
24+
build-and-publish:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- name: Determine parameters
28+
id: params
29+
run: |
30+
if [ "${{ github.event_name }}" = "repository_dispatch" ]; then
31+
VERSION="${{ github.event.client_payload.version }}"
32+
IS_PRERELEASE="${{ github.event.client_payload.is_prerelease }}"
33+
DRY_RUN="${{ github.event.client_payload.dry_run }}"
34+
else
35+
VERSION="${{ inputs.version }}"
36+
DRY_RUN="${{ inputs.dry_run }}"
37+
if [[ "$VERSION" == *-* ]]; then
38+
IS_PRERELEASE="true"
39+
else
40+
IS_PRERELEASE="false"
41+
fi
42+
fi
43+
44+
echo "version=${VERSION}" >> $GITHUB_OUTPUT
45+
echo "is_prerelease=${IS_PRERELEASE}" >> $GITHUB_OUTPUT
46+
echo "dry_run=${DRY_RUN}" >> $GITHUB_OUTPUT
47+
48+
if [ "${DRY_RUN}" = "true" ]; then
49+
echo "make_draft=true" >> $GITHUB_OUTPUT
50+
else
51+
echo "make_draft=false" >> $GITHUB_OUTPUT
52+
fi
53+
54+
# Use PAT for checkout so the push triggers no recursive workflows
55+
# and the tag/release is created on the correct commit
56+
- uses: actions/checkout@v4
57+
with:
58+
token: ${{ secrets.PAT_DISPATCH }}
59+
fetch-depth: 0
60+
61+
# ── Version Guard ────────────────────────────────
62+
- name: Check if version already exists
63+
if: steps.params.outputs.dry_run != 'true'
64+
uses: actions/github-script@v7
65+
with:
66+
github-token: ${{ secrets.PAT_DISPATCH }}
67+
script: |
68+
const version = '${{ steps.params.outputs.version }}';
69+
const tag = `v${version}`;
70+
const owner = context.repo.owner;
71+
const repo = context.repo.repo;
72+
const errors = [];
73+
74+
// 1. Check git tag
75+
try {
76+
await github.rest.git.getRef({ owner, repo, ref: `tags/${tag}` });
77+
errors.push(`Git tag '${tag}' already exists`);
78+
} catch (e) {
79+
if (e.status !== 404) throw e;
80+
console.log(`✅ Git tag '${tag}' is available`);
81+
}
82+
83+
// 2. Check GitHub Release
84+
try {
85+
await github.rest.repos.getReleaseByTag({ owner, repo, tag });
86+
errors.push(`GitHub Release '${tag}' already exists`);
87+
} catch (e) {
88+
if (e.status !== 404) throw e;
89+
console.log(`✅ GitHub Release '${tag}' is available`);
90+
}
91+
92+
// 3. Check GitHub Packages (NuGet)
93+
try {
94+
const packages = await github.rest.packages.getAllPackageVersionsForPackageOwnedByOrg({
95+
package_type: 'nuget',
96+
package_name: repo,
97+
org: owner
98+
});
99+
const exists = packages.data.some(p => p.name === version);
100+
if (exists) {
101+
errors.push(`NuGet package version '${version}' already exists on GitHub Packages`);
102+
} else {
103+
console.log(`✅ NuGet package version '${version}' is available`);
104+
}
105+
} catch (e) {
106+
if (e.status === 404) {
107+
console.log(`✅ No package found yet (first publish)`);
108+
} else {
109+
console.warn(`⚠️ Could not check packages: ${e.message}`);
110+
}
111+
}
112+
113+
if (errors.length > 0) {
114+
const msg = `❌ Version guard failed:\n${errors.map(e => ` - ${e}`).join('\n')}`;
115+
core.setFailed(msg);
116+
} else {
117+
console.log('\n✅ All version checks passed');
118+
}
119+
120+
# ── Update Directory.Build.props ─────────────────
121+
- name: Read current version
122+
id: current
123+
run: |
124+
CURRENT=$(grep -oP '(?<=<Version>)[^<]+' Directory.Build.props)
125+
echo "version=${CURRENT}" >> $GITHUB_OUTPUT
126+
echo "📋 Current version in Directory.Build.props: ${CURRENT}"
127+
128+
- name: Update Directory.Build.props
129+
run: |
130+
VERSION="${{ steps.params.outputs.version }}"
131+
CURRENT="${{ steps.current.outputs.version }}"
132+
133+
if [ "${VERSION}" = "${CURRENT}" ]; then
134+
echo "ℹ️ Version already set to ${VERSION}, no change needed"
135+
else
136+
sed -i "s|<Version>${CURRENT}</Version>|<Version>${VERSION}</Version>|" Directory.Build.props
137+
echo "✅ Updated Directory.Build.props: ${CURRENT} → ${VERSION}"
138+
fi
139+
140+
# Verify
141+
grep '<Version>' Directory.Build.props
142+
143+
- name: Commit version bump
144+
if: steps.params.outputs.dry_run != 'true'
145+
run: |
146+
VERSION="${{ steps.params.outputs.version }}"
147+
git config user.name "github-actions[bot]"
148+
git config user.email "github-actions[bot]@users.noreply.github.com"
149+
150+
git add Directory.Build.props
151+
if git diff --cached --quiet; then
152+
echo "ℹ️ No changes to commit (version was already correct)"
153+
else
154+
git commit -m "release: v${VERSION}"
155+
git push
156+
echo "✅ Version bump committed and pushed"
157+
fi
158+
159+
# ── Build ────────────────────────────────────────
160+
- name: Setup .NET
161+
uses: actions/setup-dotnet@v4
162+
with:
163+
dotnet-version: ${{ env.DOTNET_VERSION }}
164+
165+
- name: Configure GitHub Packages source
166+
run: |
167+
dotnet nuget add source \
168+
"https://nuget.pkg.github.com/Payroll-Engine/index.json" \
169+
--name github \
170+
--username github-actions \
171+
--password ${{ secrets.GITHUB_TOKEN }} \
172+
--store-password-in-clear-text
173+
174+
- name: Restore
175+
run: dotnet restore
176+
177+
- name: Build
178+
run: dotnet build --configuration Release --no-restore
179+
180+
- name: Test
181+
run: dotnet test --configuration Release --no-build --verbosity normal
182+
183+
- name: Pack
184+
run: dotnet pack --configuration Release --no-build --output ./nupkgs
185+
186+
# ── Publish ──────────────────────────────────────
187+
- name: Publish to GitHub Packages
188+
run: |
189+
dotnet nuget push ./nupkgs/*.nupkg \
190+
--source "https://nuget.pkg.github.com/Payroll-Engine/index.json" \
191+
--api-key ${{ secrets.GITHUB_TOKEN }} \
192+
--skip-duplicate
193+
194+
- name: Create GitHub Release
195+
uses: softprops/action-gh-release@v2
196+
with:
197+
tag_name: v${{ steps.params.outputs.version }}
198+
name: v${{ steps.params.outputs.version }}
199+
prerelease: ${{ steps.params.outputs.is_prerelease }}
200+
draft: ${{ steps.params.outputs.make_draft }}
201+
generate_release_notes: true
202+
files: ./nupkgs/*.nupkg
203+
204+
- name: Notify orchestrator
205+
if: github.event_name == 'repository_dispatch'
206+
uses: peter-evans/repository-dispatch@v3
207+
with:
208+
token: ${{ secrets.PAT_DISPATCH }}
209+
repository: Payroll-Engine/PayrollEngine
210+
event-type: lib-published
211+
client-payload: >-
212+
{
213+
"repo": "${{ github.repository }}",
214+
"version": "${{ steps.params.outputs.version }}",
215+
"wave": "${{ github.event.client_payload.wave }}"
216+
}

0 commit comments

Comments
 (0)