Skip to content

Update release for fastsurfer 2.4.2 #137

Update release for fastsurfer 2.4.2

Update release for fastsurfer 2.4.2 #137

name: Test Release PR Containers
on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- "releases/*/**.json"
branches:
- master
- main
permissions:
contents: read
pull-requests: write
id-token: write
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
modified-releases: ${{ steps.detect.outputs.modified-releases }}
has-changes: ${{ steps.detect.outputs.has-changes }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect release files
id: detect
run: |
# Get list of modified release JSON files
MODIFIED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep 'releases/.*/.*\.json' || true)
if [ -z "$MODIFIED_FILES" ]; then
echo "has-changes=false" >> $GITHUB_OUTPUT
echo "modified-releases=[]" >> $GITHUB_OUTPUT
exit 0
fi
echo "has-changes=true" >> $GITHUB_OUTPUT
echo "Modified release files:"
echo "$MODIFIED_FILES"
# Extract container name and version from release file paths
RELEASES_JSON="["
FIRST=true
for file in $MODIFIED_FILES; do
# Extract from path like releases/container_name/version.json
CONTAINER_NAME=$(echo "$file" | sed 's|releases/\([^/]*\)/.*|\1|')
VERSION=$(echo "$file" | sed 's|releases/.*/\([^/]*\)\.json|\1|')
if [ "$FIRST" = true ]; then
FIRST=false
else
RELEASES_JSON="$RELEASES_JSON,"
fi
RELEASES_JSON="$RELEASES_JSON{\"name\":\"$CONTAINER_NAME\",\"version\":\"$VERSION\",\"file\":\"$file\"}"
done
RELEASES_JSON="$RELEASES_JSON]"
echo "Generated releases JSON: $RELEASES_JSON"
echo "modified-releases=$RELEASES_JSON" >> $GITHUB_OUTPUT
test-containers:
needs: detect-changes
if: needs.detect-changes.outputs.has-changes == 'true'
runs-on: self-hosted
strategy:
fail-fast: false
matrix:
release: ${{ fromJson(needs.detect-changes.outputs.modified-releases) }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Verify container runtimes
run: |
# Verify that container runtimes are available on self-hosted runner
docker --version
apptainer --version || singularity --version
- name: Debug matrix values
run: |
echo "Matrix release data:"
echo "Name: ${{ matrix.release.name }}"
echo "Version: ${{ matrix.release.version }}"
echo "File: ${{ matrix.release.file }}"
echo "Current directory: $(pwd)"
echo "Release file exists: $([ -f '${{ matrix.release.file }}' ] && echo 'yes' || echo 'no')"
- name: Find test configuration
id: find-tests
run: |
# Look for test configuration in recipe directory
RECIPE_DIR="recipes/${{ matrix.release.name }}"
TEST_CONFIG=""
if [ -f "$RECIPE_DIR/test.yaml" ]; then
TEST_CONFIG="$RECIPE_DIR/test.yaml"
echo "Found test.yaml"
elif [ -f "$RECIPE_DIR/build.yaml" ]; then
TEST_CONFIG="$RECIPE_DIR/build.yaml"
echo "Found build.yaml (will extract tests from build directives)"
else
echo "No test configuration found in $RECIPE_DIR"
ls -la "$RECIPE_DIR" || echo "Recipe directory not found"
fi
echo "test-config=$TEST_CONFIG" >> $GITHUB_OUTPUT
- name: Run container tests
id: test
env:
RECIPE: ${{ matrix.release.name }}
VERSION: ${{ matrix.release.version }}
RELEASE_FILE: ${{ matrix.release.file }}
TEST_CONFIG: ${{ steps.find-tests.outputs.test-config }}
run: |
echo "Testing container: ${RECIPE}:${VERSION}"
echo "Release file: ${RELEASE_FILE}"
echo "Test config: ${TEST_CONFIG}"
mkdir -p builder
python - <<'PY'
from pathlib import Path
import os
from workflows.test_runner import ContainerTestRunner, TestRequest
recipe = os.environ["RECIPE"]
version = os.environ.get("VERSION") or None
release_file = os.environ.get("RELEASE_FILE") or None
test_config = os.environ.get("TEST_CONFIG") or None
runner = ContainerTestRunner()
request = TestRequest(
recipe=recipe,
version=version,
release_file=release_file,
test_config=test_config if test_config else None,
runtime="apptainer",
location="auto",
cleanup=True,
auto_cleanup=False,
verbose=True,
allow_missing_tests=True,
output_dir=Path("builder"),
results_path=Path(f"builder/test-results-{recipe}.json"),
)
outcome = runner.run(request)
print(f"Status: {outcome.status}")
if outcome.reason:
print(f"Reason: {outcome.reason}")
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as handle:
handle.write(f"status={outcome.status}\n")
if outcome.reason:
handle.write(f"reason={outcome.reason}\n")
import sys
sys.exit(0 if outcome.status != "failed" else 1)
PY
continue-on-error: true
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-${{ matrix.release.name }}
path: |
builder/test-results-${{ matrix.release.name }}.json
builder/test-report-${{ matrix.release.name }}.md
- name: Comment test results on PR
if: always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');
const releaseName = '${{ matrix.release.name }}';
const releaseVersion = '${{ matrix.release.version }}';
const reportFile = `builder/test-report-${releaseName}.md`;
let reportContent = `## Test Results for ${releaseName}:${releaseVersion}\n\n`;
try {
if (fs.existsSync(reportFile)) {
reportContent = fs.readFileSync(reportFile, 'utf8');
} else {
reportContent += "❌ Test report not generated - check logs for errors.";
}
} catch (error) {
reportContent += `❌ Error reading test report: ${error.message}`;
}
// Find existing comment for this release
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.data.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes(`Test Results for ${releaseName}:${releaseVersion}`)
);
const commentBody = `${reportContent}\n\n---\n*Automated test results for container: ${releaseName}:${releaseVersion}*`;
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: commentBody
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: commentBody
});
}
summarize-results:
needs: [detect-changes, test-containers]
if: always() && needs.detect-changes.outputs.has-changes == 'true'
runs-on: ubuntu-latest
steps:
- name: Download all test results
uses: actions/download-artifact@v4
with:
pattern: test-results-*
merge-multiple: true
path: test-results
- name: Summarize test results
id: summary
run: |
cd test-results
TOTAL_RECIPES=0
PASSED_RECIPES=0
FAILED_RECIPES=0
for file in test-results-*.json; do
if [ -f "$file" ]; then
TOTAL_RECIPES=$((TOTAL_RECIPES + 1))
# Check if any tests failed
FAILED_TESTS=$(jq '.failed' "$file" 2>/dev/null || echo "1")
if [ "$FAILED_TESTS" -eq 0 ]; then
PASSED_RECIPES=$((PASSED_RECIPES + 1))
else
FAILED_RECIPES=$((FAILED_RECIPES + 1))
fi
fi
done
echo "total=$TOTAL_RECIPES" >> $GITHUB_OUTPUT
echo "passed=$PASSED_RECIPES" >> $GITHUB_OUTPUT
echo "failed=$FAILED_RECIPES" >> $GITHUB_OUTPUT
echo "Test Summary: $PASSED_RECIPES/$TOTAL_RECIPES recipes passed"
- name: Set PR status
uses: actions/github-script@v7
with:
script: |
const total = parseInt('${{ steps.summary.outputs.total }}');
const passed = parseInt('${{ steps.summary.outputs.passed }}');
const failed = parseInt('${{ steps.summary.outputs.failed }}');
let summary = `## 🧪 Container Test Summary\n\n`;
summary += `**Results:** ${passed}/${total} recipes passed\n\n`;
if (failed > 0) {
summary += `❌ ${failed} recipe(s) failed testing\n`;
summary += `Please check the individual test results above for details.\n\n`;
} else {
summary += `✅ All modified containers passed testing!\n\n`;
}
summary += `*This comment will be updated as tests complete.*`;
// Find existing summary comment
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const summaryComment = comments.data.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('Container Test Summary')
);
if (summaryComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: summaryComment.id,
body: summary
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: summary
});
}
// Fail the workflow if any tests failed
if (failed > 0) {
core.setFailed(`${failed} out of ${total} container tests failed`);
}