66import os
77import shutil
88import sys
9+ from datetime import datetime
910
1011from .command_utils import CommandExecutor
1112from .config import Config
@@ -29,6 +30,8 @@ def __init__(
2930 def setup_project (self ) -> None :
3031 """
3132 Set up the test project.
33+ Tries to use cached fixture first, then creates fresh project if needed.
34+ After creation, automatically caches for future runs.
3235 Exits with code 1 if project already exists or type is unknown.
3336 """
3437 if self .state .is_project_created ():
@@ -42,23 +45,23 @@ def setup_project(self) -> None:
4245 # Create target directory
4346 self .config .target_dir .mkdir (parents = True , exist_ok = True )
4447
45- # Check if we should use fixtures (default: true)
46- use_fixtures = os .getenv ("USE_TEST_FIXTURES" , "true" ).lower () == "true"
48+ # Try to use cached fixture first
49+ if self ._try_use_cached_fixture (self .config .project_type ):
50+ self .log .success (f"Project setup complete at { self .config .target_dir } (from cache)" )
51+ return
4752
53+ # Create new project if cache not available
4854 if self .config .project_type == "symfony" :
49- if use_fixtures :
50- self ._setup_from_fixtures ("symfony" )
51- else :
52- self ._setup_symfony_create_project ()
55+ self ._create_symfony_project ()
5356 elif self .config .project_type == "laravel" :
54- if use_fixtures :
55- self ._setup_from_fixtures ("laravel" )
56- else :
57- self ._setup_laravel_create_project ()
57+ self ._create_laravel_project ()
5858 else :
5959 self .log .error (f"Unknown project type: { self .config .project_type } " )
6060 sys .exit (1 )
6161
62+ # Cache the newly created project for future runs
63+ self ._cache_fixture_after_setup (self .config .project_type )
64+
6265 self .log .success (f"Project setup complete at { self .config .target_dir } " )
6366
6467 def _get_fixtures_cache_dir (self ):
@@ -81,51 +84,30 @@ def _ensure_fixtures_cache(self, project_type: str):
8184 return True
8285
8386 # No cached fixtures available
84- self .log .info (f"No cached { project_type } fixture found (will be built by CI on first run) " )
87+ self .log .info (f"No cached { project_type } fixture found" )
8588 return False
8689
87- def _setup_from_fixtures (self , project_type : str ):
88- """Set up project by copying from pre-built fixtures"""
89- self .log .info (f"Setting up { project_type } from fixtures (fast mode)..." )
90-
91- # Ensure we have the fixture (download from releases if needed)
92- if not self ._ensure_fixtures_cache (project_type ):
93- self .log .error (
94- f"Failed to download { project_type } fixture. "
95- "Falling back to create-project method..."
96- )
97- if project_type == "symfony" :
98- self ._setup_symfony_create_project ()
99- else :
100- self ._setup_laravel_create_project ()
101- return
102-
103- # Get fixture source path
104- fixtures_cache = self ._get_fixtures_cache_dir ()
105- source = fixtures_cache / project_type
90+ def _try_use_cached_fixture (self , project_type : str ) -> bool :
91+ """
92+ Try to use cached fixture. Returns True if successful, False if not available.
93+ If cache exists, sets up DDEV and runs composer install.
94+ """
95+ cache_source = self ._get_fixtures_cache_dir () / project_type
10696
107- if not source .exists ():
108- self .log .error (
109- f"Fixture not found at { source } after download. "
110- "Falling back to create-project method..."
111- )
112- if project_type == "symfony" :
113- self ._setup_symfony_create_project ()
114- else :
115- self ._setup_laravel_create_project ()
116- return
97+ if not cache_source .exists ():
98+ return False
11799
118- self .log .info (f"Copying { project_type } fixture from cache ..." )
100+ self .log .info (f"Using cached { project_type } fixture..." )
119101
120- # Copy everything including .git directory
121- for item in source .iterdir ():
102+ # Copy from cache
103+ for item in cache_source .iterdir ():
122104 dest = self .config .target_dir / item .name
123105 if item .is_dir ():
124106 shutil .copytree (item , dest )
125107 else :
126108 shutil .copy2 (item , dest )
127109
128- # Configure Git
110+ # Configure git
129111 self .log .info ("Configuring Git for test environment..." )
130112 self .cmd .run_command (
131113 ["git" , "config" , "user.name" , "Test User" ], cwd = self .config .target_dir
@@ -173,10 +155,36 @@ def _setup_from_fixtures(self, project_type: str):
173155 cwd = self .config .target_dir ,
174156 )
175157
176- self .log .success (f"{ project_type .capitalize ()} fixture setup complete!" )
158+ self .log .success (f"{ project_type .capitalize ()} fixture loaded from cache!" )
159+ return True
160+
161+ def _cache_fixture_after_setup (self , project_type : str ) -> None :
162+ """Cache the project after initial setup for future test runs"""
163+ cache_dir = self ._get_fixtures_cache_dir () / project_type
164+
165+ if cache_dir .exists ():
166+ self .log .info (f"Cache already exists at { cache_dir } , skipping..." )
167+ return
168+
169+ self .log .info (f"Caching { project_type } fixture for future runs..." )
170+ cache_dir .mkdir (parents = True , exist_ok = True )
171+
172+ # Copy project directory to cache
173+ for item in self .config .target_dir .iterdir ():
174+ dest = cache_dir / item .name
175+ if item .is_dir ():
176+ shutil .copytree (item , dest )
177+ else :
178+ shutil .copy2 (item , dest )
179+
180+ # Create version file with current date
181+ version_file = cache_dir / "FIXTURE_VERSION"
182+ version_file .write_text (datetime .now ().strftime ("%Y-%m-%d" ))
183+
184+ self .log .success (f"Cached { project_type } fixture at { cache_dir } " )
177185
178- def _setup_symfony_create_project (self ) -> None :
179- """Set up Symfony project using composer create-project (legacy/fallback method) """
186+ def _create_symfony_project (self ) -> None :
187+ """Set up Symfony project using composer create-project via DDEV """
180188 self .log .info ("Creating Symfony project using DDEV..." )
181189
182190 # Configure DDEV first
@@ -252,8 +260,8 @@ def _setup_symfony_create_project(self) -> None:
252260 cwd = self .config .target_dir ,
253261 )
254262
255- def _setup_laravel_create_project (self ):
256- """Set up Laravel project using composer create-project (legacy/fallback method) """
263+ def _create_laravel_project (self ):
264+ """Set up Laravel project using composer create-project via DDEV """
257265 self .log .info ("Creating Laravel project using DDEV..." )
258266
259267 # Configure DDEV first
0 commit comments