aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ipm/__main__.py97
-rw-r--r--src/ipm/api.py79
-rw-r--r--src/ipm/const.py6
-rw-r--r--src/ipm/exceptions.py12
-rw-r--r--src/ipm/logging.py12
-rw-r--r--src/ipm/models/ipk.py11
-rw-r--r--src/ipm/utils/freeze.py18
7 files changed, 167 insertions, 68 deletions
diff --git a/src/ipm/__main__.py b/src/ipm/__main__.py
index c40e86e..753c3a7 100644
--- a/src/ipm/__main__.py
+++ b/src/ipm/__main__.py
@@ -1,41 +1,62 @@
-from .api import install, extract, build
-import argparse
-import sys
-
-def main():
- parser = argparse.ArgumentParser(
- prog="ipm", description="Infini 包管理器", exit_on_error=False
- )
- subparsers = parser.add_subparsers(
- title="指令", dest="command", metavar="<operation>"
- )
-
- # Install command
- install_parser = subparsers.add_parser("install", help="安装一个 Infini 规则包到此计算机")
- install_parser.add_argument("uri", help="Infini 包的统一资源标识符")
- install_parser.add_argument("--index", help="IPM 包服务器")
-
- # Extract command
- extract_parser = subparsers.add_parser("extract", help="解压缩 Infini 包")
- extract_parser.add_argument("package", help="Infini 包路径")
- extract_parser.add_argument(
- "--dist",
- default=".",
- help="特定的解压路径 (默认: 当前工作目录)",
- )
-
- # Build command
- build_parser = subparsers.add_parser("build", help="打包 Infini 规则包")
- build_parser.add_argument("package", nargs="?", help="Infini 库路径", default=".")
-
- args = parser.parse_args(sys.argv[1:] or ["-h"])
-
- if args.command == "install":
- install(args.uri, args.index, echo=True)
- elif args.command == "extract":
- extract(args.package, args.dist)
- elif args.command == "build":
- build(args.package)
+from . import api
+from .exceptions import IpmException
+from .logging import logger
+import typer
+
+main = typer.Typer(
+ name="ipm", help="Infini 包管理器", no_args_is_help=True, add_completion=False
+)
+
+
+@main.command()
+def install(
+ uri: str = typer.Argument(help="Infini 包的统一资源标识符"),
+ index: str = typer.Option(None, help="IPM 包服务器"),
+):
+ """安装一个 Infini 规则包到此计算机"""
+ try:
+ api.install(uri, index, echo=True)
+ except IpmException as error:
+ logger.error(error)
+
+
+@main.command()
+def extract(
+ package: str = typer.Argument(help="Infini 项目路径"),
+ dist: str = typer.Option(".", help="特定的解压路径"),
+):
+ """解压缩 Infini 包"""
+ try:
+ api.extract(package, dist, echo=True)
+ except IpmException as error:
+ logger.error(error)
+
+
+@main.command()
+def init(force: bool = typer.Option(None, "--force", "-f", help="强制初始化")):
+ """初始化一个 Infini 项目"""
+ try:
+ api.init(".", force, echo=True)
+ except IpmException as error:
+ logger.error(error)
+
+
+@main.command()
+def new(package: str = typer.Argument(help="Infini 项目路径")):
+ """新建一个 Infini 项目"""
+ try:
+ api.new(package, echo=True)
+ except IpmException as error:
+ logger.error(error)
+
+
+@main.command()
+def build(package: str = typer.Argument(".", help="Infini 项目路径")):
+ """打包 Infini 规则包"""
+ try:
+ api.build(package, echo=True)
+ except IpmException as error:
+ logger.error(error)
if __name__ == "__main__":
diff --git a/src/ipm/api.py b/src/ipm/api.py
index faf7e0b..2cb6eee 100644
--- a/src/ipm/api.py
+++ b/src/ipm/api.py
@@ -1,16 +1,58 @@
from pathlib import Path
from .typing import StrPath
from .utils import freeze, urlparser, loader
-from .models.ipk import InfiniPackage
-from .exceptions import FileTypeMismatch
+from .models.ipk import InfiniPackage, InfiniFrozenPackage
+from .exceptions import FileTypeMismatch, TomlLoadFailed, FileNotFoundError
from .const import INDEX, HOME
-from .logging import info, success
+from .logging import info, success, warning, error
-import os
+import toml
-def build(source_path: StrPath, echo: bool = False) -> None:
- freeze.build_ipk(InfiniPackage(source_path))
+def init(source_path: StrPath, force: bool = False, echo: bool = False) -> None:
+ source_path = Path(source_path).resolve()
+ if (toml_path := (source_path / "infini.toml")).exists() and not force:
+ warning(f"无法在已经初始化的地址重新初始化, 如果你的确希望重新初始化, 请使用[ipm init --force].", echo)
+
+ toml_file = toml_path.open("w", encoding="utf-8")
+ toml.dump(
+ {
+ "infini": {
+ "name": source_path.name,
+ "version": "0.1.0",
+ "description": "COC 规则包",
+ "license": "MIT",
+ },
+ "requirements": {},
+ "dependencies": {},
+ },
+ toml_file,
+ )
+ toml_file.close()
+
+ (source_path / "src").mkdir(parents=True, exist_ok=True)
+ (source_path / "src" / "__init__.py").write_text(
+ "# Initialized `__init__.py` generated by ipm."
+ )
+
+
+def new(dist_path: StrPath, echo: bool = False) -> None:
+ info("初始化环境中...")
+ path = Path(dist_path).resolve()
+ if path.exists():
+ return warning(f"路径[{path}]已经存在.", echo)
+ path.mkdir(parents=True, exist_ok=True)
+ return init(path, echo=echo)
+
+
+def build(source_path: StrPath, echo: bool = False) -> InfiniFrozenPackage:
+ info("检查构建环境...", echo)
+ try:
+ ipk = InfiniPackage(source_path)
+ info(f"包[{ipk.name}]构建环境载入完毕.", echo)
+ except TomlLoadFailed as e:
+ return error(f"环境存在异常: {e}", echo)
+ return freeze.build_ipk(ipk, echo)
def extract(
@@ -28,12 +70,8 @@ def install(uri: str, index: str = "", echo: bool = False) -> None:
HOME.mkdir(parents=True, exist_ok=True)
index = index or INDEX
- if os.path.isabs(uri):
- info(f"检定给定的 URI 地址[{uri}]为本地路径.", echo)
- if not uri.endswith(".ipk"):
- raise FileTypeMismatch("文件类型与预期[.ipk]不匹配.")
- info("安装中...", echo)
- ipk = extract(Path(uri).resolve(), HOME, echo)
+ if uri.isalpha():
+ ...
elif urlparser.is_valid_url(uri):
filename = uri.rstrip("/").split("/")[-1]
ipk = loader.load(
@@ -41,9 +79,20 @@ def install(uri: str, index: str = "", echo: bool = False) -> None:
uri.rstrip("/").rsplit("/")[0],
filename,
)
- elif uri.isalpha():
- ...
else:
- raise FileTypeMismatch("URI指向未知的位置.")
+ info(f"检定给定的 URI 地址[{uri}]为本地路径.", echo)
+ path = Path(uri).resolve()
+ if not path.exists():
+ raise FileNotFoundError("给定的 URI 路径不存在!")
+
+ if uri.endswith(".ipk"):
+ info("安装中...", echo)
+ ipk = extract(Path(uri).resolve(), HOME, echo)
+ else:
+ raise FileTypeMismatch("文件类型与预期[.ipk]不匹配.")
success(f"包[{ipk.name}]成功安装在[{ipk.source_path}].", echo)
+
+
+def uninstall(ipk: str | InfiniPackage):
+ ...
diff --git a/src/ipm/const.py b/src/ipm/const.py
index c682fdd..d31b10a 100644
--- a/src/ipm/const.py
+++ b/src/ipm/const.py
@@ -1,8 +1,12 @@
from pathlib import Path
+# 控制参数
DEBUG = False
+# 初始化参数
INDEX = "https://ipm.hydroroll.team/index/"
HOME = Path.home() / ".ipm" / "src"
+
+# 文本参数
ATTENSION = """# This file is @generated by IPM.
-# It is not intended for manual editing."""
+# It is not intended for manual editing.\n\n"""
diff --git a/src/ipm/exceptions.py b/src/ipm/exceptions.py
index e6a01c0..b19b60c 100644
--- a/src/ipm/exceptions.py
+++ b/src/ipm/exceptions.py
@@ -2,11 +2,19 @@ class IpmException(Exception):
"""IPM Base Exception"""
-class FileNotFoundError(IpmException, FileNotFoundError):
+class FileException(IpmException):
+ """IPM File Base Exception"""
+
+
+class TomlLoadFailed(FileException):
+ """Failed to load `infini.toml`"""
+
+
+class FileNotFoundError(FileException, FileNotFoundError):
"""Raises when file not founded"""
-class FileExistsError(IpmException, FileExistsError):
+class FileExistsError(FileException, FileExistsError):
"""Raises when file not founded"""
diff --git a/src/ipm/logging.py b/src/ipm/logging.py
index 2594930..f923e64 100644
--- a/src/ipm/logging.py
+++ b/src/ipm/logging.py
@@ -4,9 +4,17 @@ from .const import DEBUG
logger = multilogger(name="IPM", level="DEBUG" if DEBUG else "INFO", notime=True)
-def info(message: str, echo: bool = True) -> None:
+def info(message: str, echo: bool = False) -> None:
return logger.info(message) if echo else None
-def success(message: str, echo: bool = True) -> None:
+def success(message: str, echo: bool = False) -> None:
return logger.success(message) if echo else None
+
+
+def warning(message: str, echo: bool = False) -> None:
+ return logger.warning(message) if echo else None
+
+
+def error(message: str, echo: bool = False) -> None:
+ return logger.error(message) if echo else None
diff --git a/src/ipm/models/ipk.py b/src/ipm/models/ipk.py
index a50ebde..27a3d68 100644
--- a/src/ipm/models/ipk.py
+++ b/src/ipm/models/ipk.py
@@ -1,6 +1,6 @@
from pathlib import Path
from ..typing import List, Dict, Literal
-from ..exceptions import SyntaxError
+from ..exceptions import SyntaxError, TomlLoadFailed
import toml
@@ -39,7 +39,11 @@ class InfiniPackage:
self.source_path = Path(path).resolve()
toml_path = self.source_path / "infini.toml"
- data_load = toml.load(toml_path.open("r", encoding="utf-8"))
+ try:
+ data_load = toml.load(toml_path.open("r", encoding="utf-8"))
+ except Exception as error:
+ raise TomlLoadFailed(f"项目文件[infini.toml]导入失败: {error}") from error
+
if "infini" not in data_load.keys():
raise SyntaxError("配置文件中缺少[infini]项.")
@@ -58,9 +62,6 @@ class InfiniPackage:
def hash_name(self) -> str:
return f"{self.name}-{self.version}.ipk.hash"
- # @property
- # def home_p
-
class InfiniFrozenPackage:
source_path: Path
diff --git a/src/ipm/utils/freeze.py b/src/ipm/utils/freeze.py
index 74f4b40..5c6951c 100644
--- a/src/ipm/utils/freeze.py
+++ b/src/ipm/utils/freeze.py
@@ -10,8 +10,9 @@ import tempfile
import shutil
-def build_ipk(ipk: InfiniPackage) -> InfiniFrozenPackage:
- build_dir = ipk.source_path / ".build"
+def build_ipk(ipk: InfiniPackage, echo: bool = False) -> InfiniFrozenPackage:
+ info("正在初始化开发环境...", echo)
+ build_dir = ipk.source_path / "build"
src_path = ipk.source_path / "src"
dist_path = ipk.source_path / "dist"
ifp_path = dist_path / ipk.default_name
@@ -24,28 +25,35 @@ def build_ipk(ipk: InfiniPackage) -> InfiniFrozenPackage:
dist_path.mkdir(parents=True, exist_ok=True)
build_dir.mkdir(parents=True, exist_ok=True)
+ info("开发环境构建完成, 开始复制工程文件...", echo)
shutil.copytree(src_path, build_dir / "src")
shutil.copy2(ipk.source_path / "infini.toml", build_dir / "infini.toml")
+ info("工程文件复制完毕, 开始打包[ipk]文件...", echo)
_freeze.create_tar_gz(
str(build_dir),
str(ifp_path),
)
- (dist_path / ipk.hash_name).write_bytes(ifp_hash(ifp_path))
+ success(f"打包文件已存至[{ifp_path}].", echo)
+ info("开始创建SHA256验证文件...", echo)
+ hash_bytes = ifp_hash(ifp_path)
+ info(f"文件SHA256值为[{hash_bytes.hex()}].", echo)
+ (dist_path / ipk.hash_name).write_bytes(hash_bytes)
+ success(f"包[{ipk.name}]构建成功.", echo)
return InfiniFrozenPackage(source_path=ifp_path, **{"name": ipk.name})
def extract_ipk(
- source_path: StrPath, dist_path: str | Path, echo: bool = False
+ source_path: StrPath, dist_path: StrPath, echo: bool = False
) -> InfiniPackage:
ifp_path = Path(source_path).resolve()
dist_path = Path(dist_path).resolve()
hash_path = ifp_path.parent / (ifp_path.name + ".hash")
if not hash_path.exists():
- raise VerifyFailed("哈希文件不存在!")
+ raise VerifyFailed(f"哈希文件[{hash_path}]不存在!")
if not ifp_verify(ifp_path, hash_path.read_bytes()):
raise VerifyFailed("文件完整性验证失败!")