Skip to content

Commit c36b29c

Browse files
authored
fix: Improve archiver gitignore matching (#60)
1 parent 6da88b1 commit c36b29c

File tree

1 file changed

+87
-21
lines changed

1 file changed

+87
-21
lines changed

packages/globe_cli/lib/src/utils/archiver.dart

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import 'dart:io';
22

33
import 'package:archive/archive_io.dart';
4+
import 'package:get_it/get_it.dart';
45
import 'package:glob/glob.dart';
6+
import 'package:mason_logger/mason_logger.dart';
57
import 'package:path/path.dart' as p;
68
import 'package:pool/pool.dart';
79

810
/// Creates a zip archive of the given [directory].
911
Future<List<int>> zipDir(Directory directory) async {
12+
final logger = GetIt.I<Logger>();
13+
14+
logger.detail('Archiving directory: ${directory.path}');
15+
1016
// NOTE: We can't use p.join here due to https://github.com/dart-lang/path/issues/37
1117
// being present on Windows.
1218
final directoryPath = directory.path + p.separator;
@@ -23,48 +29,106 @@ Future<List<int>> zipDir(Directory directory) async {
2329

2430
for (final file in gitignoreFiles) {
2531
if (file.existsSync()) {
32+
logger.detail('Found .gitignore file: ${file.path}');
2633
gitignores += '${await file.readAsString()}\n';
2734
}
2835
}
2936

30-
final exclude = gitignores
37+
// Initialize two lists to hold exclusion and inclusion patterns
38+
final excludePatterns = <String>[];
39+
final includePatterns = <String>[];
40+
41+
gitignores
3142
.split('\n')
3243
.where((line) => line.isNotEmpty && !line.startsWith('#'))
33-
.map((line) => line.startsWith('/') ? line.substring(1) : line)
34-
.map((line) => '**$line**')
35-
.toList();
44+
.forEach((line) {
45+
// Check if the line is an inclusion pattern (negation pattern)
46+
final isInclusionPattern = line.startsWith('!');
47+
48+
if (isInclusionPattern) {
49+
// Remove the leading '!' and process the line
50+
line = line.substring(1);
51+
}
52+
53+
// Process directory patterns by adding `**/` at the start if not already rooted and `/**` at the end
54+
final isDirectory = line.endsWith('/');
55+
56+
// Remove leading `/` from the line if present.
57+
var processedLine = line.startsWith('/') ? line.substring(1) : line;
58+
59+
if (isDirectory) {
60+
// If a directory, prepend with `**/` and append with `/**` if it doesn't end with `/`.
61+
processedLine =
62+
"**/$processedLine${processedLine.endsWith('/') ? '' : '/**'}";
63+
} else {
64+
// For files, add `**/` at the start unless the pattern already starts with a specific directory
65+
processedLine = '**/$processedLine';
66+
}
67+
68+
// Add the processed line to the appropriate list
69+
if (isInclusionPattern) {
70+
includePatterns.add(processedLine);
71+
} else {
72+
excludePatterns.add(processedLine);
73+
}
74+
});
3675

3776
// Exclude common files.
38-
exclude.addAll([
39-
'**.map',
40-
'**.git**',
41-
'**.dart_tool**',
42-
'**.packages**',
43-
'**.idea**',
44-
'**.vscode**',
45-
'**build**',
46-
'**android**',
47-
'**ios**',
48-
'**linux**',
49-
'**macos**',
50-
'**windows**',
77+
excludePatterns.addAll([
78+
'**/*.map',
79+
'**/.git/**',
80+
'**/.dart_tool/**',
81+
'**/.packages',
82+
'**/.idea/**',
83+
'**/.vscode/**',
84+
'**/build/**',
85+
'**/android/**',
86+
'**/ios/**',
87+
'**/linux/**',
88+
'**/macos/**',
89+
'**/windows/**',
5190
]);
5291

92+
logger.detail('Excluding patterns: $excludePatterns');
93+
logger.detail('Including patterns: $includePatterns');
94+
5395
final pool = Pool(10);
5496

5597
Future<void> addEntityToArchive(FileSystemEntity entity) async {
5698
if (entity is! File) return;
5799

58-
// Exclude files that match the patterns in the `.gitignore` file.
59-
for (final pattern in exclude) {
100+
// By default, don't exclude the entity.
101+
var excludeEntity = false;
102+
103+
// Exclude files that match the patterns in the exclude list.
104+
for (final pattern in excludePatterns) {
60105
final glob = Glob(pattern);
61106
final match = glob.matches(entity.path.replaceFirst(directoryPath, ''));
62-
63107
if (match) {
64-
return;
108+
excludeEntity = true;
109+
break;
110+
}
111+
}
112+
113+
// If the entity was marked for exclusion, check if it should be included again.
114+
if (excludeEntity) {
115+
for (final pattern in includePatterns) {
116+
final glob = Glob(pattern);
117+
final match = glob.matches(entity.path.replaceFirst(directoryPath, ''));
118+
if (match) {
119+
excludeEntity = false;
120+
break;
121+
}
65122
}
66123
}
67124

125+
// If excludeEntity is true after checking both lists, the entity should be excluded.
126+
if (excludeEntity) {
127+
logger.detail('Excluding file from archiving: ${entity.path}');
128+
return; // Skip this entity, move to the next one
129+
}
130+
131+
// Archive the file
68132
await pool.withResource(() async {
69133
final length = await entity.length();
70134
final bytes = await entity.readAsBytes();
@@ -95,5 +159,7 @@ Future<List<int>> zipDir(Directory directory) async {
95159
throw Exception('Failed to encode archive.');
96160
}
97161

162+
logger.detail('Archive size: ${encoded.length} bytes');
163+
98164
return encoded;
99165
}

0 commit comments

Comments
 (0)