repopy 0.2.0__tar.gz → 0.3.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.
Files changed (29) hide show
  1. {repopy-0.2.0/src/repopy.egg-info → repopy-0.3.0}/PKG-INFO +33 -23
  2. {repopy-0.2.0 → repopy-0.3.0}/README.md +32 -22
  3. {repopy-0.2.0 → repopy-0.3.0}/pyproject.toml +1 -1
  4. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/__main__.py +10 -8
  5. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/cli.py +16 -0
  6. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/file_system.py +58 -0
  7. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/git_engine.py +2 -34
  8. repopy-0.3.0/src/repopy/orchestrators.py +149 -0
  9. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/prompts.py +23 -1
  10. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/templates.py +14 -14
  11. {repopy-0.2.0 → repopy-0.3.0/src/repopy.egg-info}/PKG-INFO +33 -23
  12. {repopy-0.2.0 → repopy-0.3.0}/src/repopy.egg-info/SOURCES.txt +4 -0
  13. repopy-0.3.0/tests/test_cli.py +46 -0
  14. repopy-0.3.0/tests/test_file_system.py +27 -0
  15. repopy-0.3.0/tests/test_git_engine.py +106 -0
  16. repopy-0.3.0/tests/test_orchestrators.py +179 -0
  17. repopy-0.3.0/tests/test_prompts.py +106 -0
  18. repopy-0.2.0/src/repopy/orchestrators.py +0 -160
  19. repopy-0.2.0/tests/test_prompts.py +0 -45
  20. {repopy-0.2.0 → repopy-0.3.0}/LICENSE +0 -0
  21. {repopy-0.2.0 → repopy-0.3.0}/setup.cfg +0 -0
  22. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/__init__.py +0 -0
  23. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/dependencies.py +0 -0
  24. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/os_detector.py +0 -0
  25. {repopy-0.2.0 → repopy-0.3.0}/src/repopy/validators.py +0 -0
  26. {repopy-0.2.0 → repopy-0.3.0}/src/repopy.egg-info/dependency_links.txt +0 -0
  27. {repopy-0.2.0 → repopy-0.3.0}/src/repopy.egg-info/entry_points.txt +0 -0
  28. {repopy-0.2.0 → repopy-0.3.0}/src/repopy.egg-info/top_level.txt +0 -0
  29. {repopy-0.2.0 → repopy-0.3.0}/tests/test_validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repopy
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A modular CLI workspace initializer and cloning automation tool.
5
5
  Author-email: Janitha Manatunga <janithamanatunga@gmail.com>
6
6
  License: MIT
@@ -36,17 +36,17 @@ A modular, cross-platform Python CLI workspace manager and developer tooling sui
36
36
 
37
37
  ## 🌟 Key Features
