| name: Sync to HF Space | |
| on: | |
| push: | |
| branches: | |
| - beta/sanitized-minimal | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: "Branch or SHA to deploy (default: beta/sanitized-minimal)" | |
| required: false | |
| default: "beta/sanitized-minimal" | |
| concurrency: | |
| group: hf-space-sync | |
| cancel-in-progress: true | |
| jobs: | |
| hf-sync: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout deployment ref | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| lfs: true | |
| ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.ref }} | |
| - name: Resolve checked out ref | |
| id: ref | |
| run: | | |
| BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) | |
| if [ "$BRANCH_NAME" = "HEAD" ]; then | |
| BRANCH_NAME=$(git name-rev --name-only --exclude=tags/* HEAD | sed 's#^remotes/origin/##') | |
| fi | |
| echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT" | |
| echo "Deploying ref: $BRANCH_NAME @ $(git rev-parse --short HEAD)" | |
| - name: Enforce sanitized deployment policy | |
| run: | | |
| set -euo pipefail | |
| echo "Checking required runtime files..." | |
| required_files=( | |
| "README.md" | |
| "Dockerfile" | |
| "requirements-hf.txt" | |
| "gui/streamlit_app.py" | |
| "engine/pipeline_orchestrator.py" | |
| "scripts/docker_entrypoint.sh" | |
| ) | |
| for f in "${required_files[@]}"; do | |
| [ -f "$f" ] || { echo "::error::Missing required file: $f"; exit 1; } | |
| done | |
| echo "Checking blocked paths/files..." | |
| blocked_paths=( | |
| "Docs" | |
| "Docs/audit" | |
| "Vault" | |
| "logs" | |
| "runs" | |
| ".env" | |
| "TODO.md" | |
| "docs/progress.md" | |
| ) | |
| violations=0 | |
| for p in "${blocked_paths[@]}"; do | |
| if [ -e "$p" ]; then | |
| echo "::error::Blocked path present in deployment ref: $p" | |
| violations=1 | |
| fi | |
| done | |
| [ "$violations" -eq 0 ] | |
| echo "Checking for obvious secret patterns..." | |
| if command -v rg >/dev/null 2>&1; then | |
| rg -n --hidden \ | |
| -g '!.git/**' \ | |
| -g '!**/*.md' \ | |
| -e 'AKIA[0-9A-Z]{16}' \ | |
| -e 'ASIA[0-9A-Z]{16}' \ | |
| -e 'ghp_[A-Za-z0-9]{36}' \ | |
| -e 'hf_[A-Za-z0-9]{30,}' \ | |
| -e 'sk_live_[0-9A-Za-z]{20,}' \ | |
| -e '-----BEGIN (RSA|EC|OPENSSH|DSA) PRIVATE KEY-----' \ | |
| -e 'xox[baprs]-[A-Za-z0-9-]{10,}' \ | |
| . && { echo "::error::Potential secret detected"; exit 1; } || true | |
| fi | |
| - name: Check large files (>10 MB) | |
| run: | | |
| set -euo pipefail | |
| large_files=$(find . -not -path './.git/*' -type f -size +10M 2>/dev/null || true) | |
| if [ -n "$large_files" ]; then | |
| echo "::warning::Large files detected (>10MB):" | |
| echo "$large_files" | |
| echo "Consider Git LFS for required large assets." | |
| else | |
| echo "No large files >10MB found." | |
| fi | |
| - name: Push to HF Space | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${HF_TOKEN:-}" ]; then | |
| echo "::error::HF_TOKEN secret is not set. Configure it in GitHub Actions secrets." | |
| exit 1 | |
| fi | |
| git config user.email "ci@github.com" | |
| git config user.name "CI Bot" | |
| git remote remove hf 2>/dev/null || true | |
| git remote add hf "https://moddux:${HF_TOKEN}@huggingface.co/spaces/moddux/mod-osint" | |
| echo "Pushing $(git rev-parse --short HEAD) from '${{ steps.ref.outputs.branch_name }}' -> HF main" | |
| git push hf HEAD:main --force-with-lease | |
| echo "HF deploy push complete." | |
| echo "Logs: https://huggingface.co/spaces/moddux/mod-osint/logs" | |