@codebend3r/che 1.1.0 → 1.2.0
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.
- package/README.md +38 -24
- package/bin/art.py +22 -20
- package/bin/che.py +208 -587
- package/bin/commands.py +220 -231
- package/bin/doctor.py +220 -0
- package/bin/drives/eject-all-drives.py +21 -38
- package/bin/drives/mount-all-drives.py +18 -62
- package/bin/drives/ping-nas.py +77 -105
- package/bin/files/compress-folders.py +7 -11
- package/bin/files/delete-by-ext.py +10 -14
- package/bin/files/delete-empty-folders.py +33 -37
- package/bin/files/delete-smb-files.py +12 -21
- package/bin/files/files-under-size.py +5 -11
- package/bin/files/find-largest-files.py +21 -25
- package/bin/files/list-permission.py +40 -0
- package/bin/files/make-alpha-dir.py +2 -0
- package/bin/git/all-actions.py +350 -253
- package/bin/git/checkout-my-branches.py +42 -63
- package/bin/git/clean-stale-branches.py +23 -48
- package/bin/git/prune-worktrees.py +12 -51
- package/bin/git/sync-all-branches.py +54 -283
- package/bin/git/update-local-branches.py +67 -73
- package/bin/gitutils.py +263 -0
- package/bin/install.py +393 -1182
- package/bin/machine.py +345 -0
- package/bin/media.py +127 -0
- package/bin/menu.py +437 -0
- package/bin/nas.py +102 -0
- package/bin/rcfile.py +380 -0
- package/bin/shellgen.py +503 -431
- package/bin/system/btop-launch.py +17 -13
- package/bin/system/update-brew.py +57 -68
- package/bin/tui.py +156 -153
- package/bin/utils.py +165 -237
- package/bin/video/delete-duplicate-videos.py +64 -62
- package/bin/video/detect-green-magenta-videos.py +105 -95
- package/bin/video/find-movie-by-year.py +3 -0
- package/bin/video/find-video-mkv-issues.py +48 -65
- package/bin/video/fix-codecs.py +25 -30
- package/bin/video/largest-tv-shows.py +60 -57
- package/bin/video/remove-metadata.py +3 -6
- package/bin/video/rename-video-file.py +74 -84
- package/bin/video/scan-videos-audio-language.py +16 -33
- package/bin/video/show-codecs.py +20 -24
- package/bin/video/validate-video-files.py +5 -8
- package/bin/video/video-list.py +17 -20
- package/package.json +4 -10
- package/shell/che.bash +33 -33
- package/shell/che.fish +7 -7
- package/shell/che.zsh +33 -33
- package/shell/completions/_che +19 -19
- package/shell/completions/che.bash +15 -15
- package/shell/completions/che.fish +34 -50
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
**A collection of helper scripts shared amongst computers, called by name from
|
|
6
6
|
your shell, and browsable through one menu.**
|
|
7
7
|
|
|
8
|
-
[](https://github.com/codebend3r/che/actions/workflows/sanity-check.yml)
|
|
9
9
|
[](https://www.npmjs.com/package/@codebend3r/che)
|
|
10
10
|

|
|
11
11
|

|
|
@@ -79,8 +79,8 @@ git clone https://github.com/codebend3r/che.git ~/Developer/git/che
|
|
|
79
79
|
~/Developer/git/che/install.sh
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
Or in one line, which clones for you (into `~/Developer/git/
|
|
83
|
-
|
|
82
|
+
Or in one line, which clones for you (into `~/Developer/git/che`, or wherever
|
|
83
|
+
`CHE_HOME` points):
|
|
84
84
|
|
|
85
85
|
```sh
|
|
86
86
|
curl -fsSL https://raw.githubusercontent.com/codebend3r/che/main/install.sh | bash
|
|
@@ -103,7 +103,9 @@ which opens a first-run wizard: it lists the shells it found and the rc files
|
|
|
103
103
|
it would touch, asks whether to add a `che` on `PATH` and whether to keep the
|
|
104
104
|
completion chimes, offers to remove hand-written wrappers left over from before
|
|
105
105
|
the installer existed, and reports any missing external tool (offering to
|
|
106
|
-
`brew install` them when Homebrew is present).
|
|
106
|
+
`brew install` them when Homebrew is present). A question already answered on
|
|
107
|
+
the command line (`--no-sounds`, `--no-path-shim`, `--replace-legacy`,
|
|
108
|
+
`--install-deps`) is not asked again.
|
|
107
109
|
|
|
108
110
|
<div align="center">
|
|
109
111
|
<img src="first-run.png" alt="The first run screen: che is not installed in this shell yet, offering to install, open the menu, or quit" width="700">
|
|
@@ -118,7 +120,7 @@ Skip the wizard:
|
|
|
118
120
|
./install.sh --yes --shells=zsh,fish # or the ones you name
|
|
119
121
|
./install.sh --dry-run # report what would change, write nothing
|
|
120
122
|
./install.sh --yes --replace-legacy # also remove pre-che hand-written wrappers
|
|
121
|
-
./install.sh --print # print the rc block, install nothing
|
|
123
|
+
./install.sh --print # print the exact rc block, install nothing
|
|
122
124
|
```
|
|
123
125
|
|
|
124
126
|
Every rc file is copied to `~/.config/che/backups/` before it is edited.
|
|
@@ -139,9 +141,9 @@ dotfiles:
|
|
|
139
141
|
```zsh
|
|
140
142
|
# >>> che shell functions >>>
|
|
141
143
|
# Managed by `che install` (che 1.0.0). Edits here are overwritten.
|
|
142
|
-
export CHE_HOME=/Users/you/Developer/git/
|
|
144
|
+
export CHE_HOME=/Users/you/Developer/git/che
|
|
143
145
|
export CHE_PYTHON=/opt/homebrew/bin/python3
|
|
144
|
-
[ -r /Users/you/Developer/git/
|
|
146
|
+
[ -r /Users/you/Developer/git/che/shell/che.zsh ] && . /Users/you/Developer/git/che/shell/che.zsh
|
|
145
147
|
case ":$PATH:" in *":/Users/you/.local/bin:"*) ;; *) PATH="/Users/you/.local/bin:$PATH" ;; esac
|
|
146
148
|
# <<< che shell functions <<<
|
|
147
149
|
```
|
|
@@ -164,7 +166,7 @@ python3 exists. `brew install python@3.13` if none does.
|
|
|
164
166
|
|
|
165
167
|
```sh
|
|
166
168
|
che uninstall # remove the block, the shim and the completions
|
|
167
|
-
che uninstall --purge # also delete ~/.config/che
|
|
169
|
+
che uninstall --purge # also delete ~/.config/che and the menu's input history
|
|
168
170
|
```
|
|
169
171
|
|
|
170
172
|
The repo itself is left alone; delete the directory to finish.
|
|
@@ -214,13 +216,13 @@ have used, and `che help <command>` prints that script's own `--help`.
|
|
|
214
216
|
| Command | Does | Preview |
|
|
215
217
|
| --- | --- | --- |
|
|
216
218
|
| **Git** | *Branch hygiene, worktrees and GitHub Actions* | |
|
|
217
|
-
| `all-actions` | GitHub Actions status for every open PR
|
|
219
|
+
| `all-actions` | GitHub Actions status for every open PR and every default branch | |
|
|
218
220
|
| `all-actions-watch` | Same as all-actions, refreshed on an interval | |
|
|
219
221
|
| `checkout-my-branches` | Check out recent remote branches you authored that aren't local yet | |
|
|
220
222
|
| `clean-stale-branches` | Delete local branches whose upstream is gone | `clean-stale-branches-dr` |
|
|
221
223
|
| `prune-worktrees` | Remove every linked worktree, then prune stale admin records | `prune-worktrees-dr` |
|
|
222
|
-
| `sync-all-branches` |
|
|
223
|
-
| `update-from-origin` |
|
|
224
|
+
| `sync-all-branches` | Delete merged branches, rebase the rest, check out new ones; branches only | `sync-all-branches-dr` |
|
|
225
|
+
| `update-from-origin` | Alias of sync-all-branches | `--dry-run` |
|
|
224
226
|
| `update-local-branches` | Rebase every local branch that has an upstream onto origin | `--dry-run` |
|
|
225
227
|
| **Video** | *Codecs, renaming, dedupe and metadata* | |
|
|
226
228
|
| `show-codecs` | Report media files outside the Direct Play codec/container set | |
|
|
@@ -316,9 +318,16 @@ where you copy one machine's rc file to another.
|
|
|
316
318
|
```
|
|
317
319
|
bin/
|
|
318
320
|
art.py the wordmark and the face beside it, for every screen
|
|
319
|
-
che.py the dispatcher and the
|
|
321
|
+
che.py the dispatcher, and the loop that runs what the menu picks
|
|
320
322
|
commands.py the command manifest: the source of truth for everything
|
|
321
|
-
|
|
323
|
+
doctor.py `che doctor`: every check on the install
|
|
324
|
+
gitutils.py shared git helpers: worktrees, main branch, hook skipping
|
|
325
|
+
install.py installer, first-run wizard, self-update, uninstall
|
|
326
|
+
machine.py shells, rc file paths, python, config: facts about the machine
|
|
327
|
+
media.py shared ffprobe helpers for the video scripts
|
|
328
|
+
menu.py the menu's list, keys and drawing
|
|
329
|
+
nas.py the NAS drive table and SMB mounting, for the drive scripts
|
|
330
|
+
rcfile.py edits to rc files: the che block and old hand-written wrappers
|
|
322
331
|
shellgen.py renders the wrappers, completions and README tables
|
|
323
332
|
tui.py terminal primitives: raw input, frame buffer, prompts
|
|
324
333
|
utils.py shared library: logging, arg parsing, subprocess, files
|
|
@@ -351,22 +360,27 @@ Every script follows the same shape, described in full at the top of
|
|
|
351
360
|
point, so pointing a delete tool at `~/media -> /Volumes/...` used to be a
|
|
352
361
|
silent no-op. Pass the real path to act on it.
|
|
353
362
|
|
|
354
|
-
## Syncing branches
|
|
363
|
+
## Syncing branches
|
|
355
364
|
|
|
356
365
|
```sh
|
|
357
|
-
sync-all-branches #
|
|
366
|
+
sync-all-branches # delete merged branches, rebase the rest, check out new ones
|
|
358
367
|
sync-all-branches-dr # preview only
|
|
359
|
-
update-from-origin #
|
|
368
|
+
update-from-origin # alias of sync-all-branches
|
|
360
369
|
```
|
|
361
370
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
otherwise makes `main` un-checkoutable and stale branches un-deletable.
|
|
371
|
+
Only branches change. The sync deletes local branches whose upstream is gone,
|
|
372
|
+
`pull --rebase`s every branch with an upstream, and creates local tracking
|
|
373
|
+
branches for the remote branches you authored that aren't local yet, without
|
|
374
|
+
switching to them. It ends with the main clone on `main`.
|
|
367
375
|
|
|
368
|
-
|
|
369
|
-
|
|
376
|
+
It **never pushes**, **never touches a worktree** and **never runs the repo's
|
|
377
|
+
git hooks**. A branch checked out in a linked worktree is rebased in place
|
|
378
|
+
there when that worktree is clean; a stale one is reported and kept, since git
|
|
379
|
+
won't delete a branch a worktree has checked out. The branch scripts
|
|
380
|
+
(`clean-stale-branches`, `update-local-branches`, `checkout-my-branches`,
|
|
381
|
+
`prune-worktrees`, `sync-all-branches`) point `core.hooksPath` at `/dev/null`
|
|
382
|
+
for every git they run, so a lefthook or husky pre-commit, pre-push or
|
|
383
|
+
post-checkout never fires on a checkout or a rebase.
|
|
370
384
|
|
|
371
385
|
## External tools
|
|
372
386
|
|
|
@@ -408,7 +422,7 @@ This repo uses [Bun](https://bun.sh) as its task runner and
|
|
|
408
422
|
```sh
|
|
409
423
|
bun run test # pytest
|
|
410
424
|
bun run generate # rewrite shell/ and this README's tables from the manifest
|
|
411
|
-
bun run ci # ruff +
|
|
425
|
+
bun run ci # ruff + exec bits + shebangs + pytest (stale generated files fail it)
|
|
412
426
|
```
|
|
413
427
|
|
|
414
428
|
Neither `pytest` nor `ruff` needs to be installed globally: the scripts shell
|
package/bin/art.py
CHANGED
|
@@ -56,28 +56,20 @@ INDENT = " "
|
|
|
56
56
|
GAP = 1
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
LOGO_LINES: tuple[str, ...] = tuple(LOGO.strip("\n").splitlines())
|
|
60
|
+
FACE_LINES: tuple[str, ...] = tuple(FACE.strip("\n").splitlines())
|
|
61
61
|
|
|
62
|
+
HEIGHT = len(FACE_LINES)
|
|
63
|
+
WIDTH = len(INDENT) + max(len(line) for line in FACE_LINES)
|
|
62
64
|
|
|
63
|
-
def face_lines() -> list[str]:
|
|
64
|
-
return FACE.strip("\n").splitlines()
|
|
65
65
|
|
|
66
|
+
def _paint(lines: Sequence[str], colors: tui.Palette, indent: str = "") -> list[str]:
|
|
67
|
+
return [f"{colors.bright_magenta}{indent}{line}{colors.reset}" for line in lines]
|
|
66
68
|
|
|
67
|
-
HEIGHT = len(face_lines())
|
|
68
|
-
WIDTH = len(INDENT) + max(len(line) for line in face_lines())
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
def wordmark(colors: tui.Palette | None = None) -> list[str]:
|
|
70
|
+
def wordmark(colors: tui.Palette) -> list[str]:
|
|
72
71
|
"""The block letters, coloured the way every screen prints them."""
|
|
73
|
-
|
|
74
|
-
return [f"{colors.bright_magenta}{line}{colors.reset}" for line in logo_lines()]
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def face(colors: tui.Palette | None = None) -> list[str]:
|
|
78
|
-
"""The face, indented to line up with the wordmark and in the same colour."""
|
|
79
|
-
colors = colors or tui.palette()
|
|
80
|
-
return [f"{colors.bright_magenta}{INDENT}{line}{colors.reset}" for line in face_lines()]
|
|
72
|
+
return _paint(LOGO_LINES, colors)
|
|
81
73
|
|
|
82
74
|
|
|
83
75
|
def compose(left: Sequence[str], right: Sequence[str], *, gap: int = GAP) -> list[str]:
|
|
@@ -105,17 +97,27 @@ def fits(right: Sequence[str], columns: int) -> bool:
|
|
|
105
97
|
return WIDTH + GAP + right_width <= columns
|
|
106
98
|
|
|
107
99
|
|
|
108
|
-
def banner(
|
|
109
|
-
right: Sequence[str], *, columns: int | None = None, colors: tui.Palette | None = None
|
|
110
|
-
) -> list[str]:
|
|
100
|
+
def banner(right: Sequence[str], *, colors: tui.Palette, columns: int | None = None) -> list[str]:
|
|
111
101
|
"""The face with ``right`` beside it, or ``right`` alone when it would not fit.
|
|
112
102
|
|
|
113
103
|
``right`` is normally the wordmark plus whatever a screen wants under it (a
|
|
114
104
|
subtitle, the command count). It comes back untouched, indent and all, on a
|
|
115
105
|
terminal too narrow for both, so every caller keeps working at 40 columns.
|
|
106
|
+
The face is indented to line up with the wordmark and painted in its colour.
|
|
116
107
|
"""
|
|
117
108
|
if columns is None:
|
|
118
109
|
columns = tui.size()[0]
|
|
119
110
|
if not fits(right, columns):
|
|
120
111
|
return list(right)
|
|
121
|
-
return compose(
|
|
112
|
+
return compose(_paint(FACE_LINES, colors, INDENT), right)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def print_logo(subtitle: str = "") -> None:
|
|
116
|
+
"""The face beside the wordmark, with ``subtitle`` under the letters."""
|
|
117
|
+
colors = tui.palette()
|
|
118
|
+
right = wordmark(colors)
|
|
119
|
+
if subtitle:
|
|
120
|
+
right.append(f" {colors.grey}{subtitle}{colors.reset}")
|
|
121
|
+
for line in banner(right, colors=colors):
|
|
122
|
+
print(line)
|
|
123
|
+
print()
|