Skip to content

CI/CD Pipeline

This document describes the LBS Foundry continuous integration and deployment pipeline, implemented in .github/workflows/Build-Container-Apps.yml.

Overview

The pipeline is triggered on: - Push to main branch: Builds production releases - Pull requests to main: Builds prerelease versions with -pr suffix

Pipeline Flow

graph TD
    A[Push/PR to main] --> B[build-and-test]
    B --> C[build-lbs-api]
    B --> D[build-ballr-api]
    B --> E[build-mcp-server]
    B --> F[build-python-sdk]
    B --> G[build-typescript-sdk]
    B --> I[build-foundry-web]
    C --> H[create-release]
    D --> H
    E --> H
    F --> H
    G --> H
    I --> H

Jobs

1. build-and-test (Main Job)

The foundation job that builds everything and provides artifacts for downstream jobs.

Version Generation

Generates timestamp-based semantic versions:

Format: YYYY.MMDD.HHmm.RUN_NUMBER
Example: 2025.1015.0308.577
PR builds: 2025.1015.0308.577-pr

Version Properties: - VERSION: Full version with -pr suffix for PRs - NUMERIC_VERSION: Numeric-only version for assemblies - ASSEMBLY_VERSION: Assembly version number - FILE_VERSION: File version number - INFORMATIONAL_VERSION: Version with -github suffix

Build Steps

1. Setup Environment

- Checkout code (full history)
- Install .NET 10.0
- Generate version numbers

2. Build .NET Solution

dotnet restore LBS.slnx
dotnet build LBS.slnx --configuration Release --no-restore \
  /p:Version=$VERSION \
  /p:AssemblyVersion=$NUMERIC_VERSION \
  /p:FileVersion=$NUMERIC_VERSION \
  /p:InformationalVersion=$INFORMATIONAL_VERSION \
  /p:PackageVersion=$VERSION

Builds all projects in the solution: - Core libraries (Anchor, Augment, EventSourcing) - Domain projects (Core, Sport, Fantasy) - Integration projects - APIs (LBS.Api, Ballr.WebApi) - Tools (SDK Generator) - Tests

3. Run Unit Tests

cd src/Tests/LBS.UnitTests
dotnet run --configuration Release --no-build -- -trx ../../../TestResults/test-results.trx

  • Executes all unit tests
  • Generates TRX format test results
  • Uploads test results as artifacts (always, even on failure)

4. Publish NuGet Packages

dotnet pack LBS.slnx --configuration Release --no-build \
  /p:Version=$VERSION \
  /p:PackageVersion=$VERSION \
  --output ./nupkgs

dotnet nuget push "./nupkgs/*.nupkg" \
  --source "https://nuget.pkg.github.com/$REPO_OWNER/index.json" \
  --api-key $NUGET_PUBLISH_TOKEN \
  --skip-duplicate

Publishes all packable projects to GitHub Packages: - LBS.Anchor - LBS.Augment - LBS.EventSourcing - LBS.Domain.* - LBS.*.Integration

5. Generate SDK Source Code

Uses the LBS.Tools.SdkGenerator to analyze API contracts and generate client SDKs.

TypeScript SDK:

dotnet run --project src/Tools/LBS.Tools.SdkGenerator/LBS.Tools.SdkGenerator.csproj \
  --configuration Release --no-build -- \
  "src/Apps/Ballr.WebApi/bin/Release/net10.0" \
  "/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/10.0.0/ref/net10.0" \
  "sdk/typescript/generated" \
  --language typescript

Python SDK:

dotnet run --project src/Tools/LBS.Tools.SdkGenerator/LBS.Tools.SdkGenerator.csproj \
  --configuration Release --no-build -- \
  "src/Apps/Ballr.WebApi/bin/Release/net10.0" \
  "/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/10.0.0/ref/net10.0" \
  "sdk/python/src/foundry_sdk/generated" \
  --language python

Generates: - TypeScript interfaces and API client - Python classes and API client - Type definitions for all DTOs - Client methods for all API endpoints - Generation logs for troubleshooting

6. Upload SDK Artifacts

Uploads generated SDK source code as temporary artifacts (1-day retention) for downstream jobs.

Outputs

Exports version information for all downstream jobs: - version: Full version string - numeric-version: Numeric version - assembly-version: Assembly version - file-version: File version - informational-version: Informational version

2. build-lbs-api

Builds and pushes the LBS Foundry API Docker container.

