APIGenerator CI/CD Pipeline #32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: APIGenerator CI/CD Pipeline | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| - cron: '0 1 * * 0' # Every Sunday at 1 AM UTC | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| env: | |
| TARGET_BRANCH: automatic-api-updates | |
| # This branch is used for automatic API updates | |
| FALLBACK_BRANCH: main | |
| jobs: | |
| run: | |
| if: github.ref == 'refs/heads/main' | |
| runs-on: windows-latest | |
| steps: | |
| - name: Resolve branch to use | |
| id: resolve_branch | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TARGET_BRANCH: ${{ env.TARGET_BRANCH }} | |
| FALLBACK_BRANCH: ${{ env.FALLBACK_BRANCH }} | |
| run: | | |
| $REPO = "github.com/$env:GITHUB_REPOSITORY" | |
| $targetBranchExists = git ls-remote --heads "https://x-access-token:$env:GITHUB_TOKEN@$REPO" $env:TARGET_BRANCH | |
| if ($targetBranchExists) { | |
| "branch=$env:TARGET_BRANCH" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| } else { | |
| "branch=$env:FALLBACK_BRANCH" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| } | |
| - name: Checkout main repository | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| ref: ${{ steps.resolve_branch.outputs.branch }} | |
| fetch-depth: 0 | |
| - name: Show which branch was used | |
| run: "echo \"Using branch: ${{ steps.resolve_branch.outputs.branch }}\"" | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '8.0.x' | |
| - name: Setup MSBuild | |
| uses: microsoft/setup-msbuild@v2 | |
| - name: Setup NuGet.exe for use with actions | |
| uses: NuGet/[email protected] | |
| - name: Create Repositories directory | |
| run: mkdir Repositories | |
| - name: Read and clone repositories | |
| run: | | |
| # Check if repos file exists | |
| if (!(Test-Path "APIGenerator\APIGenerator\Repos.txt")) { | |
| Write-Error "Error: Repos.txt not found at APIGenerator\APIGenerator\Repos.txt" | |
| exit 1 | |
| } | |
| # Read repos and clone them | |
| Get-Content "APIGenerator\APIGenerator\Repos.txt" | ForEach-Object { | |
| $repo_url = $_.Trim() | |
| # Skip empty lines and comments | |
| if ([string]::IsNullOrWhiteSpace($repo_url) -or $repo_url.StartsWith("#")) { | |
| return | |
| } | |
| # Extract repo name from URL | |
| $repo_name = [System.IO.Path]::GetFileNameWithoutExtension($repo_url) | |
| if ($repo_name.EndsWith(".git")) { | |
| $repo_name = $repo_name.Substring(0, $repo_name.Length - 4) | |
| } | |
| Write-Host "Processing repository: $repo_name" | |
| # Clone repository | |
| try { | |
| git clone $repo_url "Repositories\$repo_name" | |
| Write-Host "Successfully cloned $repo_name" | |
| # Check if .sln file exists in base folder | |
| $sln_files = Get-ChildItem "Repositories\$repo_name" -Filter "*.sln" -File | |
| if ($sln_files.Count -gt 0) { | |
| Write-Host "✓ Found .sln file in $repo_name - keeping repository" | |
| $repo_name | Add-Content "valid_repos.txt" -Encoding UTF8 | |
| } else { | |
| Write-Host "✗ No .sln file found in base folder of $repo_name - Repo not built" | |
| } | |
| } catch { | |
| Write-Host "Failed to clone $repo_name" | |
| } | |
| } | |
| # Show summary | |
| if (Test-Path "valid_repos.txt") { | |
| Write-Host "Valid repositories to compile:" | |
| Get-Content "valid_repos.txt" | |
| } else { | |
| Write-Host "No valid repositories found with .sln files" | |
| } | |
| shell: pwsh | |
| - name: Compile repositories in order | |
| run: | | |
| if (!(Test-Path "valid_repos.txt")) { | |
| Write-Host "No valid repositories to compile" | |
| exit 0 | |
| } | |
| # Compile each valid repository | |
| Get-Content "valid_repos.txt" | ForEach-Object { | |
| $repo_name = $_.Trim() | |
| Write-Host "Compiling repository: $repo_name" | |
| Push-Location "Repositories\$repo_name" | |
| try { | |
| # Find and restore packages for all .sln files | |
| Get-ChildItem -Filter "*.sln" -File | ForEach-Object { | |
| Write-Host "Restoring packages for $($_.Name)" | |
| dotnet restore $_.Name 2>&1 | Tee-Object -Variable restoreOutput | |
| Write-Host $restoreOutput | |
| } | |
| # Build all .sln files in the repository | |
| Get-ChildItem -Filter "*.sln" -File | ForEach-Object { | |
| Write-Host "Building $($_.Name) with dotnet build" | |
| dotnet build $_.Name --configuration Release --no-restore 2>&1 | Tee-Object -Variable buildOutput | |
| Write-Host $buildOutput | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Host "✓ Successfully built $($_.Name) with dotnet build" | |
| } else { | |
| Write-Host "✗ dotnet build failed for $($_.Name), trying MSBuild..." -ForegroundColor Yellow | |
| Write-Host "dotnet build errors:" -ForegroundColor Red | |
| Write-Host $buildOutput -ForegroundColor Red | |
| # Restore packages with MSBuild/NuGet | |
| Write-Host "Restoring packages with MSBuild..." | |
| nuget restore $_.Name | |
| # Try with MSBuild as fallback | |
| msbuild $_.Name /p:Configuration=Release /p:Platform="Any CPU" /m 2>&1 | Tee-Object -Variable msbuildOutput | |
| Write-Host $msbuildOutput | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Host "✓ Successfully built $($_.Name) with MSBuild" | |
| } else { | |
| Write-Host "✗ Both dotnet build and MSBuild failed for $($_.Name)" -ForegroundColor Red | |
| Write-Host "MSBuild errors:" -ForegroundColor Red | |
| Write-Host $msbuildOutput -ForegroundColor Red | |
| exit 1 | |
| } | |
| } | |
| } | |
| } finally { | |
| Pop-Location | |
| } | |
| } | |
| shell: pwsh | |
| - name: Clean docs/oM folder | |
| run: | | |
| $docsPath = "docs\oM" | |
| if (Test-Path $docsPath) { | |
| Write-Host "Found docs/oM folder, removing all subfolders and their content..." | |
| # Get all subdirectories in docs/oM | |
| $subfolders = Get-ChildItem -Path $docsPath -Directory | |
| if ($subfolders.Count -gt 0) { | |
| foreach ($folder in $subfolders) { | |
| Write-Host "Removing subfolder: $($folder.Name)" | |
| Remove-Item -Path $folder.FullName -Recurse -Force | |
| } | |
| Write-Host "✓ Successfully removed $($subfolders.Count) subfolders from docs/oM" | |
| } else { | |
| Write-Host "No subfolders found in docs/oM" | |
| } | |
| } else { | |
| Write-Host "docs/oM folder not found, skipping cleanup" | |
| } | |
| shell: pwsh | |
| - name: Compile APIGenerator solution | |
| run: | | |
| Write-Host "Compiling APIGenerator.sln" | |
| # Check if the solution file exists | |
| if (!(Test-Path "APIGenerator\APIGenerator.sln")) { | |
| Write-Error "Error: APIGenerator.sln not found at APIGenerator\APIGenerator.sln" | |
| exit 1 | |
| } | |
| # Restore packages | |
| Write-Host "Restoring packages for APIGenerator.sln" | |
| dotnet restore "APIGenerator\APIGenerator.sln" 2>&1 | Tee-Object -Variable restoreOutput | |
| Write-Host $restoreOutput | |
| # Build the solution | |
| Write-Host "Building APIGenerator.sln with dotnet build" | |
| dotnet build "APIGenerator\APIGenerator.sln" --configuration Release --no-restore 2>&1 | Tee-Object -Variable buildOutput | |
| Write-Host $buildOutput | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Host "dotnet build failed, trying MSBuild..." -ForegroundColor Yellow | |
| Write-Host "dotnet build errors:" -ForegroundColor Red | |
| Write-Host $buildOutput -ForegroundColor Red | |
| # Restore packages with MSBuild | |
| Write-Host "Restoring packages with MSBuild..." | |
| msbuild "APIGenerator\APIGenerator.sln" -t:Restore /p:Configuration=Release 2>&1 | Tee-Object -Variable msbuildRestoreOutput | |
| Write-Host $msbuildRestoreOutput | |
| msbuild "APIGenerator\APIGenerator.sln" /p:Configuration=Release /p:Platform="Any CPU" /m 2>&1 | Tee-Object -Variable msbuildOutput | |
| Write-Host $msbuildOutput | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Host "Both dotnet build and MSBuild failed for APIGenerator.sln" -ForegroundColor Red | |
| Write-Host "MSBuild errors:" -ForegroundColor Red | |
| Write-Host $msbuildOutput -ForegroundColor Red | |
| exit 1 | |
| } else { | |
| Write-Host "✓ Successfully built APIGenerator.sln with MSBuild" | |
| } | |
| } else { | |
| Write-Host "✓ Successfully built APIGenerator.sln with dotnet build" | |
| } | |
| shell: pwsh | |
| - name: Find and run APIGenerator executable | |
| run: | | |
| Write-Host "Looking for APIGenerator executable..." | |
| # Look for the executable in common build output locations | |
| $possible_paths = @( | |
| "APIGenerator\APIGenerator\bin\Release\net*\APIGenerator.exe", | |
| "APIGenerator\bin\Release\net*\APIGenerator.exe" | |
| ) | |
| $exe_path = $null | |
| foreach ($pattern in $possible_paths) { | |
| $found_files = Get-ChildItem -Path $pattern -ErrorAction SilentlyContinue | |
| if ($found_files) { | |
| $exe_path = $found_files[0].FullName | |
| break | |
| } | |
| } | |
| # If no .exe found, try using dotnet run | |
| if ([string]::IsNullOrEmpty($exe_path)) { | |
| Write-Host "No executable found, trying dotnet run..." | |
| if (Test-Path "APIGenerator\APIGenerator.csproj") { | |
| Push-Location "APIGenerator" | |
| dotnet run --project APIGenerator.csproj --configuration Release | |
| Pop-Location | |
| } elseif (Test-Path "APIGenerator\APIGenerator\APIGenerator.csproj") { | |
| Push-Location "APIGenerator" | |
| dotnet run --project APIGenerator\APIGenerator.csproj --configuration Release | |
| Pop-Location | |
| } else { | |
| Write-Error "Could not find APIGenerator.csproj file" | |
| exit 1 | |
| } | |
| } else { | |
| Write-Host "Found executable at: $exe_path" | |
| Write-Host "Running APIGenerator..." | |
| # Run the executable | |
| & $exe_path | |
| } | |
| shell: pwsh | |
| - name: Commit and push changes | |
| run: | | |
| # Configure git | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| # Create and checkout new branch (or switch if exists) | |
| $branch_name = "${{ env.TARGET_BRANCH }}" | |
| git checkout $branch_name 2>$null || git checkout -b $branch_name | |
| # Add all changes (including new files and directories) | |
| git add -A | |
| # Check if there are any changes to commit | |
| $changes = git status --porcelain | |
| if ($changes) { | |
| Write-Host "Changes detected, committing..." | |
| git commit -m "APIGenerator workflow results - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | |
| # Push the branch | |
| git push origin $branch_name | |
| Write-Host "Successfully pushed changes to branch: $branch_name" | |
| # Check if a PR already exists for this branch | |
| $existingPr = gh pr list --head $branch_name --base main --json number --jq '.[0].number' | |
| if ($existingPr) { | |
| Write-Host "PR already exists (#$existingPr), adding a comment..." | |
| gh pr comment $existingPr --body "Automated API documentation updates pushed to branch '$branch_name' on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')." | |
| Write-Host "✓ Comment added to existing PR" | |
| } else { | |
| Write-Host "Creating pull request..." | |
| gh pr create --title "API Updates" --body "Automated API documentation updates generated by APIGenerator workflow on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" --head $branch_name --base main | |
| Write-Host "✓ Pull request created successfully" | |
| } | |
| } else { | |
| Write-Host "No changes to commit" | |
| } | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |