mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-04-30 17:48:27 -05:00
Restore Windows container build support that was accidentally removed
in commits a76aab467..a90c8803f. This restores:
- Windows Docker infrastructure (book/docker/windows/)
- Windows container build workflow (infra-container-windows.yml)
- Windows matrix entries in book-build-container.yml
- Windows health check support in infra-health-check.yml
- Windows build flags in book-validate-dev.yml and book-publish-live.yml
Restored from pre-removal state at f85e319d6.
816 lines
35 KiB
YAML
816 lines
35 KiB
YAML
name: '🔧 Infra · ❤️ Health Check'
|
|
|
|
# Comprehensive health validation for build containers
|
|
# Tests essential Quarto build tools and functionality
|
|
# Runs daily to ensure container reliability
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
container_registry:
|
|
description: 'Container registry URL'
|
|
required: false
|
|
default: 'ghcr.io'
|
|
type: string
|
|
container_tag:
|
|
description: 'Container tag to test'
|
|
required: false
|
|
default: 'latest'
|
|
type: string
|
|
test_linux:
|
|
description: 'Test Linux container'
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
test_windows:
|
|
description: 'Test Windows container'
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
workflow_call:
|
|
inputs:
|
|
container_registry:
|
|
description: 'Container registry URL'
|
|
required: false
|
|
default: 'ghcr.io'
|
|
type: string
|
|
container_tag:
|
|
description: 'Container tag to test'
|
|
required: false
|
|
default: 'latest'
|
|
type: string
|
|
test_linux:
|
|
description: 'Test Linux container'
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
test_windows:
|
|
description: 'Test Windows container'
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
schedule:
|
|
# Run daily at 2 AM UTC
|
|
- cron: '0 2 * * *'
|
|
|
|
# Centralized Container Configuration - Single Source of Truth
|
|
env:
|
|
# ==========================================================================
|
|
# PATH CONFIGURATION - Uses GitHub Repository Variables (Settings > Variables)
|
|
# ==========================================================================
|
|
# MLSysBook content lives under book/ to accommodate TinyTorch at root
|
|
# Use ${{ vars.BOOK_ROOT }}, ${{ vars.BOOK_DOCKER }}, etc. in workflow steps
|
|
# Variables: BOOK_ROOT, BOOK_DOCKER, BOOK_TOOLS, BOOK_QUARTO, BOOK_DEPS
|
|
|
|
# Registry Configuration
|
|
REGISTRY: ${{ inputs.container_registry || 'ghcr.io' }}
|
|
CONTAINER_TAG: ${{ inputs.container_tag || 'latest' }}
|
|
LINUX_CONTAINER_NAME: 'quarto-linux'
|
|
WINDOWS_CONTAINER_NAME: 'quarto-windows'
|
|
# Computed full image names
|
|
LINUX_IMAGE: ${{ inputs.container_registry || 'ghcr.io' }}/${{ github.repository }}/quarto-linux:${{ inputs.container_tag || 'latest' }}
|
|
WINDOWS_IMAGE: ${{ inputs.container_registry || 'ghcr.io' }}/${{ github.repository }}/quarto-windows:${{ inputs.container_tag || 'latest' }}
|
|
|
|
jobs:
|
|
# Matrix strategy for both container platforms
|
|
container-health-check:
|
|
runs-on: ${{ matrix.os }}
|
|
if: github.repository_owner == 'harvard-edge'
|
|
timeout-minutes: 60
|
|
strategy:
|
|
fail-fast: false # Test both containers even if one fails
|
|
matrix:
|
|
include:
|
|
- os: ubuntu-latest
|
|
platform: linux
|
|
container_name: quarto-linux
|
|
shell: bash
|
|
- os: windows-latest
|
|
platform: windows
|
|
container_name: quarto-windows
|
|
shell: pwsh
|
|
|
|
env:
|
|
CONTAINER_IMAGE: ${{ inputs.container_registry || 'ghcr.io' }}/${{ github.repository }}/${{ matrix.container_name }}:${{ inputs.container_tag || 'latest' }}
|
|
PLATFORM: ${{ matrix.platform }}
|
|
# Using vars.BOOK_DOCKER (repository variable) - works in all contexts
|
|
DOCKERFILE_PATH: ./${{ vars.BOOK_DOCKER }}/${{ matrix.platform }}/Dockerfile
|
|
|
|
steps:
|
|
- name: 📥 Checkout repository
|
|
if: |
|
|
(matrix.platform == 'linux' && inputs.test_linux != false) ||
|
|
(matrix.platform == 'windows' && inputs.test_windows != false)
|
|
uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: 🔑 Log in to GitHub Container Registry
|
|
if: |
|
|
(matrix.platform == 'linux' && inputs.test_linux != false) ||
|
|
(matrix.platform == 'windows' && inputs.test_windows != false)
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: 🐳 Pull Docker Image
|
|
if: |
|
|
(matrix.platform == 'linux' && inputs.test_linux != false) ||
|
|
(matrix.platform == 'windows' && inputs.test_windows != false)
|
|
run: docker pull ${{ env.CONTAINER_IMAGE }}
|
|
|
|
- name: 📊 Container Information
|
|
if: |
|
|
(matrix.platform == 'linux' && inputs.test_linux != false) ||
|
|
(matrix.platform == 'windows' && inputs.test_windows != false)
|
|
run: |
|
|
echo "📊 === CONTAINER INFORMATION ==="
|
|
echo "📋 Platform: ${{ matrix.platform }}"
|
|
echo "📋 Image: ${{ env.CONTAINER_IMAGE }}"
|
|
echo "📋 Container Details:"
|
|
docker images ${{ env.CONTAINER_IMAGE }} --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
|
|
|
|
echo ""
|
|
echo "📊 Container Size Analysis:"
|
|
docker images ${{ env.CONTAINER_IMAGE }} --format "{{.Size}}"
|
|
|
|
- name: 🐧 Linux Container - Tool Version Check
|
|
if: matrix.platform == 'linux' && inputs.test_linux != false
|
|
run: |
|
|
echo "🐧 === LINUX CONTAINER TOOL VERSIONS ==="
|
|
echo "📋 Testing Linux tools with version capture:"
|
|
echo "============================================"
|
|
|
|
docker run --rm ${{ env.CONTAINER_IMAGE }} bash -c "
|
|
echo '🔍 === LINUX CONTAINER TOOL VERSIONS ==='
|
|
echo ''
|
|
|
|
echo '📊 QUARTO:'
|
|
echo '----------'
|
|
if command -v quarto >/dev/null 2>&1; then
|
|
echo '📍 Location: $(which quarto)'
|
|
echo '📋 Version: $(quarto --version 2>&1 | head -1)'
|
|
echo '✅ Status: OK'
|
|
else
|
|
echo '❌ Status: NOT FOUND'
|
|
fi
|
|
echo ''
|
|
|
|
echo '📊 PYTHON:'
|
|
echo '-----------'
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
echo '📍 Location: $(which python3)'
|
|
echo '📋 Version: $(python3 --version 2>&1)'
|
|
echo '✅ Status: OK'
|
|
else
|
|
echo '❌ Status: NOT FOUND'
|
|
fi
|
|
echo ''
|
|
|
|
echo '📊 R:'
|
|
echo '------'
|
|
if command -v R >/dev/null 2>&1; then
|
|
echo '📍 Location: $(which R)'
|
|
echo '📋 Version: $(R --version 2>&1 | head -1)'
|
|
echo '✅ Status: OK'
|
|
else
|
|
echo '❌ Status: NOT FOUND'
|
|
fi
|
|
echo ''
|
|
|
|
echo '📊 LUALATEX:'
|
|
echo '------------'
|
|
if command -v lualatex >/dev/null 2>&1; then
|
|
echo '📍 Location: $(which lualatex)'
|
|
echo '📋 Version: $(lualatex --version 2>&1 | head -1)'
|
|
echo '✅ Status: OK'
|
|
else
|
|
echo '❌ Status: NOT FOUND'
|
|
fi
|
|
echo ''
|
|
|
|
echo '📊 GHOSTSCRIPT:'
|
|
echo '---------------'
|
|
if command -v gs >/dev/null 2>&1; then
|
|
echo '📍 Location: $(which gs)'
|
|
echo '📋 Version: $(gs --version 2>&1)'
|
|
echo '✅ Status: OK'
|
|
else
|
|
echo '❌ Status: NOT FOUND'
|
|
fi
|
|
echo ''
|
|
|
|
echo '📊 INKSCAPE:'
|
|
echo '------------'
|
|
if command -v inkscape >/dev/null 2>&1; then
|
|
echo '📍 Location: $(which inkscape)'
|
|
echo '📋 Version: $(inkscape --version 2>&1 | head -1)'
|
|
echo '✅ Status: OK'
|
|
else
|
|
echo '❌ Status: NOT FOUND'
|
|
fi
|
|
echo ''
|
|
|
|
echo '🎯 === LINUX TOOL CHECK COMPLETE ==='
|
|
echo ''
|
|
|
|
# Summary of all tool statuses
|
|
echo '📋 LINUX TOOL STATUS SUMMARY:'
|
|
echo '=============================='
|
|
|
|
# Check each tool and show status
|
|
if command -v quarto >/dev/null 2>&1; then
|
|
echo '✅ Quarto: AVAILABLE'
|
|
else
|
|
echo '❌ Quarto: MISSING'
|
|
fi
|
|
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
echo '✅ Python: AVAILABLE'
|
|
else
|
|
echo '❌ Python: MISSING'
|
|
fi
|
|
|
|
if command -v R >/dev/null 2>&1; then
|
|
echo '✅ R: AVAILABLE'
|
|
else
|
|
echo '❌ R: MISSING'
|
|
fi
|
|
|
|
if command -v lualatex >/dev/null 2>&1; then
|
|
echo '✅ LuaLaTeX: AVAILABLE'
|
|
else
|
|
echo '❌ LuaLaTeX: MISSING'
|
|
fi
|
|
|
|
if command -v gs >/dev/null 2>&1; then
|
|
echo '✅ Ghostscript: AVAILABLE'
|
|
else
|
|
echo '❌ Ghostscript: MISSING'
|
|
fi
|
|
|
|
if command -v inkscape >/dev/null 2>&1; then
|
|
echo '✅ Inkscape: AVAILABLE'
|
|
else
|
|
echo '❌ Inkscape: MISSING'
|
|
fi
|
|
|
|
echo ''
|
|
|
|
# Check if any tools are missing and fail if so
|
|
FAILED_TOOLS=0
|
|
|
|
if ! command -v quarto >/dev/null 2>&1; then
|
|
FAILED_TOOLS=$((FAILED_TOOLS + 1))
|
|
fi
|
|
|
|
if ! command -v python3 >/dev/null 2>&1; then
|
|
FAILED_TOOLS=$((FAILED_TOOLS + 1))
|
|
fi
|
|
|
|
if ! command -v R >/dev/null 2>&1; then
|
|
FAILED_TOOLS=$((FAILED_TOOLS + 1))
|
|
fi
|
|
|
|
if ! command -v lualatex >/dev/null 2>&1; then
|
|
FAILED_TOOLS=$((FAILED_TOOLS + 1))
|
|
fi
|
|
|
|
if ! command -v gs >/dev/null 2>&1; then
|
|
FAILED_TOOLS=$((FAILED_TOOLS + 1))
|
|
fi
|
|
|
|
if ! command -v inkscape >/dev/null 2>&1; then
|
|
FAILED_TOOLS=$((FAILED_TOOLS + 1))
|
|
fi
|
|
|
|
if [ $FAILED_TOOLS -eq 0 ]; then
|
|
echo '🎯 ✅ Linux container tool validation: PASSED'
|
|
echo 'All essential Quarto build tools are available!'
|
|
else
|
|
echo \"🎯 ❌ Linux container tool validation: FAILED\"
|
|
echo \"$FAILED_TOOLS essential tool(s) are missing!\"
|
|
echo 'This container is NOT ready for Quarto builds.'
|
|
exit 1
|
|
fi
|
|
" | tee linux-tool-versions.log
|
|
|
|
echo "📋 Linux tool versions saved to linux-tool-versions.log"
|
|
|
|
- name: 🪟 Windows Container - Tool Version Check
|
|
if: matrix.platform == 'windows' && inputs.test_windows != false
|
|
run: |
|
|
Write-Output "🪟 === WINDOWS CONTAINER TOOL VERSIONS ==="
|
|
Write-Output "📋 Testing Windows tools with enhanced path detection using 'scoop which':"
|
|
Write-Output "=============================================="
|
|
|
|
docker run --rm ${{ env.CONTAINER_IMAGE }} pwsh -Command "
|
|
Write-Output '🔍 === WINDOWS CONTAINER TOOL VERSIONS ==='
|
|
Write-Output ''
|
|
|
|
# Check Scoop environment
|
|
Write-Output '📦 SCOOP ENVIRONMENT:'
|
|
Write-Output '--------------------'
|
|
if (Test-Path 'C:\Users\ContainerAdministrator\scoop') {
|
|
Write-Output '✅ Scoop directory found'
|
|
if (Test-Path 'C:\Users\ContainerAdministrator\scoop\shims') {
|
|
`$shimCount = (Get-ChildItem 'C:\Users\ContainerAdministrator\scoop\shims' -File | Measure-Object).Count
|
|
Write-Output `"📁 Scoop shims directory: `$shimCount files`"
|
|
}
|
|
} else {
|
|
Write-Output '❌ Scoop directory not found'
|
|
}
|
|
Write-Output ''
|
|
|
|
# --- Tool Checks ---
|
|
|
|
Write-Output '📊 QUARTO:'
|
|
Write-Output '----------'
|
|
try {
|
|
`$quartoPath = (scoop which quarto 2>`$null).Trim()
|
|
if (`$LASTEXITCODE -ne 0) { throw }
|
|
Write-Output `"📍 Location: `$quartoPath`"
|
|
Write-Output '📦 Method: scoop which'
|
|
`$quartoVersion = & `$quartoPath --version 2>&1
|
|
Write-Output `"📋 Version: `$quartoVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
try {
|
|
`$quartoPath = (Get-Command quarto -ErrorAction Stop).Source
|
|
Write-Output `"📍 Location: `$quartoPath`"
|
|
Write-Output '📦 Method: Get-Command (PATH)'
|
|
`$quartoVersion = & `$quartoPath --version 2>&1
|
|
Write-Output `"📋 Version: `$quartoVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
Write-Output '❌ Status: NOT FOUND'
|
|
}
|
|
}
|
|
Write-Output ''
|
|
|
|
Write-Output '📊 PYTHON:'
|
|
Write-Output '-----------'
|
|
try {
|
|
`$pythonPath = (scoop which python 2>`$null).Trim()
|
|
if (`$LASTEXITCODE -ne 0) { throw }
|
|
Write-Output `"📍 Location: `$pythonPath`"
|
|
Write-Output '📦 Method: scoop which'
|
|
`$pythonVersion = & `$pythonPath --version 2>&1
|
|
Write-Output `"📋 Version: `$pythonVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
try {
|
|
`$pythonPath = (Get-Command python -ErrorAction Stop).Source
|
|
Write-Output `"📍 Location: `$pythonPath`"
|
|
Write-Output '📦 Method: Get-Command (PATH)'
|
|
`$pythonVersion = & `$pythonPath --version 2>&1
|
|
Write-Output `"📋 Version: `$pythonVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
Write-Output '❌ Status: NOT FOUND'
|
|
}
|
|
}
|
|
Write-Output ''
|
|
|
|
Write-Output '📊 R:'
|
|
Write-Output '------'
|
|
try {
|
|
`$rCmd = Get-Command R.exe -ErrorAction SilentlyContinue
|
|
if (-not `$rCmd) { `$rCmd = Get-Command R -ErrorAction Stop }
|
|
`$rPath = `$rCmd.Source
|
|
Write-Output `"📍 Location: `$rPath`"
|
|
Write-Output '📦 Method: Get-Command (PATH)'
|
|
`$rVersion = & `$rCmd.Name --version 2>&1 | Select-Object -First 1
|
|
Write-Output `"📋 Version: `$rVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
Write-Output '❌ Status: NOT FOUND'
|
|
}
|
|
Write-Output ''
|
|
|
|
Write-Output '📊 LUALATEX:'
|
|
Write-Output '------------'
|
|
try {
|
|
`$lualatexPath = (Get-Command lualatex -ErrorAction Stop).Source
|
|
Write-Output `"📍 Location: `$lualatexPath`"
|
|
`$lualatexVersion = & `$lualatexPath --version 2>&1 | Select-Object -First 1
|
|
Write-Output `"📋 Version: `$lualatexVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
Write-Output '❌ Status: NOT FOUND'
|
|
}
|
|
Write-Output ''
|
|
|
|
Write-Output '📊 GHOSTSCRIPT:'
|
|
Write-Output '---------------'
|
|
try {
|
|
`$gsPath = (scoop which gswin64c 2>`$null).Trim()
|
|
if (`$LASTEXITCODE -ne 0) { `$gsPath = (scoop which gs 2>`$null).Trim() }
|
|
if (`$LASTEXITCODE -ne 0) { throw }
|
|
Write-Output `"📍 Location: `$gsPath`"
|
|
Write-Output '📦 Method: scoop which'
|
|
`$gsVersion = & `$gsPath --version 2>&1
|
|
Write-Output `"📋 Version: `$gsVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
try {
|
|
`$gsCmd = Get-Command gswin64c -ErrorAction SilentlyContinue
|
|
if (-not `$gsCmd) { `$gsCmd = Get-Command gs -ErrorAction Stop }
|
|
`$gsPath = `$gsCmd.Source
|
|
Write-Output `"📍 Location: `$gsPath`"
|
|
Write-Output '📦 Method: Get-Command (PATH)'
|
|
`$gsVersion = & `$gsCmd.Name --version 2>&1
|
|
Write-Output `"📋 Version: `$gsVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
Write-Output '❌ Status: NOT FOUND'
|
|
}
|
|
}
|
|
Write-Output ''
|
|
|
|
Write-Output '📊 INKSCAPE:'
|
|
Write-Output '------------'
|
|
try {
|
|
`$inkscapePath = (scoop which inkscape 2>`$null).Trim()
|
|
if (`$LASTEXITCODE -ne 0) { throw }
|
|
Write-Output `"📍 Location: `$inkscapePath`"
|
|
Write-Output '📦 Method: scoop which'
|
|
`$inkscapeVersion = & `$inkscapePath --version 2>&1 | Select-Object -First 1
|
|
Write-Output `"📋 Version: `$inkscapeVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
try {
|
|
`$inkscapePath = (Get-Command inkscape -ErrorAction Stop).Source
|
|
Write-Output `"📍 Location: `$inkscapePath`"
|
|
Write-Output '📦 Method: Get-Command (PATH)'
|
|
`$inkscapeVersion = & `$inkscapePath --version 2>&1 | Select-Object -First 1
|
|
Write-Output `"📋 Version: `$inkscapeVersion`"
|
|
Write-Output '✅ Status: OK'
|
|
} catch {
|
|
Write-Output '❌ Status: NOT FOUND'
|
|
}
|
|
}
|
|
Write-Output ''
|
|
|
|
Write-Output '🎯 === WINDOWS TOOL CHECK COMPLETE ==='
|
|
Write-Output ''
|
|
|
|
# Summary of all tool statuses
|
|
Write-Output '📋 WINDOWS TOOL STATUS SUMMARY:'
|
|
Write-Output '==============================='
|
|
|
|
# Check each tool and show status
|
|
if ((scoop which quarto 2>`$null) -or (Get-Command quarto -ErrorAction SilentlyContinue)) {
|
|
Write-Output '✅ Quarto: AVAILABLE'
|
|
} else {
|
|
Write-Output '❌ Quarto: MISSING'
|
|
}
|
|
if ((scoop which python 2>`$null) -or (Get-Command python -ErrorAction SilentlyContinue)) {
|
|
Write-Output '✅ Python: AVAILABLE'
|
|
} else {
|
|
Write-Output '❌ Python: MISSING'
|
|
}
|
|
if ((Get-Command R.exe -ErrorAction SilentlyContinue) -or (Get-Command R -ErrorAction SilentlyContinue)) {
|
|
Write-Output '✅ R: AVAILABLE'
|
|
} else {
|
|
Write-Output '❌ R: MISSING'
|
|
}
|
|
if (Get-Command lualatex -ErrorAction SilentlyContinue) {
|
|
Write-Output '✅ LuaLaTeX: AVAILABLE'
|
|
} else {
|
|
Write-Output '❌ LuaLaTeX: MISSING'
|
|
}
|
|
if ((scoop which gswin64c 2>`$null) -or (scoop which gs 2>`$null) -or (Get-Command gswin64c -ErrorAction SilentlyContinue) -or (Get-Command gs -ErrorAction SilentlyContinue)) {
|
|
Write-Output '✅ Ghostscript: AVAILABLE'
|
|
} else {
|
|
Write-Output '❌ Ghostscript: MISSING'
|
|
}
|
|
if ((scoop which inkscape 2>`$null) -or (Get-Command inkscape -ErrorAction SilentlyContinue)) {
|
|
Write-Output '✅ Inkscape: AVAILABLE'
|
|
} else {
|
|
Write-Output '❌ Inkscape: MISSING'
|
|
}
|
|
|
|
Write-Output ''
|
|
|
|
# Check if any tools are missing and fail if so
|
|
`$FailedTools = 0
|
|
|
|
if (-not ((scoop which quarto 2>`$null) -or (Get-Command quarto -ErrorAction SilentlyContinue))) { `$FailedTools++ }
|
|
if (-not ((scoop which python 2>`$null) -or (Get-Command python -ErrorAction SilentlyContinue))) { `$FailedTools++ }
|
|
if (-not ((Get-Command R.exe -ErrorAction SilentlyContinue) -or (Get-Command R -ErrorAction SilentlyContinue))) { `$FailedTools++ }
|
|
if (-not (Get-Command lualatex -ErrorAction SilentlyContinue)) { `$FailedTools++ }
|
|
if (-not ((scoop which gswin64c 2>`$null) -or (scoop which gs 2>`$null) -or (Get-Command gswin64c -ErrorAction SilentlyContinue) -or (Get-Command gs -ErrorAction SilentlyContinue))) { `$FailedTools++ }
|
|
if (-not ((scoop which inkscape 2>`$null) -or (Get-Command inkscape -ErrorAction SilentlyContinue))) { `$FailedTools++ }
|
|
|
|
if (`$FailedTools -eq 0) {
|
|
Write-Output '🎯 ✅ Windows container tool validation: PASSED'
|
|
Write-Output 'All essential Quarto build tools are available!'
|
|
} else {
|
|
Write-Output '🎯 ❌ Windows container tool validation: FAILED'
|
|
Write-Output "Missing `$FailedTools essential tools - container not ready!"
|
|
Write-Output 'This container is NOT ready for Quarto builds.'
|
|
exit 1
|
|
}
|
|
Write-Output ''
|
|
|
|
# List all Scoop apps for debugging
|
|
Write-Output '📦 SCOOP INSTALLED APPS:'
|
|
Write-Output '------------------------'
|
|
if (Get-Command scoop -ErrorAction SilentlyContinue) {
|
|
try {
|
|
`$scoopApps = & scoop list 2>&1
|
|
Write-Output `$scoopApps
|
|
} catch {
|
|
Write-Output 'Failed to list Scoop apps'
|
|
}
|
|
} else {
|
|
Write-Output 'Scoop command not found'
|
|
}
|
|
" | Tee-Object -FilePath "windows-tool-versions.log"
|
|
|
|
Write-Output "📋 Windows tool versions saved to windows-tool-versions.log"
|
|
|
|
- name: 🐧 Linux Container - Quarto Check
|
|
if: matrix.platform == 'linux' && inputs.test_linux != false
|
|
continue-on-error: true # Don't fail workflow if quarto check fails
|
|
run: |
|
|
echo "🐧 === LINUX QUARTO CHECK (COMPREHENSIVE) ==="
|
|
echo "📋 Running quarto check to validate full installation:"
|
|
echo "===================================================="
|
|
|
|
docker run --rm ${{ env.CONTAINER_IMAGE }} bash -c "
|
|
echo '🔍 Running quarto check with enhanced raw output capture...'
|
|
echo '📋 Capturing full output for debugging...'
|
|
echo ''
|
|
|
|
# Set maximum verbosity for raw output
|
|
export QUARTO_LOG_LEVEL=DEBUG
|
|
export QUARTO_PRINT_STACK=true
|
|
|
|
echo '⏰ Quarto check started at:' \$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo '--- RAW QUARTO CHECK OUTPUT START ---'
|
|
echo '┌─────────────────────────────────────────────────────────────────────────────┐'
|
|
|
|
# Capture and display raw output with error handling
|
|
if quarto check 2>&1 | while IFS= read -r line; do echo \"│ \$line\"; done; then
|
|
QUARTO_EXIT_CODE=0
|
|
else
|
|
QUARTO_EXIT_CODE=\$?
|
|
fi
|
|
|
|
echo '└─────────────────────────────────────────────────────────────────────────────┘'
|
|
echo '--- RAW QUARTO CHECK OUTPUT END ---'
|
|
echo '⏰ Quarto check completed at:' \$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo ''
|
|
|
|
# Determine final status
|
|
if [ \$QUARTO_EXIT_CODE -eq 0 ]; then
|
|
echo ''
|
|
echo '✅ Quarto check: PASSED - All components verified!'
|
|
QUARTO_STATUS='PASSED'
|
|
else
|
|
echo ''
|
|
echo '❌ Quarto check: FAILED - Issues detected!'
|
|
echo '🔍 This indicates potential container configuration issues.'
|
|
echo '📋 Check the full output above for specific error details.'
|
|
QUARTO_STATUS='FAILED'
|
|
fi
|
|
|
|
echo ''
|
|
echo \"📊 QUARTO CHECK SUMMARY: \$QUARTO_STATUS\"
|
|
" | tee linux-quarto-check.log
|
|
|
|
echo "📋 Linux quarto check results saved to linux-quarto-check.log"
|
|
|
|
# Also extract just the raw quarto check output for easy viewing
|
|
if [ -f "linux-quarto-check.log" ]; then
|
|
echo "📄 Extracting raw Quarto output..."
|
|
grep -A 1000 "RAW QUARTO CHECK OUTPUT START" linux-quarto-check.log | \
|
|
grep -B 1000 "RAW QUARTO CHECK OUTPUT END" | \
|
|
sed '1d;$d' > linux-quarto-raw-output.txt || echo "Could not extract raw output"
|
|
|
|
if [ -f "linux-quarto-raw-output.txt" ]; then
|
|
echo "📄 Raw Quarto output extracted to linux-quarto-raw-output.txt"
|
|
echo "📊 Raw output preview (first 10 lines):"
|
|
head -10 linux-quarto-raw-output.txt | sed 's/^/ /'
|
|
fi
|
|
fi
|
|
|
|
- name: 🪟 Windows Container - Quarto Check
|
|
if: matrix.platform == 'windows' && inputs.test_windows != false
|
|
continue-on-error: true # Don't fail workflow if quarto check fails
|
|
run: |
|
|
Write-Output "🪟 === WINDOWS QUARTO CHECK (COMPREHENSIVE) ==="
|
|
Write-Output "📋 Running quarto check to validate full installation:"
|
|
Write-Output "====================================================="
|
|
|
|
# 1) Emit the inner script to a file (no escaping headaches)
|
|
$inner = @'
|
|
Write-Output "🔍 Running enhanced Quarto diagnostics..."
|
|
Write-Output "📋 Capturing comprehensive diagnostic information..."
|
|
Write-Output ""
|
|
|
|
Write-Output '--- SYSTEM DIAGNOSTIC INFO START ---'
|
|
Write-Output '🔧 Environment Variables:'
|
|
Write-Output ("PATH: {0}" -f $env:PATH)
|
|
Write-Output ("QUARTO_LOG_LEVEL: {0}" -f $env:QUARTO_LOG_LEVEL)
|
|
Write-Output ""
|
|
|
|
Write-Output '🔧 Visual C++ Redistributable DLLs:'
|
|
Get-ChildItem 'C:\Windows\System32' -Filter 'msvcp*.dll' -ErrorAction SilentlyContinue |
|
|
Select-Object Name, Length |
|
|
Format-Table -AutoSize | Out-String | Write-Output
|
|
Get-ChildItem 'C:\Windows\System32' -Filter 'vcruntime*.dll' -ErrorAction SilentlyContinue |
|
|
Select-Object Name, Length |
|
|
Format-Table -AutoSize | Out-String | Write-Output
|
|
Write-Output ""
|
|
|
|
Write-Output '🔧 Quarto Installation Info:'
|
|
try {
|
|
$quartoCmd = Get-Command quarto.exe -ErrorAction Stop
|
|
Write-Output ("Quarto.exe location: {0}" -f $quartoCmd.Source)
|
|
Write-Output ("Quarto.exe file size: {0} bytes" -f (Get-Item $quartoCmd.Source).Length)
|
|
$quartoVersion = & $quartoCmd.Source --version 2>&1
|
|
Write-Output ("Quarto version: {0}" -f $quartoVersion.Trim())
|
|
} catch {
|
|
Write-Output 'quarto.exe not found in PATH'
|
|
}
|
|
Write-Output '--- SYSTEM DIAGNOSTIC INFO END ---'
|
|
Write-Output ""
|
|
|
|
# Run quarto check with enhanced error handling and raw output capture
|
|
$quartoExitCode = 0
|
|
try {
|
|
Write-Output '--- QUARTO CHECK OUTPUT START ---'
|
|
Write-Output '🔍 Setting maximum verbosity for raw output capture...'
|
|
$env:QUARTO_LOG_LEVEL = 'DEBUG'
|
|
$env:QUARTO_PRINT_STACK = 'true'
|
|
|
|
# Capture raw output with timestamps using a more robust method
|
|
Write-Output "⏰ Quarto check started at: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
|
|
|
|
$stdOutPath = [System.IO.Path]::GetTempFileName()
|
|
$stdErrPath = [System.IO.Path]::GetTempFileName()
|
|
|
|
$quartoExitCode = 0
|
|
|
|
try {
|
|
$process = Start-Process -FilePath "quarto.exe" -ArgumentList "check", "--log-level=debug" -Wait -PassThru -RedirectStandardOutput $stdOutPath -RedirectStandardError $stdErrPath
|
|
$quartoExitCode = $process.ExitCode
|
|
} catch {
|
|
$quartoExitCode = 1 # Indicate failure
|
|
Write-Output "‼️ Error executing Start-Process: $($_.Exception.Message)"
|
|
}
|
|
|
|
$rawOutput = Get-Content $stdOutPath -Raw -ErrorAction SilentlyContinue
|
|
$errorOutput = Get-Content $stdErrPath -Raw -ErrorAction SilentlyContinue
|
|
|
|
# Combine stdout and stderr for the log
|
|
if (![string]::IsNullOrEmpty($errorOutput)) {
|
|
$rawOutput += "`n--- STDERR ---`n" + $errorOutput
|
|
}
|
|
|
|
Remove-Item $stdOutPath, $stdErrPath -Force -ErrorAction SilentlyContinue
|
|
|
|
Write-Output "⏰ Quarto check completed at: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
|
|
|
|
# Display raw output line by line with prefixes for clarity
|
|
Write-Output '📋 RAW QUARTO CHECK OUTPUT:'
|
|
Write-Output '┌─────────────────────────────────────────────────────────────────────────────┐'
|
|
if (-not [string]::IsNullOrEmpty($rawOutput)) {
|
|
foreach ($line in $rawOutput.Split("`n")) {
|
|
Write-Output "│ $line"
|
|
}
|
|
}
|
|
Write-Output '└─────────────────────────────────────────────────────────────────────────────┘'
|
|
|
|
Write-Output '--- QUARTO CHECK OUTPUT END ---'
|
|
} catch {
|
|
Write-Output '--- QUARTO CHECK EXCEPTION ---'
|
|
Write-Output ("Exception Type: {0}" -f $($_.Exception.GetType().FullName))
|
|
Write-Output ("Exception Message: {0}" -f $($_.Exception.Message))
|
|
if ($_.Exception.InnerException) {
|
|
Write-Output ("Inner Exception: {0}" -f $($_.Exception.InnerException.Message))
|
|
}
|
|
Write-Output '--- QUARTO CHECK EXCEPTION END ---'
|
|
$quartoExitCode = 1
|
|
}
|
|
|
|
Write-Output ""
|
|
if ($quartoExitCode -eq 0) {
|
|
Write-Output '✅ Quarto check: PASSED - All components verified!'
|
|
$QuartoStatus = 'PASSED'
|
|
} else {
|
|
Write-Output '❌ Quarto check: FAILED - Issues detected!'
|
|
Write-Output '🔍 This indicates potential container configuration issues.'
|
|
Write-Output '📋 Check the diagnostic information above for details.'
|
|
Write-Output ("📊 Exit Code: {0}" -f $quartoExitCode)
|
|
switch ($quartoExitCode) {
|
|
-1073741515 { Write-Output '🔍 Exit code -1073741515 (0xC0000135): DLL not found - Missing Visual C++ Runtime or dependencies' }
|
|
1 { Write-Output '🔍 Exit code 1: General error - Check Quarto dependencies' }
|
|
default { Write-Output ("🔍 Exit code {0}: Unknown error - Check logs above" -f $quartoExitCode) }
|
|
}
|
|
$QuartoStatus = 'FAILED'
|
|
}
|
|
|
|
Write-Output ""
|
|
Write-Output ("📊 QUARTO CHECK SUMMARY: {0}" -f $QuartoStatus)
|
|
'@
|
|
|
|
$scriptPath = Join-Path $PWD 'windows-quarto-check.ps1'
|
|
Set-Content -Path $scriptPath -Value $inner -Encoding UTF8
|
|
|
|
# 2) Run it inside the Windows container and tee the output
|
|
docker run --rm `
|
|
-v "${PWD}:C:\work" `
|
|
${{ env.CONTAINER_IMAGE }} `
|
|
pwsh -NoProfile -ExecutionPolicy Bypass -File "C:\work\windows-quarto-check.ps1" `
|
|
| Tee-Object -FilePath "windows-quarto-check.log"
|
|
|
|
Write-Output "📋 Windows quarto check results saved to windows-quarto-check.log"
|
|
|
|
# Also save just the raw quarto check output to a separate file for easy viewing
|
|
if (Test-Path "windows-quarto-check.log") {
|
|
$logContent = Get-Content "windows-quarto-check.log" -Raw
|
|
if ($logContent -match '📋 RAW QUARTO CHECK OUTPUT:(.*?)--- QUARTO CHECK OUTPUT END ---') {
|
|
$rawOutput = $matches[1].Trim()
|
|
$rawOutput | Out-File "windows-quarto-raw-output.txt" -Encoding UTF8
|
|
Write-Output "📄 Raw Quarto output extracted to windows-quarto-raw-output.txt"
|
|
}
|
|
}
|
|
|
|
- name: 📊 Linux Container Analysis
|
|
if: matrix.platform == 'linux' && inputs.test_linux != false
|
|
run: |
|
|
echo "LINUX CONTAINER TEST SUMMARY"
|
|
echo "✅ Container pulled successfully"
|
|
echo "✅ Essential tools validated with pass/fail status (Quarto, Python, R, LaTeX, Ghostscript, Inkscape)"
|
|
echo "✅ Quarto check completed"
|
|
echo "✅ Container size displayed"
|
|
echo "✅ Tool validation enforced - will FAIL if any essential tool missing"
|
|
echo "LINUX CONTAINER TESTS COMPLETE"
|
|
|
|
- name: 📊 Windows Container Analysis
|
|
if: matrix.platform == 'windows' && inputs.test_windows != false
|
|
run: |
|
|
Write-Output "WINDOWS CONTAINER TEST SUMMARY"
|
|
Write-Output "✅ Container pulled successfully"
|
|
Write-Output "✅ Essential tools validated with pass/fail status (Quarto, Python, R, LaTeX, Ghostscript, Inkscape)"
|
|
Write-Output "✅ Quarto check completed"
|
|
Write-Output "✅ Container size displayed"
|
|
Write-Output "✅ Tool validation enforced - will FAIL if any essential tool missing"
|
|
Write-Output "WINDOWS CONTAINER TESTS COMPLETE"
|
|
|
|
- name: 📤 Upload Test Artifacts
|
|
if: always() && ((matrix.platform == 'linux' && inputs.test_linux != false) || (matrix.platform == 'windows' && inputs.test_windows != false))
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: ${{ matrix.platform }}-container-test-results
|
|
path: |
|
|
${{ matrix.platform == 'linux' && 'linux-tool-versions.log' || 'windows-tool-versions.log' }}
|
|
${{ matrix.platform == 'linux' && 'linux-quarto-check.log' || 'windows-quarto-check.log' }}
|
|
${{ matrix.platform == 'linux' && 'linux-quarto-raw-output.txt' || 'windows-quarto-raw-output.txt' }}
|
|
${{ matrix.platform == 'windows' && 'windows-quarto-check.ps1' || '' }}
|
|
if-no-files-found: warn
|
|
|
|
# Final summary job
|
|
final-summary:
|
|
needs: container-health-check
|
|
runs-on: ubuntu-latest
|
|
if: always() # Always run to provide summary
|
|
|
|
steps:
|
|
- name: 🎯 Final Container Health Summary
|
|
run: |
|
|
echo "🎯 === FINAL CONTAINER HEALTH SUMMARY ==="
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# Get container sizes
|
|
LINUX_IMAGE="${{ inputs.container_registry || 'ghcr.io' }}/${{ github.repository }}/quarto-linux:${{ inputs.container_tag || 'latest' }}"
|
|
WINDOWS_IMAGE="${{ inputs.container_registry || 'ghcr.io' }}/${{ github.repository }}/quarto-windows:${{ inputs.container_tag || 'latest' }}"
|
|
LINUX_SIZE=$(docker images "$LINUX_IMAGE" --format "{{.Size}}" 2>/dev/null || echo "Not available")
|
|
WINDOWS_SIZE=$(docker images "$WINDOWS_IMAGE" --format "{{.Size}}" 2>/dev/null || echo "Not available")
|
|
|
|
echo "📦 CONTAINER SIZES:"
|
|
echo "------------------"
|
|
echo "🐧 Linux: $LINUX_SIZE"
|
|
echo "🪟 Windows: $WINDOWS_SIZE"
|
|
echo ""
|
|
|
|
echo "🔍 TEST RESULTS:"
|
|
echo "---------------"
|
|
echo "✅ Container health checks completed"
|
|
echo "✅ All test artifacts uploaded"
|
|
echo ""
|
|
|
|
echo "🎯 FINAL STATUS:"
|
|
echo "---------------"
|
|
echo "🟢 CONTAINER TESTING: COMPLETED SUCCESSFULLY ✅"
|
|
echo "📋 Containers validated and ready for production use"
|
|
echo "📊 Detailed logs available in artifacts"
|
|
echo "========================================"
|