odooflow-cli 0.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.
- odooflow_cli-0.1.0/LICENSE +21 -0
- odooflow_cli-0.1.0/PKG-INFO +174 -0
- odooflow_cli-0.1.0/README.md +141 -0
- odooflow_cli-0.1.0/odooflow/__init__.py +1 -0
- odooflow_cli-0.1.0/odooflow/cli.py +105 -0
- odooflow_cli-0.1.0/odooflow/commands/__init__.py +0 -0
- odooflow_cli-0.1.0/odooflow/commands/clone_module.py +160 -0
- odooflow_cli-0.1.0/odooflow/commands/config.py +63 -0
- odooflow_cli-0.1.0/odooflow/commands/init_module_env.py +37 -0
- odooflow_cli-0.1.0/odooflow/commands/keygen.py +56 -0
- odooflow_cli-0.1.0/odooflow/commands/push.py +132 -0
- odooflow_cli-0.1.0/odooflow/commands/remote.py +133 -0
- odooflow_cli-0.1.0/odooflow/commands/sync_env.py +38 -0
- odooflow_cli-0.1.0/odooflow/config_manager.py +56 -0
- odooflow_cli-0.1.0/odooflow/utils/env.py +40 -0
- odooflow_cli-0.1.0/odooflow/utils/ssh.py +137 -0
- odooflow_cli-0.1.0/odooflow_cli.egg-info/PKG-INFO +174 -0
- odooflow_cli-0.1.0/odooflow_cli.egg-info/SOURCES.txt +31 -0
- odooflow_cli-0.1.0/odooflow_cli.egg-info/dependency_links.txt +1 -0
- odooflow_cli-0.1.0/odooflow_cli.egg-info/entry_points.txt +2 -0
- odooflow_cli-0.1.0/odooflow_cli.egg-info/requires.txt +15 -0
- odooflow_cli-0.1.0/odooflow_cli.egg-info/top_level.txt +4 -0
- odooflow_cli-0.1.0/pyproject.toml +54 -0
- odooflow_cli-0.1.0/setup.cfg +4 -0
- odooflow_cli-0.1.0/tests/__init__.py +0 -0
- odooflow_cli-0.1.0/tests/test_commands_config.py +104 -0
- odooflow_cli-0.1.0/tests/test_commands_init_module_env.py +74 -0
- odooflow_cli-0.1.0/tests/test_commands_keygen.py +23 -0
- odooflow_cli-0.1.0/tests/test_commands_remote.py +32 -0
- odooflow_cli-0.1.0/tests/test_commands_sync_env.py +70 -0
- odooflow_cli-0.1.0/tests/test_config_manager.py +123 -0
- odooflow_cli-0.1.0/tests/test_utils_env.py +106 -0
- odooflow_cli-0.1.0/tests/test_utils_ssh.py +23 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mohammad Hamdan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: odooflow-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: OdooFlow CLI - streamline your Odoo development workflow
|
|
5
|
+
Author: Mohammad A. Hamdan
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/anomalyco/odooflow
|
|
8
|
+
Project-URL: Repository, https://github.com/anomalyco/odooflow
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
15
|
+
Requires-Python: >=3.7
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: typer[all]
|
|
19
|
+
Requires-Dist: rich
|
|
20
|
+
Requires-Dist: GitPython>=3.1.44
|
|
21
|
+
Requires-Dist: requests>=2.32.3
|
|
22
|
+
Requires-Dist: paramiko>=3.5.1
|
|
23
|
+
Requires-Dist: tqdm>=4.67.1
|
|
24
|
+
Requires-Dist: bcrypt>=4.3.0
|
|
25
|
+
Requires-Dist: cryptography>=45.0.3
|
|
26
|
+
Requires-Dist: PyNaCl>=1.5.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: build>=1.2.2; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=6.1.0; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# đ Odooflow CLI
|
|
35
|
+
|
|
36
|
+
**Odooflow CLI** is a command-line interface tool designed to streamline the development workflow for Odoo projects. It helps clone Odoo modules (and their dependencies), handles GitLab lookups, and provides options for deep recursive cloning.
|
|
37
|
+
|
|
38
|
+
## đ Features
|
|
39
|
+
|
|
40
|
+
- Clone an Odoo module by Git URL
|
|
41
|
+
- Recursively resolve and clone all dependencies
|
|
42
|
+
- Smart skip of Odoo core modules
|
|
43
|
+
- Branch selection for cloning
|
|
44
|
+
- Helpful and colorful CLI output
|
|
45
|
+
- Built using [Typer](https://typer.tiangolo.com/) and Python 3.9+
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## đĻ Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git clone https://github.com/YOUR_USERNAME/odooflow-cli.git
|
|
53
|
+
cd odooflow-cli
|
|
54
|
+
pip install .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or install directly from source for development:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install -e .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## đ ī¸ Usage
|
|
66
|
+
|
|
67
|
+
Once installed, you can use the CLI by running:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
odooflow --help
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Available Commands:
|
|
74
|
+
|
|
75
|
+
- **`init`**: Initialize the Odoo module environment file and sync metadata with manifest
|
|
76
|
+
- **`sync-env`**: Sync the environment file from manifest
|
|
77
|
+
- **`config`**: Update or show OdooFlow CLI configuration
|
|
78
|
+
- **`clone`**: Clone a module and its dependencies from a git repository
|
|
79
|
+
- **`remote`**: Manage remote connections for Git and deployment server
|
|
80
|
+
- **`ssh-keygen`**: Generate a secure SSH key pair
|
|
81
|
+
- **`push`**: Push the current Git branch and upload the project to the test server
|
|
82
|
+
|
|
83
|
+
### Clone Command Options:
|
|
84
|
+
|
|
85
|
+
| Flag | Description |
|
|
86
|
+
|-------------|------------------------------------------|
|
|
87
|
+
| `--url` | Full HTTP URL of the module repo |
|
|
88
|
+
| `--branch` | (Optional) Git branch to clone from |
|
|
89
|
+
| `--deep` | Recursively clone all dependencies |
|
|
90
|
+
|
|
91
|
+
### đ Examples:
|
|
92
|
+
|
|
93
|
+
Clone a single module:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
odooflow clone --url https://gitlab.com/mygroup/my_odoo_module.git
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Clone with specific branch:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
odooflow clone --url https://gitlab.com/mygroup/my_odoo_module.git --branch 17.0
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Clone deeply with dependencies:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
odooflow clone --url https://gitlab.com/mygroup/my_odoo_module.git --deep
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## đ Project Structure
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
odooflow/
|
|
117
|
+
âââ odooflow/
|
|
118
|
+
â âââ __init__.py
|
|
119
|
+
â âââ cli.py
|
|
120
|
+
â âââ config_manager.py
|
|
121
|
+
â âââ commands/
|
|
122
|
+
â â âââ __init__.py
|
|
123
|
+
â â âââ clone_module.py
|
|
124
|
+
â â âââ config.py
|
|
125
|
+
â â âââ init_module_env.py
|
|
126
|
+
â â âââ keygen.py
|
|
127
|
+
â â âââ push.py
|
|
128
|
+
â â âââ remote.py
|
|
129
|
+
â â âââ sync_env.py
|
|
130
|
+
â âââ utils/
|
|
131
|
+
â âââ env.py
|
|
132
|
+
â âââ ssh.py
|
|
133
|
+
âââ tests/
|
|
134
|
+
âââ README.md
|
|
135
|
+
âââ requirements.txt
|
|
136
|
+
âââ setup.py
|
|
137
|
+
âââ LICENSE
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## đ¤ Contributing
|
|
143
|
+
|
|
144
|
+
Contributions are welcome! Please open an issue or submit a pull request with any improvements, bug fixes, or new features.
|
|
145
|
+
|
|
146
|
+
1. Fork the repository
|
|
147
|
+
2. Create a new branch (`git checkout -b feature/your-feature`)
|
|
148
|
+
3. Commit your changes (`git commit -am 'Add new feature'`)
|
|
149
|
+
4. Push to the branch (`git push origin feature/your-feature`)
|
|
150
|
+
5. Open a Pull Request
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## đ License
|
|
155
|
+
|
|
156
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## WISH-LIST:
|
|
161
|
+
|
|
162
|
+
Move this CLI into fully-integrated Odoo environment, using Odoo, users can create issues, add the amount of details, then sync these issues with Odooflow.
|
|
163
|
+
|
|
164
|
+
We can do integration with any code agent to help developers to achieve these issues
|
|
165
|
+
|
|
166
|
+
same thing for pipelines, I think it will be amazing if developers can build pipelines using Odoo, then apply the same pipelines using Odooflow.
|
|
167
|
+
|
|
168
|
+
I have many things in my head, I will back soon to this project.
|
|
169
|
+
|
|
170
|
+
## đ¨âđģ Author
|
|
171
|
+
|
|
172
|
+
Made with â¤ī¸ by Mohammad A. Hamdan
|
|
173
|
+
|
|
174
|
+
---
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# đ Odooflow CLI
|
|
2
|
+
|
|
3
|
+
**Odooflow CLI** is a command-line interface tool designed to streamline the development workflow for Odoo projects. It helps clone Odoo modules (and their dependencies), handles GitLab lookups, and provides options for deep recursive cloning.
|
|
4
|
+
|
|
5
|
+
## đ Features
|
|
6
|
+
|
|
7
|
+
- Clone an Odoo module by Git URL
|
|
8
|
+
- Recursively resolve and clone all dependencies
|
|
9
|
+
- Smart skip of Odoo core modules
|
|
10
|
+
- Branch selection for cloning
|
|
11
|
+
- Helpful and colorful CLI output
|
|
12
|
+
- Built using [Typer](https://typer.tiangolo.com/) and Python 3.9+
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## đĻ Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git clone https://github.com/YOUR_USERNAME/odooflow-cli.git
|
|
20
|
+
cd odooflow-cli
|
|
21
|
+
pip install .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or install directly from source for development:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install -e .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## đ ī¸ Usage
|
|
33
|
+
|
|
34
|
+
Once installed, you can use the CLI by running:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
odooflow --help
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Available Commands:
|
|
41
|
+
|
|
42
|
+
- **`init`**: Initialize the Odoo module environment file and sync metadata with manifest
|
|
43
|
+
- **`sync-env`**: Sync the environment file from manifest
|
|
44
|
+
- **`config`**: Update or show OdooFlow CLI configuration
|
|
45
|
+
- **`clone`**: Clone a module and its dependencies from a git repository
|
|
46
|
+
- **`remote`**: Manage remote connections for Git and deployment server
|
|
47
|
+
- **`ssh-keygen`**: Generate a secure SSH key pair
|
|
48
|
+
- **`push`**: Push the current Git branch and upload the project to the test server
|
|
49
|
+
|
|
50
|
+
### Clone Command Options:
|
|
51
|
+
|
|
52
|
+
| Flag | Description |
|
|
53
|
+
|-------------|------------------------------------------|
|
|
54
|
+
| `--url` | Full HTTP URL of the module repo |
|
|
55
|
+
| `--branch` | (Optional) Git branch to clone from |
|
|
56
|
+
| `--deep` | Recursively clone all dependencies |
|
|
57
|
+
|
|
58
|
+
### đ Examples:
|
|
59
|
+
|
|
60
|
+
Clone a single module:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
odooflow clone --url https://gitlab.com/mygroup/my_odoo_module.git
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Clone with specific branch:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
odooflow clone --url https://gitlab.com/mygroup/my_odoo_module.git --branch 17.0
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Clone deeply with dependencies:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
odooflow clone --url https://gitlab.com/mygroup/my_odoo_module.git --deep
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## đ Project Structure
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
odooflow/
|
|
84
|
+
âââ odooflow/
|
|
85
|
+
â âââ __init__.py
|
|
86
|
+
â âââ cli.py
|
|
87
|
+
â âââ config_manager.py
|
|
88
|
+
â âââ commands/
|
|
89
|
+
â â âââ __init__.py
|
|
90
|
+
â â âââ clone_module.py
|
|
91
|
+
â â âââ config.py
|
|
92
|
+
â â âââ init_module_env.py
|
|
93
|
+
â â âââ keygen.py
|
|
94
|
+
â â âââ push.py
|
|
95
|
+
â â âââ remote.py
|
|
96
|
+
â â âââ sync_env.py
|
|
97
|
+
â âââ utils/
|
|
98
|
+
â âââ env.py
|
|
99
|
+
â âââ ssh.py
|
|
100
|
+
âââ tests/
|
|
101
|
+
âââ README.md
|
|
102
|
+
âââ requirements.txt
|
|
103
|
+
âââ setup.py
|
|
104
|
+
âââ LICENSE
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## đ¤ Contributing
|
|
110
|
+
|
|
111
|
+
Contributions are welcome! Please open an issue or submit a pull request with any improvements, bug fixes, or new features.
|
|
112
|
+
|
|
113
|
+
1. Fork the repository
|
|
114
|
+
2. Create a new branch (`git checkout -b feature/your-feature`)
|
|
115
|
+
3. Commit your changes (`git commit -am 'Add new feature'`)
|
|
116
|
+
4. Push to the branch (`git push origin feature/your-feature`)
|
|
117
|
+
5. Open a Pull Request
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## đ License
|
|
122
|
+
|
|
123
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## WISH-LIST:
|
|
128
|
+
|
|
129
|
+
Move this CLI into fully-integrated Odoo environment, using Odoo, users can create issues, add the amount of details, then sync these issues with Odooflow.
|
|
130
|
+
|
|
131
|
+
We can do integration with any code agent to help developers to achieve these issues
|
|
132
|
+
|
|
133
|
+
same thing for pipelines, I think it will be amazing if developers can build pipelines using Odoo, then apply the same pipelines using Odooflow.
|
|
134
|
+
|
|
135
|
+
I have many things in my head, I will back soon to this project.
|
|
136
|
+
|
|
137
|
+
## đ¨âđģ Author
|
|
138
|
+
|
|
139
|
+
Made with â¤ī¸ by Mohammad A. Hamdan
|
|
140
|
+
|
|
141
|
+
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from odooflow.commands.init_module_env import init_module_env
|
|
5
|
+
from odooflow.commands.sync_env import sync_env as sync_env_command
|
|
6
|
+
from odooflow.commands.config import config as config_command
|
|
7
|
+
from odooflow.commands.clone_module import clone_module_command
|
|
8
|
+
from odooflow.commands.remote import remote as remote_command
|
|
9
|
+
from odooflow.commands.keygen import generate_ssh_key as keygen_command
|
|
10
|
+
from odooflow.commands.push import push_command
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(help="OdooFlow CLI â streamline your Odoo development workflow.")
|
|
13
|
+
|
|
14
|
+
@app.command(name="init")
|
|
15
|
+
def init_manifest(
|
|
16
|
+
author: Optional[str] = typer.Option(None, help="Author name"),
|
|
17
|
+
website : Optional[str] = typer.Option(None, help="Website"),
|
|
18
|
+
odoo_version: Optional[str] = typer.Option(None, help="Odoo version"),
|
|
19
|
+
license_name: Optional[str] = typer.Option(None, help="License"),
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Initialize the Odoo module environment file and sync metadata with manifest.
|
|
23
|
+
"""
|
|
24
|
+
init_module_env(author=author, odoo_version=odoo_version, license_name=license_name, website=website)
|
|
25
|
+
|
|
26
|
+
@app.command(name="sync-env")
|
|
27
|
+
def sync_env(
|
|
28
|
+
keys: Optional[str] = typer.Option(None, "--keys", help="Comma-separated keys to sync from manifest to env file.")
|
|
29
|
+
):
|
|
30
|
+
"""
|
|
31
|
+
Sync the environment file (.odooflow.env.json) from manifest.
|
|
32
|
+
"""
|
|
33
|
+
sync_env_command(keys)
|
|
34
|
+
|
|
35
|
+
@app.command("config", help="Update configuration")
|
|
36
|
+
def config(
|
|
37
|
+
env_file: Optional[str] = typer.Option(None, help="Set custom env file name"),
|
|
38
|
+
manifest_file: Optional[str] = typer.Option(None, help="Set custom manifest file name"),
|
|
39
|
+
access_token : Optional[str] = typer.Option(None, help="Set Git access token"),
|
|
40
|
+
add_core_module: Optional[str] = typer.Option(None, "--add-core-module", help="Comma-separated list of core modules to add"),
|
|
41
|
+
sync_keys: Optional[str] = typer.Option(None, help="Set default keys to sync from manifest to .env (comma-separated)"),
|
|
42
|
+
show: Optional[bool] = typer.Option(False, "--show", help="Show current config")
|
|
43
|
+
):
|
|
44
|
+
"""
|
|
45
|
+
Update or show OdooFlow CLI configuration (.odooflowrc)
|
|
46
|
+
"""
|
|
47
|
+
config_command(env_file, manifest_file, access_token, add_core_module, sync_keys,show)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@app.command("clone")
|
|
51
|
+
def clone_command(
|
|
52
|
+
repo_url: str = typer.Argument(..., help="HTTP URL of the module repository."),
|
|
53
|
+
branch: Optional[str] = typer.Option(None, "--branch", '-b', help="Branch to clone"),
|
|
54
|
+
depth: int = typer.Option(1, "--depth", "-d", help="Max dependency depth to clone. 1 clones only the target module, 2 clones target + immediate dependencies, etc.")
|
|
55
|
+
):
|
|
56
|
+
"""
|
|
57
|
+
Clone a module and its dependencies from a git repository.
|
|
58
|
+
"""
|
|
59
|
+
clone_module_command(repo_url, branch, depth)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@app.command()
|
|
63
|
+
def remote(
|
|
64
|
+
add_repo: Optional[str] = typer.Option(None, help="Add Git remote URL"),
|
|
65
|
+
branch: Optional[str] = typer.Option(None, help="Target Git branch (defaults to current)"),
|
|
66
|
+
server_json: Optional[str] = typer.Option(None, help="Server config as JSON: '{\"host\": \"127.0.0.1\", \"port\": 22}'")
|
|
67
|
+
):
|
|
68
|
+
"""
|
|
69
|
+
Manage remote connections for Git and deployment server.
|
|
70
|
+
"""
|
|
71
|
+
remote_command(
|
|
72
|
+
add_repo=add_repo,
|
|
73
|
+
server_json=server_json,
|
|
74
|
+
branch=branch
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@app.command("ssh-keygen")
|
|
78
|
+
def generate_ssh_key(
|
|
79
|
+
key_name: str = typer.Option("odooflow_rsa", help="Name of the SSH key file to generate (without extension)."),
|
|
80
|
+
output_dir: Optional[str] = typer.Option(None, help="Directory to save the SSH key. Defaults to ~/.ssh"),
|
|
81
|
+
overwrite: bool = typer.Option(False, help="Overwrite existing key files if they exist."),
|
|
82
|
+
):
|
|
83
|
+
"""
|
|
84
|
+
Generate a secure SSH key pair.
|
|
85
|
+
"""
|
|
86
|
+
keygen_command(key_name=key_name, output_dir=output_dir, overwrite=overwrite)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@app.command()
|
|
90
|
+
def push(
|
|
91
|
+
remote_only: bool = typer.Option(False, "--remote-only", help="Skip Git push and only upload to server"),
|
|
92
|
+
exec_cmd: Optional[str] = typer.Option(None, "--exec", help="Custom shell command to execute on the server after pushing"),
|
|
93
|
+
):
|
|
94
|
+
"""
|
|
95
|
+
Push the current Git branch and upload the project to the test server.
|
|
96
|
+
"""
|
|
97
|
+
push_command(remote_only=remote_only, exec_cmd=exec_cmd)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def main():
|
|
102
|
+
app()
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import typer
|
|
3
|
+
import requests
|
|
4
|
+
import threading
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
8
|
+
|
|
9
|
+
from urllib.parse import urlparse, urlunparse
|
|
10
|
+
from git import Repo, GitCommandError
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from odooflow.config_manager import get_access_token, get_core_modules_from_config, load_config
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_project_url_from_gitlab(module_name: str, base_url: Optional[str] = None) -> Optional[str]:
|
|
17
|
+
"""
|
|
18
|
+
Search GitLab for a project by name and return its HTTPS URL.
|
|
19
|
+
"""
|
|
20
|
+
if base_url is None:
|
|
21
|
+
config = load_config()
|
|
22
|
+
base_url = config.get("gitlab_url", "https://gitlab.ebtech-solution.com")
|
|
23
|
+
|
|
24
|
+
api_url = f"{base_url}/api/v4/projects"
|
|
25
|
+
params = {"search": module_name, "simple": "true", "per_page": 100, "access_token": get_access_token()}
|
|
26
|
+
headers = {"Accept": "application/json"}
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
typer.secho(f"đ Searching GitLab for module: {module_name}", fg="cyan")
|
|
30
|
+
response = requests.get(api_url, headers=headers, params=params)
|
|
31
|
+
response.raise_for_status()
|
|
32
|
+
|
|
33
|
+
for project in response.json():
|
|
34
|
+
if project["name"] == module_name or project["path"] == module_name:
|
|
35
|
+
typer.secho(f"â
Found module '{module_name}'", fg="green")
|
|
36
|
+
return project["http_url_to_repo"]
|
|
37
|
+
|
|
38
|
+
typer.secho(f"â Module '{module_name}' not found in GitLab.", fg="yellow")
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
except requests.RequestException as e:
|
|
42
|
+
typer.secho(f"â GitLab API error: {e}", fg="red")
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def inject_token_into_url(url: str, token: str) -> str:
|
|
47
|
+
parsed = urlparse(url)
|
|
48
|
+
if not parsed.netloc:
|
|
49
|
+
raise ValueError(f"Invalid URL: {url}")
|
|
50
|
+
netloc = f"oauth2:{token}@{parsed.netloc}"
|
|
51
|
+
return urlunparse(parsed._replace(netloc=netloc, scheme="https"))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def extract_module_name_from_url(url: str) -> str:
|
|
55
|
+
return url.rstrip("/").split("/")[-1].removesuffix(".git")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def safe_eval_manifest(content: str) -> dict:
|
|
59
|
+
try:
|
|
60
|
+
return ast.literal_eval(content)
|
|
61
|
+
except Exception as e:
|
|
62
|
+
typer.secho(f"â Failed to evaluate manifest: {e}", fg="red")
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def clone_repo(url: str, target_dir: Path, branch: str = "main") -> bool:
|
|
67
|
+
if target_dir.exists():
|
|
68
|
+
typer.secho(f"â ī¸ Skipping '{target_dir.name}': already exists.", fg="yellow")
|
|
69
|
+
return True
|
|
70
|
+
try:
|
|
71
|
+
access_token = get_access_token()
|
|
72
|
+
url_with_token = inject_token_into_url(url, access_token)
|
|
73
|
+
typer.secho(f"đĨ Cloning into '{target_dir}'...", fg="cyan")
|
|
74
|
+
Repo.clone_from(url_with_token, target_dir, branch=branch)
|
|
75
|
+
typer.secho(f"â
Successfully cloned '{url}'", fg="green")
|
|
76
|
+
return True
|
|
77
|
+
except GitCommandError as e:
|
|
78
|
+
typer.secho(f"â Git error: Failed to clone '{url}'\n{e}", fg="red")
|
|
79
|
+
return False
|
|
80
|
+
except Exception as e:
|
|
81
|
+
typer.secho(f"â Unexpected error: {e}", fg="red")
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def clone_module_command(
|
|
86
|
+
url: str = typer.Option(..., "--url", help="Full HTTP URL of the module repo."),
|
|
87
|
+
branch: Optional[str] = None,
|
|
88
|
+
depth: int = typer.Option(1, "--depth", "-d", help="Max dependency depth to clone. 1 clones only the target module, 2 clones target + immediate dependencies, etc."),
|
|
89
|
+
):
|
|
90
|
+
"""
|
|
91
|
+
Clone a module and its dependencies into the current directory.
|
|
92
|
+
"""
|
|
93
|
+
typer.secho("đ Starting module cloning process...", fg="cyan", bold=True)
|
|
94
|
+
core_modules = get_core_modules_from_config()
|
|
95
|
+
visited = set()
|
|
96
|
+
fail_count = 0
|
|
97
|
+
lock = threading.Lock()
|
|
98
|
+
|
|
99
|
+
def clone_recursive(module_url: str, current_branch: Optional[str], current_depth: int):
|
|
100
|
+
nonlocal fail_count
|
|
101
|
+
module_name = extract_module_name_from_url(module_url)
|
|
102
|
+
|
|
103
|
+
with lock:
|
|
104
|
+
if module_name in visited:
|
|
105
|
+
typer.secho(f"đ Already processed '{module_name}', skipping.", fg="yellow")
|
|
106
|
+
return False
|
|
107
|
+
visited.add(module_name)
|
|
108
|
+
|
|
109
|
+
target_path = Path.cwd() / module_name
|
|
110
|
+
|
|
111
|
+
if not clone_repo(module_url, target_path, current_branch or "main"):
|
|
112
|
+
typer.secho(f"â Failed to clone '{module_name}'. Skipping its dependencies.", fg="red")
|
|
113
|
+
with lock:
|
|
114
|
+
fail_count += 1
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
if current_depth <= 0:
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
manifest_path = target_path / "__manifest__.py"
|
|
121
|
+
if not manifest_path.exists():
|
|
122
|
+
typer.secho(f"đĻ No manifest found in '{module_name}', skipping dependencies.", fg="yellow")
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
manifest_data = safe_eval_manifest(manifest_path.read_text())
|
|
126
|
+
dependencies = manifest_data.get("depends", [])
|
|
127
|
+
|
|
128
|
+
if not dependencies:
|
|
129
|
+
typer.secho(f"âšī¸ No dependencies for '{module_name}'.", fg="blue")
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
candidate_deps = [dep for dep in dependencies if dep not in core_modules]
|
|
133
|
+
if not candidate_deps:
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
next_depth = current_depth - 1
|
|
137
|
+
|
|
138
|
+
def _resolve_and_run(dep_name: str):
|
|
139
|
+
dep_url = get_project_url_from_gitlab(module_name=dep_name)
|
|
140
|
+
if not dep_url:
|
|
141
|
+
typer.secho(f"â Could not resolve dependency: '{dep_name}'", fg="red")
|
|
142
|
+
nonlocal_assign = True
|
|
143
|
+
with lock:
|
|
144
|
+
fail_count += 1
|
|
145
|
+
return
|
|
146
|
+
clone_recursive(dep_url, current_branch, next_depth)
|
|
147
|
+
|
|
148
|
+
with ThreadPoolExecutor(max_workers=4) as executor:
|
|
149
|
+
futures = [executor.submit(_resolve_and_run, dep) for dep in candidate_deps]
|
|
150
|
+
for f in futures:
|
|
151
|
+
f.result()
|
|
152
|
+
|
|
153
|
+
return True
|
|
154
|
+
|
|
155
|
+
clone_recursive(url, branch, depth)
|
|
156
|
+
|
|
157
|
+
if fail_count > 0:
|
|
158
|
+
typer.secho(f"â ī¸ Finished with {fail_count} failed clones.", fg="yellow", bold=True)
|
|
159
|
+
else:
|
|
160
|
+
typer.secho("â
All done without errors!", fg="green", bold=True)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from rich import print
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from odooflow import config_manager
|
|
6
|
+
|
|
7
|
+
def config(
|
|
8
|
+
env_file: str = typer.Option(None),
|
|
9
|
+
manifest_file: str = typer.Option(None),
|
|
10
|
+
access_token: str = typer.Option(None),
|
|
11
|
+
add_core_module: Optional[str] = typer.Option(None),
|
|
12
|
+
sync_keys: Optional[str] = typer.Option(None),
|
|
13
|
+
show: bool = typer.Option(False, "--show", help="Display current configuration")
|
|
14
|
+
):
|
|
15
|
+
current_config = config_manager.load_config()
|
|
16
|
+
updated = False
|
|
17
|
+
|
|
18
|
+
if show:
|
|
19
|
+
typer.secho("đĻ Current Configuration:", fg="cyan", bold=True)
|
|
20
|
+
print(current_config)
|
|
21
|
+
raise typer.Exit()
|
|
22
|
+
|
|
23
|
+
if env_file:
|
|
24
|
+
current_config["env_file"] = env_file
|
|
25
|
+
updated = True
|
|
26
|
+
|
|
27
|
+
if manifest_file:
|
|
28
|
+
current_config["manifest_file"] = manifest_file
|
|
29
|
+
updated = True
|
|
30
|
+
|
|
31
|
+
if access_token:
|
|
32
|
+
current_config["access_token"] = access_token
|
|
33
|
+
updated = True
|
|
34
|
+
|
|
35
|
+
if add_core_module:
|
|
36
|
+
modules = [m.strip() for m in add_core_module.split(",") if m.strip()]
|
|
37
|
+
if modules:
|
|
38
|
+
existing_modules = set(current_config.get(
|
|
39
|
+
"core_modules",
|
|
40
|
+
config_manager.DEFAULT_CONFIG.get("core_modules", [])
|
|
41
|
+
))
|
|
42
|
+
new_modules = set(modules)
|
|
43
|
+
combined_modules = sorted(existing_modules.union(new_modules))
|
|
44
|
+
current_config["core_modules"] = combined_modules
|
|
45
|
+
updated = True
|
|
46
|
+
typer.secho(f"â
Added core module(s): {', '.join(new_modules)}", fg="green")
|
|
47
|
+
|
|
48
|
+
if sync_keys:
|
|
49
|
+
new_keys = [k.strip() for k in sync_keys.split(",") if k.strip()]
|
|
50
|
+
existing_keys = set(current_config.get(
|
|
51
|
+
"sync_keys",
|
|
52
|
+
config_manager.DEFAULT_CONFIG['sync_keys']
|
|
53
|
+
))
|
|
54
|
+
combined_keys = sorted(existing_keys.union(new_keys))
|
|
55
|
+
current_config["sync_keys"] = combined_keys
|
|
56
|
+
updated = True
|
|
57
|
+
typer.secho(f"đ Updated sync keys: {', '.join(new_keys)}", fg="cyan")
|
|
58
|
+
|
|
59
|
+
if updated:
|
|
60
|
+
config_manager.save_config(current_config)
|
|
61
|
+
typer.secho("đž Configuration updated successfully.", fg="green")
|
|
62
|
+
else:
|
|
63
|
+
typer.secho("â ī¸ No changes provided. Use --help to see available options.", fg="yellow")
|