38
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`).
39
+ - **Lifecycle Project Scaffolding (`repopy init`)**: Provisions clean directory structures, dedicated virtual environments, automated `.gitignore` templates, and PEP 621 `pyproject.toml` or `requirements.txt` configs.
40
40
 
41
- - **Remote Cloning (`repopy clone`)**: Clones remote Git repositories into structured local footprints and auto-provisions dedicated virtual environments and dependencies.
41
+ - **Smart Remote Cloning (`repopy clone`)**: Clones remote Git repositories, creates isolated `.venv` environments, previews declared dependencies, and installs them interactively or via flags.
42
42
 
43
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
44
 
45
- - **Architectural Themes**: Supports presets (`minimal`, `web_api`, `cli_package`, `data_science`) tailored to the specific packaging needs of the application.
45
+ - **Architectural Themes**: Built-in layout presets (`minimal`, `web_api`, `cli_package`, `data_science`) tailored to modern packaging standards.
46
46
 
47
- - **Transactional Cleanups**: Employs defensive rollback mechanisms (`shutil.rmtree`) to purge incomplete workspaces if initialization fails midway.
47
+ - **Transactional Cleanups**: Employs defensive rollback mechanisms (`shutil.rmtree`) to safely purge half-baked directories if setup fails midway.
48
48
 
49
- - **Automated Test Suite & CI**: Fully covered by local `pytest` test suites and verified via continuous integration on GitHub Actions.
49
+ - **Isolated & Tested**: Fully decoupled architecture backed by comprehensive unit and mock test suites.
50
50
 
51
51
  ---
52
52
 
@@ -55,11 +55,11 @@ A modular, cross-platform Python CLI workspace manager and developer tooling sui
55
55
  `repopy` is organized into single-responsibility layers:
56
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
57
 
58
- - **Supervision (`orchestrators.py`)**: Broker layer executing lifecycle validation checks, branch sequencing, and command handoffs.
58
+ - **Supervision (`orchestrators.py`)**: Broker layer executing lifecycle validation checks, branch sequencing, rollback triggers and command handoffs.
59
59
 
60
- - **Low-Level Engines (`file_system.py`, `git_engine.py`)**: Decoupled engines handling transactional directory allocations, venv isolation, and direct subprocess execution.
60
+ - **Low-Level Engines (`file_system.py`, `git_engine.py`)**: Decoupled engines handling transactional directory allocations, venv isolation, dependency installation and direct subprocess execution.
61
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.
62
+ - **Validation & Translation (`validators.py`, `dependencies.py`, `os_detector.py`)**: Sanitizes names and paths, validates Git URLs, verifies host binaries (`python`, `git`), and resolves cross-platform paths.
63
63
 
64
64
  ---
65
65
 
@@ -100,15 +100,25 @@ repopy init my-cli -t cli_package --link <git-url>
100
100
  - `minimal`: Bare directory setup with `requirements.txt`.
101
101
  - `web_api`: Layered service layout prepared for modern frameworks.
102
102
  - `cli_package`: Modular structure configured with PEP 621 compliant `pyproject.toml`.
103
- - `data_science`: Notebooks, data folders, and pipeline layouts.
104
- <br>
103
+ - `data_science`: Notebooks, data directories (raw, processed), and pipeline layout.
104
+
105
105
  <br>
106
106
 
107
107
  ### 2. Clone a Remote Project (`repopy clone`)
108
- Fetch a remote repository and immediately auto-provision an isolated `.venv`:
108
+ Fetch a remote repository, provision a virtual environment, and manage dependencies:
109
109
 
110
110
  ```bash
111
- repopy clone <git-url> -n [custom-project-name]
111
+ # Interactive mode (previews requirements.txt and prompts for installation)
112
+ repopy clone <git-url>
113
+
114
+ # Custom directory name
115
+ repopy clone <git-url> -n custom-folder-name
116
+
117
+ # Auto-install dependencies without prompting
118
+ repopy clone <git-url> -i
119
+
120
+ # Fetch and provision environment only (skip dependency installation)
121
+ repopy clone <git-url> --no-install
112
122
  ```
113
123
 
114
124
  <br>
@@ -117,7 +127,11 @@ repopy clone <git-url> -n [custom-project-name]
117
127
  Run inside an existing workspace to push tracking history to an upstream origin:
118
128
 
119
129
  ```bash
120
- repopy link <git-url> -m ["custom-initial-commit-message"]
130
+ # Link with default initial commit message
131
+ repopy link <git-url>
132
+
133
+ # Link with custom commit message
134
+ repopy link <git-url> -m "feat: initial project structure"
121
135
  ```
122
136
 
123
137
  ---
@@ -130,8 +144,6 @@ Run the test suite using `pytest`:
130
144
  pytest -v
131
145
  ```
132
146
 
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
147
  ---
136
148
 
137
149
  ## 🗂️ Project Structure
@@ -155,11 +167,16 @@ repopy/
155
167
  │ ├── templates.py # Static asset definitions & manifests
156
168
  │ └── validators.py # Input sanitation & regex filters
157
169
  ├── tests/
170
+ │ ├── test_cli.py # CLI argument parser & flag exclusivity tests
171
+ │ ├── test_file_system.py # Workspace scaffolding & requirements parsing tests
172
+ │ ├── test_git_engine.py # Subprocess Git mocking & exit code tests
173
+ │ ├── test_orchestrators.py # Pipeline logic, prompts, and rollback tests
158
174
  │ ├── test_prompts.py # Input loop & default manifest tests
159
175
  │ └── test_validators.py # Path and schema validation tests
160
176
  ├── pyproject.toml # PEP 621 packaging configuration
161
177
  ├── LICENSE # MIT License
162
178
  └── README.md
179
+
163
180
  ```
164
181
 
165
182
  ---
@@ -167,10 +184,3 @@ repopy/
167
184
  ## 📄 License
168
185
 
169
186
  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`.
