chore(ci): add versioning
Some checks failed
CI / build (push) Failing after 2m34s

This commit is contained in:
Louis Seubert 2026-10-25 21:19:42 +01:00
commit 75f6a9f756
Signed by: louis9902
SSH key fingerprint: SHA256:HkW1JCsONisFW0uQchtmqod/1QUpxsEIxbg7aIquMX0
3 changed files with 333 additions and 60 deletions

View file

@ -0,0 +1,176 @@
name: Calculate version
description: Validate Conventional Commits and calculate the build version
inputs:
commit-types:
description: Comma-separated Conventional Commit types
required: false
default: feat,fix,docs,style,refactor,perf,test,build,ci,chore,revert
outputs:
version:
description: Version for dotnet build and pack
value: ${{ steps.version.outputs.version }}
base-version:
description: Version before the current changes
value: ${{ steps.version.outputs.base-version }}
label:
description: Build label, such as next or pr.42
value: ${{ steps.version.outputs.label }}
metadata:
description: Build metadata, such as sha.abc123
value: ${{ steps.version.outputs.metadata }}
bump:
description: Highest version increment found
value: ${{ steps.version.outputs.bump }}
runs:
using: composite
steps:
- id: version
shell: bash
env:
VERSION_EVENT_NAME: ${{ github.event_name }}
VERSION_REF_NAME: ${{ github.ref_name }}
VERSION_BASE_REF: ${{ github.base_ref }}
VERSION_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
VERSION_PR_NUMBER: ${{ github.event.pull_request.number }}
VERSION_COMMIT_TYPES: ${{ inputs.commit-types }}
run: |
set -euo pipefail
error() {
echo "::error::$*" >&2
exit 1
}
command -v git >/dev/null || error "git is required"
event_name="${VERSION_EVENT_NAME:-${GITHUB_EVENT_NAME:-}}"
ref_name="${VERSION_REF_NAME:-${GITHUB_REF_NAME:-}}"
base_ref="${VERSION_BASE_REF:-${GITHUB_BASE_REF:-}}"
head_sha="${VERSION_HEAD_SHA:-${GITHUB_SHA:-HEAD}}"
pr_number="${VERSION_PR_NUMBER:-${PR_NUMBER:-}}"
commit_types="${VERSION_COMMIT_TYPES:-feat,fix,docs,style,refactor,perf,test,build,ci,chore,revert}"
type_pattern="${commit_types//,/|}"
output_file="${GITHUB_OUTPUT:-}"
write_output() {
[[ -n "$output_file" ]] && printf '%s=%s\n' "$1" "$2" >>"$output_file"
}
valid_version() {
[[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
}
tag_version() {
valid_version "$1" || error "Tag '$1' is not a valid release version (expected MAJOR.MINOR.PATCH)"
printf '%s' "$1"
}
latest_release_tag() {
git tag --list '[0-9]*' --sort=-version:refname | head -n 1
}
version_parts() {
IFS=. read -r major minor patch <<<"$1"
printf '%s %s %s' "$major" "$minor" "$patch"
}
bump_version() {
local version=$1 bump=$2 major minor patch
read -r major minor patch <<<"$(version_parts "$version")"
case "$bump" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
patch) patch=$((patch + 1)) ;;
none) ;;
*) error "Unknown version bump '$bump'" ;;
esac
printf '%s.%s.%s' "$major" "$minor" "$patch"
}
commit_bump() {
local commit=$1 validate=${2:-1} subject body bump=none type
subject=$(git log -1 --format=%s "$commit")
body=$(git log -1 --format=%b "$commit")
if ! [[ "$subject" =~ ^($type_pattern)(\([^()]+\))?(!)?:[[:space:]]+[^[:space:]].*$ ]]; then
[[ "$validate" == 0 ]] && { printf none; return; }
error "Commit $commit has an invalid Conventional Commit subject: $subject"
fi
type=${BASH_REMATCH[1]}
if [[ "$subject" =~ ^[^:]+\!: ]] || [[ "$body" == *"BREAKING CHANGE:"* ]] || [[ "$body" == *"BREAKING-CHANGE:"* ]]; then
bump=major
elif [[ "$type" == feat ]]; then
bump=minor
elif [[ "$type" == fix ]]; then
bump='patch'
fi
printf '%s' "$bump"
}
higher_bump() {
case "$1:$2" in
major:*) printf major ;; *:major) printf major ;;
minor:minor|minor:patch|patch:minor) printf minor ;;
patch:patch) printf patch ;;
none:*) printf '%s' "$2" ;;
*:none) printf '%s' "$1" ;;
*) error "Cannot compare bumps '$1' and '$2'" ;;
esac
}
commits_between() {
git rev-list --reverse --no-merges "$1..$2"
}
calculate_bump() {
local commits=$1 validate=${2:-1} commit bump=none current
while IFS= read -r commit; do
[[ -n "$commit" ]] || continue
current=$(commit_bump "$commit" "$validate")
bump=$(higher_bump "$bump" "$current")
done <<<"$commits"
printf '%s' "$bump"
}
tag=$(latest_release_tag || true)
if [[ -n "$tag" ]]; then
base_version=$(tag_version "$tag")
tag_start=$tag
else
base_version=0.0.0
tag_start=$(git rev-list --max-parents=0 HEAD | tail -n 1)
fi
if [[ "$event_name" == pull_request || -n "$base_ref" ]]; then
[[ -n "$pr_number" ]] || error "Pull request number is required"
[[ -n "$base_ref" ]] || error "Pull request base ref is required"
base_commit=$(git rev-parse "$base_ref" 2>/dev/null || git rev-parse "origin/$base_ref")
head_commit=$(git rev-parse "$head_sha")
pr_commits=$(commits_between "$base_commit" "$head_commit")
pr_bump=$(calculate_bump "$pr_commits")
base_bump=$(calculate_bump "$(commits_between "$tag_start" "$base_commit")" 0)
bump=$(higher_bump "$base_bump" "$pr_bump")
version=$(bump_version "$base_version" "$bump")-pr."$pr_number".sha."$head_commit"
label="pr.$pr_number"
metadata="sha.$head_commit"
elif [[ "$event_name" == push && "$ref_name" == [0-9]* || "$ref_name" == [0-9]* ]]; then
version=$(tag_version "$ref_name")
bump=none
label=release
metadata=none
else
head_commit=$(git rev-parse "$head_sha")
commits=$(commits_between "$tag_start" "$head_commit")
bump=$(calculate_bump "$commits")
version=$(bump_version "$base_version" "$bump")-next.sha."$head_commit"
label=next
metadata="sha.$head_commit"
fi
write_output version "$version"
write_output base-version "$base_version"
write_output label "$label"
write_output metadata "$metadata"
write_output bump "$bump"
printf 'Calculated version: %s\n' "$version"

