GitHub Actions Workflow Architecture
This document describes the complete CI/CD pipeline architecture for building, testing, and publishing the Datadog SDK for .NET MAUI.
Overview
The build system consists of multiple GitHub Actions workflows that work together to provide a complete CI/CD pipeline. The workflows are designed to:
- Run in Parallel: Android and iOS builds execute simultaneously
- Cache Aggressively: Downloaded dependencies and built packages are cached
- Support Multi-Targeting: Build for multiple .NET versions (8, 9, 10)
- Validate Quality: Check code formatting, links, and package integrity
- Automate Releases: Create GitHub releases and publish to NuGet.org
Workflow Structure
build-all.yml (Orchestrator)
├── build-android.yml (Parallel)
│ ├── download-aars
│ ├── build-android-net9
│ ├── build-android-net10
│ ├── combine-android-packages
│ └── build-android-sample
└── build-ios.yml (Parallel)
├── download-xcframeworks
├── build-ios-net8
├── build-ios-net9
├── build-ios-net10
├── combine-ios-packages
└── build-ios-sample
Core Workflows
1. Build All Platforms (build-all.yml)
Triggers:
- Push to
mainordevelopbranches - Pull requests to
mainordevelop - Manual workflow dispatch
- Weekly schedule (Mondays at 9 AM UTC)
Path Filters: Only runs when these paths change:
Datadog.MAUI.Android.Binding/**Datadog.MAUI.iOS.Binding/**samples/**scripts/**Directory.Build.props.github/workflows/**
Jobs:
- build-android: Calls Android build workflow (runs in parallel)
- build-ios: Calls iOS build workflow (runs in parallel)
- validate-builds: Validates both builds completed, generates summary
- create-release: Creates GitHub release when tagged (only for version tags)
Outputs:
- Combined NuGet packages artifact (
all-nuget-packages) - Build summary with package counts
- SHA256 checksums for package verification
2. Build Android Bindings (build-android.yml)
Workflow Type: Reusable workflow (called by build-all.yml)
Job Flow:
graph TD
A[download-aars] --> B[build-android-net9]
A --> C[build-android-net10]
B --> D[combine-android-packages]
C --> D
D --> E[build-android-sample]
Job: download-aars
- Runner:
ubuntu-latest - Purpose: Download Android AAR files from Maven/GitHub
- Caching:
- Cache key:
${{ runner.os }}-aars-${{ version }} - Cached paths:
Datadog.MAUI.Android.Binding/**/aars/*.aar
- Cache key:
- Output: Uploads AAR files as artifact (
android-aar-files)
Job: build-android-net9
- Runner:
ubuntu-latest - Purpose: Build Android bindings for .NET 9
- Caching:
- Workloads:
~/.dotnet/sdk-manifests,~/.dotnet/metadata,~/.nuget/packages - NuGet packages:
./artifacts-net9/*.nupkg
- Workloads:
- Steps:
- Download AAR files artifact
- Setup .NET 9 SDK with global.json
- Install Android workload
- Workload restore + package restore
- Build with
net9.0-androidtarget - Pack NuGet packages
- Output:
android-nuget-packages-net9artifact
Job: build-android-net10
- Runner:
ubuntu-latest - Purpose: Build Android bindings for .NET 10
- Caching: Similar to net9 build
- Steps: Same as net9 but with .NET 10 SDK
- Output:
android-nuget-packages-net10artifact
Job: combine-android-packages
- Runner:
ubuntu-latest - Purpose: Merge net9 and net10 packages into unified multi-targeting packages
- Process:
- Download both net9 and net10 artifacts
- Extract each package
- Copy net10 framework libs into net9 package structure
- Repackage with both target frameworks
- Output:
android-nuget-packagesartifact (combined)
Job: build-android-sample
- Runner:
ubuntu-latest - Purpose: Validate packages by building sample app
- Steps:
- Download combined packages
- Add local NuGet source
- Build sample app for
net10.0-android
- Output: Sample APK artifact
3. Build iOS Bindings (build-ios.yml)
Workflow Type: Reusable workflow (called by build-all.yml)
Job Flow:
graph TD
A[download-xcframeworks] --> B[build-ios-net8]
A --> C[build-ios-net9]
A --> D[build-ios-net10]
B --> E[combine-ios-packages]
C --> E
D --> E
E --> F[build-ios-sample]
Job: download-xcframeworks
- Runner:
macos-latest - Purpose: Download iOS XCFrameworks from GitHub releases
- Caching:
- Cache key:
${{ runner.os }}-xcframeworks-${{ version }} - Cached paths:
Datadog.MAUI.iOS.Binding/Libs/*.xcframework
- Cache key:
- Xcode: Selects Xcode 15.4
- Output: Uploads XCFrameworks as artifact (
ios-xcframeworks)
Job: build-ios-net8
- Runner:
ubuntu-latest(iOS bindings can build on Linux!) - Purpose: Build iOS bindings for .NET 8
- Caching:
- Workloads: iOS workload manifests
- NuGet packages:
./artifacts-net8/*.nupkg
- Steps:
- Download XCFrameworks artifact
- Setup .NET 8 SDK with temporary global.json
- Install iOS workload
- Workload restore + package restore
- Build with
net8.0-iostarget - Pack NuGet packages
- Output:
ios-nuget-packages-net8artifact - Note: Uses
continue-on-error: trueas iOS bindings are not yet complete
Jobs: build-ios-net9 and build-ios-net10
- Similar to net8 build but with .NET 9 and .NET 10 SDKs respectively
Job: combine-ios-packages
- Runner:
ubuntu-latest - Purpose: Merge net8, net9, and net10 packages into unified multi-targeting packages
- Process:
- Download all three framework artifacts
- Determine base package (try net8 first, then net9)
- Extract and merge all framework versions
- Repackage with all target frameworks
- Output:
ios-nuget-packagesartifact (combined) - Note: Handles missing packages gracefully (continues even if iOS builds fail)
Job: build-ios-sample
- Runner:
macos-latest(required for iOS app building) - Purpose: Validate packages by building sample iOS app
- Output: Sample .app artifact
Supporting Workflows
4. Publish to NuGet (publish-to-nuget.yml)
Trigger: Manual workflow dispatch
Inputs:
release_tag: GitHub release tag to publish from (e.g.,v3.5.0)dry_run: Boolean to validate without publishing (default:true)
Jobs:
- download-release-packages: Download .nupkg files from GitHub release
- validate-packages: Verify package structure and metadata
- publish-to-nuget: Push packages to NuGet.org (if not dry-run)
- dry-run-summary: Show what would be published (if dry-run)
Environment: Uses nuget-production environment for secrets
Features:
- Checksum verification (SHA256)
- Duplicate detection (
--skip-duplicate) - Detailed success/failure tracking
- Safe dry-run mode for testing
5. Validate Pull Request (validate-pr.yml)
Trigger: Pull request events (opened, synchronize, reopened)
Jobs:
- validate-changes: Check for version updates and documentation
- check-build: Quick build validation
- link-checker: Verify markdown links
- size-check: Report AAR and XCFramework sizes
Features:
- Uses
tj-actions/changed-filesto detect what changed - Warns if code changes without README updates
- Non-blocking (uses
continue-on-errorfor checks)
6. Check SDK Updates (check-sdk-updates.yml)
Triggers:
- Weekly schedule (Mondays at 9 AM UTC)
- Manual workflow dispatch
Purpose: Monitor for new Datadog SDK releases
Process:
- Fetch current version from
Directory.Build.props - Query GitHub API for latest Android and iOS SDK releases
- Compare versions
- Create/update GitHub issue if updates available
Output: Issue with update instructions and links to release notes
Caching Strategy
AAR Files (Android)
- Key:
${{ runner.os }}-aars-${{ sdk-version }} - Paths:
Datadog.MAUI.Android.Binding/**/aars/*.aar - Retention: Permanent (until version changes)
XCFrameworks (iOS)
- Key:
${{ runner.os }}-xcframeworks-${{ sdk-version }} - Paths:
Datadog.MAUI.iOS.Binding/Libs/*.xcframework - Retention: Permanent (until version changes)
.NET Workloads
- Key:
${{ runner.os }}-dotnet-workload-{platform}-${{ dotnet-version }} - Paths:
~/.dotnet/sdk-manifests~/.dotnet/metadata~/.dotnet/workloadsets~/.nuget/packages
- Retention: Permanent (until .NET version changes)
NuGet Packages
- Key:
${{ runner.os }}-{platform}-{framework}-packages-${{ sdk-version }}-${{ hashFiles('**/*.csproj') }} - Paths:
./artifacts-{framework}/*.nupkg - Retention: Per-framework, invalidated by .csproj changes
Artifact Retention
| Artifact Type | Retention Days | Purpose |
|---|---|---|
| AAR files | 1 day | Temporary transfer between jobs |
| XCFrameworks | 1 day | Temporary transfer between jobs |
| NuGet packages (framework-specific) | 7 days | Debugging individual builds |
| Combined NuGet packages | 30 days | Release preparation |
| Sample APK/App | 7 days | Testing and validation |
| Build logs | 3 days | Debugging failures |
Performance Optimizations
- Parallel Builds: Android and iOS workflows run simultaneously
- Aggressive Caching: Dependencies cached per SDK version
- Conditional Steps: Skip cached builds entirely
- Ubuntu Runners: Use Linux for iOS binding compilation (faster/cheaper)
- Artifact Reuse: Download once, use across multiple jobs
Release Process
Creating a Release
- Tag the commit:
git tag v3.5.0 git push origin v3.5.0 -
Automatic build:
build-all.ymldetects tag and triggerscreate-releasejob - GitHub Release created with:
- All NuGet packages
- SHA256 checksums
- Release notes
- Documentation links
Publishing to NuGet
- Navigate to Actions → “Publish to NuGet”
- Click “Run workflow”
- Enter release tag (e.g.,
v3.5.0) - Choose “Dry run” =
truefor validation - Review validation results
- Re-run with “Dry run” =
falseto publish
Monitoring
Build Status
- Check Actions tab for workflow runs
- Review job summaries for package counts
- Download artifacts for local testing
SDK Updates
- Weekly automated checks
- GitHub issues created automatically
- Release notes linked in issues
Package Health
- Checksum verification on publish
- Structure validation before release
- Sample app builds confirm usability
Troubleshooting
Build Failures
- Check cache status: Invalidate if corrupted
- Review build logs: Download from failed job
- Test locally: Use same commands from workflow
- Check dependencies: Verify AAR/XCFramework downloads
Cache Issues
Clear caches via Actions settings or change version in Directory.Build.props
Publishing Failures
- Review validation job output
- Check NuGet API key is valid
- Verify packages not already published
- Check package size limits
Future Enhancements
- Add automated testing workflows
- Implement code signing for packages
- Add automated changelog generation
- Create performance benchmarking workflow
- Add automated dependency updates (Dependabot)