@@ -11,17 +11,17 @@ A modular, cross-platform Python CLI workspace manager and developer tooling sui
11
11
 
12
12
  ## 🌟 Key Features
13
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`).
14
+ - **Lifecycle Project Scaffolding (`repopy init`)**: Provisions clean directory structures, dedicated virtual environments, automated `.gitignore` templates, and PEP 621 `pyproject.toml` or `requirements.txt` configs.
15
15
 
16
- - **Remote Cloning (`repopy clone`)**: Clones remote Git repositories into structured local footprints and auto-provisions dedicated virtual environments and dependencies.
16
+ - **Smart Remote Cloning (`repopy clone`)**: Clones remote Git repositories, creates isolated `.venv` environments, previews declared dependencies, and installs them interactively or via flags.
17
17
 
18
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
19
 
20
- - **Architectural Themes**: Supports presets (`minimal`, `web_api`, `cli_package`, `data_science`) tailored to the specific packaging needs of the application.
20
+ - **Architectural Themes**: Built-in layout presets (`minimal`, `web_api`, `cli_package`, `data_science`) tailored to modern packaging standards.
21
21
 
22
- - **Transactional Cleanups**: Employs defensive rollback mechanisms (`shutil.rmtree`) to purge incomplete workspaces if initialization fails midway.
22
+ - **Transactional Cleanups**: Employs defensive rollback mechanisms (`shutil.rmtree`) to safely purge half-baked directories if setup fails midway.
23
23
 
24
- - **Automated Test Suite & CI**: Fully covered by local `pytest` test suites and verified via continuous integration on GitHub Actions.
24
+ - **Isolated & Tested**: Fully decoupled architecture backed by comprehensive unit and mock test suites.
25
25
 
26
26
  ---
27
27
 
@@ -30,11 +30,11 @@ A modular, cross-platform Python CLI workspace manager and developer tooling sui
30
30
  `repopy` is organized into single-responsibility layers:
31
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
32
 
33
- - **Supervision (`orchestrators.py`)**: Broker layer executing lifecycle validation checks, branch sequencing, and command handoffs.
33
+ - **Supervision (`orchestrators.py`)**: Broker layer executing lifecycle validation checks, branch sequencing, rollback triggers and command handoffs.
34
34
 
35
- - **Low-Level Engines (`file_system.py`, `git_engine.py`)**: Decoupled engines handling transactional directory allocations, venv isolation, and direct subprocess execution.
35
+ - **Low-Level Engines (`file_system.py`, `git_engine.py`)**: Decoupled engines handling transactional directory allocations, venv isolation, dependency installation and direct subprocess execution.
36
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.
37
+ - **Validation & Translation (`validators.py`, `dependencies.py`, `os_detector.py`)**: Sanitizes names and paths, validates Git URLs, verifies host binaries (`python`, `git`), and resolves cross-platform paths.
38
38
 
39
39
  ---
40
40
 
@@ -75,15 +75,25 @@ repopy init my-cli -t cli_package --link <git-url>
75
75
  - `minimal`: Bare directory setup with `requirements.txt`.
76
76
  - `web_api`: Layered service layout prepared for modern frameworks.
77
77
  - `cli_package`: Modular structure configured with PEP 621 compliant `pyproject.toml`.
78
- - `data_science`: Notebooks, data folders, and pipeline layouts.
79
- <br>
78
+ - `data_science`: Notebooks, data directories (raw, processed), and pipeline layout.
79
+
80
80
  <br>
81
81
 
82
82
  ### 2. Clone a Remote Project (`repopy clone`)
83
- Fetch a remote repository and immediately auto-provision an isolated `.venv`:
83
+ Fetch a remote repository, provision a virtual environment, and manage dependencies:
84
84
 
85
85
  ```bash
86
- repopy clone <git-url> -n [custom-project-name]
86
+ # Interactive mode (previews requirements.txt and prompts for installation)
87
+ repopy clone <git-url>
88
+
89
+ # Custom directory name
90
+ repopy clone <git-url> -n custom-folder-name
91
+
92
+ # Auto-install dependencies without prompting
93
+ repopy clone <git-url> -i
94
+
95
+ # Fetch and provision environment only (skip dependency installation)
96
+ repopy clone <git-url> --no-install
87
97
  ```
88
98
 
89
99
  <br>
@@ -92,7 +102,11 @@ repopy clone <git-url> -n [custom-project-name]
92
102
  Run inside an existing workspace to push tracking history to an upstream origin:
93
103
 
94
104
  ```bash
