Skip to content
Merged
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
60 changes: 60 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: CI/CD Pipeline

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
release:
types: [ published ]

jobs:
build-and-test:
runs-on: ubuntu-latest
name: Build and Test

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

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'

- name: Run build and test
shell: pwsh
run: ./CI-CD/ci-cd.ps1 -Phase build

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: nuget-packages
path: ./artifacts/*.nupkg

publish-nuget:
runs-on: ubuntu-latest
name: Publish to NuGet
needs: build-and-test
if: github.event_name == 'release' && github.event.action == 'published'

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

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'

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

- name: Publish to NuGet
shell: pwsh
run: ./CI-CD/ci-cd.ps1 -Phase publish -PublishToNuGet $true
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
129 changes: 129 additions & 0 deletions CI-CD/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# CI/CD Pipeline for TimeWarp.Fixie

This folder contains the CI/CD pipeline scripts for the TimeWarp.Fixie project. The pipeline is designed to be testable locally using PowerShell scripts, with minimal GitHub Actions orchestration.

## Scripts

### `ci-cd.ps1` - Main Orchestration Script
The main entry point for the CI/CD pipeline. Can run individual phases or the complete pipeline.

**Usage:**
```powershell
# Run complete pipeline (build + test only)
./CI-CD/ci-cd.ps1

# Run only build and test
./CI-CD/ci-cd.ps1 -Phase build

# Run complete pipeline with NuGet publishing
./CI-CD/ci-cd.ps1 -PublishToNuGet $true

# Run only publish phase
./CI-CD/ci-cd.ps1 -Phase publish -PublishToNuGet $true
```

**Parameters:**
- `Phase`: 'build', 'publish', or 'all' (default: 'all')
- `Configuration`: 'Debug' or 'Release' (default: 'Release')
- `PublishToNuGet`: Whether to publish to NuGet (default: false)
- `NuGetApiKey`: NuGet API key (uses NUGET_API_KEY env var if not provided)

### `build-and-test.ps1` - Build and Test Phase
Handles the build and test phase of the pipeline.

**What it does:**
1. Restores dotnet tools
2. Runs `dotnet cleanup`
3. Restores dependencies
4. Builds the project
5. Runs tests
6. Creates NuGet packages

**Usage:**
```powershell
./CI-CD/build-and-test.ps1 -Configuration Release -OutputPath ./artifacts
```

### `publish-nuget.ps1` - NuGet Publishing Phase
Handles publishing NuGet packages to NuGet.org.

**What it does:**
1. Validates NuGet API key
2. Finds packages in the specified directory
3. Publishes packages to NuGet.org
4. Supports skip-duplicate option

**Usage:**
```powershell
# Using environment variable
$env:NUGET_API_KEY = "your-api-key"
./CI-CD/publish-nuget.ps1

# Using parameter
./CI-CD/publish-nuget.ps1 -NuGetApiKey "your-api-key"
```

## GitHub Actions Integration

The pipeline integrates with GitHub Actions through [`.github/workflows/ci-cd.yml`](../.github/workflows/ci-cd.yml):

- **Build and Test**: Runs on every push and PR to master branch
- **Publish**: Runs only on GitHub releases, publishes to NuGet.org

## Local Testing

You can test the entire pipeline locally:

```powershell
# Test build and test phase
./CI-CD/ci-cd.ps1 -Phase build

# Test complete pipeline (without publishing)
./CI-CD/ci-cd.ps1

# Test with publishing (requires API key)
$env:NUGET_API_KEY = "your-test-api-key"
./CI-CD/ci-cd.ps1 -PublishToNuGet $true
```

## Environment Variables

- `NUGET_API_KEY`: Required for publishing to NuGet.org

## Migration from Manual Process

The previous manual [`publish.ps1`](../publish.ps1) script functionality has been integrated into this CI/CD pipeline:

| Old Script Step | New Location |
|----------------|--------------|
| `Push-Location $PSScriptRoot` | Handled in each script |
| `dotnet tool restore` | `build-and-test.ps1` |
| `dotnet cleanup -y` | `build-and-test.ps1` |
| `dotnet pack` | `build-and-test.ps1` |
| `dotnet nuget push` | `publish-nuget.ps1` |
| Error handling | Enhanced in all scripts |

## Security Considerations

- NuGet API key is handled through GitHub Secrets in CI/CD
- Scripts validate API key presence before attempting to publish
- Local testing can use environment variables
- No API keys are stored in source code

## Troubleshooting

### Build Failures
1. Check that all dependencies are properly restored
2. Ensure .NET 9.0 SDK is installed
3. Verify project builds locally with `dotnet build`

### Test Failures
1. Run tests locally with `dotnet fixie`
2. Check test output for specific failure details
3. Ensure test dependencies are properly configured

### Publish Failures
1. Verify NuGet API key is correctly set
2. Check that packages don't already exist (unless using skip-duplicate)
3. Ensure network connectivity to NuGet.org
4. Verify package metadata is valid
79 changes: 79 additions & 0 deletions CI-CD/build-and-test.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Build and test the TimeWarp.Fixie project
.DESCRIPTION
This script handles the build and test phase of the CI/CD pipeline.
It restores tools, cleans, restores dependencies, builds, and runs tests.
.PARAMETER Configuration
The build configuration (Debug or Release). Default is Release.
.PARAMETER OutputPath
The output path for build artifacts. Default is ./artifacts
#>

param(
[string]$Configuration = "Release",
[string]$OutputPath = "./artifacts"
)

$ErrorActionPreference = "Stop"
$ProjectPath = "./source/TimeWarp.Fixie/TimeWarp.Fixie.csproj"

try {
Push-Location $PSScriptRoot/..

Write-Host "Starting build and test process..." -ForegroundColor Green

# Restore tools
Write-Host "Restoring tools..." -ForegroundColor Yellow
dotnet tool restore
if ($LASTEXITCODE -ne 0) { throw "Tool restore failed" }

# Clean
Write-Host "Cleaning..." -ForegroundColor Yellow
dotnet cleanup -y
if ($LASTEXITCODE -ne 0) { throw "Clean failed" }

# Restore dependencies
Write-Host "Restoring dependencies..." -ForegroundColor Yellow
dotnet restore
if ($LASTEXITCODE -ne 0) { throw "Restore failed" }

# Build
Write-Host "Building..." -ForegroundColor Yellow
dotnet build --no-restore --configuration $Configuration
if ($LASTEXITCODE -ne 0) { throw "Build failed" }

# Test
Write-Host "Running tests..." -ForegroundColor Yellow
dotnet fixie ./tests/TimeWarp.Fixie.Tests --configuration $Configuration --no-build
if ($LASTEXITCODE -ne 0) { throw "Tests failed" }

# Create output directory
if (!(Test-Path $OutputPath)) {
New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
}

# Pack
Write-Host "Creating NuGet package..." -ForegroundColor Yellow
dotnet pack $ProjectPath --no-build --configuration $Configuration --output $OutputPath
if ($LASTEXITCODE -ne 0) { throw "Pack failed" }

Write-Host "Build and test completed successfully!" -ForegroundColor Green

# List created packages
$packages = Get-ChildItem -Path $OutputPath -Filter "*.nupkg"
if ($packages) {
Write-Host "Created packages:" -ForegroundColor Green
foreach ($package in $packages) {
Write-Host " - $($package.Name)" -ForegroundColor Cyan
}
}
}
catch {
Write-Error "Build and test failed: $_"
exit 1
}
finally {
Pop-Location
}
94 changes: 94 additions & 0 deletions CI-CD/ci-cd.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Main CI/CD orchestration script for TimeWarp.Fixie
.DESCRIPTION
This script orchestrates the complete CI/CD pipeline including build, test, and publish phases.
It can be run locally for testing or called from GitHub Actions.
.PARAMETER Phase
The phase to run: 'build', 'publish', or 'all'. Default is 'all'.
.PARAMETER Configuration
The build configuration (Debug or Release). Default is Release.
.PARAMETER PublishToNuGet
Whether to publish to NuGet. Default is false for safety.
.PARAMETER NuGetApiKey
The NuGet API key. If not provided, will use the NUGET_API_KEY environment variable.
#>

param(
[ValidateSet("build", "publish", "all")]
[string]$Phase = "all",
[string]$Configuration = "Release",
[bool]$PublishToNuGet = $false,
[string]$NuGetApiKey = $env:NUGET_API_KEY
)

$ErrorActionPreference = "Stop"
$ArtifactsPath = "./artifacts"

function Write-PhaseHeader {
param([string]$PhaseName)
Write-Host ""
Write-Host "=" * 60 -ForegroundColor Magenta
Write-Host " $PhaseName" -ForegroundColor Magenta
Write-Host "=" * 60 -ForegroundColor Magenta
Write-Host ""
}

try {
Push-Location $PSScriptRoot

Write-Host "TimeWarp.Fixie CI/CD Pipeline" -ForegroundColor Green
Write-Host "Phase: $Phase" -ForegroundColor Cyan
Write-Host "Configuration: $Configuration" -ForegroundColor Cyan
Write-Host "Publish to NuGet: $PublishToNuGet" -ForegroundColor Cyan

# Build and Test Phase
if ($Phase -eq "build" -or $Phase -eq "all") {
Write-PhaseHeader "BUILD AND TEST PHASE"

& ./build-and-test.ps1 -Configuration $Configuration -OutputPath $ArtifactsPath
if ($LASTEXITCODE -ne 0) {
throw "Build and test phase failed"
}
}

# Publish Phase
if (($Phase -eq "publish" -or $Phase -eq "all") -and $PublishToNuGet) {
Write-PhaseHeader "PUBLISH PHASE"

if ([string]::IsNullOrWhiteSpace($NuGetApiKey)) {
Write-Warning "NuGet API key not provided. Skipping publish phase."
Write-Host "To publish, provide the API key via -NuGetApiKey parameter or NUGET_API_KEY environment variable." -ForegroundColor Yellow
} else {
& ./publish-nuget.ps1 -PackagePath $ArtifactsPath -NuGetApiKey $NuGetApiKey
if ($LASTEXITCODE -ne 0) {
throw "Publish phase failed"
}
}
} elseif ($Phase -eq "publish" -or $Phase -eq "all") {
Write-Host "Publish phase skipped (PublishToNuGet = $PublishToNuGet)" -ForegroundColor Yellow
}

Write-PhaseHeader "PIPELINE COMPLETED SUCCESSFULLY"
Write-Host "All phases completed successfully!" -ForegroundColor Green

# Show artifacts
if (Test-Path $ArtifactsPath) {
$artifacts = Get-ChildItem -Path $ArtifactsPath -Filter "*.nupkg"
if ($artifacts) {
Write-Host ""
Write-Host "Generated artifacts:" -ForegroundColor Green
foreach ($artifact in $artifacts) {
Write-Host " - $($artifact.Name)" -ForegroundColor Cyan
}
}
}
}
catch {
Write-Error "CI/CD Pipeline failed: $_"
exit 1
}
finally {
Pop-Location
}
Loading