Skip to content

Docker Entrypoint for Secret Mounts #107

Docker Entrypoint for Secret Mounts

Docker Entrypoint for Secret Mounts #107

Workflow file for this run

name: Deploy Documentation
on:
push:
branches: [main]
paths:
- 'docs/**'
- 'src/**'
- 'assets/**'
- '.github/workflows/deploy-docs.yml'
workflow_dispatch:
permissions:
contents: write
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.13'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
pip install Pillow
- name: Run Tests (I18n Integrity)
run: python -m pytest tests/test_i18n_integrity.py
- name: Pre-install Addons (Web)
run: |
# Helper to unzip addons into source tree so they are bundled as python packages
# Must ensure destination exists
mkdir -p src/switchcraft/addons
python -c "import zipfile, os, glob; from pathlib import Path; [zipfile.ZipFile(z).extractall(Path('src/switchcraft/addons') / Path(z).stem.replace('switchcraft_', '')) for z in glob.glob('src/switchcraft/assets/addons/*.zip')]"
- name: Build Web Demo
run: |
# Bake translations for WASM
python scripts/prepare_web_dist.py --bake
# Create an isolated build directory
mkdir -p build_web
cp -r src/switchcraft build_web/
# Create entry point inside build_web
cat > build_web/web_entry.py <<EOF
import os
import sys
print("DEBUG: WEB ENTRY RELOADED")
print(f"BUILD_TIME: $(date)")
# ============================================================
# CRITICAL: Patch ssl module BEFORE any urllib3 import
# Pyodide injects a MagicMock for ssl, breaking urllib3's version check
# ============================================================
if sys.platform == "emscripten":
import types
# Force patch if 'ssl' is a MagicMock or missing version info
should_patch_ssl = "ssl" not in sys.modules
if not should_patch_ssl:
ssl_mod = sys.modules["ssl"]
# Check if it's a MagicMock
if "Mock" in str(type(ssl_mod)) or not hasattr(ssl_mod, "OPENSSL_VERSION_INFO"):
should_patch_ssl = True
else:
try:
if not isinstance(ssl_mod.OPENSSL_VERSION_INFO, tuple):
should_patch_ssl = True
except (AttributeError, TypeError):
should_patch_ssl = True
if should_patch_ssl:
ssl_mock = types.ModuleType("ssl")
ssl_mock.SSLContext = type("SSLContext", (), {"__init__": lambda *a, **kw: None})
ssl_mock.PROTOCOL_TLS = 2
ssl_mock.PROTOCOL_TLS_CLIENT = 16
ssl_mock.create_default_context = lambda *a, **kw: None
ssl_mock.HAS_SNI = True
ssl_mock.CERT_NONE = 0
ssl_mock.CERT_OPTIONAL = 1
ssl_mock.CERT_REQUIRED = 2
ssl_mock.OPENSSL_VERSION_NUMBER = 0x101010CF
ssl_mock.OPENSSL_VERSION = "OpenSSL 1.1.1 (Pyodide Mock)"
ssl_mock.OPENSSL_VERSION_INFO = (1, 1, 1, 15, 15)
ssl_mock.HAS_NEVER_CHECK_COMMON_NAME = True
sys.modules["ssl"] = ssl_mock
print("DEBUG: SSL module patched successfully")
# Now safe to patch pyodide_http
try:
import pyodide_http
pyodide_http.patch_all()
print("DEBUG: pyodide_http patched successfully")
except ImportError:
pass
# ============================================================
# GLOBAL THREADING MONKEYPATCH FOR WASM
# ============================================================
import threading
import asyncio
class WASMThread(threading.Thread):
"""Bridge between standard threading and WASM-friendly asyncio tasks."""
def start(self):
async def _bridge():
try:
self.run()
except Exception as e:
print(f"ERROR in WASM background thread '{self.name}': {e}")
try:
asyncio.create_task(_bridge())
except RuntimeError:
self.run()
threading.Thread = WASMThread
print("DEBUG: Global threading monkeypatch applied")
# ============================================================
# Normal imports AFTER ssl patching
# ============================================================
import flet as ft
# Ensure current dir is in path
sys.path.insert(0, os.getcwd())
import switchcraft
switchcraft.IS_DEMO = True
import switchcraft.main
if __name__ == "__main__":
# Use ft.run for modern Flet (0.80.0+)
ft.run(switchcraft.main.main, assets_dir="assets")
EOF
# Generate dynamic splash screen
VERSION=$(python -c "import src.switchcraft as s; print(s.__version__)")
echo "Generating Splash for Version: $VERSION"
python scripts/generate_splash.py --version "$VERSION" --output "build_web/switchcraft/assets/splash.png"
# Manually create requirements.txt
cat > build_web/requirements.txt <<EOF
flet==0.80.3
pyodide-http
pefile
olefile
requests
click
rich
PyYAML
defusedxml
PyJWT
anyio>=4.12.1
EOF
# Publish
cd build_web
cp web_entry.py ../web_entry.py.patched
flet publish web_entry.py --app-name "SwitchCraft Demo" --app-short-name "SwitchCraft" --app-description "SwitchCraft Web Demo" --base-url "/demo/" --distpath ../dist --assets switchcraft/assets
# Overwrite with patched entry
cd ..
cp web_entry.py.patched dist/web_entry.py
# Customize Flet loading screen and Overwrite Icons
python scripts/prepare_web_dist.py --patch dist
cp src/switchcraft/assets/icon-192.png dist/icons/icon-192.png
cp src/switchcraft/assets/icon-512.png dist/icons/icon-512.png
cp src/switchcraft/assets/favicon.png dist/favicon.png
cp src/switchcraft/assets/apple-touch-icon.png dist/apple-touch-icon.png || true
# Deploy to docs public
rm -rf docs/public/demo
mkdir -p docs/public/demo
cp -r dist/* docs/public/demo/
# Clean up
rm -rf dist
rm -rf build_web
rm -f web_entry.py.patched
shell: bash
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24.13.0
- name: Install dependencies
run: |
cd docs
if [ -f package-lock.json ]; then
npm ci
else
npm install vitepress vue
fi
- name: Build with VitePress
run: |
cd docs
npx vitepress build
env:
BASE_URL: /
- name: Upload to gh-pages
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: docs/.vitepress/dist
branch: gh-pages
clean: true
# Preserve the pr-preview directory
clean-exclude: |
pr-preview/