95
- repopy link <git-url> -m ["custom-initial-commit-message"]
105
+ # Link with default initial commit message
106
+ repopy link <git-url>
107
+
108
+ # Link with custom commit message
109
+ repopy link <git-url> -m "feat: initial project structure"
96
110
  ```
97
111
 
98
112
  ---
@@ -105,8 +119,6 @@ Run the test suite using `pytest`:
105
119
  pytest -v
106
120
  ```
107
121
 
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
122
  ---
111
123
 
112
124
  ## 🗂️ Project Structure
@@ -130,11 +142,16 @@ repopy/
130
142
  │ ├── templates.py # Static asset definitions & manifests
131
143
  │ └── validators.py # Input sanitation & regex filters
132
144
  ├── tests/
145
+ │ ├── test_cli.py # CLI argument parser & flag exclusivity tests
146
+ │ ├── test_file_system.py # Workspace scaffolding & requirements parsing tests
147
+ │ ├── test_git_engine.py # Subprocess Git mocking & exit code tests
148
+ │ ├── test_orchestrators.py # Pipeline logic, prompts, and rollback tests
133
149
  │ ├── test_prompts.py # Input loop & default manifest tests
134
150
  │ └── test_validators.py # Path and schema validation tests
135
151
  ├── pyproject.toml # PEP 621 packaging configuration
136
152
  ├── LICENSE # MIT License
137
153
  └── README.md
154
+
138
155
  ```
139
156
 
140
157
  ---
@@ -142,10 +159,3 @@ repopy/
142
159
  ## 📄 License
143
160
 
144
161
  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`.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "repopy"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "A modular CLI workspace initializer and cloning automation tool."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -12,32 +12,34 @@ def main() -> None:
12
12
  args = parse_arguments()
13
13
 
14
14
  if args.command == 'init':
15
- print('Working on it...\n')
15
+ print('⌛ Working on it...\n')
16
16
  manifest = capture_project_manifests(args)
17
17
  initializer = LocalInitializer(manifest)
18
18
  success = initializer.run()
19
19
 
20
20
  if success and args.link:
21
- print('Initiating automated repository link shortcut...')
21
+ print('⌛ Initiating automated repository link shortcut...')
22
22
  link_initializer = LinkInitializer(args.link, args.message)
23
23
  link_initializer.run()
24
24
 
25
25
  elif args.command == 'clone':
26
- print('Working on it...\n')
27
- initializer = CloneInitializer(args.repo_url, args.name)
26
+ print('⌛ Working on it...\n')
27
+ initializer = CloneInitializer(
28
+ args.repo_url, args.name, args.install, args.no_install
29
+ )
28
30
  initializer.run()
29
31
 
30
32
  elif args.command == 'link':
31
- print('Working on it...\n')
33
+ print('⌛ Working on it...\n')
32
34
  initializer = LinkInitializer(args.repo_url, args.message)
33
35
  initializer.run()
34
36
 
35
37
  else:
36
- print('Usage: repopy [local | clone] --help')
37
- print('Error: Please specify a subcommand')
38
+ print('Usage: repopy [init | clone | link] --help')
39
+ print('⚠️ Error: Please specify a subcommand')
38
40
 
39
41
  except KeyboardInterrupt:
40
- print('\n Workspace operation cancelled by user.')
42
+ print('\n ✖️ Workspace operation cancelled by user.')
41
43
  sys.exit(1)
42
44
 
43
45
 
@@ -76,6 +76,22 @@ def parse_arguments() -> argparse.Namespace:
76
76
  help='Name of the project folder'
77
77
  )
78
78
 
79
+ # Mutually exclusive commands regarding installing dependencies:
80
+ # install flag (-i / --install) for installing dependencies, bypassing prompt
81
+ # download-only flag (-d / --download-only) for only fetching dependencies without installing, bypassing prompt
82
+ deps_group = clone_parser.add_mutually_exclusive_group()
83
+ deps_group.add_argument(
84
+ '-i', '--install',
85
+ action='store_true',
86
+ help='Automatically install dependencies without confirmation'
87
+ )
88
+
89
+ deps_group.add_argument(
90
+ '--no-install',
91
+ action='store_true',
92
+ help='Skip dependency installation completely (fetch-only safe mode)'
93
+ )
94
+
79
95
  #---------------------------------------------------------------------------------