docker build \
  --target final-api \
  --build-arg VERSION=$VERSION \
  --build-arg ASSEMBLY_VERSION=$ASSEMBLY_VERSION \
  --build-arg FILE_VERSION=$FILE_VERSION \
  --build-arg INFORMATIONAL_VERSION=$INFORMATIONAL_VERSION \
  -t $REGISTRY/lbs-foundry-api:$VERSION \
  -t $REGISTRY/lbs-foundry-api:latest \
  .

Pushes to Azure Container Registry with tags: - $VERSION (e.g., 2025.1015.0308.577) - latest

3. build-ballr-api

Builds and pushes the Ballr WebAPI Docker container.

docker build \
  --target final-ballr \
  --build-arg VERSION=$VERSION \
  -t $REGISTRY/ballr-web-api:$VERSION \
  -t $REGISTRY/ballr-web-api:latest \
  .

Pushes to Azure Container Registry with tags: - $VERSION - latest

4. build-mcp-server

Builds and pushes the MCP Server Docker container.

docker build \
  --target final-mcpserver \
  --build-arg VERSION=$VERSION \
  -t $REGISTRY/lbs-foundry-mcpserver:$VERSION \
  -t $REGISTRY/lbs-foundry-mcpserver:latest \
  .

Pushes to Azure Container Registry with tags: - $VERSION - latest

5. build-foundry-web

Builds the Foundry Web UI (SvelteKit frontend) and runs quality checks.

Steps

1. Download Generated TypeScript SDK Source Downloads the TypeScript SDK source from build-and-test artifacts into the sdk/typescript/ workspace package.

2. Install Workspace Dependencies

pnpm install --frozen-lockfile

Installs all workspace dependencies. The @luckboxstudios/foundry-sdk dependency resolves via workspace:* link to the local SDK.

3. Build TypeScript SDK

cd sdk/typescript && npm run build

Compiles the generated TypeScript SDK so the workspace link provides built artifacts to foundry-web.

4. Lint

cd src/Apps/foundry-web && pnpm lint

Runs Prettier formatting checks and ESLint.

5. Type Check

cd src/Apps/foundry-web && pnpm check

Runs svelte-check for TypeScript and Svelte type validation.

6. Build

cd src/Apps/foundry-web && pnpm build

Runs vite build producing the Cloudflare Workers-compatible output in .svelte-kit/cloudflare/.

6. build-python-sdk

Builds Python SDK packages for distribution.

Steps

1. Download Generated Source Downloads the Python SDK source from build-and-test artifacts.

2. Version Conversion Converts version to PEP 440 format:

# Production: 2025.1015.0308.577 -> 2025.1015.0308.577
# PR builds: 2025.1015.0308.577-pr -> 2025.1015.0308.577.dev0
VERSION="2025.1015.0308.577-pr"
PYTHON_VERSION="${VERSION//-pr/.dev0}"
sed -i "s/^version = .*/version = \"$PYTHON_VERSION\"/" pyproject.toml

3. Build Packages

python -m build

Creates: - foundry_sdk-$VERSION-py3-none-any.whl (wheel distribution) - foundry_sdk-$VERSION.tar.gz (source distribution)

4. Upload Artifacts Uploads packages as artifacts (90-day retention) for release attachment.

Outputs

  • sdk-version: PEP 440 compliant version string

7. build-typescript-sdk

Builds and publishes TypeScript SDK to GitHub Packages npm registry.

Steps

1. Download Generated Source Downloads the TypeScript SDK source from build-and-test artifacts.

2. Version Update

npm version "$VERSION" --no-git-tag-version

3. Build Package

npm install
npm run build

Compiles TypeScript to JavaScript, generates type definitions, and creates distributable package.

4. Publish to GitHub Packages

# Production builds (main branch)
npm publish --tag latest

# PR builds
npm publish --tag pr

Publishes to https://npm.pkg.github.com/@luckboxstudios/foundry-sdk

npm Tags: - Production builds: Tagged as latest (default install) - PR builds: Tagged as pr (requires explicit version or tag)

Installation:

# Latest production version
npm install @luckboxstudios/foundry-sdk

# Specific PR version
npm install @luckboxstudios/foundry-sdk@2025.1015.0308.577-pr

# Latest PR build
npm install @luckboxstudios/foundry-sdk@pr

8. create-release

Creates GitHub Release with SDK packages and comprehensive release notes.

Steps

1. Download Python SDK Packages Downloads Python SDK artifacts for release attachment.

2. Create and Push Git Tag

