From 7814a6aebafc45f5af02a577ebe300c476f3639d Mon Sep 17 00:00:00 2001 From: Nicolai Stucki - SNa Date: Mon, 24 Feb 2025 21:49:34 +0100 Subject: [PATCH 01/10] Adding copies config --- gitman/models/source.py | 33 +++++++++++++++++++++++++++++++++ gitman/shell.py | 12 ++++++++++++ 2 files changed, 45 insertions(+) diff --git a/gitman/models/source.py b/gitman/models/source.py index 886fbf7..825d3d9 100644 --- a/gitman/models/source.py +++ b/gitman/models/source.py @@ -15,6 +15,11 @@ class Link: source: str = "" target: str = "" +@dataclass +class Copy: + source: str = "" + target: str = "" + @dataclass class Source: @@ -29,6 +34,7 @@ class Source: | `params` | Additional arguments for `clone` | No | `null` | | `sparse_paths` | Controls partial checkout | No | `[]` | | `links` | Creates symlinks within a project | No | `[]` | + | `copies` | Creates copies within of a file or folder | No | `[]` | | `scripts` | Shell commands to run after checkout | No | `[]` | ### Params @@ -72,6 +78,7 @@ class Source: params: Optional[str] = None sparse_paths: List[str] = field(default_factory=list) links: List[Link] = field(default_factory=list) + copies: List[Copy] = field(default_factory=list) scripts: List[str] = field(default_factory=list) @@ -205,6 +212,17 @@ def create_links(self, root: str, *, force: bool = False): source = os.path.join(relpath, os.path.normpath(link.source)) create_sym_link(source, target, force=force) + def create_copies(self, root: str, *, force: bool = False): + """Create copies from source to target""" + if not self.copies: + return + + for copy in self.copies: + target = os.path.join(root, os.path.normpath(copy.target)) + relpath = os.path.relpath(os.getcwd(), os.path.dirname(target)) + source = os.path.join(relpath, os.path.normpath(copy.source)) + create_copy(source, target, force=force) + def run_scripts(self, force: bool = False, show_shell_stdout: bool = False): log.info("Running install scripts...") @@ -317,6 +335,7 @@ def lock( name=self.name, rev=rev, links=self.links, + copy=self.copy, scripts=self.scripts, sparse_paths=self.sparse_paths, ) @@ -349,3 +368,17 @@ def create_sym_link(source: str, target: str, *, force: bool): raise exceptions.UncommittedChanges(msg) shell.ln(source, target) + + +def create_copy(source: str, target: str, *, force: bool): + log.info("Creating a copy...") + + if os.path.islink(target): + os.remove(target) + elif os.path.exists(target): + if force: + shell.rm(target) + else: + msg = "Preexisting target location at {}".format(target) + log.warn(msg) + shell.cp(source, target) diff --git a/gitman/shell.py b/gitman/shell.py index 336e47e..5cea11a 100644 --- a/gitman/shell.py +++ b/gitman/shell.py @@ -2,6 +2,7 @@ import os import subprocess +import shutil import log @@ -127,6 +128,17 @@ def ln(source, target): os.symlink(source, target) +def cp(source, target): + dirpath = os.path.dirname(target) + if not os.path.isdir(dirpath): + mkdir(dirpath) + + if os.path.isdir(source): + shutil.copytree(src=source, dst=target) + else: + shutil.copy2(src=source, dst=target) + + def rm(path): if os.name == "nt": if os.path.isfile(path): From f9be2cb4e2685bc032ccae03ad4de6e2157e88ee Mon Sep 17 00:00:00 2001 From: Nicolai Stucki - SNa Date: Mon, 24 Feb 2025 22:00:04 +0100 Subject: [PATCH 02/10] Adding documentation --- docs/use-cases/multiple-copies.md | 48 +++++++++++++++++++++++++++++++ gitman.yml | 17 +++++++++++ gitman/models/source.py | 4 +++ 3 files changed, 69 insertions(+) create mode 100644 docs/use-cases/multiple-copies.md diff --git a/docs/use-cases/multiple-copies.md b/docs/use-cases/multiple-copies.md new file mode 100644 index 0000000..21b8753 --- /dev/null +++ b/docs/use-cases/multiple-copies.md @@ -0,0 +1,48 @@ +# Using Multiple Copies + +This feature can be used to create as many copies as you need from one repository. This can be helpful e.g. on Windows where you need administrator priviledges in order to create a symbolic link. + +## The Syntax + +Let's say we have a simple project structure: + +```text +|- include +|- src +|- docs +``` + +with the following `gitman.yml`: + +```yaml +location: .gitman + +sources: + - repo: + name: my_dependency + rev: v1.0.3 + copies: + - source: include + target: vendor/partial_repo + - target: vendor/full_repo +``` + +This will result in the following copies: + +- `/vendor/partial_repo` -> `/.gitman/my_dependency/include` +- `/vendor/full_repo` -> `/.gitman/my_dependency` + +## Alternative Syntax + +```yaml +location: vendor + +sources: + - repo: + name: my_dependency + rev: v1.0.3 + copies: + - { source: src, target: partial_repo } + - { target: full_repo } +``` + diff --git a/gitman.yml b/gitman.yml index 8e69277..fd0bfb4 100644 --- a/gitman.yml +++ b/gitman.yml @@ -10,6 +10,8 @@ sources: links: - source: '' target: demo/example + copies: + - scripts: - cat .noserc - make foobar @@ -22,6 +24,9 @@ sources: - links: - + copies: + - source: '' + target: demo/example scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -33,6 +38,8 @@ sources: - links: - + copies: + - scripts: - echo "Hello, World!" - pwd @@ -45,6 +52,8 @@ sources: - links: - + copies: + - scripts: - sources_locked: @@ -57,6 +66,8 @@ sources_locked: - links: - + copies: + - scripts: - cat .noserc - make foobar @@ -69,6 +80,8 @@ sources_locked: - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -80,6 +93,8 @@ sources_locked: - links: - + copies: + - scripts: - echo "Hello, World!" - pwd @@ -92,6 +107,8 @@ sources_locked: - links: - + copies: + - scripts: - groups: diff --git a/gitman/models/source.py b/gitman/models/source.py index 825d3d9..664c2de 100644 --- a/gitman/models/source.py +++ b/gitman/models/source.py @@ -57,6 +57,10 @@ class Source: See [using multiple links][using-multiple-links] for more information. + ### Copies + + See [using multiple copies][using-multiple-copies] for more information. + ### Scripts Scripts can be used to run post-checkout commands such us build steps. For example: From db644d09136b046a488989767924553168fe8bcf Mon Sep 17 00:00:00 2001 From: Nicolai Stucki - SNa Date: Mon, 24 Feb 2025 22:05:19 +0100 Subject: [PATCH 03/10] Adding more documentation and testing dummies --- README.md | 2 ++ docs/setup/git.md | 2 +- mkdocs.yml | 1 + tests/test_api.py | 20 ++++++++++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index abd4575..e3c5903 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,8 @@ sources: - repo: "https://github.com/google/material-design-icons" name: material-design-icons rev: master + copies: + - target: Icons groups: - name: code diff --git a/docs/setup/git.md b/docs/setup/git.md index f284743..901a983 100644 --- a/docs/setup/git.md +++ b/docs/setup/git.md @@ -52,7 +52,7 @@ The token can also be written to `.netrc` during builds, see the guide for [Trav ## Symlinks on Windows -If you're using Windows, there are some additional prerequisites to ensure Gitman works seamlessly with symbolic links. +If you're using Windows, there are some additional prerequisites to ensure Gitman works seamlessly with symbolic links. You could consider using copies instead. ### Grant Permissions diff --git a/mkdocs.yml b/mkdocs.yml index d1acf1e..911e0af 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -36,6 +36,7 @@ nav: - Sparse Checkouts: use-cases/sparse-checkouts.md - Default Groups: use-cases/default-groups.md - Multiple Links: use-cases/multiple-links.md + - Multiple Copies: use-cases/multiple-copies.md - Extras: - Git SVN Bridge: extras/git-svn-bridge.md - Bundled Application: extras/bundled-application.md diff --git a/tests/test_api.py b/tests/test_api.py index 623b647..d85cc21 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -32,6 +32,8 @@ - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -43,6 +45,8 @@ - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -54,6 +58,8 @@ - links: - + copies: + - scripts: - sources_locked: @@ -105,6 +111,8 @@ def it_creates_a_new_config_file(tmpdir): - links: - + copies: + - scripts: - sources_locked: @@ -117,6 +125,8 @@ def it_creates_a_new_config_file(tmpdir): - links: - + copies: + - scripts: - default_group: '' @@ -156,6 +166,8 @@ def it_merges_sources(config): rev: main links: - + copies: + - scripts: - sources_locked: @@ -165,6 +177,8 @@ def it_merges_sources(config): rev: example-branch links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -173,6 +187,8 @@ def it_merges_sources(config): rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 links: - + copies: + - scripts: - """ @@ -202,6 +218,8 @@ def it_can_handle_missing_locked_sources(config): rev: example-branch links: - + copies: + - scripts: - sources_locked: @@ -211,6 +229,8 @@ def it_can_handle_missing_locked_sources(config): rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 links: - + copies: + - scripts: - """ From feed45bf6a51a92224188aaa13a7218485ada69a Mon Sep 17 00:00:00 2001 From: sningo Date: Tue, 25 Feb 2025 21:43:06 +0100 Subject: [PATCH 04/10] Fixed existing tests --- gitman/models/source.py | 2 +- tests/test_api.py | 96 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/gitman/models/source.py b/gitman/models/source.py index 664c2de..ba16926 100644 --- a/gitman/models/source.py +++ b/gitman/models/source.py @@ -339,7 +339,7 @@ def lock( name=self.name, rev=rev, links=self.links, - copy=self.copy, + copies=self.copies, scripts=self.scripts, sparse_paths=self.sparse_paths, ) diff --git a/tests/test_api.py b/tests/test_api.py index d85cc21..856de78 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -264,6 +264,8 @@ def config_with_link(config): rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 links: - target: my_link + copies: + - scripts: - """ @@ -310,6 +312,8 @@ def config_with_links(config): target: gmd_3 - source: gitman_sources/gmd_4 target: gmd_4 + copies: + - scripts: - """ @@ -355,6 +359,8 @@ def config_with_scripts(config): rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 links: - + copies: + - scripts: - make foobar """ @@ -386,6 +392,8 @@ def config_with_scripts(config): rev: ddbe17ef173538d1fda29bd99a14bab3c5d86e78 links: - + copies: + - scripts: - """ @@ -421,6 +429,8 @@ def config_with_group(config): rev: example-branch links: - + copies: + - scripts: - - name: gitman_2 @@ -432,6 +442,8 @@ def config_with_group(config): rev: example-tag links: - + copies: + - scripts: - groups: @@ -485,6 +497,8 @@ def config_with_default_group(config): rev: example-branch links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -496,6 +510,8 @@ def config_with_default_group(config): rev: example-tag links: - + copies: + - scripts: - groups: @@ -527,6 +543,8 @@ def config_without_default_group(config): rev: example-branch links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -538,6 +556,8 @@ def config_without_default_group(config): rev: example-tag links: - + copies: + - scripts: - groups: @@ -668,6 +688,8 @@ def it_locks_previously_unlocked_dependencies(config): rev: example-branch links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -679,6 +701,8 @@ def it_locks_previously_unlocked_dependencies(config): rev: example-tag links: - + copies: + - scripts: - sources_locked: @@ -691,6 +715,8 @@ def it_locks_previously_unlocked_dependencies(config): rev: (old revision) links: - + copies: + - scripts: - groups: @@ -715,6 +741,8 @@ def it_locks_previously_unlocked_dependencies(config): - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -726,6 +754,8 @@ def it_locks_previously_unlocked_dependencies(config): - links: - + copies: + - scripts: - sources_locked: @@ -738,6 +768,8 @@ def it_locks_previously_unlocked_dependencies(config): - links: - + copies: + - scripts: - groups: @@ -760,6 +792,8 @@ def it_should_not_lock_dependencies_when_disabled(config): - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -771,6 +805,8 @@ def it_should_not_lock_dependencies_when_disabled(config): rev: example-tag links: - + copies: + - scripts: - sources_locked: @@ -783,6 +819,8 @@ def it_should_not_lock_dependencies_when_disabled(config): - links: - + copies: + - scripts: - groups: @@ -806,6 +844,8 @@ def it_should_not_lock_dependencies_when_disabled(config): - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -817,6 +857,8 @@ def it_should_not_lock_dependencies_when_disabled(config): - links: - + copies: + - scripts: - sources_locked: @@ -829,6 +871,8 @@ def it_should_not_lock_dependencies_when_disabled(config): - links: - + copies: + - scripts: - groups: @@ -878,6 +922,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): rev: example-branch links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -889,6 +935,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): rev: example-tag links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -900,6 +948,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): rev: example-tag links: - + copies: + - scripts: - sources_locked: @@ -912,6 +962,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): rev: (old revision) links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -923,6 +975,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): rev: (old revision) links: - + copies: + - scripts: - groups: @@ -950,6 +1004,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -961,6 +1017,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -972,6 +1030,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): - links: - + copies: + - scripts: - sources_locked: @@ -984,6 +1044,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -995,6 +1057,8 @@ def it_locks_previously_locked_dependencies_by_group_name(config): - links: - + copies: + - scripts: - groups: @@ -1036,6 +1100,8 @@ def git_changes( rev: example-tag links: - + copies: + - scripts: - sources_locked: @@ -1048,6 +1114,8 @@ def git_changes( rev: (old revision) links: - + copies: + - scripts: - groups: @@ -1071,6 +1139,8 @@ def git_changes( - links: - + copies: + - scripts: - sources_locked: @@ -1083,6 +1153,8 @@ def git_changes( - links: - + copies: + - scripts: - groups: @@ -1128,6 +1200,8 @@ def git_changes( rev: example-tag links: - + copies: + - scripts: - sources_locked: @@ -1140,6 +1214,8 @@ def git_changes( rev: (old revision) links: - + copies: + - scripts: - groups: @@ -1164,6 +1240,8 @@ def git_changes( - links: - + copies: + - scripts: - sources_locked: @@ -1176,6 +1254,8 @@ def git_changes( - links: - + copies: + - scripts: - groups: @@ -1196,6 +1276,8 @@ def it_merges_sources(config): rev: example-branch links: - + copies: + - scripts: - sources_locked: @@ -1205,6 +1287,8 @@ def it_merges_sources(config): rev: example-branch links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -1213,6 +1297,8 @@ def it_merges_sources(config): rev: 7bd138fe7359561a8c2ff9d195dff238794ccc04 links: - + copies: + - scripts: - """ @@ -1282,6 +1368,8 @@ def it_records_all_versions_when_no_arguments(config): - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -1293,6 +1381,8 @@ def it_records_all_versions_when_no_arguments(config): - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -1304,6 +1394,8 @@ def it_records_all_versions_when_no_arguments(config): - links: - + copies: + - scripts: - """ @@ -1328,6 +1420,8 @@ def it_records_specified_dependencies(config): - links: - + copies: + - scripts: - - repo: https://github.com/jacebrowning/gitman-demo @@ -1339,6 +1433,8 @@ def it_records_specified_dependencies(config): - links: - + copies: + - scripts: - """ From 325aad60c6603e1a711651bf08691d5d17d46d23 Mon Sep 17 00:00:00 2001 From: sningo Date: Tue, 25 Feb 2025 22:19:09 +0100 Subject: [PATCH 05/10] adding tests --- tests/test_api.py | 103 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/tests/test_api.py b/tests/test_api.py index 856de78..978b309 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -345,6 +345,109 @@ def it_overwrites_files_with_force(config_with_links): expect(gitman.install(depth=1, force=True)) == True + def describe_copies(): + @pytest.fixture + def config_with_copy(config): + config.datafile.text = strip( + """ + location: deps + sources: + - name: gitman_1 + repo: https://github.com/jacebrowning/gitman-demo + rev: 954e166c17d61935037fbd0799fbe0176c29df10 + links: + - + copies: + - target: my_copy + - source: gdm/common.py + target: libCommon.py + scripts: + - + """ + ) + config.datafile.load() + + return config + + def it_should_create_copies(config_with_copy): + expect(gitman.install(depth=1)) == True + + expect(os.listdir()).contains("my_copy") + expect(os.listdir()).contains("libCommon.py") + + def it_should_not_overwrite_files(config_with_copy): + os.system("touch my_copy") + + with pytest.raises(RuntimeError): + gitman.install(depth=1) + + def it_should_not_overwrite_non_empty_directories(config_with_copy): + os.system("mkdir my_copy") + os.system("touch my_copy/my_copy") + + with pytest.raises(RuntimeError): + gitman.install(depth=1) + + def it_overwrites_files_with_force(config_with_copy): + os.system("touch my_copy") + + expect(gitman.install(depth=1, force=True)) == True + + def describe_multi_copies(): + @pytest.fixture + def config_with_copies(config): + config.datafile.text = strip( + """ + location: deps + sources: + - name: gitman_1 + repo: https://github.com/jacebrowning/gitman-demo + rev: e6e7595cef573589bcfbbf6c9398b7c166cdda9d + links: + - + copies: + - source: gdm/test + target: gmd_test + - source: gdm/command.py + target: gdmCommand.py + scripts: + - + """ + ) + config.datafile.load() + + return config + + def it_should_create_copies(config_with_copies): + expect(gitman.install(depth=1)) == True + expect(os.listdir()).contains("gmd_test") + expect(os.listdir()).contains("gdmCommand.py") + + def it_should_not_overwrite_files_with_folders(config_with_copies): + os.system("touch gmd_test") + + with pytest.raises(RuntimeError): + gitman.install(depth=1) + + def it_should_merge_with_non_empty_directories(config_with_copies): + os.system("mkdir gmd_test") + os.system("touch gmd_test/my_copy") + + expect(os.listdir("gmd_test")).contains("my_copy") + + def it_should_overwrite_files(config_with_copies): + os.system("mkdir gmd_test") + os.system("touch gmd_test/test_all.py") + os.system("touch gdmCommand.py") + + expect(os.listdir("gmd_test")).contains("test_all.py") + expect(os.listdir()).contains("gdmCommand.py") + + def it_overwrites_files_with_force(config_with_copies): + os.system("mkdir gmd_test") + os.system("touch gmd_test/my_copy") + assert "my_copy" not in os.listdir("gmd_test") + def describe_scripts(): @pytest.fixture def config_with_scripts(config): From c911c9f0ce35645ec149a38fb85e7646286cdd2d Mon Sep 17 00:00:00 2001 From: sningo Date: Tue, 25 Feb 2025 22:30:16 +0100 Subject: [PATCH 06/10] add copy call --- gitman/models/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gitman/models/config.py b/gitman/models/config.py index 57e23fe..a2f42cf 100644 --- a/gitman/models/config.py +++ b/gitman/models/config.py @@ -115,6 +115,7 @@ def install_dependencies( ) assert self.root, f"Missing root: {self}" source.create_links(self.root, force=force) + source.create_copies(self.root, force=force) common.newline() count += 1 From 45d6e2984815717a4d7da04f7cb13a6923b7949a Mon Sep 17 00:00:00 2001 From: sningo Date: Fri, 28 Feb 2025 22:15:38 +0100 Subject: [PATCH 07/10] Working tests --- gitman/models/config.py | 1 + gitman/models/source.py | 7 ++++++- gitman/shell.py | 3 +-- tests/test_api.py | 46 ++++++++++++++++++++--------------------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/gitman/models/config.py b/gitman/models/config.py index a2f42cf..993bbe7 100644 --- a/gitman/models/config.py +++ b/gitman/models/config.py @@ -115,6 +115,7 @@ def install_dependencies( ) assert self.root, f"Missing root: {self}" source.create_links(self.root, force=force) + common.newline() source.create_copies(self.root, force=force) common.newline() count += 1 diff --git a/gitman/models/source.py b/gitman/models/source.py index ba16926..9ec798f 100644 --- a/gitman/models/source.py +++ b/gitman/models/source.py @@ -224,7 +224,7 @@ def create_copies(self, root: str, *, force: bool = False): for copy in self.copies: target = os.path.join(root, os.path.normpath(copy.target)) relpath = os.path.relpath(os.getcwd(), os.path.dirname(target)) - source = os.path.join(relpath, os.path.normpath(copy.source)) + source = os.path.join(root, os.path.join(relpath, copy.source) if copy.source else relpath) create_copy(source, target, force=force) def run_scripts(self, force: bool = False, show_shell_stdout: bool = False): @@ -382,6 +382,11 @@ def create_copy(source: str, target: str, *, force: bool): elif os.path.exists(target): if force: shell.rm(target) + elif os.path.isfile(target) and os.path.isdir(source): + msg = "Preexisting file location to be replaced by folder at {}".format(target) + raise exceptions.UncommittedChanges(msg) + elif os.path.isfile(target) and os.path.isfile(source): + shell.rm(target) else: msg = "Preexisting target location at {}".format(target) log.warn(msg) diff --git a/gitman/shell.py b/gitman/shell.py index 5cea11a..ea098fc 100644 --- a/gitman/shell.py +++ b/gitman/shell.py @@ -132,9 +132,8 @@ def cp(source, target): dirpath = os.path.dirname(target) if not os.path.isdir(dirpath): mkdir(dirpath) - if os.path.isdir(source): - shutil.copytree(src=source, dst=target) + shutil.copytree(src=source, dst=target, dirs_exist_ok=True) else: shutil.copy2(src=source, dst=target) diff --git a/tests/test_api.py b/tests/test_api.py index 978b309..42bbb2a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -375,20 +375,17 @@ def it_should_create_copies(config_with_copy): expect(os.listdir()).contains("my_copy") expect(os.listdir()).contains("libCommon.py") - def it_should_not_overwrite_files(config_with_copy): - os.system("touch my_copy") - - with pytest.raises(RuntimeError): - gitman.install(depth=1) + def it_should_overwrite_files(config_with_copy): + os.system("touch libCommon.py") + expect(os.listdir()).contains("libCommon.py") - def it_should_not_overwrite_non_empty_directories(config_with_copy): + def it_should_merge_non_empty_directories(config_with_copy): os.system("mkdir my_copy") os.system("touch my_copy/my_copy") - with pytest.raises(RuntimeError): - gitman.install(depth=1) + expect(os.listdir("my_copy")).contains("my_copy") - def it_overwrites_files_with_force(config_with_copy): + def it_overwrites_folders_with_force(config_with_copy): os.system("touch my_copy") expect(gitman.install(depth=1, force=True)) == True @@ -407,9 +404,9 @@ def config_with_copies(config): - copies: - source: gdm/test - target: gmd_test - - source: gdm/command.py - target: gdmCommand.py + target: gdm_test + - source: gdm/commands.py + target: gdmCommands.py scripts: - """ @@ -420,8 +417,8 @@ def config_with_copies(config): def it_should_create_copies(config_with_copies): expect(gitman.install(depth=1)) == True - expect(os.listdir()).contains("gmd_test") - expect(os.listdir()).contains("gdmCommand.py") + expect(os.listdir()).contains("gdm_test") + expect(os.listdir()).contains("gdmCommands.py") def it_should_not_overwrite_files_with_folders(config_with_copies): os.system("touch gmd_test") @@ -430,23 +427,26 @@ def it_should_not_overwrite_files_with_folders(config_with_copies): gitman.install(depth=1) def it_should_merge_with_non_empty_directories(config_with_copies): - os.system("mkdir gmd_test") - os.system("touch gmd_test/my_copy") + os.system("mkdir gdm_test") + os.system("touch gdm_test/my_copy") + expect(gitman.install(depth=1)) == True expect(os.listdir("gmd_test")).contains("my_copy") def it_should_overwrite_files(config_with_copies): - os.system("mkdir gmd_test") - os.system("touch gmd_test/test_all.py") - os.system("touch gdmCommand.py") + os.system("mkdir gdm_test") + os.system("touch gdm_test/test_all.py") + os.system("touch gdmCommands.py") - expect(os.listdir("gmd_test")).contains("test_all.py") + expect(gitman.install(depth=1)) == True + expect(os.listdir("gdm_test")).contains("test_all.py") expect(os.listdir()).contains("gdmCommand.py") def it_overwrites_files_with_force(config_with_copies): - os.system("mkdir gmd_test") - os.system("touch gmd_test/my_copy") - assert "my_copy" not in os.listdir("gmd_test") + os.system("mkdir gdm_test") + os.system("touch gdm_test/my_copy") + expect(gitman.install(depth=1, force=True)) == True + expect("my_copy" not in os.listdir("gdm_test")) == True def describe_scripts(): @pytest.fixture From b638d9406302755086716d914cca1fcdca415559 Mon Sep 17 00:00:00 2001 From: sningo Date: Fri, 28 Feb 2025 22:19:02 +0100 Subject: [PATCH 08/10] Fixing two typos in test harness --- tests/test_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 42bbb2a..f4c290f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -421,7 +421,7 @@ def it_should_create_copies(config_with_copies): expect(os.listdir()).contains("gdmCommands.py") def it_should_not_overwrite_files_with_folders(config_with_copies): - os.system("touch gmd_test") + os.system("touch gdm_test") with pytest.raises(RuntimeError): gitman.install(depth=1) @@ -431,7 +431,7 @@ def it_should_merge_with_non_empty_directories(config_with_copies): os.system("touch gdm_test/my_copy") expect(gitman.install(depth=1)) == True - expect(os.listdir("gmd_test")).contains("my_copy") + expect(os.listdir("gdm_test")).contains("my_copy") def it_should_overwrite_files(config_with_copies): os.system("mkdir gdm_test") @@ -440,7 +440,7 @@ def it_should_overwrite_files(config_with_copies): expect(gitman.install(depth=1)) == True expect(os.listdir("gdm_test")).contains("test_all.py") - expect(os.listdir()).contains("gdmCommand.py") + expect(os.listdir()).contains("gdmCommands.py") def it_overwrites_files_with_force(config_with_copies): os.system("mkdir gdm_test") From 9ea7db37251241dbf97eab042f8852f79101c490 Mon Sep 17 00:00:00 2001 From: sningo Date: Fri, 28 Feb 2025 22:24:25 +0100 Subject: [PATCH 09/10] Adjusting documentation --- docs/use-cases/multiple-copies.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/use-cases/multiple-copies.md b/docs/use-cases/multiple-copies.md index 21b8753..93360d6 100644 --- a/docs/use-cases/multiple-copies.md +++ b/docs/use-cases/multiple-copies.md @@ -29,8 +29,8 @@ sources: This will result in the following copies: -- `/vendor/partial_repo` -> `/.gitman/my_dependency/include` -- `/vendor/full_repo` -> `/.gitman/my_dependency` +- `/.gitman/my_dependency/include` -> `/vendor/partial_repo` +- `/.gitman/my_dependency` -> `/vendor/full_repo` ## Alternative Syntax From 980400f5d01f1f0a9c075a435de0fa25e0d5a581 Mon Sep 17 00:00:00 2001 From: sningo Date: Sat, 1 Mar 2025 09:09:20 +0100 Subject: [PATCH 10/10] fixing code checks --- gitman/models/source.py | 11 ++++++++--- gitman/shell.py | 2 +- tests/test_api.py | 10 +++++----- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/gitman/models/source.py b/gitman/models/source.py index 9ec798f..c62a519 100644 --- a/gitman/models/source.py +++ b/gitman/models/source.py @@ -15,6 +15,7 @@ class Link: source: str = "" target: str = "" + @dataclass class Copy: source: str = "" @@ -217,14 +218,16 @@ def create_links(self, root: str, *, force: bool = False): create_sym_link(source, target, force=force) def create_copies(self, root: str, *, force: bool = False): - """Create copies from source to target""" + """Create copies from source to target.""" if not self.copies: return for copy in self.copies: target = os.path.join(root, os.path.normpath(copy.target)) relpath = os.path.relpath(os.getcwd(), os.path.dirname(target)) - source = os.path.join(root, os.path.join(relpath, copy.source) if copy.source else relpath) + source = os.path.join( + root, os.path.join(relpath, copy.source) if copy.source else relpath + ) create_copy(source, target, force=force) def run_scripts(self, force: bool = False, show_shell_stdout: bool = False): @@ -383,7 +386,9 @@ def create_copy(source: str, target: str, *, force: bool): if force: shell.rm(target) elif os.path.isfile(target) and os.path.isdir(source): - msg = "Preexisting file location to be replaced by folder at {}".format(target) + msg = "Preexisting file location to be replaced by folder at {}".format( + target + ) raise exceptions.UncommittedChanges(msg) elif os.path.isfile(target) and os.path.isfile(source): shell.rm(target) diff --git a/gitman/shell.py b/gitman/shell.py index ea098fc..3b390af 100644 --- a/gitman/shell.py +++ b/gitman/shell.py @@ -1,8 +1,8 @@ """Utilities to call shell programs.""" import os -import subprocess import shutil +import subprocess import log diff --git a/tests/test_api.py b/tests/test_api.py index f4c290f..c93a664 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -356,7 +356,7 @@ def config_with_copy(config): repo: https://github.com/jacebrowning/gitman-demo rev: 954e166c17d61935037fbd0799fbe0176c29df10 links: - - + - copies: - target: my_copy - source: gdm/common.py @@ -389,7 +389,7 @@ def it_overwrites_folders_with_force(config_with_copy): os.system("touch my_copy") expect(gitman.install(depth=1, force=True)) == True - + def describe_multi_copies(): @pytest.fixture def config_with_copies(config): @@ -422,7 +422,7 @@ def it_should_create_copies(config_with_copies): def it_should_not_overwrite_files_with_folders(config_with_copies): os.system("touch gdm_test") - + with pytest.raises(RuntimeError): gitman.install(depth=1) @@ -441,13 +441,13 @@ def it_should_overwrite_files(config_with_copies): expect(gitman.install(depth=1)) == True expect(os.listdir("gdm_test")).contains("test_all.py") expect(os.listdir()).contains("gdmCommands.py") - + def it_overwrites_files_with_force(config_with_copies): os.system("mkdir gdm_test") os.system("touch gdm_test/my_copy") expect(gitman.install(depth=1, force=True)) == True expect("my_copy" not in os.listdir("gdm_test")) == True - + def describe_scripts(): @pytest.fixture def config_with_scripts(config):