gitacc-switcher 1.0.4__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gitacc_switcher-1.0.4/gitacc_switcher.egg-info → gitacc_switcher-1.1.0}/PKG-INFO +9 -2
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/README.md +8 -1
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher/__init__.py +1 -1
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher/account_manager.py +18 -3
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher/cli.py +10 -1
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher/config_manager.py +30 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0/gitacc_switcher.egg-info}/PKG-INFO +9 -2
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/pyproject.toml +1 -1
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/setup.py +1 -1
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/tests/test_account_manager.py +39 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/tests/test_cli.py +19 -2
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/tests/test_config_manager.py +24 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/LICENSE +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/MANIFEST.in +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher/completion.py +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher/hook_manager.py +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher/ssh_manager.py +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher/utils.py +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher.egg-info/SOURCES.txt +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher.egg-info/dependency_links.txt +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher.egg-info/entry_points.txt +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher.egg-info/requires.txt +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher.egg-info/top_level.txt +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/requirements.txt +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/setup.cfg +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/tests/__init__.py +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/tests/test_completion.py +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/tests/test_hook_manager.py +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/tests/test_ssh_manager.py +0 -0
- {gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitacc-switcher
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Git Account Switcher - Manage multiple Git SSH accounts easily
|
|
5
5
|
Home-page: https://github.com/ktechhub/gitacc_switcher
|
|
6
6
|
Author: ktechhub
|
|
@@ -75,6 +75,7 @@ gitacc add --type ed25519 # specify key type
|
|
|
75
75
|
# 2. Switch between them
|
|
76
76
|
gitacc switch work # full form
|
|
77
77
|
gitacc work # shorthand — same thing
|
|
78
|
+
gitacc switch work --local # only this repo, leave the global identity alone
|
|
78
79
|
|
|
79
80
|
# 3. See what's registered (active account is marked with *)
|
|
80
81
|
gitacc list
|
|
@@ -101,7 +102,7 @@ gitacc add
|
|
|
101
102
|
gitacc add --type ed25519
|
|
102
103
|
```
|
|
103
104
|
|
|
104
|
-
### `gitacc switch <account
|
|
105
|
+
### `gitacc switch <account> [--local]` / `gitacc <account>`
|
|
105
106
|
|
|
106
107
|
Switch to a registered account. Clears all keys from the SSH agent, loads only this account's key, and sets `git config --global user.name/email`.
|
|
107
108
|
|
|
@@ -110,6 +111,12 @@ gitacc switch mywork
|
|
|
110
111
|
gitacc mywork # shorthand
|
|
111
112
|
```
|
|
112
113
|
|
|
114
|
+
Pass `--local`/`-l` to set the identity for the current repository only (`git config` local scope), leaving the global `user.name`/`user.email` untouched. Must be run from inside a Git repository.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
gitacc switch mywork --local
|
|
118
|
+
```
|
|
119
|
+
|
|
113
120
|
> The SSH agent must already be running (`eval $(ssh-agent)`). If it isn't, gitacc will tell you.
|
|
114
121
|
|
|
115
122
|
### `gitacc list`
|
|
@@ -42,6 +42,7 @@ gitacc add --type ed25519 # specify key type
|
|
|
42
42
|
# 2. Switch between them
|
|
43
43
|
gitacc switch work # full form
|
|
44
44
|
gitacc work # shorthand — same thing
|
|
45
|
+
gitacc switch work --local # only this repo, leave the global identity alone
|
|
45
46
|
|
|
46
47
|
# 3. See what's registered (active account is marked with *)
|
|
47
48
|
gitacc list
|
|
@@ -68,7 +69,7 @@ gitacc add
|
|
|
68
69
|
gitacc add --type ed25519
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
### `gitacc switch <account
|
|
72
|
+
### `gitacc switch <account> [--local]` / `gitacc <account>`
|
|
72
73
|
|
|
73
74
|
Switch to a registered account. Clears all keys from the SSH agent, loads only this account's key, and sets `git config --global user.name/email`.
|
|
74
75
|
|
|
@@ -77,6 +78,12 @@ gitacc switch mywork
|
|
|
77
78
|
gitacc mywork # shorthand
|
|
78
79
|
```
|
|
79
80
|
|
|
81
|
+
Pass `--local`/`-l` to set the identity for the current repository only (`git config` local scope), leaving the global `user.name`/`user.email` untouched. Must be run from inside a Git repository.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
gitacc switch mywork --local
|
|
85
|
+
```
|
|
86
|
+
|
|
80
87
|
> The SSH agent must already be running (`eval $(ssh-agent)`). If it isn't, gitacc will tell you.
|
|
81
88
|
|
|
82
89
|
### `gitacc list`
|
|
@@ -156,11 +156,13 @@ class AccountManager:
|
|
|
156
156
|
echo_color("g", f'Account "{account_name}" removed successfully!')
|
|
157
157
|
return True
|
|
158
158
|
|
|
159
|
-
def switch_account(self, account_name: str) -> bool:
|
|
159
|
+
def switch_account(self, account_name: str, local: bool = False) -> bool:
|
|
160
160
|
"""Switch to a Git account.
|
|
161
161
|
|
|
162
162
|
Args:
|
|
163
163
|
account_name: Account name to switch to
|
|
164
|
+
local: If True, set the identity for the current repository only
|
|
165
|
+
(git config local scope) instead of globally
|
|
164
166
|
|
|
165
167
|
Returns:
|
|
166
168
|
True if successful, False otherwise
|
|
@@ -171,6 +173,11 @@ class AccountManager:
|
|
|
171
173
|
echo_color("r", "Wrong: account name!!")
|
|
172
174
|
return False
|
|
173
175
|
|
|
176
|
+
if local and not self.config_manager.is_git_repo():
|
|
177
|
+
echo_color("r", "Not a Git repository!")
|
|
178
|
+
echo_color("y", "--local requires running inside a Git repository.")
|
|
179
|
+
return False
|
|
180
|
+
|
|
174
181
|
# Check if SSH agent is already running
|
|
175
182
|
agent_running = self.ssh_manager.is_ssh_agent_running()
|
|
176
183
|
|
|
@@ -218,11 +225,19 @@ class AccountManager:
|
|
|
218
225
|
# Set Git config
|
|
219
226
|
name = account_info.get("name")
|
|
220
227
|
email = account_info.get("email")
|
|
221
|
-
if
|
|
228
|
+
if local:
|
|
229
|
+
config_success = self.config_manager.set_git_config_local(name, email)
|
|
230
|
+
else:
|
|
231
|
+
config_success = self.config_manager.set_git_config(name, email)
|
|
232
|
+
if not config_success:
|
|
222
233
|
echo_color("r", "Failed to set Git config!")
|
|
223
234
|
return False
|
|
224
235
|
|
|
225
|
-
|
|
236
|
+
scope = "this repository only" if local else "globally"
|
|
237
|
+
echo_color(
|
|
238
|
+
"g",
|
|
239
|
+
f'Switched to account "{account_name}" successfully! ({scope})',
|
|
240
|
+
)
|
|
226
241
|
return True
|
|
227
242
|
|
|
228
243
|
def logout(self) -> bool:
|
|
@@ -68,6 +68,7 @@ Examples:
|
|
|
68
68
|
gitacc add --type ed25519 Add account with specific SSH key type
|
|
69
69
|
Available types: {key_types}
|
|
70
70
|
gitacc switch myaccount Switch to an account
|
|
71
|
+
gitacc switch myaccount -l Switch to an account for this repo only
|
|
71
72
|
gitacc myaccount Switch to an account (short form)
|
|
72
73
|
gitacc remove myaccount Remove an account
|
|
73
74
|
gitacc list List all registered accounts
|
|
@@ -121,6 +122,12 @@ Examples:
|
|
|
121
122
|
"account_name",
|
|
122
123
|
help="Account name to switch to",
|
|
123
124
|
)
|
|
125
|
+
parser.add_argument(
|
|
126
|
+
"-l",
|
|
127
|
+
"--local",
|
|
128
|
+
action="store_true",
|
|
129
|
+
help="Set the identity for this repository only, instead of globally",
|
|
130
|
+
)
|
|
124
131
|
if argcomplete:
|
|
125
132
|
account_arg.completer = lambda **kwargs: get_account_names()
|
|
126
133
|
parser.set_defaults(func=self._handle_switch)
|
|
@@ -321,7 +328,9 @@ Examples:
|
|
|
321
328
|
|
|
322
329
|
def _handle_switch(self, args: argparse.Namespace) -> int:
|
|
323
330
|
"""Handle the 'switch' command."""
|
|
324
|
-
success = self.account_manager.switch_account(
|
|
331
|
+
success = self.account_manager.switch_account(
|
|
332
|
+
args.account_name, local=getattr(args, "local", False)
|
|
333
|
+
)
|
|
325
334
|
return 0 if success else 1
|
|
326
335
|
|
|
327
336
|
def _handle_list(self, args: argparse.Namespace) -> int:
|
|
@@ -44,6 +44,36 @@ class ConfigManager:
|
|
|
44
44
|
except subprocess.CalledProcessError:
|
|
45
45
|
return False
|
|
46
46
|
|
|
47
|
+
def set_git_config_local(
|
|
48
|
+
self, name: str, email: str, repo_path: Optional[Path] = None
|
|
49
|
+
) -> bool:
|
|
50
|
+
"""Set Git local (repository-scoped) user.name and user.email.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
name: Git user name
|
|
54
|
+
email: Git user email
|
|
55
|
+
repo_path: Path to repository (default: current directory)
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
True if successful, False otherwise
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
subprocess.run(
|
|
62
|
+
["git", "config", "user.name", name],
|
|
63
|
+
cwd=repo_path,
|
|
64
|
+
check=True,
|
|
65
|
+
capture_output=True,
|
|
66
|
+
)
|
|
67
|
+
subprocess.run(
|
|
68
|
+
["git", "config", "user.email", email],
|
|
69
|
+
cwd=repo_path,
|
|
70
|
+
check=True,
|
|
71
|
+
capture_output=True,
|
|
72
|
+
)
|
|
73
|
+
return True
|
|
74
|
+
except subprocess.CalledProcessError:
|
|
75
|
+
return False
|
|
76
|
+
|
|
47
77
|
def unset_git_config(self) -> bool:
|
|
48
78
|
"""Unset Git global user.name and user.email.
|
|
49
79
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitacc-switcher
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Git Account Switcher - Manage multiple Git SSH accounts easily
|
|
5
5
|
Home-page: https://github.com/ktechhub/gitacc_switcher
|
|
6
6
|
Author: ktechhub
|
|
@@ -75,6 +75,7 @@ gitacc add --type ed25519 # specify key type
|
|
|
75
75
|
# 2. Switch between them
|
|
76
76
|
gitacc switch work # full form
|
|
77
77
|
gitacc work # shorthand — same thing
|
|
78
|
+
gitacc switch work --local # only this repo, leave the global identity alone
|
|
78
79
|
|
|
79
80
|
# 3. See what's registered (active account is marked with *)
|
|
80
81
|
gitacc list
|
|
@@ -101,7 +102,7 @@ gitacc add
|
|
|
101
102
|
gitacc add --type ed25519
|
|
102
103
|
```
|
|
103
104
|
|
|
104
|
-
### `gitacc switch <account
|
|
105
|
+
### `gitacc switch <account> [--local]` / `gitacc <account>`
|
|
105
106
|
|
|
106
107
|
Switch to a registered account. Clears all keys from the SSH agent, loads only this account's key, and sets `git config --global user.name/email`.
|
|
107
108
|
|
|
@@ -110,6 +111,12 @@ gitacc switch mywork
|
|
|
110
111
|
gitacc mywork # shorthand
|
|
111
112
|
```
|
|
112
113
|
|
|
114
|
+
Pass `--local`/`-l` to set the identity for the current repository only (`git config` local scope), leaving the global `user.name`/`user.email` untouched. Must be run from inside a Git repository.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
gitacc switch mywork --local
|
|
118
|
+
```
|
|
119
|
+
|
|
113
120
|
> The SSH agent must already be running (`eval $(ssh-agent)`). If it isn't, gitacc will tell you.
|
|
114
121
|
|
|
115
122
|
### `gitacc list`
|
|
@@ -9,7 +9,7 @@ long_description = readme_file.read_text() if readme_file.exists() else ""
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name="gitacc-switcher",
|
|
12
|
-
version="1.0
|
|
12
|
+
version="1.1.0",
|
|
13
13
|
description="Git Account Switcher - Manage multiple Git SSH accounts easily",
|
|
14
14
|
long_description=long_description,
|
|
15
15
|
long_description_content_type="text/markdown",
|
|
@@ -103,6 +103,45 @@ class TestSwitchAccount:
|
|
|
103
103
|
manager.switch_account("work")
|
|
104
104
|
manager.ssh_manager.clear_all_keys.assert_called_once()
|
|
105
105
|
|
|
106
|
+
def test_local_fails_when_not_a_git_repo(self, manager):
|
|
107
|
+
manager.config_manager.get_account.return_value = WORK_ACCOUNT
|
|
108
|
+
manager.config_manager.is_git_repo.return_value = False
|
|
109
|
+
assert manager.switch_account("work", local=True) is False
|
|
110
|
+
manager.config_manager.set_git_config_local.assert_not_called()
|
|
111
|
+
|
|
112
|
+
def test_local_sets_local_git_config(self, manager, tmp_path):
|
|
113
|
+
key = tmp_path / "id_rsa_work"
|
|
114
|
+
key.write_text("key")
|
|
115
|
+
account = {**WORK_ACCOUNT, "private_key": str(key)}
|
|
116
|
+
manager.config_manager.get_account.return_value = account
|
|
117
|
+
manager.config_manager.is_git_repo.return_value = True
|
|
118
|
+
manager.ssh_manager.is_ssh_agent_running.return_value = True
|
|
119
|
+
manager.ssh_manager.clear_all_keys.return_value = True
|
|
120
|
+
manager.ssh_manager.add_key_to_agent.return_value = (True, None)
|
|
121
|
+
manager.config_manager.set_git_config_local.return_value = True
|
|
122
|
+
with patch("subprocess.run") as mock_run:
|
|
123
|
+
mock_run.return_value = MagicMock(returncode=0, stdout="1 key\n")
|
|
124
|
+
result = manager.switch_account("work", local=True)
|
|
125
|
+
assert result is True
|
|
126
|
+
manager.config_manager.set_git_config_local.assert_called_once_with(
|
|
127
|
+
"Work User", "work@company.com"
|
|
128
|
+
)
|
|
129
|
+
manager.config_manager.set_git_config.assert_not_called()
|
|
130
|
+
|
|
131
|
+
def test_global_switch_does_not_check_git_repo(self, manager, tmp_path):
|
|
132
|
+
key = tmp_path / "id_rsa_work"
|
|
133
|
+
key.write_text("key")
|
|
134
|
+
account = {**WORK_ACCOUNT, "private_key": str(key)}
|
|
135
|
+
manager.config_manager.get_account.return_value = account
|
|
136
|
+
manager.ssh_manager.is_ssh_agent_running.return_value = True
|
|
137
|
+
manager.ssh_manager.clear_all_keys.return_value = True
|
|
138
|
+
manager.ssh_manager.add_key_to_agent.return_value = (True, None)
|
|
139
|
+
manager.config_manager.set_git_config.return_value = True
|
|
140
|
+
with patch("subprocess.run") as mock_run:
|
|
141
|
+
mock_run.return_value = MagicMock(returncode=0, stdout="1 key\n")
|
|
142
|
+
manager.switch_account("work")
|
|
143
|
+
manager.config_manager.is_git_repo.assert_not_called()
|
|
144
|
+
|
|
106
145
|
|
|
107
146
|
class TestLogout:
|
|
108
147
|
def test_kills_agent_when_running(self, manager):
|
|
@@ -37,6 +37,15 @@ class TestParserCommands:
|
|
|
37
37
|
args = cli.parser.parse_args(["switch", "mywork"])
|
|
38
38
|
assert args.command == "switch"
|
|
39
39
|
assert args.account_name == "mywork"
|
|
40
|
+
assert args.local is False
|
|
41
|
+
|
|
42
|
+
def test_switch_local_flag(self, cli):
|
|
43
|
+
args = cli.parser.parse_args(["switch", "mywork", "--local"])
|
|
44
|
+
assert args.local is True
|
|
45
|
+
|
|
46
|
+
def test_switch_local_short_flag(self, cli):
|
|
47
|
+
args = cli.parser.parse_args(["switch", "mywork", "-l"])
|
|
48
|
+
assert args.local is True
|
|
40
49
|
|
|
41
50
|
def test_remove_with_account(self, cli):
|
|
42
51
|
args = cli.parser.parse_args(["remove", "mywork"])
|
|
@@ -103,11 +112,19 @@ class TestHandlers:
|
|
|
103
112
|
|
|
104
113
|
def test_handle_switch_success(self, cli):
|
|
105
114
|
cli.account_manager.switch_account.return_value = True
|
|
106
|
-
|
|
115
|
+
args = argparse.Namespace(account_name="work", local=False)
|
|
116
|
+
assert cli._handle_switch(args) == 0
|
|
107
117
|
|
|
108
118
|
def test_handle_switch_failure(self, cli):
|
|
109
119
|
cli.account_manager.switch_account.return_value = False
|
|
110
|
-
|
|
120
|
+
args = argparse.Namespace(account_name="work", local=False)
|
|
121
|
+
assert cli._handle_switch(args) == 1
|
|
122
|
+
|
|
123
|
+
def test_handle_switch_passes_local_flag(self, cli):
|
|
124
|
+
cli.account_manager.switch_account.return_value = True
|
|
125
|
+
args = argparse.Namespace(account_name="work", local=True)
|
|
126
|
+
cli._handle_switch(args)
|
|
127
|
+
cli.account_manager.switch_account.assert_called_once_with("work", local=True)
|
|
111
128
|
|
|
112
129
|
def test_handle_remove_success(self, cli):
|
|
113
130
|
cli.account_manager.remove_account.return_value = True
|
|
@@ -181,6 +181,30 @@ class TestSetGitConfig:
|
|
|
181
181
|
assert config.set_git_config("Test", "test@test.com") is False
|
|
182
182
|
|
|
183
183
|
|
|
184
|
+
class TestSetGitConfigLocal:
|
|
185
|
+
def test_calls_git_config_without_global_flag(self, config):
|
|
186
|
+
with patch("subprocess.run") as mock_run:
|
|
187
|
+
mock_run.return_value = MagicMock(returncode=0)
|
|
188
|
+
result = config.set_git_config_local("Test User", "test@test.com")
|
|
189
|
+
assert result is True
|
|
190
|
+
assert mock_run.call_count == 2
|
|
191
|
+
for call in mock_run.call_args_list:
|
|
192
|
+
assert "--global" not in call.args[0]
|
|
193
|
+
|
|
194
|
+
def test_passes_repo_path_as_cwd(self, config, tmp_path):
|
|
195
|
+
with patch("subprocess.run") as mock_run:
|
|
196
|
+
mock_run.return_value = MagicMock(returncode=0)
|
|
197
|
+
config.set_git_config_local("Test User", "test@test.com", tmp_path)
|
|
198
|
+
for call in mock_run.call_args_list:
|
|
199
|
+
assert call.kwargs["cwd"] == tmp_path
|
|
200
|
+
|
|
201
|
+
def test_returns_false_on_error(self, config):
|
|
202
|
+
with patch(
|
|
203
|
+
"subprocess.run", side_effect=subprocess.CalledProcessError(1, "git")
|
|
204
|
+
):
|
|
205
|
+
assert config.set_git_config_local("Test", "test@test.com") is False
|
|
206
|
+
|
|
207
|
+
|
|
184
208
|
class TestGetCurrentGitConfig:
|
|
185
209
|
def test_returns_name_and_email_keys(self, config):
|
|
186
210
|
with patch("subprocess.run") as mock_run:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitacc_switcher-1.0.4 → gitacc_switcher-1.1.0}/gitacc_switcher.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|