TAG_NAME="v$VERSION"
git tag -a "$TAG_NAME" -m "Release version $VERSION"
git push origin "$TAG_NAME"

3. Create GitHub Release

Uses softprops/action-gh-release@v1 to create release with:

Release Notes Template:

## LBS Foundry Release {VERSION}

### Container Images
- **LBS API**: `{REGISTRY}/lbs-foundry-api:{VERSION}`
- **Ballr API**: `{REGISTRY}/ballr-web-api:{VERSION}`
- **MCP Server**: `{REGISTRY}/lbs-foundry-mcpserver:{VERSION}`

### SDK Packages
- **TypeScript SDK**: Published to GitHub Packages npm registry
  ```bash
  npm install @luckboxstudios/foundry-sdk@{VERSION}
  ```
- **Python SDK**: Download from release assets below
  ```bash
  pip install foundry_sdk-{VERSION}-py3-none-any.whl
  ```

### NuGet Packages
Published to GitHub Packages

Release Properties: - Prerelease: Automatically marked for PR builds - Assets: Python SDK packages (.whl and .tar.gz) - Notes: Auto-generated from commits since last release

Environment Variables

Required secrets in GitHub repository settings:

Secret Description Used By
ACR_REGISTRY Azure Container Registry URL Container builds
ACR_USERNAME ACR username Container builds
ACR_PASSWORD ACR password Container builds
NUGET_PUBLISH_TOKEN GitHub token for NuGet publishing NuGet push
GITHUB_TOKEN Automatic GitHub token SDK publishing, releases

Build Artifacts

Temporary Artifacts (1-day retention)

  • typescript-sdk-source: Generated TypeScript SDK source code
  • python-sdk-source: Generated Python SDK source code

Permanent Artifacts (90-day retention)

  • test-results: Unit test results (TRX format)
  • typescript-generation-log: TypeScript SDK generation log
  • python-generation-log: Python SDK generation log
  • python-sdk-{VERSION}: Python SDK packages

Package Distribution

NuGet Packages

Location: GitHub Packages URL: https://nuget.pkg.github.com/luckboxstudios/index.json Authentication: Required (GitHub PAT)

Installation:

dotnet add package LBS.Anchor --version {VERSION}
dotnet add package LBS.EventSourcing --version {VERSION}

TypeScript SDK

Location: GitHub Packages (npm registry) URL: https://npm.pkg.github.com/@luckboxstudios Authentication: Required (GitHub PAT)

Installation:

# Configure npm registry
echo "@luckboxstudios:registry=https://npm.pkg.github.com" >> .npmrc
echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> .npmrc

# Install package
npm install @luckboxstudios/foundry-sdk@{VERSION}

Python SDK

Location: GitHub Releases (downloadable assets) URL: https://github.com/luckboxstudios/LBS.Foundry/releases Authentication: Not required for public releases

Installation:

# Download wheel from release assets, then:
pip install foundry_sdk-{VERSION}-py3-none-any.whl

Container Images

Location: Azure Container Registry URL: Configured in ACR_REGISTRY secret Authentication: Required (ACR credentials)

Pull Images:

docker pull {REGISTRY}/lbs-foundry-api:{VERSION}
docker pull {REGISTRY}/ballr-web-api:{VERSION}
docker pull {REGISTRY}/lbs-foundry-mcpserver:{VERSION}

Troubleshooting

Build Failures

Tests Failing

# Run locally to reproduce (xUnit v3 — use `dotnet run`, not `dotnet test`)
dotnet run --project src/Tests/LBS.UnitTests/LBS.UnitTests.csproj

# Check test results artifact in GitHub Actions

SDK Generation Fails

# Run locally
dotnet run --project src/Tools/LBS.Tools.SdkGenerator/LBS.Tools.SdkGenerator.csproj \
  --configuration Release -- \
  "src/Apps/Ballr.WebApi/bin/Release/net10.0" \
  "/path/to/dotnet/ref/net10.0" \
  "sdk/typescript/generated" \
  --language typescript

# Check generation logs in artifacts

Docker Build Fails

# Test Docker build locally
docker build --target final-api -t test-api .

# Check Dockerfile and build context

Publishing Failures

NuGet Push Fails - Verify NUGET_PUBLISH_TOKEN is valid - Check package version doesn't already exist - Ensure GitHub Packages is enabled

npm Publish Fails - Verify GITHUB_TOKEN has package write permissions - Check package version doesn't already exist - Ensure package.json has correct registry configuration

Release Creation Fails - Verify all prerequisite jobs succeeded - Check Python SDK artifacts exist - Ensure tag doesn't already exist

