| // Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| @TestOn('vm') |
| library; |
| |
| import 'dart:io'; |
| |
| import 'package:pub/src/path.dart'; |
| import 'package:test/test.dart'; |
| |
| import '../../descriptor.dart' as d; |
| import '../../test_pub.dart'; |
| |
| void main() { |
| test('git dependencies with LFS', () async { |
| final result = Process.runSync('git', ['lfs', 'version']); |
| if (result.exitCode != 0) { |
| fail('git-lfs not installed'); |
| } |
| |
| final repo = d.git('foo', [ |
| d.libDir('foo'), |
| d.libPubspec('foo', '1.0.0'), |
| d.file('large.lfs', 'actual lfs content'), |
| ]); |
| await repo.create(); |
| |
| // Initialize LFS in the remote repo. |
| // --local Sets the "lfs" smudge and clean filters in the local repository's |
| // git config, instead of the global git config (~/.gitconfig). |
| await repo.runGit(['lfs', 'install', '--local']); |
| await repo.runGit(['lfs', 'track', '*.lfs']); |
| await repo.runGit(['add', '.gitattributes']); |
| // We need to re-add the lfs file to make sure it's picked up by LFS. |
| await repo.runGit(['add', 'large.lfs']); |
| await repo.commit(); |
| |
| // Verify it is an LFS file in the remote. |
| final catResult = Process.runSync('git', [ |
| 'cat-file', |
| '-p', |
| 'HEAD:large.lfs', |
| ], workingDirectory: p.join(d.sandbox, 'foo')); |
| expect( |
| catResult.stdout.toString(), |
| contains('version https://git-lfs.github.com/spec/v1'), |
| ); |
| |
| await d.dir(appPath, [ |
| d.pubspec({ |
| 'name': 'myapp', |
| 'dependencies': { |
| 'foo': { |
| 'git': {'url': '../foo'}, |
| }, |
| }, |
| }), |
| ]).create(); |
| |
| await runPub(args: ['get']); |
| |
| final fooPathInCache = p.join(d.sandbox, cachePath, 'git'); |
| final revisionCacheDirs = |
| Directory(fooPathInCache) |
| .listSync() |
| .whereType<Directory>() |
| .where( |
| (dir) => |
| p.basename(dir.path).startsWith('foo-') && |
| p.basename(p.dirname(dir.path)) == 'git', |
| ) |
| .toList(); |
| |
| expect(revisionCacheDirs, hasLength(1)); |
| final revisionCacheDir = revisionCacheDirs.first.path; |
| |
| // Verify lfsurl is set. |
| final configResult = Process.runSync('git', [ |
| 'config', |
| 'remote.origin.lfsurl', |
| ], workingDirectory: revisionCacheDir); |
| expect(configResult.stdout.toString().trim(), isNotEmpty); |
| |
| // Verify the file is smudged (contains actual content, not LFS pointer). |
| final lfsFile = File(p.join(revisionCacheDir, 'large.lfs')); |
| expect(lfsFile.readAsStringSync(), 'actual lfs content'); |
| |
| // Test repair as well. |
| lfsFile.writeAsStringSync('corrupted content'); |
| await runPub(args: ['cache', 'repair', '--all']); |
| |
| expect(lfsFile.readAsStringSync(), 'actual lfs content'); |
| }); |
| |
| test( |
| 'regular git dependencies work even if git-lfs is "broken" or missing', |
| () async { |
| final repo = d.git('foo', [ |
| d.libDir('foo'), |
| d.libPubspec('foo', '1.0.0'), |
| ]); |
| await repo.create(); |
| |
| await d.dir(appPath, [ |
| d.pubspec({ |
| 'name': 'myapp', |
| 'dependencies': { |
| 'foo': { |
| 'git': {'url': '../foo'}, |
| }, |
| }, |
| }), |
| ]).create(); |
| |
| // Create a directory with a "broken" git-lfs to simulate it failing if |
| // called. |
| final fakeBinDir = p.join(d.sandbox, 'fake_bin'); |
| Directory(fakeBinDir).createSync(); |
| final fakeLfs = p.join(fakeBinDir, 'git-lfs'); |
| File(fakeLfs).writeAsStringSync(''' |
| #!/bin/sh |
| echo "LFS called unexpectedly" |
| exit 1 |
| '''); |
| if (!Platform.isWindows) { |
| Process.runSync('chmod', ['+x', fakeLfs]); |
| } |
| |
| final separator = Platform.isWindows ? ';' : ':'; |
| final newPath = '$fakeBinDir$separator${Platform.environment['PATH']}'; |
| |
| // This should work because a regular repo doesn't trigger git-lfs. |
| await runPub(args: ['get'], environment: {'PATH': newPath}); |
| |
| await d.dir(appPath, [ |
| d.dir('.dart_tool', [d.file('package_config.json', contains('foo'))]), |
| ]).validate(); |
| |
| // Also verify that even if we set lfsurl, git doesn't care if lfs is |
| // missing for a regular repo. |
| final fooPathInCache = p.join(d.sandbox, cachePath, 'git'); |
| final revisionCacheDir = |
| Directory(fooPathInCache) |
| .listSync() |
| .whereType<Directory>() |
| .firstWhere((dir) => p.basename(dir.path).startsWith('foo-')) |
| .path; |
| |
| final result = Process.runSync('git', [ |
| 'config', |
| 'remote.origin.lfsurl', |
| ], workingDirectory: revisionCacheDir); |
| expect(result.stdout.toString().trim(), isNotEmpty); |
| }, |
| ); |
| } |