From c935f2188800de6981cacc7f3c4aa8e65c65895f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 15 Jan 2025 23:25:57 +0000 Subject: [PATCH] Don't follow links when copying paths The original intent of this method was to copy links as links, not to deeply copy the content of linked directories. This was never tested and the original implementation did not have this behavior. Change behavior in place without a major version change since the new behavior is what was intended. Add a test that both the sync and async implementation copies links as links. --- pkgs/io/CHANGELOG.md | 5 +++++ pkgs/io/lib/src/copy_path.dart | 6 ++++-- pkgs/io/pubspec.yaml | 2 +- pkgs/io/test/copy_path_test.dart | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/pkgs/io/CHANGELOG.md b/pkgs/io/CHANGELOG.md index e0631fa95..d0a95f614 100644 --- a/pkgs/io/CHANGELOG.md +++ b/pkgs/io/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.1.0 + +* Copy links as links instead of deeply copying the content for all links in + `copyPath` and `copyPathSync`. + ## 1.0.5 * Require Dart 3.4. diff --git a/pkgs/io/lib/src/copy_path.dart b/pkgs/io/lib/src/copy_path.dart index 3a999b610..60f382e9e 100644 --- a/pkgs/io/lib/src/copy_path.dart +++ b/pkgs/io/lib/src/copy_path.dart @@ -30,7 +30,8 @@ Future copyPath(String from, String to) async { return; } await Directory(to).create(recursive: true); - await for (final file in Directory(from).list(recursive: true)) { + await for (final file + in Directory(from).list(recursive: true, followLinks: false)) { final copyTo = p.join(to, p.relative(file.path, from: from)); if (file is Directory) { await Directory(copyTo).create(recursive: true); @@ -56,7 +57,8 @@ void copyPathSync(String from, String to) { return; } Directory(to).createSync(recursive: true); - for (final file in Directory(from).listSync(recursive: true)) { + for (final file + in Directory(from).listSync(recursive: true, followLinks: false)) { final copyTo = p.join(to, p.relative(file.path, from: from)); if (file is Directory) { Directory(copyTo).createSync(recursive: true); diff --git a/pkgs/io/pubspec.yaml b/pkgs/io/pubspec.yaml index 7e00d993d..46b76b362 100644 --- a/pkgs/io/pubspec.yaml +++ b/pkgs/io/pubspec.yaml @@ -2,7 +2,7 @@ name: io description: >- Utilities for the Dart VM Runtime including support for ANSI colors, file copying, and standard exit code values. -version: 1.0.5 +version: 1.1.0-wip repository: https://github.com/dart-lang/tools/tree/main/pkgs/io environment: diff --git a/pkgs/io/test/copy_path_test.dart b/pkgs/io/test/copy_path_test.dart index 0c72a0b4d..565fc96f0 100644 --- a/pkgs/io/test/copy_path_test.dart +++ b/pkgs/io/test/copy_path_test.dart @@ -5,6 +5,8 @@ @TestOn('vm') library; +import 'dart:io'; + import 'package:io/io.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; @@ -33,6 +35,36 @@ void main() { throwsArgumentError, ); }); + + test('links are preserved in async copy', () async { + await _create(); + const linkTarget = 'link_target'; + final targetPath = p.join(d.sandbox, linkTarget); + const linkSource = 'link_source'; + await d.dir(linkTarget).create(); + await Link(p.join(d.sandbox, _parentDir, linkSource)).create(targetPath); + + await copyPath(p.join(d.sandbox, _parentDir), p.join(d.sandbox, _copyDir)); + + final expectedLink = Link(p.join(d.sandbox, _copyDir, linkSource)); + expect(await expectedLink.exists(), isTrue); + expect(await expectedLink.target(), targetPath); + }); + + test('links are preserved in sync copy', () async { + await _create(); + const linkTarget = 'link_target'; + final targetPath = p.join(d.sandbox, linkTarget); + const linkSource = 'link_source'; + await d.dir(linkTarget).create(); + await Link(p.join(d.sandbox, _parentDir, linkSource)).create(targetPath); + + copyPathSync(p.join(d.sandbox, _parentDir), p.join(d.sandbox, _copyDir)); + + final expectedLink = Link(p.join(d.sandbox, _copyDir, linkSource)); + expect(await expectedLink.exists(), isTrue); + expect(await expectedLink.target(), targetPath); + }); } const _parentDir = 'parent';