80
96
  # `repopy link` subcommand
81
97
  #---------------------------------------------------------------------------------
@@ -10,6 +10,7 @@ import logging
10
10
  import subprocess
11
11
  from pathlib import Path
12
12
 
13
+ from repopy.os_detector import is_windows
13
14
  from repopy.templates import GITIGNORE_TEMPLATE, generate_pyproject_toml
14
15
 
15
16
  logger = logging.getLogger(__name__)
@@ -105,6 +106,63 @@ class FileSystemEngine:
105
106
  return False
106
107
 
107
108
 
109
+ def read_requirements(self) -> list[str]:
110
+ '''Read repository dependencies if requirements.txt exists and displays them'''
111
+ req_path = self.project_path / 'requirements.txt'
112
+ if not req_path.is_file():
113
+ return []
114
+
115
+ with open(req_path, 'r') as f:
116
+ lines = [
117
+ cleaned_line
118
+ for line in f
119
+ if (cleaned_line := line.strip()) and not cleaned_line.startswith('#')
120
+ ]
121
+
122
+ return lines
123
+
124
+
125
+ def install_dependencies(self) -> bool:
126
+ '''Install repository dependencies if requirements.txt is present'''
127
+ req_file = self.project_path / 'requirements.txt'
128
+
129
+ if not req_file.exists():
130
+ return True
131
+
132
+ if is_windows():
133
+ pip_path = self.project_path / '.venv' / 'Scripts' / 'pip.exe'
134
+ else:
135
+ pip_path = self.project_path / '.venv' / 'bin' / 'pip'
136
+
137
+ try:
138
+ result = subprocess.run([str(pip_path), 'install', '-r', str(req_file)], capture_output=True, text=True)
139
+ return result.returncode == 0
140
+
141
+ except OSError as e:
142
+ logger.error(f'Failed to install dependencies at {self.project_path}: {e}')
143
+ return False
144
+
145
+
146
+ def get_activation_guide(self) -> str:
147
+ if is_windows():
148
+ venv_activation = '''
149
+ .\\.venv\\Scripts\\Activate.ps1 (if on Powershell)
150
+ .\\.venv\\Scripts\\activate.bat (if on cmd)\n
151
+ '''
152
+ else:
153
+ venv_activation = '''
154
+ source .venv/bin/activate\n
155
+ '''
156
+
157
+ return f'''
158
+ To activate the virtual environment and get started, enter the following:
159
+
160
+ cd {self.project_path}
161
+
162
+ {venv_activation}
163
+ '''
164
+
165
+
108
166
  def cleanup(self) -> None:
109
167
  '''Removes any partially created project directories if a failure occurs'''
110
168
  if self.project_path.exists():
@@ -4,13 +4,10 @@ holds the methods required to cone a GitHub repository and install
4
4
  dependencies.
