Merge pull request #14 from SKaiNET-developers/feature/MNIST-rework-0… #43
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build & Deploy Web Apps to GitHub Pages | |
| on: | |
| push: | |
| tags: | |
| - '*' # Push events to matching *, i.e. 1.0, 20.15.10 | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| concurrency: | |
| group: "pages" | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repo | |
| uses: actions/checkout@v4 | |
| - name: Set up JDK | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: "22" | |
| - name: Make gradlew executable (if present) | |
| run: | | |
| if [ -f "./gradlew" ]; then | |
| chmod +x ./gradlew | |
| fi | |
| - name: Build all web apps (per subproject) | |
| run: | | |
| set -e | |
| # Optional: ensure jq exists (Ubuntu images usually have it already) | |
| if ! command -v jq >/dev/null 2>&1; then | |
| sudo apt-get update | |
| sudo apt-get install -y jq | |
| fi | |
| # Find all webapp.json files | |
| while IFS= read -r meta; do | |
| app_dir="$(dirname "$meta")" | |
| build_cmd="$(jq -r '.build.command // empty' "$meta")" | |
| if [ -z "$build_cmd" ]; then | |
| echo "[INFO] $meta has no build.command, skipping build step." | |
| continue | |
| fi | |
| echo "[INFO] Building app in $app_dir with: $build_cmd" | |
| ( | |
| cd "$app_dir" | |
| # Execute the command as defined in webapp.json | |
| set -e | |
| eval "$build_cmd" | |
| ) | |
| done < <(find . -name webapp.json -print) | |
| - name: Prepare static site folder (scan apps & generate index) | |
| run: | | |
| set -e | |
| SITE_DIR="site" | |
| mkdir -p "$SITE_DIR" | |
| python << 'PYTHON_EOF' | |
| import json, os, shutil, html | |
| ROOT = os.getcwd() | |
| SITE_DIR = os.path.join(ROOT, "site") | |
| os.makedirs(SITE_DIR, exist_ok=True) | |
| release_tag = os.environ.get("GITHUB_REF_NAME", "") | |
| apps = [] | |
| # Walk repo and look for webapp.json | |
| for dirpath, dirnames, filenames in os.walk(ROOT): | |
| if "webapp.json" not in filenames: | |
| continue | |
| meta_path = os.path.join(dirpath, "webapp.json") | |
| with open(meta_path, "r", encoding="utf-8") as f: | |
| try: | |
| meta = json.load(f) | |
| except Exception as e: | |
| print(f"[WARN] Cannot parse {meta_path}: {e}") | |
| continue | |
| app_id = meta.get("id") | |
| name = meta.get("name", app_id) | |
| description = meta.get("description", "") | |
| screenshot_rel = meta.get("screenshot") | |
| # Only support multi dist directories; ignore legacy single distDir | |
| dist_dirs_rel = meta.get("distDirs", []) if isinstance(meta.get("distDirs"), list) else [] | |
| if not app_id: | |
| print(f"[WARN] {meta_path} missing 'id', skipping.") | |
| continue | |
| # If no distDirs provided, just register app for index but skip copy | |
| if not dist_dirs_rel: | |
| print(f"[INFO] {meta_path} has no 'distDirs'; will only list app on index (no files copied).") | |
| project_root = dirpath | |
| # Helper: merge-copy contents of src into dst (overwrite on conflicts) | |
| def merge_copy(src: str, dst: str): | |
| if not os.path.exists(src): | |
| return | |
| os.makedirs(dst, exist_ok=True) | |
| for name in os.listdir(src): | |
| s = os.path.join(src, name) | |
| d = os.path.join(dst, name) | |
| if os.path.isdir(s): | |
| merge_copy(s, d) | |
| else: | |
| os.makedirs(os.path.dirname(d), exist_ok=True) | |
| try: | |
| shutil.copy2(s, d) | |
| except Exception as e: | |
| print(f"[WARN] Failed to copy {s} -> {d}: {e}") | |
| # Copy contents of every configured distDir into site/<id>/ (no validation) | |
| target_app_dir = os.path.join(SITE_DIR, app_id) | |
| if os.path.exists(target_app_dir): | |
| shutil.rmtree(target_app_dir) | |
| os.makedirs(target_app_dir, exist_ok=True) | |
| for rel in dist_dirs_rel: | |
| src_dir = os.path.join(project_root, rel) | |
| if not os.path.exists(src_dir): | |
| print(f"[INFO] distDir does not exist (skipped): {src_dir}") | |
| continue | |
| print(f"[OK] Merging distDir for {app_id}: {src_dir} -> {target_app_dir}") | |
| merge_copy(src_dir, target_app_dir) | |
| screenshot_target = None | |
| if screenshot_rel: | |
| screenshot_src = os.path.join(project_root, screenshot_rel) | |
| if os.path.exists(screenshot_src): | |
| screenshot_name = os.path.basename(screenshot_src) | |
| screenshot_target = f"{app_id}/{screenshot_name}" | |
| shutil.copy2(screenshot_src, os.path.join(target_app_dir, screenshot_name)) | |
| else: | |
| print(f"[WARN] Screenshot configured but not found: {screenshot_src}") | |
| apps.append({ | |
| "id": app_id, | |
| "name": name or app_id, | |
| "description": description, | |
| "screenshot": screenshot_target | |
| }) | |
| # Generate root index.html | |
| apps.sort(key=lambda a: a["id"]) | |
| index_html_path = os.path.join(SITE_DIR, "index.html") | |
| with open(index_html_path, "w", encoding="utf-8") as f: | |
| f.write("<!DOCTYPE html>\n<html lang='en'>\n<head>\n") | |
| f.write(" <meta charset='UTF-8'>\n") | |
| f.write(" <meta name='viewport' content='width=device-width, initial-scale=1.0'>\n") | |
| f.write(" <title>KMP Web Apps</title>\n") | |
| f.write(" <style>\n") | |
| f.write(" body { font-family: system-ui, sans-serif; max-width: 960px; margin: 2rem auto; padding: 0 1rem; }\n") | |
| f.write(" h1 { margin-bottom: 0.25rem; }\n") | |
| f.write(" .tag { color: #666; font-size: 0.9rem; margin-bottom: 1.5rem; }\n") | |
| f.write(" .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1rem; }\n") | |
| f.write(" .card { border: 1px solid #ddd; border-radius: 0.75rem; padding: 1rem; text-decoration: none; color: inherit; background: #fafafa; transition: box-shadow 0.15s, transform 0.15s; }\n") | |
| f.write(" .card:hover { box-shadow: 0 6px 18px rgba(0,0,0,0.08); transform: translateY(-2px); }\n") | |
| f.write(" .card h2 { margin-top: 0; margin-bottom: 0.25rem; font-size: 1.1rem; }\n") | |
| f.write(" .card p { margin: 0.25rem 0 0.5rem 0; font-size: 0.9rem; color: #555; }\n") | |
| f.write(" .badge { display: inline-block; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em; color: #555; background: #eaeaea; padding: 0.1rem 0.45rem; border-radius: 999px; margin-bottom: 0.3rem; }\n") | |
| f.write(" .screenshot { width: 100%; border-radius: 0.5rem; margin-top: 0.5rem; border: 1px solid #e0e0e0; object-fit: cover; max-height: 180px; }\n") | |
| f.write(" .empty { color: #777; font-style: italic; }\n") | |
| f.write(" </style>\n") | |
| f.write("</head>\n<body>\n") | |
| f.write(" <h1>KMP Web Apps</h1>\n") | |
| if release_tag: | |
| f.write(f" <div class='tag'>Deployed from release <strong>{html.escape(release_tag)}</strong></div>\n") | |
| if not apps: | |
| f.write(" <p class='empty'>No web apps detected for this release (no valid <code>webapp.json</code> with a working <code>index.html</code>).</p>\n") | |
| else: | |
| f.write(" <div class='grid'>\n") | |
| for app in apps: | |
| href = f"./{app['id']}/" | |
| name = html.escape(app["name"]) | |
| desc = html.escape(app["description"] or "") | |
| f.write(f" <a class='card' href='{href}'>\n") | |
| f.write(f" <div class='badge'>/{html.escape(app['id'])}</div>\n") | |
| f.write(f" <h2>{name}</h2>\n") | |
| if desc: | |
| f.write(f" <p>{desc}</p>\n") | |
| if app["screenshot"]: | |
| f.write(f" <img class='screenshot' src='{html.escape(app['screenshot'])}' alt='Screenshot of {name}' />\n") | |
| f.write(" </a>\n") | |
| f.write(" </div>\n") | |
| f.write("</body>\n</html>\n") | |
| print(f"[OK] Root index generated at {index_html_path}") | |
| PYTHON_EOF | |
| - name: Upload artifact for GitHub Pages | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: site | |
| deploy: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| steps: | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 |