Workflow1111 / deploy_space.py
ysharma's picture
ysharma HF Staff
Workflow1111 — Automatic1111-style diffusion studio on gr.Workflow
6fe13ea verified
Raw
History Blame Contribute Delete
4.68 kB
"""
Stage and deploy Workflow1111 to a Hugging Face Space.
python apps/05_workflow1111/deploy_space.py --stage # build staging dir only
python apps/05_workflow1111/deploy_space.py --push # create + upload
The Space must be **public**: the reference-node sample images are referenced by
their public `.../resolve/main/samples/...` URL, which is the only default shape
the canvas renders *and* a remote inference provider can fetch. A private Space
would serve 401s for those URLs and the defaults would come up blank.
OAuth is enabled in the README frontmatter, so each visitor signs in and their
own token pays for their own inference — the Space ships no token of its own.
"""
import argparse
import os
import shutil
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.abspath(os.path.join(HERE, "..", ".."))
STAGE = os.path.join(HERE, "_space")
SPACE_ID = os.environ.get("WORKFLOW1111_SPACE", "ysharma/Workflow1111")
SHIP = [
"test_api.py",
"deploy_space.py",
"app.py",
"nodes.py",
"workflow.json",
"requirements.txt",
"build_workflow.py",
"layout.json",
"make_samples.py",
"test_nodes.py",
"test_pipelines.py",
]
FRONTMATTER = """---
title: Workflow1111 Diffusion Studio
emoji: 🎨
colorFrom: indigo
colorTo: purple
sdk: gradio
sdk_version: 6.22.0
app_file: app.py
pinned: false
license: mit
hf_oauth: true
hf_oauth_scopes:
- inference-api
short_description: Automatic1111-style studio on one gr.Workflow canvas
---
> **Sign in with Hugging Face** (button at the top right) before running
> anything. This Space carries no token of its own — every `model`, `space` and
> inference-calling `fn` node runs on *your* token and your own inference quota.
"""
def read_token():
for path in (os.path.join(ROOT, "hf-write-token.txt"),):
if os.path.exists(path):
with open(path, encoding="utf-8") as f:
tok = f.read().strip()
if tok:
return tok
tok = os.environ.get("HF_TOKEN")
if tok:
return tok
raise SystemExit("No write token found (hf-write-token.txt or HF_TOKEN).")
def stage():
if os.path.exists(STAGE):
shutil.rmtree(STAGE)
os.makedirs(STAGE)
for name in SHIP:
src = os.path.join(HERE, name)
if not os.path.exists(src):
raise SystemExit(f"missing file to ship: {name}")
shutil.copy2(src, os.path.join(STAGE, name))
samples_src = os.path.join(HERE, "samples")
samples_dst = os.path.join(STAGE, "samples")
os.makedirs(samples_dst)
for name in sorted(os.listdir(samples_src)):
if name.startswith("_"):
continue
shutil.copy2(os.path.join(samples_src, name), os.path.join(samples_dst, name))
with open(os.path.join(HERE, "README.md"), encoding="utf-8") as f:
body = f.read()
with open(os.path.join(STAGE, "README.md"), "w", encoding="utf-8") as f:
f.write(FRONTMATTER + body)
# Nothing secret may ever reach the Space.
for dirpath, _dirs, files in os.walk(STAGE):
for name in files:
low = name.lower()
if "token" in low and low.endswith(".txt"):
raise SystemExit(f"refusing to ship {name}")
total = sum(os.path.getsize(os.path.join(d, f))
for d, _s, fs in os.walk(STAGE) for f in fs)
files = [os.path.relpath(os.path.join(d, f), STAGE)
for d, _s, fs in os.walk(STAGE) for f in fs]
print(f"staged {len(files)} files ({total // 1024} KB) in {os.path.relpath(STAGE, os.getcwd())}")
for f in sorted(files):
size = os.path.getsize(os.path.join(STAGE, f)) // 1024
print(f" {f:34} {size:>5} KB")
return files
def push():
from huggingface_hub import HfApi
api = HfApi(token=read_token())
who = api.whoami()
print(f"authenticated as: {who.get('name')}")
url = api.create_repo(repo_id=SPACE_ID, repo_type="space", space_sdk="gradio",
exist_ok=True, private=False)
print(f"space repo ready: {url}")
api.upload_folder(
folder_path=STAGE,
repo_id=SPACE_ID,
repo_type="space",
commit_message="Workflow1111 — Automatic1111-style diffusion studio on gr.Workflow",
)
print(f"uploaded → https://huggingface.co/spaces/{SPACE_ID}")
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("--stage", action="store_true")
ap.add_argument("--push", action="store_true")
args = ap.parse_args()
if not (args.stage or args.push):
ap.error("pass --stage or --push")
stage()
if args.push:
push()