Skip to content

Commit 8f883ee

Browse files
authored
fix: always bundle runpod-flash in flash build + versioning report (#227)
* feat(build): always bundle runpod_flash, remove --use-local-flash runpod_flash is now unconditionally bundled from site-packages during every build, making --use-local-flash unnecessary. Adds __version__ constant to __init__.py so bundled code carries its own version for accurate runtime reporting in deployed workers. - Rename copy functions to reflect site-packages source - Remove --use-local-flash from deploy command and docs - Add tests for unconditional bundling behavior - Update dotenv test to allow __version__ before imports * chore(version): automate __version__ sync via release-please extra-files Added x-release-please-version annotation to __init__.py and configured extra-files in release-please-config.json so __version__ is bumped automatically on each release. * fix(user-agent): use __version__ instead of importlib.metadata importlib.metadata reads the pip-installed version, which differs from the bundled version inside worker containers. Using __version__ ensures the user-agent reports the actual running version. * fix(user-agent): defer __version__ import to avoid circular imports * fix(build): normalize package name matching to lowercase per PEP 503 Package names are case-insensitive per PEP 503, so entries like `Runpod-Flash` or `RUNPOD_FLASH` in requirements.txt would survive filtering and reintroduce the duplicated-install conflict. * fix(test): scope ExitStack patches with @contextmanager in _bundle_patches Patches were entered immediately on ExitStack before the `with` block started, risking leaks if an exception occurred during setup. Wrapping in @contextmanager ensures patch lifetime is strictly scoped.
1 parent 6e1f304 commit 8f883ee

File tree

10 files changed

+284
-83
lines changed

10 files changed

+284
-83
lines changed

release-please-config.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
{"type": "ci", "section": "Continuous Integration", "hidden": true},
1717
{"type": "chore", "section": "Miscellaneous", "hidden": true}
1818
],
19-
"pull-request-title-pattern": "chore: release ${version}"
19+
"pull-request-title-pattern": "chore: release ${version}",
20+
"extra-files": [
21+
"src/runpod_flash/__init__.py"
22+
]
2023
}
2124
}
2225
}

src/runpod_flash/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
__version__ = "1.4.1" # x-release-please-version
2+
13
# Load .env vars from file before everything else
24
from dotenv import load_dotenv
35

src/runpod_flash/cli/commands/build.py

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -56,50 +56,56 @@
5656
PIP_MODULE = "pip"
5757

5858

59-
def _find_local_runpod_flash() -> Optional[Path]:
60-
"""Find local runpod_flash source directory if available.
59+
def _find_runpod_flash(project_dir: Optional[Path] = None) -> Optional[Path]:
60+
"""Find installed runpod_flash package directory.
61+
62+
Tries two strategies:
63+
1. importlib.util.find_spec -- works for any installed runpod_flash
64+
(dev-installed or site-packages)
65+
2. Relative path search -- walks upward from project_dir looking for a sibling
66+
flash repo (worktree or standard layout)
67+
68+
Args:
69+
project_dir: Flash project directory, used for relative path search fallback
6170
6271
Returns:
63-
Path to runpod_flash package directory, or None if not found or installed from PyPI
72+
Path to runpod_flash package directory, or None if not found
6473
"""
74+
# Strategy 1: importlib (any installed runpod_flash -- dev or site-packages)
6575
try:
6676
spec = importlib.util.find_spec("runpod_flash")
77+
if spec and spec.origin:
78+
return Path(spec.origin).parent
79+
except Exception:
80+
pass
6781

68-
if not spec or not spec.origin:
69-
return None
70-
71-
# Get package directory (spec.origin is __init__.py path)
72-
pkg_dir = Path(spec.origin).parent
73-
74-
# Skip if installed in site-packages (PyPI install)
75-
if "site-packages" in str(pkg_dir):
76-
return None
82+
# Strategy 2: search upward from project_dir for flash repo
83+
if project_dir is None:
84+
return None
7785

78-
# Must be development install
79-
return pkg_dir
86+
current = project_dir.resolve()
87+
for _ in range(6):
88+
# Worktree layout: flash-project/flash/main/src/runpod_flash/
89+
# Standard layout: flash-project/flash/src/runpod_flash/
90+
for sub in ("flash/main/src/runpod_flash", "flash/src/runpod_flash"):
91+
candidate = current / sub
92+
if (candidate / "__init__.py").is_file():
93+
return candidate
94+
parent = current.parent
95+
if parent == current:
96+
break
97+
current = parent
8098

81-
except Exception:
82-
return None
99+
return None
83100

84101

85-
def _bundle_local_runpod_flash(build_dir: Path) -> bool:
86-
"""Copy local runpod_flash source into build directory.
102+
def _bundle_runpod_flash(build_dir: Path, flash_pkg: Path) -> None:
103+
"""Copy runpod_flash source into build directory.
87104
88105
Args:
89106
build_dir: Target build directory
90-
91-
Returns:
92-
True if bundled successfully, False otherwise
107+
flash_pkg: Path to the runpod_flash package directory to bundle
93108
"""
94-
flash_pkg = _find_local_runpod_flash()
95-
96-
if not flash_pkg:
97-
console.print(
98-
"[yellow]⚠ Local runpod_flash not found or using PyPI install[/yellow]"
99-
)
100-
return False
101-
102-
# Copy runpod_flash to build
103109
dest = build_dir / "runpod_flash"
104110
if dest.exists():
105111
shutil.rmtree(dest)
@@ -110,8 +116,7 @@ def _bundle_local_runpod_flash(build_dir: Path) -> bool:
110116
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", ".pytest_cache"),
111117
)
112118