View file

@ -3,12 +3,9 @@ name: CI
on:
push:
branches: [main]
tags: ['*']
pull_request:
branches: [main]
enable-openid-connect: true
jobs:
build:
name: build
@ -18,65 +15,15 @@ jobs:
- name: checkout
uses: https://code.geekeey.de/actions/checkout@54953ce9d6c7d0a9cbd89d185c5b2c7515358c5f # 1
- name: version
id: version
uses: ./.forgejo/actions/version
- name: dotnet format check
run: dotnet format arcade.slnx --verify-no-changes --verbosity minimal
- name: dotnet pack
shell: bash
env:
TAG: ${{ forgejo.ref }}
TAG_NAME: ${{ forgejo.ref_name }}
run: |
set -euo pipefail
if [[ "$TAG" == refs/tags/* ]]; then
version="${TAG_NAME#v}"
if ! printf '%s' "$version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "::error::Tag '$TAG_NAME' is not a valid release version (expected MAJOR.MINOR.PATCH)"
exit 1
fi
export version
fi
dotnet pack arcade.slnx
- name: upload packages
uses: https://code.geekeey.de/actions/artifacts/push@ae8d20e174a8706675e6d54ebd3719a2802ea9dd # 1
with:
name: nuget-packages
path: artifacts/package/release
pattern: |
*.nupkg
*.snupkg
- name: dotnet build
run: dotnet build arcade.slnx -p:Version=${{ steps.version.outputs.version }}
- name: dotnet test
run: dotnet test src/sdk.tests/Geekeey.Arcade.NET.Tests.csproj
- name: publish to nuget
if: startsWith(forgejo.ref, 'refs/tags/')
shell: bash
env:
AUDIENCE: "u:1:00000000-0000-0000-0000-000000000000"
run: |
set -euo pipefail
if [[ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" || -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ]]; then
echo "::error::OpenID Connect is not enabled. Set enable-openid-connect: true on the workflow or job."
exit 1
fi
response=$(curl --fail --silent --show-error \
--header "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${AUDIENCE}")
token=$(printf '%s' "${response}" | grep --only-matching '"value":"[^"]*"' | cut --delimiter '"' --fields 4)
if [[ -z "${token}" ]]; then
echo "::error::Failed to obtain an OpenID Connect token from Forgejo."
exit 1
fi
echo "::add-mask::${token}"
feed="${{ forgejo.server_url }}/api/packages/${{ forgejo.repository_owner }}/nuget/index.json"
dotnet nuget add source --name forgejo --username forgejo-publish --password "$token" --store-password-in-clear-text "$feed"
dotnet nuget push "artifacts/package/release/*.nupkg" "artifacts/package/release/*.snupkg" --source forgejo --skip-duplicate
run: dotnet test arcade.slnx -p:Version=${{ steps.version.outputs.version }}

View file

@ -0,0 +1,60 @@
name: release
on:
push:
tags: ['*']
enable-openid-connect: true
jobs:
publish:
name: publish
runs-on: debian-latest
container: mcr.microsoft.com/dotnet/sdk:10.0@sha256:0d99ebf1ccad15037b5c98c9fe8979d6b8dbac23812feae2277a0b277b5c5ffa
steps:
- name: checkout
uses: https://code.geekeey.de/actions/checkout@54953ce9d6c7d0a9cbd89d185c5b2c7515358c5f # 1
- name: version
id: version
uses: ./.forgejo/actions/version
- name: dotnet pack
run: dotnet pack arcade.slnx -p:Version=${{ steps.version.outputs.version }}
- name: upload packages
uses: https://code.geekeey.de/actions/artifacts/push@ae8d20e174a8706675e6d54ebd3719a2802ea9dd # 1
with:
name: nuget-packages
path: artifacts/package/release
pattern: |
*.nupkg
*.snupkg
- name: publish to nuget
shell: bash
env:
AUDIENCE: "u:1:49f9431c-8baf-4432-8254-72fd1b00b8f9"
run: |
set -euo pipefail
if [[ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" || -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ]]; then
echo "::error::OpenID Connect is not enabled. Set enable-openid-connect: true on the workflow or job."
exit 1
fi
response=$(curl --fail --silent --show-error \
--header "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${AUDIENCE}")
token=$(printf '%s' "${response}" | grep --only-matching '"value":"[^"]*"' | cut --delimiter '"' --fields 4)
if [[ -z "${token}" ]]; then
echo "::error::Failed to obtain an OpenID Connect token from Forgejo."
exit 1
fi
echo "::add-mask::${token}"
feed="${{ forgejo.server_url }}/api/packages/${{ forgejo.repository_owner }}/nuget/index.json"
dotnet nuget add source --name forgejo --username forgejo-publish --password "$token" --store-password-in-clear-text "$feed"
dotnet nuget push "artifacts/package/release/*.nupkg" "artifacts/package/release/*.snupkg" --source forgejo --skip-duplicate