5
5
  '''
6
6
 
7
- import shutil
8
7
  import logging
9
8
  import subprocess
10
9
  from pathlib import Path
11
10
 
12
- from repopy.os_detector import is_windows
13
-
14
11
  logger = logging.getLogger(__name__)
15
12
 
16
13
  class GitEngine:
@@ -27,42 +24,13 @@ class GitEngine:
27
24
  def clone_repository(self) -> bool:
28
25
  '''Clone GitHub repository using the given url to the given directory name'''
29
26
  try:
30
- result = subprocess.run(['git', 'clone', self.repo_url, str(self.project_path)], capture_output=True, text=True)
27
+ result = subprocess.run(['git', 'clone', self.repo_url, str(self.project_path)], text=True)
31
28
  return result.returncode == 0
32
29
  except OSError as e:
33
30
  logger.error(f'Failed to clone repository at {self.project_path}: {e}')
34
31
  return False
35
32
 
36
33
 
37
- def install_dependencies(self) -> bool:
38
- '''Install repository dependencies if requirements.txt is present'''
39
- req_file = self.project_path / 'requirements.txt'
40
-
41
- if not req_file.exists():
42
- return True
43
-
44
- if is_windows():
45
- pip_path = self.project_path / '.venv' / 'Scripts' / 'pip.exe'
46
- else:
47
- pip_path = self.project_path / '.venv' / 'bin' / 'pip'
48
-
49
- try:
50
- result = subprocess.run([str(pip_path), 'install', '-r', str(req_file)], capture_output=True, text=True)
51
- return result.returncode == 0
52
- except OSError as e:
53
- logger.error(f'Failed to install dependencies at {self.project_path}: {e}')
54
- return False
55
-
56
- def cleanup(self) -> None:
57
- '''Removes any partially created project directories if a failure occurs'''
58
- if self.project_path.exists():
59
- try:
60
- shutil.rmtree(self.project_path)
61
- logger.info(f'Successfully cleaned up half-baked workspace at {self.project_path}')
62
- except OSError as e:
63
- logger.error(f'Failed to clean up directory at {self.project_path}: {e}')
64
-
65
-
66
34
  class GitLinkEngine:
67
35
 
68
36
  def __init__(self, repo_url: str, message: str):
@@ -97,6 +65,6 @@ class GitLinkEngine:
97
65
  return True
98
66
 
99
67
  except OSError as e:
100
- logger.error(f'Failed to execute Git linking operationsL {e}')
68
+ logger.error(f'Failed to execute Git linking operations: {e}')
101
69
  return False
102
70
 
@@ -0,0 +1,149 @@
1
+ '''
2
+ Orchestration Layer. Core supervisor that amalgamates all inspectors and building
3
+ tools and manages the entire user request loop from start to finish.
4
+ '''
5
+
6
+ import logging
7
+ from pathlib import Path
8
+
9
+ from repopy.file_system import FileSystemEngine
10
+ from repopy.git_engine import GitEngine, GitLinkEngine
11
+ from repopy.dependencies import has_python, has_git
12
+ from repopy.prompts import confirm_dependency_installation, confirm_cleanup
13
+ from repopy.validators import is_valid_project_name, is_valid_git_url
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ class LocalInitializer:
18
+
19
+ def __init__(self, manifest: dict):
20
+ self.manifest = manifest
21
+ self.project_name = manifest['project_name']
22
+ self.project_path = Path(self.project_name).resolve()
23
+ self.theme = manifest['theme']
24
+
25
+
26
+ def run(self) -> bool:
27
+ '''Runs when `repopy local` is called'''
28
+ if not has_python():
29
+ print('⚠️ Python is not installed in the local machine.')
30
+ return False
31
+
32
+ if not is_valid_project_name(self.project_name):
33
+ print(f'⚠️ "{self.project_name}" is not a valid directory name.')
34
+ return False
35
+
36
+ fs_engine = FileSystemEngine(self.project_path)
37
+ if fs_engine.build_workspace(self.theme, self.manifest):
38
+ print(f'✅ Success! Workspace provisioned inside: {self.project_path}')
39
+ print(f'✅ Layout theme configured: [{self.theme.upper()}]')
40
+ print(fs_engine.get_activation_guide())
41
+ return True
42
+
43
+ else:
44
+ print(f'❌ Construction failed. Triggering cleanup on half-baked directories...')
45
+ fs_engine.cleanup()
46
+ return False
47
+
48
+
49
+ class CloneInitializer:
50
+
51
+ def __init__(self, repo_url: str, dir_name: str | None, install: bool, no_install: bool):
52
+ self.repo_url = repo_url
53
+ self.dir_name = dir_name if dir_name else None
54
+ self.auto_install = bool(install)
55
+ self.no_install = bool(no_install)
56
+
57
+ def run(self) -> bool:
58
+ '''Runs when `repopy clone` is called'''
59
+ if not has_python() or not has_git():
60
+ print(f'❌ Failed to initiate repopy due to absence of Python or Git on local machine.')
61
+ return False
62
+
63
+ if not is_valid_git_url(self.repo_url):
64
+ print('⚠️ Invalid Git URL format')
65
+ return False
66
+
67
+ if self.dir_name:
68
+ predicted_name = self.dir_name
69
+ else:
70
+ cleaned_url = self.repo_url.removesuffix('.git')
71
+ predicted_name = Path(cleaned_url).name
72
+
73
+ if not is_valid_project_name(str(predicted_name)):
74
+ print(f'⚠️ "{predicted_name}" is not a valid directory name.')
75
+ return False
76
+
77
+ git_engine = GitEngine(self.repo_url, self.dir_name)
78
+ fs_engine = FileSystemEngine(git_engine.project_path)
79
+
80
+ if not git_engine.clone_repository():
81
+ return False
82
+
83
+ if not fs_engine.create_virtual_environment():
84
+ print(f'❌ Failed to create virtual environment at {git_engine.project_path}. Cleaning up half-baked files...')
85
+ fs_engine.cleanup()
86
+ return False
87
+
88
+ print('☑️ Cloning complete and .venv has been created.')
89
+ deps = fs_engine.read_requirements()
90
+
91
+ if not deps:
92
+ print('No dependencies to install in repository (or requirements.txt is absent).')
93
+ should_install = False
94
+
95
+ elif self.no_install:
96
+ print('Skipping installation of dependencies.')
97
+ should_install = False
98
+
99
+ elif self.auto_install:
100
+ print('Installing dependencies...')
101
+ should_install = True
102
+
103
+ else:
104
+ should_install = confirm_dependency_installation(deps)
105
+
106
+ if should_install:
107
+ if not fs_engine.install_dependencies():
108
+ print(f'✅ GitHub repository is ready at {git_engine.project_path}.')
109
+ print(f'⚠️ Failed to install dependencies from requirements.txt at {git_engine.project_path}')
110
+ print(fs_engine.get_activation_guide())
111
+ print('To install dependencies, run the following on your terminal:\n')
112
+ print(' pip install -r requirements.txt\n')
113
+ print('Or install them individually by `pip install <module>`\n')
114
+
115
+ if confirm_cleanup():
116
+ fs_engine.cleanup()
117
+ print('Half-baked directory has been cleaned up.')
118
+ return False
119
+
120
+ print(f'✅ Success! GitHub repository is ready at {git_engine.project_path}.')
121
+ print(fs_engine.get_activation_guide())
122
+ return True
123
+
124
+
125
+ class LinkInitializer:
126
+
127
+ def __init__(self, repo_url: str, message: str | None):
128
+ self.repo_url = repo_url
129
+ self.message = message if message else 'Initial commit: Workspace structured by repopy'
130
+
131
+
132
+ def run(self) -> bool:
133
+ '''Runs when `repopy link` is called'''
134
+ if not has_git():
135
+ print('⚠️ Git is not installed on this machine.')
136
+ return False
137
+
138
+ if not Path('.git').exists():
139
+ print('❌ No local Git repository detected. Please initialize this project first.')
140
+ return False
141
+
142
+ git_link_engine = GitLinkEngine(self.repo_url, self.message)
143
+ if git_link_engine.link_and_push():
144
+ print(f'✅ Success! Your local repository has been linked to {self.repo_url}.')
145
+ print(f'Commit message baseline: "{self.message}"')
146
+ return True
147
+ else:
148
+ print(f'❌ Failed to establish communication or push to remote repository at {self.repo_url}.')
149
+ return False
@@ -5,7 +5,7 @@ loops, and fallback default configurations.
5
5
 
6
6
  import re
7
7
  from pathlib import Path
8
- from repopy.orchestrators import is_valid_project_name
8
+ from repopy.validators import is_valid_project_name
9
9
 
10
10
  THEME_OPTIONS = {
11
11
  '1': 'minimal',
@@ -106,4 +106,26 @@ def capture_project_manifests(cli_args) -> dict:
106
106
  }
107
107
 
108
108
 
109
+ def confirm_dependency_installation(dependencies: list[str]) -> bool:
110
+ print('\nThis repository contains the following dependencies:')
111
+ for dep in dependencies:
112
+ print(f' • {dep}')
113
+
114
+ print()
109
115
 
116
+ try:
117
+ choice = input('Do you want to install these dependencies? [y/N]: ').strip().lower()
118
+ return choice in ('y', 'yes')
119
+
120
+ except (KeyboardInterrupt, EOFError):
121
+ print('\nSkipping installation.')
122
+ return False
123
+
124
+
125
+ def confirm_cleanup() -> bool:
126
+ try:
127
+ answer = input('If not, would you like to cleanup the new directory? [y/N]: ').strip().lower()
128
+ return answer in ('y', 'yes')
129
+
130
+ except (KeyboardInterrupt, EOFError):
131
+ return False