113-
console.print(f"[cyan]✓ Bundled local runpod_flash from {flash_pkg}[/cyan]")
114-
return True
119+
console.print(f"[cyan]Bundled runpod_flash from {flash_pkg}[/cyan]")
115120

116121

117122
def _extract_runpod_flash_dependencies(flash_pkg_dir: Path) -> list[str]:
@@ -171,8 +176,8 @@ def _remove_runpod_flash_from_requirements(build_dir: Path) -> None:
171176
filtered = [
172177
line
173178
for line in lines
174-
if not line.strip().startswith("runpod_flash")
175-
and not line.strip().startswith("runpod-flash")
179+
if not line.strip().lower().startswith("runpod_flash")
180+
and not line.strip().lower().startswith("runpod-flash")
176181
]
177182

178183
req_file.write_text("\n".join(filtered) + "\n")
@@ -190,12 +195,12 @@ def run_build(
190195
no_deps: bool = False,
191196
output_name: str | None = None,
192197
exclude: str | None = None,
193-
use_local_flash: bool = False,
194198
verbose: bool = False,
195199
) -> Path:
196200
"""Run the build process and return the artifact path.
197201
198202
Contains all build steps: validate, collect files, manifest, deps, tarball.
203+
Always bundles the runpod_flash installed in the current environment.
199204
Always keeps the build directory — caller decides cleanup.
200205
201206
Args:
@@ -204,7 +209,6 @@ def run_build(
204209
no_deps: Skip transitive dependencies during pip install
205210
output_name: Custom archive name (default: artifact.tar.gz)
206211
exclude: Comma-separated packages to exclude
207-
use_local_flash: Bundle local runpod_flash source
208212
verbose: Show archive and build directory paths in summary
209213
210214
Returns:
@@ -309,13 +313,19 @@ def run_build(
309313
console.print("[red]Error:[/red] Failed to install dependencies")
310314
raise typer.Exit(1)
311315

312-
# bundle local runpod_flash if requested
313-
if use_local_flash:
314-
if _bundle_local_runpod_flash(build_dir):
315-
_remove_runpod_flash_from_requirements(build_dir)
316+
# Always bundle the installed runpod_flash
317+
flash_pkg = _find_runpod_flash(project_dir)
318+
if not flash_pkg:
319+
console.print(
320+
"[red]Error:[/red] Could not find runpod_flash.\n"
321+
" Ensure runpod-flash is installed: pip install runpod-flash"
322+
)
323+
raise typer.Exit(1)
324+
_bundle_runpod_flash(build_dir, flash_pkg)
325+
_remove_runpod_flash_from_requirements(build_dir)
316326

317327
# Generate _flash_resource_config.py for @remote local-vs-stub dispatch.
318-
# Must happen AFTER _bundle_local_runpod_flash which replaces build_dir/runpod_flash/.
328+
# Must happen AFTER _bundle_runpod_flash which replaces build_dir/runpod_flash/.
319329
manifest_json_path = build_dir / "flash_manifest.json"
320330
if manifest_json_path.exists():
321331
manifest_data = json.loads(manifest_json_path.read_text())
@@ -371,11 +381,6 @@ def build_command(
371381
"--exclude",
372382
help="Comma-separated packages to exclude (e.g., 'torch,torchvision')",
373383
),
374-
use_local_flash: bool = typer.Option(
375-
False,
376-
"--use-local-flash",
377-
help="Bundle local runpod_flash source instead of PyPI version (for development/testing)",
378-
),
379384
):
380385
"""
381386
Build Flash application for debugging (build only, no deploy).
@@ -398,7 +403,6 @@ def build_command(
398403
no_deps=no_deps,
399404
output_name=output_name,
400405
exclude=exclude,
401-
use_local_flash=use_local_flash,
402406
verbose=True,
403407
)
404408

