Skip to content

docs(lessons): credit Prof Benjamin Abel #1

docs(lessons): credit Prof Benjamin Abel

docs(lessons): credit Prof Benjamin Abel #1

Workflow file for this run

# ----------------------------------------------------------------------------
# Publish SolverForge uc-* apps to Hugging Face Spaces solverforge-*.
#
# Canonical release flow:
# 1. Cut an app-scoped tag such as solverforge-hospital@1.0.2.
# 2. This workflow validates the tag against uc-hospital/Cargo.toml,
# uc-hospital/Cargo.lock, and uc-hospital/CHANGELOG.md.
# 3. The matching uc-* subtree is pushed to the matching Hugging Face Space.
#
# Required secrets : HF_TOKEN (HF token with write role on the spaces)
# Required variables: HF_ORGANIZATION (HF org or username, e.g. SolverForge)
# ----------------------------------------------------------------------------
name: Sync SolverForge Use Cases
on:
push:
tags:
- "solverforge-deliveries@*"
- "solverforge-fsr@*"
- "solverforge-hospital@*"
- "solverforge-lessons@*"
workflow_dispatch:
inputs:
target:
description: >
Folder to sync (e.g., "uc-hospital") or "all" to sync everything.
Manual syncs are for recovery; app tags are the release path.
required: true
default: "all"
concurrency:
group: hf-sync-${{ github.ref }}
cancel-in-progress: false
jobs:
detect:
name: Resolve release target
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.build-matrix.outputs.matrix }}
empty: ${{ steps.build-matrix.outputs.empty }}
release_tag: ${{ steps.release-tag.outputs.tag_name }}
release_version: ${{ steps.release-tag.outputs.version }}
release_folder: ${{ steps.release-tag.outputs.folder }}
steps:
- name: Checkout full history
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate app release tag
id: release-tag
if: github.event_name == 'push'
run: node scripts/verify-usecase-release-tag.cjs "${GITHUB_REF_NAME}"
- name: Build matrix
id: build-matrix
env:
EVENT_NAME: ${{ github.event_name }}
RELEASE_FOLDER: ${{ steps.release-tag.outputs.folder }}
TARGET: ${{ github.event.inputs.target }}
run: |
set -euo pipefail
OPEN_SOURCE_FOLDERS=$(cat <<'FOLDERS'
uc-deliveries
uc-fsr
uc-hospital
uc-lessons
FOLDERS
)
filter_open_source() {
while IFS= read -r folder; do
[ -n "$folder" ] || continue
if printf '%s\n' "$OPEN_SOURCE_FOLDERS" | grep -Fxq "$folder"; then
printf '%s\n' "$folder"
else
echo "::error::Folder '$folder' is not an open-source SolverForge use case."
exit 1
fi
done
}
if [ "$EVENT_NAME" = "push" ]; then
FOLDERS=$(printf '%s\n' "$RELEASE_FOLDER" | filter_open_source)
elif [ "${TARGET:-all}" = "all" ]; then
FOLDERS="$OPEN_SOURCE_FOLDERS"
else
[ -d "$TARGET" ] || { echo "::error::Folder '$TARGET' not found."; exit 1; }
FOLDERS=$(printf '%s\n' "$TARGET" | filter_open_source)
fi
if [ -z "$FOLDERS" ]; then
echo "::notice::No uc-* folder selected, nothing to sync."
echo "matrix={\"folder\":[]}" >> "$GITHUB_OUTPUT"
echo "empty=true" >> "$GITHUB_OUTPUT"
else
JSON=$(echo "$FOLDERS" | jq -R . | jq -sc '{"folder":.}')
echo "Detected folders: $FOLDERS"
echo "matrix=$JSON" >> "$GITHUB_OUTPUT"
echo "empty=false" >> "$GITHUB_OUTPUT"
fi
sync:
name: Sync ${{ matrix.folder }}
needs: detect
if: needs.detect.outputs.empty == 'false'
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJson(needs.detect.outputs.matrix) }}
fail-fast: false
steps:
- name: Checkout full history
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure git
run: |
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install Node dependencies
uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- name: Install browser test dependencies
run: make install-e2e
- name: Validate release tag
if: github.event_name == 'push'
run: make release-ci APP="${{ matrix.folder }}" RELEASE_VERSION="${{ needs.detect.outputs.release_version }}"
- name: Validate manual sync target
if: github.event_name != 'push'
run: make release-ci APP="${{ matrix.folder }}"
- name: git subtree split to temporary branch
id: split
run: |
FOLDER="${{ matrix.folder }}"
BRANCH="hf-push/${FOLDER}/${GITHUB_RUN_ID}"
echo "Extracting $FOLDER history..."
git subtree split --prefix="$FOLDER" -b "$BRANCH"
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
- name: Push to Hugging Face Space
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
HF_ORGANIZATION: ${{ vars.HF_ORGANIZATION }}
run: |
FOLDER="${{ matrix.folder }}"
BRANCH="${{ steps.split.outputs.branch }}"
SPACE_NAME=$(echo "$FOLDER" | sed 's/^uc/solverforge/')
SPACE="${HF_ORGANIZATION}/${SPACE_NAME}"
HF_REMOTE="https://user:${HF_TOKEN}@huggingface.co/spaces/${SPACE}"
echo "Pushing $FOLDER to hf.co/spaces/$SPACE (main branch)..."
git push "$HF_REMOTE" "${BRANCH}:main" --force
echo "::notice title=Sync OK::$FOLDER synced to hf.co/spaces/$SPACE"
- name: Push release tag to Hugging Face Space
if: github.event_name == 'push'
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
HF_ORGANIZATION: ${{ vars.HF_ORGANIZATION }}
RELEASE_TAG: ${{ needs.detect.outputs.release_tag }}
run: |
FOLDER="${{ matrix.folder }}"
BRANCH="${{ steps.split.outputs.branch }}"
SPACE_NAME=$(echo "$FOLDER" | sed 's/^uc/solverforge/')
SPACE="${HF_ORGANIZATION}/${SPACE_NAME}"
HF_REMOTE="https://user:${HF_TOKEN}@huggingface.co/spaces/${SPACE}"
git tag -f "$RELEASE_TAG" "$BRANCH"
git push "$HF_REMOTE" "refs/tags/${RELEASE_TAG}:refs/tags/${RELEASE_TAG}" --force
echo "::notice title=Tag Sync OK::$RELEASE_TAG synced to hf.co/spaces/$SPACE"
- name: Cleanup temporary branch
if: always()
run: |
BRANCH="${{ steps.split.outputs.branch }}"
git branch -D "$BRANCH" 2>/dev/null || true