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.
- assetout-0.1.1/LICENSE.md +21 -0
- assetout-0.1.1/PKG-INFO +160 -0
- assetout-0.1.1/README.md +134 -0
- assetout-0.1.1/pyproject.toml +41 -0
- assetout-0.1.1/setup.cfg +4 -0
- assetout-0.1.1/src/assetout/__init__.py +1 -0
- assetout-0.1.1/src/assetout/cli.py +135 -0
- assetout-0.1.1/src/assetout/commands/__init__.py +0 -0
- assetout-0.1.1/src/assetout/commands/cmd_add.py +138 -0
- assetout-0.1.1/src/assetout/commands/cmd_git_wrappers.py +51 -0
- assetout-0.1.1/src/assetout/commands/cmd_init.py +45 -0
- assetout-0.1.1/src/assetout/commands/cmd_pull.py +247 -0
- assetout-0.1.1/src/assetout/commands/cmd_push.py +215 -0
- assetout-0.1.1/src/assetout/commands/cmd_rm.py +83 -0
- assetout-0.1.1/src/assetout/commands/cmd_status.py +92 -0
- assetout-0.1.1/src/assetout/commands/cmd_sync.py +284 -0
- assetout-0.1.1/src/assetout/constants.py +29 -0
- assetout-0.1.1/src/assetout/utils/__init__.py +0 -0
- assetout-0.1.1/src/assetout/utils/configs.py +106 -0
- assetout-0.1.1/src/assetout/utils/helpers.py +257 -0
- assetout-0.1.1/src/assetout/utils/packaging.py +132 -0
- assetout-0.1.1/src/assetout/utils/paths.py +80 -0
- assetout-0.1.1/src/assetout/utils/shared.py +217 -0
- assetout-0.1.1/src/assetout.egg-info/PKG-INFO +160 -0
- assetout-0.1.1/src/assetout.egg-info/SOURCES.txt +27 -0
- assetout-0.1.1/src/assetout.egg-info/dependency_links.txt +1 -0
- assetout-0.1.1/src/assetout.egg-info/entry_points.txt +2 -0
- assetout-0.1.1/src/assetout.egg-info/requires.txt +4 -0
- 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.
|
assetout-0.1.1/PKG-INFO
ADDED
|
@@ -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
|
assetout-0.1.1/README.md
ADDED
|
@@ -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"]
|
assetout-0.1.1/setup.cfg
ADDED
|
@@ -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)
|