Version Strategy

Production Releases (main branch)

Format: YYYY.MMDD.HHmm.RUN_NUMBER
Example: 2025.1015.0308.577

Components: - YYYY: 4-digit year - MM: 2-digit month - DD: 2-digit day - HH: 2-digit hour (UTC) - mm: 2-digit minute (UTC) - RUN_NUMBER: GitHub Actions run number

Benefits: - Sortable by version number - Identifies build time instantly - Unique for every build - Compatible with semantic versioning tools

Prerelease Versions (PRs)

Format: YYYY.MMDD.HHmm.RUN_NUMBER-pr
Example: 2025.1015.0308.577-pr
Python: 2025.1015.0308.577.dev0

Characteristics: - Marked as prerelease in GitHub - Not considered stable - Automatically cleaned up - Python uses PEP 440 .dev0 suffix

Workflow Modifications

Adding a New Job

  1. Define job in .github/workflows/Build-Container-Apps.yml:

    my-new-job:
      needs: build-and-test
      runs-on: ubuntu-latest
      permissions:
        contents: read
    
      steps:
      - uses: actions/checkout@v4
      - name: Do something
        run: echo "Hello"
    

  2. Add to create-release dependencies:

    create-release:
      needs: [build-and-test, ..., my-new-job]
    

Adding a New Package

  1. Ensure project is packable in .csproj:

    <IsPackable>true</IsPackable>
    <PackageId>LBS.MyNewPackage</PackageId>
    

  2. Add to solution:

    dotnet sln add src/MyNewPackage/MyNewPackage.csproj
    

  3. Build pipeline automatically packs and publishes

Modifying SDK Generation

Edit generation commands in build-and-test job:

- name: Generate TypeScript SDK
  run: |
    dotnet run --project src/Tools/LBS.Tools.SdkGenerator/... \
      "src/Apps/Ballr.WebApi/bin/Release/net10.0" \
      ... \
      --language typescript \
      --new-option value  # Add new options here

Performance Optimization

Parallel Job Execution

Jobs run in parallel when possible:

build-and-test (sequential steps)
  
├─ build-lbs-api ────────┐
├─ build-ballr-api ──────┤
├─ build-mcp-server ─────┤ (parallel)
├─ build-foundry-web ────┤
├─ build-python-sdk ─────┤
└─ build-typescript-sdk ─┘
  
create-release

Caching Strategy

Docker Layer Caching: - Uses Docker BuildKit - Caches intermediate layers - Reduces rebuild time

NuGet Caching:

- uses: actions/setup-dotnet@v4
  with:
    cache: true  # Caches NuGet packages

npm Caching:

- uses: actions/setup-node@v4
  with:
    cache: 'npm'  # Caches node_modules

Build Time Optimization

Current build times (approximate): - build-and-test: 4-6 minutes - Container builds: 2-3 minutes each (parallel) - SDK builds: 1-2 minutes each (parallel) - Total pipeline: 6-10 minutes

Optimization tips: - Keep Docker images small - Use --no-build for already-built projects - Minimize test execution time - Use efficient test runners

Security Considerations

Secret Management

  • Never log secrets
  • Rotate secrets regularly
  • Use minimal required permissions
  • Audit secret access

Container Security

  • Images scanned by Azure Security Center
  • Base images regularly updated
  • No secrets in container images
  • Runtime security monitoring

Package Security

  • Dependency scanning enabled
  • Vulnerability alerts configured
  • Regular dependency updates
  • Code signing for packages

Monitoring and Alerts

Build Health

  • GitHub Actions dashboard
  • Email notifications on failure
  • Slack/Teams integration (optional)

Package Health

  • NuGet package stats
  • npm download metrics
  • Container pull statistics

Release Tracking

  • GitHub Release notes
  • Version tags in Git
  • Changelog generation

Quick Reference

Check Pipeline Status

# View recent runs
gh run list --workflow="Build and Push Container Images"

# View specific run
gh run view RUN_ID

# View failed jobs
gh run view RUN_ID --log-failed

Trigger Manual Run

# Trigger on current branch
gh workflow run "Build and Push Container Images"

Download Artifacts

# List artifacts for a run
gh run view RUN_ID --json artifacts

# Download specific artifact
gh run download RUN_ID --name python-sdk-VERSION

View Releases

# List releases
gh release list

# View specific release
gh release view v2025.1015.0308.577

# Download release assets
gh release download v2025.1015.0308.577