dockermind 0.1.2__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.
- dockermind-0.1.2/PKG-INFO +119 -0
- dockermind-0.1.2/README.md +105 -0
- dockermind-0.1.2/dockermind/__init__.py +5 -0
- dockermind-0.1.2/dockermind/analyzer.py +285 -0
- dockermind-0.1.2/dockermind/cli.py +288 -0
- dockermind-0.1.2/dockermind/compose_generator.py +43 -0
- dockermind-0.1.2/dockermind/dockerfile_generator.py +389 -0
- dockermind-0.1.2/dockermind/doctor.py +88 -0
- dockermind-0.1.2/dockermind/runner.py +139 -0
- dockermind-0.1.2/dockermind/utils.py +141 -0
- dockermind-0.1.2/dockermind.egg-info/PKG-INFO +119 -0
- dockermind-0.1.2/dockermind.egg-info/SOURCES.txt +16 -0
- dockermind-0.1.2/dockermind.egg-info/dependency_links.txt +1 -0
- dockermind-0.1.2/dockermind.egg-info/entry_points.txt +2 -0
- dockermind-0.1.2/dockermind.egg-info/requires.txt +7 -0
- dockermind-0.1.2/dockermind.egg-info/top_level.txt +1 -0
- dockermind-0.1.2/pyproject.toml +37 -0
- dockermind-0.1.2/setup.cfg +4 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dockermind
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Smart Dockerfile generation CLI for Node.js and Python projects
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: typer>=0.9.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
12
|
+
Requires-Dist: ruff>=0.1; extra == "dev"
|
|
13
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
14
|
+
|
|
15
|
+
# Dockermind
|
|
16
|
+
|
|
17
|
+
> Smart Dockerfile generation CLI for Node.js and Python projects.
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
Dockermind analyses a project (Node.js or Python), infers the runtime and framework, and generates production-optimised Dockerfile and .dockerignore (optionally a docker-compose.yml). It also provides a small CLI to build and run the resulting image and a doctor command to validate the local Docker environment.
|
|
22
|
+
|
|
23
|
+
This repository contains a Next.js frontend (in the `app/` and `components/` folders) and the `dockermind` Python package that implements analysis and generation logic.
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- Python 3.10 or newer (the package declares `requires-python = ">=3.10"`)
|
|
28
|
+
- Docker (for `dockermind build`, `dockermind run` and to validate with `dockermind doctor`)
|
|
29
|
+
- Node.js (for the Next.js frontend; development and build use the `next` CLI)
|
|
30
|
+
|
|
31
|
+
When running commands below, ensure you are in the repository root.
|
|
32
|
+
|
|
33
|
+
## Node / Frontend commands (npm scripts)
|
|
34
|
+
|
|
35
|
+
The repository defines the following npm scripts in `package.json`. These are the standard Next.js scripts and an ESLint target.
|
|
36
|
+
|
|
37
|
+
- `npm run dev` (alias: `pnpm dev` / `yarn dev`) — Runs `next dev`.
|
|
38
|
+
- What it does: starts the Next.js development server. The server serves the app in development mode, enables fast refresh, and rebuilds pages on change. Default listening port is 3000 unless overridden by environment variables.
|
|
39
|
+
|
|
40
|
+
- `npm run build` — Runs `next build`.
|
|
41
|
+
- What it does: produces a production build for Next.js, compiling pages and assets into the `.next` directory. Run this before `npm run start` for production usage.
|
|
42
|
+
|
|
43
|
+
- `npm run start` — Runs `next start`.
|
|
44
|
+
- What it does: launches the Next.js production server that serves the contents of the `.next` build directory. Requires a successful `npm run build` beforehand.
|
|
45
|
+
|
|
46
|
+
- `npm run lint` — Runs `eslint .`.
|
|
47
|
+
- What it does: runs ESLint across the repository (the configuration and rules are determined by the repo). Use this to detect and fix lint issues.
|
|
48
|
+
|
|
49
|
+
Notes:
|
|
50
|
+
|
|
51
|
+
- Use your preferred package manager. The repository contains a `pnpm-lock.yaml` file; if you use pnpm prefer `pnpm install`. Otherwise `npm ci` or `npm install` will work for npm users.
|
|
52
|
+
|
|
53
|
+
## Python / Dockermind CLI
|
|
54
|
+
|
|
55
|
+
The Python package is configured in `pyproject.toml`. It exposes a console entry point named `dockermind` (see `[project.scripts]`). After installing the package, the `dockermind` command will be available.
|
|
56
|
+
|
|
57
|
+
Installation (editable/development install):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
python -m pip install -e .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
What that does: installs the package in editable mode and registers the `dockermind` CLI entry point. You can then run the CLI from the repository root.
|
|
64
|
+
|
|
65
|
+
Available CLI commands (usage: `dockermind <command> [options]`):
|
|
66
|
+
|
|
67
|
+
- `dockermind init [path] [--compose] [--dry-run] [--force]`
|
|
68
|
+
- What it does: Analyses the project at `path` (defaults to `.`) to detect whether the project is Node.js or Python, determines framework, ports, and build/start steps, then generates a Dockerfile and `.dockerignore`. If `--compose` is specified it also generates a `docker-compose.yml`. `--dry-run` prints generated files to the console without writing them. Without `--force`, the command will prompt before overwriting an existing Dockerfile.
|
|
69
|
+
|
|
70
|
+
- `dockermind build [path] [--tag <tag>]`
|
|
71
|
+
- What it does: Runs `docker build` using the `Dockerfile` in `path` (defaults to `.`). The `--tag` (`-t`) option sets the image name (default `dockermind-app`). The command streams `docker build` output to the terminal and returns a non-zero exit code on failure.
|
|
72
|
+
|
|
73
|
+
- `dockermind run [--tag <tag>] [--port <port>] [--detach]`
|
|
74
|
+
- What it does: Runs a Docker container from an image built by `dockermind build`. The command maps host port to container port as `port:port` and runs the container in foreground by default. Use `--detach` (`-d`) to run in background. The command checks if the selected port appears in use and warns if so.
|
|
75
|
+
|
|
76
|
+
- `dockermind doctor`
|
|
77
|
+
- What it does: Verifies that Docker is installed and that the Docker daemon is running. Also checks for Docker Compose and prints helpful messages and next steps. Returns a non-zero exit code if checks fail.
|
|
78
|
+
|
|
79
|
+
- `dockermind version`
|
|
80
|
+
- What it does: Prints the installed dockermind package version.
|
|
81
|
+
|
|
82
|
+
You can also invoke the CLI directly (without installing) with:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
python -m dockermind.cli init --dry-run
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This runs the `init` command using the local source tree without installing the package.
|
|
89
|
+
|
|
90
|
+
## Test and helper scripts
|
|
91
|
+
|
|
92
|
+
- `scripts/run_tests.py` — Self-contained smoke tests.
|
|
93
|
+
- What it does: creates a temporary copy of the `dockermind` package inside `/tmp`, imports it and runs a suite of smoke tests that exercise analysis, Dockerfile generation and compose generation for representative Node.js and Python fixtures. The script prints pass/fail results and exits with non-zero status if any tests fail. It is intended as an on-host smoke test and may assume a POSIX environment (it uses `/tmp`).
|
|
94
|
+
|
|
95
|
+
- `scripts/run_tests.sh` — Simple shell wrapper for tests.
|
|
96
|
+
- What it does: installs minimal Python dependencies if needed and then runs the repo test harness under a specific path used by some CI environments.
|
|
97
|
+
|
|
98
|
+
Notes about tests: the tests in `scripts/run_tests.py` are not a replacement for a full test suite. They are smoke tests that exercise the core behaviour of the analysis and generation modules.
|
|
99
|
+
|
|
100
|
+
## Docker usage notes
|
|
101
|
+
|
|
102
|
+
- Generated Dockerfiles attempt to create small, production-ready images. For Node.js projects the generator may use multi-stage builds when a build step is present. For Python projects it will attempt to use `uvicorn` for FastAPI projects or `flask` for Flask projects and will add minimal system packages when native dependencies are detected.
|
|
103
|
+
- The `dockermind run` and `dockermind build` commands rely on the local Docker CLI and daemon.
|
|
104
|
+
|
|
105
|
+
## Contributing & Development
|
|
106
|
+
|
|
107
|
+
- Install Python development dependencies from `pyproject.toml` for tests and linters (e.g. `pytest`, `ruff`, `mypy`).
|
|
108
|
+
- Follow the linting and type checks present in the repo. Use `npm run lint` for JavaScript/TypeScript code quality checks.
|
|
109
|
+
|
|
110
|
+
## Developer Contact
|
|
111
|
+
|
|
112
|
+
- GitHub: https://github.com/Aakashsingh0388
|
|
113
|
+
- LinkedIn: https://www.linkedin.com/in/aakash-singh-7b8416318/
|
|
114
|
+
|
|
115
|
+
If you need further details about the project or help building images, please open an issue on GitHub or contact the developer directly via LinkedIn.
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
This project is provided under the MIT License (see `dockermind.egg-info` for packaged metadata).
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Dockermind
|
|
2
|
+
|
|
3
|
+
> Smart Dockerfile generation CLI for Node.js and Python projects.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Dockermind analyses a project (Node.js or Python), infers the runtime and framework, and generates production-optimised Dockerfile and .dockerignore (optionally a docker-compose.yml). It also provides a small CLI to build and run the resulting image and a doctor command to validate the local Docker environment.
|
|
8
|
+
|
|
9
|
+
This repository contains a Next.js frontend (in the `app/` and `components/` folders) and the `dockermind` Python package that implements analysis and generation logic.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- Python 3.10 or newer (the package declares `requires-python = ">=3.10"`)
|
|
14
|
+
- Docker (for `dockermind build`, `dockermind run` and to validate with `dockermind doctor`)
|
|
15
|
+
- Node.js (for the Next.js frontend; development and build use the `next` CLI)
|
|
16
|
+
|
|
17
|
+
When running commands below, ensure you are in the repository root.
|
|
18
|
+
|
|
19
|
+
## Node / Frontend commands (npm scripts)
|
|
20
|
+
|
|
21
|
+
The repository defines the following npm scripts in `package.json`. These are the standard Next.js scripts and an ESLint target.
|
|
22
|
+
|
|
23
|
+
- `npm run dev` (alias: `pnpm dev` / `yarn dev`) — Runs `next dev`.
|
|
24
|
+
- What it does: starts the Next.js development server. The server serves the app in development mode, enables fast refresh, and rebuilds pages on change. Default listening port is 3000 unless overridden by environment variables.
|
|
25
|
+
|
|
26
|
+
- `npm run build` — Runs `next build`.
|
|
27
|
+
- What it does: produces a production build for Next.js, compiling pages and assets into the `.next` directory. Run this before `npm run start` for production usage.
|
|
28
|
+
|
|
29
|
+
- `npm run start` — Runs `next start`.
|
|
30
|
+
- What it does: launches the Next.js production server that serves the contents of the `.next` build directory. Requires a successful `npm run build` beforehand.
|
|
31
|
+
|
|
32
|
+
- `npm run lint` — Runs `eslint .`.
|
|
33
|
+
- What it does: runs ESLint across the repository (the configuration and rules are determined by the repo). Use this to detect and fix lint issues.
|
|
34
|
+
|
|
35
|
+
Notes:
|
|
36
|
+
|
|
37
|
+
- Use your preferred package manager. The repository contains a `pnpm-lock.yaml` file; if you use pnpm prefer `pnpm install`. Otherwise `npm ci` or `npm install` will work for npm users.
|
|
38
|
+
|
|
39
|
+
## Python / Dockermind CLI
|
|
40
|
+
|
|
41
|
+
The Python package is configured in `pyproject.toml`. It exposes a console entry point named `dockermind` (see `[project.scripts]`). After installing the package, the `dockermind` command will be available.
|
|
42
|
+
|
|
43
|
+
Installation (editable/development install):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python -m pip install -e .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
What that does: installs the package in editable mode and registers the `dockermind` CLI entry point. You can then run the CLI from the repository root.
|
|
50
|
+
|
|
51
|
+
Available CLI commands (usage: `dockermind <command> [options]`):
|
|
52
|
+
|
|
53
|
+
- `dockermind init [path] [--compose] [--dry-run] [--force]`
|
|
54
|
+
- What it does: Analyses the project at `path` (defaults to `.`) to detect whether the project is Node.js or Python, determines framework, ports, and build/start steps, then generates a Dockerfile and `.dockerignore`. If `--compose` is specified it also generates a `docker-compose.yml`. `--dry-run` prints generated files to the console without writing them. Without `--force`, the command will prompt before overwriting an existing Dockerfile.
|
|
55
|
+
|
|
56
|
+
- `dockermind build [path] [--tag <tag>]`
|
|
57
|
+
- What it does: Runs `docker build` using the `Dockerfile` in `path` (defaults to `.`). The `--tag` (`-t`) option sets the image name (default `dockermind-app`). The command streams `docker build` output to the terminal and returns a non-zero exit code on failure.
|
|
58
|
+
|
|
59
|
+
- `dockermind run [--tag <tag>] [--port <port>] [--detach]`
|
|
60
|
+
- What it does: Runs a Docker container from an image built by `dockermind build`. The command maps host port to container port as `port:port` and runs the container in foreground by default. Use `--detach` (`-d`) to run in background. The command checks if the selected port appears in use and warns if so.
|
|
61
|
+
|
|
62
|
+
- `dockermind doctor`
|
|
63
|
+
- What it does: Verifies that Docker is installed and that the Docker daemon is running. Also checks for Docker Compose and prints helpful messages and next steps. Returns a non-zero exit code if checks fail.
|
|
64
|
+
|
|
65
|
+
- `dockermind version`
|
|
66
|
+
- What it does: Prints the installed dockermind package version.
|
|
67
|
+
|
|
68
|
+
You can also invoke the CLI directly (without installing) with:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
python -m dockermind.cli init --dry-run
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This runs the `init` command using the local source tree without installing the package.
|
|
75
|
+
|
|
76
|
+
## Test and helper scripts
|
|
77
|
+
|
|
78
|
+
- `scripts/run_tests.py` — Self-contained smoke tests.
|
|
79
|
+
- What it does: creates a temporary copy of the `dockermind` package inside `/tmp`, imports it and runs a suite of smoke tests that exercise analysis, Dockerfile generation and compose generation for representative Node.js and Python fixtures. The script prints pass/fail results and exits with non-zero status if any tests fail. It is intended as an on-host smoke test and may assume a POSIX environment (it uses `/tmp`).
|
|
80
|
+
|
|
81
|
+
- `scripts/run_tests.sh` — Simple shell wrapper for tests.
|
|
82
|
+
- What it does: installs minimal Python dependencies if needed and then runs the repo test harness under a specific path used by some CI environments.
|
|
83
|
+
|
|
84
|
+
Notes about tests: the tests in `scripts/run_tests.py` are not a replacement for a full test suite. They are smoke tests that exercise the core behaviour of the analysis and generation modules.
|
|
85
|
+
|
|
86
|
+
## Docker usage notes
|
|
87
|
+
|
|
88
|
+
- Generated Dockerfiles attempt to create small, production-ready images. For Node.js projects the generator may use multi-stage builds when a build step is present. For Python projects it will attempt to use `uvicorn` for FastAPI projects or `flask` for Flask projects and will add minimal system packages when native dependencies are detected.
|
|
89
|
+
- The `dockermind run` and `dockermind build` commands rely on the local Docker CLI and daemon.
|
|
90
|
+
|
|
91
|
+
## Contributing & Development
|
|
92
|
+
|
|
93
|
+
- Install Python development dependencies from `pyproject.toml` for tests and linters (e.g. `pytest`, `ruff`, `mypy`).
|
|
94
|
+
- Follow the linting and type checks present in the repo. Use `npm run lint` for JavaScript/TypeScript code quality checks.
|
|
95
|
+
|
|
96
|
+
## Developer Contact
|
|
97
|
+
|
|
98
|
+
- GitHub: https://github.com/Aakashsingh0388
|
|
99
|
+
- LinkedIn: https://www.linkedin.com/in/aakash-singh-7b8416318/
|
|
100
|
+
|
|
101
|
+
If you need further details about the project or help building images, please open an issue on GitHub or contact the developer directly via LinkedIn.
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
This project is provided under the MIT License (see `dockermind.egg-info` for packaged metadata).
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project analyzer -- detects project type, framework, version, port, and scripts.
|
|
3
|
+
|
|
4
|
+
Only Node.js and Python are supported in v1.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
|
|
14
|
+
from dockermind.utils import file_exists, read_file, extract_port_from_source, yellow
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# Analysis result dataclass
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class AnalysisResult:
|
|
24
|
+
"""Everything dockermind needs to know about the target project."""
|
|
25
|
+
|
|
26
|
+
# Core detection
|
|
27
|
+
language: str | None = None # "node" | "python" | None
|
|
28
|
+
framework: str | None = None # "express" | "fastapi" | "flask" | None
|
|
29
|
+
|
|
30
|
+
# Runtime version
|
|
31
|
+
node_version: str = "18" # Default Node version
|
|
32
|
+
python_version: str = "3.11" # Default Python version
|
|
33
|
+
|
|
34
|
+
# Scripts (Node)
|
|
35
|
+
has_build_script: bool = False
|
|
36
|
+
build_script: str | None = None
|
|
37
|
+
start_script: str | None = None
|
|
38
|
+
|
|
39
|
+
# Entry point (Python)
|
|
40
|
+
python_entry_module: str | None = None # e.g. "main:app"
|
|
41
|
+
uses_uvicorn: bool = False
|
|
42
|
+
|
|
43
|
+
# Port
|
|
44
|
+
port: int | None = None
|
|
45
|
+
port_is_default: bool = False # True if we fell back to a default
|
|
46
|
+
|
|
47
|
+
# Package manager
|
|
48
|
+
has_package_lock: bool = False
|
|
49
|
+
has_yarn_lock: bool = False
|
|
50
|
+
has_pnpm_lock: bool = False
|
|
51
|
+
|
|
52
|
+
# Raw Python dependency names (lowercase) for native-package inference
|
|
53
|
+
python_deps: set[str] = field(default_factory=set)
|
|
54
|
+
|
|
55
|
+
# Warnings generated during analysis
|
|
56
|
+
warnings: list[str] = field(default_factory=list)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def runtime_version(self) -> str:
|
|
60
|
+
"""Return the resolved runtime version string."""
|
|
61
|
+
if self.language == "node":
|
|
62
|
+
return self.node_version
|
|
63
|
+
return self.python_version
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
# Analyzer
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Analyzer:
|
|
72
|
+
"""Analyse a project directory and return an AnalysisResult."""
|
|
73
|
+
|
|
74
|
+
def __init__(self, project_path: str) -> None:
|
|
75
|
+
if not os.path.isdir(project_path):
|
|
76
|
+
raise FileNotFoundError(f"Project path does not exist: {project_path}")
|
|
77
|
+
self.path = project_path
|
|
78
|
+
|
|
79
|
+
def run(self) -> AnalysisResult:
|
|
80
|
+
"""Run full analysis pipeline and return the result."""
|
|
81
|
+
result = AnalysisResult()
|
|
82
|
+
|
|
83
|
+
has_node = file_exists(self.path, "package.json")
|
|
84
|
+
has_python = (
|
|
85
|
+
file_exists(self.path, "requirements.txt")
|
|
86
|
+
or file_exists(self.path, "pyproject.toml")
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Language detection -- only one allowed
|
|
90
|
+
if has_node and has_python:
|
|
91
|
+
# Hard stop: ambiguous multi-runtime projects are not supported.
|
|
92
|
+
raise SystemExit(
|
|
93
|
+
"Error: Multiple backend runtimes detected (Node.js and Python).\n"
|
|
94
|
+
"Please run dockermind inside the specific service directory."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if has_node:
|
|
98
|
+
result.language = "node"
|
|
99
|
+
self._analyse_node(result)
|
|
100
|
+
elif has_python:
|
|
101
|
+
result.language = "python"
|
|
102
|
+
self._analyse_python(result)
|
|
103
|
+
else:
|
|
104
|
+
# Unsupported or empty project
|
|
105
|
+
result.language = None
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
# ------------------------------------------------------------------
|
|
110
|
+
# Node.js analysis
|
|
111
|
+
# ------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
def _analyse_node(self, result: AnalysisResult) -> None:
|
|
114
|
+
"""Populate *result* with Node.js-specific details."""
|
|
115
|
+
raw = read_file(self.path, "package.json")
|
|
116
|
+
if not raw:
|
|
117
|
+
result.warnings.append("package.json exists but could not be read.")
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
pkg = json.loads(raw)
|
|
122
|
+
except json.JSONDecodeError:
|
|
123
|
+
result.warnings.append("package.json is not valid JSON.")
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
# -- Node version from engines field --
|
|
127
|
+
engines = pkg.get("engines", {})
|
|
128
|
+
requested_version = engines.get("node", "")
|
|
129
|
+
if requested_version:
|
|
130
|
+
result.node_version = self._parse_node_version(requested_version)
|
|
131
|
+
# else: stays at default "18"
|
|
132
|
+
|
|
133
|
+
# -- Scripts --
|
|
134
|
+
scripts = pkg.get("scripts", {})
|
|
135
|
+
if "build" in scripts:
|
|
136
|
+
result.has_build_script = True
|
|
137
|
+
result.build_script = scripts["build"]
|
|
138
|
+
result.start_script = scripts.get("start")
|
|
139
|
+
|
|
140
|
+
# -- Framework detection --
|
|
141
|
+
all_deps = {
|
|
142
|
+
**pkg.get("dependencies", {}),
|
|
143
|
+
**pkg.get("devDependencies", {}),
|
|
144
|
+
}
|
|
145
|
+
if "express" in all_deps:
|
|
146
|
+
result.framework = "express"
|
|
147
|
+
elif "fastify" in all_deps:
|
|
148
|
+
result.framework = "fastify"
|
|
149
|
+
elif "koa" in all_deps:
|
|
150
|
+
result.framework = "koa"
|
|
151
|
+
# For v1 we just note whatever framework; Dockerfile is the same.
|
|
152
|
+
|
|
153
|
+
# -- Lock file detection --
|
|
154
|
+
result.has_package_lock = file_exists(self.path, "package-lock.json")
|
|
155
|
+
result.has_yarn_lock = file_exists(self.path, "yarn.lock")
|
|
156
|
+
result.has_pnpm_lock = file_exists(self.path, "pnpm-lock.yaml")
|
|
157
|
+
|
|
158
|
+
# -- Port detection --
|
|
159
|
+
result.port = self._detect_node_port(pkg)
|
|
160
|
+
if result.port is None:
|
|
161
|
+
result.port = 3000
|
|
162
|
+
result.port_is_default = True
|
|
163
|
+
result.warnings.append(
|
|
164
|
+
"Could not detect port from source. Defaulting to 3000."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def _parse_node_version(self, version_spec: str) -> str:
|
|
168
|
+
"""Extract a major version number from an engines.node specifier.
|
|
169
|
+
|
|
170
|
+
Examples:
|
|
171
|
+
">=18" -> "18"
|
|
172
|
+
"^20.0.0" -> "20"
|
|
173
|
+
"18.x" -> "18"
|
|
174
|
+
">=18 <22" -> "18"
|
|
175
|
+
"""
|
|
176
|
+
match = re.search(r"(\d{1,3})", version_spec)
|
|
177
|
+
if match:
|
|
178
|
+
return match.group(1)
|
|
179
|
+
return "18" # safe fallback
|
|
180
|
+
|
|
181
|
+
def _detect_node_port(self, pkg: dict) -> int | None:
|
|
182
|
+
"""Try to detect port from source files or scripts."""
|
|
183
|
+
# Check common entry points for .listen(PORT)
|
|
184
|
+
candidates = ["server.js", "index.js", "app.js", "src/index.js", "src/server.js",
|
|
185
|
+
"src/app.js", "dist/index.js", "server.ts", "index.ts", "src/index.ts"]
|
|
186
|
+
|
|
187
|
+
for filename in candidates:
|
|
188
|
+
source = read_file(self.path, filename)
|
|
189
|
+
if source:
|
|
190
|
+
port = extract_port_from_source(source)
|
|
191
|
+
if port:
|
|
192
|
+
return port
|
|
193
|
+
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
# ------------------------------------------------------------------
|
|
197
|
+
# Python analysis
|
|
198
|
+
# ------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
def _analyse_python(self, result: AnalysisResult) -> None:
|
|
201
|
+
"""Populate *result* with Python-specific details."""
|
|
202
|
+
|
|
203
|
+
# Gather all dependency names
|
|
204
|
+
deps_lower: set[str] = set()
|
|
205
|
+
|
|
206
|
+
# From requirements.txt
|
|
207
|
+
reqs = read_file(self.path, "requirements.txt")
|
|
208
|
+
if reqs:
|
|
209
|
+
for line in reqs.splitlines():
|
|
210
|
+
line = line.strip()
|
|
211
|
+
if not line or line.startswith("#"):
|
|
212
|
+
continue
|
|
213
|
+
# "fastapi==0.100.0" -> "fastapi"
|
|
214
|
+
dep_name = re.split(r"[>=<!~\[]", line)[0].strip().lower()
|
|
215
|
+
if dep_name:
|
|
216
|
+
deps_lower.add(dep_name)
|
|
217
|
+
|
|
218
|
+
# From pyproject.toml (simple parse -- no toml library needed for v1)
|
|
219
|
+
pyproject = read_file(self.path, "pyproject.toml")
|
|
220
|
+
if pyproject:
|
|
221
|
+
# Quick regex extraction for dependencies list
|
|
222
|
+
for match in re.finditer(r'"([a-zA-Z0-9_-]+)', pyproject):
|
|
223
|
+
deps_lower.add(match.group(1).lower())
|
|
224
|
+
|
|
225
|
+
# Expose raw deps for native-package inference in dockerfile_generator
|
|
226
|
+
result.python_deps = deps_lower
|
|
227
|
+
|
|
228
|
+
# -- Framework detection --
|
|
229
|
+
if "fastapi" in deps_lower:
|
|
230
|
+
result.framework = "fastapi"
|
|
231
|
+
result.port = 8000
|
|
232
|
+
elif "flask" in deps_lower:
|
|
233
|
+
result.framework = "flask"
|
|
234
|
+
result.port = 5000
|
|
235
|
+
else:
|
|
236
|
+
# Unknown Python framework -- set a sensible default
|
|
237
|
+
result.framework = None
|
|
238
|
+
result.port = 8000
|
|
239
|
+
result.warnings.append(
|
|
240
|
+
"No recognised framework (FastAPI/Flask). Defaulting to port 8000."
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# -- Uvicorn detection --
|
|
244
|
+
if "uvicorn" in deps_lower:
|
|
245
|
+
result.uses_uvicorn = True
|
|
246
|
+
|
|
247
|
+
# -- Detect entry module --
|
|
248
|
+
result.python_entry_module = self._detect_python_entry(result)
|
|
249
|
+
|
|
250
|
+
# -- Python version from pyproject.toml requires-python --
|
|
251
|
+
if pyproject:
|
|
252
|
+
match = re.search(r'requires-python\s*=\s*["\']([^"\']+)["\']', pyproject)
|
|
253
|
+
if match:
|
|
254
|
+
ver_match = re.search(r"(\d+\.\d+)", match.group(1))
|
|
255
|
+
if ver_match:
|
|
256
|
+
result.python_version = ver_match.group(1)
|
|
257
|
+
|
|
258
|
+
def _detect_python_entry(self, result: AnalysisResult) -> str | None:
|
|
259
|
+
"""Detect the Python entry module (e.g. 'main:app')."""
|
|
260
|
+
|
|
261
|
+
# Look for common entry files
|
|
262
|
+
candidates = ["main.py", "app.py", "application.py", "server.py",
|
|
263
|
+
"src/main.py", "src/app.py"]
|
|
264
|
+
|
|
265
|
+
for filename in candidates:
|
|
266
|
+
source = read_file(self.path, filename)
|
|
267
|
+
if source is None:
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
# Look for FastAPI() or Flask() app instantiation
|
|
271
|
+
app_match = re.search(
|
|
272
|
+
r"(\w+)\s*=\s*(?:FastAPI|Flask)\s*\(", source
|
|
273
|
+
)
|
|
274
|
+
if app_match:
|
|
275
|
+
app_var = app_match.group(1)
|
|
276
|
+
module_name = filename.replace("/", ".").replace(".py", "")
|
|
277
|
+
return f"{module_name}:{app_var}"
|
|
278
|
+
|
|
279
|
+
# Fallback: if main.py exists, assume main:app
|
|
280
|
+
if file_exists(self.path, "main.py"):
|
|
281
|
+
return "main:app"
|
|
282
|
+
if file_exists(self.path, "app.py"):
|
|
283
|
+
return "app:app"
|
|
284
|
+
|
|
285
|
+
return None
|