initstack 1.5.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.
- initstack-1.5.0/PKG-INFO +9 -0
- initstack-1.5.0/README.md +20 -0
- initstack-1.5.0/initstack/__init__.py +0 -0
- initstack-1.5.0/initstack/cli/__init__.py +0 -0
- initstack-1.5.0/initstack/cli/commands.py +95 -0
- initstack-1.5.0/initstack/cli/main.py +19 -0
- initstack-1.5.0/initstack/core/__init__.py +0 -0
- initstack-1.5.0/initstack/core/environment.py +11 -0
- initstack-1.5.0/initstack/core/filesystem.py +9 -0
- initstack-1.5.0/initstack/core/gitutils.py +22 -0
- initstack-1.5.0/initstack/core/plugins.py +23 -0
- initstack-1.5.0/initstack/core/project.py +37 -0
- initstack-1.5.0/initstack/core/schema/__init__.py +6 -0
- initstack-1.5.0/initstack/core/schema.py +12 -0
- initstack-1.5.0/initstack/core/templates.py +108 -0
- initstack-1.5.0/initstack/utils/__init__.py +0 -0
- initstack-1.5.0/initstack/utils/config.py +12 -0
- initstack-1.5.0/initstack/utils/output.py +13 -0
- initstack-1.5.0/initstack.egg-info/PKG-INFO +9 -0
- initstack-1.5.0/initstack.egg-info/SOURCES.txt +24 -0
- initstack-1.5.0/initstack.egg-info/dependency_links.txt +1 -0
- initstack-1.5.0/initstack.egg-info/entry_points.txt +2 -0
- initstack-1.5.0/initstack.egg-info/requires.txt +1 -0
- initstack-1.5.0/initstack.egg-info/top_level.txt +1 -0
- initstack-1.5.0/setup.cfg +4 -0
- initstack-1.5.0/setup.py +18 -0
initstack-1.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Initstack
|
|
2
|
+
|
|
3
|
+
Initstack is a universal, environment-aware CLI tool for initializing project
|
|
4
|
+
structures across multiple platforms.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
- Works on Termux, Linux, RNX, macOS, WSL
|
|
8
|
+
- No root required
|
|
9
|
+
- User-space friendly
|
|
10
|
+
- Offline templates
|
|
11
|
+
- Simple and extensible
|
|
12
|
+
|
|
13
|
+
## Philosophy
|
|
14
|
+
Initstack adapts to your environment. It does not install system dependencies,
|
|
15
|
+
require sudo, or assume a specific OS or package manager.
|
|
16
|
+
|
|
17
|
+
## š· Version
|
|
18
|
+
Release: (v1.5.0)
|
|
19
|
+
Stability: (Stable)
|
|
20
|
+
Python: ā„ (3.8)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
import platform
|
|
4
|
+
|
|
5
|
+
from initstack.core.templates import list_templates, get_template
|
|
6
|
+
from initstack.core.project import create_project
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# -------------------------
|
|
10
|
+
# Command handlers
|
|
11
|
+
# -------------------------
|
|
12
|
+
|
|
13
|
+
def cmd_list(args):
|
|
14
|
+
print("ā¹ Available templates:")
|
|
15
|
+
for name in list_templates():
|
|
16
|
+
print(f" - {name}")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def cmd_new(args):
|
|
20
|
+
template = get_template(args.template)
|
|
21
|
+
create_project(template, args.name)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def cmd_doctor(args):
|
|
25
|
+
print("\n𩺠Initstack Doctor Report\n")
|
|
26
|
+
|
|
27
|
+
# Python
|
|
28
|
+
print(f"ā Python executable: {sys.executable}")
|
|
29
|
+
print(f"ā Python version: {platform.python_version()}")
|
|
30
|
+
|
|
31
|
+
# OS
|
|
32
|
+
print(f"ā Platform: {platform.system().lower()}")
|
|
33
|
+
|
|
34
|
+
# Home directory
|
|
35
|
+
home = os.path.expanduser("~")
|
|
36
|
+
print(f"ā Home directory: {home}")
|
|
37
|
+
|
|
38
|
+
# Plugin directory
|
|
39
|
+
plugin_dir = os.path.expanduser("~/.initstack/plugins")
|
|
40
|
+
print(f"\nš Plugin directory: {plugin_dir}")
|
|
41
|
+
|
|
42
|
+
if not os.path.isdir(plugin_dir):
|
|
43
|
+
print("ā Plugin directory does not exist")
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
plugins = os.listdir(plugin_dir)
|
|
47
|
+
if not plugins:
|
|
48
|
+
print("ā No plugins installed")
|
|
49
|
+
else:
|
|
50
|
+
print("ā Installed plugins:")
|
|
51
|
+
for p in plugins:
|
|
52
|
+
print(f" - {p}")
|
|
53
|
+
|
|
54
|
+
print("\nā
Doctor check complete\n")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def cmd_self_update(args):
|
|
58
|
+
print("ā¹ Self-update is not yet implemented")
|
|
59
|
+
print("ā¹ Use: pip install --upgrade initstack")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# -------------------------
|
|
63
|
+
# Subcommand registration
|
|
64
|
+
# -------------------------
|
|
65
|
+
|
|
66
|
+
def build_parser(subparsers):
|
|
67
|
+
# list
|
|
68
|
+
p_list = subparsers.add_parser(
|
|
69
|
+
"list",
|
|
70
|
+
help="List available templates"
|
|
71
|
+
)
|
|
72
|
+
p_list.set_defaults(func=cmd_list)
|
|
73
|
+
|
|
74
|
+
# new
|
|
75
|
+
p_new = subparsers.add_parser(
|
|
76
|
+
"new",
|
|
77
|
+
help="Create a new project"
|
|
78
|
+
)
|
|
79
|
+
p_new.add_argument("template", help="Template name")
|
|
80
|
+
p_new.add_argument("name", help="Project directory name")
|
|
81
|
+
p_new.set_defaults(func=cmd_new)
|
|
82
|
+
|
|
83
|
+
# doctor
|
|
84
|
+
p_doctor = subparsers.add_parser(
|
|
85
|
+
"doctor",
|
|
86
|
+
help="Check environment health"
|
|
87
|
+
)
|
|
88
|
+
p_doctor.set_defaults(func=cmd_doctor)
|
|
89
|
+
|
|
90
|
+
# self-update
|
|
91
|
+
p_update = subparsers.add_parser(
|
|
92
|
+
"self-update",
|
|
93
|
+
help="Update initstack to the latest version"
|
|
94
|
+
)
|
|
95
|
+
p_update.set_defaults(func=cmd_self_update)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import platform
|
|
3
|
+
|
|
4
|
+
from initstack.cli.commands import build_parser
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
print(f"ā¹ Initstack running on: {platform.system().lower()}")
|
|
9
|
+
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
prog="initstack",
|
|
12
|
+
description="Universal, environment-aware project initializer"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
16
|
+
build_parser(subparsers)
|
|
17
|
+
|
|
18
|
+
args = parser.parse_args()
|
|
19
|
+
args.func(args)
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# initstack/core/environment.py
|
|
2
|
+
import os
|
|
3
|
+
import platform
|
|
4
|
+
|
|
5
|
+
def detect_environment():
|
|
6
|
+
if "TERMUX_VERSION" in os.environ or os.path.exists("/data/data/com.termux"):
|
|
7
|
+
return "termux"
|
|
8
|
+
if os.environ.get("RNX_WORKSPACE"):
|
|
9
|
+
return "rnx"
|
|
10
|
+
system = platform.system().lower()
|
|
11
|
+
return system
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# initstack/core/filesystem.py
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
def create_structure(base_path, structure):
|
|
5
|
+
for path, content in structure.items():
|
|
6
|
+
full = os.path.join(base_path, path)
|
|
7
|
+
os.makedirs(os.path.dirname(full), exist_ok=True)
|
|
8
|
+
with open(full, "w", encoding="utf-8") as f:
|
|
9
|
+
f.write(content)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
def init_git_repo(path):
|
|
7
|
+
subprocess.run(["git", "init"], cwd=path)
|
|
8
|
+
|
|
9
|
+
def add_license(path, license_name, author):
|
|
10
|
+
src = os.path.join(
|
|
11
|
+
os.path.dirname(__file__), "..", "licenses", f"{license_name}.txt"
|
|
12
|
+
)
|
|
13
|
+
dst = os.path.join(path, "LICENSE")
|
|
14
|
+
|
|
15
|
+
with open(src, "r") as f:
|
|
16
|
+
content = f.read()
|
|
17
|
+
|
|
18
|
+
content = content.replace("{{year}}", str(datetime.now().year))
|
|
19
|
+
content = content.replace("{{author}}", author)
|
|
20
|
+
|
|
21
|
+
with open(dst, "w") as f:
|
|
22
|
+
f.write(content)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
PLUGIN_DIRS = [
|
|
5
|
+
Path.cwd() / ".initstack/templates",
|
|
6
|
+
Path.home() / ".initstack/templates"
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
def load_plugins():
|
|
10
|
+
plugins = {}
|
|
11
|
+
|
|
12
|
+
for base in PLUGIN_DIRS:
|
|
13
|
+
if not base.exists():
|
|
14
|
+
continue
|
|
15
|
+
|
|
16
|
+
for tpl in base.iterdir():
|
|
17
|
+
cfg = tpl / "template.json"
|
|
18
|
+
if cfg.exists():
|
|
19
|
+
with open(cfg) as f:
|
|
20
|
+
data = json.load(f)
|
|
21
|
+
plugins[data["name"]] = (tpl, data)
|
|
22
|
+
|
|
23
|
+
return plugins
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def create_project(template: dict, project_name: str):
|
|
6
|
+
"""
|
|
7
|
+
Create a project directory structure from a template definition.
|
|
8
|
+
"""
|
|
9
|
+
root = Path(project_name)
|
|
10
|
+
|
|
11
|
+
if root.exists():
|
|
12
|
+
raise FileExistsError(f"Directory '{project_name}' already exists")
|
|
13
|
+
|
|
14
|
+
print(f"š Creating project: {project_name}")
|
|
15
|
+
root.mkdir(parents=True)
|
|
16
|
+
|
|
17
|
+
structure = template.get("structure", {})
|
|
18
|
+
|
|
19
|
+
_create_structure(root, structure)
|
|
20
|
+
|
|
21
|
+
print("ā
Project created successfully")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _create_structure(base: Path, structure: dict):
|
|
25
|
+
for name, content in structure.items():
|
|
26
|
+
path = base / name
|
|
27
|
+
|
|
28
|
+
if isinstance(content, dict):
|
|
29
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
30
|
+
_create_structure(path, content)
|
|
31
|
+
|
|
32
|
+
elif isinstance(content, str):
|
|
33
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
34
|
+
path.write_text(content, encoding="utf-8")
|
|
35
|
+
|
|
36
|
+
else:
|
|
37
|
+
raise ValueError(f"Invalid structure item: {name}")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
TEMPLATE_SCHEMA = {
|
|
2
|
+
"type": "object",
|
|
3
|
+
"required": ["name", "version", "files"],
|
|
4
|
+
"properties": {
|
|
5
|
+
"name": {"type": "string"},
|
|
6
|
+
"version": {"type": "string"},
|
|
7
|
+
"files": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"additionalProperties": {"type": "string"}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from jsonschema import validate, ValidationError
|
|
5
|
+
|
|
6
|
+
# Built-in templates directory
|
|
7
|
+
BUILTIN_DIR = Path(__file__).parent / "builtin"
|
|
8
|
+
|
|
9
|
+
# User plugin directory
|
|
10
|
+
PLUGIN_DIR = Path(os.path.expanduser("~/.initstack/plugins"))
|
|
11
|
+
|
|
12
|
+
# Load JSON schema
|
|
13
|
+
from initstack.core.schema import template_schema
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _load_template_from_dir(template_dir: Path):
|
|
17
|
+
"""
|
|
18
|
+
Load and validate a template from a directory.
|
|
19
|
+
Expects template.json inside the directory.
|
|
20
|
+
"""
|
|
21
|
+
template_file = template_dir / "template.json"
|
|
22
|
+
|
|
23
|
+
if not template_file.exists():
|
|
24
|
+
return None
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
with open(template_file, "r", encoding="utf-8") as f:
|
|
28
|
+
data = json.load(f)
|
|
29
|
+
except Exception as e:
|
|
30
|
+
print(f"ā Failed to parse {template_file}: {e}")
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
# Schema validation
|
|
34
|
+
try:
|
|
35
|
+
validate(instance=data, schema=template_schema)
|
|
36
|
+
except ValidationError as e:
|
|
37
|
+
print(f"ā Invalid template.json in {template_dir.name}: {e.message}")
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
# Name consistency
|
|
41
|
+
name = data.get("name")
|
|
42
|
+
if not name:
|
|
43
|
+
print(f"ā Template in {template_dir} missing 'name'")
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
return name, data
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_builtin_templates():
|
|
50
|
+
templates = {}
|
|
51
|
+
|
|
52
|
+
if not BUILTIN_DIR.exists():
|
|
53
|
+
return templates
|
|
54
|
+
|
|
55
|
+
for item in BUILTIN_DIR.iterdir():
|
|
56
|
+
if not item.is_dir():
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
result = _load_template_from_dir(item)
|
|
60
|
+
if result:
|
|
61
|
+
name, data = result
|
|
62
|
+
templates[name] = data
|
|
63
|
+
|
|
64
|
+
return templates
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def load_plugins():
|
|
68
|
+
templates = {}
|
|
69
|
+
|
|
70
|
+
if not PLUGIN_DIR.exists():
|
|
71
|
+
return templates
|
|
72
|
+
|
|
73
|
+
for item in PLUGIN_DIR.iterdir():
|
|
74
|
+
if not item.is_dir():
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
result = _load_template_from_dir(item)
|
|
78
|
+
if result:
|
|
79
|
+
name, data = result
|
|
80
|
+
templates[name] = data
|
|
81
|
+
|
|
82
|
+
return templates
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def list_templates():
|
|
86
|
+
"""
|
|
87
|
+
Return sorted list of all available template names.
|
|
88
|
+
"""
|
|
89
|
+
templates = {}
|
|
90
|
+
templates.update(load_builtin_templates())
|
|
91
|
+
templates.update(load_plugins())
|
|
92
|
+
|
|
93
|
+
return sorted(templates.keys())
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_template(name: str):
|
|
97
|
+
"""
|
|
98
|
+
Return template structure by name.
|
|
99
|
+
Raises ValueError if not found.
|
|
100
|
+
"""
|
|
101
|
+
templates = {}
|
|
102
|
+
templates.update(load_builtin_templates())
|
|
103
|
+
templates.update(load_plugins())
|
|
104
|
+
|
|
105
|
+
if name not in templates:
|
|
106
|
+
raise ValueError(f"Unknown template: {name}")
|
|
107
|
+
|
|
108
|
+
return templates[name]
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# initstack/utils/output.py
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
PLAIN = os.environ.get("INITSTACK_PLAIN") == "1"
|
|
5
|
+
|
|
6
|
+
def info(msg):
|
|
7
|
+
print(msg if PLAIN else f"ā¹ {msg}")
|
|
8
|
+
|
|
9
|
+
def success(msg):
|
|
10
|
+
print(msg if PLAIN else f"ā
{msg}")
|
|
11
|
+
|
|
12
|
+
def error(msg):
|
|
13
|
+
print(msg if PLAIN else f"ā {msg}")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
initstack/__init__.py
|
|
4
|
+
initstack.egg-info/PKG-INFO
|
|
5
|
+
initstack.egg-info/SOURCES.txt
|
|
6
|
+
initstack.egg-info/dependency_links.txt
|
|
7
|
+
initstack.egg-info/entry_points.txt
|
|
8
|
+
initstack.egg-info/requires.txt
|
|
9
|
+
initstack.egg-info/top_level.txt
|
|
10
|
+
initstack/cli/__init__.py
|
|
11
|
+
initstack/cli/commands.py
|
|
12
|
+
initstack/cli/main.py
|
|
13
|
+
initstack/core/__init__.py
|
|
14
|
+
initstack/core/environment.py
|
|
15
|
+
initstack/core/filesystem.py
|
|
16
|
+
initstack/core/gitutils.py
|
|
17
|
+
initstack/core/plugins.py
|
|
18
|
+
initstack/core/project.py
|
|
19
|
+
initstack/core/schema.py
|
|
20
|
+
initstack/core/templates.py
|
|
21
|
+
initstack/core/schema/__init__.py
|
|
22
|
+
initstack/utils/__init__.py
|
|
23
|
+
initstack/utils/config.py
|
|
24
|
+
initstack/utils/output.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jsonschema>=4.0.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
initstack
|
initstack-1.5.0/setup.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# setup.py
|
|
2
|
+
from setuptools import setup, find_packages
|
|
3
|
+
|
|
4
|
+
setup(
|
|
5
|
+
name="initstack",
|
|
6
|
+
version="1.5.0",
|
|
7
|
+
description="Universal, environment-aware project initializer",
|
|
8
|
+
packages=find_packages(),
|
|
9
|
+
install_requires=[
|
|
10
|
+
"jsonschema>=4.0.0",
|
|
11
|
+
],
|
|
12
|
+
entry_points={
|
|
13
|
+
"console_scripts": [
|
|
14
|
+
"initstack=initstack.cli.main:main",
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
python_requires=">=3.8",
|
|
18
|
+
)
|