Skip to content

Commit ad955fd

Browse files
committed
feat(android): port iOS rsync optimisations to Android build pipeline
- Add shared getExcludedPaths() with comprehensive exclude list matching iOS - Add loadVendorExportIgnorePatterns() for .gitattributes export-ignore support - Fix vendor glob pattern from vendor/*/vendor to vendor/*/*/vendor - Replace platform-specific match block with unified getExcludedPaths() call - Stream ZIP iterator directly instead of materialising via iterator_to_array() - Remove redundant addDirectoryToZip() excludes now handled by rsync
1 parent 5235199 commit ad955fd

2 files changed

Lines changed: 136 additions & 19 deletions

File tree

src/Traits/PlatformFileOperations.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ protected function platformOptimizedCopy(string $source, string $destination, ar
3232
} else {
3333
// Use rsync on Unix-like systems
3434
if (! empty($excludedDirs)) {
35-
// Add specific exclusions for nested vendor directories that cause rsync cycles
36-
$excludedDirs[] = 'vendor/*/vendor';
37-
$excludedDirs[] = 'vendor/nativephp/mobile/vendor';
3835
$excludeFlags = implode(' ', array_map(fn ($d) => "--exclude='{$d}'", $excludedDirs));
3936
$cmd = "rsync -aL {$excludeFlags} \"{$source}/\" \"{$destination}/\"";
4037
} else {

src/Traits/PreparesBuild.php

Lines changed: 136 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,14 @@ protected function prepareLaravelBundle(bool $excludeDevDependencies = true): vo
225225
unlink($destinationZip);
226226
}
227227

228-
$excludedDirs = match (PHP_OS_FAMILY) {
229-
'Windows' => array_merge(config('nativephp.cleanup_exclude_files'), ['.git', 'node_modules', 'nativephp', 'vendor/nativephp/mobile/resources']),
230-
'Linux' => array_merge(config('nativephp.cleanup_exclude_files'), ['.git', 'node_modules', 'nativephp/ios', 'nativephp/android']),
231-
'Darwin' => array_merge(config('nativephp.cleanup_exclude_files'), ['.git', 'node_modules', 'nativephp/ios', 'nativephp/android']),
232-
default => config('nativephp.cleanup_exclude_files'),
233-
};
228+
$excludedDirs = $this->getExcludedPaths();
229+
230+
// Respect export-ignore from vendor package .gitattributes files
231+
foreach ($this->loadVendorExportIgnorePatterns($source) as $prefix => $patterns) {
232+
foreach ($patterns as $pattern) {
233+
$excludedDirs[] = $prefix.ltrim($pattern, '/');
234+
}
235+
}
234236

235237
$this->logToFile(' Excluded directories: '.implode(', ', $excludedDirs));
236238

@@ -304,6 +306,129 @@ protected function prepareLaravelBundle(bool $excludeDevDependencies = true): vo
304306
}
305307
}
306308

