fbuild 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fbuild might be problematic. Click here for more details.

Files changed (93) hide show
  1. fbuild/__init__.py +0 -0
  2. fbuild/assets/example.txt +1 -0
  3. fbuild/build/__init__.py +117 -0
  4. fbuild/build/archive_creator.py +186 -0
  5. fbuild/build/binary_generator.py +444 -0
  6. fbuild/build/build_component_factory.py +131 -0
  7. fbuild/build/build_state.py +325 -0
  8. fbuild/build/build_utils.py +98 -0
  9. fbuild/build/compilation_executor.py +422 -0
  10. fbuild/build/compiler.py +165 -0
  11. fbuild/build/compiler_avr.py +574 -0
  12. fbuild/build/configurable_compiler.py +612 -0
  13. fbuild/build/configurable_linker.py +637 -0
  14. fbuild/build/flag_builder.py +186 -0
  15. fbuild/build/library_dependency_processor.py +185 -0
  16. fbuild/build/linker.py +708 -0
  17. fbuild/build/orchestrator.py +67 -0
  18. fbuild/build/orchestrator_avr.py +656 -0
  19. fbuild/build/orchestrator_esp32.py +797 -0
  20. fbuild/build/orchestrator_teensy.py +543 -0
  21. fbuild/build/source_compilation_orchestrator.py +220 -0
  22. fbuild/build/source_scanner.py +516 -0
  23. fbuild/cli.py +566 -0
  24. fbuild/cli_utils.py +312 -0
  25. fbuild/config/__init__.py +16 -0
  26. fbuild/config/board_config.py +457 -0
  27. fbuild/config/board_loader.py +92 -0
  28. fbuild/config/ini_parser.py +209 -0
  29. fbuild/config/mcu_specs.py +88 -0
  30. fbuild/daemon/__init__.py +34 -0
  31. fbuild/daemon/client.py +929 -0
  32. fbuild/daemon/compilation_queue.py +293 -0
  33. fbuild/daemon/daemon.py +474 -0
  34. fbuild/daemon/daemon_context.py +196 -0
  35. fbuild/daemon/error_collector.py +263 -0
  36. fbuild/daemon/file_cache.py +332 -0
  37. fbuild/daemon/lock_manager.py +270 -0
  38. fbuild/daemon/logging_utils.py +149 -0
  39. fbuild/daemon/messages.py +301 -0
  40. fbuild/daemon/operation_registry.py +288 -0
  41. fbuild/daemon/process_tracker.py +366 -0
  42. fbuild/daemon/processors/__init__.py +12 -0
  43. fbuild/daemon/processors/build_processor.py +157 -0
  44. fbuild/daemon/processors/deploy_processor.py +327 -0
  45. fbuild/daemon/processors/monitor_processor.py +146 -0
  46. fbuild/daemon/request_processor.py +401 -0
  47. fbuild/daemon/status_manager.py +216 -0
  48. fbuild/daemon/subprocess_manager.py +316 -0
  49. fbuild/deploy/__init__.py +17 -0
  50. fbuild/deploy/deployer.py +67 -0
  51. fbuild/deploy/deployer_esp32.py +314 -0
  52. fbuild/deploy/monitor.py +495 -0
  53. fbuild/interrupt_utils.py +34 -0
  54. fbuild/packages/__init__.py +53 -0
  55. fbuild/packages/archive_utils.py +1098 -0
  56. fbuild/packages/arduino_core.py +412 -0
  57. fbuild/packages/cache.py +249 -0
  58. fbuild/packages/downloader.py +366 -0
  59. fbuild/packages/framework_esp32.py +538 -0
  60. fbuild/packages/framework_teensy.py +346 -0
  61. fbuild/packages/github_utils.py +96 -0
  62. fbuild/packages/header_trampoline_cache.py +394 -0
  63. fbuild/packages/library_compiler.py +203 -0
  64. fbuild/packages/library_manager.py +549 -0
  65. fbuild/packages/library_manager_esp32.py +413 -0
  66. fbuild/packages/package.py +163 -0
  67. fbuild/packages/platform_esp32.py +383 -0
  68. fbuild/packages/platform_teensy.py +312 -0
  69. fbuild/packages/platform_utils.py +131 -0
  70. fbuild/packages/platformio_registry.py +325 -0
  71. fbuild/packages/sdk_utils.py +231 -0
  72. fbuild/packages/toolchain.py +436 -0
  73. fbuild/packages/toolchain_binaries.py +196 -0
  74. fbuild/packages/toolchain_esp32.py +484 -0
  75. fbuild/packages/toolchain_metadata.py +185 -0
  76. fbuild/packages/toolchain_teensy.py +404 -0
  77. fbuild/platform_configs/esp32.json +150 -0
  78. fbuild/platform_configs/esp32c2.json +144 -0
  79. fbuild/platform_configs/esp32c3.json +143 -0
  80. fbuild/platform_configs/esp32c5.json +151 -0
  81. fbuild/platform_configs/esp32c6.json +151 -0
  82. fbuild/platform_configs/esp32p4.json +149 -0
  83. fbuild/platform_configs/esp32s3.json +151 -0
  84. fbuild/platform_configs/imxrt1062.json +56 -0
  85. fbuild-1.1.0.dist-info/METADATA +447 -0
  86. fbuild-1.1.0.dist-info/RECORD +93 -0
  87. fbuild-1.1.0.dist-info/WHEEL +5 -0
  88. fbuild-1.1.0.dist-info/entry_points.txt +5 -0
  89. fbuild-1.1.0.dist-info/licenses/LICENSE +21 -0
  90. fbuild-1.1.0.dist-info/top_level.txt +2 -0
  91. fbuild_lint/__init__.py +0 -0
  92. fbuild_lint/ruff_plugins/__init__.py +0 -0
  93. fbuild_lint/ruff_plugins/keyboard_interrupt_checker.py +158 -0