src/runpod_flash/cli/commands/deploy.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,6 @@ def deploy_command(
3333
"--exclude",
3434
help="Comma-separated packages to exclude (e.g., 'torch,torchvision')",
3535
),
36-
use_local_flash: bool = typer.Option(
37-
False,
38-
"--use-local-flash",
39-
help="Bundle local runpod_flash source instead of PyPI version (for development/testing)",
40-
),
4136
output_name: str | None = typer.Option(
4237
None, "--output", "-o", help="Custom archive name (default: artifact.tar.gz)"
4338
),
@@ -71,7 +66,6 @@ def deploy_command(
7166
no_deps=no_deps,
7267
output_name=output_name,
7368
exclude=exclude,
74-
use_local_flash=use_local_flash,
7569
)
7670

7771
if preview:

src/runpod_flash/cli/docs/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ flash deploy [OPTIONS]
105105
- `--app, -a`: Flash app name
106106
- `--no-deps`: Skip transitive dependencies during pip install
107107
- `--exclude`: Comma-separated packages to exclude (e.g., 'torch,torchvision')
108-
- `--use-local-flash`: Bundle local runpod_flash source (for development)
109108
- `--output, -o`: Custom archive name (default: artifact.tar.gz)
110109
- `--preview`: Build and launch local preview instead of deploying
111110

src/runpod_flash/cli/docs/flash-deploy.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ flash deploy [OPTIONS]
8686
- `--app, -a`: Flash app name (auto-detected from current directory)
8787
- `--no-deps`: Skip transitive dependencies during pip install (default: false)
8888
- `--exclude`: Comma-separated packages to exclude (e.g., 'torch,torchvision')
89-
- `--use-local-flash`: Bundle local runpod_flash source instead of PyPI version (for development/testing)
9089
- `--output, -o`: Custom archive name (default: artifact.tar.gz)
9190
- `--preview`: Build and launch local preview environment instead of deploying
9291

@@ -162,14 +161,6 @@ flash deploy --exclude torch,torchvision,torchaudio
162161

163162
Skips specified packages during dependency installation. Critical for staying under Runpod's 500MB deployment limit. See [flash build](./flash-build.md#managing-deployment-size) for base image package reference.
164163

165-
### Local Flash Development
166-
167-
```bash
168-
flash deploy --use-local-flash
169-
```
170-
171-
Bundles your local `runpod_flash` source instead of the PyPI version. Only use this for development and testing.
172-
173164
## Preview Mode
174165

175166
```bash
Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""User-Agent header generation for HTTP requests."""
22

33
import platform
4-
from importlib.metadata import version
54

65

76
def get_user_agent() -> str:
@@ -12,16 +11,13 @@ def get_user_agent() -> str:
1211
1312
Example:
1413
>>> get_user_agent()
15-
'Runpod Flash/1.1.1 (Python 3.11.12; Darwin 25.2.0; arm64)'
14+
'Runpod Flash/1.4.1 (Python 3.11.12; Darwin 25.2.0; arm64)'
1615
"""
17-
try:
18-
pkg_version = version("runpod-flash")
19-
except Exception:
20-
pkg_version = "unknown"
16+
from runpod_flash import __version__
2117

2218
python_version = platform.python_version()
2319
os_name = platform.system()
2420
os_version = platform.release()
2521
arch = platform.machine()
2622

27-
return f"Runpod Flash/{pkg_version} (Python {python_version}; {os_name} {os_version}; {arch})"
23+
return f"Runpod Flash/{__version__} (Python {python_version}; {os_name} {os_version}; {arch})"

0 commit comments

Comments
 (0)