gcloud-account-changer 0.1__py3-none-any.whl
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.
- gcloud_account_changer-0.1.dist-info/METADATA +134 -0
- gcloud_account_changer-0.1.dist-info/RECORD +7 -0
- gcloud_account_changer-0.1.dist-info/WHEEL +5 -0
- gcloud_account_changer-0.1.dist-info/entry_points.txt +2 -0
- gcloud_account_changer-0.1.dist-info/top_level.txt +1 -0
- gcloud_changer/__init__.py +0 -0
- gcloud_changer/gcloud_set_account.py +175 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: gcloud-account-changer
|
3
|
+
Version: 0.1
|
4
|
+
Summary: Google Cloud CLI Account & Project Selector
|
5
|
+
Author-email: Lee Ji-Ho <search5@gmail.com>
|
6
|
+
Project-URL: Homepage, https://github.com/search5/gcloud-account-changer
|
7
|
+
Keywords: gcp,gcloud
|
8
|
+
Classifier: Programming Language :: Python
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
10
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
11
|
+
Classifier: Topic :: Software Development :: Build Tools
|
12
|
+
Requires-Python: >=3.8
|
13
|
+
Description-Content-Type: text/markdown
|
14
|
+
Requires-Dist: urwid
|
15
|
+
|
16
|
+
# gcloud-account-changer
|
17
|
+
|
18
|
+
Google Cloud CLI Account & Project Selector - A terminal-based interactive tool for managing multiple Google Cloud accounts and projects.
|
19
|
+
|
20
|
+
## Features
|
21
|
+
|
22
|
+
- π Easy switching between multiple Google Cloud accounts
|
23
|
+
- π Interactive project selection for each account
|
24
|
+
- π¨ Terminal-based user interface using urwid
|
25
|
+
- β‘ Quick account and project configuration
|
26
|
+
- πΎ Remember your preferred settings
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
|
30
|
+
### Using pipx (Recommended)
|
31
|
+
|
32
|
+
```bash
|
33
|
+
pipx install gcloud-account-changer
|
34
|
+
```
|
35
|
+
|
36
|
+
### Using uv
|
37
|
+
|
38
|
+
```bash
|
39
|
+
uv tool install gcloud-account-changer
|
40
|
+
```
|
41
|
+
|
42
|
+
### Using pip
|
43
|
+
|
44
|
+
```bash
|
45
|
+
pip install gcloud-account-changer
|
46
|
+
```
|
47
|
+
|
48
|
+
## Prerequisites
|
49
|
+
|
50
|
+
- Google Cloud CLI (`gcloud`) must be installed and configured
|
51
|
+
- Python 3.7 or higher
|
52
|
+
- At least one Google Cloud account authenticated with `gcloud auth login`
|
53
|
+
|
54
|
+
## Usage
|
55
|
+
|
56
|
+
After installation, run the tool using:
|
57
|
+
|
58
|
+
```bash
|
59
|
+
gcloud-account-changer
|
60
|
+
```
|
61
|
+
|
62
|
+
The interactive interface will guide you through:
|
63
|
+
|
64
|
+
1. **Account Selection**: Choose from your authenticated Google Cloud accounts
|
65
|
+
2. **Project Selection**: Select a project from the chosen account
|
66
|
+
3. **Configuration**: Apply the selected account and project to your gcloud CLI
|
67
|
+
|
68
|
+
### Keyboard Navigation
|
69
|
+
|
70
|
+
- **Arrow Keys**: Navigate through options
|
71
|
+
- **Enter**: Select an option
|
72
|
+
- **Esc/Q**: Quit the application
|
73
|
+
|
74
|
+
## Getting Started
|
75
|
+
|
76
|
+
1. First, authenticate your Google Cloud accounts:
|
77
|
+
```bash
|
78
|
+
gcloud auth login
|
79
|
+
```
|
80
|
+
|
81
|
+
2. Install gcloud-account-changer:
|
82
|
+
```bash
|
83
|
+
pipx install gcloud-account-changer
|
84
|
+
```
|
85
|
+
|
86
|
+
3. Run the tool:
|
87
|
+
```bash
|
88
|
+
gcloud-account-changer
|
89
|
+
```
|
90
|
+
|
91
|
+
4. Select your desired account and project from the interactive interface
|
92
|
+
|
93
|
+
## Requirements
|
94
|
+
|
95
|
+
- `urwid` - Terminal user interface library
|
96
|
+
- `gcloud` CLI tool (must be installed separately)
|
97
|
+
|
98
|
+
## Development
|
99
|
+
|
100
|
+
To contribute to this project:
|
101
|
+
|
102
|
+
1. Clone the repository:
|
103
|
+
```bash
|
104
|
+
git clone https://github.com/search5/gcloud-account-changer.git
|
105
|
+
cd gcloud-account-changer
|
106
|
+
```
|
107
|
+
|
108
|
+
2. Install in development mode:
|
109
|
+
```bash
|
110
|
+
uv sync
|
111
|
+
```
|
112
|
+
|
113
|
+
## License
|
114
|
+
|
115
|
+
This project is licensed under the BSD License.
|
116
|
+
|
117
|
+
## Author
|
118
|
+
|
119
|
+
**Lee Ji-Ho** - [search5@gmail.com](mailto:search5@gmail.com)
|
120
|
+
|
121
|
+
## Contributing
|
122
|
+
|
123
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
124
|
+
|
125
|
+
## Issues
|
126
|
+
|
127
|
+
If you encounter any problems or have feature requests, please create an issue on the [GitHub repository](https://github.com/search5/gcloud-account-changer/issues).
|
128
|
+
|
129
|
+
## Changelog
|
130
|
+
|
131
|
+
### v0.1
|
132
|
+
- Initial release
|
133
|
+
- Basic account and project switching functionality
|
134
|
+
- Terminal-based user interface
|
@@ -0,0 +1,7 @@
|
|
1
|
+
gcloud_changer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
gcloud_changer/gcloud_set_account.py,sha256=Z-BGSPbTq8i7wd4bgV_CcMsjww6PRZW3zTgULV19iDo,6252
|
3
|
+
gcloud_account_changer-0.1.dist-info/METADATA,sha256=pHX3XhEjFnrBDg-vl6mgCeQEMH5-WcTx1tX9sef4cTo,3090
|
4
|
+
gcloud_account_changer-0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
5
|
+
gcloud_account_changer-0.1.dist-info/entry_points.txt,sha256=n5vsiMoQwMf0E3m8rv0bDDfxzth_8AiUCatotTlS-Uo,89
|
6
|
+
gcloud_account_changer-0.1.dist-info/top_level.txt,sha256=0LTrWKGvsr8PT6zt_k1ikvfQj2GJaL_1yZ4eS9HolLM,15
|
7
|
+
gcloud_account_changer-0.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
gcloud_changer
|
File without changes
|
@@ -0,0 +1,175 @@
|
|
1
|
+
import urwid
|
2
|
+
import shutil
|
3
|
+
from subprocess import run, PIPE
|
4
|
+
import shlex
|
5
|
+
# import logging
|
6
|
+
import re
|
7
|
+
# import os
|
8
|
+
|
9
|
+
# os.unlink('example.log')
|
10
|
+
|
11
|
+
# logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
|
12
|
+
|
13
|
+
gcloud_cmd = shutil.which("gcloud")
|
14
|
+
if not gcloud_cmd:
|
15
|
+
raise OSError('gcloudλ₯Ό μ°Ύμ μ μμ΅λλ€')
|
16
|
+
|
17
|
+
program_top_menus = []
|
18
|
+
|
19
|
+
# Gcloud account statusλ gcloud auth list κ²°κ³Όλ₯Ό μ½μ΄ keyλ₯Ό μ£Όλ©΄ νμ±ν μνμΈμ§ μλμ§ μλ €μ£Όκ³ νλ‘μ νΈ λ³κ²½λ κ°λ₯ν΄μΌ νλ€
|
20
|
+
class GCloudAuthStatus:
|
21
|
+
def __init__(self) -> None:
|
22
|
+
self.__account_list = {}
|
23
|
+
|
24
|
+
def status_load(self):
|
25
|
+
# global loop
|
26
|
+
# loop.stop()
|
27
|
+
gcloud_auth_list = run(shlex.split("gcloud auth list"), stdout=PIPE, stderr=PIPE)
|
28
|
+
if gcloud_auth_list.stdout:
|
29
|
+
for account_item in gcloud_auth_list.stdout.decode('utf-8').splitlines()[2:]:
|
30
|
+
activate_account, account_address = re.match(r'(\*?)\s+([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]*)', account_item).groups()
|
31
|
+
self.__account_list[account_address] = {'activate': activate_account.startswith('*')}
|
32
|
+
# loop.start()
|
33
|
+
|
34
|
+
def __getitem__(self, account):
|
35
|
+
if account not in self.__account_list:
|
36
|
+
return KeyError('The account that requested activation is not logged into gcloud.')
|
37
|
+
|
38
|
+
return self.__account_list[account]['activate']
|
39
|
+
|
40
|
+
def activate(self, account):
|
41
|
+
if account not in self.__account_list:
|
42
|
+
raise ValueError('The account that requested activation is not logged into gcloud.')
|
43
|
+
|
44
|
+
global loop
|
45
|
+
loop.stop()
|
46
|
+
run(shlex.split(f"gcloud config set account {account}"), stdout=PIPE, stderr=PIPE)
|
47
|
+
loop.start()
|
48
|
+
|
49
|
+
return True
|
50
|
+
|
51
|
+
def getlist(self):
|
52
|
+
return self.__account_list.keys()
|
53
|
+
|
54
|
+
|
55
|
+
account_checker = GCloudAuthStatus()
|
56
|
+
account_checker.status_load()
|
57
|
+
|
58
|
+
program_top_menus.extend(account_checker.getlist())
|
59
|
+
program_top_menus.append('New Google account login')
|
60
|
+
|
61
|
+
palette = [('reversed', 'standout', ''),
|
62
|
+
('bg', 'white', 'dark blue')]
|
63
|
+
|
64
|
+
|
65
|
+
def exit_on_q(key):
|
66
|
+
if key in ('q', 'Q'):
|
67
|
+
raise urwid.ExitMainLoop()
|
68
|
+
elif key == 'left':
|
69
|
+
back(None)
|
70
|
+
|
71
|
+
|
72
|
+
def menu(title, choices):
|
73
|
+
body = [urwid.Text(title), urwid.Divider()]
|
74
|
+
for c in choices:
|
75
|
+
button = urwid.Button(c)
|
76
|
+
urwid.connect_signal(button, 'click', account_chosen, c)
|
77
|
+
body.append(urwid.AttrMap(button, None, focus_map='reversed'))
|
78
|
+
return urwid.ListBox(urwid.SimpleFocusListWalker(body))
|
79
|
+
|
80
|
+
|
81
|
+
class ProjectSelector:
|
82
|
+
def __init__(self) -> None:
|
83
|
+
self.__project_list = {}
|
84
|
+
|
85
|
+
def load_project_list(self):
|
86
|
+
global loop
|
87
|
+
loop.stop()
|
88
|
+
# Billing Quota νλ‘μ νΈκ° μ€μ λμ΄ μλ κ²½μ° νλ‘μ νΈ λͺ©λ‘μ κ°μ Έμ€λ λ©μλκ° μ€λμ νλ―λ‘ λ―Έλ¦¬ ν΄μ ν΄λλ€
|
89
|
+
run(shlex.split("gcloud config unset billing/quota_project"), stdout=PIPE, stderr=PIPE)
|
90
|
+
|
91
|
+
gcloud_project_list_cmd = run(shlex.split("gcloud projects list"), stdout=PIPE, stderr=PIPE)
|
92
|
+
gcloud_project_list = gcloud_project_list_cmd.stdout.decode('utf-8').splitlines()
|
93
|
+
|
94
|
+
project_header_split_pos = [0, gcloud_project_list[0].index('NAME'), gcloud_project_list[0].index('PROJECT_NUMBER')]
|
95
|
+
|
96
|
+
for row in gcloud_project_list[1:]:
|
97
|
+
project_id = row[project_header_split_pos[0]:project_header_split_pos[1]].strip()
|
98
|
+
project_name = row[project_header_split_pos[1]:project_header_split_pos[2]].strip()
|
99
|
+
project_number = row[project_header_split_pos[2]:].strip()
|
100
|
+
self.__project_list[project_name] = {'id': project_id, 'number': project_number}
|
101
|
+
|
102
|
+
loop.start()
|
103
|
+
|
104
|
+
def activate(self, project_name):
|
105
|
+
if project_name not in self.__project_list:
|
106
|
+
raise ValueError('The project you requested to activate cannot be found in gcloud.')
|
107
|
+
|
108
|
+
global loop
|
109
|
+
loop.stop()
|
110
|
+
run(shlex.split(f"gcloud config set project {self.__project_list[project_name]['id']}"), stderr=PIPE, stdout=PIPE)
|
111
|
+
loop.start()
|
112
|
+
|
113
|
+
return True
|
114
|
+
|
115
|
+
def getlist(self):
|
116
|
+
return self.__project_list.keys()
|
117
|
+
|
118
|
+
|
119
|
+
project_selector = ProjectSelector()
|
120
|
+
|
121
|
+
|
122
|
+
def account_chosen(button, choice):
|
123
|
+
if button.get_label() == "New Google account login":
|
124
|
+
gcloud_auth_login = run(shlex.split("gcloud auth login"), stdout=PIPE, stderr=PIPE)
|
125
|
+
logined_account = next(filter(lambda x: x.startswith(b'You are now logged in as'), gcloud_auth_login.stderr.splitlines()))
|
126
|
+
choice = logined_account.removeprefix(b"You are now logged in as [").removesuffix(b"].").decode('utf-8')
|
127
|
+
|
128
|
+
# νμ¬ λ‘κ·ΈμΈ μ€μΈ κ³μ μ΄ μ νν κ³μ κ³Ό κ°μ§ μμΌλ©΄ κ³μ μ μ ννλλ‘ ν΄μΌ νλ€
|
129
|
+
account_checker.status_load()
|
130
|
+
if not account_checker[choice]:
|
131
|
+
account_checker.activate(choice)
|
132
|
+
|
133
|
+
project_selector.load_project_list()
|
134
|
+
project_list = project_selector.getlist()
|
135
|
+
|
136
|
+
response = urwid.Text([f'You chosee Project ', f'[{choice}]', u'\n'])
|
137
|
+
|
138
|
+
choice_project = [response]
|
139
|
+
for project in project_list:
|
140
|
+
button = urwid.Button(project)
|
141
|
+
urwid.connect_signal(button, 'click', project_select, project)
|
142
|
+
choice_project.append(urwid.AttrMap(button, 'bg', focus_map='reversed'))
|
143
|
+
|
144
|
+
main.original_widget = urwid.Filler(urwid.Pile(choice_project), valign='top')
|
145
|
+
|
146
|
+
|
147
|
+
def project_select(button, project):
|
148
|
+
project_selector.activate(project)
|
149
|
+
raise urwid.ExitMainLoop()
|
150
|
+
|
151
|
+
|
152
|
+
def exit_program(button):
|
153
|
+
raise urwid.ExitMainLoop()
|
154
|
+
|
155
|
+
|
156
|
+
def back(button):
|
157
|
+
global main
|
158
|
+
main.original_widget = menu('Select the Google account you want to use.', program_top_menus)
|
159
|
+
|
160
|
+
|
161
|
+
loop = None
|
162
|
+
main = None
|
163
|
+
|
164
|
+
def program_run():
|
165
|
+
global main
|
166
|
+
global loop
|
167
|
+
|
168
|
+
main = urwid.Padding(menu('Select the Google account you want to use.', program_top_menus), left=2, right=2)
|
169
|
+
main = urwid.AttrMap(main, 'bg')
|
170
|
+
top = urwid.Overlay(main, urwid.SolidFill(u'\N{MEDIUM SHADE}'),
|
171
|
+
align='center', width=('relative', 100),
|
172
|
+
valign='middle', height=('relative', 100),
|
173
|
+
min_width=20, min_height=9)
|
174
|
+
loop = urwid.MainLoop(top, palette=palette, unhandled_input=exit_on_q)
|
175
|
+
loop.run()
|