Spaces:
Running
Fix transformers.js deployment: Upload all files (html, js, css) individually
Browse filesIssue: Transformers.js apps were not deploying correctly - CSS and JavaScript
files were missing from the deployed space.
Root cause: backend_deploy.py was using upload_folder() for all file types,
but transformers.js requires individual file uploads like the original deploy.py.
Changes to backend_deploy.py:
1. Added validation for transformers.js files:
- Check all three files exist (index.html, index.js, style.css)
- Return error if any file is missing
2. Template space duplication (NEW):
- Use duplicate_space() from static-templates/transformers.js template
- Matches original deploy.py behavior
- Falls back to create_repo() if duplication fails
3. Individual file upload for transformers.js:
- Added use_individual_uploads flag
- Upload each file separately using api.upload_file()
- NOT using upload_folder() for transformers.js
4. Retry logic with error handling:
- 3 attempts per file
- 2-second delay between retries
- Proper permission error detection
- User-friendly error messages
5. Separate commit messages:
- Each file gets its own commit: "{message} - {filename}"
- Preserves git history for each file
Upload flow for transformers.js:
1. Parse code to extract index.html, index.js, style.css
2. Validate all three files are present
3. Duplicate template space (or create static space)
4. Upload index.html individually (with retry)
5. Upload index.js individually (with retry)
6. Upload style.css individually (with retry)
7. Add anycoder tag to README
This matches the original deploy.py pattern where transformers.js files
are uploaded one at a time rather than as a folder.
Other languages (html, gradio, streamlit, react) continue to use
upload_folder() which works fine for them.
- backend_deploy.py +82 -14
|
@@ -321,14 +321,22 @@ def deploy_to_huggingface_space(
|
|
| 321 |
|
| 322 |
# Parse code based on language
|
| 323 |
app_port = None # Track if we need app_port for Docker spaces
|
|
|
|
| 324 |
|
| 325 |
if language == "transformers.js":
|
| 326 |
files = parse_transformers_js_output(code)
|
| 327 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
# Write transformers.js files
|
| 329 |
for filename, content in files.items():
|
| 330 |
(temp_path / filename).write_text(content, encoding='utf-8')
|
| 331 |
|
|
|
|
|
|
|
|
|
|
| 332 |
elif language == "html":
|
| 333 |
html_code = parse_html_code(code)
|
| 334 |
(temp_path / "index.html").write_text(html_code, encoding='utf-8')
|
|
@@ -387,13 +395,36 @@ def deploy_to_huggingface_space(
|
|
| 387 |
# Create the space (only for new deployments)
|
| 388 |
if not is_update:
|
| 389 |
try:
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 397 |
except Exception as e:
|
| 398 |
if "already exists" in str(e).lower():
|
| 399 |
# Space exists, treat as update
|
|
@@ -401,17 +432,54 @@ def deploy_to_huggingface_space(
|
|
| 401 |
else:
|
| 402 |
return False, f"Failed to create space: {str(e)}", None
|
| 403 |
|
| 404 |
-
# Upload
|
| 405 |
if not commit_message:
|
| 406 |
commit_message = "Update from anycoder" if is_update else "Deploy from anycoder"
|
| 407 |
|
| 408 |
try:
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
except Exception as e:
|
| 416 |
return False, f"Failed to upload files: {str(e)}", None
|
| 417 |
|
|
|
|
| 321 |
|
| 322 |
# Parse code based on language
|
| 323 |
app_port = None # Track if we need app_port for Docker spaces
|
| 324 |
+
use_individual_uploads = False # Flag for transformers.js
|
| 325 |
|
| 326 |
if language == "transformers.js":
|
| 327 |
files = parse_transformers_js_output(code)
|
| 328 |
|
| 329 |
+
# Validate all three files are present
|
| 330 |
+
if not files.get('index.html') or not files.get('index.js') or not files.get('style.css'):
|
| 331 |
+
return False, "Error: Could not parse transformers.js output. Missing index.html, index.js, or style.css", None
|
| 332 |
+
|
| 333 |
# Write transformers.js files
|
| 334 |
for filename, content in files.items():
|
| 335 |
(temp_path / filename).write_text(content, encoding='utf-8')
|
| 336 |
|
| 337 |
+
# For transformers.js, we'll upload files individually (not via upload_folder)
|
| 338 |
+
use_individual_uploads = True
|
| 339 |
+
|
| 340 |
elif language == "html":
|
| 341 |
html_code = parse_html_code(code)
|
| 342 |
(temp_path / "index.html").write_text(html_code, encoding='utf-8')
|
|
|
|
| 395 |
# Create the space (only for new deployments)
|
| 396 |
if not is_update:
|
| 397 |
try:
|
| 398 |
+
if language == "transformers.js":
|
| 399 |
+
# For transformers.js, duplicate the template space
|
| 400 |
+
from huggingface_hub import duplicate_space
|
| 401 |
+
|
| 402 |
+
try:
|
| 403 |
+
duplicate_space(
|
| 404 |
+
from_id="static-templates/transformers.js",
|
| 405 |
+
to_id=repo_id,
|
| 406 |
+
token=token,
|
| 407 |
+
exist_ok=True
|
| 408 |
+
)
|
| 409 |
+
except Exception as e:
|
| 410 |
+
# If template duplication fails, fall back to regular create
|
| 411 |
+
print(f"[Deploy] Template duplication failed, creating regular static space: {e}")
|
| 412 |
+
api.create_repo(
|
| 413 |
+
repo_id=repo_id,
|
| 414 |
+
repo_type="space",
|
| 415 |
+
space_sdk=sdk,
|
| 416 |
+
private=private,
|
| 417 |
+
exist_ok=False
|
| 418 |
+
)
|
| 419 |
+
else:
|
| 420 |
+
# For other languages, create space normally
|
| 421 |
+
api.create_repo(
|
| 422 |
+
repo_id=repo_id,
|
| 423 |
+
repo_type="space",
|
| 424 |
+
space_sdk=sdk,
|
| 425 |
+
private=private,
|
| 426 |
+
exist_ok=False
|
| 427 |
+
)
|
| 428 |
except Exception as e:
|
| 429 |
if "already exists" in str(e).lower():
|
| 430 |
# Space exists, treat as update
|
|
|
|
| 432 |
else:
|
| 433 |
return False, f"Failed to create space: {str(e)}", None
|
| 434 |
|
| 435 |
+
# Upload files
|
| 436 |
if not commit_message:
|
| 437 |
commit_message = "Update from anycoder" if is_update else "Deploy from anycoder"
|
| 438 |
|
| 439 |
try:
|
| 440 |
+
if use_individual_uploads:
|
| 441 |
+
# For transformers.js, upload each file individually (matches original deploy.py)
|
| 442 |
+
import time
|
| 443 |
+
files_to_upload = ["index.html", "index.js", "style.css"]
|
| 444 |
+
|
| 445 |
+
max_attempts = 3
|
| 446 |
+
for filename in files_to_upload:
|
| 447 |
+
file_path = temp_path / filename
|
| 448 |
+
if not file_path.exists():
|
| 449 |
+
return False, f"Failed to upload: {filename} not found", None
|
| 450 |
+
|
| 451 |
+
# Upload with retry logic (like original)
|
| 452 |
+
success = False
|
| 453 |
+
last_error = None
|
| 454 |
+
|
| 455 |
+
for attempt in range(max_attempts):
|
| 456 |
+
try:
|
| 457 |
+
api.upload_file(
|
| 458 |
+
path_or_fileobj=str(file_path),
|
| 459 |
+
path_in_repo=filename,
|
| 460 |
+
repo_id=repo_id,
|
| 461 |
+
repo_type="space",
|
| 462 |
+
commit_message=f"{commit_message} - {filename}"
|
| 463 |
+
)
|
| 464 |
+
success = True
|
| 465 |
+
break
|
| 466 |
+
except Exception as e:
|
| 467 |
+
last_error = e
|
| 468 |
+
if "403" in str(e) or "Forbidden" in str(e):
|
| 469 |
+
return False, f"Permission denied uploading {filename}. Check your token has write access.", None
|
| 470 |
+
if attempt < max_attempts - 1:
|
| 471 |
+
time.sleep(2) # Wait before retry
|
| 472 |
+
|
| 473 |
+
if not success:
|
| 474 |
+
return False, f"Failed to upload {filename} after {max_attempts} attempts: {last_error}", None
|
| 475 |
+
else:
|
| 476 |
+
# For other languages, use upload_folder
|
| 477 |
+
api.upload_folder(
|
| 478 |
+
folder_path=str(temp_path),
|
| 479 |
+
repo_id=repo_id,
|
| 480 |
+
repo_type="space",
|
| 481 |
+
commit_message=commit_message
|
| 482 |
+
)
|
| 483 |
except Exception as e:
|
| 484 |
return False, f"Failed to upload files: {str(e)}", None
|
| 485 |
|