runongpu 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.
- runongpu-0.1.0/LICENSE +21 -0
- runongpu-0.1.0/PKG-INFO +172 -0
- runongpu-0.1.0/README.md +160 -0
- runongpu-0.1.0/pyproject.toml +32 -0
- runongpu-0.1.0/runongpu/cli.py +107 -0
- runongpu-0.1.0/runongpu/colab.py +199 -0
- runongpu-0.1.0/runongpu/config.py +111 -0
- runongpu-0.1.0/runongpu/parser.py +46 -0
- runongpu-0.1.0/runongpu.egg-info/PKG-INFO +172 -0
- runongpu-0.1.0/runongpu.egg-info/SOURCES.txt +17 -0
- runongpu-0.1.0/runongpu.egg-info/dependency_links.txt +1 -0
- runongpu-0.1.0/runongpu.egg-info/entry_points.txt +2 -0
- runongpu-0.1.0/runongpu.egg-info/requires.txt +3 -0
- runongpu-0.1.0/runongpu.egg-info/top_level.txt +1 -0
- runongpu-0.1.0/setup.cfg +4 -0
- runongpu-0.1.0/tests/test_config.py +28 -0
- runongpu-0.1.0/tests/test_config_persistance.py +43 -0
- runongpu-0.1.0/tests/test_parser.py +95 -0
- runongpu-0.1.0/tests/test_template.py +34 -0
runongpu-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MashrafeeAryan
|
|
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.
|
runongpu-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: runongpu
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool that runs GitHub projects on free cloud GPUs
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: typer
|
|
9
|
+
Requires-Dist: rich
|
|
10
|
+
Requires-Dist: playwright
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# RunOnGPU
|
|
14
|
+
|
|
15
|
+
RunOnGPU is a CLI tool that helps you run GitHub projects on a GPU with minimal setup.
|
|
16
|
+
|
|
17
|
+
It is useful if you want to test CUDA, PyTorch, or other GPU code but do not have a local NVIDIA GPU.
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
* Windows
|
|
22
|
+
* Python 3.10+
|
|
23
|
+
* Git
|
|
24
|
+
* Google Chrome
|
|
25
|
+
* Google account for Colab
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install runongpu
|
|
31
|
+
python -m playwright install
|
|
32
|
+
|
|
33
|
+
Check setup:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
runongpu doctor
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
Go to the project you want to run and initialize RunOnGPU:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
runongpu init
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Enter your GitHub repo URL when asked.
|
|
48
|
+
|
|
49
|
+
This creates a `runongpu.txt` file. Edit this file to tell RunOnGPU how to set up, build, test, and run your project.
|
|
50
|
+
|
|
51
|
+
Then run:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
runongpu run
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
RunOnGPU will open Colab, copy the starter notebook, clone your repo, set the runtime to a T4 GPU, and run your commands.
|
|
58
|
+
|
|
59
|
+
## runongpu.txt
|
|
60
|
+
|
|
61
|
+
`runongpu.txt` controls what happens inside Colab.
|
|
62
|
+
|
|
63
|
+
It has four sections:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
[setup]
|
|
67
|
+
# install dependencies here
|
|
68
|
+
|
|
69
|
+
[build]
|
|
70
|
+
# compile or build the project here
|
|
71
|
+
|
|
72
|
+
[test]
|
|
73
|
+
# run tests here
|
|
74
|
+
|
|
75
|
+
[run]
|
|
76
|
+
# run the final program here
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Add one command per line.
|
|
80
|
+
|
|
81
|
+
## Example: CUDA
|
|
82
|
+
|
|
83
|
+
If your repo has this structure:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
my-cuda-project/
|
|
87
|
+
├── main.cu
|
|
88
|
+
└── runongpu.txt
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
[setup]
|
|
95
|
+
|
|
96
|
+
[build]
|
|
97
|
+
nvcc main.cu -o vector_add
|
|
98
|
+
|
|
99
|
+
[test]
|
|
100
|
+
|
|
101
|
+
[run]
|
|
102
|
+
./vector_add
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Example: CUDA in a Subfolder
|
|
106
|
+
|
|
107
|
+
If your repo has this structure:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
runongpu-examples/
|
|
111
|
+
├── cuda/
|
|
112
|
+
│ └── vector-add/
|
|
113
|
+
│ └── main.cu
|
|
114
|
+
└── runongpu.txt
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Use:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
[setup]
|
|
121
|
+
|
|
122
|
+
[build]
|
|
123
|
+
cd cuda/vector-add && nvcc main.cu -o vector_add
|
|
124
|
+
|
|
125
|
+
[test]
|
|
126
|
+
|
|
127
|
+
[run]
|
|
128
|
+
cd cuda/vector-add && ./vector_add
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Example: Python
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
[setup]
|
|
135
|
+
pip install -r requirements.txt
|
|
136
|
+
|
|
137
|
+
[build]
|
|
138
|
+
|
|
139
|
+
[test]
|
|
140
|
+
pytest
|
|
141
|
+
|
|
142
|
+
[run]
|
|
143
|
+
python main.py
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Example: CMake
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
[setup]
|
|
150
|
+
|
|
151
|
+
[build]
|
|
152
|
+
cmake -S . -B build
|
|
153
|
+
cmake --build build
|
|
154
|
+
|
|
155
|
+
[test]
|
|
156
|
+
ctest --test-dir build --output-on-failure
|
|
157
|
+
|
|
158
|
+
[run]
|
|
159
|
+
./build/my_program
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Notes
|
|
163
|
+
|
|
164
|
+
On the first run, you may need to sign into Google Colab. RunOnGPU saves the copied notebook URL and reuses it on future runs.
|
|
165
|
+
|
|
166
|
+
Do not interact with the Colab window while RunOnGPU is setting it up.
|
|
167
|
+
|
|
168
|
+
## Run Tests
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
python -m pytest
|
|
172
|
+
```
|
runongpu-0.1.0/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# RunOnGPU
|
|
2
|
+
|
|
3
|
+
RunOnGPU is a CLI tool that helps you run GitHub projects on a GPU with minimal setup.
|
|
4
|
+
|
|
5
|
+
It is useful if you want to test CUDA, PyTorch, or other GPU code but do not have a local NVIDIA GPU.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
* Windows
|
|
10
|
+
* Python 3.10+
|
|
11
|
+
* Git
|
|
12
|
+
* Google Chrome
|
|
13
|
+
* Google account for Colab
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install runongpu
|
|
19
|
+
python -m playwright install
|
|
20
|
+
|
|
21
|
+
Check setup:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
runongpu doctor
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
Go to the project you want to run and initialize RunOnGPU:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
runongpu init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Enter your GitHub repo URL when asked.
|
|
36
|
+
|
|
37
|
+
This creates a `runongpu.txt` file. Edit this file to tell RunOnGPU how to set up, build, test, and run your project.
|
|
38
|
+
|
|
39
|
+
Then run:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
runongpu run
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
RunOnGPU will open Colab, copy the starter notebook, clone your repo, set the runtime to a T4 GPU, and run your commands.
|
|
46
|
+
|
|
47
|
+
## runongpu.txt
|
|
48
|
+
|
|
49
|
+
`runongpu.txt` controls what happens inside Colab.
|
|
50
|
+
|
|
51
|
+
It has four sections:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
[setup]
|
|
55
|
+
# install dependencies here
|
|
56
|
+
|
|
57
|
+
[build]
|
|
58
|
+
# compile or build the project here
|
|
59
|
+
|
|
60
|
+
[test]
|
|
61
|
+
# run tests here
|
|
62
|
+
|
|
63
|
+
[run]
|
|
64
|
+
# run the final program here
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Add one command per line.
|
|
68
|
+
|
|
69
|
+
## Example: CUDA
|
|
70
|
+
|
|
71
|
+
If your repo has this structure:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
my-cuda-project/
|
|
75
|
+
├── main.cu
|
|
76
|
+
└── runongpu.txt
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use:
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
[setup]
|
|
83
|
+
|
|
84
|
+
[build]
|
|
85
|
+
nvcc main.cu -o vector_add
|
|
86
|
+
|
|
87
|
+
[test]
|
|
88
|
+
|
|
89
|
+
[run]
|
|
90
|
+
./vector_add
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Example: CUDA in a Subfolder
|
|
94
|
+
|
|
95
|
+
If your repo has this structure:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
runongpu-examples/
|
|
99
|
+
├── cuda/
|
|
100
|
+
│ └── vector-add/
|
|
101
|
+
│ └── main.cu
|
|
102
|
+
└── runongpu.txt
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Use:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
[setup]
|
|
109
|
+
|
|
110
|
+
[build]
|
|
111
|
+
cd cuda/vector-add && nvcc main.cu -o vector_add
|
|
112
|
+
|
|
113
|
+
[test]
|
|
114
|
+
|
|
115
|
+
[run]
|
|
116
|
+
cd cuda/vector-add && ./vector_add
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Example: Python
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
[setup]
|
|
123
|
+
pip install -r requirements.txt
|
|
124
|
+
|
|
125
|
+
[build]
|
|
126
|
+
|
|
127
|
+
[test]
|
|
128
|
+
pytest
|
|
129
|
+
|
|
130
|
+
[run]
|
|
131
|
+
python main.py
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Example: CMake
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
[setup]
|
|
138
|
+
|
|
139
|
+
[build]
|
|
140
|
+
cmake -S . -B build
|
|
141
|
+
cmake --build build
|
|
142
|
+
|
|
143
|
+
[test]
|
|
144
|
+
ctest --test-dir build --output-on-failure
|
|
145
|
+
|
|
146
|
+
[run]
|
|
147
|
+
./build/my_program
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Notes
|
|
151
|
+
|
|
152
|
+
On the first run, you may need to sign into Google Colab. RunOnGPU saves the copied notebook URL and reuses it on future runs.
|
|
153
|
+
|
|
154
|
+
Do not interact with the Colab window while RunOnGPU is setting it up.
|
|
155
|
+
|
|
156
|
+
## Run Tests
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
python -m pytest
|
|
160
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Package metadata used by pip and Python build tools.
|
|
2
|
+
# This section defines what RunOnGPU is, what Python version it supports,
|
|
3
|
+
# and which third-party libraries must be installed with it.
|
|
4
|
+
[project]
|
|
5
|
+
name = "runongpu"
|
|
6
|
+
version = "0.1.0"
|
|
7
|
+
description = "A CLI tool that runs GitHub projects on free cloud GPUs"
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
|
|
11
|
+
# Runtime dependencies for the CLI.
|
|
12
|
+
# typer: builds clean terminal commands like `runongpu doctor`
|
|
13
|
+
# rich: prints readable, colored terminal output
|
|
14
|
+
# playwright: controls Chrome so RunOnGPU can automate Google Colab
|
|
15
|
+
dependencies = [
|
|
16
|
+
"typer",
|
|
17
|
+
"rich",
|
|
18
|
+
"playwright"
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
# Exposes RunOnGPU as a terminal command.
|
|
22
|
+
# After installing the package, users can type `runongpu` in their terminal,
|
|
23
|
+
# and Python will run the Typer app defined in runongpu/cli.py.
|
|
24
|
+
[project.scripts]
|
|
25
|
+
runongpu = "runongpu.cli:app"
|
|
26
|
+
|
|
27
|
+
# Build configuration for modern Python packaging.
|
|
28
|
+
# setuptools packages the project so it can be installed locally during
|
|
29
|
+
# development and later published as a real Python package.
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["setuptools"]
|
|
32
|
+
build-backend = "setuptools.build_meta"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Builds the command-line interface for RunOnGPU.
|
|
2
|
+
import typer
|
|
3
|
+
|
|
4
|
+
# Prints styled terminal output for a better CLI experience.
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
|
|
7
|
+
# Project helpers for saving local config, creating runongpu.txt, and loading user settings.
|
|
8
|
+
from runongpu.config import create_runongpu_template_file, get_folder_name_from_repo_url, save_notebook_url, save_repo_url, load_config
|
|
9
|
+
from runongpu.colab import open_colab
|
|
10
|
+
from runongpu.parser import parse_config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
app = typer.Typer()
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.command()
|
|
18
|
+
def doctor():
|
|
19
|
+
# Verifies that the user's local RunOnGPU setup has the required pieces installed.
|
|
20
|
+
console.print("[bold cyan]Checking RunOnGPU setup...[/bold cyan]")
|
|
21
|
+
|
|
22
|
+
console.print("[green]✓ CLI is running[/green]")
|
|
23
|
+
console.print("[green]✓ Typer is installed[/green]")
|
|
24
|
+
console.print("[green]✓ Rich is installed[/green]")
|
|
25
|
+
|
|
26
|
+
saved_config = load_config()
|
|
27
|
+
|
|
28
|
+
if saved_config is None:
|
|
29
|
+
console.print("[yellow]⚠ No repo URL saved yet[/yellow]")
|
|
30
|
+
console.print("[yellow]Run: runongpu init[/yellow]")
|
|
31
|
+
else:
|
|
32
|
+
console.print("[green]✓ Repo URL is saved[/green]")
|
|
33
|
+
console.print(f"[green] Repo URL: {saved_config["repo_url"]}")
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
import playwright
|
|
37
|
+
console.print("[green]✓ Playwright is installed[/green]")
|
|
38
|
+
except ImportError:
|
|
39
|
+
console.print("[red]✗ Playwright is not installed[/red]")
|
|
40
|
+
console.print("[yellow]Run: pip install playwright[/yellow]")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@app.command()
|
|
44
|
+
def init():
|
|
45
|
+
# Store the GitHub repo that RunOnGPU should clone inside Colab.
|
|
46
|
+
repo_url = typer.prompt("Enter your Github repo URL")
|
|
47
|
+
|
|
48
|
+
console.print("[yellow]Deriving folder name[/yellow]")
|
|
49
|
+
folder_name = get_folder_name_from_repo_url(repo_url)
|
|
50
|
+
console.print("[green]Successfully derived folder name[/green]")
|
|
51
|
+
|
|
52
|
+
save_repo_url(repo_url, folder_name)
|
|
53
|
+
|
|
54
|
+
# Create a starter runongpu.txt so users know where to put setup/build/test/run commands.
|
|
55
|
+
create_runongpu_template_file()
|
|
56
|
+
|
|
57
|
+
console.print("[green]✓ Github repo URL saved. [/green]")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@app.command()
|
|
61
|
+
def config():
|
|
62
|
+
# Show the saved local settings without opening Colab.
|
|
63
|
+
saved_config = load_config()
|
|
64
|
+
|
|
65
|
+
if saved_config is None:
|
|
66
|
+
console.print("[yellow]No repo url found. Run `runongpu init` first.[/yellow]")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
console.print("[bold cyan]Saved RunOnGPU config:[/bold cyan]")
|
|
70
|
+
console.print(f"GitHub repo URL: [green]{saved_config['repo_url']}[/green]")
|
|
71
|
+
|
|
72
|
+
notebook_url = saved_config.get("notebook_url", "")
|
|
73
|
+
|
|
74
|
+
if notebook_url:
|
|
75
|
+
console.print(f"Colab notebook URL: [green]{notebook_url}[/green]")
|
|
76
|
+
else:
|
|
77
|
+
console.print("Colab notebook URL: [yellow]Not saved yet[/yellow]")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@app.command()
|
|
81
|
+
def run():
|
|
82
|
+
# Main workflow: parse project commands, open Colab, write the notebook cell, and save the notebook URL.
|
|
83
|
+
saved_config = load_config()
|
|
84
|
+
|
|
85
|
+
if saved_config is None:
|
|
86
|
+
console.print("[red]No repo URL saved. Run `runongpu init` first.[/red]")
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
console.print("[yellow]Parsing runongpu.txt. [/yellow]")
|
|
91
|
+
project_config = parse_config()
|
|
92
|
+
console.print("[green] Successfully parsed through runongpu.txt")
|
|
93
|
+
except ValueError as error:
|
|
94
|
+
console.print(f"[red] runongpu.txt error: [/red]\n {error}")
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
notebook_url = saved_config.get("notebook_url", "")
|
|
98
|
+
|
|
99
|
+
console.print("[bold cyan]Starting RunOnGPU...[/bold cyan]")
|
|
100
|
+
current_notebook_url = open_colab(notebook_url, project_config)
|
|
101
|
+
|
|
102
|
+
# Save the copied Colab notebook so future runs reuse it instead of creating another copy.
|
|
103
|
+
save_notebook_url(current_notebook_url)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == "__main__":
|
|
107
|
+
app()
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import time
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from urllib.error import URLError
|
|
6
|
+
from urllib.request import urlopen
|
|
7
|
+
|
|
8
|
+
from playwright.sync_api import sync_playwright
|
|
9
|
+
|
|
10
|
+
from runongpu.config import load_config
|
|
11
|
+
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
# Path to the real Chrome executable on Windows.
|
|
17
|
+
# RunOnGPU uses real Chrome because Colab/Google login is more reliable there
|
|
18
|
+
# than in Playwright's bundled browser.
|
|
19
|
+
CHROME_EXE = (
|
|
20
|
+
Path(os.environ["PROGRAMFILES"])
|
|
21
|
+
/ "Google"
|
|
22
|
+
/ "Chrome"
|
|
23
|
+
/ "Application"
|
|
24
|
+
/ "chrome.exe"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Dedicated Chrome profile for RunOnGPU.
|
|
28
|
+
# This keeps Colab login/session data persistent without touching the user's
|
|
29
|
+
# everyday Chrome profile.
|
|
30
|
+
RUNONGPU_PROFILE_DIR = Path.home() / ".runongpu" / "chrome-profile"
|
|
31
|
+
|
|
32
|
+
# Local Chrome DevTools Protocol port.
|
|
33
|
+
# Playwright connects to this port to control the real Chrome window.
|
|
34
|
+
DEBUG_PORT = 9222
|
|
35
|
+
|
|
36
|
+
# Shared starter notebook used only when the user does not already have a saved
|
|
37
|
+
# RunOnGPU notebook URL.
|
|
38
|
+
TEMPLATE_URL = "https://colab.research.google.com/drive/1pB8iVjR4-tPVSEBFjY8ow6N_F34bcMwi?usp=sharing"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def wait_for_debug_port(timeout_seconds: int = 15) -> None:
|
|
42
|
+
"""Wait until Chrome is ready for Playwright to connect."""
|
|
43
|
+
start_time = time.time()
|
|
44
|
+
|
|
45
|
+
while time.time() - start_time < timeout_seconds:
|
|
46
|
+
try:
|
|
47
|
+
# Chrome exposes this local endpoint after remote debugging starts.
|
|
48
|
+
with urlopen(f"http://127.0.0.1:{DEBUG_PORT}/json/version", timeout=1):
|
|
49
|
+
return
|
|
50
|
+
except URLError:
|
|
51
|
+
# Chrome can take a moment to launch, so retry briefly instead of failing immediately.
|
|
52
|
+
time.sleep(0.5)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
raise RuntimeError(
|
|
56
|
+
f"Chrome did not open remote debugging port {DEBUG_PORT}. "
|
|
57
|
+
"Close Chrome and try again, or use a different debug port."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def open_colab(notebook_url: str = "", project_config: dict | None = None) -> str:
|
|
62
|
+
"""Open a saved Colab notebook, or copy the template notebook on first run."""
|
|
63
|
+
|
|
64
|
+
target_url = notebook_url or TEMPLATE_URL
|
|
65
|
+
|
|
66
|
+
# Launch real Chrome with remote debugging enabled so Playwright can attach.
|
|
67
|
+
# The custom profile lets users sign into Colab once and reuse that session.
|
|
68
|
+
subprocess.Popen([
|
|
69
|
+
str(CHROME_EXE),
|
|
70
|
+
f"--remote-debugging-port={DEBUG_PORT}",
|
|
71
|
+
f"--user-data-dir={RUNONGPU_PROFILE_DIR}",
|
|
72
|
+
"--no-first-run",
|
|
73
|
+
"--no-default-browser-check",
|
|
74
|
+
target_url,
|
|
75
|
+
])
|
|
76
|
+
|
|
77
|
+
# Avoid connecting before Chrome has opened its debugging endpoint.
|
|
78
|
+
wait_for_debug_port()
|
|
79
|
+
|
|
80
|
+
with sync_playwright() as playwright:
|
|
81
|
+
# Attach to the already-open real Chrome window instead of launching a new browser.
|
|
82
|
+
browser = playwright.chromium.connect_over_cdp(
|
|
83
|
+
f"http://127.0.0.1:{DEBUG_PORT}"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Use the active browser context and newest tab opened by RunOnGPU.
|
|
87
|
+
context = browser.contexts[0]
|
|
88
|
+
page = context.pages[-1]
|
|
89
|
+
|
|
90
|
+
if not notebook_url:
|
|
91
|
+
while True:
|
|
92
|
+
# Saving a copy usually opens a new Colab tab. expect_page captures
|
|
93
|
+
# that tab directly instead of guessing with context.pages[-1].
|
|
94
|
+
with context.expect_page() as new_page_info:
|
|
95
|
+
page.get_by_role("button", name="File", exact=True).click()
|
|
96
|
+
page.get_by_text("Save a copy in Drive").click()
|
|
97
|
+
|
|
98
|
+
# Switch automation to the copied notebook tab.
|
|
99
|
+
page = new_page_info.value
|
|
100
|
+
page.wait_for_load_state("domcontentloaded")
|
|
101
|
+
|
|
102
|
+
copied_successfully = (
|
|
103
|
+
page.url != target_url
|
|
104
|
+
and "accounts.google.com" not in page.url
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if copied_successfully:
|
|
108
|
+
break
|
|
109
|
+
|
|
110
|
+
input("Please sign into Colab, then press Enter to try again. If already signed in, press enter...")
|
|
111
|
+
|
|
112
|
+
current_url = page.url
|
|
113
|
+
|
|
114
|
+
if project_config is None:
|
|
115
|
+
raise RuntimeError("project_config was not passed into open_colab().")
|
|
116
|
+
|
|
117
|
+
# project_config contains the parsed setup/build/test/run commands from runongpu.txt.
|
|
118
|
+
console.print(f"[cyan]project_config:[/cyan] {project_config}")
|
|
119
|
+
console.print("[cyan]Writing RunOnGPU cell...[/cyan]")
|
|
120
|
+
write_runongpu_cell(page, project_config)
|
|
121
|
+
|
|
122
|
+
console.print("[green]✓ RunOnGPU cell written[/green]")
|
|
123
|
+
set_t4_gpu_and_run_all(page)
|
|
124
|
+
|
|
125
|
+
input("Colab is open. Press Enter when done...")
|
|
126
|
+
|
|
127
|
+
browser.close()
|
|
128
|
+
|
|
129
|
+
return current_url
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def write_runongpu_cell(page, project_config: dict):
|
|
133
|
+
saved_config = load_config()
|
|
134
|
+
|
|
135
|
+
# Close popups such as Gemini/Colab assistant before selecting the target cell.
|
|
136
|
+
page.keyboard.press("Escape")
|
|
137
|
+
page.wait_for_timeout(1000)
|
|
138
|
+
|
|
139
|
+
# Target a known marker in the template instead of relying on the first visible editor.
|
|
140
|
+
# This avoids accidentally pasting into Gemini or another popup editor.
|
|
141
|
+
target_cell = page.get_by_text("# Paste your code here:", exact=False).first
|
|
142
|
+
target_cell.click()
|
|
143
|
+
|
|
144
|
+
# Preserve the intended execution order from runongpu.txt:
|
|
145
|
+
# install dependencies, build the project, run tests, then run the program.
|
|
146
|
+
all_commands = (
|
|
147
|
+
project_config["setup"]
|
|
148
|
+
+ project_config["build"]
|
|
149
|
+
+ project_config["test"]
|
|
150
|
+
+ project_config["run"]
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Colab shell commands use !, so each parsed command becomes a notebook shell line.
|
|
154
|
+
command_lines = "\n".join(f"!{command}" for command in all_commands)
|
|
155
|
+
|
|
156
|
+
page.keyboard.press("Control+A")
|
|
157
|
+
|
|
158
|
+
folder_name = saved_config["folder_name"]
|
|
159
|
+
repo_url = saved_config["repo_url"]
|
|
160
|
+
|
|
161
|
+
code = f"""
|
|
162
|
+
# Paste your code here:
|
|
163
|
+
folder_name = "{folder_name}"
|
|
164
|
+
github_repo_url = "{repo_url}"
|
|
165
|
+
!rm -rf {folder_name}
|
|
166
|
+
!git clone {{github_repo_url}}
|
|
167
|
+
%cd {{folder_name}}
|
|
168
|
+
{command_lines}
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
# Clipboard paste is faster and more reliable than typing long generated cells.
|
|
172
|
+
page.evaluate("text => navigator.clipboard.writeText(text)", code)
|
|
173
|
+
page.keyboard.press("Control+V")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def set_t4_gpu_and_run_all(page) -> None:
|
|
177
|
+
# Open Colab runtime settings so the notebook uses a GPU runtime.
|
|
178
|
+
console.print("[cyan]Opening Runtime menu...[/cyan]")
|
|
179
|
+
page.get_by_role("button", name="Runtime", exact=True).click()
|
|
180
|
+
|
|
181
|
+
console.print("[cyan]Opening Change runtime type...[/cyan]")
|
|
182
|
+
page.get_by_role("menuitem", name="Change runtime type", exact=True).click()
|
|
183
|
+
|
|
184
|
+
# Select the free T4 GPU option available in Colab.
|
|
185
|
+
console.print("[cyan]Selecting T4 GPU...[/cyan]")
|
|
186
|
+
page.get_by_role("radio", name="T4 GPU").click()
|
|
187
|
+
|
|
188
|
+
console.print("[cyan]Saving runtime settings...[/cyan]")
|
|
189
|
+
page.get_by_role("button", name="Save").click()
|
|
190
|
+
|
|
191
|
+
# Give Colab time to close the runtime dialog before sending keyboard shortcuts.
|
|
192
|
+
console.print("[cyan]Waiting for runtime dialog to close...[/cyan]")
|
|
193
|
+
page.wait_for_timeout(3000)
|
|
194
|
+
page.keyboard.press("Escape")
|
|
195
|
+
|
|
196
|
+
# Start the notebook after the runtime is configured.
|
|
197
|
+
console.print("[cyan]Running all cells...[/cyan]")
|
|
198
|
+
page.keyboard.press("Control+F9")
|
|
199
|
+
console.print("[green]✓ Runtime set and cells started[/green]")
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Configuration helpers for RunOnGPU.
|
|
2
|
+
# This file manages small local settings that should persist between CLI runs,
|
|
3
|
+
# such as the GitHub repository URL and the copied Colab notebook URL.
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
console = Console()
|
|
10
|
+
|
|
11
|
+
# Store RunOnGPU settings in the user's home directory instead of the project repo.
|
|
12
|
+
# This keeps personal/local state out of Git.
|
|
13
|
+
CONFIG_DIR = Path.home() / ".runongpu"
|
|
14
|
+
|
|
15
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def save_repo_url(repo_url: str, folder_name: str) -> None:
|
|
19
|
+
CONFIG_DIR.mkdir(exist_ok=True)
|
|
20
|
+
config = {
|
|
21
|
+
"repo_url": repo_url,
|
|
22
|
+
"notebook_url": "",
|
|
23
|
+
"folder_name": folder_name
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Persist the selected project so future `runongpu run` calls know what to clone.
|
|
27
|
+
with open(CONFIG_FILE, "w", encoding="utf-8") as file:
|
|
28
|
+
json.dump(config, file, indent=4)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load_config() -> dict | None:
|
|
32
|
+
# Return None when the user has not run `runongpu init` yet.
|
|
33
|
+
if not CONFIG_FILE.exists():
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
# Convert the saved JSON config back into a Python dictionary.
|
|
37
|
+
with open(CONFIG_FILE, "r", encoding="utf-8") as file:
|
|
38
|
+
return json.load(file)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def save_notebook_url(notebook_url: str) -> None:
|
|
42
|
+
# Keep the copied Colab notebook URL so RunOnGPU reuses it instead of
|
|
43
|
+
# creating a new notebook copy every time.
|
|
44
|
+
config = load_config()
|
|
45
|
+
|
|
46
|
+
if config is None:
|
|
47
|
+
config = {}
|
|
48
|
+
|
|
49
|
+
config["notebook_url"] = notebook_url
|
|
50
|
+
|
|
51
|
+
with open(CONFIG_FILE, "w", encoding="utf-8") as file:
|
|
52
|
+
json.dump(config, file, indent=4)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def create_runongpu_template_file() -> None:
|
|
56
|
+
# Create a starter runongpu.txt in the current project folder.
|
|
57
|
+
# The file documents the command sections that RunOnGPU later parses.
|
|
58
|
+
txt_path = Path("runongpu.txt")
|
|
59
|
+
|
|
60
|
+
if not txt_path.exists():
|
|
61
|
+
|
|
62
|
+
txt_path.write_text(
|
|
63
|
+
"""# RunOnGPU Configuration
|
|
64
|
+
#
|
|
65
|
+
# Add one command per line under each section.
|
|
66
|
+
# Empty sections are allowed.
|
|
67
|
+
#
|
|
68
|
+
# Example Python project:
|
|
69
|
+
#
|
|
70
|
+
# [setup]
|
|
71
|
+
# pip install -r requirements.txt
|
|
72
|
+
#
|
|
73
|
+
# [run]
|
|
74
|
+
# python main.py
|
|
75
|
+
#
|
|
76
|
+
# Example CUDA project:
|
|
77
|
+
#
|
|
78
|
+
# [build]
|
|
79
|
+
# nvcc main.cu -o main
|
|
80
|
+
#
|
|
81
|
+
# [run]
|
|
82
|
+
# ./main
|
|
83
|
+
#
|
|
84
|
+
# Example CMake project:
|
|
85
|
+
#
|
|
86
|
+
# [build]
|
|
87
|
+
# cmake -S . -B build
|
|
88
|
+
# cmake --build build
|
|
89
|
+
#
|
|
90
|
+
# [run]
|
|
91
|
+
# ./build/my_program
|
|
92
|
+
|
|
93
|
+
[setup]
|
|
94
|
+
|
|
95
|
+
[build]
|
|
96
|
+
|
|
97
|
+
[test]
|
|
98
|
+
|
|
99
|
+
[run]
|
|
100
|
+
""",
|
|
101
|
+
encoding="utf-8",
|
|
102
|
+
)
|
|
103
|
+
console.print("[green]✓ Added runongpu.txt to the repo. [/green]")
|
|
104
|
+
else:
|
|
105
|
+
console.print("[yellow]runongpu.txt already exists. Good job! Proud of you! [/yellow]")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_folder_name_from_repo_url(repo_url: str) -> str:
|
|
109
|
+
# Git clones repositories into a folder named after the repo, so derive that
|
|
110
|
+
# folder name from the URL instead of asking the user to type it manually.
|
|
111
|
+
return repo_url.rstrip("/").split("/")[-1].replace(".git", "")
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def parse_config() -> dict:
|
|
5
|
+
# Each section maps to the list of commands RunOnGPU should run in Colab.
|
|
6
|
+
config = {
|
|
7
|
+
"setup": [],
|
|
8
|
+
"build": [],
|
|
9
|
+
"test": [],
|
|
10
|
+
"run": [],
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
current_section = None
|
|
14
|
+
|
|
15
|
+
# Path("runongpu.txt") looks for the file in the current terminal directory.
|
|
16
|
+
# Users should run RunOnGPU from the project folder that contains runongpu.txt.
|
|
17
|
+
txt_path = Path("runongpu.txt")
|
|
18
|
+
|
|
19
|
+
for line in txt_path.read_text(encoding="utf-8").splitlines():
|
|
20
|
+
line = line.strip()
|
|
21
|
+
|
|
22
|
+
# Ignore spacing and documentation inside runongpu.txt.
|
|
23
|
+
if not line:
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
if line.startswith("#"):
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
# Section headers decide where the following commands should be stored.
|
|
30
|
+
# Example: [build] makes current_section equal to "build".
|
|
31
|
+
if line.startswith("[") and line.endswith("]"):
|
|
32
|
+
section = line[1:-1].lower()
|
|
33
|
+
|
|
34
|
+
if section not in config:
|
|
35
|
+
raise ValueError(f"Unknown section: {section}")
|
|
36
|
+
|
|
37
|
+
current_section = section
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
# Commands must appear under a valid section so RunOnGPU knows when to run them.
|
|
41
|
+
if current_section is None:
|
|
42
|
+
raise ValueError("Command found before any section header.")
|
|
43
|
+
|
|
44
|
+
config[current_section].append(line)
|
|
45
|
+
|
|
46
|
+
return config
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: runongpu
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool that runs GitHub projects on free cloud GPUs
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: typer
|
|
9
|
+
Requires-Dist: rich
|
|
10
|
+
Requires-Dist: playwright
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# RunOnGPU
|
|
14
|
+
|
|
15
|
+
RunOnGPU is a CLI tool that helps you run GitHub projects on a GPU with minimal setup.
|
|
16
|
+
|
|
17
|
+
It is useful if you want to test CUDA, PyTorch, or other GPU code but do not have a local NVIDIA GPU.
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
* Windows
|
|
22
|
+
* Python 3.10+
|
|
23
|
+
* Git
|
|
24
|
+
* Google Chrome
|
|
25
|
+
* Google account for Colab
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install runongpu
|
|
31
|
+
python -m playwright install
|
|
32
|
+
|
|
33
|
+
Check setup:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
runongpu doctor
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
Go to the project you want to run and initialize RunOnGPU:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
runongpu init
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Enter your GitHub repo URL when asked.
|
|
48
|
+
|
|
49
|
+
This creates a `runongpu.txt` file. Edit this file to tell RunOnGPU how to set up, build, test, and run your project.
|
|
50
|
+
|
|
51
|
+
Then run:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
runongpu run
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
RunOnGPU will open Colab, copy the starter notebook, clone your repo, set the runtime to a T4 GPU, and run your commands.
|
|
58
|
+
|
|
59
|
+
## runongpu.txt
|
|
60
|
+
|
|
61
|
+
`runongpu.txt` controls what happens inside Colab.
|
|
62
|
+
|
|
63
|
+
It has four sections:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
[setup]
|
|
67
|
+
# install dependencies here
|
|
68
|
+
|
|
69
|
+
[build]
|
|
70
|
+
# compile or build the project here
|
|
71
|
+
|
|
72
|
+
[test]
|
|
73
|
+
# run tests here
|
|
74
|
+
|
|
75
|
+
[run]
|
|
76
|
+
# run the final program here
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Add one command per line.
|
|
80
|
+
|
|
81
|
+
## Example: CUDA
|
|
82
|
+
|
|
83
|
+
If your repo has this structure:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
my-cuda-project/
|
|
87
|
+
├── main.cu
|
|
88
|
+
└── runongpu.txt
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
[setup]
|
|
95
|
+
|
|
96
|
+
[build]
|
|
97
|
+
nvcc main.cu -o vector_add
|
|
98
|
+
|
|
99
|
+
[test]
|
|
100
|
+
|
|
101
|
+
[run]
|
|
102
|
+
./vector_add
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Example: CUDA in a Subfolder
|
|
106
|
+
|
|
107
|
+
If your repo has this structure:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
runongpu-examples/
|
|
111
|
+
├── cuda/
|
|
112
|
+
│ └── vector-add/
|
|
113
|
+
│ └── main.cu
|
|
114
|
+
└── runongpu.txt
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Use:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
[setup]
|
|
121
|
+
|
|
122
|
+
[build]
|
|
123
|
+
cd cuda/vector-add && nvcc main.cu -o vector_add
|
|
124
|
+
|
|
125
|
+
[test]
|
|
126
|
+
|
|
127
|
+
[run]
|
|
128
|
+
cd cuda/vector-add && ./vector_add
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Example: Python
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
[setup]
|
|
135
|
+
pip install -r requirements.txt
|
|
136
|
+
|
|
137
|
+
[build]
|
|
138
|
+
|
|
139
|
+
[test]
|
|
140
|
+
pytest
|
|
141
|
+
|
|
142
|
+
[run]
|
|
143
|
+
python main.py
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Example: CMake
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
[setup]
|
|
150
|
+
|
|
151
|
+
[build]
|
|
152
|
+
cmake -S . -B build
|
|
153
|
+
cmake --build build
|
|
154
|
+
|
|
155
|
+
[test]
|
|
156
|
+
ctest --test-dir build --output-on-failure
|
|
157
|
+
|
|
158
|
+
[run]
|
|
159
|
+
./build/my_program
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Notes
|
|
163
|
+
|
|
164
|
+
On the first run, you may need to sign into Google Colab. RunOnGPU saves the copied notebook URL and reuses it on future runs.
|
|
165
|
+
|
|
166
|
+
Do not interact with the Colab window while RunOnGPU is setting it up.
|
|
167
|
+
|
|
168
|
+
## Run Tests
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
python -m pytest
|
|
172
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
runongpu/cli.py
|
|
5
|
+
runongpu/colab.py
|
|
6
|
+
runongpu/config.py
|
|
7
|
+
runongpu/parser.py
|
|
8
|
+
runongpu.egg-info/PKG-INFO
|
|
9
|
+
runongpu.egg-info/SOURCES.txt
|
|
10
|
+
runongpu.egg-info/dependency_links.txt
|
|
11
|
+
runongpu.egg-info/entry_points.txt
|
|
12
|
+
runongpu.egg-info/requires.txt
|
|
13
|
+
runongpu.egg-info/top_level.txt
|
|
14
|
+
tests/test_config.py
|
|
15
|
+
tests/test_config_persistance.py
|
|
16
|
+
tests/test_parser.py
|
|
17
|
+
tests/test_template.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
runongpu
|
runongpu-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from runongpu.config import get_folder_name_from_repo_url
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_get_folder_name_from_https_repo_url():
|
|
5
|
+
# Git clones this URL into a local folder named "RunOnGPU".
|
|
6
|
+
result = get_folder_name_from_repo_url(
|
|
7
|
+
"https://github.com/MashrafeeAryan/RunOnGPU.git"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
assert result == "RunOnGPU"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_get_folder_name_from_url_without_git_suffix():
|
|
14
|
+
# GitHub URLs may be copied without ".git", so both formats should work.
|
|
15
|
+
result = get_folder_name_from_repo_url(
|
|
16
|
+
"https://github.com/MashrafeeAryan/runongpu-examples"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
assert result == "runongpu-examples"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_get_folder_name_from_url_with_trailing_slash():
|
|
23
|
+
# Trailing slashes should not create an empty folder name.
|
|
24
|
+
result = get_folder_name_from_repo_url(
|
|
25
|
+
"https://github.com/MashrafeeAryan/runongpu-examples/"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
assert result == "runongpu-examples"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import runongpu.config as config_module
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_save_and_load_repo_config(tmp_path, monkeypatch):
|
|
5
|
+
# Store config in a temp folder so this test never touches the user's real ~/.runongpu.
|
|
6
|
+
fake_config_dir = tmp_path / ".runongpu"
|
|
7
|
+
fake_config_file = fake_config_dir / "config.json"
|
|
8
|
+
|
|
9
|
+
monkeypatch.setattr(config_module, "CONFIG_DIR", fake_config_dir)
|
|
10
|
+
monkeypatch.setattr(config_module, "CONFIG_FILE", fake_config_file)
|
|
11
|
+
|
|
12
|
+
config_module.save_repo_url(
|
|
13
|
+
"https://github.com/MashrafeeAryan/RunOnGPU.git",
|
|
14
|
+
"RunOnGPU",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
result = config_module.load_config()
|
|
18
|
+
|
|
19
|
+
assert result["repo_url"] == "https://github.com/MashrafeeAryan/RunOnGPU.git"
|
|
20
|
+
assert result["folder_name"] == "RunOnGPU"
|
|
21
|
+
assert result["notebook_url"] == ""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_save_notebook_url_updates_existing_config(tmp_path, monkeypatch):
|
|
25
|
+
# Notebook URL should be saved so future runs reuse the same Colab notebook.
|
|
26
|
+
fake_config_dir = tmp_path / ".runongpu"
|
|
27
|
+
fake_config_file = fake_config_dir / "config.json"
|
|
28
|
+
|
|
29
|
+
monkeypatch.setattr(config_module, "CONFIG_DIR", fake_config_dir)
|
|
30
|
+
monkeypatch.setattr(config_module, "CONFIG_FILE", fake_config_file)
|
|
31
|
+
|
|
32
|
+
config_module.save_repo_url(
|
|
33
|
+
"https://github.com/MashrafeeAryan/RunOnGPU.git",
|
|
34
|
+
"RunOnGPU",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
config_module.save_notebook_url("https://colab.research.google.com/drive/test")
|
|
38
|
+
|
|
39
|
+
result = config_module.load_config()
|
|
40
|
+
|
|
41
|
+
assert result["repo_url"] == "https://github.com/MashrafeeAryan/RunOnGPU.git"
|
|
42
|
+
assert result["folder_name"] == "RunOnGPU"
|
|
43
|
+
assert result["notebook_url"] == "https://colab.research.google.com/drive/test"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from runongpu.parser import parse_config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_parse_valid_config(tmp_path, monkeypatch):
|
|
7
|
+
config_file = tmp_path / "runongpu.txt"
|
|
8
|
+
config_file.write_text(
|
|
9
|
+
"""
|
|
10
|
+
# RunOnGPU config
|
|
11
|
+
|
|
12
|
+
[setup]
|
|
13
|
+
pip install -r requirements.txt
|
|
14
|
+
|
|
15
|
+
[build]
|
|
16
|
+
nvcc main.cu -o vector_add
|
|
17
|
+
|
|
18
|
+
[test]
|
|
19
|
+
pytest
|
|
20
|
+
|
|
21
|
+
[run]
|
|
22
|
+
./vector_add
|
|
23
|
+
""",
|
|
24
|
+
encoding="utf-8",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
monkeypatch.chdir(tmp_path)
|
|
28
|
+
|
|
29
|
+
result = parse_config()
|
|
30
|
+
|
|
31
|
+
assert result == {
|
|
32
|
+
"setup": ["pip install -r requirements.txt"],
|
|
33
|
+
"build": ["nvcc main.cu -o vector_add"],
|
|
34
|
+
"test": ["pytest"],
|
|
35
|
+
"run": ["./vector_add"],
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_parse_empty_sections(tmp_path, monkeypatch):
|
|
40
|
+
config_file = tmp_path / "runongpu.txt"
|
|
41
|
+
config_file.write_text(
|
|
42
|
+
"""
|
|
43
|
+
[setup]
|
|
44
|
+
|
|
45
|
+
[build]
|
|
46
|
+
|
|
47
|
+
[test]
|
|
48
|
+
|
|
49
|
+
[run]
|
|
50
|
+
""",
|
|
51
|
+
encoding="utf-8",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
monkeypatch.chdir(tmp_path)
|
|
55
|
+
|
|
56
|
+
assert parse_config() == {
|
|
57
|
+
"setup": [],
|
|
58
|
+
"build": [],
|
|
59
|
+
"test": [],
|
|
60
|
+
"run": [],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_unknown_section_raises_error(tmp_path, monkeypatch):
|
|
65
|
+
config_file = tmp_path / "runongpu.txt"
|
|
66
|
+
config_file.write_text(
|
|
67
|
+
"""
|
|
68
|
+
[install]
|
|
69
|
+
pip install numpy
|
|
70
|
+
""",
|
|
71
|
+
encoding="utf-8",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
monkeypatch.chdir(tmp_path)
|
|
75
|
+
|
|
76
|
+
with pytest.raises(ValueError, match="Unknown section"):
|
|
77
|
+
parse_config()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_command_before_section_raises_error(tmp_path, monkeypatch):
|
|
81
|
+
config_file = tmp_path / "runongpu.txt"
|
|
82
|
+
config_file.write_text(
|
|
83
|
+
"""
|
|
84
|
+
pip install numpy
|
|
85
|
+
|
|
86
|
+
[run]
|
|
87
|
+
python main.py
|
|
88
|
+
""",
|
|
89
|
+
encoding="utf-8",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
monkeypatch.chdir(tmp_path)
|
|
93
|
+
|
|
94
|
+
with pytest.raises(ValueError, match="Command found before any section header"):
|
|
95
|
+
parse_config()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from runongpu.config import create_runongpu_template_file
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_create_runongpu_template_file(tmp_path, monkeypatch):
|
|
7
|
+
# Run the test inside a temporary folder so we do not touch the real repo.
|
|
8
|
+
monkeypatch.chdir(tmp_path)
|
|
9
|
+
|
|
10
|
+
create_runongpu_template_file()
|
|
11
|
+
|
|
12
|
+
template_file = Path("runongpu.txt")
|
|
13
|
+
|
|
14
|
+
assert template_file.exists()
|
|
15
|
+
|
|
16
|
+
contents = template_file.read_text(encoding="utf-8")
|
|
17
|
+
|
|
18
|
+
# The generated template should include all sections the parser understands.
|
|
19
|
+
assert "[setup]" in contents
|
|
20
|
+
assert "[build]" in contents
|
|
21
|
+
assert "[test]" in contents
|
|
22
|
+
assert "[run]" in contents
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_create_runongpu_template_file_does_not_overwrite_existing_file(tmp_path, monkeypatch):
|
|
26
|
+
# Existing user configs should be preserved instead of overwritten.
|
|
27
|
+
monkeypatch.chdir(tmp_path)
|
|
28
|
+
|
|
29
|
+
template_file = Path("runongpu.txt")
|
|
30
|
+
template_file.write_text("[run]\npython main.py\n", encoding="utf-8")
|
|
31
|
+
|
|
32
|
+
create_runongpu_template_file()
|
|
33
|
+
|
|
34
|
+
assert template_file.read_text(encoding="utf-8") == "[run]\npython main.py\n"
|