fbuild/cli.py ADDED
@@ -0,0 +1,566 @@
1
+ """
2
+ Command-line interface for fbuild.
3
+
4
+ This module provides the `fbuild` CLI tool for building embedded firmware.
5
+ """
6
+
7
+ import argparse
8
+ import sys
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ from fbuild.cli_utils import (
14
+ EnvironmentDetector,
15
+ ErrorFormatter,
16
+ MonitorFlagParser,
17
+ PathValidator,
18
+ )
19
+ from fbuild.daemon import client as daemon_client
20
+
21
+
22
+ @dataclass
23
+ class BuildArgs:
24
+ """Arguments for the build command."""
25
+
26
+ project_dir: Path
27
+ environment: Optional[str] = None
28
+ clean: bool = False
29
+ verbose: bool = False
30
+
31
+
32
+ @dataclass
33
+ class DeployArgs:
34
+ """Arguments for the deploy command."""
35
+
36
+ project_dir: Path
37
+ environment: Optional[str] = None
38
+ port: Optional[str] = None
39
+ clean: bool = False
40
+ monitor: Optional[str] = None
41
+ verbose: bool = False
42
+
43
+
44
+ @dataclass
45
+ class MonitorArgs:
46
+ """Arguments for the monitor command."""
47
+
48
+ project_dir: Path
49
+ environment: Optional[str] = None
50
+ port: Optional[str] = None
51
+ baud: int = 115200
52
+ timeout: Optional[int] = None
53
+ halt_on_error: Optional[str] = None
54
+ halt_on_success: Optional[str] = None
55
+ expect: Optional[str] = None
56
+ verbose: bool = False
57
+
58
+
59
+ def build_command(args: BuildArgs) -> None:
60
+ """Build firmware for embedded target.
61
+
62
+ Examples:
63
+ fbuild build # Build default environment
64
+ fbuild build tests/uno # Build specific project
65
+ fbuild build -e uno # Build 'uno' environment
66
+ fbuild build --clean # Clean build
67
+ fbuild build --verbose # Verbose output
68
+ """
69
+ # Print header
70
+ print("fbuild Build System v0.1.0")
71
+ print()
72
+
73
+ try:
74
+ # Determine environment name
75
+ env_name = EnvironmentDetector.detect_environment(args.project_dir, args.environment)
76
+
77
+ # Show build start message
78
+ if args.verbose:
79
+ print(f"Building project: {args.project_dir}")
80
+ print(f"Environment: {env_name}")
81
+ print()
82
+ else:
83
+ print(f"Building environment: {env_name}...")
84
+
85
+ # Route build through daemon for background processing
86
+ success = daemon_client.request_build(
87
+ project_dir=args.project_dir,
88
+ environment=env_name,
89
+ clean_build=args.clean,
90
+ verbose=args.verbose,
91
+ )
92
+
93
+ # Exit with appropriate code
94
+ sys.exit(0 if success else 1)
95
+
96
+ except FileNotFoundError as e:
97
+ ErrorFormatter.handle_file_not_found(e)
98
+ except PermissionError as e:
99
+ ErrorFormatter.handle_permission_error(e)
100
+ except KeyboardInterrupt:
101
+ ErrorFormatter.handle_keyboard_interrupt()
102
+ except Exception as e:
103
+ ErrorFormatter.handle_unexpected_error(e, args.verbose)
104
+
105
+
106
+ def deploy_command(args: DeployArgs) -> None:
107
+ """Deploy firmware to embedded target.
108
+
109
+ Examples:
110
+ fbuild deploy # Deploy default environment
111
+ fbuild deploy tests/esp32c6 # Deploy specific project
112
+ fbuild deploy -e esp32c6 # Deploy 'esp32c6' environment
113
+ fbuild deploy -p COM3 # Deploy to specific port
114
+ fbuild deploy --clean # Clean build before deploy
115
+ fbuild deploy --monitor="--timeout 60 --halt-on-success \"TEST PASSED\"" # Deploy and monitor
116
+ """
117
+ print("fbuild Deployment System v0.1.0")
118
+ print()
119
+
120
+ try:
121
+ # Determine environment name
122
+ env_name = EnvironmentDetector.detect_environment(args.project_dir, args.environment)
123
+
124
+ # Parse monitor flags if provided
125
+ monitor_after = args.monitor is not None
126
+ monitor_timeout = None
127
+ monitor_halt_on_error = None
128
+ monitor_halt_on_success = None
129
+ monitor_expect = None
130
+ if monitor_after and args.monitor is not None:
131
+ flags = MonitorFlagParser.parse_monitor_flags(args.monitor)
132
+ monitor_timeout = flags.timeout
133
+ monitor_halt_on_error = flags.halt_on_error
134
+ monitor_halt_on_success = flags.halt_on_success
135
+ monitor_expect = flags.expect
136
+
137
+ # Use daemon for concurrent deploy management
138
+ success = daemon_client.request_deploy(
139
+ project_dir=args.project_dir,
140
+ environment=env_name,
141
+ port=args.port,
142
+ clean_build=args.clean,
143
+ monitor_after=monitor_after,
144
+ monitor_timeout=monitor_timeout,
145
+ monitor_halt_on_error=monitor_halt_on_error,
146
+ monitor_halt_on_success=monitor_halt_on_success,
147
+ monitor_expect=monitor_expect,
148
+ timeout=1800, # 30 minute timeout for deploy
149
+ )
150
+
151
+ if success:
152
+ sys.exit(0)
153
+ else:
154
+ sys.exit(1)
155
+
156
+ except FileNotFoundError as e:
157
+ ErrorFormatter.handle_file_not_found(e)
158
+ except KeyboardInterrupt as ke:
159
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
160
+
161
+ handle_keyboard_interrupt_properly(ke)
162
+ except Exception as e:
163
+ ErrorFormatter.handle_unexpected_error(e, args.verbose)
164
+
165
+
166
+ def monitor_command(args: MonitorArgs) -> None:
167
+ """Monitor serial output from embedded target.
168
+
169
+ Examples:
170
+ fbuild monitor # Monitor default environment
171
+ fbuild monitor -p COM3 # Monitor specific port
172
+ fbuild monitor --timeout 60 # Monitor with 60s timeout
173
+ fbuild monitor --halt-on-error "ERROR" # Exit on error
174
+ fbuild monitor --halt-on-success "TEST PASSED" # Exit on success
175
+ """
176
+ try:
177
+ # Determine environment name
178
+ env_name = EnvironmentDetector.detect_environment(args.project_dir, args.environment)
179
+
180
+ # Use daemon for concurrent monitor management
181
+ success = daemon_client.request_monitor(
182
+ project_dir=args.project_dir,
183
+ environment=env_name,
184
+ port=args.port,
185
+ baud_rate=args.baud,
186
+ halt_on_error=args.halt_on_error,
187
+ halt_on_success=args.halt_on_success,
188
+ expect=args.expect,
189
+ timeout=args.timeout,
190
+ )
191
+
192
+ if success:
193
+ sys.exit(0)
194
+ else:
195
+ sys.exit(1)
196
+
197
+ except FileNotFoundError as e:
198
+ ErrorFormatter.handle_file_not_found(e)
199
+ except KeyboardInterrupt as ke:
200
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
201
+
202
+ handle_keyboard_interrupt_properly(ke)
203
+ except Exception as e:
204
+ ErrorFormatter.handle_unexpected_error(e, args.verbose)
205
+
206
+
207
+ def daemon_command(action: str) -> None:
208
+ """Manage the fbuild daemon.
209
+
210
+ Examples:
211
+ fbuild daemon status # Show daemon status
212
+ fbuild daemon stop # Stop the daemon
213
+ fbuild daemon restart # Restart the daemon
214
+ """
215
+ try:
216
+ if action == "status":
217
+ # Get daemon status
218
+ status = daemon_client.get_daemon_status()
219
+
220
+ if status["running"]:
221
+ print("✅ Daemon is running")
222
+ print(f" PID: {status.get('pid', 'unknown')}")
223
+
224
+ if "current_status" in status:
225
+ current = status["current_status"]
226
+ print(f" State: {current.get('state', 'unknown')}")
227
+ print(f" Message: {current.get('message', 'N/A')}")
228
+
229
+ if current.get("operation_in_progress"):
230
+ print(" 🔄 Operation in progress:")
231
+ print(f" Environment: {current.get('environment', 'N/A')}")
232
+ print(f" Project: {current.get('project_dir', 'N/A')}")
233
+ else:
234
+ print("❌ Daemon is not running")
235
+
236
+ elif action == "stop":
237
+ # Stop daemon
238
+ if daemon_client.stop_daemon():
239
+ sys.exit(0)
240
+ else:
241
+ ErrorFormatter.print_error("Failed to stop daemon", "")
242
+ sys.exit(1)
243
+
244
+ elif action == "restart":
245
+ # Restart daemon
246
+ print("Restarting daemon...")
247
+ if daemon_client.is_daemon_running():
248
+ if not daemon_client.stop_daemon():
249
+ ErrorFormatter.print_error("Failed to stop daemon", "")
250
+ sys.exit(1)
251
+
252
+ # Start fresh daemon
253
+ if daemon_client.ensure_daemon_running():
254
+ print("✅ Daemon restarted successfully")
255
+ sys.exit(0)
256
+ else:
257
+ ErrorFormatter.print_error("Failed to restart daemon", "")
258
+ sys.exit(1)
259
+ else:
260
+ ErrorFormatter.print_error(f"Unknown daemon action: {action}", "")
261
+ print("Valid actions: status, stop, restart")
262
+ sys.exit(1)
263
+
264
+ except KeyboardInterrupt:
265
+ ErrorFormatter.handle_keyboard_interrupt()
266
+ except Exception as e:
267
+ ErrorFormatter.handle_unexpected_error(e, verbose=False)
268
+
269
+
270
+ def parse_default_action_args(argv: list[str]) -> DeployArgs:
271
+ """Parse arguments for the default action (fbuild <project_dir> [flags]).
272
+
273
+ Args:
274
+ argv: Command-line arguments (sys.argv)
275
+
276
+ Returns:
277
+ DeployArgs with parsed values
278
+
279
+ Raises:
280
+ SystemExit: If project directory is invalid or required arguments are missing
281
+ """
282
+ if len(argv) < 2:
283
+ ErrorFormatter.print_error("Missing project directory", "")
284
+ sys.exit(1)
285
+
286
+ project_dir = Path(argv[1])
287
+ PathValidator.validate_project_dir(project_dir)
288
+
289
+ # Parse remaining arguments
290
+ monitor: Optional[str] = None
291
+ port: Optional[str] = None
292
+ environment: Optional[str] = None
293
+ clean = False
294
+ verbose = False
295
+
296
+ i = 2
297
+ while i < len(argv):
298
+ arg = argv[i]
299
+
300
+ # Handle --monitor flag
301
+ if arg.startswith("--monitor="):
302
+ monitor = arg.split("=", 1)[1]
303
+ i += 1
304
+ elif arg == "--monitor" and i + 1 < len(argv):
305
+ monitor = argv[i + 1]
306
+ i += 2
307
+ # Handle --port flag
308
+ elif arg.startswith("--port="):
309
+ port = arg.split("=", 1)[1]
310
+ i += 1
311
+ elif arg in ("-p", "--port") and i + 1 < len(argv):
312
+ port = argv[i + 1]
313
+ i += 2
314
+ # Handle --environment flag
315
+ elif arg.startswith("--environment="):
316
+ environment = arg.split("=", 1)[1]
317
+ i += 1
318
+ elif arg.startswith("-e="):
319
+ environment = arg.split("=", 1)[1]
320
+ i += 1
321
+ elif arg in ("-e", "--environment") and i + 1 < len(argv):
322
+ environment = argv[i + 1]
323
+ i += 2
324
+ # Handle --clean flag
325
+ elif arg in ("-c", "--clean"):
326
+ clean = True
327
+ i += 1
328
+ # Handle --verbose flag
329
+ elif arg in ("-v", "--verbose"):
330
+ verbose = True
331
+ i += 1
332
+ else:
333
+ # Unknown flag - warn and skip
334
+ ErrorFormatter.print_error(f"Unknown flag in default action: {arg}", "")
335
+ print("Hint: Use 'fbuild deploy --help' to see available flags")
336
+ sys.exit(1)
337
+
338
+ return DeployArgs(
339
+ project_dir=project_dir,
340
+ environment=environment,
341
+ port=port,
342
+ clean=clean,
343
+ monitor=monitor if monitor is not None else "", # Empty string means monitor with default settings
344
+ verbose=verbose,
345
+ )
346
+
347
+
348
+ def main() -> None:
349
+ """fbuild - Modern embedded build system.
350
+
351
+ Replace PlatformIO with URL-based platform/toolchain management.
352
+ """
353
+ # Handle default action: fbuild <project_dir> [flags] → deploy with monitor
354
+ # This check must happen before argparse to avoid conflicts
355
+ if len(sys.argv) >= 2 and not sys.argv[1].startswith("-") and sys.argv[1] not in ["build", "deploy", "monitor", "daemon"]:
356
+ # User provided a path without a subcommand - use default action
357
+ deploy_args = parse_default_action_args(sys.argv)
358
+ deploy_command(deploy_args)
359
+ return
360
+
361
+ parser = argparse.ArgumentParser(
362
+ prog="fbuild",
363
+ description="fbuild - Modern embedded build system",
364
+ )
365
+ parser.add_argument(
366
+ "--version",
367
+ action="version",
368
+ version="fbuild 0.1.0",
369
+ )
370
+
371
+ subparsers = parser.add_subparsers(dest="command", help="Command to run")
372
+
373
+ # Build command
374
+ build_parser = subparsers.add_parser(
375
+ "build",
376
+ help="Build firmware for embedded target",
377
+ )
378
+ build_parser.add_argument(
379
+ "project_dir",
380
+ nargs="?",
381
+ type=Path,
382
+ default=Path.cwd(),
383
+ help="Project directory (default: current directory)",
384
+ )
385
+ build_parser.add_argument(
386
+ "-e",
387
+ "--environment",
388
+ default=None,
389
+ help="Build environment (default: auto-detect from platformio.ini)",
390
+ )
391
+ build_parser.add_argument(
392
+ "-c",
393
+ "--clean",
394
+ action="store_true",
395
+ help="Clean build artifacts before building",
396
+ )
397
+ build_parser.add_argument(
398
+ "-v",
399
+ "--verbose",
400
+ action="store_true",
401
+ help="Show verbose build output",
402
+ )
403
+
404
+ # Deploy command
405
+ deploy_parser = subparsers.add_parser(
406
+ "deploy",
407
+ help="Deploy firmware to embedded target",
408
+ )
409
+ deploy_parser.add_argument(
410
+ "project_dir",
411
+ nargs="?",
412
+ type=Path,
413
+ default=Path.cwd(),
414
+ help="Project directory (default: current directory)",
415
+ )
416
+ deploy_parser.add_argument(
417
+ "-e",
418
+ "--environment",
419
+ default=None,
420
+ help="Build environment (default: auto-detect from platformio.ini)",
421
+ )
422
+ deploy_parser.add_argument(
423
+ "-p",
424
+ "--port",
425
+ default=None,
426
+ help="Serial port (default: auto-detect)",
427
+ )
428
+ deploy_parser.add_argument(
429
+ "-c",
430
+ "--clean",
431
+ action="store_true",
432
+ help="Clean build artifacts before building",
433
+ )
434
+ deploy_parser.add_argument(
435
+ "--monitor",
436
+ default=None,
437
+ help="Monitor flags to pass after deployment (e.g., '--timeout 60 --halt-on-success \"TEST PASSED\"')",
438
+ )
439
+ deploy_parser.add_argument(
440
+ "-v",
441
+ "--verbose",
442
+ action="store_true",
443
+ help="Show verbose output",
444
+ )
445
+
446
+ # Monitor command
447
+ monitor_parser = subparsers.add_parser(
448
+ "monitor",
449
+ help="Monitor serial output from embedded target",
450
+ )
451
+ monitor_parser.add_argument(
452
+ "project_dir",
453
+ nargs="?",
454
+ type=Path,
455
+ default=Path.cwd(),
456
+ help="Project directory (default: current directory)",
457
+ )
458
+ monitor_parser.add_argument(
459
+ "-e",
460
+ "--environment",
461
+ default=None,
462
+ help="Build environment (default: auto-detect from platformio.ini)",
463
+ )
464
+ monitor_parser.add_argument(
465
+ "-p",
466
+ "--port",
467
+ default=None,
468
+ help="Serial port (default: auto-detect)",
469
+ )
470
+ monitor_parser.add_argument(
471
+ "-b",
472
+ "--baud",
473
+ default=115200,
474
+ type=int,
475
+ help="Baud rate (default: 115200)",
476
+ )
477
+ monitor_parser.add_argument(
478
+ "-t",
479
+ "--timeout",
480
+ default=None,
481
+ type=int,
482
+ help="Timeout in seconds (default: no timeout)",
483
+ )
484
+ monitor_parser.add_argument(
485
+ "--halt-on-error",
486
+ default=None,
487
+ help="Pattern that triggers error exit (regex)",
488
+ )
489
+ monitor_parser.add_argument(
490
+ "--halt-on-success",
491
+ default=None,
492
+ help="Pattern that triggers success exit (regex)",
493
+ )
494
+ monitor_parser.add_argument(
495
+ "--expect",
496
+ default=None,
497
+ help="Expected pattern - checked at timeout/success, exit 0 if found, 1 if not (regex)",
498
+ )
499
+ monitor_parser.add_argument(
500
+ "-v",
501
+ "--verbose",
502
+ action="store_true",
503
+ help="Show verbose output",
504
+ )
505
+
506
+ # Daemon command
507
+ daemon_parser = subparsers.add_parser(
508
+ "daemon",
509
+ help="Manage the fbuild daemon",
510
+ )
511
+ daemon_parser.add_argument(
512
+ "action",
513
+ choices=["status", "stop", "restart"],
514
+ help="Daemon action to perform",
515
+ )
516
+
517
+ # Parse arguments
518
+ parsed_args = parser.parse_args()
519
+
520
+ # If no command specified, show help
521
+ if not parsed_args.command:
522
+ parser.print_help()
523
+ sys.exit(0)
524
+
525
+ # Validate project directory exists
526
+ if hasattr(parsed_args, "project_dir"):
527
+ PathValidator.validate_project_dir(parsed_args.project_dir)
528
+
529
+ # Execute command
530
+ if parsed_args.command == "build":
531
+ build_args = BuildArgs(
532
+ project_dir=parsed_args.project_dir,
533
+ environment=parsed_args.environment,
534
+ clean=parsed_args.clean,
535
+ verbose=parsed_args.verbose,
536
+ )
537
+ build_command(build_args)
538
+ elif parsed_args.command == "deploy":
539
+ deploy_args = DeployArgs(
540
+ project_dir=parsed_args.project_dir,
541
+ environment=parsed_args.environment,
542
+ port=parsed_args.port,
543
+ clean=parsed_args.clean,
544
+ monitor=parsed_args.monitor,
545
+ verbose=parsed_args.verbose,
546
+ )
547
+ deploy_command(deploy_args)
548
+ elif parsed_args.command == "monitor":
549
+ monitor_args = MonitorArgs(
550
+ project_dir=parsed_args.project_dir,
551
+ environment=parsed_args.environment,
552
+ port=parsed_args.port,
553
+ baud=parsed_args.baud,
554
+ timeout=parsed_args.timeout,
555
+ halt_on_error=parsed_args.halt_on_error,
556
+ halt_on_success=parsed_args.halt_on_success,
557
+ expect=parsed_args.expect,
558
+ verbose=parsed_args.verbose,
559
+ )
560
+ monitor_command(monitor_args)
561
+ elif parsed_args.command == "daemon":
562
+ daemon_command(parsed_args.action)
563
+
564
+
565
+ if __name__ == "__main__":
566
+ main()