assetout 0.1.1__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.
Files changed (29) hide show
  1. assetout-0.1.1/LICENSE.md +21 -0
  2. assetout-0.1.1/PKG-INFO +160 -0
  3. assetout-0.1.1/README.md +134 -0
  4. assetout-0.1.1/pyproject.toml +41 -0
  5. assetout-0.1.1/setup.cfg +4 -0
  6. assetout-0.1.1/src/assetout/__init__.py +1 -0
  7. assetout-0.1.1/src/assetout/cli.py +135 -0
  8. assetout-0.1.1/src/assetout/commands/__init__.py +0 -0
  9. assetout-0.1.1/src/assetout/commands/cmd_add.py +138 -0
  10. assetout-0.1.1/src/assetout/commands/cmd_git_wrappers.py +51 -0
  11. assetout-0.1.1/src/assetout/commands/cmd_init.py +45 -0
  12. assetout-0.1.1/src/assetout/commands/cmd_pull.py +247 -0
  13. assetout-0.1.1/src/assetout/commands/cmd_push.py +215 -0
  14. assetout-0.1.1/src/assetout/commands/cmd_rm.py +83 -0
  15. assetout-0.1.1/src/assetout/commands/cmd_status.py +92 -0
  16. assetout-0.1.1/src/assetout/commands/cmd_sync.py +284 -0
  17. assetout-0.1.1/src/assetout/constants.py +29 -0
  18. assetout-0.1.1/src/assetout/utils/__init__.py +0 -0
  19. assetout-0.1.1/src/assetout/utils/configs.py +106 -0
  20. assetout-0.1.1/src/assetout/utils/helpers.py +257 -0
  21. assetout-0.1.1/src/assetout/utils/packaging.py +132 -0
  22. assetout-0.1.1/src/assetout/utils/paths.py +80 -0
  23. assetout-0.1.1/src/assetout/utils/shared.py +217 -0
  24. assetout-0.1.1/src/assetout.egg-info/PKG-INFO +160 -0
  25. assetout-0.1.1/src/assetout.egg-info/SOURCES.txt +27 -0
  26. assetout-0.1.1/src/assetout.egg-info/dependency_links.txt +1 -0
  27. assetout-0.1.1/src/assetout.egg-info/entry_points.txt +2 -0
  28. assetout-0.1.1/src/assetout.egg-info/requires.txt +4 -0
  29. assetout-0.1.1/src/assetout.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ambientneon
