jonathanagustin commited on
Commit
2820d7e
·
verified ·
1 Parent(s): 8030564

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +177 -11
app.py CHANGED
@@ -196,12 +196,15 @@ def build_and_push(
196
  tags: list = None,
197
  build_args: dict = None,
198
  ) -> bool:
199
- """Build Docker image using Kaniko and push to registry."""
 
 
 
 
200
 
201
  if tags is None:
202
  tags = ["latest"]
203
 
204
- # Full image name with registry
205
  full_image = f"{REGISTRY_URL}/{image_name}"
206
 
207
  log(f"Building {full_image}...")
@@ -217,11 +220,29 @@ def build_and_push(
217
  "started_at": datetime.now(timezone.utc).isoformat(),
218
  }
219
 
220
- # Build Kaniko command
221
- # Note: dockerfile path should be relative to context for multi-stage builds
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  cmd = [
223
  "/kaniko/executor",
224
- f"--context=dir://{context_dir}",
225
  f"--dockerfile={dockerfile}",
226
  ]
227
 
@@ -236,10 +257,9 @@ def build_and_push(
236
 
237
  # Kaniko options
238
  cmd.extend([
239
- "--cache=false", # Disabled due to HF Space network issues
240
  "--ignore-path=/product_uuid",
241
  "--ignore-path=/sys",
242
- "--verbosity=info",
243
  ])
244
 
245
  log(f"Executing: {' '.join(cmd[:5])}...")
@@ -295,6 +315,120 @@ def build_and_push(
295
  state["current_build"] = None
296
 
297
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  def run_build(build_config: dict) -> bool:
299
  """Run a build from config."""
300
  repo_url = build_config.get("repo_url")
@@ -305,20 +439,49 @@ def run_build(build_config: dict) -> bool:
305
  tags = build_config.get("tags", ["latest"])
306
  build_args = build_config.get("build_args", {})
307
  github_token = build_config.get("github_token") # Optional per-build token
 
 
308
 
309
  if not repo_url or not image_name:
310
  log("✗ Missing repo_url or image_name")
311
  return False
312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  tmpdir = None
 
314
  try:
315
- # Clone repo
316
  tmpdir = clone_repo(repo_url, branch, github_token=github_token)
317
-
318
- # Set context directory
319
  context_dir = str(tmpdir / context_path) if context_path != "." else str(tmpdir)
320
 
321
- # Build and push
322
  return build_and_push(
323
  context_dir=context_dir,
324
  image_name=image_name,
@@ -328,8 +491,11 @@ def run_build(build_config: dict) -> bool:
328
  )
329
 
330
  finally:
 
331
  if tmpdir and tmpdir.exists():
332
  shutil.rmtree(tmpdir, ignore_errors=True)
 
 
333
 
334
 
335
  # =============================================================================
 
196
  tags: list = None,
197
  build_args: dict = None,
198
  ) -> bool:
199
+ """Build Docker image using Kaniko and push to registry.
200
+
201
+ Uses tar context to avoid Kaniko's 'failed to get files used from context'
202
+ bug with multi-stage Dockerfiles that use FROM <previous_stage>.
203
+ """
204
 
205
  if tags is None:
206
  tags = ["latest"]
207
 
 
208
  full_image = f"{REGISTRY_URL}/{image_name}"
209
 
210
  log(f"Building {full_image}...")
 
220
  "started_at": datetime.now(timezone.utc).isoformat(),
221
  }
222
 
223
+ # Create tar of context - this avoids Kaniko's dir:// context resolution bug
224
+ # that causes "failed to get files used from context" on multi-stage builds
225
+ tar_path = f"{context_dir}.tar.gz"
226
+ log(f"Creating tar context: {tar_path}")
227
+ try:
228
+ tar_result = subprocess.run(
229
+ ["tar", "-czf", tar_path, "-C", context_dir, "."],
230
+ capture_output=True,
231
+ text=True,
232
+ timeout=120,
233
+ )
234
+ if tar_result.returncode != 0:
235
+ log(f"✗ Failed to create tar: {tar_result.stderr}")
236
+ return False
237
+ log(f"✓ Tar created")
238
+ except Exception as e:
239
+ log(f"✗ Tar error: {e}")
240
+ return False
241
+
242
+ # Build Kaniko command with tar context
243
  cmd = [
244
  "/kaniko/executor",
245
+ f"--context=tar://{tar_path}",
246
  f"--dockerfile={dockerfile}",
247
  ]
248
 
 
257
 
258
  # Kaniko options
259
  cmd.extend([
260
+ "--cache=false",
261
  "--ignore-path=/product_uuid",
262
  "--ignore-path=/sys",
 
263
  ])
264
 
265
  log(f"Executing: {' '.join(cmd[:5])}...")
 
315
  state["current_build"] = None
316
 
317
 
318
+ def build_and_push_git(
319
+ git_context: str,
320
+ image_name: str,
321
+ dockerfile: str = "Dockerfile",
322
+ context_subpath: str = None,
323
+ tags: list = None,
324
+ build_args: dict = None,
325
+ ) -> bool:
326
+ """Build Docker image using Kaniko with git context source."""
327
+
328
+ if tags is None:
329
+ tags = ["latest"]
330
+
331
+ full_image = f"{REGISTRY_URL}/{image_name}"
332
+
333
+ # Mask token in logs
334
+ safe_context = git_context
335
+ if "@github.com" in git_context:
336
+ safe_context = git_context.split("@")[0][:10] + "...@github.com" + git_context.split("@github.com")[1]
337
+
338
+ log(f"Building {full_image}...")
339
+ log(f" Git context: {safe_context}")
340
+ log(f" Dockerfile: {dockerfile}")
341
+ log(f" Tags: {tags}")
342
+
343
+ with state_lock:
344
+ state["status"] = "building"
345
+ state["current_build"] = {
346
+ "image": full_image,
347
+ "tags": tags,
348
+ "started_at": datetime.now(timezone.utc).isoformat(),
349
+ }
350
+
351
+ # Build Kaniko command with git context
352
+ cmd = [
353
+ "/kaniko/executor",
354
+ f"--context={git_context}",
355
+ f"--dockerfile={dockerfile}",
356
+ ]
357
+
358
+ if context_subpath:
359
+ cmd.append(f"--context-sub-path={context_subpath}")
360
+
361
+ # Add destination tags
362
+ for tag in tags:
363
+ cmd.append(f"--destination={full_image}:{tag}")
364
+
365
+ # Add build args
366
+ if build_args:
367
+ for key, value in build_args.items():
368
+ cmd.append(f"--build-arg={key}={value}")
369
+
370
+ # Kaniko options
371
+ cmd.extend([
372
+ "--cache=false",
373
+ "--ignore-path=/product_uuid",
374
+ "--ignore-path=/sys",
375
+ ])
376
+
377
+ log(f"Executing kaniko with git context...")
378
+
379
+ try:
380
+ process = subprocess.Popen(
381
+ cmd,
382
+ stdout=subprocess.PIPE,
383
+ stderr=subprocess.STDOUT,
384
+ text=True,
385
+ )
386
+
387
+ for line in process.stdout:
388
+ line = line.strip()
389
+ if line:
390
+ # Mask any tokens in output
391
+ if GITHUB_TOKEN and GITHUB_TOKEN in line:
392
+ line = line.replace(GITHUB_TOKEN, "***")
393
+ log(f" {line[:100]}")
394
+
395
+ process.wait()
396
+
397
+ if process.returncode == 0:
398
+ log(f"✓ Build successful: {full_image}")
399
+ with state_lock:
400
+ state["builds_completed"] += 1
401
+ state["last_build"] = {
402
+ "image": full_image,
403
+ "tags": tags,
404
+ "status": "success",
405
+ "completed_at": datetime.now(timezone.utc).isoformat(),
406
+ }
407
+ return True
408
+ else:
409
+ log(f"✗ Build failed with exit code {process.returncode}")
410
+ with state_lock:
411
+ state["builds_failed"] += 1
412
+ state["last_build"] = {
413
+ "image": full_image,
414
+ "tags": tags,
415
+ "status": "failed",
416
+ "exit_code": process.returncode,
417
+ "completed_at": datetime.now(timezone.utc).isoformat(),
418
+ }
419
+ return False
420
+
421
+ except Exception as e:
422
+ log(f"✗ Build error: {e}")
423
+ with state_lock:
424
+ state["builds_failed"] += 1
425
+ return False
426
+ finally:
427
+ with state_lock:
428
+ state["status"] = "idle"
429
+ state["current_build"] = None
430
+
431
+
432
  def run_build(build_config: dict) -> bool:
433
  """Run a build from config."""
434
  repo_url = build_config.get("repo_url")
 
439
  tags = build_config.get("tags", ["latest"])
440
  build_args = build_config.get("build_args", {})
441
  github_token = build_config.get("github_token") # Optional per-build token
442
+ # Default to tar context (clone + tar) for reliability with multi-stage builds
443
+ use_git_context = build_config.get("use_git_context", False)
444
 
445
  if not repo_url or not image_name:
446
  log("✗ Missing repo_url or image_name")
447
  return False
448
 
449
+ # Option: Use git context source (Kaniko handles clone internally)
450
+ # Note: git context can have issues with multi-stage builds, prefer tar context
451
+ if use_git_context and "github.com" in repo_url:
452
+ token = github_token or GITHUB_TOKEN
453
+ if token:
454
+ git_context = repo_url.replace("https://github.com", f"git://{token}@github.com")
455
+ else:
456
+ git_context = repo_url.replace("https://github.com", "git://github.com")
457
+ git_context = f"{git_context}#refs/heads/{branch}"
458
+
459
+ if context_path and context_path != ".":
460
+ return build_and_push_git(
461
+ git_context=git_context,
462
+ image_name=image_name,
463
+ dockerfile=dockerfile,
464
+ context_subpath=context_path,
465
+ tags=tags,
466
+ build_args=build_args,
467
+ )
468
+ else:
469
+ return build_and_push_git(
470
+ git_context=git_context,
471
+ image_name=image_name,
472
+ dockerfile=dockerfile,
473
+ tags=tags,
474
+ build_args=build_args,
475
+ )
476
+
477
+ # Default: clone repo, create tar context (most reliable for multi-stage builds)
478
  tmpdir = None
479
+ tar_path = None
480
  try:
 
481
  tmpdir = clone_repo(repo_url, branch, github_token=github_token)
 
 
482
  context_dir = str(tmpdir / context_path) if context_path != "." else str(tmpdir)
483
 
484
+ tar_path = f"{context_dir}.tar.gz" # Track for cleanup
485
  return build_and_push(
486
  context_dir=context_dir,
487
  image_name=image_name,
 
491
  )
492
 
493
  finally:
494
+ # Cleanup: remove cloned repo and tar file
495
  if tmpdir and tmpdir.exists():
496
  shutil.rmtree(tmpdir, ignore_errors=True)
497
+ if tar_path and os.path.exists(tar_path):
498
+ os.remove(tar_path)
499
 
500
 
501
  # =============================================================================