mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-03-08 23:03:55 -05:00
Improve container preflight diagnostics and docs references.
Add explicit per-check preflight logging and matrix failure instance reporting in the container build workflow, and update stale documentation links and workflow/file path references.
This commit is contained in:
180
.github/workflows/book-build-container.yml
vendored
180
.github/workflows/book-build-container.yml
vendored
@@ -104,6 +104,12 @@ on:
|
||||
build_target:
|
||||
description: "Build target used (vol1/vol2/all)"
|
||||
value: ${{ jobs.collect-outputs.outputs.build_target }}
|
||||
failed_instances:
|
||||
description: "Failed matrix build instances"
|
||||
value: ${{ jobs.collect-outputs.outputs.failed_instances }}
|
||||
cancelled_instances:
|
||||
description: "Cancelled matrix build instances"
|
||||
value: ${{ jobs.collect-outputs.outputs.cancelled_instances }}
|
||||
# Volume I artifacts
|
||||
linux_html_vol1_artifact:
|
||||
description: "Linux HTML artifact name (Volume I)"
|
||||
@@ -148,6 +154,7 @@ on:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
actions: read
|
||||
|
||||
# =============================================================================
|
||||
# PATH CONFIGURATION - Uses GitHub Repository Variables (Settings > Variables)
|
||||
@@ -167,7 +174,7 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
# =================================================================
|
||||
# Volume I builds (Linux only for now)
|
||||
# Volume I builds
|
||||
# =================================================================
|
||||
- platform: linux
|
||||
platform_name: Linux
|
||||
@@ -206,7 +213,7 @@ jobs:
|
||||
artifact_name: ${{ inputs.target }}-epub-vol1-linux
|
||||
output_dir: _build/epub-vol1
|
||||
# =================================================================
|
||||
# Volume II builds (Linux only for now)
|
||||
# Volume II builds
|
||||
# =================================================================
|
||||
- platform: linux
|
||||
platform_name: Linux
|
||||
@@ -380,29 +387,41 @@ jobs:
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
set -eu
|
||||
echo "🧪 Running Linux toolchain preflight checks..."
|
||||
command -v quarto
|
||||
quarto --version | sed -n '1p'
|
||||
command -v pandoc
|
||||
pandoc --version | sed -n '1p'
|
||||
command -v python3
|
||||
python3 --version
|
||||
python3 -c "import mlsysim,sys; print('mlsysim:', mlsysim.__file__); print('python:', sys.executable)"
|
||||
command -v Rscript
|
||||
Rscript --version | sed -n '1p'
|
||||
command -v inkscape
|
||||
inkscape --version | sed -n '1p'
|
||||
run_check() {
|
||||
CHECK_NAME="$1"
|
||||
CHECK_CMD="$2"
|
||||
echo "▶ preflight: $CHECK_NAME"
|
||||
if sh -c "$CHECK_CMD"; then
|
||||
echo "✅ preflight: $CHECK_NAME"
|
||||
else
|
||||
echo "❌ preflight failed: $CHECK_NAME"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
run_check "quarto on PATH" "command -v quarto >/dev/null"
|
||||
run_check "quarto version" "quarto --version | sed -n '1p'"
|
||||
run_check "pandoc on PATH" "command -v pandoc >/dev/null"
|
||||
run_check "pandoc version" "pandoc --version | sed -n '1p'"
|
||||
run_check "python3 on PATH" "command -v python3 >/dev/null"
|
||||
run_check "python3 version" "python3 --version"
|
||||
run_check "mlsysim import" "python3 -c \"import mlsysim,sys; print('mlsysim:', mlsysim.__file__); print('python:', sys.executable)\""
|
||||
run_check "Rscript on PATH" "command -v Rscript >/dev/null"
|
||||
run_check "Rscript version" "Rscript --version | sed -n '1p'"
|
||||
run_check "inkscape on PATH" "command -v inkscape >/dev/null"
|
||||
run_check "inkscape version" "inkscape --version | sed -n '1p'"
|
||||
|
||||
if [ "${{ matrix.format_name }}" = "PDF" ]; then
|
||||
command -v lualatex
|
||||
lualatex --version | sed -n '1p'
|
||||
command -v gs
|
||||
gs --version
|
||||
run_check "lualatex on PATH" "command -v lualatex >/dev/null"
|
||||
run_check "lualatex version" "lualatex --version | sed -n '1p'"
|
||||
run_check "ghostscript (gs) on PATH" "command -v gs >/dev/null"
|
||||
run_check "ghostscript version" "gs --version"
|
||||
fi
|
||||
|
||||
if [ "${{ matrix.format_name }}" = "EPUB" ]; then
|
||||
python3 -c "import PIL; print('Pillow:', PIL.__version__)"
|
||||
run_check "Pillow import" "python3 -c \"import PIL; print('Pillow:', PIL.__version__)\""
|
||||
fi
|
||||
|
||||
echo "✅ Linux toolchain preflight checks passed"
|
||||
@@ -415,32 +434,55 @@ jobs:
|
||||
docker run --rm -e PYTHONPATH=C:\workspace -v "$($PWD.Path):C:\workspace" -w "C:\workspace\book\quarto" ${{ env.CONTAINER_IMAGE }} pwsh -NoLogo -Command "
|
||||
`$ErrorActionPreference = 'Stop'
|
||||
`$PSNativeCommandUseErrorActionPreference = `$true
|
||||
`$required = @('quarto', 'pandoc', 'python', 'python3', 'Rscript', 'inkscape')
|
||||
foreach (`$cmd in `$required) {
|
||||
`$resolved = Get-Command `$cmd -ErrorAction Stop
|
||||
Write-Host \"✅ `$cmd -> `$(`$resolved.Source)\"
|
||||
|
||||
function Invoke-Check {
|
||||
param(
|
||||
[string]`$Name,
|
||||
[scriptblock]`$Action
|
||||
)
|
||||
Write-Host \"▶ preflight: `$Name\"
|
||||
try {
|
||||
& `$Action
|
||||
if (-not `$?) { throw 'Command returned non-zero status' }
|
||||
Write-Host \"✅ preflight: `$Name\"
|
||||
} catch {
|
||||
Write-Error \"❌ preflight failed: `$Name -- `$(`$_.Exception.Message)\"
|
||||
throw
|
||||
}
|
||||
}
|
||||
|
||||
quarto --version | Select-Object -First 1
|
||||
pandoc --version | Select-Object -First 1
|
||||
python --version
|
||||
python3 --version
|
||||
python -c 'import mlsysim,sys; print(\"mlsysim:\", mlsysim.__file__); print(\"python:\", sys.executable)'
|
||||
Rscript --version | Select-Object -First 1
|
||||
inkscape --version | Select-Object -First 1
|
||||
Invoke-Check 'quarto on PATH' { `$resolved = Get-Command quarto -ErrorAction Stop; Write-Host \" quarto -> `$(`$resolved.Source)\" }
|
||||
Invoke-Check 'quarto version' { quarto --version | Select-Object -First 1 }
|
||||
Invoke-Check 'pandoc on PATH' { `$resolved = Get-Command pandoc -ErrorAction Stop; Write-Host \" pandoc -> `$(`$resolved.Source)\" }
|
||||
Invoke-Check 'pandoc version' { pandoc --version | Select-Object -First 1 }
|
||||
Invoke-Check 'python on PATH' { `$resolved = Get-Command python -ErrorAction Stop; Write-Host \" python -> `$(`$resolved.Source)\" }
|
||||
Invoke-Check 'python version' { python --version }
|
||||
Invoke-Check 'python3 on PATH' { `$resolved = Get-Command python3 -ErrorAction Stop; Write-Host \" python3 -> `$(`$resolved.Source)\" }
|
||||
Invoke-Check 'python3 version' { python3 --version }
|
||||
Invoke-Check 'mlsysim import' { python -c 'import mlsysim,sys; print(\"mlsysim:\", mlsysim.__file__); print(\"python:\", sys.executable)' }
|
||||
Invoke-Check 'Rscript on PATH' { `$resolved = Get-Command Rscript -ErrorAction Stop; Write-Host \" Rscript -> `$(`$resolved.Source)\" }
|
||||
Invoke-Check 'Rscript version' { Rscript --version | Select-Object -First 1 }
|
||||
Invoke-Check 'inkscape on PATH' { `$resolved = Get-Command inkscape -ErrorAction Stop; Write-Host \" inkscape -> `$(`$resolved.Source)\" }
|
||||
Invoke-Check 'inkscape version' { inkscape --version | Select-Object -First 1 }
|
||||
|
||||
if ('${{ matrix.format_name }}' -eq 'PDF') {
|
||||
Get-Command lualatex -ErrorAction Stop | Out-Null
|
||||
lualatex --version | Select-Object -First 1
|
||||
`$gsCmd = Get-Command gs -ErrorAction SilentlyContinue
|
||||
if (-not `$gsCmd) { `$gsCmd = Get-Command gswin64c -ErrorAction SilentlyContinue }
|
||||
if (-not `$gsCmd) { throw 'Ghostscript not found (expected gs or gswin64c)' }
|
||||
Write-Host \"✅ ghostscript -> `$(`$gsCmd.Source)\"
|
||||
& `$gsCmd.Source --version
|
||||
Invoke-Check 'lualatex on PATH' { `$resolved = Get-Command lualatex -ErrorAction Stop; Write-Host \" lualatex -> `$(`$resolved.Source)\" }
|
||||
Invoke-Check 'lualatex version' { lualatex --version | Select-Object -First 1 }
|
||||
Invoke-Check 'ghostscript on PATH' {
|
||||
`$gsCmd = Get-Command gs -ErrorAction SilentlyContinue
|
||||
if (-not `$gsCmd) { `$gsCmd = Get-Command gswin64c -ErrorAction SilentlyContinue }
|
||||
if (-not `$gsCmd) { throw 'Ghostscript not found (expected gs or gswin64c)' }
|
||||
Write-Host \" ghostscript -> `$(`$gsCmd.Source)\"
|
||||
}
|
||||
Invoke-Check 'ghostscript version' {
|
||||
`$gsCmd = Get-Command gs -ErrorAction SilentlyContinue
|
||||
if (-not `$gsCmd) { `$gsCmd = Get-Command gswin64c -ErrorAction SilentlyContinue }
|
||||
& `$gsCmd.Source --version
|
||||
}
|
||||
}
|
||||
|
||||
if ('${{ matrix.format_name }}' -eq 'EPUB') {
|
||||
python -c 'import PIL; print(\"Pillow:\", PIL.__version__)'
|
||||
Invoke-Check 'Pillow import' { python -c 'import PIL; print(\"Pillow:\", PIL.__version__)' }
|
||||
}
|
||||
"
|
||||
Write-Host "✅ Windows toolchain preflight checks passed"
|
||||
@@ -632,6 +674,8 @@ jobs:
|
||||
outputs:
|
||||
build_success: ${{ steps.collect.outputs.build_success }}
|
||||
build_target: ${{ steps.collect.outputs.build_target }}
|
||||
failed_instances: ${{ steps.matrix-status.outputs.failed_instances }}
|
||||
cancelled_instances: ${{ steps.matrix-status.outputs.cancelled_instances }}
|
||||
# Volume I artifacts
|
||||
linux_html_vol1_artifact: ${{ steps.collect.outputs.linux_html_vol1_artifact }}
|
||||
linux_pdf_vol1_artifact: ${{ steps.collect.outputs.linux_pdf_vol1_artifact }}
|
||||
@@ -650,6 +694,46 @@ jobs:
|
||||
windows_epub_vol2_artifact: ${{ steps.collect.outputs.windows_epub_vol2_artifact }}
|
||||
|
||||
steps:
|
||||
- name: 🔎 Collect matrix job status details
|
||||
id: matrix-status
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const runId = context.runId;
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
const allJobs = [];
|
||||
for (let page = 1; ; page++) {
|
||||
const resp = await github.rest.actions.listJobsForWorkflowRun({
|
||||
owner,
|
||||
repo,
|
||||
run_id: runId,
|
||||
per_page: 100,
|
||||
page,
|
||||
});
|
||||
allJobs.push(...resp.data.jobs);
|
||||
if (resp.data.jobs.length < 100) break;
|
||||
}
|
||||
|
||||
const matrixBuildJobs = allJobs.filter((job) => {
|
||||
return job.name.includes(" Build ") && job.name.includes("Vol ");
|
||||
});
|
||||
|
||||
const failedJobs = matrixBuildJobs.filter((job) =>
|
||||
["failure", "timed_out", "action_required", "stale"].includes(job.conclusion || "")
|
||||
);
|
||||
const cancelledJobs = matrixBuildJobs.filter((job) =>
|
||||
(job.conclusion || "") === "cancelled"
|
||||
);
|
||||
|
||||
const failedNames = failedJobs.map((job) => job.name);
|
||||
const cancelledNames = cancelledJobs.map((job) => job.name);
|
||||
|
||||
core.info(`Detected ${failedNames.length} failed matrix instance(s).`);
|
||||
core.info(`Detected ${cancelledNames.length} cancelled matrix instance(s).`);
|
||||
core.setOutput("failed_instances", failedNames.join(" | "));
|
||||
core.setOutput("cancelled_instances", cancelledNames.join(" | "));
|
||||
|
||||
- name: 📊 Collect results
|
||||
id: collect
|
||||
run: |
|
||||
@@ -662,6 +746,26 @@ jobs:
|
||||
BUILD_SUCCESS_MSG="❌ Failure"
|
||||
fi
|
||||
|
||||
FAILED_INSTANCES="${{ steps.matrix-status.outputs.failed_instances }}"
|
||||
CANCELLED_INSTANCES="${{ steps.matrix-status.outputs.cancelled_instances }}"
|
||||
if [[ -n "$FAILED_INSTANCES" ]]; then
|
||||
echo "❌ Failed matrix instances: $FAILED_INSTANCES"
|
||||
fi
|
||||
if [[ -n "$CANCELLED_INSTANCES" ]]; then
|
||||
echo "⚠️ Cancelled matrix instances (often fail-fast cascade): $CANCELLED_INSTANCES"
|
||||
fi
|
||||
|
||||
{
|
||||
echo "## Container Matrix Result"
|
||||
echo "- Overall: $BUILD_SUCCESS_MSG"
|
||||
if [[ -n "$FAILED_INSTANCES" ]]; then
|
||||
echo "- Failed instances: $FAILED_INSTANCES"
|
||||
fi
|
||||
if [[ -n "$CANCELLED_INSTANCES" ]]; then
|
||||
echo "- Cancelled instances: $CANCELLED_INSTANCES"
|
||||
fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
# 🔌 API-style artifact name generation (reliable and predictable)
|
||||
TARGET="${{ inputs.target }}"
|
||||
BUILD_TARGET="${{ inputs.build_target }}"
|
||||
|
||||
@@ -630,7 +630,7 @@ Once everything is set up, you'll be able to:
|
||||
### Next Steps
|
||||
1. Read [BINDER.md](BINDER.md) for complete CLI reference
|
||||
2. Check [DEVELOPMENT.md](DEVELOPMENT.md) for development workflow
|
||||
3. Review [contribute.md](contribute.md) for contribution guidelines
|
||||
3. Review [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines
|
||||
4. Join discussions at [GitHub Discussions](https://github.com/harvard-edge/cs249r_book/discussions)
|
||||
|
||||
---
|
||||
@@ -640,8 +640,8 @@ Once everything is set up, you'll be able to:
|
||||
### Documentation
|
||||
- **[BINDER.md](BINDER.md)** - Complete Book Binder CLI reference
|
||||
- **[DEVELOPMENT.md](DEVELOPMENT.md)** - Development guidelines and workflow
|
||||
- **[contribute.md](contribute.md)** - Contribution guidelines
|
||||
- **[PUBLISH_LIVE_WORKFLOW.md](PUBLISH_LIVE_WORKFLOW.md)** - Publishing workflow
|
||||
- **[CONTRIBUTING.md](CONTRIBUTING.md)** - Contribution guidelines
|
||||
- **[`book-publish-live.yml`](../../.github/workflows/book-publish-live.yml)** - Publishing workflow definition
|
||||
|
||||
### Community
|
||||
- **[GitHub Discussions](https://github.com/harvard-edge/cs249r_book/discussions)** - Ask questions and share knowledge
|
||||
@@ -671,7 +671,7 @@ We welcome contributions! The easiest way to get started:
|
||||
4. **Test locally**: `./binder preview <chapter>`
|
||||
5. **Submit a pull request**
|
||||
|
||||
For detailed contribution guidelines, see [contribute.md](contribute.md).
|
||||
For detailed contribution guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@ Containerized Linux Build (5-10 minutes):
|
||||
## Files
|
||||
|
||||
### Core Files
|
||||
- `docker/linux/Dockerfile` - A single Dockerfile for Linux builds.
|
||||
- `docker/linux/README.md` - Linux container documentation
|
||||
- `docker/linux/.dockerignore` - Build exclusions
|
||||
- `docker/windows/Dockerfile` - A single Dockerfile for Windows builds.
|
||||
- `docker/windows/README.md` - Windows container documentation
|
||||
- `docker/windows/.dockerignore` - Build exclusions
|
||||
- `book/docker/linux/Dockerfile` - Linux build container definition
|
||||
- `book/docker/linux/README.md` - Linux container documentation
|
||||
- `book/docker/linux/.dockerignore` - Linux build exclusions
|
||||
- `book/docker/windows/Dockerfile` - Windows build container definition
|
||||
- `book/docker/windows/README.md` - Windows container documentation
|
||||
- `book/docker/windows/.dockerignore` - Windows build exclusions
|
||||
|
||||
### Container Lifecycle
|
||||
1. **Build**: Weekly automatic rebuilds + manual triggers
|
||||
@@ -55,17 +55,24 @@ Containerized Linux Build (5-10 minutes):
|
||||
You can build the containers locally using these commands:
|
||||
- **Linux**:
|
||||
```bash
|
||||
docker build -f docker/linux/Dockerfile -t mlsysbook-linux .
|
||||
docker build -f book/docker/linux/Dockerfile -t mlsysbook-linux .
|
||||
```
|
||||
- **Windows**:
|
||||
```powershell
|
||||
docker build -f docker/windows/Dockerfile -t mlsysbook-windows .
|
||||
docker build -f book/docker/windows/Dockerfile -t mlsysbook-windows .
|
||||
```
|
||||
|
||||
### Manual Build Test
|
||||
```bash
|
||||
# Test containerized build
|
||||
gh workflow run quarto-build-container.yml --field os=ubuntu-latest --field format=html
|
||||
gh workflow run book-build-container.yml \
|
||||
--field build_linux=true \
|
||||
--field build_windows=false \
|
||||
--field build_html=true \
|
||||
--field build_pdf=false \
|
||||
--field build_epub=false \
|
||||
--field build_target=vol1 \
|
||||
--field target=dev
|
||||
```
|
||||
|
||||
### Container Information
|
||||
@@ -78,9 +85,9 @@ gh workflow run quarto-build-container.yml --field os=ubuntu-latest --field form
|
||||
## Workflow Integration
|
||||
|
||||
### Current Workflows
|
||||
- `quarto-build-container.yml` - Containerized version (fast path, recommended)
|
||||
- `build-linux-container.yml` - Linux container management
|
||||
- `build-windows-container.yml` - Windows container management
|
||||
- `book-build-container.yml` - Containerized book build matrix
|
||||
- `infra-container-linux.yml` - Linux container image management
|
||||
- `infra-container-windows.yml` - Windows container image management
|
||||
|
||||
### Migration Status
|
||||
1. **✅ Phase 1**: Containerized builds tested and validated
|
||||
@@ -174,8 +181,8 @@ To build the containers, use the standard `docker build` command:
|
||||
|
||||
```bash
|
||||
# For Linux
|
||||
docker build -f docker/linux/Dockerfile -t mlsysbook-linux .
|
||||
docker build -f book/docker/linux/Dockerfile -t mlsysbook-linux .
|
||||
|
||||
# For Windows
|
||||
docker build -f docker/windows/Dockerfile -t mlsysbook-windows .
|
||||
docker build -f book/docker/windows/Dockerfile -t mlsysbook-windows .
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user