4
+
5
+ Permission is substantive granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: assetout
3
+ Version: 0.1.1
4
+ Summary: Keep large binary assets out of Git — store them in GitHub Releases
5
+ Author: ambientneon
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ambientneon/assetout
8
+ Project-URL: Repository, https://github.com/ambientneon/assetout
9
+ Project-URL: Issues, https://github.com/ambientneon/assetout/issues
10
+ Keywords: git,binaries,assets,github-releases,lfs
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Version Control :: Git
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE.md
21
+ Requires-Dist: typer>=0.12
22
+ Requires-Dist: rich>=13.0
23
+ Requires-Dist: pyyaml>=6.0
24
+ Requires-Dist: xxhash>=3.0
25
+ Dynamic: license-file
26
+
27
+ # assetout
28
+
29
+ **Keep large binary assets out of Git.**
30
+ Store them in a local mirror (`.assetout/`) and sync with [GitHub Releases](https://docs.github.com/en/repositories/releasing-projects-on-github).
31
+
32
+ A simple alternative to Git LFS when you only need to keep big files out of the repository history and distribute them via Releases.
33
+
34
+ ## Features
35
+
36
+ - Track large files outside Git (manifest + hashes)
37
+ - Local mirror in `.assetout/data/`
38
+ - Pack into zip archives and upload to GitHub Releases
39
+ - Automatic split into multiple archives when over the size limit
40
+ - Optional grouping by top-level project folder
41
+ - Sync between project files and the mirror (`remote` / `local` / `manual`)
42
+ - Wrappers: `git-add`, `git-push`
43
+
44
+ ## Requirements
45
+
46
+ - Python 3.11+
47
+ - Git repository with `origin` on GitHub
48
+ - [GitHub CLI (`gh`)](https://cli.github.com/) — authenticated (`gh auth login`)
49
+
50
+ ## Install
51
+
52
+ ```bash
53
+ pip install assetout
54
+ ```
55
+
56
+ If the `assetout` command is not found (common on Windows):
57
+
58
+ ```bash
59
+ # option A — recommended for CLI tools
60
+ pipx install assetout
61
+
62
+ # option B — module form (after adding __main__.py)
63
+ python -m assetout --help
64
+
65
+ # option C — add Python Scripts folder to PATH, then:
66
+ assetout --help
67
+ ```
68
+
69
+ ## Quick start
70
+
71
+ ```bash
72
+ # inside your git repo
73
+ assetout init
74
+
75
+ # register large files (copy into .assetout/data/, update manifest & .gitignore)
76
+ assetout add path/to/big/files_or_dirs
77
+
78
+ assetout status
79
+
80
+ # upload archives to GitHub Releases
81
+ assetout push
82
+
83
+ # on another machine / clean checkout
84
+ assetout pull
85
+ assetout sync # mirror → project files
86
+ ```
87
+
88
+ ## Commands
89
+
90
+ | Command | Description |
91
+ |---------|-------------|
92
+ | `init` | Create `.assetout/` (config, manifest, data dir) |
93
+ | `add` | Add large files to the mirror |
94
+ | `rm` | Remove from manifest and mirror |
95
+ | `status` | Show tracked files and their state |
96
+ | `push` | Pack mirror and upload to GitHub Releases |
97
+ | `pull` | Download release archives into `.assetout/data/` |
98
+ | `sync` | Sync between `.assetout/data/` and project files |
99
+ | `git-add` | `assetout add` then `git add` |
100
+ | `git-push` | `git push` then `assetout push` |
101
+
102
+ ### Useful flags
103
+
104
+ ```bash
105
+ assetout push --replace-all # delete old assetout archives, then upload
106
+ assetout sync --policy local --force
107
+ assetout rm path/to/file --project # also delete from working tree
108
+ ```
109
+
110
+ ## How it works
111
+
112
+ ```text
113
+ project files ←→ .assetout/data/ ←→ GitHub Release (zip archives)
114
+ ↑ sync ↑ push / pull
115
+ ```
116
+
117
+ - Files larger than `max_git_file_size` are kept out of Git
118
+ - Manifest stores size + hash for each path
119
+ - Archives are named `binaries-01.zip`, `project-binaries-01.zip`, …
120
+
121
+ ## Configuration
122
+
123
+ Created by `assetout init` at `.assetout/config.yaml`:
124
+
125
+ ```yaml
126
+ max_git_file_size: 10MB
127
+ hash_algorithm: xxh3
128
+ use_only_git_files: true
129
+ storage_backend: github_releases
130
+ sync_policy: remote # remote | local | manual
131
+
132
+ packaging:
133
+ group_by_top_level: false # true → group by first folder name
134
+ max_archive_size: 1900MB
135
+ max_file_size: 2048MB
136
+
137
+ github:
138
+ tag: assets-latest
139
+ asset_name: binaries.zip # legacy; names are generated automatically
140
+ owner: "" # optional; taken from git remote if empty
141
+ repo: ""
142
+ ```
143
+
144
+ ## Typical workflow
145
+
146
+ ```bash
147
+ # day-to-day
148
+ assetout git-add .
149
+ git commit -m "Update assets"
150
+ assetout git-push
151
+
152
+ # or explicitly
153
+ assetout add assets/
154
+ assetout push
155
+ git add . && git commit -m "..." && git push
156
+ ```
157
+
158
+ ## License
159
+
160
+ MIT
@@ -0,0 +1,134 @@
1
+ # assetout
2
+
3
+ **Keep large binary assets out of Git.**
4
+ Store them in a local mirror (`.assetout/`) and sync with [GitHub Releases](https://docs.github.com/en/repositories/releasing-projects-on-github).
5
+
6
+ A simple alternative to Git LFS when you only need to keep big files out of the repository history and distribute them via Releases.
7
+
8
+ ## Features
9
+
10
+ - Track large files outside Git (manifest + hashes)
11
+ - Local mirror in `.assetout/data/`
12
+ - Pack into zip archives and upload to GitHub Releases
13
+ - Automatic split into multiple archives when over the size limit
14
+ - Optional grouping by top-level project folder
15
+ - Sync between project files and the mirror (`remote` / `local` / `manual`)
16
+ - Wrappers: `git-add`, `git-push`
17
+
18
+ ## Requirements
19
+
20
+ - Python 3.11+
21
+ - Git repository with `origin` on GitHub
22
+ - [GitHub CLI (`gh`)](https://cli.github.com/) — authenticated (`gh auth login`)
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install assetout
28
+ ```
29
+
30
+ If the `assetout` command is not found (common on Windows):
31
+
32
+ ```bash
33
+ # option A — recommended for CLI tools
34
+ pipx install assetout
35
+
36
+ # option B — module form (after adding __main__.py)
37
+ python -m assetout --help
38
+
39
+ # option C — add Python Scripts folder to PATH, then:
40
+ assetout --help
41
+ ```
42
+
43
+ ## Quick start
44
+
45
+ ```bash
46
+ # inside your git repo
47
+ assetout init
48
+
49
+ # register large files (copy into .assetout/data/, update manifest & .gitignore)
50
+ assetout add path/to/big/files_or_dirs
51
+
52
+ assetout status
53
+
54
+ # upload archives to GitHub Releases
55
+ assetout push
56
+
57
+ # on another machine / clean checkout
58
+ assetout pull
59
+ assetout sync # mirror → project files
60
+ ```
61
+
62
+ ## Commands
63
+
64
+ | Command | Description |
65
+ |---------|-------------|
66
+ | `init` | Create `.assetout/` (config, manifest, data dir) |
67
+ | `add` | Add large files to the mirror |
68
+ | `rm` | Remove from manifest and mirror |
69
+ | `status` | Show tracked files and their state |
70
+ | `push` | Pack mirror and upload to GitHub Releases |
71
+ | `pull` | Download release archives into `.assetout/data/` |
72
+ | `sync` | Sync between `.assetout/data/` and project files |
73
+ | `git-add` | `assetout add` then `git add` |
74
+ | `git-push` | `git push` then `assetout push` |
75
+
76
+ ### Useful flags
77
+
78
+ ```bash
79
+ assetout push --replace-all # delete old assetout archives, then upload
80
+ assetout sync --policy local --force
81
+ assetout rm path/to/file --project # also delete from working tree
82
+ ```
83
+
84
+ ## How it works
85
+
86
+ ```text
87
+ project files ←→ .assetout/data/ ←→ GitHub Release (zip archives)
88
+ ↑ sync ↑ push / pull
89
+ ```
90
+
91
+ - Files larger than `max_git_file_size` are kept out of Git
92
+ - Manifest stores size + hash for each path
93
+ - Archives are named `binaries-01.zip`, `project-binaries-01.zip`, …
94
+
95
+ ## Configuration
96
+
97
+ Created by `assetout init` at `.assetout/config.yaml`:
98
+
99
+ ```yaml
100
+ max_git_file_size: 10MB
101
+ hash_algorithm: xxh3
102
+ use_only_git_files: true
103
+ storage_backend: github_releases
104
+ sync_policy: remote # remote | local | manual
105
+
106
+ packaging:
107
+ group_by_top_level: false # true → group by first folder name
108
+ max_archive_size: 1900MB
109
+ max_file_size: 2048MB
110
+
111
+ github:
112
+ tag: assets-latest
113
+ asset_name: binaries.zip # legacy; names are generated automatically
114
+ owner: "" # optional; taken from git remote if empty
115
+ repo: ""
116
+ ```
117
+
118
+ ## Typical workflow
119
+
120
+ ```bash
121
+ # day-to-day
122
+ assetout git-add .
123
+ git commit -m "Update assets"
124
+ assetout git-push
125
+
126
+ # or explicitly
127
+ assetout add assets/
128
+ assetout push
129
+ git add . && git commit -m "..." && git push
130
+ ```
131
+
132
+ ## License
133
+
134
+ MIT
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "assetout"
7
+ version = "0.1.1"
8
+ description = "Keep large binary assets out of Git — store them in GitHub Releases"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.11"
12
+ authors = [
13
+ { name = "ambientneon" } # поменяй на себя
14
+ ]
15
+ keywords = ["git", "binaries", "assets", "github-releases", "lfs"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Software Development :: Version Control :: Git",
24
+ ]
25
+ dependencies = [
26
+ "typer>=0.12",
27
+ "rich>=13.0",
28
+ "pyyaml>=6.0",
29
+ "xxhash>=3.0",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/ambientneon/assetout"
34
+ Repository = "https://github.com/ambientneon/assetout"
35
+ Issues = "https://github.com/ambientneon/assetout/issues"
36
+
37
+ [project.scripts]
38
+ assetout = "assetout.cli:app"
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,135 @@
1
+ # assetout/cli.py
2
+
3
+ from pathlib import Path
4
+
5
+ import typer
6
+ from typing import Optional
7
+
8
+ from rich.console import Console
9
+
10
+ from assetout.constants import SYNC_ALLOWED_POLICIES
11
+ from assetout.commands.cmd_init import run_init
12
+ from assetout.commands.cmd_add import run_add
13
+ from assetout.commands.cmd_status import run_status
14
+ from assetout.commands.cmd_rm import run_rm
15
+ from assetout.commands.cmd_push import run_push
16
+ from assetout.commands.cmd_pull import run_pull
17
+ from assetout.commands.cmd_sync import run_sync
18
+ from assetout.commands.cmd_git_wrappers import (
19
+ run_git_add,
20
+ run_git_push
21
+ )
22
+
23
+ app = typer.Typer(
24
+ name="assetout",
25
+ help="Keep large binary assets out of Git",
26
+ no_args_is_help=True
27
+ )
28
+ console = Console()
29
+
30
+ @app.callback()
31
+ def main() -> None:
32
+ """Keep large binary assets out of Git."""
33
+ pass
34
+
35
+
36
+ @app.command()
37
+ def init() -> None:
38
+ """Initialize assetout in the current repository."""
39
+ run_init()
40
+
41
+
42
+ @app.command()
43
+ def add(
44
+ paths: list[Path] = typer.Argument(
45
+ ...,
46
+ help="Files or directories to scan for large assets"
47
+ ),
48
+ ) -> None:
49
+ """Add large file(s) to the mirror and manifest (not to Git)."""
50
+ run_add([str(p) for p in paths])
51
+
52
+
53
+ @app.command()
54
+ def status() -> None:
55
+ """Show status of tracked large files."""
56
+ run_status()
57
+
58
+
59
+ @app.command()
60
+ def rm(
61
+ paths: list[Path] = typer.Argument(..., help="Tracked files to remove"),
62
+ project: bool = typer.Option(
63
+ False,
64
+ "--project",
65
+ help="Also delete the file from the project working tree"
66
+ ),
67
+ ) -> None:
68
+ """Remove file(s) from manifest and mirror (not from cloud until push)."""
69
+ run_rm([str(p) for p in paths], delete_project=project)
70
+
71
+
72
+ @app.command()
73
+ def push(
74
+ replace_all: bool = typer.Option(
75
+ False,
76
+ "--replace-all",
77
+ "-ra",
78
+ help="Delete all existing assetout archives from the release before uploading.",
79
+ ),
80
+ ) -> None:
81
+ """Pack mirror into zip(s) and upload to GitHub Releases."""
82
+ run_push(replace_all=replace_all)
83
+
84
+
85
+ @app.command()
86
+ def pull() -> None:
87
+ """Download Release zip into .assetout/data/ (replace)."""
88
+ run_pull()
89
+
90
+
91
+ @app.command()
92
+ def sync(
93
+ policy: Optional[str] = typer.Option(
94
+ None,
95
+ "--policy",
96
+ "-p",
97
+ help=f"Sync direction. Allowed: {', '.join(SYNC_ALLOWED_POLICIES)}. Default: from config (sync_policy).",
98
+ ),
99
+ force: bool = typer.Option(
100
+ False,
101
+ "--force",
102
+ "-f",
103
+ help="Overwrite on hash mismatch (remote→project or local→data).",
104
+ ),
105
+ ) -> None:
106
+ """Sync between .assetout/data/ and project files (manifest paths only)."""
107
+ run_sync(policy=policy, force=force)
108
+
109
+
110
+ @app.command("git-add")
111
+ def git_add(
112
+ paths: list[Path] = typer.Argument(..., help="Files/dirs to add"),
113
+ ) -> None:
114
+ """
115
+ 1. assetout add (переносит большие файлы в mirror)
116
+ 2. git add (остальное)
117
+ """
118
+ run_git_add([str(p) for p in paths])
119
+
120
+
121
+ @app.command("git-push")
122
+ def git_push(
123
+ remote: str = typer.Argument("origin"),
124
+ branch: Optional[str] = None,
125
+ replace_all: bool = typer.Option(False, "--replace-all", "-ra"),
126
+ ) -> None:
127
+ """
128
+ 1. git push
129
+ 2. assetout push (binaries)
130
+ """
131
+ run_git_push(remote, branch, replace_all=replace_all)
132
+
133
+
134
+ if __name__ == "__main__":
135
+ app()
File without changes
@@ -0,0 +1,138 @@
1
+ # assetout/commands/cmd_add.py
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+ from pathlib import Path
7
+ from rich.console import Console
8
+
9
+ from assetout.constants import SKIP_DIR_NAMES
10
+ from assetout.utils.paths import (
11
+ find_repo_root,
12
+ get_data_path,
13
+ get_rel_path
14
+ )
15
+ from assetout.utils.configs import (
16
+ load_config,
17
+ load_manifest,
18
+ save_manifest,
19
+ )
20
+ from assetout.utils.helpers import (
21
+ human_size,
22
+ parse_size,
23
+ safe_copy,
24
+ make_manifest_entry,
25
+ sync_gitignore_tracked,
26
+ run_process
27
+ )
28
+
29
+
30
+ console = Console()
31
+
32
+ # public
33
+ def run_add(paths: list[str]) -> None:
34
+ root = find_repo_root(after_init=True)
35
+ config = load_config(root)
36
+ manifest = load_manifest(root)
37
+
38
+ max_size = parse_size(config.get("max_git_file_size", "10MB"))
39
+ use_only_git_files = bool(config.get("use_only_git_files", True))
40
+
41
+ files = _collect_files(paths, root)
42
+ if not files:
43
+ console.print("[dim]No files to process.[/dim]")
44
+ return
45
+
46
+ added = skipped = errors = 0
47
+ for path in files:
48
+ result = _add_one(path, root, max_size, use_only_git_files, manifest)
49
+
50
+ if result == "added":
51
+ added += 1
52
+ elif result == "skipped":
53
+ skipped += 1
54
+ else:
55
+ errors += 1
56
+
57
+ if added:
58
+ save_manifest(root, manifest)
59
+
60
+ sync_gitignore_tracked(root, manifest)
61
+
62
+ console.print(f"\n[green]Added: {added},[/green] [yellow]skipped: {skipped},[/yellow] [red]errors: {errors}[/red]")
63
+
64
+
65
+ # private
66
+ def _is_git_ignored(root: Path, rel: str) -> bool:
67
+ """True, усли Git игнорирует файл."""
68
+ result = run_process(
69
+ ["git", "check-ignore", "-q", rel],
70
+ cwd=root
71
+ )
72
+
73
+ # exit 0 = ignored, 1 = not ignored, 128 = error (нет git и т.п.)
74
+ return result.returncode == 0
75
+
76
+ def _collect_files(raw_paths: list[str], root: Path) -> list[Path]:
77
+ """Разворачивает файлы и директории в плоский список файлов."""
78
+ collected: list[Path] = []
79
+
80
+ for raw in raw_paths:
81
+ path = Path(raw).resolve()
82
+
83
+ if path.is_file():
84
+ collected.append(path)
85
+ continue
86
+
87
+ if path.is_dir():
88
+ for child in path.rglob("*"):
89
+ if not child.is_file():
90
+ continue
91
+
92
+ # пропускаем служебные директории
93
+ try:
94
+ rel_parts = child.relative_to(root).parts
95
+ except ValueError:
96
+ continue
97
+
98
+ if any(part in SKIP_DIR_NAMES for part in rel_parts):
99
+ continue
100
+
101
+ collected.append(child)
102
+
103
+ continue
104
+
105
+ console.print(f"[yellow]Skip[/yellow] {raw} - path not found")
106
+
107
+ # уникальные пути, стабильный порядок
108
+ unique = sorted(set(collected), key=lambda p: str(p).lower())
109
+ return unique
110
+
111
+
112
+ def _add_one(path: Path, root: Path, max_size: int, use_only_git_files: bool, manifest: dict[str, Any]) -> str:
113
+ """Возвращает: 'added' | 'skipped' | 'error'."""
114
+ rel = get_rel_path(path, root)
115
+
116
+ if rel is None:
117
+ console.print(f"[yellow]Skip[/yellow] {path} - outside repository")
118
+ return "error"
119
+
120
+ if not path.is_file():
121
+ console.print(f"[yellow]Skip[/yellow] {rel} - not a file")
122
+ return "error"
123
+
124
+ if use_only_git_files and _is_git_ignored(root, rel):
125
+ # dimMsg(f"Skip {rel} - ignored by Git")
126
+ return "skipped"
127
+
128
+ size = path.stat().st_size
129
+ if size <= max_size:
130
+ # warningMsg(f"Skip {rel} - {human_size(size)} ≤ max ({human_size(max_size)}). Use git add .")
131
+ return "skipped"
132
+
133
+ dest = get_data_path(root, rel)
134
+ safe_copy(path, dest)
135
+
136
+ manifest["files"][rel] = make_manifest_entry(root, path)
137
+ console.print(f"[green]✓[/green] Added {rel} ({human_size(size)})")
138
+ return "added"
@@ -0,0 +1,51 @@
1
+ # commands/cmd_git_wrappers.py
2
+
3
+ from __future__ import annotations
4
+
5
+ from rich.console import Console
6
+
7
+ from assetout.commands.cmd_add import run_add
8
+ from assetout.commands.cmd_push import run_push
9
+ from assetout.utils.helpers import run_process
10
+ from assetout.utils.paths import find_repo_root
11
+
12
+ console = Console()
13
+
14
+ def run_git_add(paths: list[str]) -> None:
15
+ root = find_repo_root(after_init=True)
16
+
17
+ # 1. сначала assetout
18
+ console.print("[bold]assetout add[/bold] ...")
19
+ run_add(paths)
20
+
21
+ # 2. потом обычный git add
22
+ console.print("[bold]git add[/bold] ...")
23
+ result = run_process(["git", "add", "--"] + paths, cwd=root)
24
+
25
+ if result.returncode != 0:
26
+ console.print(f"[red]Error:[/red] {result.stderr or 'git add failed'}")
27
+ raise SystemExit(result.returncode)
28
+
29
+
30
+ def run_git_push(
31
+ remote: str = "origin",
32
+ branch: str | None = None,
33
+ replace_all: bool = False,
34
+ ) -> None:
35
+ root = find_repo_root(after_init=True)
36
+
37
+ # 1. сначала код
38
+ cmd = ["git", "push", remote]
39
+ if branch:
40
+ cmd.append(branch)
41
+
42
+ console.print(f"[bold]git push[/bold] {' '.join(cmd[2:])} ...")
43
+ result = run_process(cmd, cwd=root)
44
+
45
+ if result.returncode != 0:
46
+ console.print(f"[red]Error:[/red] {result.stderr or 'git push failed'}")
47
+ raise SystemExit(result.returncode)
48
+
49
+ # 2. потом бинарники
50
+ console.print("[bold]assetout push[/bold] ...")
51
+ run_push(replace_all=replace_all)