Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
296 changes: 296 additions & 0 deletions .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
name: Build and Deploy to NuGet

on:
push:
branches: [ main, dev ]
paths-ignore:
- '.github/**'
- 'src/CloudNimble.Breakdance.Docs/**'
- 'specs/**'
workflow_dispatch:
inputs:
deploy_to_nuget:
description: 'Deploy to NuGet'
required: false
default: 'false'
type: choice
options:
- 'true'
- 'false'

permissions:
contents: write
actions: write
id-token: write

env:
DOTNET_VERSION: '10.0.x'
SOLUTION_FILE: 'src/CloudNimble.Breakdance.slnx'

jobs:
build:
runs-on: windows-latest
outputs:
version: ${{ steps.version.outputs.VERSION }}

steps:
- name: Enable long paths
run: git config --system core.longpaths true

- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0 # Full history for versioning

- name: Install .NET versions
shell: pwsh
run: |
Write-Host "Installing .NET versions..."
$versions = @("8.0", "9.0", "10.0")
foreach ($version in $versions) {
Write-Host "Installing .NET $version..."
Invoke-WebRequest -Uri "https://dot.net/v1/dotnet-install.ps1" -OutFile "dotnet-install.ps1"
./dotnet-install.ps1 -Channel $version -InstallDir "$env:ProgramFiles\dotnet"
}

# Add to PATH for this job
echo "$env:ProgramFiles\dotnet" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

# Verify installation
dotnet --list-sdks

- name: Get version variables
id: version
shell: pwsh
run: |
# Get version components from repository variables
$majorVersion = "${{ vars.VERSION_MAJOR }}"
if ([string]::IsNullOrEmpty($majorVersion)) { $majorVersion = "1" }

$minorVersion = "${{ vars.VERSION_MINOR }}"
if ([string]::IsNullOrEmpty($minorVersion)) { $minorVersion = "0" }

$patchVersion = "${{ vars.VERSION_PATCH }}"
if ([string]::IsNullOrEmpty($patchVersion)) { $patchVersion = "0" }

$previewSuffix = "${{ vars.VERSION_PREVIEW_SUFFIX }}"
if ([string]::IsNullOrEmpty($previewSuffix)) { $previewSuffix = "0" }

Write-Host "Version variables: MAJOR=$majorVersion, MINOR=$minorVersion, PATCH=$patchVersion, PREVIEW_SUFFIX=$previewSuffix"

# Determine version based on branch
$ref = "${{ github.ref }}"
if ($ref -eq "refs/heads/main") {
# Main branch: use patch version from repo variables
Write-Host "Main branch: using PATCH_VERSION from repo variables"

$version = "$majorVersion.$minorVersion.$patchVersion"
$buildNumber = $patchVersion

# Calculate next patch version for update after successful deployment
$nextPatchVersion = [int]$patchVersion + 1
echo "NEXT_PATCH_VERSION=$nextPatchVersion" >> $env:GITHUB_OUTPUT

Write-Host "Main branch version: $version (next patch will be $nextPatchVersion)"
}
elseif ($ref -eq "refs/heads/dev") {
# Dev branch: use preview versioning with incremented suffix
$nextPreviewSuffix = [int]$previewSuffix + 1
$version = "$majorVersion.$minorVersion.$patchVersion-preview.$nextPreviewSuffix"
$buildNumber = 0

# Store the next preview suffix for later update
echo "NEXT_PREVIEW_SUFFIX=$nextPreviewSuffix" >> $env:GITHUB_OUTPUT
Write-Host "Dev branch version: $version (next suffix will be $nextPreviewSuffix)"
}
else {
# Other branches (features/PRs): use CI versioning with timestamp
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss" -AsUTC
$version = "$majorVersion.$minorVersion.$patchVersion-CI-$timestamp"
$buildNumber = 0
Write-Host "Feature branch version: $version"
}

# Output variables
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
echo "MAJOR_VERSION=$majorVersion" >> $env:GITHUB_OUTPUT
echo "MINOR_VERSION=$minorVersion" >> $env:GITHUB_OUTPUT
echo "PATCH_VERSION=$patchVersion" >> $env:GITHUB_OUTPUT
echo "BUILD_NUMBER=$buildNumber" >> $env:GITHUB_OUTPUT
echo "BRANCH_TYPE=$(if ($ref -eq 'refs/heads/main') { 'main' } elseif ($ref -eq 'refs/heads/dev') { 'dev' } else { 'feature' })" >> $env:GITHUB_OUTPUT

Write-Host "Final version: $version"

- name: Restore dependencies
run: dotnet restore ${{ env.SOLUTION_FILE }}

- name: Build solution
run: dotnet build ${{ env.SOLUTION_FILE }} --configuration Release --no-restore /p:Version=${{ steps.version.outputs.VERSION }} /p:PackageVersion=${{ steps.version.outputs.VERSION }}

- name: Test
working-directory: src
run: dotnet test --configuration Release --no-build

