- Refactored `Command.help_signature` to return `(usage, description, tags)` instead of a Rich `Padding`/`Panel`. - Replaced `show_help()` with `render_help()` in `Command` and `Falyx`. - Updated Falyx help rendering to use Rich `Panel`/`Padding` consistently for cleaner UI. - Swapped `print()` calls for `console.print()` for styled output. - Added hooks to `ProcessAction` to announce analysis start/finish. - Added spinners to test and deploy steps; simplified retry setup. - Converted `remove()` to `async def remove()` for consistency. - Added async lock to prevent concurrent Live loop start/stop races. - Added debug logging when starting/stopping the Live loop. - Updated `spinner_teardown_hook` to `await sm.remove(...)` to align with async `remove()`. - Removed `rich.panel`/`rich.padding` from `Command` since panels are now built in `Falyx` help rendering. - Bumped `rich` dependency to `^14.0`. - Bumped version to 0.1.78. This commit polishes help display, demo UX, and spinner lifecycle safety—making spinners thread/async safe and help output more structured and readable.
		
			
				
	
	
		
			122 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			122 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import asyncio
 | |
| import random
 | |
| import time
 | |
| 
 | |
| from falyx import Falyx
 | |
| from falyx.action import Action, ActionGroup, ChainedAction, ProcessAction
 | |
| from falyx.console import console
 | |
| 
 | |
| 
 | |
| # Step 1: Fast I/O-bound setup (standard Action)
 | |
| async def checkout_code():
 | |
|     console.print("🔄 Checking out code...")
 | |
|     await asyncio.sleep(0.5)
 | |
|     console.print("📦 Code checked out successfully.")
 | |
| 
 | |
| 
 | |
| # Step 2: CPU-bound task (ProcessAction)
 | |
| def run_static_analysis():
 | |
|     total = 0
 | |
|     for i in range(10_000_000):
 | |
|         total += i % 3
 | |
|     time.sleep(2)
 | |
|     return total
 | |
| 
 | |
| 
 | |
| # Step 3: Simulated flaky test with retry
 | |
| async def flaky_tests():
 | |
|     console.print("🧪 Running tests...")
 | |
|     await asyncio.sleep(0.3)
 | |
|     if random.random() < 0.3:
 | |
|         raise RuntimeError("❌ Random test failure!")
 | |
|     console.print("🧪 Tests passed.")
 | |
|     return "ok"
 | |
| 
 | |
| 
 | |
| # Step 4: Multiple deploy targets (parallel ActionGroup)
 | |
| async def deploy_to(target: str):
 | |
|     console.print(f"🚀 Deploying to {target}...")
 | |
|     await asyncio.sleep(random.randint(2, 6))
 | |
|     console.print(f"✅ Deployment to {target} complete.")
 | |
|     return f"{target} complete"
 | |
| 
 | |
| 
 | |
| def build_pipeline():
 | |
|     # Base actions
 | |
|     checkout = Action("Checkout", checkout_code)
 | |
|     analysis = ProcessAction(
 | |
|         "Static Analysis",
 | |
|         run_static_analysis,
 | |
|         spinner=True,
 | |
|         spinner_message="Analyzing code...",
 | |
|     )
 | |
|     analysis.hooks.register(
 | |
|         "before", lambda ctx: console.print("🧠 Running static analysis (CPU-bound)...")
 | |
|     )
 | |
|     analysis.hooks.register("after", lambda ctx: console.print("🧠 Analysis complete!"))
 | |
|     tests = Action(
 | |
|         "Run Tests",
 | |
|         flaky_tests,
 | |
|         retry=True,
 | |
|         spinner=True,
 | |
|         spinner_message="Running tests...",
 | |
|     )
 | |
| 
 | |
|     # Parallel deploys
 | |
|     deploy_group = ActionGroup(
 | |
|         "Deploy to All",
 | |
|         [
 | |
|             Action(
 | |
|                 "Deploy US",
 | |
|                 deploy_to,
 | |
|                 args=("us-west",),
 | |
|                 spinner=True,
 | |
|                 spinner_message="Deploying US...",
 | |
|             ),
 | |
|             Action(
 | |
|                 "Deploy EU",
 | |
|                 deploy_to,
 | |
|                 args=("eu-central",),
 | |
|                 spinner=True,
 | |
|                 spinner_message="Deploying EU...",
 | |
|             ),
 | |
|             Action(
 | |
|                 "Deploy Asia",
 | |
|                 deploy_to,
 | |
|                 args=("asia-east",),
 | |
|                 spinner=True,
 | |
|                 spinner_message="Deploying Asia...",
 | |
|             ),
 | |
|         ],
 | |
|     )
 | |
| 
 | |
|     # Full pipeline
 | |
|     return ChainedAction("CI/CD Pipeline", [checkout, analysis, tests, deploy_group])
 | |
| 
 | |
| 
 | |
| pipeline = build_pipeline()
 | |
| 
 | |
| 
 | |
| # Run the pipeline
 | |
| async def main():
 | |
| 
 | |
|     flx = Falyx(
 | |
|         hide_menu_table=True, program="pipeline_demo.py", show_placeholder_menu=True
 | |
|     )
 | |
|     flx.add_command(
 | |
|         "P",
 | |
|         "Run Pipeline",
 | |
|         pipeline,
 | |
|         spinner=True,
 | |
|         spinner_type="line",
 | |
|         spinner_message="Running pipeline...",
 | |
|         tags=["pipeline", "demo"],
 | |
|         help_text="Run the full CI/CD pipeline demo.",
 | |
|     )
 | |
| 
 | |
|     await flx.run()
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     asyncio.run(main())
 |