From 29f332c82263cd8e8897069007f0cc3e6fd8b62d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Mon, 5 Aug 2024 13:26:16 -0700 Subject: [PATCH] Add migration to git ignore SwiftPM build directories (#152766) Opening a Swift package in Xcode generates `.build` and `.swiftpm` directories. These should be ignored as they contain intermediary build artifacts that aren't meant to be checked-in. Part of https://github.com/flutter/flutter/issues/148018 --- packages/flutter_tools/lib/src/ios/mac.dart | 2 + .../lib/src/macos/build_macos.dart | 2 + ...t_package_manager_gitignore_migration.dart | 63 +++++++++ packages/flutter_tools/lib/src/project.dart | 3 + .../templates/app_shared/.gitignore.tmpl | 2 + .../templates/plugin_shared/.gitignore.tmpl | 2 + ...kage_manager_gitignore_migration_test.dart | 132 ++++++++++++++++++ 7 files changed, 206 insertions(+) create mode 100644 packages/flutter_tools/lib/src/migrations/swift_package_manager_gitignore_migration.dart create mode 100644 packages/flutter_tools/test/general.shard/migrations/swift_package_manager_gitignore_migration_test.dart diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index a6b7616cd5..96ec94dc82 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart @@ -24,6 +24,7 @@ import '../globals.dart' as globals; import '../macos/cocoapod_utils.dart'; import '../macos/swift_package_manager.dart'; import '../macos/xcode.dart'; +import '../migrations/swift_package_manager_gitignore_migration.dart'; import '../migrations/swift_package_manager_integration_migration.dart'; import '../migrations/xcode_project_object_version_migration.dart'; import '../migrations/xcode_script_build_phase_migration.dart'; @@ -175,6 +176,7 @@ Future buildXcodeProject({ fileSystem: globals.fs, plistParser: globals.plistParser, ), + SwiftPackageManagerGitignoreMigration(project, globals.logger), ]; final ProjectMigration migration = ProjectMigration(migrators); diff --git a/packages/flutter_tools/lib/src/macos/build_macos.dart b/packages/flutter_tools/lib/src/macos/build_macos.dart index 4a13eda6fb..5b8b186a23 100644 --- a/packages/flutter_tools/lib/src/macos/build_macos.dart +++ b/packages/flutter_tools/lib/src/macos/build_macos.dart @@ -16,6 +16,7 @@ import '../convert.dart'; import '../globals.dart' as globals; import '../ios/xcode_build_settings.dart'; import '../ios/xcodeproj.dart'; +import '../migrations/swift_package_manager_gitignore_migration.dart'; import '../migrations/swift_package_manager_integration_migration.dart'; import '../migrations/xcode_project_object_version_migration.dart'; import '../migrations/xcode_script_build_phase_migration.dart'; @@ -100,6 +101,7 @@ Future buildMacOS({ fileSystem: globals.fs, plistParser: globals.plistParser, ), + SwiftPackageManagerGitignoreMigration(flutterProject, globals.logger), ]; final ProjectMigration migration = ProjectMigration(migrators); diff --git a/packages/flutter_tools/lib/src/migrations/swift_package_manager_gitignore_migration.dart b/packages/flutter_tools/lib/src/migrations/swift_package_manager_gitignore_migration.dart new file mode 100644 index 0000000000..9665698a9c --- /dev/null +++ b/packages/flutter_tools/lib/src/migrations/swift_package_manager_gitignore_migration.dart @@ -0,0 +1,63 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import '../base/file_system.dart'; +import '../base/project_migrator.dart'; +import '../project.dart'; + +const String _gitignoreBefore = ''' +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ +'''; + +const String _gitignoreAfter = ''' +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ +'''; + +/// Adds `.build/` and `.swiftpm/` to the .gitignore file. +class SwiftPackageManagerGitignoreMigration extends ProjectMigrator { + SwiftPackageManagerGitignoreMigration(FlutterProject project, super.logger) + : _gitignoreFile = project.gitignoreFile; + + final File _gitignoreFile; + + @override + Future migrate() async { + if (!_gitignoreFile.existsSync()) { + logger.printTrace( + '.gitignore file not found, skipping Swift Package Manager .gitignore migration.', + ); + return; + } + + final String originalContent = _gitignoreFile.readAsStringSync(); + + // Skip if .gitignore is already migrated. + if (originalContent.contains('.build/') && originalContent.contains('.swiftpm/')) { + return; + } + + final String newContent = originalContent.replaceFirst( + _gitignoreBefore, + _gitignoreAfter, + ); + if (newContent != originalContent) { + logger.printWarning( + '.gitignore does not ignore Swift Package Manager build directories, updating.', + ); + _gitignoreFile.writeAsStringSync(newContent); + } + } +} diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index fd8a814d0a..96f7d189f3 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -233,6 +233,9 @@ class FlutterProject { /// which contains the dependencies each plugin depends on. File get flutterPluginsDependenciesFile => directory.childFile('.flutter-plugins-dependencies'); + /// The `.gitignore` file of this project. + File get gitignoreFile => directory.childFile('.gitignore'); + /// The `.dart-tool` directory of this project. Directory get dartTool => directory.childDirectory('.dart_tool'); diff --git a/packages/flutter_tools/templates/app_shared/.gitignore.tmpl b/packages/flutter_tools/templates/app_shared/.gitignore.tmpl index 29a3a5017f..79c113f9b5 100644 --- a/packages/flutter_tools/templates/app_shared/.gitignore.tmpl +++ b/packages/flutter_tools/templates/app_shared/.gitignore.tmpl @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/packages/flutter_tools/templates/plugin_shared/.gitignore.tmpl b/packages/flutter_tools/templates/plugin_shared/.gitignore.tmpl index ac5aa9893e..ea810a521d 100644 --- a/packages/flutter_tools/templates/plugin_shared/.gitignore.tmpl +++ b/packages/flutter_tools/templates/plugin_shared/.gitignore.tmpl @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/packages/flutter_tools/test/general.shard/migrations/swift_package_manager_gitignore_migration_test.dart b/packages/flutter_tools/test/general.shard/migrations/swift_package_manager_gitignore_migration_test.dart new file mode 100644 index 0000000000..6b442d7752 --- /dev/null +++ b/packages/flutter_tools/test/general.shard/migrations/swift_package_manager_gitignore_migration_test.dart @@ -0,0 +1,132 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/terminal.dart'; +import 'package:flutter_tools/src/migrations/swift_package_manager_gitignore_migration.dart'; + +import 'package:flutter_tools/src/project.dart'; +import 'package:test/fake.dart'; + +import '../../src/common.dart'; + +void main() { + group('Swift Package Manager .gitignore migration', () { + late MemoryFileSystem memoryFileSystem; + late BufferLogger testLogger; + late FakeFlutterProject mockProject; + late File gitignoreFile; + + setUp(() { + memoryFileSystem = MemoryFileSystem.test(); + gitignoreFile = memoryFileSystem.file('.gitignore'); + + testLogger = BufferLogger( + terminal: Terminal.test(), + outputPreferences: OutputPreferences.test(), + ); + + mockProject = FakeFlutterProject(fileSystem: memoryFileSystem); + }); + + testWithoutContext('skipped if .gitignore file is missing', () { + final SwiftPackageManagerGitignoreMigration migration = SwiftPackageManagerGitignoreMigration( + mockProject, + testLogger, + ); + migration.migrate(); + expect(gitignoreFile.existsSync(), isFalse); + + expect( + testLogger.traceText, + contains('.gitignore file not found, skipping Swift Package Manager .gitignore migration.'), + ); + expect(testLogger.warningText, isEmpty); + }); + + testWithoutContext('skipped if nothing to migrate', () { + const String gitignoreFileContents = 'Nothing to migrate'; + + gitignoreFile.writeAsStringSync(gitignoreFileContents); + + final DateTime updatedAt = gitignoreFile.lastModifiedSync(); + + final SwiftPackageManagerGitignoreMigration migration = SwiftPackageManagerGitignoreMigration( + mockProject, + testLogger, + ); + migration.migrate(); + + expect(gitignoreFile.lastModifiedSync(), updatedAt); + expect(gitignoreFile.readAsStringSync(), gitignoreFileContents); + expect(testLogger.warningText, isEmpty); + }); + + testWithoutContext('skipped if already migrated', () { + const String gitignoreFileContents = ''' +.build/ +.swiftpm/ +'''; + + gitignoreFile.writeAsStringSync(gitignoreFileContents); + + final DateTime updatedAt = gitignoreFile.lastModifiedSync(); + + final SwiftPackageManagerGitignoreMigration migration = SwiftPackageManagerGitignoreMigration( + mockProject, + testLogger, + ); + migration.migrate(); + + expect(gitignoreFile.lastModifiedSync(), updatedAt); + expect(gitignoreFile.readAsStringSync(), gitignoreFileContents); + expect(testLogger.warningText, isEmpty); + }); + + testWithoutContext('migrates project to ignore Swift Package Manager build directories', () { + gitignoreFile.writeAsStringSync( + '.DS_Store\n' + '.atom/\n' + '.buildlog/\n' + '.history\n' + '.svn/\n' + 'migrate_working_dir/\n' + ); + + final SwiftPackageManagerGitignoreMigration migration = SwiftPackageManagerGitignoreMigration( + mockProject, + testLogger, + ); + migration.migrate(); + + expect( + gitignoreFile.readAsStringSync(), + '.DS_Store\n' + '.atom/\n' + '.build/\n' + '.buildlog/\n' + '.history\n' + '.svn/\n' + '.swiftpm/\n' + 'migrate_working_dir/\n' + ); + + expect( + testLogger.warningText, + contains('.gitignore does not ignore Swift Package Manager build directories, updating.'), + ); + }); + }); +} + +class FakeFlutterProject extends Fake implements FlutterProject { + FakeFlutterProject({ + required MemoryFileSystem fileSystem, + }) : gitignoreFile = fileSystem.file('.gitignore'); + + @override + File gitignoreFile; +}