Files
git.stella-ops.org/.gitea/workflows/module-publish.yml
2025-12-26 18:11:06 +02:00

406 lines
14 KiB
YAML

# .gitea/workflows/module-publish.yml
# Per-module NuGet and container publishing to Gitea registry
# Sprint: SPRINT_20251226_004_CICD
name: Module Publish
on:
workflow_dispatch:
inputs:
module:
description: 'Module to publish'
required: true
type: choice
options:
- Authority
- Attestor
- Concelier
- Scanner
- Policy
- Signer
- Excititor
- Gateway
- Scheduler
- Orchestrator
- TaskRunner
- Notify
- CLI
version:
description: 'Semantic version (e.g., 1.2.3)'
required: true
type: string
publish_nuget:
description: 'Publish NuGet packages'
type: boolean
default: true
publish_container:
description: 'Publish container image'
type: boolean
default: true
prerelease:
description: 'Mark as prerelease'
type: boolean
default: false
push:
tags:
- 'module-*-v*' # e.g., module-authority-v1.2.3
env:
DOTNET_VERSION: '10.0.100'
DOTNET_NOLOGO: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
REGISTRY: git.stella-ops.org
NUGET_SOURCE: https://git.stella-ops.org/api/packages/stella-ops.org/nuget/index.json
jobs:
# ===========================================================================
# PARSE TAG (for tag-triggered builds)
# ===========================================================================
parse-tag:
name: Parse Tag
runs-on: ubuntu-22.04
if: github.event_name == 'push'
outputs:
module: ${{ steps.parse.outputs.module }}
version: ${{ steps.parse.outputs.version }}
steps:
- name: Parse module and version from tag
id: parse
run: |
TAG="${{ github.ref_name }}"
# Expected format: module-{name}-v{version}
# Example: module-authority-v1.2.3
if [[ "$TAG" =~ ^module-([a-zA-Z]+)-v([0-9]+\.[0-9]+\.[0-9]+.*)$ ]]; then
MODULE="${BASH_REMATCH[1]}"
VERSION="${BASH_REMATCH[2]}"
# Capitalize first letter
MODULE="$(echo "${MODULE:0:1}" | tr '[:lower:]' '[:upper:]')${MODULE:1}"
echo "module=$MODULE" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Parsed: module=$MODULE, version=$VERSION"
else
echo "::error::Invalid tag format. Expected: module-{name}-v{version}"
exit 1
fi
# ===========================================================================
# VALIDATE
# ===========================================================================
validate:
name: Validate Inputs
runs-on: ubuntu-22.04
needs: [parse-tag]
if: always() && (needs.parse-tag.result == 'success' || needs.parse-tag.result == 'skipped')
outputs:
module: ${{ steps.resolve.outputs.module }}
version: ${{ steps.resolve.outputs.version }}
publish_nuget: ${{ steps.resolve.outputs.publish_nuget }}
publish_container: ${{ steps.resolve.outputs.publish_container }}
steps:
- name: Resolve inputs
id: resolve
run: |
if [[ "${{ github.event_name }}" == "push" ]]; then
MODULE="${{ needs.parse-tag.outputs.module }}"
VERSION="${{ needs.parse-tag.outputs.version }}"
PUBLISH_NUGET="true"
PUBLISH_CONTAINER="true"
else
MODULE="${{ github.event.inputs.module }}"
VERSION="${{ github.event.inputs.version }}"
PUBLISH_NUGET="${{ github.event.inputs.publish_nuget }}"
PUBLISH_CONTAINER="${{ github.event.inputs.publish_container }}"
fi
echo "module=$MODULE" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "publish_nuget=$PUBLISH_NUGET" >> "$GITHUB_OUTPUT"
echo "publish_container=$PUBLISH_CONTAINER" >> "$GITHUB_OUTPUT"
echo "=== Resolved Configuration ==="
echo "Module: $MODULE"
echo "Version: $VERSION"
echo "Publish NuGet: $PUBLISH_NUGET"
echo "Publish Container: $PUBLISH_CONTAINER"
- name: Validate version format
run: |
VERSION="${{ steps.resolve.outputs.version }}"
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
echo "::error::Invalid version format. Expected: MAJOR.MINOR.PATCH[-prerelease]"
exit 1
fi
# ===========================================================================
# PUBLISH NUGET
# ===========================================================================
publish-nuget:
name: Publish NuGet
runs-on: ubuntu-22.04
needs: [validate]
if: needs.validate.outputs.publish_nuget == 'true'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Determine project path
id: path
run: |
MODULE="${{ needs.validate.outputs.module }}"
# Map module names to project paths
case "$MODULE" in
Authority)
PROJECT="src/Authority/StellaOps.Authority.WebService/StellaOps.Authority.WebService.csproj"
;;
Attestor)
PROJECT="src/Attestor/StellaOps.Attestor.WebService/StellaOps.Attestor.WebService.csproj"
;;
Concelier)
PROJECT="src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj"
;;
Scanner)
PROJECT="src/Scanner/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj"
;;
Policy)
PROJECT="src/Policy/StellaOps.Policy.Gateway/StellaOps.Policy.Gateway.csproj"
;;
Signer)
PROJECT="src/Signer/StellaOps.Signer.WebService/StellaOps.Signer.WebService.csproj"
;;
Excititor)
PROJECT="src/Excititor/StellaOps.Excititor.WebService/StellaOps.Excititor.WebService.csproj"
;;
Gateway)
PROJECT="src/Gateway/StellaOps.Gateway.WebService/StellaOps.Gateway.WebService.csproj"
;;
Scheduler)
PROJECT="src/Scheduler/StellaOps.Scheduler.WebService/StellaOps.Scheduler.WebService.csproj"
;;
Orchestrator)
PROJECT="src/Orchestrator/StellaOps.Orchestrator.WebService/StellaOps.Orchestrator.WebService.csproj"
;;
TaskRunner)
PROJECT="src/TaskRunner/StellaOps.TaskRunner.WebService/StellaOps.TaskRunner.WebService.csproj"
;;
Notify)
PROJECT="src/Notify/StellaOps.Notify.WebService/StellaOps.Notify.WebService.csproj"
;;
CLI)
PROJECT="src/Cli/StellaOps.Cli/StellaOps.Cli.csproj"
;;
*)
echo "::error::Unknown module: $MODULE"
exit 1
;;
esac
echo "project=$PROJECT" >> "$GITHUB_OUTPUT"
echo "Project path: $PROJECT"
- name: Restore dependencies
run: dotnet restore ${{ steps.path.outputs.project }}
- name: Build
run: |
dotnet build ${{ steps.path.outputs.project }} \
--configuration Release \
--no-restore \
-p:Version=${{ needs.validate.outputs.version }}
- name: Pack NuGet
run: |
dotnet pack ${{ steps.path.outputs.project }} \
--configuration Release \
--no-build \
-p:Version=${{ needs.validate.outputs.version }} \
-p:PackageVersion=${{ needs.validate.outputs.version }} \
--output out/packages
- name: Push to Gitea NuGet registry
run: |
for nupkg in out/packages/*.nupkg; do
echo "Pushing: $nupkg"
dotnet nuget push "$nupkg" \
--source "${{ env.NUGET_SOURCE }}" \
--api-key "${{ secrets.GITEA_TOKEN }}" \
--skip-duplicate
done
- name: Upload NuGet artifacts
uses: actions/upload-artifact@v4
with:
name: nuget-${{ needs.validate.outputs.module }}-${{ needs.validate.outputs.version }}
path: out/packages/*.nupkg
retention-days: 30
# ===========================================================================
# PUBLISH CONTAINER
# ===========================================================================
publish-container:
name: Publish Container
runs-on: ubuntu-22.04
needs: [validate]
if: needs.validate.outputs.publish_container == 'true' && needs.validate.outputs.module != 'CLI'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Gitea Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITEA_TOKEN }}
- name: Determine image name
id: image
run: |
MODULE="${{ needs.validate.outputs.module }}"
VERSION="${{ needs.validate.outputs.version }}"
MODULE_LOWER=$(echo "$MODULE" | tr '[:upper:]' '[:lower:]')
IMAGE="${{ env.REGISTRY }}/stella-ops.org/${MODULE_LOWER}"
echo "name=$IMAGE" >> "$GITHUB_OUTPUT"
echo "tag_version=${IMAGE}:${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag_latest=${IMAGE}:latest" >> "$GITHUB_OUTPUT"
echo "Image: $IMAGE"
echo "Tags: ${VERSION}, latest"
- name: Build and push container
uses: docker/build-push-action@v5
with:
context: .
file: devops/docker/Dockerfile.platform
target: ${{ needs.validate.outputs.module | lower }}
push: true
tags: |
${{ steps.image.outputs.tag_version }}
${{ steps.image.outputs.tag_latest }}
cache-from: type=gha
cache-to: type=gha,mode=max
labels: |
org.opencontainers.image.title=StellaOps ${{ needs.validate.outputs.module }}
org.opencontainers.image.version=${{ needs.validate.outputs.version }}
org.opencontainers.image.source=https://git.stella-ops.org/stella-ops.org/git.stella-ops.org
org.opencontainers.image.revision=${{ github.sha }}
# ===========================================================================
# PUBLISH CLI BINARIES (multi-platform)
# ===========================================================================
publish-cli:
name: Publish CLI (${{ matrix.runtime }})
runs-on: ubuntu-22.04
needs: [validate]
if: needs.validate.outputs.module == 'CLI'
strategy:
matrix:
runtime:
- linux-x64
- linux-arm64
- win-x64
- osx-x64
- osx-arm64
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Install cross-compilation tools
if: matrix.runtime == 'linux-arm64'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends binutils-aarch64-linux-gnu
- name: Publish CLI
run: |
dotnet publish src/Cli/StellaOps.Cli/StellaOps.Cli.csproj \
--configuration Release \
--runtime ${{ matrix.runtime }} \
--self-contained true \
-p:Version=${{ needs.validate.outputs.version }} \
-p:PublishSingleFile=true \
-p:PublishTrimmed=true \
-p:EnableCompressionInSingleFile=true \
--output out/cli/${{ matrix.runtime }}
- name: Create archive
run: |
VERSION="${{ needs.validate.outputs.version }}"
RUNTIME="${{ matrix.runtime }}"
cd out/cli/$RUNTIME
if [[ "$RUNTIME" == win-* ]]; then
zip -r ../stellaops-cli-${VERSION}-${RUNTIME}.zip .
else
tar -czvf ../stellaops-cli-${VERSION}-${RUNTIME}.tar.gz .
fi
- name: Upload CLI artifacts
uses: actions/upload-artifact@v4
with:
name: cli-${{ needs.validate.outputs.version }}-${{ matrix.runtime }}
path: |
out/cli/*.zip
out/cli/*.tar.gz
retention-days: 30
# ===========================================================================
# SUMMARY
# ===========================================================================
summary:
name: Publish Summary
runs-on: ubuntu-22.04
needs: [validate, publish-nuget, publish-container, publish-cli]
if: always()
steps:
- name: Generate Summary
run: |
echo "## Module Publish Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Module | ${{ needs.validate.outputs.module }} |" >> $GITHUB_STEP_SUMMARY
echo "| Version | ${{ needs.validate.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| NuGet | ${{ needs.publish-nuget.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Container | ${{ needs.publish-container.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| CLI | ${{ needs.publish-cli.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Registry URLs" >> $GITHUB_STEP_SUMMARY
echo "- NuGet: \`${{ env.NUGET_SOURCE }}\`" >> $GITHUB_STEP_SUMMARY
echo "- Container: \`${{ env.REGISTRY }}/stella-ops.org/${{ needs.validate.outputs.module | lower }}\`" >> $GITHUB_STEP_SUMMARY
- name: Check for failures
if: contains(needs.*.result, 'failure')
run: |
echo "::error::One or more publish jobs failed"
exit 1