309+
/**
310+
* Paths to exclude from the app bundle.
311+
*
312+
* Patterns without a leading / match at any depth (e.g. inside vendor packages).
313+
* Patterns with a leading / are anchored to the project root.
314+
* Used by rsync during the initial copy step.
315+
*/
316+
protected function getExcludedPaths(): array
317+
{
318+
// Any depth (project root + inside vendor packages)
319+
$excludes = [
320+
'.git',
321+
'.github',
322+
'node_modules',
323+
'tests',
324+
'.DS_Store',
325+
'.gitignore',
326+
'.gitattributes',
327+
'.gitkeep',
328+
'.editorconfig',
329+
330+
// Non-runtime files inside vendor packages
331+
'*.md',
332+
'LICENSE*',
333+
'docs',
334+
'*.yml',
335+
'*.yaml',
336+
'*.neon',
337+
'*.neon.dist',
338+
339+
// Vendor-specific
340+
'vendor/nativephp/mobile/resources',
341+
'vendor/*/*/vendor',
342+
'vendor/livewire/livewire/src/Features/SupportFileUploads/browser_test_image_big.jpg',
343+
];
344+
345+
// Platform-specific project-level excludes
346+
if (PHP_OS_FAMILY === 'Windows') {
347+
$excludes[] = '/nativephp';
348+
} else {
349+
$excludes[] = '/nativephp/ios';
350+
$excludes[] = '/nativephp/android';
351+
}
352+
353+
// Project-level directories
354+
$excludes = array_merge($excludes, [
355+
'/output',
356+
'/build',
357+
'/dist',
358+
'/artifacts',
359+
'/storage/logs',
360+
'/storage/framework',
361+
'/public/storage',
362+
]);
363+
364+
// Project-level files
365+
$excludes = array_merge($excludes, [
366+
'/*.js',
367+
'/*.md',
368+
'/*.lock',
369+
'/*.xml',
370+
'/.env.example',
371+
'/artisan',
372+
]);
373+
374+
// User-configured exclusions
375+
$excludes = array_merge($excludes, config('nativephp.cleanup_exclude_files', []));
376+
377+
return $excludes;
378+
}
379+
380+
/**
381+
* Load export-ignore patterns from .gitattributes in vendor packages.
382+
*
383+
* @return array<string, string[]>
384+
*/
385+
protected function loadVendorExportIgnorePatterns(string $source): array
386+
{
387+
$patterns = [];
388+
$vendorPath = $source.'/vendor/';
389+
390+
if (! is_dir($vendorPath)) {
391+
return $patterns;
392+
}
393+
394+
foreach (new \DirectoryIterator($vendorPath) as $namespace) {
395+
if ($namespace->isDot() || ! $namespace->isDir()) {
396+
continue;
397+
}
398+
399+
foreach (new \DirectoryIterator($namespace->getPathname()) as $package) {
400+
if ($package->isDot() || ! $package->isDir()) {
401+
continue;
402+
}
403+
404+
$gitattributes = $package->getPathname().'/.gitattributes';
405+
if (! file_exists($gitattributes)) {
406+
continue;
407+
}
408+
409+
$ignores = [];
410+
foreach (file($gitattributes, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
411+
$line = trim($line);
412+
if ($line === '' || $line[0] === '#' || ! str_contains($line, 'export-ignore')) {
413+
continue;
414+
}
415+
416+
$path = trim(preg_split('/\s+/', $line, 2)[0] ?? '');
417+
if ($path !== '') {
418+
$ignores[] = ltrim($path, '/');
419+
}
420+
}
421+
422+
if (! empty($ignores)) {
423+
$prefix = 'vendor/'.$namespace->getFilename().'/'.$package->getFilename().'/';
424+
$patterns[$prefix] = $ignores;
425+
}
426+
}
427+
}
428+
429+
return $patterns;
430+
}
431+
307432
/**
308433
* Create ZIP bundle with cross-platform support
309434
*/
@@ -363,12 +488,12 @@ protected function addDirectoryToZip(\ZipArchive $zip, string $source, string $p
363488
{
364489
$source = rtrim(str_replace('\\', '/', $source), '/').'/';
365490

366-
$files = iterator_to_array(new \RecursiveIteratorIterator(
491+
$iterator = new \RecursiveIteratorIterator(
367492
new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
368493
\RecursiveIteratorIterator::LEAVES_ONLY
369-
));
494+
);
370495

371-
foreach ($files as $file) {
496+
foreach ($iterator as $file) {
372497
$filePath = str_replace('\\', '/', $file->getRealPath());
373498
$relativePath = ltrim(str_replace('\\', '/', substr($filePath, strlen($source))), '/');
374499

@@ -391,19 +516,14 @@ protected function addDirectoryToZip(\ZipArchive $zip, string $source, string $p
391516
}
392517
}
393518

394-
// Always exclude these directories
519+
// Safety net for files that may be created between rsync and zip
395520
if ($shouldExclude ||
396-
Str::startsWith($relativePath, 'vendor/nativephp/mobile/resources') ||
397-
Str::startsWith($relativePath, 'vendor/nativephp/mobile/vendor') ||
398-
Str::startsWith($relativePath, '.idea') ||
399-
Str::startsWith($relativePath, 'output') ||
400521
Str::startsWith($relativePath, 'storage/framework/views/') ||
401522
Str::startsWith($relativePath, 'storage/framework/cache/') ||
402523
Str::startsWith($relativePath, 'storage/framework/sessions/') ||
403524
Str::startsWith($relativePath, 'storage/app/native-build') ||
404525
Str::startsWith($relativePath, 'bootstrap/cache/') ||
405-
Str::startsWith($relativePath, 'nativephp') ||
406-
Str::startsWith($relativePath, 'public/storage') ||
526+
Str::startsWith($relativePath, '.idea') ||
407527
Str::endsWith($relativePath, '.jks') ||
408528
Str::endsWith($relativePath, '.zip')) {
409529
continue;

0 commit comments

Comments
 (0)