diff --git a/Cargo.lock b/Cargo.lock index 4b3e337..597d7d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,6 +195,7 @@ dependencies = [ "git2", "regex", "serde", + "serde_yaml", "shellexpand", "tempdir", "toml", @@ -336,6 +337,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + [[package]] name = "lock_api" version = "0.4.5" @@ -592,6 +599,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + [[package]] name = "scopeguard" version = "1.1.0" @@ -618,6 +631,18 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_yaml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + [[package]] name = "shellexpand" version = "2.1.0" @@ -850,3 +875,12 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml index 2f6d8ff..9329e3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,5 +62,8 @@ version = "=1.5.4" [dependencies.comfy-table] version = "=5.0.0" +[dependencies.serde_yaml] +version = "=0.8.23" + [dev-dependencies.tempdir] version = "=0.3.7" diff --git a/docs/src/overview.md b/docs/src/overview.md index 7583d19..082678a 100644 --- a/docs/src/overview.md +++ b/docs/src/overview.md @@ -5,7 +5,8 @@ Manager](https://github.com/hakoerber/git-repo-manager/) (GRM for short), a tool that helps you manage git repositories. GRM helps you manage git repositories in a declarative way. Configure your -repositories in a TOML file, GRM does the rest. Take a look at [the example +repositories in a TOML or YAML file, GRM does the rest. Take a look at [the +example configuration](https://github.com/hakoerber/git-repo-manager/blob/master/example.config.toml) to get a feel for the way you configure your repositories. See the [repository tree chapter](./repos.md) for details. diff --git a/docs/src/repos.md b/docs/src/repos.md index 32d6ccb..69004f8 100644 --- a/docs/src/repos.md +++ b/docs/src/repos.md @@ -74,3 +74,9 @@ $ grm repos status ╰──────────┴──────────┴────────┴──────────┴───────┴─────────╯ ``` +## YAML + +By default, the repo configuration uses TOML. If you prefer YAML, just give it +a YAML file instead (file ending does not matter, `grm` will figure out the format +itself). For generating a configuration, pass `--format yaml` to `grm repo find` +to generate YAML instead of TOML. diff --git a/e2e_tests/test_repos_find.py b/e2e_tests/test_repos_find.py index 2939e95..cf6e692 100644 --- a/e2e_tests/test_repos_find.py +++ b/e2e_tests/test_repos_find.py @@ -3,6 +3,8 @@ import tempfile import toml +import pytest +import yaml from helpers import * @@ -30,6 +32,16 @@ def test_repos_find_empty(): assert len(cmd.stderr) != 0 +def test_repos_find_invalid_format(): + with tempfile.TemporaryDirectory() as tmpdir: + cmd = grm( + ["repos", "find", tmpdir, "--format", "invalidformat"], is_invalid=True + ) + assert cmd.returncode != 0 + assert len(cmd.stdout) == 0 + assert "isn't a valid value" in cmd.stderr + + def test_repos_find_non_git_repos(): with tempfile.TemporaryDirectory() as tmpdir: shell( @@ -50,7 +62,9 @@ def test_repos_find_non_git_repos(): assert len(cmd.stderr) != 0 -def test_repos_find(): +@pytest.mark.parametrize("default", [True, False]) +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_find(configtype, default): with tempfile.TemporaryDirectory() as tmpdir: shell( f""" @@ -83,11 +97,19 @@ def test_repos_find(): """ ) - cmd = grm(["repos", "find", tmpdir]) + args = ["repos", "find", tmpdir] + if not default: + args += ["--format", configtype] + cmd = grm(args) assert cmd.returncode == 0 assert len(cmd.stderr) == 0 - output = toml.loads(cmd.stdout) + if default or configtype == "toml": + output = toml.loads(cmd.stdout) + elif configtype == "yaml": + output = yaml.safe_load(cmd.stdout) + else: + raise NotImplementedError() assert isinstance(output, dict) assert set(output.keys()) == {"trees"} @@ -125,14 +147,24 @@ def test_repos_find(): assert origin["url"] == "https://example.com/repo2.git" -def test_repos_find_in_root(): +@pytest.mark.parametrize("default", [True, False]) +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_find_in_root(configtype, default): with TempGitRepository() as repo_dir: - cmd = grm(["repos", "find", repo_dir]) + args = ["repos", "find", repo_dir] + if not default: + args += ["--format", configtype] + cmd = grm(args) assert cmd.returncode == 0 assert len(cmd.stderr) == 0 - output = toml.loads(cmd.stdout) + if default or configtype == "toml": + output = toml.loads(cmd.stdout) + elif configtype == "yaml": + output = yaml.safe_load(cmd.stdout) + else: + raise NotImplementedError() assert isinstance(output, dict) assert set(output.keys()) == {"trees"} @@ -160,7 +192,9 @@ def test_repos_find_in_root(): assert someremote["type"] == "file" -def test_repos_find_with_invalid_repo(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +@pytest.mark.parametrize("default", [True, False]) +def test_repos_find_with_invalid_repo(configtype, default): with tempfile.TemporaryDirectory() as tmpdir: shell( f""" @@ -193,11 +227,19 @@ def test_repos_find_with_invalid_repo(): """ ) - cmd = grm(["repos", "find", tmpdir]) + args = ["repos", "find", tmpdir] + if not default: + args += ["--format", configtype] + cmd = grm(args) assert cmd.returncode == 0 assert "broken" in cmd.stderr - output = toml.loads(cmd.stdout) + if default or configtype == "toml": + output = toml.loads(cmd.stdout) + elif configtype == "yaml": + output = yaml.safe_load(cmd.stdout) + else: + raise NotImplementedError() assert isinstance(output, dict) assert set(output.keys()) == {"trees"} diff --git a/e2e_tests/test_repos_sync.py b/e2e_tests/test_repos_sync.py index 62e0cca..2586943 100644 --- a/e2e_tests/test_repos_sync.py +++ b/e2e_tests/test_repos_sync.py @@ -2,6 +2,7 @@ import tempfile import re +import textwrap import pytest import toml @@ -9,8 +10,134 @@ import git from helpers import * +templates = { + "repo_simple": { + "toml": """ + [[trees]] + root = "{root}" -def test_repos_sync_config_is_valid_symlink(): + [[trees.repos]] + name = "test" + """, + "yaml": """ + trees: + - root: "{root}" + repos: + - name: "test" + """, + }, + "repo_with_remote": { + "toml": """ + [[trees]] + root = "{root}" + + [[trees.repos]] + name = "test" + + [[trees.repos.remotes]] + name = "{remotename}" + url = "file://{remote}" + type = "file" + """, + "yaml": textwrap.dedent( + """ + trees: + - root: "{root}" + repos: + - name: test + remotes: + - name: "{remotename}" + url: "file://{remote}" + type: "file" + """ + ), + }, + "repo_with_two_remotes": { + "toml": """ + [[trees]] + root = "{root}" + + [[trees.repos]] + name = "test" + + [[trees.repos.remotes]] + name = "origin" + url = "file://{remote1}" + type = "file" + + [[trees.repos.remotes]] + name = "origin2" + url = "file://{remote2}" + type = "file" + """, + "yaml": textwrap.dedent( + """ + trees: + - root: "{root}" + repos: + - name: "test" + remotes: + - name: "origin" + url: "file://{remote1}" + type: "file" + - name: "origin2" + url: "file://{remote2}" + type: "file" + """ + ), + }, + "worktree_repo_simple": { + "toml": """ + [[trees]] + root = "{root}" + + [[trees.repos]] + name = "test" + worktree_setup = true + """, + "yaml": textwrap.dedent( + """ + trees: + - root: "{root}" + repos: + - name: test + worktree_setup: true + """ + ), + }, + "worktree_repo_with_remote": { + "toml": """ + [[trees]] + root = "{root}" + + [[trees.repos]] + name = "test" + worktree_setup = true + + [[trees.repos.remotes]] + name = "origin" + url = "file://{remote}" + type = "file" + """, + "yaml": textwrap.dedent( + """ + trees: + - root: "{root}" + repos: + - name: test + worktree_setup: true + remotes: + - name: origin + url: "file://{remote}" + type: "file" + """ + ), + }, +} + + +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_config_is_valid_symlink(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote, head_commit_sha): with tempfile.NamedTemporaryFile() as config: @@ -20,20 +147,13 @@ def test_repos_sync_config_is_valid_symlink(): with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote}" - type = "file" - """ + templates["repo_with_remote"][configtype].format( + root=target, remote=remote, remotename="origin" + ) ) + subprocess.run(["cat", config.name]) + cmd = grm(["repos", "sync", "--config", config_symlink]) assert cmd.returncode == 0 @@ -85,20 +205,13 @@ def test_repos_sync_config_is_unreadable(): assert "permission denied" in cmd.stderr.lower() -def test_repos_sync_unmanaged_repos(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_unmanaged_repos(configtype): with tempfile.TemporaryDirectory() as root: with TempGitRepository(dir=root) as unmanaged_repo: with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: - f.write( - f""" - [[trees]] - root = "{root}" - - [[trees.repos]] - name = "test" - """ - ) + f.write(templates["repo_simple"][configtype].format(root=root)) cmd = grm(["repos", "sync", "--config", config.name]) assert cmd.returncode == 0 @@ -112,19 +225,12 @@ def test_repos_sync_unmanaged_repos(): assert any([re.match(regex, l) for l in cmd.stderr.lower().split("\n")]) -def test_repos_sync_root_is_file(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_root_is_file(configtype): with tempfile.NamedTemporaryFile() as target: with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: - f.write( - f""" - [[trees]] - root = "{target.name}" - - [[trees.repos]] - name = "test" - """ - ) + f.write(templates["repo_simple"][configtype].format(root=target.name)) cmd = grm(["repos", "sync", "--config", config.name]) assert cmd.returncode != 0 @@ -132,30 +238,17 @@ def test_repos_sync_root_is_file(): assert "not a directory" in cmd.stderr.lower() -def test_repos_sync_normal_clone(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_normal_clone(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote1, remote1_head_commit_sha): with TempGitFileRemote() as (remote2, remote2_head_commit_sha): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - - [[trees.repos.remotes]] - name = "origin2" - url = "file://{remote2}" - type = "file" - """ + templates["repo_with_two_remotes"][configtype].format( + root=target, remote1=remote1, remote2=remote2 + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -183,19 +276,12 @@ def test_repos_sync_normal_clone(): assert urls[0] == f"file://{remote2}" -def test_repos_sync_normal_init(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_normal_init(configtype): with tempfile.TemporaryDirectory() as target: with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: - f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - """ - ) + f.write(templates["repo_simple"][configtype].format(root=target)) cmd = grm(["repos", "sync", "--config", config.name]) assert cmd.returncode == 0 @@ -210,25 +296,17 @@ def test_repos_sync_normal_init(): assert not repo.head.is_valid() -def test_repos_sync_normal_add_remote(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_normal_add_remote(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote1, remote1_head_commit_sha): with TempGitFileRemote() as (remote2, remote2_head_commit_sha): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - """ + templates["repo_with_remote"][configtype].format( + root=target, remote=remote1, remotename="origin" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -246,23 +324,9 @@ def test_repos_sync_normal_add_remote(): with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - - [[trees.repos.remotes]] - name = "origin2" - url = "file://{remote2}" - type = "file" - """ + templates["repo_with_two_remotes"][configtype].format( + root=target, remote1=remote1, remote2=remote2 + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -282,30 +346,17 @@ def test_repos_sync_normal_add_remote(): assert urls[0] == f"file://{remote2}" -def test_repos_sync_normal_remove_remote(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_normal_remove_remote(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote1, remote1_head_commit_sha): with TempGitFileRemote() as (remote2, remote2_head_commit_sha): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - - [[trees.repos.remotes]] - name = "origin2" - url = "file://{remote2}" - type = "file" - """ + templates["repo_with_two_remotes"][configtype].format( + root=target, remote1=remote1, remote2=remote2 + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -326,18 +377,9 @@ def test_repos_sync_normal_remove_remote(): with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin2" - url = "file://{remote2}" - type = "file" - """ + templates["repo_with_remote"][configtype].format( + root=target, remote=remote2, remotename="origin2" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -369,25 +411,17 @@ def test_repos_sync_normal_remove_remote(): assert urls[0] == f"file://{remote2}" -def test_repos_sync_normal_change_remote_url(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_normal_change_remote_url(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote1, remote1_head_commit_sha): with TempGitFileRemote() as (remote2, remote2_head_commit_sha): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - """ + templates["repo_with_remote"][configtype].format( + root=target, remote=remote1, remotename="origin" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -405,18 +439,9 @@ def test_repos_sync_normal_change_remote_url(): with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote2}" - type = "file" - """ + templates["repo_with_remote"][configtype].format( + root=target, remote=remote2, remotename="origin" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -429,25 +454,17 @@ def test_repos_sync_normal_change_remote_url(): assert urls[0] == f"file://{remote2}" -def test_repos_sync_normal_change_remote_name(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_normal_change_remote_name(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote1, remote1_head_commit_sha): with TempGitFileRemote() as (remote2, remote2_head_commit_sha): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - """ + templates["repo_with_remote"][configtype].format( + root=target, remote=remote1, remotename="origin" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -465,18 +482,9 @@ def test_repos_sync_normal_change_remote_name(): with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin2" - url = "file://{remote1}" - type = "file" - """ + templates["repo_with_remote"][configtype].format( + root=target, remote=remote1, remotename="origin2" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -492,25 +500,16 @@ def test_repos_sync_normal_change_remote_name(): assert urls[0] == f"file://{remote1}" -def test_repos_sync_worktree_clone(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_worktree_clone(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote, head_commit_sha): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - worktree_setup = true - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote}" - type = "file" - """ + templates["worktree_repo_with_remote"][configtype].format( + root=target, remote=remote, remotename="origin" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -530,19 +529,13 @@ def test_repos_sync_worktree_clone(): assert str(repo.head.commit) == head_commit_sha -def test_repos_sync_worktree_init(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_worktree_init(configtype): with tempfile.TemporaryDirectory() as target: with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - worktree_setup = true - """ + templates["worktree_repo_simple"][configtype].format(root=target) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -559,43 +552,42 @@ def test_repos_sync_worktree_init(): assert not repo.head.is_valid() -def test_repos_sync_invalid_toml(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_invalid_syntax(configtype): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: - f.write( - f""" - [[trees]] - root = invalid as there are no quotes ;) - """ - ) + if configtype == "toml": + f.write( + f""" + [[trees]] + root = invalid as there are no quotes ;) + """ + ) + elif configtype == "yaml": + f.write( + f""" + trees: + wrong: + indentation: + """ + ) + else: + raise NotImplementedError() cmd = grm(["repos", "sync", "--config", config.name]) assert cmd.returncode != 0 -def test_repos_sync_unchanged(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_unchanged(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote1, remote1_head_commit_sha): with TempGitFileRemote() as (remote2, remote2_head_commit_sha): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - - [[trees.repos.remotes]] - name = "origin2" - url = "file://{remote2}" - type = "file" - """ + templates["repo_with_two_remotes"][configtype].format( + root=target, remote1=remote1, remote2=remote2 + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -609,25 +601,17 @@ def test_repos_sync_unchanged(): assert before == after -def test_repos_sync_normal_change_to_worktree(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_normal_change_to_worktree(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote1, remote1_head_commit_sha): with TempGitFileRemote() as (remote2, remote2_head_commit_sha): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - """ + templates["repo_with_remote"][configtype].format( + root=target, remote=remote1, remotename="origin" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -637,19 +621,9 @@ def test_repos_sync_normal_change_to_worktree(): with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - worktree_setup = true - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - """ + templates["worktree_repo_with_remote"][configtype].format( + root=target, remote=remote1, remotename="origin" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -658,26 +632,17 @@ def test_repos_sync_normal_change_to_worktree(): assert "not using a worktree setup" in cmd.stderr -def test_repos_sync_worktree_change_to_normal(): +@pytest.mark.parametrize("configtype", ["toml", "yaml"]) +def test_repos_sync_worktree_change_to_normal(configtype): with tempfile.TemporaryDirectory() as target: with TempGitFileRemote() as (remote1, remote1_head_commit_sha): with TempGitFileRemote() as (remote2, remote2_head_commit_sha): with tempfile.NamedTemporaryFile() as config: with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - worktree_setup = true - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - """ + templates["worktree_repo_with_remote"][configtype].format( + root=target, remote=remote1, remotename="origin" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) @@ -687,18 +652,9 @@ def test_repos_sync_worktree_change_to_normal(): with open(config.name, "w") as f: f.write( - f""" - [[trees]] - root = "{target}" - - [[trees.repos]] - name = "test" - - [[trees.repos.remotes]] - name = "origin" - url = "file://{remote1}" - type = "file" - """ + templates["repo_with_remote"][configtype].format( + root=target, remote=remote1, remotename="origin" + ) ) cmd = grm(["repos", "sync", "--config", config.name]) diff --git a/example.config.yaml b/example.config.yaml new file mode 100644 index 0000000..2c4152d --- /dev/null +++ b/example.config.yaml @@ -0,0 +1,16 @@ +trees: +- root: "~/example-projects/" + repos: + - name: "git-repo-manager" + remotes: + - name: "origin" + url: "https://code.hkoerber.de/hannes/git-repo-manager.git" + type: "https" + - name: "github" + url: "https://github.com/hakoerber/git-repo-manager.git" + type: "https" + - name: "dotfiles" + remotes: + - name: "origin" + url: "https://github.com/hakoerber/dotfiles.git" + type: "https" diff --git a/src/config.rs b/src/config.rs index efcf6e6..e81c6a0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -36,6 +36,10 @@ impl Config { Err(error) => Err(error.to_string()), } } + + pub fn as_yaml(&self) -> Result { + serde_yaml::to_string(self).map_err(|e| e.to_string()) + } } #[derive(Debug, Serialize, Deserialize)] @@ -62,12 +66,15 @@ pub fn read_config(path: &str) -> Result { let config: Config = match toml::from_str(&content) { Ok(c) => c, - Err(e) => { - return Err(format!( - "Error parsing configuration file \"{}\": {}", - path, e - )) - } + Err(_) => match serde_yaml::from_str(&content) { + Ok(c) => c, + Err(e) => { + return Err(format!( + "Error parsing configuration file \"{}\": {}", + path, e + )) + } + }, }; Ok(config) diff --git a/src/grm/cmd.rs b/src/grm/cmd.rs index 4307a66..1ea9a02 100644 --- a/src/grm/cmd.rs +++ b/src/grm/cmd.rs @@ -61,10 +61,25 @@ pub struct OptionalConfig { pub config: Option, } +#[derive(clap::ArgEnum, Clone)] +pub enum ConfigFormat { + Yaml, + Toml, +} + #[derive(Parser)] pub struct Find { #[clap(help = "The path to search through")] pub path: String, + + #[clap( + arg_enum, + short, + long, + help = "Format to produce", + default_value_t = ConfigFormat::Toml, + )] + pub format: ConfigFormat, } #[derive(Parser)] diff --git a/src/grm/main.rs b/src/grm/main.rs index bc63641..50547da 100644 --- a/src/grm/main.rs +++ b/src/grm/main.rs @@ -119,15 +119,34 @@ fn main() { } else { let config = trees.to_config(); - let toml = match config.as_toml() { - Ok(toml) => toml, - Err(error) => { - print_error(&format!("Failed converting config to TOML: {}", &error)); - process::exit(1); + match find.format { + cmd::ConfigFormat::Toml => { + let toml = match config.as_toml() { + Ok(toml) => toml, + Err(error) => { + print_error(&format!( + "Failed converting config to TOML: {}", + &error + )); + process::exit(1); + } + }; + print!("{}", toml); } - }; - - print!("{}", toml); + cmd::ConfigFormat::Yaml => { + let yaml = match config.as_yaml() { + Ok(yaml) => yaml, + Err(error) => { + print_error(&format!( + "Failed converting config to YAML: {}", + &error + )); + process::exit(1); + } + }; + print!("{}", yaml); + } + } } for warning in warnings { print_warning(&warning);