diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index fcba47f..0c761ed 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -54,8 +54,5 @@ jobs: - name: Install dependencies run: flutter pub get - - name: Download libgit2 library - run: flutter pub run libgit2dart:setup - - name: Run tests run: dart test --exclude-tags "remote_fetch" --platform=vm diff --git a/bin/setup.dart b/bin/setup.dart index b8b0b9c..013c3ef 100644 --- a/bin/setup.dart +++ b/bin/setup.dart @@ -1,75 +1,43 @@ import 'dart:io'; -import 'package:archive/archive.dart'; import 'package:args/command_runner.dart'; import 'package:cli_util/cli_logging.dart' show Ansi, Logger; +import 'package:libgit2dart/libgit2dart.dart'; +import 'package:libgit2dart/src/libgit2.dart'; import 'package:libgit2dart/src/util.dart'; +import 'package:path/path.dart' as path; +import 'package:pub_cache/pub_cache.dart'; -bool libgit2IsPresent(String platform) { - final result = File.fromUri( - Directory.current.uri - .resolve('.dart_tool/libgit2/$platform/${getLibName()}'), - ).existsSync(); - return result; -} - -/// Extracts a tar.gz file. -void extract(String fileName, String dir) { - final tarGzFile = File(fileName).readAsBytesSync(); - final archive = GZipDecoder().decodeBytes(tarGzFile, verify: true); - final tarData = TarDecoder().decodeBytes(archive, verify: true); - for (final file in tarData) { - File('$dir${file.name}') - ..createSync(recursive: true) - ..writeAsBytesSync(file.content as List); - } -} - -/// Downloads libgit2 from GitHub releases, extracts and places it in correct -/// directory. -Future download(String platform) async { +/// Copies prebuilt libgit2 library from package in '.pub_cache' into correct +/// directory for [platform]. +Future copyLibrary(String platform) async { final logger = Logger.standard(); final ansi = Ansi(Ansi.terminalSupportsAnsi); - if (libgit2IsPresent(platform)) { - if (libgit2Version == getVersionNumber()) { + if (File(path.join(Directory.current.path, libDir, platform, getLibName())) + .existsSync()) { + if (libgit2Version == Libgit2.version) { logger.stdout('${ansi.green}libgit2 for $platform is already available.'); } else { logger.stdout( '${ansi.red}libgit2 for $platform is outdated.\n' - 'If it is dart application run: \n' + 'Please run following commands: \n' 'dart run libgit2dart:setup clean\n' - 'dart run libgit2dart:setup\n\n' - 'If it is flutter application run: \n' - 'flutter pub run libgit2dart:setup clean\n' - 'flutter pub run libgit2dart:setup\n\n', + 'dart run libgit2dart:setup\n\n', ); } } else { - final fileName = '$platform.tar.gz'; - final downloadUrl = '$libUrl$fileName'; - logger.stdout('Downloading libgit2 for $platform'); - logger.stdout(downloadUrl); + final pubCache = PubCache(); + final pubCacheDir = + pubCache.getLatestVersion('libgit2dart')!.resolve()!.location; + final libName = getLibName(); - try { - final httpClient = HttpClient(); - final request = await httpClient.getUrl(Uri.parse(downloadUrl)); - final response = await request.close(); - final fileSink = File(fileName).openWrite(); - await response.pipe(fileSink); - await fileSink.flush(); - await fileSink.close(); - httpClient.close(); - } catch (error) { - Exception("Can't download. Check your internet connection."); - } - - logger.stdout('${ansi.yellow}Extracting libgit2 for $platform${ansi.none}'); - Directory('$libDir$platform/').createSync(recursive: true); - extract(fileName, '$libDir$platform/'); - logger.stdout('${ansi.green}Done! Cleaning up...'); - - File(fileName).deleteSync(); + logger.stdout('Copying libgit2 for $platform'); + final destination = path.join(libDir, platform); + Directory(destination).createSync(recursive: true); + File(path.join(pubCacheDir.path, platform, libName)).copySync( + path.join(destination, libName), + ); logger.stdout( '${ansi.green}Done! libgit2 for $platform is now available!' @@ -80,7 +48,7 @@ Future download(String platform) async { class CleanCommand extends Command { @override - String get description => 'Cleans downloaded libraries.'; + String get description => 'Cleans copied libgit2 libraries.'; @override String get name => 'clean'; @@ -88,7 +56,7 @@ class CleanCommand extends Command { @override void run() { final logger = Logger.standard(); - logger.stdout('cleaning...'); + logger.stdout('Cleaning...'); Directory(libDir).deleteSync(recursive: true); } } @@ -96,9 +64,9 @@ class CleanCommand extends Command { void main(List args) { final runner = CommandRunner( 'setup', - 'Downloads the libgit2 library.', + 'Setups the libgit2 library.', ); runner.addCommand(CleanCommand()); - (args.isEmpty) ? download(Platform.operatingSystem) : runner.run(args); + (args.isEmpty) ? copyLibrary(Platform.operatingSystem) : runner.run(args); } diff --git a/lib/libgit2dart.dart b/lib/libgit2dart.dart index 345c841..916aa7f 100644 --- a/lib/libgit2dart.dart +++ b/lib/libgit2dart.dart @@ -7,9 +7,9 @@ export 'src/config.dart'; export 'src/credentials.dart'; export 'src/diff.dart'; export 'src/error.dart'; -export 'src/features.dart'; export 'src/git_types.dart'; export 'src/index.dart'; +export 'src/libgit2.dart'; export 'src/mailmap.dart'; export 'src/note.dart'; export 'src/odb.dart'; diff --git a/lib/src/features.dart b/lib/src/features.dart deleted file mode 100644 index b6db02e..0000000 --- a/lib/src/features.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:libgit2dart/libgit2dart.dart'; -import 'package:libgit2dart/src/util.dart'; - -class Features { - /// Returns list of compile time options for libgit2. - static Set get list { - libgit2.git_libgit2_init(); - final featuresInt = libgit2.git_libgit2_features(); - return GitFeature.values - .where((e) => featuresInt & e.value == e.value) - .toSet(); - } -} diff --git a/lib/src/libgit2.dart b/lib/src/libgit2.dart new file mode 100644 index 0000000..5a5bc28 --- /dev/null +++ b/lib/src/libgit2.dart @@ -0,0 +1,34 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:libgit2dart/libgit2dart.dart'; +import 'package:libgit2dart/src/util.dart'; + +class Libgit2 { + /// Returns libgit2 version number. + static String get version { + libgit2.git_libgit2_init(); + + final major = calloc(); + final minor = calloc(); + final rev = calloc(); + libgit2.git_libgit2_version(major, minor, rev); + + final version = '${major.value}.${minor.value}.${rev.value}'; + + calloc.free(major); + calloc.free(minor); + calloc.free(rev); + + return version; + } + + /// Returns list of options libgit2 was compiled with. + static Set get features { + libgit2.git_libgit2_init(); + final featuresInt = libgit2.git_libgit2_features(); + return GitFeature.values + .where((e) => featuresInt & e.value == e.value) + .toSet(); + } +} diff --git a/lib/src/util.dart b/lib/src/util.dart index eec9bfd..9895215 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -4,14 +4,12 @@ import 'dart:ffi'; import 'dart:io'; import 'package:cli_util/cli_logging.dart' show Ansi, Logger; -import 'package:ffi/ffi.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; +import 'package:path/path.dart' as path; +import 'package:pub_cache/pub_cache.dart'; -const tag = 'libs-v1.3.0'; -const libUrl = - 'https://github.com/SkinnyMind/libgit2dart/releases/download/$tag/'; const libgit2Version = '1.3.0'; -const libDir = '.dart_tool/libgit2/'; +final libDir = path.join('.dart_tool', 'libgit2'); String getLibName() { var ext = 'so'; @@ -20,7 +18,7 @@ String getLibName() { ext = 'dll'; } else if (Platform.isMacOS) { ext = 'dylib'; - } else if (!(Platform.isLinux || Platform.isAndroid)) { + } else if (!(Platform.isLinux)) { throw Exception('Unsupported platform.'); } @@ -28,23 +26,43 @@ String getLibName() { } /// Checks if [File]/[Link] exists for an [uri]. -bool _doesFileExist(Uri uri) { - return File.fromUri(uri).existsSync() || Link.fromUri(uri).existsSync(); +bool _doesFileExist(String path) { + return File(path).existsSync() || Link(path).existsSync(); } -String? _resolveLibUri(String name) { - var libUri = Directory.current.uri.resolve(name); - final dartToolDir = '$libDir${Platform.operatingSystem}'; +/// Returns path to dynamic library if found. +String? _resolveLibPath(String name) { + var libPath = path.join(Directory.current.path, name); // If lib is in Present Working Directory. - if (_doesFileExist(libUri)) { - return libUri.toFilePath(windows: Platform.isWindows); + if (_doesFileExist(libPath)) { + return libPath; } - // If lib is in Present Working Directory's .dart_tool folder. - libUri = Directory.current.uri.resolve('$dartToolDir/$name'); - if (_doesFileExist(libUri)) { - return libUri.toFilePath(windows: Platform.isWindows); + // If lib is in Present Working Directory's '.dart_tool/libgit2/[platform]' folder. + libPath = path.join( + Directory.current.path, + libDir, + Platform.operatingSystem, + name, + ); + if (_doesFileExist(libPath)) { + return libPath; + } + + // If lib is in Present Working Directory's '[platform]' folder. + libPath = path.join(Directory.current.path, Platform.operatingSystem, name); + if (_doesFileExist(libPath)) { + return libPath; + } + + // If lib is in '.pub_cache' folder. + final pubCache = PubCache(); + final pubCacheDir = + pubCache.getLatestVersion('libgit2dart')!.resolve()!.location; + libPath = path.join(pubCacheDir.path, Platform.operatingSystem, name); + if (_doesFileExist(libPath)) { + return libPath; } return null; @@ -53,28 +71,15 @@ String? _resolveLibUri(String name) { DynamicLibrary loadLibrary(String name) { try { return DynamicLibrary.open( - _resolveLibUri(name) ?? name, + _resolveLibPath(name) ?? name, ); } catch (e) { final logger = Logger.standard(); final ansi = Ansi(Ansi.terminalSupportsAnsi); logger.stderr( - '${ansi.red}Failed to open the library. Make sure that required ' - 'library is in place.${ansi.none}', - ); - logger.stdout( - 'To download the library, please run the following command from the ' - 'root of your project:', - ); - logger.stdout( - '${ansi.yellow}dart run libgit2dart:setup${ansi.none} for ' - 'dart application', - ); - logger.stdout(ansi.none); - logger.stdout( - '${ansi.yellow}flutter pub run libgit2dart:setup${ansi.none} for ' - 'flutter application', + '${ansi.red}Failed to open the library. Make sure that libgit2 ' + 'library is bundled with the application.${ansi.none}', ); logger.stdout(ansi.none); rethrow; @@ -83,23 +88,6 @@ DynamicLibrary loadLibrary(String name) { final libgit2 = Libgit2(loadLibrary(getLibName())); -String getVersionNumber() { - libgit2.git_libgit2_init(); - - final major = calloc(); - final minor = calloc(); - final rev = calloc(); - libgit2.git_libgit2_version(major, minor, rev); - - final version = '${major.value}.${minor.value}.${rev.value}'; - - calloc.free(major); - calloc.free(minor); - calloc.free(rev); - - return version; -} - bool isValidShaHex(String str) { final hexRegExp = RegExp(r'^[0-9a-fA-F]+$'); return hexRegExp.hasMatch(str) && diff --git a/pubspec.yaml b/pubspec.yaml index 16c4f59..c9e0e3f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,10 +7,10 @@ environment: flutter: ">=2.5.0" dependencies: - archive: ^3.1.6 args: ^2.3.0 cli_util: ^0.3.5 ffi: ^1.1.2 + pub_cache: ^0.3.1 dev_dependencies: ffigen: ^4.0.0 diff --git a/test/features_test.dart b/test/features_test.dart deleted file mode 100644 index 457dcb4..0000000 --- a/test/features_test.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:libgit2dart/libgit2dart.dart'; -import 'package:test/test.dart'; - -void main() { - group('Features', () { - test('returns list of compile time options for libgit2', () { - expect( - Features.list, - {GitFeature.threads, GitFeature.https, GitFeature.ssh, GitFeature.nsec}, - ); - }); - }); -} diff --git a/test/libgit2_test.dart b/test/libgit2_test.dart new file mode 100644 index 0000000..5ac089a --- /dev/null +++ b/test/libgit2_test.dart @@ -0,0 +1,19 @@ +import 'package:libgit2dart/libgit2dart.dart'; +import 'package:libgit2dart/src/libgit2.dart'; +import 'package:libgit2dart/src/util.dart'; +import 'package:test/test.dart'; + +void main() { + group('Libgit2', () { + test('returns up to date version of libgit2', () { + expect(Libgit2.version, libgit2Version); + }); + + test('returns list of options libgit2 was compiled with', () { + expect( + Libgit2.features, + {GitFeature.threads, GitFeature.https, GitFeature.ssh, GitFeature.nsec}, + ); + }); + }); +}