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.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: initstack
3
+ Version: 1.5.0
4
+ Summary: Universal, environment-aware project initializer
5
+ Requires-Python: >=3.8
6
+ Requires-Dist: jsonschema>=4.0.0
7
+ Dynamic: requires-dist
8
+ Dynamic: requires-python
9
+ Dynamic: summary
@@ -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,6 @@
1
+ import json
2
+ from pathlib import Path
3
+
4
+ template_schema = json.load(
5
+ open(Path(__file__).parent / "template.schema.json")
6
+ )
@@ -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,12 @@
1
+ # initstack/utils/config.py
2
+ import os
3
+ import yaml
4
+
5
+ CONFIG_FILE = "initstack.yaml"
6
+
7
+ def load_config():
8
+ if not os.path.exists(CONFIG_FILE):
9
+ return {}
10
+
11
+ with open(CONFIG_FILE, "r") as f:
12
+ return yaml.safe_load(f) or {}
@@ -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,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: initstack
3
+ Version: 1.5.0
4
+ Summary: Universal, environment-aware project initializer
5
+ Requires-Python: >=3.8
6
+ Requires-Dist: jsonschema>=4.0.0
7
+ Dynamic: requires-dist
8
+ Dynamic: requires-python
9
+ Dynamic: summary
@@ -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,2 @@
1
+ [console_scripts]
2
+ initstack = initstack.cli.main:main
@@ -0,0 +1 @@
1
+ jsonschema>=4.0.0
@@ -0,0 +1 @@
1
+ initstack
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ )