Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::Path, process::Command};

Expand Down Expand Up @@ -68,22 +69,31 @@ impl Config {
pub fn save(&self, repo_root: &Path) -> anyhow::Result<()> {
for (name, m) in &self.mappings {
let key_remote = format!("{CONFIG_PREFIX}.{}.remote", name);
Command::new("git")
let status = Command::new("git")
.args(["config", "--local", "--replace-all", &key_remote, &m.remote])
.current_dir(repo_root)
.status()?;
if !status.success() {
return Err(anyhow!("git config failed to set {key_remote}"));
}

let key_url = format!("{CONFIG_PREFIX}.{}.url", name);
Command::new("git")
let status = Command::new("git")
.args(["config", "--local", "--replace-all", &key_url, &m.url])
.current_dir(repo_root)
.status()?;
if !status.success() {
return Err(anyhow!("git config failed to set {key_url}"));
}

let key_branch = format!("{CONFIG_PREFIX}.{}.branch", name);
Command::new("git")
let status = Command::new("git")
.args(["config", "--local", "--replace-all", &key_branch, &m.branch])
.current_dir(repo_root)
.status()?;
if !status.success() {
return Err(anyhow!("git config failed to set {key_branch}"));
}
}
Comment on lines 70 to 97

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's significant code duplication in this loop for setting the remote, url, and branch git config values. This can be refactored to improve maintainability and reduce redundancy.

You can iterate over a collection of field names and their corresponding values to avoid repeating the Command setup and execution logic.

        for (name, m) in &self.mappings {
            let fields_to_set = [
                ("remote", &m.remote),
                ("url", &m.url),
                ("branch", &m.branch),
            ];

            for (field, value) in fields_to_set {
                let key = format!("{CONFIG_PREFIX}.{}.{}", name, field);
                let status = Command::new("git")
                    .args(["config", "--local", "--replace-all", &key, value])
                    .current_dir(repo_root)
                    .status()?;
                if !status.success() {
                    return Err(anyhow!("git config failed to set {key}"));
                }
            }
        }


Ok(())
Expand Down
37 changes: 37 additions & 0 deletions tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ fi
shim
}

/// `git config` が失敗するシム
fn fake_git_fail_config(dir: &tempfile::TempDir) -> std::path::PathBuf {
let shim = dir.path().join("git");
fs::write(
&shim,
"#!/usr/bin/env sh\nif [ \"$1\" = \"config\" ]; then\n exit 1\nelse\n echo git \"$@\"\n exit 0\nfi\n",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better readability and consistency with other test helpers in this file (like fake_git_fail_pull), consider using a raw string literal for the multi-line shell script. This avoids the need for escaping quotes and explicitly adding newline characters (\n).

Suggested change
"#!/usr/bin/env sh\nif [ \"$1\" = \"config\" ]; then\n exit 1\nelse\n echo git \"$@\"\n exit 0\nfi\n",
r#"#!/usr/bin/env sh
if [ "$1" = "config" ]; then
exit 1
else
echo git "$@"
exit 0
fi
"#,

)
.unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(&shim, fs::Permissions::from_mode(0o755)).unwrap();
}
shim
}

#[test]
fn connect_and_list_roundtrip() {
let repo = setup_repo();
Expand Down Expand Up @@ -189,3 +205,24 @@ fn remove_mapping() {
.success()
.stdout(predicate::str::contains("No mappings"));
}

#[test]
fn connect_fails_on_git_config_error() {
let repo = setup_repo();
let git_shim = fake_git_fail_config(&repo);

let path_env = format!(
"{}:{}",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The PATH separator ":" is hardcoded. This will cause the test to fail on Windows, where the separator is ";". For cross-platform compatibility, you should use std::env::consts::PATH_SEPARATOR.

Suggested change
"{}:{}",
"{}{}{}",

git_shim.parent().unwrap().display(),
std::env::var("PATH").unwrap()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To accommodate the change to a three-part format string, you'll need to add std::env::consts::PATH_SEPARATOR as an argument here.

Suggested change
std::env::var("PATH").unwrap()
git_shim.parent().unwrap().display(),
std::env::consts::PATH_SEPARATOR,
std::env::var("PATH").unwrap()

);

Command::cargo_bin("gh-sync")
.unwrap()
.current_dir(repo.path())
.env("PATH", &path_env)
.args(&["connect", "web-app", "git@github.com:a/b.git"])
.assert()
.failure()
.stderr(predicate::str::contains("git config"));
}
Loading