repopy 0.1.0__tar.gz → 0.2.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.
- repopy-0.2.0/PKG-INFO +176 -0
- repopy-0.2.0/README.md +151 -0
- repopy-0.2.0/pyproject.toml +37 -0
- repopy-0.2.0/src/repopy/__main__.py +45 -0
- repopy-0.2.0/src/repopy/cli.py +99 -0
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy/file_system.py +52 -32
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy/git_engine.py +39 -1
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy/orchestrators.py +63 -29
- repopy-0.2.0/src/repopy/prompts.py +109 -0
- repopy-0.2.0/src/repopy/templates.py +47 -0
- repopy-0.2.0/src/repopy.egg-info/PKG-INFO +176 -0
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy.egg-info/SOURCES.txt +5 -1
- repopy-0.2.0/tests/test_prompts.py +45 -0
- repopy-0.2.0/tests/test_validators.py +41 -0
- repopy-0.1.0/PKG-INFO +0 -108
- repopy-0.1.0/README.md +0 -95
- repopy-0.1.0/pyproject.toml +0 -19
- repopy-0.1.0/src/repopy/__main__.py +0 -27
- repopy-0.1.0/src/repopy/cli.py +0 -19
- repopy-0.1.0/src/repopy.egg-info/PKG-INFO +0 -108
- {repopy-0.1.0 → repopy-0.2.0}/LICENSE +0 -0
- {repopy-0.1.0 → repopy-0.2.0}/setup.cfg +0 -0
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy/__init__.py +0 -0
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy/dependencies.py +0 -0
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy/os_detector.py +0 -0
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy/validators.py +0 -0
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy.egg-info/dependency_links.txt +0 -0
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy.egg-info/entry_points.txt +0 -0
- {repopy-0.1.0 → repopy-0.2.0}/src/repopy.egg-info/top_level.txt +0 -0
repopy-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: repopy
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A modular CLI workspace initializer and cloning automation tool.
|
|
5
|
+
Author-email: Janitha Manatunga <janithamanatunga@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/manatunga/repopy
|
|
8
|
+
Project-URL: Repository, https://github.com/manatunga/repopy
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/manatunga/repopy/issues
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# repopy
|
|
27
|
+
|
|
28
|
+
A modular, cross-platform Python CLI workspace manager and developer tooling suite designed to automate reproducible local scaffolding, virtual environment creation, and remote repository synchronization.
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
[](https://github.com/manatunga/repopy/actions/workflows/tests.yml)
|
|
32
|
+
[](https://pypi.org)
|
|
33
|
+
[](https://opensource.org/license/mit)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 🌟 Key Features
|
|
38
|
+
|
|
39
|
+
- **Lifecycle Project Scaffolding (`repopy init`)**: Replaces manual setup by provisioning clean directory structures, virtual environments, automated `.gitignore` presets, and standardized metadata configurations (`pyproject.toml` or `requirements.txt`).
|
|
40
|
+
|
|
41
|
+
- **Remote Cloning (`repopy clone`)**: Clones remote Git repositories into structured local footprints and auto-provisions dedicated virtual environments and dependencies.
|
|
42
|
+
|
|
43
|
+
- **Upstream Linking (`repopy link`)**: Instantly connects an unlinked local workspace to a remote Git upstream, staging files, executing initial commits, and pushing to the default branch in one step.
|
|
44
|
+
|
|
45
|
+
- **Architectural Themes**: Supports presets (`minimal`, `web_api`, `cli_package`, `data_science`) tailored to the specific packaging needs of the application.
|
|
46
|
+
|
|
47
|
+
- **Transactional Cleanups**: Employs defensive rollback mechanisms (`shutil.rmtree`) to purge incomplete workspaces if initialization fails midway.
|
|
48
|
+
|
|
49
|
+
- **Automated Test Suite & CI**: Fully covered by local `pytest` test suites and verified via continuous integration on GitHub Actions.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 🛠️ Architecture & Separation of Concerns
|
|
54
|
+
|
|
55
|
+
`repopy` is organized into single-responsibility layers:
|
|
56
|
+
- **Presentation & Routing (`cli.py`, `prompts.py`, `__main__.py`)**: Evaluates native terminal arguments via `argparse`, orchestrates interactive prompt flows, and handles OS interrupts gracefully.
|
|
57
|
+
|
|
58
|
+
- **Supervision (`orchestrators.py`)**: Broker layer executing lifecycle validation checks, branch sequencing, and command handoffs.
|
|
59
|
+
|
|
60
|
+
- **Low-Level Engines (`file_system.py`, `git_engine.py`)**: Decoupled engines handling transactional directory allocations, venv isolation, and direct subprocess execution.
|
|
61
|
+
|
|
62
|
+
- **Validation & Translation (`validators.py`, `dependencies.py`, `os_detector.py`)**: Sanitizes paths, ensures semantic regex compliance, verifies system binaries (such as `git`), and resolves cross-platform paths.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 📥 Installation
|
|
67
|
+
|
|
68
|
+
Install the latest release from PyPI:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install repopy
|
|
72
|
+
```
|
|
73
|
+
Alternatively, you can install the latest development version directly from the source:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/manatunga/repopy.git
|
|
77
|
+
cd repopy
|
|
78
|
+
pip install -e .
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 🚀 Usage Guide
|
|
84
|
+
|
|
85
|
+
### 1. Initialize a Project (`repopy init`)
|
|
86
|
+
Generate a new workspace interactively or via CLI flags:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Interactive setup
|
|
90
|
+
repopy init my-app
|
|
91
|
+
|
|
92
|
+
# Non-interactive generation with a specific theme
|
|
93
|
+
repopy init my-api -t web_api -s
|
|
94
|
+
|
|
95
|
+
# Initialize and immediately connect to a remote repository
|
|
96
|
+
repopy init my-cli -t cli_package --link <git-url>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Available Themes:
|
|
100
|
+
- `minimal`: Bare directory setup with `requirements.txt`.
|
|
101
|
+
- `web_api`: Layered service layout prepared for modern frameworks.
|
|
102
|
+
- `cli_package`: Modular structure configured with PEP 621 compliant `pyproject.toml`.
|
|
103
|
+
- `data_science`: Notebooks, data folders, and pipeline layouts.
|
|
104
|
+
<br>
|
|
105
|
+
<br>
|
|
106
|
+
|
|
107
|
+
### 2. Clone a Remote Project (`repopy clone`)
|
|
108
|
+
Fetch a remote repository and immediately auto-provision an isolated `.venv`:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
repopy clone <git-url> -n [custom-project-name]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
<br>
|
|
115
|
+
|
|
116
|
+
### 3. Link an Existing Workspace (`repopy link`)
|
|
117
|
+
Run inside an existing workspace to push tracking history to an upstream origin:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
repopy link <git-url> -m ["custom-initial-commit-message"]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 🧪 Testing & Continuous Integration
|
|
126
|
+
|
|
127
|
+
Run the test suite using `pytest`:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
pytest -v
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
All contributions and pull requests are validated through our GitHub Actions workflow matrix (`.github/workflows/tests.yml`), running integration and validation tests across supported Python versions.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 🗂️ Project Structure
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
repopy/
|
|
141
|
+
├── .github/
|
|
142
|
+
│ └── workflows/
|
|
143
|
+
│ └── tests.yml # CI test runner pipeline
|
|
144
|
+
├── src/
|
|
145
|
+
│ └── repopy/
|
|
146
|
+
│ ├── __init__.py # Package initialization
|
|
147
|
+
│ ├── __main__.py # Application execution entry
|
|
148
|
+
│ ├── cli.py # CLI subparser configuration
|
|
149
|
+
│ ├── dependencies.py # Binary prerequisite verification
|
|
150
|
+
│ ├── file_system.py # Atomic workspace & venv operations
|
|
151
|
+
│ ├── git_engine.py # Subprocess Git execution engine
|
|
152
|
+
│ ├── orchestrators.py # High-level pipeline management
|
|
153
|
+
│ ├── os_detector.py # Cross-platform environment resolver
|
|
154
|
+
│ ├── prompts.py # Dynamic terminal questionnaires
|
|
155
|
+
│ ├── templates.py # Static asset definitions & manifests
|
|
156
|
+
│ └── validators.py # Input sanitation & regex filters
|
|
157
|
+
├── tests/
|
|
158
|
+
│ ├── test_prompts.py # Input loop & default manifest tests
|
|
159
|
+
│ └── test_validators.py # Path and schema validation tests
|
|
160
|
+
├── pyproject.toml # PEP 621 packaging configuration
|
|
161
|
+
├── LICENSE # MIT License
|
|
162
|
+
└── README.md
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 📄 License
|
|
168
|
+
|
|
169
|
+
Distributed under the MIT License. See `LICENSE` for details.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Key Review Notes
|
|
174
|
+
|
|
175
|
+
* Verify the GitHub Actions workflow file path (`.github/workflows/tests.yml`) matches your actual workflow file name so the build badge renders correctly.
|
|
176
|
+
* In the `repopy init` theme section, verify that the theme descriptions match what you have mapped out in `templates.py`.
|
repopy-0.2.0/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# repopy
|
|
2
|
+
|
|
3
|
+
A modular, cross-platform Python CLI workspace manager and developer tooling suite designed to automate reproducible local scaffolding, virtual environment creation, and remote repository synchronization.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
[](https://github.com/manatunga/repopy/actions/workflows/tests.yml)
|
|
7
|
+
[](https://pypi.org)
|
|
8
|
+
[](https://opensource.org/license/mit)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🌟 Key Features
|
|
13
|
+
|
|
14
|
+
- **Lifecycle Project Scaffolding (`repopy init`)**: Replaces manual setup by provisioning clean directory structures, virtual environments, automated `.gitignore` presets, and standardized metadata configurations (`pyproject.toml` or `requirements.txt`).
|
|
15
|
+
|
|
16
|
+
- **Remote Cloning (`repopy clone`)**: Clones remote Git repositories into structured local footprints and auto-provisions dedicated virtual environments and dependencies.
|
|
17
|
+
|
|
18
|
+
- **Upstream Linking (`repopy link`)**: Instantly connects an unlinked local workspace to a remote Git upstream, staging files, executing initial commits, and pushing to the default branch in one step.
|
|
19
|
+
|
|
20
|
+
- **Architectural Themes**: Supports presets (`minimal`, `web_api`, `cli_package`, `data_science`) tailored to the specific packaging needs of the application.
|
|
21
|
+
|
|
22
|
+
- **Transactional Cleanups**: Employs defensive rollback mechanisms (`shutil.rmtree`) to purge incomplete workspaces if initialization fails midway.
|
|
23
|
+
|
|
24
|
+
- **Automated Test Suite & CI**: Fully covered by local `pytest` test suites and verified via continuous integration on GitHub Actions.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 🛠️ Architecture & Separation of Concerns
|
|
29
|
+
|
|
30
|
+
`repopy` is organized into single-responsibility layers:
|
|
31
|
+
- **Presentation & Routing (`cli.py`, `prompts.py`, `__main__.py`)**: Evaluates native terminal arguments via `argparse`, orchestrates interactive prompt flows, and handles OS interrupts gracefully.
|
|
32
|
+
|
|
33
|
+
- **Supervision (`orchestrators.py`)**: Broker layer executing lifecycle validation checks, branch sequencing, and command handoffs.
|
|
34
|
+
|
|
35
|
+
- **Low-Level Engines (`file_system.py`, `git_engine.py`)**: Decoupled engines handling transactional directory allocations, venv isolation, and direct subprocess execution.
|
|
36
|
+
|
|
37
|
+
- **Validation & Translation (`validators.py`, `dependencies.py`, `os_detector.py`)**: Sanitizes paths, ensures semantic regex compliance, verifies system binaries (such as `git`), and resolves cross-platform paths.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 📥 Installation
|
|
42
|
+
|
|
43
|
+
Install the latest release from PyPI:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install repopy
|
|
47
|
+
```
|
|
48
|
+
Alternatively, you can install the latest development version directly from the source:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/manatunga/repopy.git
|
|
52
|
+
cd repopy
|
|
53
|
+
pip install -e .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 🚀 Usage Guide
|
|
59
|
+
|
|
60
|
+
### 1. Initialize a Project (`repopy init`)
|
|
61
|
+
Generate a new workspace interactively or via CLI flags:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Interactive setup
|
|
65
|
+
repopy init my-app
|
|
66
|
+
|
|
67
|
+
# Non-interactive generation with a specific theme
|
|
68
|
+
repopy init my-api -t web_api -s
|
|
69
|
+
|
|
70
|
+
# Initialize and immediately connect to a remote repository
|
|
71
|
+
repopy init my-cli -t cli_package --link <git-url>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Available Themes:
|
|
75
|
+
- `minimal`: Bare directory setup with `requirements.txt`.
|
|
76
|
+
- `web_api`: Layered service layout prepared for modern frameworks.
|
|
77
|
+
- `cli_package`: Modular structure configured with PEP 621 compliant `pyproject.toml`.
|
|
78
|
+
- `data_science`: Notebooks, data folders, and pipeline layouts.
|
|
79
|
+
<br>
|
|
80
|
+
<br>
|
|
81
|
+
|
|
82
|
+
### 2. Clone a Remote Project (`repopy clone`)
|
|
83
|
+
Fetch a remote repository and immediately auto-provision an isolated `.venv`:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
repopy clone <git-url> -n [custom-project-name]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
<br>
|
|
90
|
+
|
|
91
|
+
### 3. Link an Existing Workspace (`repopy link`)
|
|
92
|
+
Run inside an existing workspace to push tracking history to an upstream origin:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
repopy link <git-url> -m ["custom-initial-commit-message"]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 🧪 Testing & Continuous Integration
|
|
101
|
+
|
|
102
|
+
Run the test suite using `pytest`:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pytest -v
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
All contributions and pull requests are validated through our GitHub Actions workflow matrix (`.github/workflows/tests.yml`), running integration and validation tests across supported Python versions.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🗂️ Project Structure
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
repopy/
|
|
116
|
+
├── .github/
|
|
117
|
+
│ └── workflows/
|
|
118
|
+
│ └── tests.yml # CI test runner pipeline
|
|
119
|
+
├── src/
|
|
120
|
+
│ └── repopy/
|
|
121
|
+
│ ├── __init__.py # Package initialization
|
|
122
|
+
│ ├── __main__.py # Application execution entry
|
|
123
|
+
│ ├── cli.py # CLI subparser configuration
|
|
124
|
+
│ ├── dependencies.py # Binary prerequisite verification
|
|
125
|
+
│ ├── file_system.py # Atomic workspace & venv operations
|
|
126
|
+
│ ├── git_engine.py # Subprocess Git execution engine
|
|
127
|
+
│ ├── orchestrators.py # High-level pipeline management
|
|
128
|
+
│ ├── os_detector.py # Cross-platform environment resolver
|
|
129
|
+
│ ├── prompts.py # Dynamic terminal questionnaires
|
|
130
|
+
│ ├── templates.py # Static asset definitions & manifests
|
|
131
|
+
│ └── validators.py # Input sanitation & regex filters
|
|
132
|
+
├── tests/
|
|
133
|
+
│ ├── test_prompts.py # Input loop & default manifest tests
|
|
134
|
+
│ └── test_validators.py # Path and schema validation tests
|
|
135
|
+
├── pyproject.toml # PEP 621 packaging configuration
|
|
136
|
+
├── LICENSE # MIT License
|
|
137
|
+
└── README.md
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 📄 License
|
|
143
|
+
|
|
144
|
+
Distributed under the MIT License. See `LICENSE` for details.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Key Review Notes
|
|
149
|
+
|
|
150
|
+
* Verify the GitHub Actions workflow file path (`.github/workflows/tests.yml`) matches your actual workflow file name so the build badge renders correctly.
|
|
151
|
+
* In the `repopy init` theme section, verify that the theme descriptions match what you have mapped out in `templates.py`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "repopy"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "A modular CLI workspace initializer and cloning automation tool."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
|
|
13
|
+
authors = [
|
|
14
|
+
{name = "Janitha Manatunga", email = "janithamanatunga@gmail.com"}
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
"License :: OSI Approved :: MIT License",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/manatunga/repopy"
|
|
33
|
+
Repository = "https://github.com/manatunga/repopy"
|
|
34
|
+
"Bug Tracker" = "https://github.com/manatunga/repopy/issues"
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
repopy = "repopy.__main__:main"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Main entrypoint of repopy. Brings together the argument parser and
|
|
3
|
+
the orchestrators together under a main function.
|
|
4
|
+
'''
|
|
5
|
+
import sys
|
|
6
|
+
from repopy.cli import parse_arguments
|
|
7
|
+
from repopy.prompts import capture_project_manifests
|
|
8
|
+
from repopy.orchestrators import LocalInitializer, CloneInitializer, LinkInitializer
|
|
9
|
+
|
|
10
|
+
def main() -> None:
|
|
11
|
+
try:
|
|
12
|
+
args = parse_arguments()
|
|
13
|
+
|
|
14
|
+
if args.command == 'init':
|
|
15
|
+
print('Working on it...\n')
|
|
16
|
+
manifest = capture_project_manifests(args)
|
|
17
|
+
initializer = LocalInitializer(manifest)
|
|
18
|
+
success = initializer.run()
|
|
19
|
+
|
|
20
|
+
if success and args.link:
|
|
21
|
+
print('Initiating automated repository link shortcut...')
|
|
22
|
+
link_initializer = LinkInitializer(args.link, args.message)
|
|
23
|
+
link_initializer.run()
|
|
24
|
+
|
|
25
|
+
elif args.command == 'clone':
|
|
26
|
+
print('Working on it...\n')
|
|
27
|
+
initializer = CloneInitializer(args.repo_url, args.name)
|
|
28
|
+
initializer.run()
|
|
29
|
+
|
|
30
|
+
elif args.command == 'link':
|
|
31
|
+
print('Working on it...\n')
|
|
32
|
+
initializer = LinkInitializer(args.repo_url, args.message)
|
|
33
|
+
initializer.run()
|
|
34
|
+
|
|
35
|
+
else:
|
|
36
|
+
print('Usage: repopy [local | clone] --help')
|
|
37
|
+
print('Error: Please specify a subcommand')
|
|
38
|
+
|
|
39
|
+
except KeyboardInterrupt:
|
|
40
|
+
print('\n Workspace operation cancelled by user.')
|
|
41
|
+
sys.exit(1)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == '__main__':
|
|
45
|
+
main()
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Parser layer. Imports Python's built-in argparse module
|
|
3
|
+
to identify user intent and any extra information passed.
|
|
4
|
+
'''
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
|
|
8
|
+
def parse_arguments() -> argparse.Namespace:
|
|
9
|
+
parser = argparse.ArgumentParser(
|
|
10
|
+
description='repopy: A modular project scaffolder and workflow manager for Python.'
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
subparsers = parser.add_subparsers(dest='command', required=True)
|
|
14
|
+
|
|
15
|
+
#---------------------------------------------------------------------------------
|
|
16
|
+
# `repopy init` subcommand
|
|
17
|
+
#---------------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
init_parser = subparsers.add_parser(
|
|
20
|
+
'init',
|
|
21
|
+
help='Initializes a standardized Python development workspace in a new directory'
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Optional argument to add a project_name
|
|
25
|
+
init_parser.add_argument(
|
|
26
|
+
'project_name',
|
|
27
|
+
nargs='?',
|
|
28
|
+
help='Name of the target project folder to create'
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Project theme shortcut flag (-t / --theme)
|
|
32
|
+
init_parser.add_argument(
|
|
33
|
+
'-t', '--theme',
|
|
34
|
+
choices=['minimal', 'web_api', 'cli_package', 'data_science'],
|
|
35
|
+
help='Pre-select an architectural theme layout'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Skip flag (-s / --skip) to bypass input prompts completely
|
|
39
|
+
init_parser.add_argument(
|
|
40
|
+
'-s', '--skip',
|
|
41
|
+
action='store_true',
|
|
42
|
+
help='Skip interactive loops and generate workspace using default values'
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Link flag (-l / --link) to pass an existing remote URL during creation
|
|
46
|
+
init_parser.add_argument(
|
|
47
|
+
'-l', '--link',
|
|
48
|
+
metavar='GIT_URL',
|
|
49
|
+
help='Automatically link and push the new local repo to this remote Git URL'
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Custom commit message shortcut allowed directly during init orchestration
|
|
53
|
+
init_parser.add_argument(
|
|
54
|
+
'-m', '--message',
|
|
55
|
+
help='Custom commit message to use for the initial repository push'
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
#---------------------------------------------------------------------------------
|
|
59
|
+
# `repopy clone` subcommand
|
|
60
|
+
#---------------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
clone_parser = subparsers.add_parser(
|
|
63
|
+
'clone',
|
|
64
|
+
help='Clones a remote Git repository to the local machine'
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Positional argument to add remote Git URL
|
|
68
|
+
clone_parser.add_argument(
|
|
69
|
+
'repo_url',
|
|
70
|
+
help='Repository URL from Github to clone locally'
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Name flag (-n / --name) to pass an optional name for the to-be-cloned repository
|
|
74
|
+
clone_parser.add_argument(
|
|
75
|
+
'-n', '--name',
|
|
76
|
+
help='Name of the project folder'
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
#---------------------------------------------------------------------------------
|
|
80
|
+
# `repopy link` subcommand
|
|
81
|
+
#---------------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
link_parser = subparsers.add_parser(
|
|
84
|
+
'link',
|
|
85
|
+
help='Stage, commit and link current local repository to a remote Git URL'
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Positional argument to add a dedicated existing remote Git URL for the local repository
|
|
89
|
+
link_parser.add_argument(
|
|
90
|
+
'repo_url',
|
|
91
|
+
help='Target remote Git repository URL (e.g., git@github.com:user/repo.git)'
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
link_parser.add_argument(
|
|
95
|
+
'-m', '--message',
|
|
96
|
+
help='Custom commit message to use instead of the default repopy message'
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return parser.parse_args()
|
|
@@ -10,30 +10,12 @@ import logging
|
|
|
10
10
|
import subprocess
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
|
+
from repopy.templates import GITIGNORE_TEMPLATE, generate_pyproject_toml
|
|
14
|
+
|
|
13
15
|
logger = logging.getLogger(__name__)
|
|
14
16
|
|
|
15
17
|
class FileSystemEngine:
|
|
16
18
|
|
|
17
|
-
GITIGNORE_TEMPLATE = '''# Compiled Python files
|
|
18
|
-
__pycache__/
|
|
19
|
-
*.pyc
|
|
20
|
-
|
|
21
|
-
# Virtual environments
|
|
22
|
-
# .venv/
|
|
23
|
-
venv/
|
|
24
|
-
env/
|
|
25
|
-
ENV/
|
|
26
|
-
|
|
27
|
-
# IDEs and Editors
|
|
28
|
-
.vscode/
|
|
29
|
-
.idea/
|
|
30
|
-
*.swp
|
|
31
|
-
*.swo
|
|
32
|
-
|
|
33
|
-
# OS files
|
|
34
|
-
.DS_Store
|
|
35
|
-
Thumbs_db'''
|
|
36
|
-
|
|
37
19
|
def __init__(self, project_path: Path):
|
|
38
20
|
self.project_path = project_path
|
|
39
21
|
|
|
@@ -49,17 +31,54 @@ Thumbs_db'''
|
|
|
49
31
|
return False
|
|
50
32
|
|
|
51
33
|
|
|
52
|
-
def
|
|
53
|
-
'''
|
|
54
|
-
|
|
34
|
+
def create_theme_directories(self, theme: str, project_name: str):
|
|
35
|
+
'''Maps and structures the child directory tree based on the project theme'''
|
|
36
|
+
if theme == 'web_api':
|
|
37
|
+
sub_dirs = ['src/app', 'src/app/routes', 'src/app/models']
|
|
38
|
+
|
|
39
|
+
elif theme == 'cli_package':
|
|
40
|
+
sub_dirs = [f'src/{project_name}', 'tests']
|
|
41
|
+
|
|
42
|
+
elif theme == 'data_science':
|
|
43
|
+
sub_dirs = ['data/raw', 'data/processed', 'notebooks', 'src/pipeline']
|
|
44
|
+
|
|
45
|
+
else:
|
|
46
|
+
return True
|
|
55
47
|
|
|
56
48
|
try:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
for folder in sub_dirs:
|
|
50
|
+
target_folder_path = (self.project_path / folder).resolve()
|
|
51
|
+
target_folder_path.mkdir(parents=True, exist_ok=True)
|
|
52
|
+
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
except OSError as e:
|
|
56
|
+
logger.error(f'Failed to build nested structures for theme {theme}: {e}')
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def write_theme_configurations(self, theme: str, manifest: dict) -> bool:
|
|
61
|
+
'''Deploys standard configuration templates (gitignore, requirements.txt or toml)'''
|
|
62
|
+
try:
|
|
63
|
+
gitignore_path = self.project_path / '.gitignore'
|
|
64
|
+
with open(gitignore_path, 'w') as f:
|
|
65
|
+
f.write(GITIGNORE_TEMPLATE)
|
|
66
|
+
|
|
67
|
+
if theme == 'minimal':
|
|
68
|
+
req_path = self.project_path / 'requirements.txt'
|
|
69
|
+
with open(req_path, 'w') as f:
|
|
70
|
+
f.write('')
|
|
71
|
+
|
|
72
|
+
else:
|
|
73
|
+
toml_content = generate_pyproject_toml(manifest)
|
|
74
|
+
toml_path = self.project_path / 'pyproject.toml'
|
|
75
|
+
with open(toml_path, 'w') as f:
|
|
76
|
+
f.write(toml_content)
|
|
77
|
+
|
|
78
|
+
return True
|
|
79
|
+
|
|
61
80
|
except OSError as e:
|
|
62
|
-
logger.error(f'Failed to
|
|
81
|
+
logger.error(f'Failed to deploy structural file sheets: {e}')
|
|
63
82
|
return False
|
|
64
83
|
|
|
65
84
|
|
|
@@ -96,13 +115,14 @@ Thumbs_db'''
|
|
|
96
115
|
logger.error(f'Failed to clean up directory at {self.project_path}: {e}')
|
|
97
116
|
|
|
98
117
|
|
|
99
|
-
def build_workspace(self) -> bool:
|
|
118
|
+
def build_workspace(self, theme: str, manifest: dict) -> bool:
|
|
100
119
|
'''Orchestrate entire file-system creation sequence'''
|
|
101
120
|
|
|
102
121
|
if self.create_root_directory():
|
|
103
122
|
if self.initialize_git():
|
|
104
|
-
if self.
|
|
105
|
-
if self.
|
|
106
|
-
|
|
123
|
+
if self.create_theme_directories(theme, manifest['project_name']):
|
|
124
|
+
if self.write_theme_configurations(theme, manifest):
|
|
125
|
+
if self.create_virtual_environment():
|
|
126
|
+
return True
|
|
107
127
|
|
|
108
128
|
return False
|
|
@@ -61,4 +61,42 @@ class GitEngine:
|
|
|
61
61
|
logger.info(f'Successfully cleaned up half-baked workspace at {self.project_path}')
|
|
62
62
|
except OSError as e:
|
|
63
63
|
logger.error(f'Failed to clean up directory at {self.project_path}: {e}')
|
|
64
|
-
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class GitLinkEngine:
|
|
67
|
+
|
|
68
|
+
def __init__(self, repo_url: str, message: str):
|
|
69
|
+
self.repo_url = repo_url
|
|
70
|
+
self.message = message
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def link_and_push(self) -> bool:
|
|
74
|
+
'''Runs the sequence of Git terminal execution blocks to link the repo'''
|
|
75
|
+
try:
|
|
76
|
+
stage_result = subprocess.run(['git', 'add', '.'], capture_output=True, text=True)
|
|
77
|
+
if stage_result.returncode != 0:
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
commit_result = subprocess.run(['git', 'commit', '-m', self.message], capture_output=True, text=True)
|
|
81
|
+
if commit_result.returncode != 0:
|
|
82
|
+
if 'nothing to commit' not in commit_result.stdout:
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
branch_result = subprocess.run(['git', 'branch', '-M', 'main'], capture_output=True, text=True)
|
|
86
|
+
if branch_result.returncode != 0:
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
remote_result = subprocess.run(['git', 'remote', 'add', 'origin', self.repo_url], capture_output=True, text=True)
|
|
90
|
+
if remote_result.returncode != 0:
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
push_result = subprocess.run(['git', 'push', '-u', 'origin', 'main'], text=True)
|
|
94
|
+
if push_result.returncode != 0:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
except OSError as e:
|
|
100
|
+
logger.error(f'Failed to execute Git linking operationsL {e}')
|
|
101
|
+
return False
|
|
102
|
+
|