- name: Pack
run: dotnet pack ${{ env.SOLUTION_FILE }} --configuration Release --no-build --output ./artifacts /p:PackageVersion=${{ steps.version.outputs.VERSION }}

- name: Upload artifacts
uses: actions/upload-artifact@v7
with:
name: nuget-packages
path: ./artifacts/*.nupkg
retention-days: 7

deploy:
needs: build
runs-on: windows-latest
environment: production
if: |
(github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_nuget == 'true')

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: nuget-packages
path: ./artifacts

- name: Get version data from build
id: version
shell: pwsh
run: |
# Re-calculate version data for this job (since we can't pass complex data between jobs)
$majorVersion = "${{ vars.VERSION_MAJOR }}"
if ([string]::IsNullOrEmpty($majorVersion)) { $majorVersion = "1" }

$minorVersion = "${{ vars.VERSION_MINOR }}"
if ([string]::IsNullOrEmpty($minorVersion)) { $minorVersion = "0" }

$patchVersion = "${{ vars.VERSION_PATCH }}"
if ([string]::IsNullOrEmpty($patchVersion)) { $patchVersion = "0" }

$previewSuffix = "${{ vars.VERSION_PREVIEW_SUFFIX }}"
if ([string]::IsNullOrEmpty($previewSuffix)) { $previewSuffix = "0" }

$ref = "${{ github.ref }}"
if ($ref -eq "refs/heads/main") {
$nextPatchVersion = [int]$patchVersion + 1
echo "NEXT_PATCH_VERSION=$nextPatchVersion" >> $env:GITHUB_OUTPUT
echo "BRANCH_TYPE=main" >> $env:GITHUB_OUTPUT
}
elseif ($ref -eq "refs/heads/dev") {
$nextPreviewSuffix = [int]$previewSuffix + 1
echo "NEXT_PREVIEW_SUFFIX=$nextPreviewSuffix" >> $env:GITHUB_OUTPUT
echo "BRANCH_TYPE=dev" >> $env:GITHUB_OUTPUT
}
else {
echo "BRANCH_TYPE=other" >> $env:GITHUB_OUTPUT
}

- name: NuGet login (Trusted Publishing)
uses: nuget/login@v1
id: nuget_login
with:
user: ${{ secrets.NUGET_USER }}

- name: Push to NuGet
id: nuget_push
shell: bash
run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ steps.nuget_login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate

- name: Update preview suffix (dev branch only)
if: steps.version.outputs.BRANCH_TYPE == 'dev'
shell: pwsh
env:
GH_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
run: |
$nextSuffix = "${{ steps.version.outputs.NEXT_PREVIEW_SUFFIX }}"
Write-Host "Updating VERSION_PREVIEW_SUFFIX to $nextSuffix"

try {
gh variable set VERSION_PREVIEW_SUFFIX --body "$nextSuffix"
Write-Host "Successfully updated VERSION_PREVIEW_SUFFIX to $nextSuffix"
}
catch {
Write-Host "WARNING: Failed to update VERSION_PREVIEW_SUFFIX: $($_.Exception.Message)"
# Don't fail the build for this
}

- name: Update patch version (main branch only)
if: steps.version.outputs.BRANCH_TYPE == 'main'
shell: pwsh
env:
GH_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
run: |
$nextPatch = "${{ steps.version.outputs.NEXT_PATCH_VERSION }}"
Write-Host "Updating VERSION_PATCH to $nextPatch and resetting VERSION_PREVIEW_SUFFIX to 0"

try {
gh variable set VERSION_PATCH --body "$nextPatch"
Write-Host "Successfully updated VERSION_PATCH to $nextPatch"

gh variable set VERSION_PREVIEW_SUFFIX --body "0"
Write-Host "Successfully reset VERSION_PREVIEW_SUFFIX to 0"
}
catch {
Write-Host "WARNING: Failed to update version variables: $($_.Exception.Message)"
# Don't fail the build for this
}

create-release:
needs: [build, deploy]
runs-on: windows-latest
permissions:
contents: write
if: |
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' && github.event.inputs.deploy_to_nuget == 'true')

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: nuget-packages
path: ./artifacts

- name: Prepare Release Notes
shell: pwsh
run: |
$version = "${{ needs.build.outputs.version }}"
Write-Host "Creating release for version: $version"

$packages = Get-ChildItem ./artifacts/*.nupkg | ForEach-Object { $_.Name -replace '\.nupkg$', '' }
$packageList = ""
foreach ($package in $packages) {
$packageName = $package -replace "\.$version$", ""
$packageList += "- [$packageName](https://www.nuget.org/packages/$packageName/$version)`n"
}

$lines = @(
"## NuGet Packages",
$packageList,
"",
"## Installation",
'```',
"dotnet add package Breakdance.Assemblies --version $version",
'```'
)
$body = $lines -join "`n"
Set-Content -Path "./release-notes.md" -Value $body -Encoding utf8

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.build.outputs.version }}
name: Release v${{ needs.build.outputs.version }}
body_path: ./release-notes.md
generate_release_notes: true
draft: false
prerelease: false
Loading
Loading